From 11e8f7750757864ad463d57d57acceff7be9e1d0 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Sat, 6 Jan 2024 14:58:10 +0530 Subject: [PATCH 01/61] perf: remove unchecked in for loop --- .gas-snapshot | 64 ++++++++++++++++++------------------- src/SablierV2Batch.sol | 40 +++++------------------ test/utils/ArrayBuilder.sol | 4 +-- test/utils/BatchBuilder.sol | 16 +++++----- test/utils/Murky.sol | 4 +-- 5 files changed, 52 insertions(+), 76 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index d44f0f21..97828581 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,33 +1,33 @@ -Claim_Integration_Test:test_Claim() (gas: 278831) -Claim_Integration_Test:test_RevertGiven_AlreadyClaimed() (gas: 266771) -Claim_Integration_Test:test_RevertGiven_CampaignExpired() (gas: 17765) -Claim_Integration_Test:test_RevertGiven_ProtocolFeeNotZero() (gas: 84689) -Clawback_Integration_Test:testFuzz_Clawback(address) (runs: 20, μ: 316927, ~: 316927) -Clawback_Integration_Test:testFuzz_Clawback_CampaignNotExpired(address) (runs: 20, μ: 88771, ~: 88771) -Clawback_Integration_Test:test_Clawback() (gas: 272109) -Clawback_Integration_Test:test_RevertGiven_CampaignNotExpired() (gas: 30440) -Constructor_MerkleStreamerLL_Integration_Test:test_Constructor() (gas: 1219550) -CreateMerkleStreamerLL_Integration_Test:testFuzz_CreateMerkleStreamerLL(address,uint40) (runs: 20, μ: 1139893, ~: 1139893) -CreateMerkleStreamerLL_Integration_Test:test_RevertGiven_AlreadyDeployed() (gas: 8937393460516730625) -CreateWithDeltas_Integration_Test:test_BatchCreateWithDeltas() (gas: 2070549) -CreateWithDurations_Integration_Test:test_BatchCreateWithDurations() (gas: 1334021) -CreateWithMilestones_Integration_Test:test_BatchCreateWithMilestones() (gas: 2063959) -CreateWithRange_Integration_Test:test_CreateWithRange() (gas: 1336190) -HasClaimed_Integration_Test:test_HasClaimed() (gas: 251894) -HasClaimed_Integration_Test:test_HasClaimed_IndexNotInTree() (gas: 8011) -HasClaimed_Integration_Test:test_HasClaimed_NotClaimed() (gas: 13268) -HasExpired_Integration_Test:test_HasExpired_ExpirationEqualToCurrentTime() (gas: 13971) -HasExpired_Integration_Test:test_HasExpired_ExpirationGreaterThanCurrentTime() (gas: 14065) -HasExpired_Integration_Test:test_HasExpired_ExpirationLessThanCurrentTime() (gas: 5760) -HasExpired_Integration_Test:test_HasExpired_ExpirationZero() (gas: 1081876) +Claim_Integration_Test:test_Claim() (gas: 278641) +Claim_Integration_Test:test_RevertGiven_AlreadyClaimed() (gas: 266327) +Claim_Integration_Test:test_RevertGiven_CampaignExpired() (gas: 17745) +Claim_Integration_Test:test_RevertGiven_ProtocolFeeNotZero() (gas: 84510) +Clawback_Integration_Test:testFuzz_Clawback(address) (runs: 20, μ: 316725, ~: 316725) +Clawback_Integration_Test:testFuzz_Clawback_CampaignNotExpired(address) (runs: 20, μ: 88746, ~: 88746) +Clawback_Integration_Test:test_Clawback() (gas: 271907) +Clawback_Integration_Test:test_RevertGiven_CampaignNotExpired() (gas: 30429) +Constructor_MerkleStreamerLL_Integration_Test:test_Constructor() (gas: 1198502) +CreateMerkleStreamerLL_Integration_Test:testFuzz_CreateMerkleStreamerLL(address,uint40) (runs: 20, μ: 1126334, ~: 1126334) +CreateMerkleStreamerLL_Integration_Test:test_RevertGiven_AlreadyDeployed() (gas: 8937393460516730624) +CreateWithDeltas_Integration_Test:test_BatchCreateWithDeltas() (gas: 2070068) +CreateWithDurations_Integration_Test:test_BatchCreateWithDurations() (gas: 1333914) +CreateWithMilestones_Integration_Test:test_BatchCreateWithMilestones() (gas: 2063258) +CreateWithRange_Integration_Test:test_CreateWithRange() (gas: 1336083) +HasClaimed_Integration_Test:test_HasClaimed() (gas: 251704) +HasClaimed_Integration_Test:test_HasClaimed_IndexNotInTree() (gas: 7997) +HasClaimed_Integration_Test:test_HasClaimed_NotClaimed() (gas: 13254) +HasExpired_Integration_Test:test_HasExpired_ExpirationEqualToCurrentTime() (gas: 13957) +HasExpired_Integration_Test:test_HasExpired_ExpirationGreaterThanCurrentTime() (gas: 14051) +HasExpired_Integration_Test:test_HasExpired_ExpirationLessThanCurrentTime() (gas: 5746) +HasExpired_Integration_Test:test_HasExpired_ExpirationZero() (gas: 1068623) MerkleBuilder_Test:testFuzz_ComputeLeaf(uint256,address,uint128) (runs: 20, μ: 1176, ~: 1176) -MerkleBuilder_Test:testFuzz_ComputeLeaves((uint256,address,uint128)[]) (runs: 20, μ: 353773, ~: 397124) -Precompiles_Test:test_DeployBatch() (gas: 2794484) -Precompiles_Test:test_DeployMerkleStreamerFactory() (gas: 3124723) -Precompiles_Test:test_DeployPeriphery() (gas: 5913539) -USDC_CreateWithMilestones_Batch_Fork_Test:testForkFuzz_CreateWithMilestones((uint128,address,address,uint128,uint40,(uint128,uint64,uint40)[])) (runs: 20, μ: 34894028, ~: 21953046) -USDC_CreateWithRange_Batch_Fork_Test:testForkFuzz_CreateWithRange((uint128,(uint40,uint40,uint40),address,address,uint128)) (runs: 20, μ: 1223264, ~: 1038250) -USDC_MerkleStreamerLL_Fork_Test:testForkFuzz_MerkleStreamerLL((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 20, μ: 4359652, ~: 3909187) -USDT_CreateWithMilestones_Batch_Fork_Test:testForkFuzz_CreateWithMilestones((uint128,address,address,uint128,uint40,(uint128,uint64,uint40)[])) (runs: 20, μ: 34894028, ~: 21953046) -USDT_CreateWithRange_Batch_Fork_Test:testForkFuzz_CreateWithRange((uint128,(uint40,uint40,uint40),address,address,uint128)) (runs: 20, μ: 1223264, ~: 1038250) -USDT_MerkleStreamerLL_Fork_Test:testForkFuzz_MerkleStreamerLL((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 20, μ: 4359652, ~: 3909187) \ No newline at end of file +MerkleBuilder_Test:testFuzz_ComputeLeaves((uint256,address,uint128)[]) (runs: 20, μ: 328702, ~: 368679) +Precompiles_Test:test_DeployBatch() (gas: 2794084) +Precompiles_Test:test_DeployMerkleStreamerFactory() (gas: 3098051) +Precompiles_Test:test_DeployPeriphery() (gas: 5886439) +USDC_CreateWithMilestones_Batch_Fork_Test:testForkFuzz_CreateWithMilestones((uint128,address,address,uint128,uint40,(uint128,uint64,uint40)[])) (runs: 20, μ: 35758280, ~: 21718385) +USDC_CreateWithRange_Batch_Fork_Test:testForkFuzz_CreateWithRange((uint128,(uint40,uint40,uint40),address,address,uint128)) (runs: 20, μ: 1353143, ~: 1508161) +USDC_MerkleStreamerLL_Fork_Test:testForkFuzz_MerkleStreamerLL((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 20, μ: 4504409, ~: 3860802) +USDT_CreateWithMilestones_Batch_Fork_Test:testForkFuzz_CreateWithMilestones((uint128,address,address,uint128,uint40,(uint128,uint64,uint40)[])) (runs: 20, μ: 35758280, ~: 21718385) +USDT_CreateWithRange_Batch_Fork_Test:testForkFuzz_CreateWithRange((uint128,(uint40,uint40,uint40),address,address,uint128)) (runs: 20, μ: 1353143, ~: 1508161) +USDT_MerkleStreamerLL_Fork_Test:testForkFuzz_MerkleStreamerLL((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 20, μ: 4504409, ~: 3860802) \ No newline at end of file diff --git a/src/SablierV2Batch.sol b/src/SablierV2Batch.sol index c8569cc2..11108af9 100644 --- a/src/SablierV2Batch.sol +++ b/src/SablierV2Batch.sol @@ -40,10 +40,9 @@ contract SablierV2Batch is ISablierV2Batch { // transactions will revert if there is overflow. uint256 i; uint256 transferAmount; - for (i = 0; i < batchSize;) { + for (i = 0; i < batchSize; ++i) { unchecked { transferAmount += batch[i].totalAmount; - i += 1; } } @@ -52,7 +51,7 @@ contract SablierV2Batch is ISablierV2Batch { // Create a stream for each element in the parameter array. streamIds = new uint256[](batchSize); - for (i = 0; i < batchSize;) { + for (i = 0; i < batchSize; ++i) { // Create the stream. streamIds[i] = lockupLinear.createWithDurations( LockupLinear.CreateWithDurations({ @@ -66,11 +65,6 @@ contract SablierV2Batch is ISablierV2Batch { transferable: batch[i].transferable }) ); - - // Increment the for loop iterator. - unchecked { - i += 1; - } } } @@ -94,10 +88,9 @@ contract SablierV2Batch is ISablierV2Batch { // transactions will revert if there is overflow. uint256 i; uint256 transferAmount; - for (i = 0; i < batchSize;) { + for (i = 0; i < batchSize; ++i) { unchecked { transferAmount += batch[i].totalAmount; - i += 1; } } @@ -106,7 +99,7 @@ contract SablierV2Batch is ISablierV2Batch { // Create a stream for each element in the parameter array. streamIds = new uint256[](batchSize); - for (i = 0; i < batchSize;) { + for (i = 0; i < batchSize; ++i) { // Create the stream. streamIds[i] = lockupLinear.createWithRange( LockupLinear.CreateWithRange({ @@ -120,11 +113,6 @@ contract SablierV2Batch is ISablierV2Batch { transferable: batch[i].transferable }) ); - - // Increment the for loop iterator. - unchecked { - i += 1; - } } } @@ -152,10 +140,9 @@ contract SablierV2Batch is ISablierV2Batch { // transactions will revert if there is overflow. uint256 i; uint256 transferAmount; - for (i = 0; i < batchSize;) { + for (i = 0; i < batchSize; ++i) { unchecked { transferAmount += batch[i].totalAmount; - i += 1; } } @@ -164,7 +151,7 @@ contract SablierV2Batch is ISablierV2Batch { // Create a stream for each element in the parameter array. streamIds = new uint256[](batchSize); - for (i = 0; i < batchSize;) { + for (i = 0; i < batchSize; ++i) { // Create the stream. streamIds[i] = lockupDynamic.createWithDeltas( LockupDynamic.CreateWithDeltas({ @@ -178,11 +165,6 @@ contract SablierV2Batch is ISablierV2Batch { transferable: batch[i].transferable }) ); - - // Increment the for loop iterator. - unchecked { - i += 1; - } } } @@ -206,10 +188,9 @@ contract SablierV2Batch is ISablierV2Batch { // transactions will revert if there is overflow. uint256 i; uint256 transferAmount; - for (i = 0; i < batchSize;) { + for (i = 0; i < batchSize; ++i) { unchecked { transferAmount += batch[i].totalAmount; - i += 1; } } @@ -218,7 +199,7 @@ contract SablierV2Batch is ISablierV2Batch { // Create a stream for each element in the parameter array. streamIds = new uint256[](batchSize); - for (i = 0; i < batchSize;) { + for (i = 0; i < batchSize; ++i) { // Create the stream. streamIds[i] = lockupDynamic.createWithMilestones( LockupDynamic.CreateWithMilestones({ @@ -233,11 +214,6 @@ contract SablierV2Batch is ISablierV2Batch { transferable: batch[i].transferable }) ); - - // Increment the for loop iterator. - unchecked { - i += 1; - } } } diff --git a/test/utils/ArrayBuilder.sol b/test/utils/ArrayBuilder.sol index 4ca431a7..5e4622a2 100644 --- a/test/utils/ArrayBuilder.sol +++ b/test/utils/ArrayBuilder.sol @@ -13,8 +13,8 @@ library ArrayBuilder { returns (uint256[] memory streamIds) { streamIds = new uint256[](batchSize); - unchecked { - for (uint256 i = 0; i < batchSize; ++i) { + for (uint256 i = 0; i < batchSize; ++i) { + unchecked { streamIds[i] = firstStreamId + i; } } diff --git a/test/utils/BatchBuilder.sol b/test/utils/BatchBuilder.sol index 361a5f0a..973642ae 100644 --- a/test/utils/BatchBuilder.sol +++ b/test/utils/BatchBuilder.sol @@ -16,8 +16,8 @@ library BatchBuilder { returns (Batch.CreateWithDeltas[] memory batch) { batch = new Batch.CreateWithDeltas[](batchSize); - unchecked { - for (uint256 i = 0; i < batchSize; ++i) { + for (uint256 i = 0; i < batchSize; ++i) { + unchecked { batch[i] = batchSingle; } } @@ -55,8 +55,8 @@ library BatchBuilder { returns (Batch.CreateWithDurations[] memory batch) { batch = new Batch.CreateWithDurations[](batchSize); - unchecked { - for (uint256 i = 0; i < batchSize; ++i) { + for (uint256 i = 0; i < batchSize; ++i) { + unchecked { batch[i] = batchSingle; } } @@ -94,8 +94,8 @@ library BatchBuilder { returns (Batch.CreateWithMilestones[] memory batch) { batch = new Batch.CreateWithMilestones[](batchSize); - unchecked { - for (uint256 i = 0; i < batchSize; ++i) { + for (uint256 i = 0; i < batchSize; ++i) { + unchecked { batch[i] = batchSingle; } } @@ -134,8 +134,8 @@ library BatchBuilder { returns (Batch.CreateWithRange[] memory batch) { batch = new Batch.CreateWithRange[](batchSize); - unchecked { - for (uint256 i = 0; i < batchSize; ++i) { + for (uint256 i = 0; i < batchSize; ++i) { + unchecked { batch[i] = batchSingle; } } diff --git a/test/utils/Murky.sol b/test/utils/Murky.sol index 85324189..178a8263 100644 --- a/test/utils/Murky.sol +++ b/test/utils/Murky.sol @@ -27,8 +27,8 @@ abstract contract MurkyBase { // proof length must be less than max array size bytes32 rollingHash = valueToProve; uint256 length = proof.length; - unchecked { - for (uint256 i = 0; i < length; ++i) { + for (uint256 i = 0; i < length; ++i) { + unchecked { rollingHash = hashLeafPairs(rollingHash, proof[i]); } } From da9f857bdb3b2550cc9b98a78684efd151e30d02 Mon Sep 17 00:00:00 2001 From: andreivladbrg Date: Sat, 6 Jan 2024 15:08:48 +0200 Subject: [PATCH 02/61] test: remove unnecessary unchecked blocks test: undo changes in Murky.sol --- test/utils/ArrayBuilder.sol | 4 +--- test/utils/BatchBuilder.sol | 16 ++++------------ test/utils/Murky.sol | 4 ++-- 3 files changed, 7 insertions(+), 17 deletions(-) diff --git a/test/utils/ArrayBuilder.sol b/test/utils/ArrayBuilder.sol index 5e4622a2..a892009f 100644 --- a/test/utils/ArrayBuilder.sol +++ b/test/utils/ArrayBuilder.sol @@ -14,9 +14,7 @@ library ArrayBuilder { { streamIds = new uint256[](batchSize); for (uint256 i = 0; i < batchSize; ++i) { - unchecked { - streamIds[i] = firstStreamId + i; - } + streamIds[i] = firstStreamId + i; } } } diff --git a/test/utils/BatchBuilder.sol b/test/utils/BatchBuilder.sol index 973642ae..bb92c252 100644 --- a/test/utils/BatchBuilder.sol +++ b/test/utils/BatchBuilder.sol @@ -17,9 +17,7 @@ library BatchBuilder { { batch = new Batch.CreateWithDeltas[](batchSize); for (uint256 i = 0; i < batchSize; ++i) { - unchecked { - batch[i] = batchSingle; - } + batch[i] = batchSingle; } } @@ -56,9 +54,7 @@ library BatchBuilder { { batch = new Batch.CreateWithDurations[](batchSize); for (uint256 i = 0; i < batchSize; ++i) { - unchecked { - batch[i] = batchSingle; - } + batch[i] = batchSingle; } } @@ -95,9 +91,7 @@ library BatchBuilder { { batch = new Batch.CreateWithMilestones[](batchSize); for (uint256 i = 0; i < batchSize; ++i) { - unchecked { - batch[i] = batchSingle; - } + batch[i] = batchSingle; } } @@ -135,9 +129,7 @@ library BatchBuilder { { batch = new Batch.CreateWithRange[](batchSize); for (uint256 i = 0; i < batchSize; ++i) { - unchecked { - batch[i] = batchSingle; - } + batch[i] = batchSingle; } } diff --git a/test/utils/Murky.sol b/test/utils/Murky.sol index 178a8263..85324189 100644 --- a/test/utils/Murky.sol +++ b/test/utils/Murky.sol @@ -27,8 +27,8 @@ abstract contract MurkyBase { // proof length must be less than max array size bytes32 rollingHash = valueToProve; uint256 length = proof.length; - for (uint256 i = 0; i < length; ++i) { - unchecked { + unchecked { + for (uint256 i = 0; i < length; ++i) { rollingHash = hashLeafPairs(rollingHash, proof[i]); } } From 46c71432759148ab8967ba7389e3cc9210e8fc98 Mon Sep 17 00:00:00 2001 From: andreivladbrg Date: Sat, 6 Jan 2024 15:14:06 +0200 Subject: [PATCH 03/61] chore: use pragma solidity >=0.8.22 --- .github/workflows/multibuild.yml | 2 +- .solhint.json | 2 +- script/Base.s.sol | 2 +- script/CreateMerkleStreamerLL.s.sol | 2 +- script/DeployBatch.t.sol | 2 +- script/DeployDeterministicBatch.s.sol | 2 +- script/DeployDeterministicPeriphery.s.sol | 2 +- script/DeployMerkleStreamerFactory.s.sol | 2 +- script/DeployPeriphery.s.sol | 2 +- script/DeployProtocol.s.sol | 2 +- src/SablierV2Batch.sol | 2 +- src/SablierV2MerkleStreamerFactory.sol | 2 +- src/SablierV2MerkleStreamerLL.sol | 2 +- src/abstracts/SablierV2MerkleStreamer.sol | 2 +- src/interfaces/ISablierV2Batch.sol | 2 +- src/interfaces/ISablierV2MerkleStreamer.sol | 2 +- src/interfaces/ISablierV2MerkleStreamerFactory.sol | 2 +- src/interfaces/ISablierV2MerkleStreamerLL.sol | 2 +- src/libraries/Errors.sol | 2 +- src/types/DataTypes.sol | 2 +- test/Base.t.sol | 2 +- test/fork/Fork.t.sol | 2 +- test/fork/assets/USDC.t.sol | 2 +- test/fork/assets/USDT.t.sol | 2 +- test/fork/batch/createWithMilestones.t.sol | 2 +- test/fork/batch/createWithRange.t.sol | 2 +- test/fork/merkle-streamer/MerkleStreamerLL.t.sol | 2 +- test/integration/Integration.t.sol | 2 +- .../integration/batch/create-with-deltas/createWithDeltas.t.sol | 2 +- .../batch/create-with-durations/createWithDurations.t.sol | 2 +- .../batch/create-with-milestones/createWithMilestones.t.sol | 2 +- test/integration/batch/create-with-range/createWithRange.t.sol | 2 +- test/integration/merkle-streamer/MerkleStreamer.t.sol | 2 +- .../create-merkle-streamer-ll/createMerkleStreamerLL.t.sol | 2 +- test/integration/merkle-streamer/ll/claim/claim.t.sol | 2 +- test/integration/merkle-streamer/ll/clawback/clawback.t.sol | 2 +- .../merkle-streamer/ll/constructor/constructor.t.sol | 2 +- .../integration/merkle-streamer/ll/has-claimed/hasClaimed.t.sol | 2 +- .../integration/merkle-streamer/ll/has-expired/hasExpired.t.sol | 2 +- test/utils/ArrayBuilder.sol | 2 +- test/utils/BatchBuilder.sol | 2 +- test/utils/Defaults.sol | 2 +- test/utils/DeployOptimized.sol | 2 +- test/utils/Events.sol | 2 +- test/utils/MerkleBuilder.sol | 2 +- test/utils/MerkleBuilder.t.sol | 2 +- test/utils/Murky.sol | 2 +- test/utils/Precompiles.sol | 2 +- test/utils/Precompiles.t.sol | 2 +- test/utils/Types.sol | 2 +- 50 files changed, 50 insertions(+), 50 deletions(-) diff --git a/.github/workflows/multibuild.yml b/.github/workflows/multibuild.yml index f8490a37..e94a4c33 100644 --- a/.github/workflows/multibuild.yml +++ b/.github/workflows/multibuild.yml @@ -21,6 +21,6 @@ jobs: - name: "Check that V2 Periphery can be built with multiple Solidity versions" uses: "PaulRBerg/foundry-multibuild@v1" with: - min: "0.8.19" + min: "0.8.22" max: "0.8.23" skip-test: "true" diff --git a/.solhint.json b/.solhint.json index 35ba4419..c73668ef 100644 --- a/.solhint.json +++ b/.solhint.json @@ -3,7 +3,7 @@ "rules": { "avoid-low-level-calls": "off", "code-complexity": ["error", 8], - "compiler-version": ["error", ">=0.8.19"], + "compiler-version": ["error", ">=0.8.22"], "contract-name-camelcase": "off", "const-name-snakecase": "off", "custom-errors": "off", diff --git a/script/Base.s.sol b/script/Base.s.sol index 8e710a6a..4b5f36ee 100644 --- a/script/Base.s.sol +++ b/script/Base.s.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later // solhint-disable no-console -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; diff --git a/script/CreateMerkleStreamerLL.s.sol b/script/CreateMerkleStreamerLL.s.sol index 860807b4..fb8612c0 100644 --- a/script/CreateMerkleStreamerLL.s.sol +++ b/script/CreateMerkleStreamerLL.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; diff --git a/script/DeployBatch.t.sol b/script/DeployBatch.t.sol index 12d1c07f..bbf92056 100644 --- a/script/DeployBatch.t.sol +++ b/script/DeployBatch.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { BaseScript } from "./Base.s.sol"; diff --git a/script/DeployDeterministicBatch.s.sol b/script/DeployDeterministicBatch.s.sol index 8ea71b36..2f953528 100644 --- a/script/DeployDeterministicBatch.s.sol +++ b/script/DeployDeterministicBatch.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { BaseScript } from "./Base.s.sol"; diff --git a/script/DeployDeterministicPeriphery.s.sol b/script/DeployDeterministicPeriphery.s.sol index e58ce3cc..f5044c18 100644 --- a/script/DeployDeterministicPeriphery.s.sol +++ b/script/DeployDeterministicPeriphery.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { BaseScript } from "./Base.s.sol"; diff --git a/script/DeployMerkleStreamerFactory.s.sol b/script/DeployMerkleStreamerFactory.s.sol index 7fa01b27..ded39d4e 100644 --- a/script/DeployMerkleStreamerFactory.s.sol +++ b/script/DeployMerkleStreamerFactory.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { BaseScript } from "./Base.s.sol"; diff --git a/script/DeployPeriphery.s.sol b/script/DeployPeriphery.s.sol index 40390bc9..a771c41e 100644 --- a/script/DeployPeriphery.s.sol +++ b/script/DeployPeriphery.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { BaseScript } from "./Base.s.sol"; diff --git a/script/DeployProtocol.s.sol b/script/DeployProtocol.s.sol index c1c0ced4..52cbd3e6 100644 --- a/script/DeployProtocol.s.sol +++ b/script/DeployProtocol.s.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { SablierV2Comptroller } from "@sablier/v2-core/src/SablierV2Comptroller.sol"; import { SablierV2LockupDynamic } from "@sablier/v2-core/src/SablierV2LockupDynamic.sol"; diff --git a/src/SablierV2Batch.sol b/src/SablierV2Batch.sol index 11108af9..0d051510 100644 --- a/src/SablierV2Batch.sol +++ b/src/SablierV2Batch.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; diff --git a/src/SablierV2MerkleStreamerFactory.sol b/src/SablierV2MerkleStreamerFactory.sol index e9f14b1f..9efdd7a5 100644 --- a/src/SablierV2MerkleStreamerFactory.sol +++ b/src/SablierV2MerkleStreamerFactory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; diff --git a/src/SablierV2MerkleStreamerLL.sol b/src/SablierV2MerkleStreamerLL.sol index fbfd99e8..94207941 100644 --- a/src/SablierV2MerkleStreamerLL.sol +++ b/src/SablierV2MerkleStreamerLL.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; import { BitMaps } from "@openzeppelin/contracts/utils/structs/BitMaps.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/src/abstracts/SablierV2MerkleStreamer.sol b/src/abstracts/SablierV2MerkleStreamer.sol index 9591a6af..6b692355 100644 --- a/src/abstracts/SablierV2MerkleStreamer.sol +++ b/src/abstracts/SablierV2MerkleStreamer.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; diff --git a/src/interfaces/ISablierV2Batch.sol b/src/interfaces/ISablierV2Batch.sol index 1d24f273..f7d328a4 100644 --- a/src/interfaces/ISablierV2Batch.sol +++ b/src/interfaces/ISablierV2Batch.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2LockupDynamic } from "@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol"; diff --git a/src/interfaces/ISablierV2MerkleStreamer.sol b/src/interfaces/ISablierV2MerkleStreamer.sol index 42dc7a4d..6ab4f733 100644 --- a/src/interfaces/ISablierV2MerkleStreamer.sol +++ b/src/interfaces/ISablierV2MerkleStreamer.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IAdminable } from "@sablier/v2-core/src/interfaces/IAdminable.sol"; diff --git a/src/interfaces/ISablierV2MerkleStreamerFactory.sol b/src/interfaces/ISablierV2MerkleStreamerFactory.sol index 958ca0e8..504db123 100644 --- a/src/interfaces/ISablierV2MerkleStreamerFactory.sol +++ b/src/interfaces/ISablierV2MerkleStreamerFactory.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; diff --git a/src/interfaces/ISablierV2MerkleStreamerLL.sol b/src/interfaces/ISablierV2MerkleStreamerLL.sol index 794ad944..eb53b90a 100644 --- a/src/interfaces/ISablierV2MerkleStreamerLL.sol +++ b/src/interfaces/ISablierV2MerkleStreamerLL.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index 93308ed9..ede4400c 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; /// @title Errors /// @notice Library containing all custom errors the protocol may revert with. diff --git a/src/types/DataTypes.sol b/src/types/DataTypes.sol index 315886a4..a31972d5 100644 --- a/src/types/DataTypes.sol +++ b/src/types/DataTypes.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; import { ISablierV2Lockup } from "@sablier/v2-core/src/interfaces/ISablierV2Lockup.sol"; import { Broker, LockupDynamic, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; diff --git a/test/Base.t.sol b/test/Base.t.sol index 16d0ed2d..8a8b1045 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: UNLICENSED // solhint-disable max-states-count -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/test/fork/Fork.t.sol b/test/fork/Fork.t.sol index fb2b3b59..c1ab84e6 100644 --- a/test/fork/Fork.t.sol +++ b/test/fork/Fork.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2LockupDynamic } from "@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol"; diff --git a/test/fork/assets/USDC.t.sol b/test/fork/assets/USDC.t.sol index 6e038fe4..cecf1298 100644 --- a/test/fork/assets/USDC.t.sol +++ b/test/fork/assets/USDC.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/test/fork/assets/USDT.t.sol b/test/fork/assets/USDT.t.sol index 423168d0..ae8cd8a0 100644 --- a/test/fork/assets/USDT.t.sol +++ b/test/fork/assets/USDT.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/test/fork/batch/createWithMilestones.t.sol b/test/fork/batch/createWithMilestones.t.sol index d564ea8a..c717dbf4 100644 --- a/test/fork/batch/createWithMilestones.t.sol +++ b/test/fork/batch/createWithMilestones.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { LockupDynamic } from "@sablier/v2-core/src/types/DataTypes.sol"; diff --git a/test/fork/batch/createWithRange.t.sol b/test/fork/batch/createWithRange.t.sol index 13e825ff..61b46135 100644 --- a/test/fork/batch/createWithRange.t.sol +++ b/test/fork/batch/createWithRange.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; diff --git a/test/fork/merkle-streamer/MerkleStreamerLL.t.sol b/test/fork/merkle-streamer/MerkleStreamerLL.t.sol index 9b176183..7e436931 100644 --- a/test/fork/merkle-streamer/MerkleStreamerLL.t.sol +++ b/test/fork/merkle-streamer/MerkleStreamerLL.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { Arrays } from "@openzeppelin/contracts/utils/Arrays.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/test/integration/Integration.t.sol b/test/integration/Integration.t.sol index 7d3c0728..f5bc9cdb 100644 --- a/test/integration/Integration.t.sol +++ b/test/integration/Integration.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { Precompiles as V2CorePrecompiles } from "@sablier/v2-core/test/utils/Precompiles.sol"; diff --git a/test/integration/batch/create-with-deltas/createWithDeltas.t.sol b/test/integration/batch/create-with-deltas/createWithDeltas.t.sol index 11c51e4b..dac44686 100644 --- a/test/integration/batch/create-with-deltas/createWithDeltas.t.sol +++ b/test/integration/batch/create-with-deltas/createWithDeltas.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; import { Batch } from "src/types/DataTypes.sol"; diff --git a/test/integration/batch/create-with-durations/createWithDurations.t.sol b/test/integration/batch/create-with-durations/createWithDurations.t.sol index 2aea5448..cfbf667c 100644 --- a/test/integration/batch/create-with-durations/createWithDurations.t.sol +++ b/test/integration/batch/create-with-durations/createWithDurations.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; import { Batch } from "src/types/DataTypes.sol"; diff --git a/test/integration/batch/create-with-milestones/createWithMilestones.t.sol b/test/integration/batch/create-with-milestones/createWithMilestones.t.sol index ca5b7383..a3e9484b 100644 --- a/test/integration/batch/create-with-milestones/createWithMilestones.t.sol +++ b/test/integration/batch/create-with-milestones/createWithMilestones.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; import { Batch } from "src/types/DataTypes.sol"; diff --git a/test/integration/batch/create-with-range/createWithRange.t.sol b/test/integration/batch/create-with-range/createWithRange.t.sol index 4f248bba..4fac3995 100644 --- a/test/integration/batch/create-with-range/createWithRange.t.sol +++ b/test/integration/batch/create-with-range/createWithRange.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; import { Batch } from "src/types/DataTypes.sol"; diff --git a/test/integration/merkle-streamer/MerkleStreamer.t.sol b/test/integration/merkle-streamer/MerkleStreamer.t.sol index fd6eeaaa..3f244cef 100644 --- a/test/integration/merkle-streamer/MerkleStreamer.t.sol +++ b/test/integration/merkle-streamer/MerkleStreamer.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; import { ISablierV2MerkleStreamerLL } from "src/interfaces/ISablierV2MerkleStreamerLL.sol"; diff --git a/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.t.sol b/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.t.sol index 73d04060..0c17b93a 100644 --- a/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.t.sol +++ b/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; diff --git a/test/integration/merkle-streamer/ll/claim/claim.t.sol b/test/integration/merkle-streamer/ll/claim/claim.t.sol index a4742934..7da4d4a7 100644 --- a/test/integration/merkle-streamer/ll/claim/claim.t.sol +++ b/test/integration/merkle-streamer/ll/claim/claim.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { Lockup, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; import { ud } from "@prb/math/src/UD60x18.sol"; diff --git a/test/integration/merkle-streamer/ll/clawback/clawback.t.sol b/test/integration/merkle-streamer/ll/clawback/clawback.t.sol index 69b28afb..d5433173 100644 --- a/test/integration/merkle-streamer/ll/clawback/clawback.t.sol +++ b/test/integration/merkle-streamer/ll/clawback/clawback.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { Errors as V2CoreErrors } from "@sablier/v2-core/src/libraries/Errors.sol"; import { ud } from "@prb/math/src/UD60x18.sol"; diff --git a/test/integration/merkle-streamer/ll/constructor/constructor.t.sol b/test/integration/merkle-streamer/ll/constructor/constructor.t.sol index 676b9d18..0837554e 100644 --- a/test/integration/merkle-streamer/ll/constructor/constructor.t.sol +++ b/test/integration/merkle-streamer/ll/constructor/constructor.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; diff --git a/test/integration/merkle-streamer/ll/has-claimed/hasClaimed.t.sol b/test/integration/merkle-streamer/ll/has-claimed/hasClaimed.t.sol index a2b12ee8..6802efed 100644 --- a/test/integration/merkle-streamer/ll/has-claimed/hasClaimed.t.sol +++ b/test/integration/merkle-streamer/ll/has-claimed/hasClaimed.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { MerkleStreamer_Integration_Test } from "../../MerkleStreamer.t.sol"; diff --git a/test/integration/merkle-streamer/ll/has-expired/hasExpired.t.sol b/test/integration/merkle-streamer/ll/has-expired/hasExpired.t.sol index eca58123..6ce7b363 100644 --- a/test/integration/merkle-streamer/ll/has-expired/hasExpired.t.sol +++ b/test/integration/merkle-streamer/ll/has-expired/hasExpired.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { ISablierV2MerkleStreamerLL } from "src/interfaces/ISablierV2MerkleStreamerLL.sol"; diff --git a/test/utils/ArrayBuilder.sol b/test/utils/ArrayBuilder.sol index a892009f..c53ec64e 100644 --- a/test/utils/ArrayBuilder.sol +++ b/test/utils/ArrayBuilder.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; library ArrayBuilder { /// @notice Generates an ordered array of integers which starts at `firstStreamId` and ends at `firstStreamId + diff --git a/test/utils/BatchBuilder.sol b/test/utils/BatchBuilder.sol index bb92c252..4afada30 100644 --- a/test/utils/BatchBuilder.sol +++ b/test/utils/BatchBuilder.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; import { LockupDynamic, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index b468c84e..a5586f6b 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { Arrays } from "@openzeppelin/contracts/utils/Arrays.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/test/utils/DeployOptimized.sol b/test/utils/DeployOptimized.sol index f49840f8..46b0ae52 100644 --- a/test/utils/DeployOptimized.sol +++ b/test/utils/DeployOptimized.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { StdCheats } from "forge-std/src/StdCheats.sol"; diff --git a/test/utils/Events.sol b/test/utils/Events.sol index 1c6aed6a..5977a16c 100644 --- a/test/utils/Events.sol +++ b/test/utils/Events.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; diff --git a/test/utils/MerkleBuilder.sol b/test/utils/MerkleBuilder.sol index e0a454bd..ad6bb0c2 100644 --- a/test/utils/MerkleBuilder.sol +++ b/test/utils/MerkleBuilder.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later // solhint-disable reason-string -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; import { LibSort } from "solady/src/utils/LibSort.sol"; diff --git a/test/utils/MerkleBuilder.t.sol b/test/utils/MerkleBuilder.t.sol index 38963573..9a92b4ee 100644 --- a/test/utils/MerkleBuilder.t.sol +++ b/test/utils/MerkleBuilder.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; import { PRBTest } from "@prb/test/src/PRBTest.sol"; import { StdUtils } from "forge-std/src/StdUtils.sol"; diff --git a/test/utils/Murky.sol b/test/utils/Murky.sol index 85324189..df29f9a4 100644 --- a/test/utils/Murky.sol +++ b/test/utils/Murky.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // solhint-disable code-complexity,no-inline-assembly,reason-string -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; /// @dev Credits to https://github.com/dmfxyz/murky abstract contract MurkyBase { diff --git a/test/utils/Precompiles.sol b/test/utils/Precompiles.sol index f52c768e..c2775276 100644 --- a/test/utils/Precompiles.sol +++ b/test/utils/Precompiles.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later // solhint-disable max-line-length,no-inline-assembly,reason-string -pragma solidity >=0.8.19; +pragma solidity >=0.8.22; import { ISablierV2Batch } from "../../src/interfaces/ISablierV2Batch.sol"; import { ISablierV2MerkleStreamerFactory } from "../../src/interfaces/ISablierV2MerkleStreamerFactory.sol"; diff --git a/test/utils/Precompiles.t.sol b/test/utils/Precompiles.t.sol index ed23578e..00595365 100644 --- a/test/utils/Precompiles.t.sol +++ b/test/utils/Precompiles.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { LibString } from "solady/src/utils/LibString.sol"; diff --git a/test/utils/Types.sol b/test/utils/Types.sol index d722cd61..39fdda5a 100644 --- a/test/utils/Types.sol +++ b/test/utils/Types.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; struct Users { address alice; From 90e1f5983ea280460bbb947e61299f8e0e8e38a4 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Wed, 10 Jan 2024 18:35:53 +0530 Subject: [PATCH 04/61] refactor: remove protocol fee checks in MerkleStreamerLL --- src/SablierV2MerkleStreamerLL.sol | 2 +- src/abstracts/SablierV2MerkleStreamer.sol | 24 ++----------------- src/interfaces/ISablierV2MerkleStreamer.sol | 3 --- src/libraries/Errors.sol | 3 --- .../merkle-streamer/ll/claim/claim.t.sol | 17 +++++++------ .../merkle-streamer/ll/claim/claim.tree | 4 +++- .../ll/clawback/clawback.t.sol | 21 +++------------- .../merkle-streamer/ll/clawback/clawback.tree | 18 ++++---------- 8 files changed, 24 insertions(+), 68 deletions(-) diff --git a/src/SablierV2MerkleStreamerLL.sol b/src/SablierV2MerkleStreamerLL.sol index 94207941..1fa4861e 100644 --- a/src/SablierV2MerkleStreamerLL.sol +++ b/src/SablierV2MerkleStreamerLL.sol @@ -50,7 +50,7 @@ contract SablierV2MerkleStreamerLL is bool cancelable, bool transferable ) - SablierV2MerkleStreamer(initialAdmin, asset, lockupLinear, merkleRoot, expiration, cancelable, transferable) + SablierV2MerkleStreamer(initialAdmin, asset, merkleRoot, expiration, cancelable, transferable) { LOCKUP_LINEAR = lockupLinear; streamDurations = streamDurations_; diff --git a/src/abstracts/SablierV2MerkleStreamer.sol b/src/abstracts/SablierV2MerkleStreamer.sol index 6b692355..75b50101 100644 --- a/src/abstracts/SablierV2MerkleStreamer.sol +++ b/src/abstracts/SablierV2MerkleStreamer.sol @@ -6,8 +6,6 @@ import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.s import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; import { BitMaps } from "@openzeppelin/contracts/utils/structs/BitMaps.sol"; import { Adminable } from "@sablier/v2-core/src/abstracts/Adminable.sol"; -import { ISablierV2Lockup } from "@sablier/v2-core/src/interfaces/ISablierV2Lockup.sol"; -import { UD60x18, ud } from "@prb/math/src/UD60x18.sol"; import { ISablierV2MerkleStreamer } from "../interfaces/ISablierV2MerkleStreamer.sol"; import { Errors } from "../libraries/Errors.sol"; @@ -34,9 +32,6 @@ abstract contract SablierV2MerkleStreamer is /// @inheritdoc ISablierV2MerkleStreamer uint40 public immutable override EXPIRATION; - /// @inheritdoc ISablierV2MerkleStreamer - ISablierV2Lockup public immutable override LOCKUP; - /// @inheritdoc ISablierV2MerkleStreamer bytes32 public immutable override MERKLE_ROOT; @@ -58,7 +53,6 @@ abstract contract SablierV2MerkleStreamer is constructor( address initialAdmin, IERC20 asset, - ISablierV2Lockup lockup, bytes32 merkleRoot, uint40 expiration, bool cancelable, @@ -66,7 +60,6 @@ abstract contract SablierV2MerkleStreamer is ) { admin = initialAdmin; ASSET = asset; - LOCKUP = lockup; MERKLE_ROOT = merkleRoot; EXPIRATION = expiration; CANCELABLE = cancelable; @@ -93,12 +86,8 @@ abstract contract SablierV2MerkleStreamer is /// @inheritdoc ISablierV2MerkleStreamer function clawback(address to, uint128 amount) external override onlyAdmin { - // Safe Interactions: query the protocol fee. This is safe because it's a known Sablier contract that does - // not call other unknown contracts. - UD60x18 protocolFee = LOCKUP.comptroller().protocolFees(ASSET); - - // Checks: the campaign is not expired and the protocol fee is zero. - if (!hasExpired() && !protocolFee.gt(ud(0))) { + // Checks: the campaign is not expired. + if (!hasExpired()) { revert Errors.SablierV2MerkleStreamer_CampaignNotExpired({ currentTime: block.timestamp, expiration: EXPIRATION @@ -135,14 +124,5 @@ abstract contract SablierV2MerkleStreamer is if (!MerkleProof.verify(merkleProof, MERKLE_ROOT, leaf)) { revert Errors.SablierV2MerkleStreamer_InvalidProof(); } - - // Safe Interactions: query the protocol fee. This is safe because it's a known Sablier contract that does - // not call other unknown contracts. - UD60x18 protocolFee = LOCKUP.comptroller().protocolFees(ASSET); - - // Checks: the protocol fee is zero. - if (protocolFee.gt(ud(0))) { - revert Errors.SablierV2MerkleStreamer_ProtocolFeeNotZero(); - } } } diff --git a/src/interfaces/ISablierV2MerkleStreamer.sol b/src/interfaces/ISablierV2MerkleStreamer.sol index 6ab4f733..c233fb49 100644 --- a/src/interfaces/ISablierV2MerkleStreamer.sol +++ b/src/interfaces/ISablierV2MerkleStreamer.sol @@ -47,9 +47,6 @@ interface ISablierV2MerkleStreamer is IAdminable { /// @notice Returns a flag indicating whether the Merkle streamer has expired. function hasExpired() external view returns (bool); - /// @notice The address of the {SablierV2Lockup} contract. - function LOCKUP() external returns (ISablierV2Lockup); - /// @notice The root of the Merkle tree used to validate the claims. /// @dev This is an immutable state variable. function MERKLE_ROOT() external returns (bytes32); diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index ede4400c..382e716a 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -23,9 +23,6 @@ library Errors { /// @notice Thrown when trying to claim with an invalid Merkle proof. error SablierV2MerkleStreamer_InvalidProof(); - /// @notice Thrown when trying to claim when the protocol fee is not zero. - error SablierV2MerkleStreamer_ProtocolFeeNotZero(); - /// @notice Thrown when trying to claim the same stream more than once. error SablierV2MerkleStreamer_StreamClaimed(uint256 index); } diff --git a/test/integration/merkle-streamer/ll/claim/claim.t.sol b/test/integration/merkle-streamer/ll/claim/claim.t.sol index 7da4d4a7..d2404229 100644 --- a/test/integration/merkle-streamer/ll/claim/claim.t.sol +++ b/test/integration/merkle-streamer/ll/claim/claim.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.22 <0.9.0; import { Lockup, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { ud } from "@prb/math/src/UD60x18.sol"; +import { ud, UD60x18 } from "@prb/math/src/UD60x18.sol"; import { Errors } from "src/libraries/Errors.sol"; @@ -102,18 +102,16 @@ contract Claim_Integration_Test is MerkleStreamer_Integration_Test { _; } - function test_RevertGiven_ProtocolFeeNotZero() + function test_Claim_ProtocolFeeNotZero() external givenCampaignNotExpired givenNotClaimed givenIncludedInMerkleTree { - uint128 claimAmount = defaults.CLAIM_AMOUNT(); - bytes32[] memory merkleProof = defaults.index1Proof(); changePrank({ msgSender: users.admin }); comptroller.setProtocolFee({ asset: asset, newProtocolFee: ud(0.1e18) }); - vm.expectRevert(Errors.SablierV2MerkleStreamer_ProtocolFeeNotZero.selector); - merkleStreamerLL.claim({ index: 1, recipient: users.recipient1, amount: claimAmount, merkleProof: merkleProof }); + + test_Claim({ protocolFee: ud(0.1e18) }); } modifier givenProtocolFeeZero() { @@ -127,7 +125,12 @@ contract Claim_Integration_Test is MerkleStreamer_Integration_Test { givenIncludedInMerkleTree givenProtocolFeeZero { + test_Claim({ protocolFee: ud(0) }); + } + + function test_Claim(UD60x18 protocolFee) internal { uint256 expectedStreamId = lockupLinear.nextStreamId(); + uint128 feeAmount = uint128(ud(defaults.CLAIM_AMOUNT()).mul(protocolFee).intoUint256()); vm.expectEmit({ emitter: address(merkleStreamerLL) }); emit Claim(defaults.INDEX1(), users.recipient1, defaults.CLAIM_AMOUNT(), expectedStreamId); @@ -135,7 +138,7 @@ contract Claim_Integration_Test is MerkleStreamer_Integration_Test { LockupLinear.Stream memory actualStream = lockupLinear.getStream(actualStreamId); LockupLinear.Stream memory expectedStream = LockupLinear.Stream({ - amounts: Lockup.Amounts({ deposited: defaults.CLAIM_AMOUNT(), refunded: 0, withdrawn: 0 }), + amounts: Lockup.Amounts({ deposited: defaults.CLAIM_AMOUNT() - feeAmount, refunded: 0, withdrawn: 0 }), asset: asset, cliffTime: uint40(block.timestamp) + defaults.CLIFF_DURATION(), endTime: uint40(block.timestamp) + defaults.TOTAL_DURATION(), diff --git a/test/integration/merkle-streamer/ll/claim/claim.tree b/test/integration/merkle-streamer/ll/claim/claim.tree index d59287b0..00fd2f61 100644 --- a/test/integration/merkle-streamer/ll/claim/claim.tree +++ b/test/integration/merkle-streamer/ll/claim/claim.tree @@ -16,7 +16,9 @@ claim.t.sol │ └── it should revert └── given the claim is included in the Merkle tree ├── given the protocol fee is greater than zero - │ └── it should revert + │ ├── it should mark the index as claimed + │ ├── it should create a LockupLinear stream + │ └── it should emit a {Claim} event └── given the protocol fee is not greater than zero ├── it should mark the index as claimed ├── it should create a LockupLinear stream diff --git a/test/integration/merkle-streamer/ll/clawback/clawback.t.sol b/test/integration/merkle-streamer/ll/clawback/clawback.t.sol index d5433173..fdaea6e9 100644 --- a/test/integration/merkle-streamer/ll/clawback/clawback.t.sol +++ b/test/integration/merkle-streamer/ll/clawback/clawback.t.sol @@ -2,7 +2,6 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors as V2CoreErrors } from "@sablier/v2-core/src/libraries/Errors.sol"; -import { ud } from "@prb/math/src/UD60x18.sol"; import { Errors } from "src/libraries/Errors.sol"; @@ -24,11 +23,7 @@ contract Clawback_Integration_Test is MerkleStreamer_Integration_Test { _; } - modifier givenProtocolFeeZero() { - _; - } - - function test_RevertGiven_CampaignNotExpired() external whenCallerAdmin givenProtocolFeeZero { + function test_RevertGiven_CampaignNotExpired() external whenCallerAdmin { vm.expectRevert( abi.encodeWithSelector( Errors.SablierV2MerkleStreamer_CampaignNotExpired.selector, block.timestamp, defaults.EXPIRATION() @@ -44,21 +39,11 @@ contract Clawback_Integration_Test is MerkleStreamer_Integration_Test { _; } - function test_Clawback() external whenCallerAdmin givenProtocolFeeZero givenCampaignExpired { + function test_Clawback() external whenCallerAdmin givenCampaignExpired { test_Clawback(users.admin); } - modifier givenProtocolFeeNotZero() { - comptroller.setProtocolFee({ asset: asset, newProtocolFee: ud(0.03e18) }); - _; - } - - function testFuzz_Clawback_CampaignNotExpired(address to) external whenCallerAdmin givenProtocolFeeNotZero { - vm.assume(to != address(0)); - test_Clawback(to); - } - - function testFuzz_Clawback(address to) external whenCallerAdmin givenCampaignExpired givenProtocolFeeNotZero { + function testFuzz_Clawback(address to) external whenCallerAdmin givenCampaignExpired { vm.assume(to != address(0)); test_Clawback(to); } diff --git a/test/integration/merkle-streamer/ll/clawback/clawback.tree b/test/integration/merkle-streamer/ll/clawback/clawback.tree index 55f5c04e..feef74bc 100644 --- a/test/integration/merkle-streamer/ll/clawback/clawback.tree +++ b/test/integration/merkle-streamer/ll/clawback/clawback.tree @@ -2,16 +2,8 @@ clawback.t.sol ├── when the caller is not the admin │ └── it should revert └── when the caller is the admin - ├── given the protocol fee is not greater than zero - │ ├── given the campaign has not expired - │ │ └── it should revert - │ └── given the campaign has expired - │ ├── it should perform the ERC-20 transfer - │ └── it should emit a {Clawback} event - └── given the protocol fee is greater than zero - ├── given the campaign has not expired - │ ├── it should perform the ERC-20 transfer - │ └── it should emit a {Clawback} event - └── given the campaign has expired - ├── it should perform the ERC-20 transfer - └── it should emit a {Clawback} event + ├── given the campaign has not expired + │ └── it should revert + └── given the campaign has expired + ├── it should perform the ERC-20 transfer + └── it should emit a {Clawback} event From 042a48bd732d62d4f25fbcd1c1234b03c786bc18 Mon Sep 17 00:00:00 2001 From: andreivladbrg Date: Wed, 10 Jan 2024 18:00:36 +0200 Subject: [PATCH 05/61] chore: remove unused import docs: remove outdated natspec --- src/interfaces/ISablierV2MerkleStreamer.sol | 4 ---- src/interfaces/ISablierV2MerkleStreamerLL.sol | 1 - 2 files changed, 5 deletions(-) diff --git a/src/interfaces/ISablierV2MerkleStreamer.sol b/src/interfaces/ISablierV2MerkleStreamer.sol index c233fb49..94c5d7de 100644 --- a/src/interfaces/ISablierV2MerkleStreamer.sol +++ b/src/interfaces/ISablierV2MerkleStreamer.sol @@ -3,7 +3,6 @@ pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IAdminable } from "@sablier/v2-core/src/interfaces/IAdminable.sol"; -import { ISablierV2Lockup } from "@sablier/v2-core/src/interfaces/ISablierV2Lockup.sol"; /// @title ISablierV2MerkleStreamer /// @notice A contract that lets user claim Sablier streams using Merkle proofs. An interesting use case for @@ -63,9 +62,6 @@ interface ISablierV2MerkleStreamer is IAdminable { /// /// @dev Emits a {Clawback} event. /// - /// Notes: - /// - If the protocol is not zero, the expiration check is not made. - /// /// Requirements: /// - The caller must be the admin. /// - The campaign must either be expired or not have an expiration. diff --git a/src/interfaces/ISablierV2MerkleStreamerLL.sol b/src/interfaces/ISablierV2MerkleStreamerLL.sol index eb53b90a..d69cbd01 100644 --- a/src/interfaces/ISablierV2MerkleStreamerLL.sol +++ b/src/interfaces/ISablierV2MerkleStreamerLL.sol @@ -29,7 +29,6 @@ interface ISablierV2MerkleStreamerLL is ISablierV2MerkleStreamer { /// Requirements: /// - The campaign must not have expired. /// - The stream must not have been claimed already. - /// - The protocol fee must be zero. /// - The Merkle proof must be valid. /// /// @param index The index of the recipient in the Merkle tree. From cee309e8d20b037dc8a2493539c40318452fc64b Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Fri, 12 Jan 2024 18:39:17 +0530 Subject: [PATCH 06/61] refactor: consistency in calldata related struct parameters (#254) --- src/SablierV2Batch.sol | 42 +++++++++++----------- src/SablierV2MerkleStreamerLL.sol | 10 +++--- src/types/DataTypes.sol | 2 +- test/fork/batch/createWithMilestones.t.sol | 12 +++---- test/fork/batch/createWithRange.t.sol | 10 +++--- test/utils/BatchBuilder.sol | 42 +++++++++++----------- test/utils/Defaults.sol | 42 +++++++++++----------- 7 files changed, 80 insertions(+), 80 deletions(-) diff --git a/src/SablierV2Batch.sol b/src/SablierV2Batch.sol index 0d051510..d8666326 100644 --- a/src/SablierV2Batch.sol +++ b/src/SablierV2Batch.sol @@ -55,14 +55,14 @@ contract SablierV2Batch is ISablierV2Batch { // Create the stream. streamIds[i] = lockupLinear.createWithDurations( LockupLinear.CreateWithDurations({ + sender: batch[i].sender, + recipient: batch[i].recipient, + totalAmount: batch[i].totalAmount, asset: asset, - broker: batch[i].broker, cancelable: batch[i].cancelable, + transferable: batch[i].transferable, durations: batch[i].durations, - recipient: batch[i].recipient, - sender: batch[i].sender, - totalAmount: batch[i].totalAmount, - transferable: batch[i].transferable + broker: batch[i].broker }) ); } @@ -103,14 +103,14 @@ contract SablierV2Batch is ISablierV2Batch { // Create the stream. streamIds[i] = lockupLinear.createWithRange( LockupLinear.CreateWithRange({ + sender: batch[i].sender, + recipient: batch[i].recipient, + totalAmount: batch[i].totalAmount, asset: asset, - broker: batch[i].broker, cancelable: batch[i].cancelable, + transferable: batch[i].transferable, range: batch[i].range, - recipient: batch[i].recipient, - sender: batch[i].sender, - totalAmount: batch[i].totalAmount, - transferable: batch[i].transferable + broker: batch[i].broker }) ); } @@ -155,14 +155,14 @@ contract SablierV2Batch is ISablierV2Batch { // Create the stream. streamIds[i] = lockupDynamic.createWithDeltas( LockupDynamic.CreateWithDeltas({ + sender: batch[i].sender, + recipient: batch[i].recipient, + totalAmount: batch[i].totalAmount, asset: asset, - broker: batch[i].broker, cancelable: batch[i].cancelable, - recipient: batch[i].recipient, + transferable: batch[i].transferable, segments: batch[i].segments, - sender: batch[i].sender, - totalAmount: batch[i].totalAmount, - transferable: batch[i].transferable + broker: batch[i].broker }) ); } @@ -203,15 +203,15 @@ contract SablierV2Batch is ISablierV2Batch { // Create the stream. streamIds[i] = lockupDynamic.createWithMilestones( LockupDynamic.CreateWithMilestones({ + sender: batch[i].sender, + recipient: batch[i].recipient, + totalAmount: batch[i].totalAmount, asset: asset, - broker: batch[i].broker, cancelable: batch[i].cancelable, - recipient: batch[i].recipient, - segments: batch[i].segments, - sender: batch[i].sender, + transferable: batch[i].transferable, startTime: batch[i].startTime, - totalAmount: batch[i].totalAmount, - transferable: batch[i].transferable + segments: batch[i].segments, + broker: batch[i].broker }) ); } diff --git a/src/SablierV2MerkleStreamerLL.sol b/src/SablierV2MerkleStreamerLL.sol index 1fa4861e..a3a451c0 100644 --- a/src/SablierV2MerkleStreamerLL.sol +++ b/src/SablierV2MerkleStreamerLL.sol @@ -87,14 +87,14 @@ contract SablierV2MerkleStreamerLL is // Interactions: create the stream via {SablierV2LockupLinear}. streamId = LOCKUP_LINEAR.createWithDurations( LockupLinear.CreateWithDurations({ + sender: admin, + recipient: recipient, + totalAmount: amount, asset: ASSET, - broker: Broker({ account: address(0), fee: ud(0) }), cancelable: CANCELABLE, + transferable: TRANSFERABLE, durations: streamDurations, - recipient: recipient, - sender: admin, - totalAmount: amount, - transferable: TRANSFERABLE + broker: Broker({ account: address(0), fee: ud(0) }) }) ); diff --git a/src/types/DataTypes.sol b/src/types/DataTypes.sol index a31972d5..e4e3f6ea 100644 --- a/src/types/DataTypes.sol +++ b/src/types/DataTypes.sol @@ -40,9 +40,9 @@ library Batch { address sender; address recipient; uint128 totalAmount; - uint40 startTime; bool cancelable; bool transferable; + uint40 startTime; LockupDynamic.Segment[] segments; Broker broker; } diff --git a/test/fork/batch/createWithMilestones.t.sol b/test/fork/batch/createWithMilestones.t.sol index c717dbf4..1a61c842 100644 --- a/test/fork/batch/createWithMilestones.t.sol +++ b/test/fork/batch/createWithMilestones.t.sol @@ -53,15 +53,15 @@ abstract contract CreateWithMilestones_Batch_Fork_Test is Fork_Test { asset.approve({ spender: address(batch), amount: totalTransferAmount }); LockupDynamic.CreateWithMilestones memory createWithMilestones = LockupDynamic.CreateWithMilestones({ + sender: params.sender, + recipient: params.recipient, + totalAmount: params.perStreamAmount, asset: asset, - broker: defaults.broker(), cancelable: true, - recipient: params.recipient, - segments: params.segments, - sender: params.sender, + transferable: true, startTime: params.startTime, - totalAmount: params.perStreamAmount, - transferable: true + segments: params.segments, + broker: defaults.broker() }); Batch.CreateWithMilestones[] memory batchParams = BatchBuilder.fillBatch(createWithMilestones, params.batchSize); diff --git a/test/fork/batch/createWithRange.t.sol b/test/fork/batch/createWithRange.t.sol index 61b46135..cc3600fb 100644 --- a/test/fork/batch/createWithRange.t.sol +++ b/test/fork/batch/createWithRange.t.sol @@ -47,14 +47,14 @@ abstract contract CreateWithRange_Batch_Fork_Test is Fork_Test { asset.approve({ spender: address(batch), amount: totalTransferAmount }); LockupLinear.CreateWithRange memory createParams = LockupLinear.CreateWithRange({ + sender: params.sender, + recipient: params.recipient, + totalAmount: params.perStreamAmount, asset: asset, - broker: defaults.broker(), cancelable: true, - recipient: params.recipient, - sender: params.sender, + transferable: true, range: params.range, - totalAmount: params.perStreamAmount, - transferable: true + broker: defaults.broker() }); Batch.CreateWithRange[] memory batchParams = BatchBuilder.fillBatch(createParams, params.batchSize); diff --git a/test/utils/BatchBuilder.sol b/test/utils/BatchBuilder.sol index 4afada30..dfc59bf9 100644 --- a/test/utils/BatchBuilder.sol +++ b/test/utils/BatchBuilder.sol @@ -32,13 +32,13 @@ library BatchBuilder { { batch = new Batch.CreateWithDeltas[](batchSize); Batch.CreateWithDeltas memory batchSingle = Batch.CreateWithDeltas({ - broker: params.broker, - cancelable: params.cancelable, - recipient: params.recipient, - segments: params.segments, sender: params.sender, + recipient: params.recipient, totalAmount: params.totalAmount, - transferable: params.transferable + cancelable: params.cancelable, + transferable: params.transferable, + segments: params.segments, + broker: params.broker }); batch = fillBatch(batchSingle, batchSize); } @@ -69,13 +69,13 @@ library BatchBuilder { { batch = new Batch.CreateWithDurations[](batchSize); Batch.CreateWithDurations memory batchSingle = Batch.CreateWithDurations({ - broker: params.broker, - cancelable: params.cancelable, - durations: params.durations, - recipient: params.recipient, sender: params.sender, + recipient: params.recipient, totalAmount: params.totalAmount, - transferable: params.transferable + cancelable: params.cancelable, + transferable: params.transferable, + durations: params.durations, + broker: params.broker }); batch = fillBatch(batchSingle, batchSize); } @@ -106,14 +106,14 @@ library BatchBuilder { { batch = new Batch.CreateWithMilestones[](batchSize); Batch.CreateWithMilestones memory batchSingle = Batch.CreateWithMilestones({ - broker: params.broker, - cancelable: params.cancelable, - recipient: params.recipient, - segments: params.segments, sender: params.sender, - startTime: params.startTime, + recipient: params.recipient, totalAmount: params.totalAmount, - transferable: params.transferable + cancelable: params.cancelable, + transferable: params.transferable, + startTime: params.startTime, + segments: params.segments, + broker: params.broker }); batch = fillBatch(batchSingle, batchSize); } @@ -144,13 +144,13 @@ library BatchBuilder { { batch = new Batch.CreateWithRange[](batchSize); Batch.CreateWithRange memory batchSingle = Batch.CreateWithRange({ - broker: params.broker, - cancelable: params.cancelable, - range: params.range, - recipient: params.recipient, sender: params.sender, + recipient: params.recipient, totalAmount: params.totalAmount, - transferable: params.transferable + cancelable: params.cancelable, + transferable: params.transferable, + range: params.range, + broker: params.broker }); batch = fillBatch(batchSingle, batchSize); } diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index a5586f6b..7ab5e3a6 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -141,14 +141,14 @@ contract Defaults is Merkle { function createWithDeltas(IERC20 asset_) public view returns (LockupDynamic.CreateWithDeltas memory) { return LockupDynamic.CreateWithDeltas({ + sender: users.alice, + recipient: users.recipient0, + totalAmount: PER_STREAM_AMOUNT, asset: asset_, - broker: broker(), cancelable: true, - recipient: users.recipient0, + transferable: true, segments: segmentsWithDeltas(), - sender: users.alice, - totalAmount: PER_STREAM_AMOUNT, - transferable: true + broker: broker() }); } @@ -158,15 +158,15 @@ contract Defaults is Merkle { function createWithMilestones(IERC20 asset_) public view returns (LockupDynamic.CreateWithMilestones memory) { return LockupDynamic.CreateWithMilestones({ + sender: users.alice, + recipient: users.recipient0, + totalAmount: PER_STREAM_AMOUNT, asset: asset_, - broker: broker(), cancelable: true, - recipient: users.recipient0, - segments: segments(), - sender: users.alice, + transferable: true, startTime: START_TIME, - totalAmount: PER_STREAM_AMOUNT, - transferable: true + segments: segments(), + broker: broker() }); } @@ -220,14 +220,14 @@ contract Defaults is Merkle { function createWithDurations(IERC20 asset_) public view returns (LockupLinear.CreateWithDurations memory) { return LockupLinear.CreateWithDurations({ + sender: users.alice, + recipient: users.recipient0, + totalAmount: PER_STREAM_AMOUNT, asset: asset_, - broker: broker(), cancelable: true, + transferable: true, durations: durations(), - recipient: users.recipient0, - sender: users.alice, - totalAmount: PER_STREAM_AMOUNT, - transferable: true + broker: broker() }); } @@ -237,14 +237,14 @@ contract Defaults is Merkle { function createWithRange(IERC20 asset_) public view returns (LockupLinear.CreateWithRange memory) { return LockupLinear.CreateWithRange({ + sender: users.alice, + recipient: users.recipient0, + totalAmount: PER_STREAM_AMOUNT, asset: asset_, - broker: broker(), cancelable: true, + transferable: true, range: linearRange(), - recipient: users.recipient0, - sender: users.alice, - totalAmount: PER_STREAM_AMOUNT, - transferable: true + broker: broker() }); } From 315956504b8ceb96e47b010e36704d88acb94c98 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Fri, 26 Jan 2024 17:35:00 +0530 Subject: [PATCH 07/61] Rename create functions to match V2 core (#264) * refactor: rename create functions to match v2-core * build: install v2 core from staging branch * test: deploy v2-core dependencies for fork testing * test: update function name --------- Co-authored-by: andreivladbrg --- bun.lockb | Bin 43293 -> 43329 bytes package.json | 2 +- src/SablierV2Batch.sol | 28 ++--- src/interfaces/ISablierV2Batch.sol | 34 +++---- src/types/DataTypes.sol | 18 ++-- test/Base.t.sol | 51 +++++----- test/fork/Fork.t.sol | 11 +- test/fork/assets/USDC.t.sol | 12 ++- test/fork/assets/USDT.t.sol | 12 ++- ...nes.t.sol => createWithTimestampsLD.t.sol} | 19 ++-- ...nge.t.sol => createWithTimestampsLL.t.sol} | 16 +-- .../createWithDurations.t.sol} | 17 ++-- .../createWithDurations.tree | 0 .../createWithTimestamps.t.sol} | 16 +-- .../createWithTimestamps.tree} | 4 +- .../createWithDurations.t.sol | 15 +-- .../createWithDurations.tree} | 4 +- .../createWithTimestamps.t.sol} | 18 ++-- .../createWithTimestamps.tree} | 4 +- test/utils/BatchBuilder.sol | 62 +++++------ test/utils/Defaults.sol | 96 +++++++++--------- 21 files changed, 239 insertions(+), 200 deletions(-) rename test/fork/batch/{createWithMilestones.t.sol => createWithTimestampsLD.t.sol} (77%) rename test/fork/batch/{createWithRange.t.sol => createWithTimestampsLL.t.sol} (79%) rename test/integration/batch/{create-with-deltas/createWithDeltas.t.sol => lockup-dynamic/create-with-durations/createWithDurations.t.sol} (65%) rename test/integration/batch/{ => lockup-dynamic}/create-with-durations/createWithDurations.tree (100%) rename test/integration/batch/{create-with-milestones/createWithMilestones.t.sol => lockup-dynamic/create-with-timestamps/createWithTimestamps.t.sol} (68%) rename test/integration/batch/{create-with-milestones/createWithMilestones.tree => lockup-dynamic/create-with-timestamps/createWithTimestamps.tree} (64%) rename test/integration/batch/{ => lockup-linear}/create-with-durations/createWithDurations.t.sol (69%) rename test/integration/batch/{create-with-range/createWithRange.tree => lockup-linear/create-with-durations/createWithDurations.tree} (64%) rename test/integration/batch/{create-with-range/createWithRange.t.sol => lockup-linear/create-with-timestamps/createWithTimestamps.t.sol} (62%) rename test/integration/batch/{create-with-deltas/createWithDeltas.tree => lockup-linear/create-with-timestamps/createWithTimestamps.tree} (64%) diff --git a/bun.lockb b/bun.lockb index 47adad30ae5cb275f714732bb2d2d87ed4aac712..2ba27d49b45464e71bbc676d57ea0a703b1d0c64 100755 GIT binary patch delta 5724 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 AKL7v# delta 5721 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 diff --git a/package.json b/package.json index b7bad1e6..619373c5 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "dependencies": { "@openzeppelin/contracts": "4.9.2", "@prb/math": "4.0.2", - "@sablier/v2-core": "1.1.2" + "@sablier/v2-core": "github:sablier-labs/v2-core#staging" }, "devDependencies": { "@prb/test": "0.6.4", diff --git a/src/SablierV2Batch.sol b/src/SablierV2Batch.sol index d8666326..12ff58e6 100644 --- a/src/SablierV2Batch.sol +++ b/src/SablierV2Batch.sol @@ -21,10 +21,10 @@ contract SablierV2Batch is ISablierV2Batch { //////////////////////////////////////////////////////////////////////////*/ /// @inheritdoc ISablierV2Batch - function createWithDurations( + function createWithDurationsLL( ISablierV2LockupLinear lockupLinear, IERC20 asset, - Batch.CreateWithDurations[] calldata batch + Batch.CreateWithDurationsLL[] calldata batch ) external override @@ -69,10 +69,10 @@ contract SablierV2Batch is ISablierV2Batch { } /// @inheritdoc ISablierV2Batch - function createWithRange( + function createWithTimestampsLL( ISablierV2LockupLinear lockupLinear, IERC20 asset, - Batch.CreateWithRange[] calldata batch + Batch.CreateWithTimestampsLL[] calldata batch ) external override @@ -101,8 +101,8 @@ contract SablierV2Batch is ISablierV2Batch { streamIds = new uint256[](batchSize); for (i = 0; i < batchSize; ++i) { // Create the stream. - streamIds[i] = lockupLinear.createWithRange( - LockupLinear.CreateWithRange({ + streamIds[i] = lockupLinear.createWithTimestamps( + LockupLinear.CreateWithTimestamps({ sender: batch[i].sender, recipient: batch[i].recipient, totalAmount: batch[i].totalAmount, @@ -121,10 +121,10 @@ contract SablierV2Batch is ISablierV2Batch { //////////////////////////////////////////////////////////////////////////*/ /// @inheritdoc ISablierV2Batch - function createWithDeltas( + function createWithDurationsLD( ISablierV2LockupDynamic lockupDynamic, IERC20 asset, - Batch.CreateWithDeltas[] calldata batch + Batch.CreateWithDurationsLD[] calldata batch ) external override @@ -153,8 +153,8 @@ contract SablierV2Batch is ISablierV2Batch { streamIds = new uint256[](batchSize); for (i = 0; i < batchSize; ++i) { // Create the stream. - streamIds[i] = lockupDynamic.createWithDeltas( - LockupDynamic.CreateWithDeltas({ + streamIds[i] = lockupDynamic.createWithDurations( + LockupDynamic.CreateWithDurations({ sender: batch[i].sender, recipient: batch[i].recipient, totalAmount: batch[i].totalAmount, @@ -169,10 +169,10 @@ contract SablierV2Batch is ISablierV2Batch { } /// @inheritdoc ISablierV2Batch - function createWithMilestones( + function createWithTimestampsLD( ISablierV2LockupDynamic lockupDynamic, IERC20 asset, - Batch.CreateWithMilestones[] calldata batch + Batch.CreateWithTimestampsLD[] calldata batch ) external override @@ -201,8 +201,8 @@ contract SablierV2Batch is ISablierV2Batch { streamIds = new uint256[](batchSize); for (i = 0; i < batchSize; ++i) { // Create the stream. - streamIds[i] = lockupDynamic.createWithMilestones( - LockupDynamic.CreateWithMilestones({ + streamIds[i] = lockupDynamic.createWithTimestamps( + LockupDynamic.CreateWithTimestamps({ sender: batch[i].sender, recipient: batch[i].recipient, totalAmount: batch[i].totalAmount, diff --git a/src/interfaces/ISablierV2Batch.sol b/src/interfaces/ISablierV2Batch.sol index f7d328a4..7b3512a4 100644 --- a/src/interfaces/ISablierV2Batch.sol +++ b/src/interfaces/ISablierV2Batch.sol @@ -25,29 +25,29 @@ interface ISablierV2Batch { /// @param batch An array of structs, each encapsulating a subset of the parameters of /// {SablierV2LockupLinear.createWithDurations}. /// @return streamIds The ids of the newly created streams. - function createWithDurations( + function createWithDurationsLL( ISablierV2LockupLinear lockupLinear, IERC20 asset, - Batch.CreateWithDurations[] calldata batch + Batch.CreateWithDurationsLL[] calldata batch ) external returns (uint256[] memory streamIds); - /// @notice Creates a batch of Lockup Linear streams using `createWithRange`. + /// @notice Creates a batch of Lockup Linear streams using `createWithTimestamps`. /// /// @dev Requirements: /// - There must be at least one element in `batch`. - /// - All requirements from {ISablierV2LockupLinear.createWithRange} must be met for each stream. + /// - All requirements from {ISablierV2LockupLinear.createWithTimestamps} must be met for each stream. /// /// @param lockupLinear The address of the {SablierV2LockupLinear} contract. /// @param asset The contract address of the ERC-20 asset used for streaming. /// @param batch An array of structs, each encapsulating a subset of the parameters of - /// {SablierV2LockupLinear.createWithRange}. + /// {SablierV2LockupLinear.createWithTimestamps}. /// @return streamIds The ids of the newly created streams. - function createWithRange( + function createWithTimestampsLL( ISablierV2LockupLinear lockupLinear, IERC20 asset, - Batch.CreateWithRange[] calldata batch + Batch.CreateWithTimestampsLL[] calldata batch ) external returns (uint256[] memory streamIds); @@ -56,40 +56,40 @@ interface ISablierV2Batch { SABLIER-V2-LOCKUP-DYNAMIC //////////////////////////////////////////////////////////////////////////*/ - /// @notice Creates a batch of Lockup Dynamic streams using `createWithDeltas`. + /// @notice Creates a batch of Lockup Dynamic streams using `createWithDurations`. /// /// @dev Requirements: /// - There must be at least one element in `batch`. - /// - All requirements from {ISablierV2LockupDynamic.createWithDeltas} must be met for each stream. + /// - All requirements from {ISablierV2LockupDynamic.createWithDurations} must be met for each stream. /// /// @param lockupDynamic The address of the {SablierV2LockupDynamic} contract. /// @param asset The contract address of the ERC-20 asset used for streaming. /// @param batch An array of structs, each encapsulating a subset of the parameters of - /// {SablierV2LockupDynamic.createWithDeltas}. + /// {SablierV2LockupDynamic.createWithDurations}. /// @return streamIds The ids of the newly created streams. - function createWithDeltas( + function createWithDurationsLD( ISablierV2LockupDynamic lockupDynamic, IERC20 asset, - Batch.CreateWithDeltas[] calldata batch + Batch.CreateWithDurationsLD[] calldata batch ) external returns (uint256[] memory streamIds); - /// @notice Creates a batch of Lockup Dynamic streams using `createWithMilestones`. + /// @notice Creates a batch of Lockup Dynamic streams using `createWithTimestamps`. /// /// @dev Requirements: /// - There must be at least one element in `batch`. - /// - All requirements from {ISablierV2LockupDynamic.createWithMilestones} must be met for each stream. + /// - All requirements from {ISablierV2LockupDynamic.createWithTimestamps} must be met for each stream. /// /// @param lockupDynamic The address of the {SablierV2LockupDynamic} contract. /// @param asset The contract address of the ERC-20 asset used for streaming. /// @param batch An array of structs, each encapsulating a subset of the parameters of - /// {SablierV2LockupDynamic.createWithMilestones}. + /// {SablierV2LockupDynamic.createWithTimestamps}. /// @return streamIds The ids of the newly created streams. - function createWithMilestones( + function createWithTimestampsLD( ISablierV2LockupDynamic lockupDynamic, IERC20 asset, - Batch.CreateWithMilestones[] calldata batch + Batch.CreateWithTimestampsLD[] calldata batch ) external returns (uint256[] memory streamIds); diff --git a/src/types/DataTypes.sol b/src/types/DataTypes.sol index e4e3f6ea..ca2d63ec 100644 --- a/src/types/DataTypes.sol +++ b/src/types/DataTypes.sol @@ -11,20 +11,21 @@ library Batch { uint256[] streamIds; } - /// @notice A struct encapsulating all parameters of {SablierV2LockupDynamic.createWithDelta} except for the asset. - struct CreateWithDeltas { + /// @notice A struct encapsulating all parameters of {SablierV2LockupDynamic.createWithDurations} except for the + /// asset. + struct CreateWithDurationsLD { address sender; address recipient; uint128 totalAmount; bool cancelable; bool transferable; - LockupDynamic.SegmentWithDelta[] segments; + LockupDynamic.SegmentWithDuration[] segments; Broker broker; } /// @notice A struct encapsulating all parameters of {SablierV2LockupLinear.createWithDurations} except for the /// asset. - struct CreateWithDurations { + struct CreateWithDurationsLL { address sender; address recipient; uint128 totalAmount; @@ -34,9 +35,9 @@ library Batch { Broker broker; } - /// @notice A struct encapsulating all parameters of {SablierV2LockupDynamic.createWithMilestones} except for the + /// @notice A struct encapsulating all parameters of {SablierV2LockupDynamic.createWithTimestamps} except for the /// asset. - struct CreateWithMilestones { + struct CreateWithTimestampsLD { address sender; address recipient; uint128 totalAmount; @@ -47,8 +48,9 @@ library Batch { Broker broker; } - /// @notice A struct encapsulating all parameters of {SablierV2LockupLinear.createWithRange} except for the asset. - struct CreateWithRange { + /// @notice A struct encapsulating all parameters of {SablierV2LockupLinear.createWithTimestamps} except for the + /// asset. + struct CreateWithTimestampsLL { address sender; address recipient; uint128 totalAmount; diff --git a/test/Base.t.sol b/test/Base.t.sol index 8a8b1045..164b60c5 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -111,35 +111,35 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions CALL EXPECTS //////////////////////////////////////////////////////////////////////////*/ - /// @dev Expects a call to {ISablierV2LockupDynamic.createWithDeltas}. - function expectCallToCreateWithDeltas(LockupDynamic.CreateWithDeltas memory params) internal { + /// @dev Expects a call to {ISablierV2LockupDynamic.createWithDurations}. + function expectCallToCreateWithDurationsLD(LockupDynamic.CreateWithDurations memory params) internal { vm.expectCall({ callee: address(lockupDynamic), - data: abi.encodeCall(ISablierV2LockupDynamic.createWithDeltas, (params)) + data: abi.encodeCall(ISablierV2LockupDynamic.createWithDurations, (params)) }); } /// @dev Expects a call to {ISablierV2LockupLinear.createWithDurations}. - function expectCallToCreateWithDurations(LockupLinear.CreateWithDurations memory params) internal { + function expectCallToCreateWithDurationsLL(LockupLinear.CreateWithDurations memory params) internal { vm.expectCall({ callee: address(lockupLinear), data: abi.encodeCall(ISablierV2LockupLinear.createWithDurations, (params)) }); } - /// @dev Expects a call to {ISablierV2LockupDynamic.createWithMilestones}. - function expectCallToCreateWithMilestones(LockupDynamic.CreateWithMilestones memory params) internal { + /// @dev Expects a call to {ISablierV2LockupDynamic.createWithTimestamps}. + function expectCallToCreateWithTimestampsLD(LockupDynamic.CreateWithTimestamps memory params) internal { vm.expectCall({ callee: address(lockupDynamic), - data: abi.encodeCall(ISablierV2LockupDynamic.createWithMilestones, (params)) + data: abi.encodeCall(ISablierV2LockupDynamic.createWithTimestamps, (params)) }); } - /// @dev Expects a call to {ISablierV2LockupLinear.createWithRange}. - function expectCallToCreateWithRange(LockupLinear.CreateWithRange memory params) internal { + /// @dev Expects a call to {ISablierV2LockupLinear.createWithTimestamps}. + function expectCallToCreateWithTimestampsLL(LockupLinear.CreateWithTimestamps memory params) internal { vm.expectCall({ callee: address(lockupLinear), - data: abi.encodeCall(ISablierV2LockupLinear.createWithRange, (params)) + data: abi.encodeCall(ISablierV2LockupLinear.createWithTimestamps, (params)) }); } @@ -163,24 +163,24 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions vm.expectCall({ callee: asset_, data: abi.encodeCall(IERC20.transferFrom, (from, to, amount)) }); } - /// @dev Expects multiple calls to {ISablierV2LockupDynamic.createWithDeltas}, each with the specified + /// @dev Expects multiple calls to {ISablierV2LockupDynamic.createWithDurations}, each with the specified /// `params`. - function expectMultipleCallsToCreateWithDeltas( + function expectMultipleCallsToCreateWithDurationsLD( uint64 count, - LockupDynamic.CreateWithDeltas memory params + LockupDynamic.CreateWithDurations memory params ) internal { vm.expectCall({ callee: address(lockupDynamic), count: count, - data: abi.encodeCall(ISablierV2LockupDynamic.createWithDeltas, (params)) + data: abi.encodeCall(ISablierV2LockupDynamic.createWithDurations, (params)) }); } - /// @dev Expects multiple calls to {ISablierV2LockupDynamic.createWithDurations}, each with the specified + /// @dev Expects multiple calls to {ISablierV2LockupLinear.createWithDurations}, each with the specified /// `params`. - function expectMultipleCallsToCreateWithDurations( + function expectMultipleCallsToCreateWithDurationsLL( uint64 count, LockupLinear.CreateWithDurations memory params ) @@ -193,28 +193,33 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions }); } - /// @dev Expects multiple calls to {ISablierV2LockupDynamic.createWithMilestones}, each with the specified + /// @dev Expects multiple calls to {ISablierV2LockupDynamic.createWithTimestamps}, each with the specified /// `params`. - function expectMultipleCallsToCreateWithMilestones( + function expectMultipleCallsToCreateWithTimestampsLD( uint64 count, - LockupDynamic.CreateWithMilestones memory params + LockupDynamic.CreateWithTimestamps memory params ) internal { vm.expectCall({ callee: address(lockupDynamic), count: count, - data: abi.encodeCall(ISablierV2LockupDynamic.createWithMilestones, (params)) + data: abi.encodeCall(ISablierV2LockupDynamic.createWithTimestamps, (params)) }); } - /// @dev Expects multiple calls to {ISablierV2LockupDynamic.createWithRange}, each with the specified + /// @dev Expects multiple calls to {ISablierV2LockupLinear.createWithTimestamps}, each with the specified /// `params`. - function expectMultipleCallsToCreateWithRange(uint64 count, LockupLinear.CreateWithRange memory params) internal { + function expectMultipleCallsToCreateWithTimestampsLL( + uint64 count, + LockupLinear.CreateWithTimestamps memory params + ) + internal + { vm.expectCall({ callee: address(lockupLinear), count: count, - data: abi.encodeCall(ISablierV2LockupLinear.createWithRange, (params)) + data: abi.encodeCall(ISablierV2LockupLinear.createWithTimestamps, (params)) }); } diff --git a/test/fork/Fork.t.sol b/test/fork/Fork.t.sol index c1ab84e6..7d1ad224 100644 --- a/test/fork/Fork.t.sol +++ b/test/fork/Fork.t.sol @@ -4,6 +4,7 @@ pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2LockupDynamic } from "@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; +import { Precompiles as V2CorePrecompiles } from "@sablier/v2-core/test/utils/Precompiles.sol"; import { Fuzzers as V2CoreFuzzers } from "@sablier/v2-core/test/utils/Fuzzers.sol"; @@ -32,7 +33,9 @@ abstract contract Fork_Test is Base_Test, V2CoreFuzzers { Base_Test.setUp(); // Load the external dependencies. - loadDependencies(); + // loadDependencies(); + // TODO: Remove this line once the v2 core contracts are deployed on Mainnet. + deployDependencies(); // Deploy the defaults contract and allow it to access cheatcodes. defaults = new Defaults(users, asset); @@ -72,4 +75,10 @@ abstract contract Fork_Test is Base_Test, V2CoreFuzzers { lockupDynamic = ISablierV2LockupDynamic(0x7CC7e125d83A581ff438608490Cc0f7bDff79127); lockupLinear = ISablierV2LockupLinear(0xAFb979d9afAd1aD27C5eFf4E27226E3AB9e5dCC9); } + + /// @dev Deploys the v2 core dependencies. + // TODO: Remove this function once the v2 core contracts are deployed on Mainnet. + function deployDependencies() private { + (, lockupDynamic, lockupLinear,) = new V2CorePrecompiles().deployCore(users.admin); + } } diff --git a/test/fork/assets/USDC.t.sol b/test/fork/assets/USDC.t.sol index cecf1298..ef356d0c 100644 --- a/test/fork/assets/USDC.t.sol +++ b/test/fork/assets/USDC.t.sol @@ -3,14 +3,18 @@ pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { CreateWithMilestones_Batch_Fork_Test } from "../batch/createWithMilestones.t.sol"; -import { CreateWithRange_Batch_Fork_Test } from "../batch/createWithRange.t.sol"; +import { CreateWithTimestamps_LockupDynamic_Batch_Fork_Test } from "../batch/createWithTimestampsLD.t.sol"; +import { CreateWithTimestamps_LockupLinear_Batch_Fork_Test } from "../batch/createWithTimestampsLL.t.sol"; import { MerkleStreamerLL_Fork_Test } from "../merkle-streamer/MerkleStreamerLL.t.sol"; IERC20 constant usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); -contract USDC_CreateWithMilestones_Batch_Fork_Test is CreateWithMilestones_Batch_Fork_Test(usdc) { } +contract USDC_CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is + CreateWithTimestamps_LockupDynamic_Batch_Fork_Test(usdc) +{ } -contract USDC_CreateWithRange_Batch_Fork_Test is CreateWithRange_Batch_Fork_Test(usdc) { } +contract USDC_CreateWithTimestamps_LockupLinear_Batch_Fork_Test is + CreateWithTimestamps_LockupLinear_Batch_Fork_Test(usdc) +{ } contract USDC_MerkleStreamerLL_Fork_Test is MerkleStreamerLL_Fork_Test(usdc) { } diff --git a/test/fork/assets/USDT.t.sol b/test/fork/assets/USDT.t.sol index ae8cd8a0..4b03564a 100644 --- a/test/fork/assets/USDT.t.sol +++ b/test/fork/assets/USDT.t.sol @@ -3,14 +3,18 @@ pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { CreateWithMilestones_Batch_Fork_Test } from "../batch/createWithMilestones.t.sol"; -import { CreateWithRange_Batch_Fork_Test } from "../batch/createWithRange.t.sol"; +import { CreateWithTimestamps_LockupDynamic_Batch_Fork_Test } from "../batch/createWithTimestampsLD.t.sol"; +import { CreateWithTimestamps_LockupLinear_Batch_Fork_Test } from "../batch/createWithTimestampsLL.t.sol"; import { MerkleStreamerLL_Fork_Test } from "../merkle-streamer/MerkleStreamerLL.t.sol"; IERC20 constant usdt = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7); -contract USDT_CreateWithMilestones_Batch_Fork_Test is CreateWithMilestones_Batch_Fork_Test(usdt) { } +contract USDT_CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is + CreateWithTimestamps_LockupDynamic_Batch_Fork_Test(usdt) +{ } -contract USDT_CreateWithRange_Batch_Fork_Test is CreateWithRange_Batch_Fork_Test(usdt) { } +contract USDT_CreateWithTimestamps_LockupLinear_Batch_Fork_Test is + CreateWithTimestamps_LockupLinear_Batch_Fork_Test(usdt) +{ } contract USDT_MerkleStreamerLL_Fork_Test is MerkleStreamerLL_Fork_Test(usdt) { } diff --git a/test/fork/batch/createWithMilestones.t.sol b/test/fork/batch/createWithTimestampsLD.t.sol similarity index 77% rename from test/fork/batch/createWithMilestones.t.sol rename to test/fork/batch/createWithTimestampsLD.t.sol index 1a61c842..81cbe2c8 100644 --- a/test/fork/batch/createWithMilestones.t.sol +++ b/test/fork/batch/createWithTimestampsLD.t.sol @@ -11,7 +11,7 @@ import { ArrayBuilder } from "../../utils/ArrayBuilder.sol"; import { BatchBuilder } from "../../utils/BatchBuilder.sol"; /// @dev Runs against multiple fork assets. -abstract contract CreateWithMilestones_Batch_Fork_Test is Fork_Test { +abstract contract CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is Fork_Test { constructor(IERC20 asset_) Fork_Test(asset_) { } function setUp() public virtual override { @@ -19,10 +19,10 @@ abstract contract CreateWithMilestones_Batch_Fork_Test is Fork_Test { } /*////////////////////////////////////////////////////////////////////////// - BATCH-CREATE-WITH-MILESTONES + BATCH-CREATE-WITH-TIMESTAMPS //////////////////////////////////////////////////////////////////////////*/ - struct CreateWithMilestonesParams { + struct CreateWithTimestampsParams { uint128 batchSize; address sender; address recipient; @@ -31,11 +31,11 @@ abstract contract CreateWithMilestones_Batch_Fork_Test is Fork_Test { LockupDynamic.Segment[] segments; } - function testForkFuzz_CreateWithMilestones(CreateWithMilestonesParams memory params) external { + function testForkFuzz_CreateWithTimestamps(CreateWithTimestampsParams memory params) external { vm.assume(params.segments.length != 0); params.batchSize = boundUint128(params.batchSize, 1, 20); params.startTime = boundUint40(params.startTime, getBlockTimestamp(), getBlockTimestamp() + 24 hours); - fuzzSegmentMilestones(params.segments, params.startTime); + fuzzSegmentTimestamps(params.segments, params.startTime); (params.perStreamAmount,) = fuzzDynamicStreamAmounts({ upperBound: MAX_UINT128 / params.batchSize, segments: params.segments, @@ -52,7 +52,7 @@ abstract contract CreateWithMilestones_Batch_Fork_Test is Fork_Test { changePrank({ msgSender: params.sender }); asset.approve({ spender: address(batch), amount: totalTransferAmount }); - LockupDynamic.CreateWithMilestones memory createWithMilestones = LockupDynamic.CreateWithMilestones({ + LockupDynamic.CreateWithTimestamps memory createWithTimestamps = LockupDynamic.CreateWithTimestamps({ sender: params.sender, recipient: params.recipient, totalAmount: params.perStreamAmount, @@ -63,7 +63,8 @@ abstract contract CreateWithMilestones_Batch_Fork_Test is Fork_Test { segments: params.segments, broker: defaults.broker() }); - Batch.CreateWithMilestones[] memory batchParams = BatchBuilder.fillBatch(createWithMilestones, params.batchSize); + Batch.CreateWithTimestampsLD[] memory batchParams = + BatchBuilder.fillBatch(createWithTimestamps, params.batchSize); expectCallToTransferFrom({ asset_: address(asset), @@ -71,7 +72,7 @@ abstract contract CreateWithMilestones_Batch_Fork_Test is Fork_Test { to: address(batch), amount: totalTransferAmount }); - expectMultipleCallsToCreateWithMilestones({ count: uint64(params.batchSize), params: createWithMilestones }); + expectMultipleCallsToCreateWithTimestampsLD({ count: uint64(params.batchSize), params: createWithTimestamps }); expectMultipleCallsToTransferFrom({ asset_: address(asset), count: uint64(params.batchSize), @@ -80,7 +81,7 @@ abstract contract CreateWithMilestones_Batch_Fork_Test is Fork_Test { amount: params.perStreamAmount }); - uint256[] memory actualStreamIds = batch.createWithMilestones(lockupDynamic, asset, batchParams); + uint256[] memory actualStreamIds = batch.createWithTimestampsLD(lockupDynamic, asset, batchParams); uint256[] memory expectedStreamIds = ArrayBuilder.fillStreamIds(firstStreamId, params.batchSize); assertEq(actualStreamIds, expectedStreamIds); } diff --git a/test/fork/batch/createWithRange.t.sol b/test/fork/batch/createWithTimestampsLL.t.sol similarity index 79% rename from test/fork/batch/createWithRange.t.sol rename to test/fork/batch/createWithTimestampsLL.t.sol index cc3600fb..179b20e4 100644 --- a/test/fork/batch/createWithRange.t.sol +++ b/test/fork/batch/createWithTimestampsLL.t.sol @@ -11,7 +11,7 @@ import { ArrayBuilder } from "../../utils/ArrayBuilder.sol"; import { BatchBuilder } from "../../utils/BatchBuilder.sol"; /// @dev Runs against multiple fork assets. -abstract contract CreateWithRange_Batch_Fork_Test is Fork_Test { +abstract contract CreateWithTimestamps_LockupLinear_Batch_Fork_Test is Fork_Test { constructor(IERC20 asset_) Fork_Test(asset_) { } function setUp() public virtual override { @@ -19,10 +19,10 @@ abstract contract CreateWithRange_Batch_Fork_Test is Fork_Test { } /*////////////////////////////////////////////////////////////////////////// - BATCH-CREATE-WITH-RANGE + BATCH-CREATE-WITH-TIMESTAMPS //////////////////////////////////////////////////////////////////////////*/ - struct CreateWithRangeParams { + struct CreateWithTimestampsParams { uint128 batchSize; LockupLinear.Range range; address sender; @@ -30,7 +30,7 @@ abstract contract CreateWithRange_Batch_Fork_Test is Fork_Test { uint128 perStreamAmount; } - function testForkFuzz_CreateWithRange(CreateWithRangeParams memory params) external { + function testForkFuzz_CreateWithTimestamps(CreateWithTimestampsParams memory params) external { params.batchSize = boundUint128(params.batchSize, 1, 20); params.perStreamAmount = boundUint128(params.perStreamAmount, 1, MAX_UINT128 / params.batchSize); params.range.start = boundUint40(params.range.start, getBlockTimestamp(), getBlockTimestamp() + 24 hours); @@ -46,7 +46,7 @@ abstract contract CreateWithRange_Batch_Fork_Test is Fork_Test { changePrank({ msgSender: params.sender }); asset.approve({ spender: address(batch), amount: totalTransferAmount }); - LockupLinear.CreateWithRange memory createParams = LockupLinear.CreateWithRange({ + LockupLinear.CreateWithTimestamps memory createParams = LockupLinear.CreateWithTimestamps({ sender: params.sender, recipient: params.recipient, totalAmount: params.perStreamAmount, @@ -56,7 +56,7 @@ abstract contract CreateWithRange_Batch_Fork_Test is Fork_Test { range: params.range, broker: defaults.broker() }); - Batch.CreateWithRange[] memory batchParams = BatchBuilder.fillBatch(createParams, params.batchSize); + Batch.CreateWithTimestampsLL[] memory batchParams = BatchBuilder.fillBatch(createParams, params.batchSize); // Asset flow: sender → batch → Sablier expectCallToTransferFrom({ @@ -65,7 +65,7 @@ abstract contract CreateWithRange_Batch_Fork_Test is Fork_Test { to: address(batch), amount: totalTransferAmount }); - expectMultipleCallsToCreateWithRange({ count: uint64(params.batchSize), params: createParams }); + expectMultipleCallsToCreateWithTimestampsLL({ count: uint64(params.batchSize), params: createParams }); expectMultipleCallsToTransferFrom({ asset_: address(asset), count: uint64(params.batchSize), @@ -74,7 +74,7 @@ abstract contract CreateWithRange_Batch_Fork_Test is Fork_Test { amount: params.perStreamAmount }); - uint256[] memory actualStreamIds = batch.createWithRange(lockupLinear, asset, batchParams); + uint256[] memory actualStreamIds = batch.createWithTimestampsLL(lockupLinear, asset, batchParams); uint256[] memory expectedStreamIds = ArrayBuilder.fillStreamIds(firstStreamId, params.batchSize); assertEq(actualStreamIds, expectedStreamIds); } diff --git a/test/integration/batch/create-with-deltas/createWithDeltas.t.sol b/test/integration/batch/lockup-dynamic/create-with-durations/createWithDurations.t.sol similarity index 65% rename from test/integration/batch/create-with-deltas/createWithDeltas.t.sol rename to test/integration/batch/lockup-dynamic/create-with-durations/createWithDurations.t.sol index dac44686..1af6d955 100644 --- a/test/integration/batch/create-with-deltas/createWithDeltas.t.sol +++ b/test/integration/batch/lockup-dynamic/create-with-durations/createWithDurations.t.sol @@ -4,28 +4,31 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; import { Batch } from "src/types/DataTypes.sol"; -import { Integration_Test } from "../../Integration.t.sol"; +import { Integration_Test } from "../../../Integration.t.sol"; -contract CreateWithDeltas_Integration_Test is Integration_Test { +contract CreateWithDurations_LockupDynamic_Integration_Test is Integration_Test { function setUp() public virtual override { Integration_Test.setUp(); } function test_RevertWhen_BatchSizeZero() external { - Batch.CreateWithDeltas[] memory batchParams = new Batch.CreateWithDeltas[](0); + Batch.CreateWithDurationsLD[] memory batchParams = new Batch.CreateWithDurationsLD[](0); vm.expectRevert(Errors.SablierV2Batch_BatchSizeZero.selector); - batch.createWithDeltas(lockupDynamic, asset, batchParams); + batch.createWithDurationsLD(lockupDynamic, asset, batchParams); } modifier whenBatchSizeNotZero() { _; } - function test_BatchCreateWithDeltas() external whenBatchSizeNotZero { + function test_BatchCreateWithDurations() external whenBatchSizeNotZero { // Asset flow: Alice → batch → Sablier // Expect transfers from Alice to the batch, and then from the batch to the Sablier contract. expectCallToTransferFrom({ from: users.alice, to: address(batch), amount: defaults.TOTAL_TRANSFER_AMOUNT() }); - expectMultipleCallsToCreateWithDeltas({ count: defaults.BATCH_SIZE(), params: defaults.createWithDeltas() }); + expectMultipleCallsToCreateWithDurationsLD({ + count: defaults.BATCH_SIZE(), + params: defaults.createWithDurationsLD() + }); expectMultipleCallsToTransferFrom({ count: defaults.BATCH_SIZE(), from: address(batch), @@ -35,7 +38,7 @@ contract CreateWithDeltas_Integration_Test is Integration_Test { // Assert that the batch of streams has been created successfully. uint256[] memory actualStreamIds = - batch.createWithDeltas(lockupDynamic, asset, defaults.batchCreateWithDeltas()); + batch.createWithDurationsLD(lockupDynamic, asset, defaults.batchCreateWithDurationsLD()); uint256[] memory expectedStreamIds = defaults.incrementalStreamIds(); assertEq(actualStreamIds, expectedStreamIds, "stream ids mismatch"); } diff --git a/test/integration/batch/create-with-durations/createWithDurations.tree b/test/integration/batch/lockup-dynamic/create-with-durations/createWithDurations.tree similarity index 100% rename from test/integration/batch/create-with-durations/createWithDurations.tree rename to test/integration/batch/lockup-dynamic/create-with-durations/createWithDurations.tree diff --git a/test/integration/batch/create-with-milestones/createWithMilestones.t.sol b/test/integration/batch/lockup-dynamic/create-with-timestamps/createWithTimestamps.t.sol similarity index 68% rename from test/integration/batch/create-with-milestones/createWithMilestones.t.sol rename to test/integration/batch/lockup-dynamic/create-with-timestamps/createWithTimestamps.t.sol index a3e9484b..81500065 100644 --- a/test/integration/batch/create-with-milestones/createWithMilestones.t.sol +++ b/test/integration/batch/lockup-dynamic/create-with-timestamps/createWithTimestamps.t.sol @@ -4,30 +4,30 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; import { Batch } from "src/types/DataTypes.sol"; -import { Integration_Test } from "../../Integration.t.sol"; +import { Integration_Test } from "../../../Integration.t.sol"; -contract CreateWithMilestones_Integration_Test is Integration_Test { +contract CreateWithTimestamps_LockupDynamic_Integration_Test is Integration_Test { function setUp() public virtual override { Integration_Test.setUp(); } function test_RevertWhen_BatchSizeZero() external { - Batch.CreateWithMilestones[] memory batchParams = new Batch.CreateWithMilestones[](0); + Batch.CreateWithTimestampsLD[] memory batchParams = new Batch.CreateWithTimestampsLD[](0); vm.expectRevert(Errors.SablierV2Batch_BatchSizeZero.selector); - batch.createWithMilestones(lockupDynamic, asset, batchParams); + batch.createWithTimestampsLD(lockupDynamic, asset, batchParams); } modifier whenBatchSizeNotZero() { _; } - function test_BatchCreateWithMilestones() external whenBatchSizeNotZero { + function test_BatchCreateWithTimestamps() external whenBatchSizeNotZero { // Asset flow: Alice → batch → Sablier // Expect transfers from Alice to the batch, and then from the batch to the Sablier contract. expectCallToTransferFrom({ from: users.alice, to: address(batch), amount: defaults.TOTAL_TRANSFER_AMOUNT() }); - expectMultipleCallsToCreateWithMilestones({ + expectMultipleCallsToCreateWithTimestampsLD({ count: defaults.BATCH_SIZE(), - params: defaults.createWithMilestones() + params: defaults.createWithTimestampsLD() }); expectMultipleCallsToTransferFrom({ count: defaults.BATCH_SIZE(), @@ -38,7 +38,7 @@ contract CreateWithMilestones_Integration_Test is Integration_Test { // Assert that the batch of streams has been created successfully. uint256[] memory actualStreamIds = - batch.createWithMilestones(lockupDynamic, asset, defaults.batchCreateWithMilestones()); + batch.createWithTimestampsLD(lockupDynamic, asset, defaults.batchCreateWithTimestampsLD()); uint256[] memory expectedStreamIds = defaults.incrementalStreamIds(); assertEq(actualStreamIds, expectedStreamIds, "stream ids mismatch"); } diff --git a/test/integration/batch/create-with-milestones/createWithMilestones.tree b/test/integration/batch/lockup-dynamic/create-with-timestamps/createWithTimestamps.tree similarity index 64% rename from test/integration/batch/create-with-milestones/createWithMilestones.tree rename to test/integration/batch/lockup-dynamic/create-with-timestamps/createWithTimestamps.tree index 41e9e3f9..7769ecd1 100644 --- a/test/integration/batch/create-with-milestones/createWithMilestones.tree +++ b/test/integration/batch/lockup-dynamic/create-with-timestamps/createWithTimestamps.tree @@ -1,6 +1,6 @@ -createWithMilestones.t.sol +createWithTimestamps.t.sol ├── when the batch size is zero │ └── it should revert └── when the batch size is not zero - ├── it should create a batch of streams with milestones + ├── it should create a batch of streams with timestamps └── it should perform the ERC-20 transfers diff --git a/test/integration/batch/create-with-durations/createWithDurations.t.sol b/test/integration/batch/lockup-linear/create-with-durations/createWithDurations.t.sol similarity index 69% rename from test/integration/batch/create-with-durations/createWithDurations.t.sol rename to test/integration/batch/lockup-linear/create-with-durations/createWithDurations.t.sol index cfbf667c..f3f4523a 100644 --- a/test/integration/batch/create-with-durations/createWithDurations.t.sol +++ b/test/integration/batch/lockup-linear/create-with-durations/createWithDurations.t.sol @@ -4,17 +4,17 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; import { Batch } from "src/types/DataTypes.sol"; -import { Integration_Test } from "../../Integration.t.sol"; +import { Integration_Test } from "../../../Integration.t.sol"; -contract CreateWithDurations_Integration_Test is Integration_Test { +contract CreateWithDurations_LockupLinear_Integration_Test is Integration_Test { function setUp() public virtual override { Integration_Test.setUp(); } function test_RevertWhen_BatchSizeZero() external { - Batch.CreateWithDurations[] memory batchParams = new Batch.CreateWithDurations[](0); + Batch.CreateWithDurationsLL[] memory batchParams = new Batch.CreateWithDurationsLL[](0); vm.expectRevert(Errors.SablierV2Batch_BatchSizeZero.selector); - batch.createWithDurations(lockupLinear, asset, batchParams); + batch.createWithDurationsLL(lockupLinear, asset, batchParams); } modifier whenBatchSizeNotZero() { @@ -25,7 +25,10 @@ contract CreateWithDurations_Integration_Test is Integration_Test { // Asset flow: Alice → batch → Sablier // Expect transfers from Alice to the batch, and then from the batch to the Sablier contract. expectCallToTransferFrom({ from: users.alice, to: address(batch), amount: defaults.TOTAL_TRANSFER_AMOUNT() }); - expectMultipleCallsToCreateWithDurations({ count: defaults.BATCH_SIZE(), params: defaults.createWithDurations() }); + expectMultipleCallsToCreateWithDurationsLL({ + count: defaults.BATCH_SIZE(), + params: defaults.createWithDurationsLL() + }); expectMultipleCallsToTransferFrom({ count: defaults.BATCH_SIZE(), from: address(batch), @@ -35,7 +38,7 @@ contract CreateWithDurations_Integration_Test is Integration_Test { // Assert that the batch of streams has been created successfully. uint256[] memory actualStreamIds = - batch.createWithDurations(lockupLinear, asset, defaults.batchCreateWithDurations()); + batch.createWithDurationsLL(lockupLinear, asset, defaults.batchCreateWithDurationsLL()); uint256[] memory expectedStreamIds = defaults.incrementalStreamIds(); assertEq(actualStreamIds, expectedStreamIds, "stream ids mismatch"); } diff --git a/test/integration/batch/create-with-range/createWithRange.tree b/test/integration/batch/lockup-linear/create-with-durations/createWithDurations.tree similarity index 64% rename from test/integration/batch/create-with-range/createWithRange.tree rename to test/integration/batch/lockup-linear/create-with-durations/createWithDurations.tree index 886e5ff0..377110d8 100644 --- a/test/integration/batch/create-with-range/createWithRange.tree +++ b/test/integration/batch/lockup-linear/create-with-durations/createWithDurations.tree @@ -1,6 +1,6 @@ -createWithRange.t.sol +createWithDurations.t.sol ├── when the batch size is zero │ └── it should revert └── when the batch size is not zero - ├── it should create a batch of streams with range + ├── it should create a batch of streams with durations └── it should perform the ERC-20 transfers diff --git a/test/integration/batch/create-with-range/createWithRange.t.sol b/test/integration/batch/lockup-linear/create-with-timestamps/createWithTimestamps.t.sol similarity index 62% rename from test/integration/batch/create-with-range/createWithRange.t.sol rename to test/integration/batch/lockup-linear/create-with-timestamps/createWithTimestamps.t.sol index 4fac3995..66639b2f 100644 --- a/test/integration/batch/create-with-range/createWithRange.t.sol +++ b/test/integration/batch/lockup-linear/create-with-timestamps/createWithTimestamps.t.sol @@ -4,28 +4,31 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; import { Batch } from "src/types/DataTypes.sol"; -import { Integration_Test } from "../../Integration.t.sol"; +import { Integration_Test } from "../../../Integration.t.sol"; -contract CreateWithRange_Integration_Test is Integration_Test { +contract CreateWithTimestamps_LockupLinear_Integration_Test is Integration_Test { function setUp() public virtual override { Integration_Test.setUp(); } function test_RevertWhen_BatchSizeZero() external { - Batch.CreateWithRange[] memory batchParams = new Batch.CreateWithRange[](0); + Batch.CreateWithTimestampsLL[] memory batchParams = new Batch.CreateWithTimestampsLL[](0); vm.expectRevert(Errors.SablierV2Batch_BatchSizeZero.selector); - batch.createWithRange(lockupLinear, asset, batchParams); + batch.createWithTimestampsLL(lockupLinear, asset, batchParams); } modifier whenBatchSizeNotZero() { _; } - function test_CreateWithRange() external whenBatchSizeNotZero { + function test_BatchCreateWithTimestamps() external whenBatchSizeNotZero { // Asset flow: Alice → batch → Sablier // Expect transfers from Alice to the batch, and then from the batch to the Sablier contract. expectCallToTransferFrom({ from: users.alice, to: address(batch), amount: defaults.TOTAL_TRANSFER_AMOUNT() }); - expectMultipleCallsToCreateWithRange({ count: defaults.BATCH_SIZE(), params: defaults.createWithRange() }); + expectMultipleCallsToCreateWithTimestampsLL({ + count: defaults.BATCH_SIZE(), + params: defaults.createWithTimestampsLL() + }); expectMultipleCallsToTransferFrom({ count: defaults.BATCH_SIZE(), from: address(batch), @@ -34,7 +37,8 @@ contract CreateWithRange_Integration_Test is Integration_Test { }); // Assert that the batch of streams has been created successfully. - uint256[] memory actualStreamIds = batch.createWithRange(lockupLinear, asset, defaults.batchCreateWithRange()); + uint256[] memory actualStreamIds = + batch.createWithTimestampsLL(lockupLinear, asset, defaults.batchCreateWithTimestampsLL()); uint256[] memory expectedStreamIds = defaults.incrementalStreamIds(); assertEq(actualStreamIds, expectedStreamIds, "stream ids mismatch"); } diff --git a/test/integration/batch/create-with-deltas/createWithDeltas.tree b/test/integration/batch/lockup-linear/create-with-timestamps/createWithTimestamps.tree similarity index 64% rename from test/integration/batch/create-with-deltas/createWithDeltas.tree rename to test/integration/batch/lockup-linear/create-with-timestamps/createWithTimestamps.tree index 37e9b52f..7769ecd1 100644 --- a/test/integration/batch/create-with-deltas/createWithDeltas.tree +++ b/test/integration/batch/lockup-linear/create-with-timestamps/createWithTimestamps.tree @@ -1,6 +1,6 @@ -createWithDeltas.t.sol +createWithTimestamps.t.sol ├── when the batch size is zero │ └── it should revert └── when the batch size is not zero - ├── it should create a batch of streams with deltas + ├── it should create a batch of streams with timestamps └── it should perform the ERC-20 transfers diff --git a/test/utils/BatchBuilder.sol b/test/utils/BatchBuilder.sol index dfc59bf9..d52e3dff 100644 --- a/test/utils/BatchBuilder.sol +++ b/test/utils/BatchBuilder.sol @@ -8,30 +8,30 @@ import { Batch } from "../../src/types/DataTypes.sol"; library BatchBuilder { /// @notice Generates an array containing `batchSize` copies of `batchSingle`. function fillBatch( - Batch.CreateWithDeltas memory batchSingle, + Batch.CreateWithDurationsLD memory batchSingle, uint256 batchSize ) internal pure - returns (Batch.CreateWithDeltas[] memory batch) + returns (Batch.CreateWithDurationsLD[] memory batch) { - batch = new Batch.CreateWithDeltas[](batchSize); + batch = new Batch.CreateWithDurationsLD[](batchSize); for (uint256 i = 0; i < batchSize; ++i) { batch[i] = batchSingle; } } - /// @notice Turns the `params` into an array of `Batch.CreateWithDeltas` structs. + /// @notice Turns the `params` into an array of `Batch.CreateWithDurationsLD` structs. function fillBatch( - LockupDynamic.CreateWithDeltas memory params, + LockupDynamic.CreateWithDurations memory params, uint256 batchSize ) internal pure - returns (Batch.CreateWithDeltas[] memory batch) + returns (Batch.CreateWithDurationsLD[] memory batch) { - batch = new Batch.CreateWithDeltas[](batchSize); - Batch.CreateWithDeltas memory batchSingle = Batch.CreateWithDeltas({ + batch = new Batch.CreateWithDurationsLD[](batchSize); + Batch.CreateWithDurationsLD memory batchSingle = Batch.CreateWithDurationsLD({ sender: params.sender, recipient: params.recipient, totalAmount: params.totalAmount, @@ -45,30 +45,30 @@ library BatchBuilder { /// @notice Generates an array containing `batchSize` copies of `batchSingle`. function fillBatch( - Batch.CreateWithDurations memory batchSingle, + Batch.CreateWithDurationsLL memory batchSingle, uint256 batchSize ) internal pure - returns (Batch.CreateWithDurations[] memory batch) + returns (Batch.CreateWithDurationsLL[] memory batch) { - batch = new Batch.CreateWithDurations[](batchSize); + batch = new Batch.CreateWithDurationsLL[](batchSize); for (uint256 i = 0; i < batchSize; ++i) { batch[i] = batchSingle; } } - /// @notice Turns the `params` into an array of `Batch.CreateWithDurations` structs. + /// @notice Turns the `params` into an array of `Batch.CreateWithDurationsLL` structs. function fillBatch( LockupLinear.CreateWithDurations memory params, uint256 batchSize ) internal pure - returns (Batch.CreateWithDurations[] memory batch) + returns (Batch.CreateWithDurationsLL[] memory batch) { - batch = new Batch.CreateWithDurations[](batchSize); - Batch.CreateWithDurations memory batchSingle = Batch.CreateWithDurations({ + batch = new Batch.CreateWithDurationsLL[](batchSize); + Batch.CreateWithDurationsLL memory batchSingle = Batch.CreateWithDurationsLL({ sender: params.sender, recipient: params.recipient, totalAmount: params.totalAmount, @@ -82,30 +82,30 @@ library BatchBuilder { /// @notice Generates an array containing `batchSize` copies of `batchSingle`. function fillBatch( - Batch.CreateWithMilestones memory batchSingle, + Batch.CreateWithTimestampsLD memory batchSingle, uint256 batchSize ) internal pure - returns (Batch.CreateWithMilestones[] memory batch) + returns (Batch.CreateWithTimestampsLD[] memory batch) { - batch = new Batch.CreateWithMilestones[](batchSize); + batch = new Batch.CreateWithTimestampsLD[](batchSize); for (uint256 i = 0; i < batchSize; ++i) { batch[i] = batchSingle; } } - /// @notice Turns the `params` into an array of `Batch.CreateWithMilestones` structs. + /// @notice Turns the `params` into an array of `Batch.CreateWithTimestampsLDs` structs. function fillBatch( - LockupDynamic.CreateWithMilestones memory params, + LockupDynamic.CreateWithTimestamps memory params, uint256 batchSize ) internal pure - returns (Batch.CreateWithMilestones[] memory batch) + returns (Batch.CreateWithTimestampsLD[] memory batch) { - batch = new Batch.CreateWithMilestones[](batchSize); - Batch.CreateWithMilestones memory batchSingle = Batch.CreateWithMilestones({ + batch = new Batch.CreateWithTimestampsLD[](batchSize); + Batch.CreateWithTimestampsLD memory batchSingle = Batch.CreateWithTimestampsLD({ sender: params.sender, recipient: params.recipient, totalAmount: params.totalAmount, @@ -120,30 +120,30 @@ library BatchBuilder { /// @notice Generates an array containing `batchSize` copies of `batchSingle`. function fillBatch( - Batch.CreateWithRange memory batchSingle, + Batch.CreateWithTimestampsLL memory batchSingle, uint256 batchSize ) internal pure - returns (Batch.CreateWithRange[] memory batch) + returns (Batch.CreateWithTimestampsLL[] memory batch) { - batch = new Batch.CreateWithRange[](batchSize); + batch = new Batch.CreateWithTimestampsLL[](batchSize); for (uint256 i = 0; i < batchSize; ++i) { batch[i] = batchSingle; } } - /// @notice Turns the `params` into an array of `Batch.CreateWithRange` structs. + /// @notice Turns the `params` into an array of `Batch.CreateWithTimestampsLL` structs. function fillBatch( - LockupLinear.CreateWithRange memory params, + LockupLinear.CreateWithTimestamps memory params, uint256 batchSize ) internal pure - returns (Batch.CreateWithRange[] memory batch) + returns (Batch.CreateWithTimestampsLL[] memory batch) { - batch = new Batch.CreateWithRange[](batchSize); - Batch.CreateWithRange memory batchSingle = Batch.CreateWithRange({ + batch = new Batch.CreateWithTimestampsLL[](batchSize); + Batch.CreateWithTimestampsLL memory batchSingle = Batch.CreateWithTimestampsLL({ sender: params.sender, recipient: params.recipient, totalAmount: params.totalAmount, diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index 7ab5e3a6..66297b86 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -135,29 +135,29 @@ contract Defaults is Merkle { SABLIER-V2-LOCKUP-DYNAMIC //////////////////////////////////////////////////////////////////////////*/ - function createWithDeltas() public view returns (LockupDynamic.CreateWithDeltas memory) { - return createWithDeltas(asset); + function createWithDurationsLD() public view returns (LockupDynamic.CreateWithDurations memory) { + return createWithDurationsLD(asset); } - function createWithDeltas(IERC20 asset_) public view returns (LockupDynamic.CreateWithDeltas memory) { - return LockupDynamic.CreateWithDeltas({ + function createWithDurationsLD(IERC20 asset_) public view returns (LockupDynamic.CreateWithDurations memory) { + return LockupDynamic.CreateWithDurations({ sender: users.alice, recipient: users.recipient0, totalAmount: PER_STREAM_AMOUNT, asset: asset_, cancelable: true, transferable: true, - segments: segmentsWithDeltas(), + segments: segmentsWithDurations(), broker: broker() }); } - function createWithMilestones() public view returns (LockupDynamic.CreateWithMilestones memory) { - return createWithMilestones(asset); + function createWithTimestampsLD() public view returns (LockupDynamic.CreateWithTimestamps memory) { + return createWithTimestampsLD(asset); } - function createWithMilestones(IERC20 asset_) public view returns (LockupDynamic.CreateWithMilestones memory) { - return LockupDynamic.CreateWithMilestones({ + function createWithTimestampsLD(IERC20 asset_) public view returns (LockupDynamic.CreateWithTimestamps memory) { + return LockupDynamic.CreateWithTimestamps({ sender: users.alice, recipient: users.recipient0, totalAmount: PER_STREAM_AMOUNT, @@ -180,45 +180,45 @@ contract Defaults is Merkle { segments_[0] = LockupDynamic.Segment({ amount: 2500e18, exponent: ud2x18(3.14e18), - milestone: START_TIME + CLIFF_DURATION + timestamp: START_TIME + CLIFF_DURATION }); segments_[1] = LockupDynamic.Segment({ amount: 7500e18, exponent: ud2x18(3.14e18), - milestone: START_TIME + TOTAL_DURATION + timestamp: START_TIME + TOTAL_DURATION }); } - /// @dev Returns a batch of `LockupDynamic.SegmentWithDelta` parameters. - function segmentsWithDeltas() public pure returns (LockupDynamic.SegmentWithDelta[] memory) { - return segmentsWithDeltas({ amount0: 2500e18, amount1: 7500e18 }); + /// @dev Returns a batch of `LockupDynamic.SegmentWithDuration` parameters. + function segmentsWithDurations() public pure returns (LockupDynamic.SegmentWithDuration[] memory) { + return segmentsWithDurations({ amount0: 2500e18, amount1: 7500e18 }); } - /// @dev Returns a batch of `LockupDynamic.SegmentWithDelta` parameters. - function segmentsWithDeltas( + /// @dev Returns a batch of `LockupDynamic.SegmentWithDuration` parameters. + function segmentsWithDurations( uint128 amount0, uint128 amount1 ) public pure - returns (LockupDynamic.SegmentWithDelta[] memory segments_) + returns (LockupDynamic.SegmentWithDuration[] memory segments_) { - segments_ = new LockupDynamic.SegmentWithDelta[](2); + segments_ = new LockupDynamic.SegmentWithDuration[](2); segments_[0] = - LockupDynamic.SegmentWithDelta({ amount: amount0, exponent: ud2x18(3.14e18), delta: 2500 seconds }); + LockupDynamic.SegmentWithDuration({ amount: amount0, exponent: ud2x18(3.14e18), duration: 2500 seconds }); segments_[1] = - LockupDynamic.SegmentWithDelta({ amount: amount1, exponent: ud2x18(3.14e18), delta: 7500 seconds }); + LockupDynamic.SegmentWithDuration({ amount: amount1, exponent: ud2x18(3.14e18), duration: 7500 seconds }); } /*////////////////////////////////////////////////////////////////////////// SABLIER-V2-LOCKUP-LINEAR //////////////////////////////////////////////////////////////////////////*/ - function createWithDurations() public view returns (LockupLinear.CreateWithDurations memory) { - return createWithDurations(asset); + function createWithDurationsLL() public view returns (LockupLinear.CreateWithDurations memory) { + return createWithDurationsLL(asset); } - function createWithDurations(IERC20 asset_) public view returns (LockupLinear.CreateWithDurations memory) { + function createWithDurationsLL(IERC20 asset_) public view returns (LockupLinear.CreateWithDurations memory) { return LockupLinear.CreateWithDurations({ sender: users.alice, recipient: users.recipient0, @@ -231,12 +231,12 @@ contract Defaults is Merkle { }); } - function createWithRange() public view returns (LockupLinear.CreateWithRange memory) { - return createWithRange(asset); + function createWithTimestampsLL() public view returns (LockupLinear.CreateWithTimestamps memory) { + return createWithTimestampsLL(asset); } - function createWithRange(IERC20 asset_) public view returns (LockupLinear.CreateWithRange memory) { - return LockupLinear.CreateWithRange({ + function createWithTimestampsLL(IERC20 asset_) public view returns (LockupLinear.CreateWithTimestamps memory) { + return LockupLinear.CreateWithTimestamps({ sender: users.alice, recipient: users.recipient0, totalAmount: PER_STREAM_AMOUNT, @@ -260,37 +260,41 @@ contract Defaults is Merkle { BATCH //////////////////////////////////////////////////////////////////////////*/ - /// @dev Returns a default-size batch of `Batch.CreateWithDeltas` parameters. - function batchCreateWithDeltas() public view returns (Batch.CreateWithDeltas[] memory batch) { - batch = BatchBuilder.fillBatch(createWithDeltas(), BATCH_SIZE); + /// @dev Returns a default-size batch of `Batch.CreateWithDurationsLD` parameters. + function batchCreateWithDurationsLD() public view returns (Batch.CreateWithDurationsLD[] memory batch) { + batch = BatchBuilder.fillBatch(createWithDurationsLD(), BATCH_SIZE); } - /// @dev Returns a default-size batch of `Batch.CreateWithDurations` parameters. - function batchCreateWithDurations() public view returns (Batch.CreateWithDurations[] memory batch) { - batch = BatchBuilder.fillBatch(createWithDurations(), BATCH_SIZE); + /// @dev Returns a default-size batch of `Batch.CreateWithDurationsLL` parameters. + function batchCreateWithDurationsLL() public view returns (Batch.CreateWithDurationsLL[] memory batch) { + batch = BatchBuilder.fillBatch(createWithDurationsLL(), BATCH_SIZE); } - /// @dev Returns a default-size batch of `Batch.CreateWithMilestones` parameters. - function batchCreateWithMilestones() public view returns (Batch.CreateWithMilestones[] memory batch) { - batch = batchCreateWithMilestones(BATCH_SIZE); + /// @dev Returns a default-size batch of `Batch.CreateWithTimestampsLD` parameters. + function batchCreateWithTimestampsLD() public view returns (Batch.CreateWithTimestampsLD[] memory batch) { + batch = batchCreateWithTimestampsLD(BATCH_SIZE); } - /// @dev Returns a batch of `Batch.CreateWithMilestones` parameters. - function batchCreateWithMilestones(uint256 batchSize) + /// @dev Returns a batch of `Batch.CreateWithTimestampsLD` parameters. + function batchCreateWithTimestampsLD(uint256 batchSize) public view - returns (Batch.CreateWithMilestones[] memory batch) + returns (Batch.CreateWithTimestampsLD[] memory batch) { - batch = BatchBuilder.fillBatch(createWithMilestones(), batchSize); + batch = BatchBuilder.fillBatch(createWithTimestampsLD(), batchSize); } - /// @dev Returns a default-size batch of `Batch.CreateWithRange` parameters. - function batchCreateWithRange() public view returns (Batch.CreateWithRange[] memory batch) { - batch = batchCreateWithRange(BATCH_SIZE); + /// @dev Returns a default-size batch of `Batch.CreateWithTimestampsLL` parameters. + function batchCreateWithTimestampsLL() public view returns (Batch.CreateWithTimestampsLL[] memory batch) { + batch = batchCreateWithTimestampsLL(BATCH_SIZE); } - /// @dev Returns a batch of `Batch.CreateWithRange` parameters. - function batchCreateWithRange(uint256 batchSize) public view returns (Batch.CreateWithRange[] memory batch) { - batch = BatchBuilder.fillBatch(createWithRange(), batchSize); + /// @dev Returns a batch of `Batch.CreateWithTimestampsLL` parameters. + function batchCreateWithTimestampsLL(uint256 batchSize) + public + view + returns (Batch.CreateWithTimestampsLL[] memory batch) + { + batch = BatchBuilder.fillBatch(createWithTimestampsLL(), batchSize); } } From 817f6fba9bfa5b67cc998db9acd24a12ee1b35c5 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Tue, 30 Jan 2024 18:29:36 +0530 Subject: [PATCH 08/61] feat: add `name`, introduces struct `ConstructorParams` (#262) * feat: add `name` string parameter in MerkleStreamer contract constructor refactor: use struct `CreateWithLockupLinear` in Merkle streamer constructor * test: name should not exceed 32 bytes * refactor: use an struct for MerkleStreamer constructor parameters * test: rename defaults function * refactor: rename custom error chore: small rewording changes refactor: update import * refactor: add constructor params in event test: update tests accordingly chore: remove unused imports * refactor: use internal for NAME * refactor: rename params to baseParams when referring to ConstructorParams struct * build: update bun lockfile --------- Co-authored-by: andreivladbrg Co-authored-by: Paul Razvan Berg --- bun.lockb | Bin 43329 -> 43329 bytes script/CreateMerkleStreamerLL.s.sol | 17 ++--- src/SablierV2MerkleStreamerFactory.sol | 47 ++++--------- src/SablierV2MerkleStreamerLL.sol | 12 ++-- src/abstracts/SablierV2MerkleStreamer.sol | 43 ++++++++---- src/interfaces/ISablierV2MerkleStreamer.sol | 3 + .../ISablierV2MerkleStreamerFactory.sol | 28 ++------ src/libraries/Errors.sol | 3 + src/types/DataTypes.sol | 21 ++++++ test/Base.t.sol | 19 ++--- .../merkle-streamer/MerkleStreamerLL.t.sol | 22 +++--- .../merkle-streamer/MerkleStreamer.t.sol | 7 +- .../createMerkleStreamerLL.t.sol | 66 +++++++++++++----- .../createMerkleStreamerLL.tree | 13 ++-- .../ll/constructor/constructor.t.sol | 18 +++-- test/utils/Defaults.sol | 28 +++++++- test/utils/Events.sol | 13 ++-- 17 files changed, 196 insertions(+), 164 deletions(-) diff --git a/bun.lockb b/bun.lockb index 2ba27d49b45464e71bbc676d57ea0a703b1d0c64..c0526ba6953ea0e9ad57efbb655de3d65d5c4c78 100755 GIT binary patch delta 66 zcmV-I0KNag(gMNK0+22szhWX+#y+AdA`^;hIz1?nVb5;tNXa{89TMX#n73$au};T_ Y2Vym1I5;$9lTnBqlaQzcvpk3eA)yxb%7 delta 66 zcmV-I0KNag(gMNK0+22s->6=>+;t2hmB7-@PIP+blEdLn^x8wkt7P~Mltxy8u};T_ Y2W4S4W-vK7lTnBqlaQzcvpk3eA%LeJrvLx| diff --git a/script/CreateMerkleStreamerLL.s.sol b/script/CreateMerkleStreamerLL.s.sol index fb8612c0..be5cd898 100644 --- a/script/CreateMerkleStreamerLL.s.sol +++ b/script/CreateMerkleStreamerLL.s.sol @@ -1,24 +1,20 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22 <0.9.0; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; + import { BaseScript } from "./Base.s.sol"; import { ISablierV2MerkleStreamerFactory } from "../src/interfaces/ISablierV2MerkleStreamerFactory.sol"; import { ISablierV2MerkleStreamerLL } from "../src/interfaces/ISablierV2MerkleStreamerLL.sol"; +import { MerkleStreamer } from "../src/types/DataTypes.sol"; contract CreateMerkleStreamerLL is BaseScript { struct Params { - address initialAdmin; + MerkleStreamer.ConstructorParams baseParams; ISablierV2LockupLinear lockupLinear; - IERC20 asset; - bytes32 merkleRoot; - uint40 expiration; LockupLinear.Durations streamDurations; - bool cancelable; - bool transferable; string ipfsCID; uint256 campaignTotalAmount; uint256 recipientsCount; @@ -33,14 +29,9 @@ contract CreateMerkleStreamerLL is BaseScript { returns (ISablierV2MerkleStreamerLL merkleStreamerLL) { merkleStreamerLL = merkleStreamerFactory.createMerkleStreamerLL( - params.initialAdmin, + params.baseParams, params.lockupLinear, - params.asset, - params.merkleRoot, - params.expiration, params.streamDurations, - params.cancelable, - params.transferable, params.ipfsCID, params.campaignTotalAmount, params.recipientsCount diff --git a/src/SablierV2MerkleStreamerFactory.sol b/src/SablierV2MerkleStreamerFactory.sol index 9efdd7a5..7c3097a0 100644 --- a/src/SablierV2MerkleStreamerFactory.sol +++ b/src/SablierV2MerkleStreamerFactory.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; +import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; +import { SablierV2MerkleStreamerLL } from "./SablierV2MerkleStreamerLL.sol"; import { ISablierV2MerkleStreamerFactory } from "./interfaces/ISablierV2MerkleStreamerFactory.sol"; import { ISablierV2MerkleStreamerLL } from "./interfaces/ISablierV2MerkleStreamerLL.sol"; -import { SablierV2MerkleStreamerLL } from "./SablierV2MerkleStreamerLL.sol"; +import { MerkleStreamer } from "./types/DataTypes.sol"; /// @title SablierV2MerkleStreamerFactory /// @notice See the documentation in {ISablierV2MerkleStreamerFactory}. @@ -18,14 +18,9 @@ contract SablierV2MerkleStreamerFactory is ISablierV2MerkleStreamerFactory { /// @notice inheritdoc ISablierV2MerkleStreamerFactory function createMerkleStreamerLL( - address initialAdmin, + MerkleStreamer.ConstructorParams memory baseParams, ISablierV2LockupLinear lockupLinear, - IERC20 asset, - bytes32 merkleRoot, - uint40 expiration, LockupLinear.Durations memory streamDurations, - bool cancelable, - bool transferable, string memory ipfsCID, uint256 aggregateAmount, uint256 recipientsCount @@ -36,36 +31,24 @@ contract SablierV2MerkleStreamerFactory is ISablierV2MerkleStreamerFactory { // Hash the parameters to generate a salt. bytes32 salt = keccak256( abi.encodePacked( - initialAdmin, + baseParams.initialAdmin, + baseParams.asset, + bytes32(abi.encodePacked(baseParams.name)), + baseParams.merkleRoot, + baseParams.expiration, + baseParams.cancelable, + baseParams.transferable, lockupLinear, - asset, - merkleRoot, - expiration, - abi.encode(streamDurations), - cancelable, - transferable + abi.encode(streamDurations) ) ); // Deploy the Merkle streamer with CREATE2. - merkleStreamerLL = new SablierV2MerkleStreamerLL{ salt: salt }( - initialAdmin, lockupLinear, asset, merkleRoot, expiration, streamDurations, cancelable, transferable - ); + merkleStreamerLL = new SablierV2MerkleStreamerLL{ salt: salt }(baseParams, lockupLinear, streamDurations); - // Log the creation of the Merkle streamer, including some metadata that is not stored on-chain. + // Using a different function to emit the event to avoid stack too deep error. emit CreateMerkleStreamerLL( - merkleStreamerLL, - initialAdmin, - lockupLinear, - asset, - merkleRoot, - expiration, - streamDurations, - cancelable, - transferable, - ipfsCID, - aggregateAmount, - recipientsCount + merkleStreamerLL, baseParams, lockupLinear, streamDurations, ipfsCID, aggregateAmount, recipientsCount ); } } diff --git a/src/SablierV2MerkleStreamerLL.sol b/src/SablierV2MerkleStreamerLL.sol index a3a451c0..ca9fb690 100644 --- a/src/SablierV2MerkleStreamerLL.sol +++ b/src/SablierV2MerkleStreamerLL.sol @@ -10,6 +10,7 @@ import { ud } from "@prb/math/src/UD60x18.sol"; import { SablierV2MerkleStreamer } from "./abstracts/SablierV2MerkleStreamer.sol"; import { ISablierV2MerkleStreamerLL } from "./interfaces/ISablierV2MerkleStreamerLL.sol"; +import { MerkleStreamer } from "./types/DataTypes.sol"; /// @title SablierV2MerkleStreamerLL /// @notice See the documentation in {ISablierV2MerkleStreamerLL}. @@ -41,16 +42,11 @@ contract SablierV2MerkleStreamerLL is /// @dev Constructs the contract by initializing the immutable state variables, and max approving the Sablier /// contract. constructor( - address initialAdmin, + MerkleStreamer.ConstructorParams memory baseParams, ISablierV2LockupLinear lockupLinear, - IERC20 asset, - bytes32 merkleRoot, - uint40 expiration, - LockupLinear.Durations memory streamDurations_, - bool cancelable, - bool transferable + LockupLinear.Durations memory streamDurations_ ) - SablierV2MerkleStreamer(initialAdmin, asset, merkleRoot, expiration, cancelable, transferable) + SablierV2MerkleStreamer(baseParams) { LOCKUP_LINEAR = lockupLinear; streamDurations = streamDurations_; diff --git a/src/abstracts/SablierV2MerkleStreamer.sol b/src/abstracts/SablierV2MerkleStreamer.sol index 75b50101..0fafbfa4 100644 --- a/src/abstracts/SablierV2MerkleStreamer.sol +++ b/src/abstracts/SablierV2MerkleStreamer.sol @@ -8,6 +8,7 @@ import { BitMaps } from "@openzeppelin/contracts/utils/structs/BitMaps.sol"; import { Adminable } from "@sablier/v2-core/src/abstracts/Adminable.sol"; import { ISablierV2MerkleStreamer } from "../interfaces/ISablierV2MerkleStreamer.sol"; +import { MerkleStreamer } from "../types/DataTypes.sol"; import { Errors } from "../libraries/Errors.sol"; /// @title SablierV2MerkleStreamer @@ -38,6 +39,13 @@ abstract contract SablierV2MerkleStreamer is /// @inheritdoc ISablierV2MerkleStreamer bool public immutable override TRANSFERABLE; + /*////////////////////////////////////////////////////////////////////////// + INTERNAL CONSTANT + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev The name of the campaign stored as bytes32. + bytes32 internal immutable NAME; + /*////////////////////////////////////////////////////////////////////////// INTERNAL STORAGE //////////////////////////////////////////////////////////////////////////*/ @@ -50,20 +58,22 @@ abstract contract SablierV2MerkleStreamer is //////////////////////////////////////////////////////////////////////////*/ /// @dev Constructs the contract by initializing the immutable state variables. - constructor( - address initialAdmin, - IERC20 asset, - bytes32 merkleRoot, - uint40 expiration, - bool cancelable, - bool transferable - ) { - admin = initialAdmin; - ASSET = asset; - MERKLE_ROOT = merkleRoot; - EXPIRATION = expiration; - CANCELABLE = cancelable; - TRANSFERABLE = transferable; + constructor(MerkleStreamer.ConstructorParams memory params) { + // Checks: the campaign name is not greater than 32 bytes + if (bytes(params.name).length > 32) { + revert Errors.SablierV2MerkleStreamer_CampaignNameTooLong({ + nameLength: bytes(params.name).length, + maxLength: 32 + }); + } + + admin = params.initialAdmin; + ASSET = params.asset; + CANCELABLE = params.cancelable; + EXPIRATION = params.expiration; + MERKLE_ROOT = params.merkleRoot; + NAME = bytes32(abi.encodePacked(params.name)); + TRANSFERABLE = params.transferable; } /*////////////////////////////////////////////////////////////////////////// @@ -80,6 +90,11 @@ abstract contract SablierV2MerkleStreamer is return EXPIRATION > 0 && EXPIRATION <= block.timestamp; } + /// @inheritdoc ISablierV2MerkleStreamer + function name() external view override returns (string memory) { + return string(abi.encodePacked(NAME)); + } + /*////////////////////////////////////////////////////////////////////////// USER-FACING NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ diff --git a/src/interfaces/ISablierV2MerkleStreamer.sol b/src/interfaces/ISablierV2MerkleStreamer.sol index 94c5d7de..0079e149 100644 --- a/src/interfaces/ISablierV2MerkleStreamer.sol +++ b/src/interfaces/ISablierV2MerkleStreamer.sol @@ -38,6 +38,9 @@ interface ISablierV2MerkleStreamer is IAdminable { /// @dev This is an immutable state variable. function EXPIRATION() external returns (uint40); + /// @notice Retrieves the name of the campaign. + function name() external returns (string memory); + /// @notice Returns a flag indicating whether a claim has been made for a given index. /// @dev Uses a bitmap to save gas. /// @param index The index of the recipient to check. diff --git a/src/interfaces/ISablierV2MerkleStreamerFactory.sol b/src/interfaces/ISablierV2MerkleStreamerFactory.sol index 504db123..46deefaa 100644 --- a/src/interfaces/ISablierV2MerkleStreamerFactory.sol +++ b/src/interfaces/ISablierV2MerkleStreamerFactory.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; import { ISablierV2MerkleStreamerLL } from "./ISablierV2MerkleStreamerLL.sol"; +import { MerkleStreamer } from "../types/DataTypes.sol"; /// @title ISablierV2MerkleStreamerFactory /// @notice Deploys new Lockup Linear Merkle streamers via CREATE2. @@ -16,15 +16,10 @@ interface ISablierV2MerkleStreamerFactory { /// @notice Emitted when a Sablier V2 Lockup Linear Merkle streamer is created. event CreateMerkleStreamerLL( - ISablierV2MerkleStreamerLL merkleStreamer, - address indexed admin, - ISablierV2LockupLinear indexed lockupLinear, - IERC20 indexed asset, - bytes32 merkleRoot, - uint40 expiration, + ISablierV2MerkleStreamerLL indexed merkleStreamerLL, + MerkleStreamer.ConstructorParams indexed baseParams, + ISablierV2LockupLinear lockupLinear, LockupLinear.Durations streamDurations, - bool cancelable, - bool transferable, string ipfsCID, uint256 aggregateAmount, uint256 recipientsCount @@ -36,27 +31,18 @@ interface ISablierV2MerkleStreamerFactory { /// @notice Creates a new Merkle streamer that uses Lockup Linear. /// @dev Emits a {CreateMerkleStreamerLL} event. - /// @param initialAdmin The initial admin of the Merkle streamer contract. + /// @param baseParams Struct encapsulating the {SablierV2MerkleStreamer} parameters, which are documented in + /// {DataTypes}. /// @param lockupLinear The address of the {SablierV2LockupLinear} contract. - /// @param asset The address of the streamed ERC-20 asset. - /// @param merkleRoot The Merkle root of the claim data. - /// @param expiration The expiration of the streaming campaign, as a Unix timestamp. /// @param streamDurations The durations for each stream due to the recipient. - /// @param cancelable Indicates if each stream will be cancelable. - /// @param transferable Indicates if each stream NFT will be transferable. /// @param ipfsCID Metadata parameter emitted for indexing purposes. /// @param aggregateAmount Total amount of ERC-20 assets to be streamed to all recipients. /// @param recipientsCount Total number of recipients eligible to claim. /// @return merkleStreamerLL The address of the newly created Merkle streamer contract. function createMerkleStreamerLL( - address initialAdmin, + MerkleStreamer.ConstructorParams memory baseParams, ISablierV2LockupLinear lockupLinear, - IERC20 asset, - bytes32 merkleRoot, - uint40 expiration, LockupLinear.Durations memory streamDurations, - bool cancelable, - bool transferable, string memory ipfsCID, uint256 aggregateAmount, uint256 recipientsCount diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index 382e716a..1c343584 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -17,6 +17,9 @@ library Errors { /// @notice Thrown when trying to claim after the campaign has expired. error SablierV2MerkleStreamer_CampaignExpired(uint256 currentTime, uint40 expiration); + /// @notice Thrown when trying to create a campaign with a name that is too long. + error SablierV2MerkleStreamer_CampaignNameTooLong(uint256 nameLength, uint256 maxLength); + /// @notice Thrown when trying to clawback when the campaign has not expired. error SablierV2MerkleStreamer_CampaignNotExpired(uint256 currentTime, uint40 expiration); diff --git a/src/types/DataTypes.sol b/src/types/DataTypes.sol index ca2d63ec..02549065 100644 --- a/src/types/DataTypes.sol +++ b/src/types/DataTypes.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2Lockup } from "@sablier/v2-core/src/interfaces/ISablierV2Lockup.sol"; import { Broker, LockupDynamic, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; @@ -60,3 +61,23 @@ library Batch { Broker broker; } } + +library MerkleStreamer { + /// @notice Struct encapsulating the base constructor parameter of a {SablierV2MerkleStreamer} contract. + /// @param initialAdmin The initial admin of the Merkle streamer contract. + /// @param asset The address of the streamed ERC-20 asset. + /// @param name The name of the Merkle streamer contract. + /// @param merkleRoot The Merkle root of the claim data. + /// @param expiration The expiration of the streaming campaign, as a Unix timestamp. + /// @param cancelable Indicates if each stream will be cancelable. + /// @param transferable Indicates if each stream NFT will be transferable. + struct ConstructorParams { + address initialAdmin; + IERC20 asset; + string name; + bytes32 merkleRoot; + uint40 expiration; + bool cancelable; + bool transferable; + } +} diff --git a/test/Base.t.sol b/test/Base.t.sol index 164b60c5..792277e6 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -261,13 +261,14 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions bytes32 salt = keccak256( abi.encodePacked( admin, - lockupLinear, asset, + defaults.NAME_BYTES32(), merkleRoot, expiration, - abi.encode(defaults.durations()), defaults.CANCELABLE(), - defaults.TRANSFERABLE() + defaults.TRANSFERABLE(), + lockupLinear, + abi.encode(defaults.durations()) ) ); bytes32 creationBytecodeHash = keccak256(getMerkleStreamerLLBytecode(admin, merkleRoot, expiration)); @@ -286,16 +287,8 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions internal returns (bytes memory) { - bytes memory constructorArgs = abi.encode( - admin, - lockupLinear, - asset, - merkleRoot, - expiration, - defaults.durations(), - defaults.CANCELABLE(), - defaults.TRANSFERABLE() - ); + bytes memory constructorArgs = + abi.encode(defaults.baseParams(admin, merkleRoot, expiration), lockupLinear, defaults.durations()); if (!isTestOptimizedProfile()) { return bytes.concat(type(SablierV2MerkleStreamerLL).creationCode, constructorArgs); } else { diff --git a/test/fork/merkle-streamer/MerkleStreamerLL.t.sol b/test/fork/merkle-streamer/MerkleStreamerLL.t.sol index 7e436931..c2741211 100644 --- a/test/fork/merkle-streamer/MerkleStreamerLL.t.sol +++ b/test/fork/merkle-streamer/MerkleStreamerLL.t.sol @@ -6,6 +6,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { Lockup, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; import { ISablierV2MerkleStreamerLL } from "src/interfaces/ISablierV2MerkleStreamerLL.sol"; +import { MerkleStreamer } from "src/types/DataTypes.sol"; import { MerkleBuilder } from "../../utils/MerkleBuilder.sol"; import { Fork_Test } from "../Fork.t.sol"; @@ -40,6 +41,7 @@ abstract contract MerkleStreamerLL_Fork_Test is Fork_Test { uint256 aggregateAmount; uint128 clawbackAmount; address expectedStreamerLL; + MerkleStreamer.ConstructorParams baseParams; LockupLinear.Stream expectedStream; uint256 expectedStreamId; uint256[] indexes; @@ -92,31 +94,25 @@ abstract contract MerkleStreamerLL_Fork_Test is Fork_Test { vars.merkleRoot = getRoot(leaves.toBytes32()); vars.expectedStreamerLL = computeMerkleStreamerLLAddress(params.admin, vars.merkleRoot, params.expiration); + + vars.baseParams = + defaults.baseParams({ admin: params.admin, merkleRoot: vars.merkleRoot, expiration: params.expiration }); + vm.expectEmit({ emitter: address(merkleStreamerFactory) }); emit CreateMerkleStreamerLL({ - merkleStreamer: ISablierV2MerkleStreamerLL(vars.expectedStreamerLL), - admin: params.admin, + merkleStreamerLL: ISablierV2MerkleStreamerLL(vars.expectedStreamerLL), + baseParams: vars.baseParams, lockupLinear: lockupLinear, - asset: asset, - merkleRoot: vars.merkleRoot, - expiration: params.expiration, streamDurations: defaults.durations(), - cancelable: defaults.CANCELABLE(), - transferable: defaults.TRANSFERABLE(), ipfsCID: defaults.IPFS_CID(), aggregateAmount: vars.aggregateAmount, recipientsCount: vars.recipientsCount }); vars.merkleStreamerLL = merkleStreamerFactory.createMerkleStreamerLL({ - initialAdmin: params.admin, + baseParams: vars.baseParams, lockupLinear: lockupLinear, - asset: asset, - merkleRoot: vars.merkleRoot, - expiration: params.expiration, streamDurations: defaults.durations(), - cancelable: defaults.CANCELABLE(), - transferable: defaults.TRANSFERABLE(), ipfsCID: defaults.IPFS_CID(), aggregateAmount: vars.aggregateAmount, recipientsCount: vars.recipientsCount diff --git a/test/integration/merkle-streamer/MerkleStreamer.t.sol b/test/integration/merkle-streamer/MerkleStreamer.t.sol index 3f244cef..63adb001 100644 --- a/test/integration/merkle-streamer/MerkleStreamer.t.sol +++ b/test/integration/merkle-streamer/MerkleStreamer.t.sol @@ -55,13 +55,8 @@ abstract contract MerkleStreamer_Integration_Test is Integration_Test { function createMerkleStreamerLL(address admin, uint40 expiration) internal returns (ISablierV2MerkleStreamerLL) { return merkleStreamerFactory.createMerkleStreamerLL({ - initialAdmin: admin, + baseParams: defaults.baseParams(admin, defaults.MERKLE_ROOT(), expiration), lockupLinear: lockupLinear, - asset: asset, - merkleRoot: defaults.MERKLE_ROOT(), - expiration: expiration, - cancelable: defaults.CANCELABLE(), - transferable: defaults.TRANSFERABLE(), streamDurations: defaults.durations(), ipfsCID: defaults.IPFS_CID(), aggregateAmount: defaults.AGGREGATE_AMOUNT(), diff --git a/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.t.sol b/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.t.sol index 0c17b93a..936cc02f 100644 --- a/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.t.sol +++ b/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.t.sol @@ -3,7 +3,9 @@ pragma solidity >=0.8.22 <0.9.0; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; +import { Errors } from "src/libraries/Errors.sol"; import { ISablierV2MerkleStreamerLL } from "src/interfaces/ISablierV2MerkleStreamerLL.sol"; +import { MerkleStreamer } from "src/types/DataTypes.sol"; import { MerkleStreamer_Integration_Test } from "../../MerkleStreamer.t.sol"; @@ -12,12 +14,38 @@ contract CreateMerkleStreamerLL_Integration_Test is MerkleStreamer_Integration_T MerkleStreamer_Integration_Test.setUp(); } + function test_RevertWhen_CampaignNameTooLong() external { + MerkleStreamer.ConstructorParams memory baseParams = defaults.baseParams(); + LockupLinear.Durations memory streamDurations = defaults.durations(); + string memory ipfsCID = defaults.IPFS_CID(); + uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); + uint256 recipientsCount = defaults.RECIPIENTS_COUNT(); + + baseParams.name = "this string is longer than 32 characters"; + + vm.expectRevert( + abi.encodeWithSelector( + Errors.SablierV2MerkleStreamer_CampaignNameTooLong.selector, bytes(baseParams.name).length, 32 + ) + ); + + merkleStreamerFactory.createMerkleStreamerLL({ + baseParams: baseParams, + lockupLinear: lockupLinear, + streamDurations: streamDurations, + ipfsCID: ipfsCID, + aggregateAmount: aggregateAmount, + recipientsCount: recipientsCount + }); + } + + modifier whenCampaignNameIsNotTooLong() { + _; + } + /// @dev This test works because a default Merkle streamer is deployed in {Integration_Test.setUp} - function test_RevertGiven_AlreadyDeployed() external { - bytes32 merkleRoot = defaults.MERKLE_ROOT(); - uint40 expiration = defaults.EXPIRATION(); - bool cancelable = defaults.CANCELABLE(); - bool transferable = defaults.TRANSFERABLE(); + function test_RevertGiven_AlreadyDeployed() external whenCampaignNameIsNotTooLong { + MerkleStreamer.ConstructorParams memory baseParams = defaults.baseParams(); LockupLinear.Durations memory streamDurations = defaults.durations(); string memory ipfsCID = defaults.IPFS_CID(); uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); @@ -25,13 +53,8 @@ contract CreateMerkleStreamerLL_Integration_Test is MerkleStreamer_Integration_T vm.expectRevert(); merkleStreamerFactory.createMerkleStreamerLL({ - initialAdmin: users.admin, + baseParams: baseParams, lockupLinear: lockupLinear, - asset: asset, - merkleRoot: merkleRoot, - expiration: expiration, - cancelable: cancelable, - transferable: transferable, streamDurations: streamDurations, ipfsCID: ipfsCID, aggregateAmount: aggregateAmount, @@ -43,21 +66,26 @@ contract CreateMerkleStreamerLL_Integration_Test is MerkleStreamer_Integration_T _; } - function testFuzz_CreateMerkleStreamerLL(address admin, uint40 expiration) external givenNotAlreadyDeployed { + function testFuzz_CreateMerkleStreamerLL( + address admin, + uint40 expiration + ) + external + givenNotAlreadyDeployed + whenCampaignNameIsNotTooLong + { vm.assume(admin != users.admin); address expectedStreamerLL = computeMerkleStreamerLLAddress(admin, expiration); + MerkleStreamer.ConstructorParams memory baseParams = + defaults.baseParams({ admin: admin, merkleRoot: defaults.MERKLE_ROOT(), expiration: expiration }); + vm.expectEmit({ emitter: address(merkleStreamerFactory) }); emit CreateMerkleStreamerLL({ - merkleStreamer: ISablierV2MerkleStreamerLL(expectedStreamerLL), - admin: admin, + merkleStreamerLL: ISablierV2MerkleStreamerLL(expectedStreamerLL), + baseParams: baseParams, lockupLinear: lockupLinear, - asset: asset, - merkleRoot: defaults.MERKLE_ROOT(), - expiration: expiration, streamDurations: defaults.durations(), - cancelable: defaults.CANCELABLE(), - transferable: defaults.TRANSFERABLE(), ipfsCID: defaults.IPFS_CID(), aggregateAmount: defaults.AGGREGATE_AMOUNT(), recipientsCount: defaults.RECIPIENTS_COUNT() diff --git a/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.tree b/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.tree index 48eeaea2..0ca44455 100644 --- a/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.tree +++ b/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.tree @@ -1,6 +1,9 @@ createMerkleStreamerLL.t.sol -├── given the Merkle streamer has been deployed with CREATE2 -│ └── it should revert -└── given the Merkle streamer has not been deployed - ├── it should deploy the Merkle streamer - └── it should emit an {CreateMerkleStreamerLL} event +├── when the campaign name is too long +│ └── it should revert +└── when the campaign name is not too long + ├── given the Merkle streamer has been deployed with CREATE2 + │ └── it should revert + └── given the Merkle streamer has not been deployed + ├── it should deploy the Merkle streamer + └── it should emit a {CreateMerkleStreamerLL} event diff --git a/test/integration/merkle-streamer/ll/constructor/constructor.t.sol b/test/integration/merkle-streamer/ll/constructor/constructor.t.sol index 0837554e..2f0d3ea2 100644 --- a/test/integration/merkle-streamer/ll/constructor/constructor.t.sol +++ b/test/integration/merkle-streamer/ll/constructor/constructor.t.sol @@ -13,6 +13,7 @@ contract Constructor_MerkleStreamerLL_Integration_Test is MerkleStreamer_Integra address actualAdmin; uint256 actualAllowance; address actualAsset; + string actualName; bool actualCancelable; bool actualTransferable; LockupLinear.Durations actualDurations; @@ -22,6 +23,7 @@ contract Constructor_MerkleStreamerLL_Integration_Test is MerkleStreamer_Integra address expectedAdmin; uint256 expectedAllowance; address expectedAsset; + bytes32 expectedName; bool expectedCancelable; bool expectedTransferable; LockupLinear.Durations expectedDurations; @@ -31,16 +33,8 @@ contract Constructor_MerkleStreamerLL_Integration_Test is MerkleStreamer_Integra } function test_Constructor() external { - SablierV2MerkleStreamerLL constructedStreamerLL = new SablierV2MerkleStreamerLL( - users.admin, - lockupLinear, - asset, - defaults.MERKLE_ROOT(), - defaults.EXPIRATION(), - defaults.durations(), - defaults.CANCELABLE(), - defaults.TRANSFERABLE() - ); + SablierV2MerkleStreamerLL constructedStreamerLL = + new SablierV2MerkleStreamerLL(defaults.baseParams(), lockupLinear, defaults.durations()); Vars memory vars; @@ -52,6 +46,10 @@ contract Constructor_MerkleStreamerLL_Integration_Test is MerkleStreamer_Integra vars.expectedAsset = address(asset); assertEq(vars.actualAsset, vars.expectedAsset, "asset"); + vars.actualName = constructedStreamerLL.name(); + vars.expectedName = defaults.NAME_BYTES32(); + assertEq(bytes32(abi.encodePacked(vars.actualName)), vars.expectedName, "name"); + vars.actualMerkleRoot = constructedStreamerLL.MERKLE_ROOT(); vars.expectedMerkleRoot = defaults.MERKLE_ROOT(); assertEq(vars.actualMerkleRoot, vars.expectedMerkleRoot, "merkleRoot"); diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index 66297b86..bb9e395f 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -7,7 +7,7 @@ import { ud2x18 } from "@prb/math/src/UD2x18.sol"; import { UD60x18 } from "@prb/math/src/UD60x18.sol"; import { Broker, LockupDynamic, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { Batch } from "src/types/DataTypes.sol"; +import { Batch, MerkleStreamer } from "src/types/DataTypes.sol"; import { ArrayBuilder } from "./ArrayBuilder.sol"; import { BatchBuilder } from "./BatchBuilder.sol"; @@ -55,6 +55,8 @@ contract Defaults is Merkle { bool public constant TRANSFERABLE = false; uint256[] public LEAVES = new uint256[](RECIPIENTS_COUNT); bytes32 public immutable MERKLE_ROOT; + string public constant NAME = "Airdrop Campaign"; + bytes32 public constant NAME_BYTES32 = bytes32(abi.encodePacked("Airdrop Campaign")); /*////////////////////////////////////////////////////////////////////////// VARIABLES @@ -114,6 +116,30 @@ contract Defaults is Merkle { return getProof(LEAVES.toBytes32(), pos); } + function baseParams() public view returns (MerkleStreamer.ConstructorParams memory) { + return baseParams(users.admin, MERKLE_ROOT, EXPIRATION); + } + + function baseParams( + address admin, + bytes32 merkleRoot, + uint40 expiration + ) + public + view + returns (MerkleStreamer.ConstructorParams memory) + { + return MerkleStreamer.ConstructorParams({ + initialAdmin: admin, + asset: asset, + name: NAME, + merkleRoot: merkleRoot, + expiration: expiration, + cancelable: CANCELABLE, + transferable: TRANSFERABLE + }); + } + /*////////////////////////////////////////////////////////////////////////// SABLIER-V2-LOCKUP //////////////////////////////////////////////////////////////////////////*/ diff --git a/test/utils/Events.sol b/test/utils/Events.sol index 5977a16c..9a8c2a96 100644 --- a/test/utils/Events.sol +++ b/test/utils/Events.sol @@ -1,26 +1,21 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; import { ISablierV2MerkleStreamerLL } from "src/interfaces/ISablierV2MerkleStreamerLL.sol"; +import { MerkleStreamer } from "src/types/DataTypes.sol"; /// @notice Abstract contract containing all the events emitted by the protocol. abstract contract Events { event Claim(uint256 index, address indexed recipient, uint128 amount, uint256 indexed streamId); event Clawback(address indexed admin, address indexed to, uint128 amount); event CreateMerkleStreamerLL( - ISablierV2MerkleStreamerLL merkleStreamer, - address indexed admin, - ISablierV2LockupLinear indexed lockupLinear, - IERC20 indexed asset, - bytes32 merkleRoot, - uint40 expiration, + ISablierV2MerkleStreamerLL indexed merkleStreamerLL, + MerkleStreamer.ConstructorParams indexed baseParams, + ISablierV2LockupLinear lockupLinear, LockupLinear.Durations streamDurations, - bool cancelable, - bool transferable, string ipfsCID, uint256 aggregateAmount, uint256 recipientsCount From 9066d847bf085f13d08820e65c687409faca9c9c Mon Sep 17 00:00:00 2001 From: andreivladbrg Date: Fri, 26 Jan 2024 18:49:56 +0200 Subject: [PATCH 09/61] style: merge storage and constants to state headers --- src/SablierV2MerkleStreamerLL.sol | 6 +----- src/abstracts/SablierV2MerkleStreamer.sol | 14 +++----------- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/SablierV2MerkleStreamerLL.sol b/src/SablierV2MerkleStreamerLL.sol index ca9fb690..4abad1bf 100644 --- a/src/SablierV2MerkleStreamerLL.sol +++ b/src/SablierV2MerkleStreamerLL.sol @@ -22,16 +22,12 @@ contract SablierV2MerkleStreamerLL is using SafeERC20 for IERC20; /*////////////////////////////////////////////////////////////////////////// - USER-FACING CONSTANTS + STATE VARIABLES //////////////////////////////////////////////////////////////////////////*/ /// @inheritdoc ISablierV2MerkleStreamerLL ISablierV2LockupLinear public immutable override LOCKUP_LINEAR; - /*////////////////////////////////////////////////////////////////////////// - USER-FACING STORAGE - //////////////////////////////////////////////////////////////////////////*/ - /// @inheritdoc ISablierV2MerkleStreamerLL LockupLinear.Durations public override streamDurations; diff --git a/src/abstracts/SablierV2MerkleStreamer.sol b/src/abstracts/SablierV2MerkleStreamer.sol index 0fafbfa4..a25b3bba 100644 --- a/src/abstracts/SablierV2MerkleStreamer.sol +++ b/src/abstracts/SablierV2MerkleStreamer.sol @@ -21,7 +21,7 @@ abstract contract SablierV2MerkleStreamer is using SafeERC20 for IERC20; /*////////////////////////////////////////////////////////////////////////// - USER-FACING CONSTANTS + STATE VARIABLES //////////////////////////////////////////////////////////////////////////*/ /// @inheritdoc ISablierV2MerkleStreamer @@ -36,19 +36,11 @@ abstract contract SablierV2MerkleStreamer is /// @inheritdoc ISablierV2MerkleStreamer bytes32 public immutable override MERKLE_ROOT; - /// @inheritdoc ISablierV2MerkleStreamer - bool public immutable override TRANSFERABLE; - - /*////////////////////////////////////////////////////////////////////////// - INTERNAL CONSTANT - //////////////////////////////////////////////////////////////////////////*/ - /// @dev The name of the campaign stored as bytes32. bytes32 internal immutable NAME; - /*////////////////////////////////////////////////////////////////////////// - INTERNAL STORAGE - //////////////////////////////////////////////////////////////////////////*/ + /// @inheritdoc ISablierV2MerkleStreamer + bool public immutable override TRANSFERABLE; /// @dev Packed booleans that record the history of claims. BitMaps.BitMap internal _claimedBitMap; From c848ae77efeabea37d2cd8db13cda1f53034b894 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Mon, 15 Jan 2024 20:39:06 +0530 Subject: [PATCH 10/61] feat: add `name` string parameter in MerkleStreamer contract constructor refactor: use struct `CreateWithLockupLinear` in Merkle streamer constructor test: name should not exceed 32 bytes refactor: use an struct for MerkleStreamer constructor parameters test: rename defaults function refactor: rename custom error chore: small rewording changes refactor: update import refactor: add constructor params in event test: update tests accordingly chore: remove unused imports refactor: use internal for NAME refactor: Rename MerkleStreamer to MerkleLockup ci: update step name refactor: capitalize Lockup --- ...mer-ll.yml => create-merkle-lockup-ll.yml} | 8 +-- .../deploy-merkle-lockup-factory.yml | 39 +++++++++++ foundry.toml | 4 +- ...merLL.s.sol => CreateMerkleLockupLL.s.sol} | 18 ++--- script/DeployDeterministicPeriphery.s.sol | 8 +-- script/DeployMerkleLockupFactory.s.sol | 12 ++++ script/DeployMerkleStreamerFactory.s.sol | 12 ---- script/DeployPeriphery.s.sol | 12 ++-- script/DeployProtocol.s.sol | 6 +- shell/prepare-artifacts.sh | 8 +-- shell/update-precompiles.sh | 4 +- ...y.sol => SablierV2MerkleLockupFactory.sol} | 30 ++++----- ...amerLL.sol => SablierV2MerkleLockupLL.sol} | 28 ++++---- ...Streamer.sol => SablierV2MerkleLockup.sol} | 45 ++++++------- ...treamer.sol => ISablierV2MerkleLockup.sol} | 12 ++-- ....sol => ISablierV2MerkleLockupFactory.sol} | 32 ++++----- ...merLL.sol => ISablierV2MerkleLockupLL.sol} | 8 +-- src/libraries/Errors.sol | 12 ++-- src/types/DataTypes.sol | 8 +-- test/Base.t.sol | 35 +++++----- test/fork/assets/USDC.t.sol | 4 +- test/fork/assets/USDT.t.sol | 4 +- .../MerkleLockupLL.t.sol} | 48 +++++++------- .../merkle-lockup/MerkleLockup.t.sol | 66 +++++++++++++++++++ .../createMerkleLockupLL.t.sol} | 46 ++++++------- .../createMerkleLockupLL.tree | 9 +++ .../ll/claim/claim.t.sol | 34 +++++----- .../ll/claim/claim.tree | 0 .../ll/clawback/clawback.t.sol | 18 ++--- .../ll/clawback/clawback.tree | 0 .../ll/constructor/constructor.t.sol | 30 ++++----- .../ll/has-claimed/hasClaimed.t.sol | 12 ++-- .../ll/has-claimed/hasClaimed.tree | 0 .../ll/has-expired/hasExpired.t.sol | 18 ++--- .../ll/has-expired/hasExpired.tree | 0 .../merkle-streamer/MerkleStreamer.t.sol | 66 ------------------- .../createMerkleStreamerLL.tree | 9 --- test/utils/Defaults.sol | 12 ++-- test/utils/DeployOptimized.sol | 16 ++--- test/utils/Events.sol | 10 +-- test/utils/Precompiles.sol | 19 +++--- test/utils/Precompiles.t.sol | 14 ++-- 42 files changed, 403 insertions(+), 373 deletions(-) rename .github/workflows/{create-merkle-streamer-ll.yml => create-merkle-lockup-ll.yml} (83%) create mode 100644 .github/workflows/deploy-merkle-lockup-factory.yml rename script/{CreateMerkleStreamerLL.s.sol => CreateMerkleLockupLL.s.sol} (57%) create mode 100644 script/DeployMerkleLockupFactory.s.sol delete mode 100644 script/DeployMerkleStreamerFactory.s.sol rename src/{SablierV2MerkleStreamerFactory.sol => SablierV2MerkleLockupFactory.sol} (55%) rename src/{SablierV2MerkleStreamerLL.sol => SablierV2MerkleLockupLL.sol} (80%) rename src/abstracts/{SablierV2MerkleStreamer.sol => SablierV2MerkleLockup.sol} (76%) rename src/interfaces/{ISablierV2MerkleStreamer.sol => ISablierV2MerkleLockup.sol} (88%) rename src/interfaces/{ISablierV2MerkleStreamerFactory.sol => ISablierV2MerkleLockupFactory.sol} (62%) rename src/interfaces/{ISablierV2MerkleStreamerLL.sol => ISablierV2MerkleLockupLL.sol} (87%) rename test/fork/{merkle-streamer/MerkleStreamerLL.t.sol => merkle-lockup/MerkleLockupLL.t.sol} (80%) create mode 100644 test/integration/merkle-lockup/MerkleLockup.t.sol rename test/integration/{merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.t.sol => merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol} (56%) create mode 100644 test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.tree rename test/integration/{merkle-streamer => merkle-lockup}/ll/claim/claim.t.sol (79%) rename test/integration/{merkle-streamer => merkle-lockup}/ll/claim/claim.tree (100%) rename test/integration/{merkle-streamer => merkle-lockup}/ll/clawback/clawback.t.sol (71%) rename test/integration/{merkle-streamer => merkle-lockup}/ll/clawback/clawback.tree (100%) rename test/integration/{merkle-streamer => merkle-lockup}/ll/constructor/constructor.t.sol (71%) rename test/integration/{merkle-streamer => merkle-lockup}/ll/has-claimed/hasClaimed.t.sol (54%) rename test/integration/{merkle-streamer => merkle-lockup}/ll/has-claimed/hasClaimed.tree (100%) rename test/integration/{merkle-streamer => merkle-lockup}/ll/has-expired/hasExpired.t.sol (50%) rename test/integration/{merkle-streamer => merkle-lockup}/ll/has-expired/hasExpired.tree (100%) delete mode 100644 test/integration/merkle-streamer/MerkleStreamer.t.sol delete mode 100644 test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.tree diff --git a/.github/workflows/create-merkle-streamer-ll.yml b/.github/workflows/create-merkle-lockup-ll.yml similarity index 83% rename from .github/workflows/create-merkle-streamer-ll.yml rename to .github/workflows/create-merkle-lockup-ll.yml index 324b68be..500bbdb7 100644 --- a/.github/workflows/create-merkle-streamer-ll.yml +++ b/.github/workflows/create-merkle-lockup-ll.yml @@ -1,4 +1,4 @@ -name: "Create Merkle Streamer LockupLinear" +name: "Create Merkle Lockup LockupLinear" env: API_KEY_INFURA: ${{ secrets.API_KEY_INFURA }} @@ -18,7 +18,7 @@ on: required: false jobs: - create-merkle-streamer-ll: + create-merkle-lockup-ll: runs-on: "ubuntu-latest" steps: - name: "Check out the repo" @@ -27,9 +27,9 @@ jobs: - name: "Install Foundry" uses: "foundry-rs/foundry-toolchain@v1" - - name: "Create a Merkle streamer contract that uses Sablier V2 Lockup Linear" + - name: "Create a Merkle Lockup contract that uses LL" run: >- - forge script script/CreateMerkleStreamerLL.s.sol + forge script script/CreateMerkleLockupLL.s.sol --broadcast --rpc-url "${{ inputs.chain }}" --sig "run(address,(address,address,address,bytes32,uint40,(uint40,uint40),bool,bool,string,uint256,uint256))" diff --git a/.github/workflows/deploy-merkle-lockup-factory.yml b/.github/workflows/deploy-merkle-lockup-factory.yml new file mode 100644 index 00000000..793eafeb --- /dev/null +++ b/.github/workflows/deploy-merkle-lockup-factory.yml @@ -0,0 +1,39 @@ +name: "Deploy Merkle Lockup Factory" + +env: + API_KEY_INFURA: ${{ secrets.API_KEY_INFURA }} + FOUNDRY_PROFILE: "optimized" + MNEMONIC: ${{ secrets.MNEMONIC }} + RPC_URL_MAINNET: ${{ secrets.RPC_URL_MAINNET }} + +on: + workflow_dispatch: + inputs: + chain: + default: "sepolia" + description: "Chain name as defined in the Foundry config." + required: false + +jobs: + deploy-merkle-lockup-factory: + runs-on: "ubuntu-latest" + steps: + - name: "Check out the repo" + uses: "actions/checkout@v4" + + - name: "Install Foundry" + uses: "foundry-rs/foundry-toolchain@v1" + + - name: "Deploy the SablierV2MerkleLockupFactory contract" + run: >- + forge script script/DeployMerkleLockupFactory.s.sol + --broadcast + --rpc-url "${{ inputs.chain }}" + --sig "run()" + --verify + -vvvv + + - name: "Add workflow summary" + run: | + echo "## Result" >> $GITHUB_STEP_SUMMARY + echo "✅ Done" >> $GITHUB_STEP_SUMMARY diff --git a/foundry.toml b/foundry.toml index 560e0ca3..910202c1 100644 --- a/foundry.toml +++ b/foundry.toml @@ -7,8 +7,8 @@ fs_permissions = [{ access = "read", path = "out-optimized" }] gas_reports = [ "SablierV2Batch", - "SablierV2MerkleStreamerFactory", - "SablierV2MerkleStreamerLL", + "SablierV2MerkleLockupFactory", + "SablierV2MerkleLockupLL", ] optimizer = true optimizer_runs = 10_000 diff --git a/script/CreateMerkleStreamerLL.s.sol b/script/CreateMerkleLockupLL.s.sol similarity index 57% rename from script/CreateMerkleStreamerLL.s.sol rename to script/CreateMerkleLockupLL.s.sol index be5cd898..57fc23e1 100644 --- a/script/CreateMerkleStreamerLL.s.sol +++ b/script/CreateMerkleLockupLL.s.sol @@ -6,13 +6,13 @@ import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; import { BaseScript } from "./Base.s.sol"; -import { ISablierV2MerkleStreamerFactory } from "../src/interfaces/ISablierV2MerkleStreamerFactory.sol"; -import { ISablierV2MerkleStreamerLL } from "../src/interfaces/ISablierV2MerkleStreamerLL.sol"; -import { MerkleStreamer } from "../src/types/DataTypes.sol"; +import { ISablierV2MerkleLockupFactory } from "../src/interfaces/ISablierV2MerkleLockupFactory.sol"; +import { ISablierV2MerkleLockupLL } from "../src/interfaces/ISablierV2MerkleLockupLL.sol"; +import { MerkleLockup } from "../src/types/DataTypes.sol"; -contract CreateMerkleStreamerLL is BaseScript { +contract CreateMerkleLockupLL is BaseScript { struct Params { - MerkleStreamer.ConstructorParams baseParams; + MerkleLockup.ConstructorParams constructorParams; ISablierV2LockupLinear lockupLinear; LockupLinear.Durations streamDurations; string ipfsCID; @@ -21,15 +21,15 @@ contract CreateMerkleStreamerLL is BaseScript { } function run( - ISablierV2MerkleStreamerFactory merkleStreamerFactory, + ISablierV2MerkleLockupFactory merkleLockupFactory, Params calldata params ) public broadcast - returns (ISablierV2MerkleStreamerLL merkleStreamerLL) + returns (ISablierV2MerkleLockupLL merkleLockupLL) { - merkleStreamerLL = merkleStreamerFactory.createMerkleStreamerLL( - params.baseParams, + merkleLockupLL = merkleLockupFactory.createMerkleLockupLL( + params.constructorParams, params.lockupLinear, params.streamDurations, params.ipfsCID, diff --git a/script/DeployDeterministicPeriphery.s.sol b/script/DeployDeterministicPeriphery.s.sol index f5044c18..0720c382 100644 --- a/script/DeployDeterministicPeriphery.s.sol +++ b/script/DeployDeterministicPeriphery.s.sol @@ -4,12 +4,12 @@ pragma solidity >=0.8.22 <0.9.0; import { BaseScript } from "./Base.s.sol"; import { SablierV2Batch } from "../src/SablierV2Batch.sol"; -import { SablierV2MerkleStreamerFactory } from "../src/SablierV2MerkleStreamerFactory.sol"; +import { SablierV2MerkleLockupFactory } from "../src/SablierV2MerkleLockupFactory.sol"; /// @notice Deploys all V2 Periphery contracts at deterministic addresses across chains, in the following order: /// /// 1. {SablierV2Batch} -/// 2. {SablierV2MerkleStreamerFactory} +/// 2. {SablierV2MerkleLockupFactory} /// /// @dev Reverts if any contract has already been deployed. contract DeployDeterministicPeriphery is BaseScript { @@ -17,10 +17,10 @@ contract DeployDeterministicPeriphery is BaseScript { public virtual broadcast - returns (SablierV2Batch batch, SablierV2MerkleStreamerFactory merkleStreamerFactory) + returns (SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory) { bytes32 salt = constructCreate2Salt(); batch = new SablierV2Batch{ salt: salt }(); - merkleStreamerFactory = new SablierV2MerkleStreamerFactory{ salt: salt }(); + merkleLockupFactory = new SablierV2MerkleLockupFactory{ salt: salt }(); } } diff --git a/script/DeployMerkleLockupFactory.s.sol b/script/DeployMerkleLockupFactory.s.sol new file mode 100644 index 00000000..0b0864e7 --- /dev/null +++ b/script/DeployMerkleLockupFactory.s.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity >=0.8.22 <0.9.0; + +import { BaseScript } from "./Base.s.sol"; + +import { SablierV2MerkleLockupFactory } from "../src/SablierV2MerkleLockupFactory.sol"; + +contract DeployMerkleLockupFactory is BaseScript { + function run() public broadcast returns (SablierV2MerkleLockupFactory merkleLockupFactory) { + merkleLockupFactory = new SablierV2MerkleLockupFactory(); + } +} diff --git a/script/DeployMerkleStreamerFactory.s.sol b/script/DeployMerkleStreamerFactory.s.sol deleted file mode 100644 index ded39d4e..00000000 --- a/script/DeployMerkleStreamerFactory.s.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.22 <0.9.0; - -import { BaseScript } from "./Base.s.sol"; - -import { SablierV2MerkleStreamerFactory } from "../src/SablierV2MerkleStreamerFactory.sol"; - -contract DeployMerkleStreamerFactory is BaseScript { - function run() public broadcast returns (SablierV2MerkleStreamerFactory merkleStreamerFactory) { - merkleStreamerFactory = new SablierV2MerkleStreamerFactory(); - } -} diff --git a/script/DeployPeriphery.s.sol b/script/DeployPeriphery.s.sol index a771c41e..375d1099 100644 --- a/script/DeployPeriphery.s.sol +++ b/script/DeployPeriphery.s.sol @@ -3,20 +3,16 @@ pragma solidity >=0.8.22 <0.9.0; import { BaseScript } from "./Base.s.sol"; -import { SablierV2MerkleStreamerFactory } from "../src/SablierV2MerkleStreamerFactory.sol"; +import { SablierV2MerkleLockupFactory } from "../src/SablierV2MerkleLockupFactory.sol"; import { SablierV2Batch } from "../src/SablierV2Batch.sol"; /// @notice Deploys all V2 Periphery contract in the following order: /// /// 1. {SablierV2Batch} -/// 2. {SablierV2MerkleStreamerFactory} +/// 2. {SablierV2MerkleLockupFactory} contract DeployPeriphery is BaseScript { - function run() - public - broadcast - returns (SablierV2Batch batch, SablierV2MerkleStreamerFactory merkleStreamerFactory) - { + function run() public broadcast returns (SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory) { batch = new SablierV2Batch(); - merkleStreamerFactory = new SablierV2MerkleStreamerFactory(); + merkleLockupFactory = new SablierV2MerkleLockupFactory(); } } diff --git a/script/DeployProtocol.s.sol b/script/DeployProtocol.s.sol index 52cbd3e6..46f1ec6a 100644 --- a/script/DeployProtocol.s.sol +++ b/script/DeployProtocol.s.sol @@ -7,7 +7,7 @@ import { SablierV2LockupLinear } from "@sablier/v2-core/src/SablierV2LockupLinea import { SablierV2NFTDescriptor } from "@sablier/v2-core/src/SablierV2NFTDescriptor.sol"; import { BaseScript } from "./Base.s.sol"; -import { SablierV2MerkleStreamerFactory } from "../src/SablierV2MerkleStreamerFactory.sol"; +import { SablierV2MerkleLockupFactory } from "../src/SablierV2MerkleLockupFactory.sol"; import { SablierV2Batch } from "../src/SablierV2Batch.sol"; /// @notice Deploys the Sablier V2 Protocol. @@ -25,7 +25,7 @@ contract DeployProtocol is BaseScript { SablierV2LockupLinear lockupLinear, SablierV2NFTDescriptor nftDescriptor, SablierV2Batch batch, - SablierV2MerkleStreamerFactory merkleStreamerFactory + SablierV2MerkleLockupFactory merkleLockupFactory ) { // Deploy V2 Core. @@ -35,6 +35,6 @@ contract DeployProtocol is BaseScript { lockupLinear = new SablierV2LockupLinear(initialAdmin, comptroller, nftDescriptor); batch = new SablierV2Batch(); - merkleStreamerFactory = new SablierV2MerkleStreamerFactory(); + merkleLockupFactory = new SablierV2MerkleLockupFactory(); } } diff --git a/shell/prepare-artifacts.sh b/shell/prepare-artifacts.sh index e417fdd2..91fdb475 100755 --- a/shell/prepare-artifacts.sh +++ b/shell/prepare-artifacts.sh @@ -25,13 +25,13 @@ FOUNDRY_PROFILE=optimized forge build # Copy the production artifacts cp out-optimized/SablierV2Batch.sol/SablierV2Batch.json $artifacts -cp out-optimized/SablierV2MerkleStreamerFactory.sol/SablierV2MerkleStreamerFactory.json $artifacts -cp out-optimized/SablierV2MerkleStreamerLL.sol/SablierV2MerkleStreamerLL.json $artifacts +cp out-optimized/SablierV2MerkleLockupFactory.sol/SablierV2MerkleLockupFactory.json $artifacts +cp out-optimized/SablierV2MerkleLockupLL.sol/SablierV2MerkleLockupLL.json $artifacts interfaces=./artifacts/interfaces cp out-optimized/ISablierV2Batch.sol/ISablierV2Batch.json $interfaces -cp out-optimized/ISablierV2MerkleStreamerFactory.sol/ISablierV2MerkleStreamerFactory.json $interfaces -cp out-optimized/ISablierV2MerkleStreamerLL.sol/ISablierV2MerkleStreamerLL.json $interfaces +cp out-optimized/ISablierV2MerkleLockupFactory.sol/ISablierV2MerkleLockupFactory.json $interfaces +cp out-optimized/ISablierV2MerkleLockupLL.sol/ISablierV2MerkleLockupLL.json $interfaces erc20=./artifacts/interfaces/erc20 cp out-optimized/IERC20.sol/IERC20.json $erc20 diff --git a/shell/update-precompiles.sh b/shell/update-precompiles.sh index b2a9602a..0842169e 100755 --- a/shell/update-precompiles.sh +++ b/shell/update-precompiles.sh @@ -13,7 +13,7 @@ FOUNDRY_PROFILE=optimized forge build # Retrieve the raw bytecodes, removing the "0x" prefix batch=$(cat out-optimized/SablierV2Batch.sol/SablierV2Batch.json | jq -r '.bytecode.object' | cut -c 3-) -merkle_streamer_factory=$(cat out-optimized/SablierV2MerkleStreamerFactory.sol/SablierV2MerkleStreamerFactory.json | jq -r '.bytecode.object' | cut -c 3-) +merkle_lockup_factory=$(cat out-optimized/SablierV2MerkleLockupFactory.sol/SablierV2MerkleLockupFactory.json | jq -r '.bytecode.object' | cut -c 3-) precompiles_path="test/utils/Precompiles.sol" if [ ! -f $precompiles_path ]; then @@ -23,7 +23,7 @@ fi # Replace the current bytecodes sd "(BYTECODE_BATCH =)[^;]+;" "\$1 hex\"$batch\";" $precompiles_path -sd "(BYTECODE_MERKLE_STREAMER_FACTORY =)[^;]+;" "\$1 hex\"$merkle_streamer_factory\";" $precompiles_path +sd "(BYTECODE_MERKLE_LOCKUP_FACTORY =)[^;]+;" "\$1 hex\"$merkle_lockup_factory\";" $precompiles_path # Reformat the code with Forge forge fmt $precompiles_path diff --git a/src/SablierV2MerkleStreamerFactory.sol b/src/SablierV2MerkleLockupFactory.sol similarity index 55% rename from src/SablierV2MerkleStreamerFactory.sol rename to src/SablierV2MerkleLockupFactory.sol index 7c3097a0..a3ced2b0 100644 --- a/src/SablierV2MerkleStreamerFactory.sol +++ b/src/SablierV2MerkleLockupFactory.sol @@ -4,21 +4,21 @@ pragma solidity >=0.8.22; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; -import { SablierV2MerkleStreamerLL } from "./SablierV2MerkleStreamerLL.sol"; -import { ISablierV2MerkleStreamerFactory } from "./interfaces/ISablierV2MerkleStreamerFactory.sol"; -import { ISablierV2MerkleStreamerLL } from "./interfaces/ISablierV2MerkleStreamerLL.sol"; -import { MerkleStreamer } from "./types/DataTypes.sol"; +import { SablierV2MerkleLockupLL } from "./SablierV2MerkleLockupLL.sol"; +import { ISablierV2MerkleLockupFactory } from "./interfaces/ISablierV2MerkleLockupFactory.sol"; +import { ISablierV2MerkleLockupLL } from "./interfaces/ISablierV2MerkleLockupLL.sol"; +import { MerkleLockup } from "./types/DataTypes.sol"; -/// @title SablierV2MerkleStreamerFactory -/// @notice See the documentation in {ISablierV2MerkleStreamerFactory}. -contract SablierV2MerkleStreamerFactory is ISablierV2MerkleStreamerFactory { +/// @title SablierV2MerkleLockupFactory +/// @notice See the documentation in {ISablierV2MerkleLockupFactory}. +contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { /*////////////////////////////////////////////////////////////////////////// USER-FACING NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @notice inheritdoc ISablierV2MerkleStreamerFactory - function createMerkleStreamerLL( - MerkleStreamer.ConstructorParams memory baseParams, + /// @notice inheritdoc ISablierV2MerkleLockupFactory + function createMerkleLockupLL( + MerkleLockup.ConstructorParams memory baseParams, ISablierV2LockupLinear lockupLinear, LockupLinear.Durations memory streamDurations, string memory ipfsCID, @@ -26,7 +26,7 @@ contract SablierV2MerkleStreamerFactory is ISablierV2MerkleStreamerFactory { uint256 recipientsCount ) external - returns (ISablierV2MerkleStreamerLL merkleStreamerLL) + returns (ISablierV2MerkleLockupLL merkleLockupLL) { // Hash the parameters to generate a salt. bytes32 salt = keccak256( @@ -43,12 +43,12 @@ contract SablierV2MerkleStreamerFactory is ISablierV2MerkleStreamerFactory { ) ); - // Deploy the Merkle streamer with CREATE2. - merkleStreamerLL = new SablierV2MerkleStreamerLL{ salt: salt }(baseParams, lockupLinear, streamDurations); + // Deploy the Merkle Lockup contract with CREATE2. + merkleLockupLL = new SablierV2MerkleLockupLL{ salt: salt }(baseParams, lockupLinear, streamDurations); // Using a different function to emit the event to avoid stack too deep error. - emit CreateMerkleStreamerLL( - merkleStreamerLL, baseParams, lockupLinear, streamDurations, ipfsCID, aggregateAmount, recipientsCount + emit CreateMerkleLockupLL( + merkleLockupLL, baseParams, lockupLinear, streamDurations, ipfsCID, aggregateAmount, recipientsCount ); } } diff --git a/src/SablierV2MerkleStreamerLL.sol b/src/SablierV2MerkleLockupLL.sol similarity index 80% rename from src/SablierV2MerkleStreamerLL.sol rename to src/SablierV2MerkleLockupLL.sol index 4abad1bf..3046612c 100644 --- a/src/SablierV2MerkleStreamerLL.sol +++ b/src/SablierV2MerkleLockupLL.sol @@ -8,15 +8,15 @@ import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablier import { Broker, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; import { ud } from "@prb/math/src/UD60x18.sol"; -import { SablierV2MerkleStreamer } from "./abstracts/SablierV2MerkleStreamer.sol"; -import { ISablierV2MerkleStreamerLL } from "./interfaces/ISablierV2MerkleStreamerLL.sol"; -import { MerkleStreamer } from "./types/DataTypes.sol"; +import { SablierV2MerkleLockup } from "./abstracts/SablierV2MerkleLockup.sol"; +import { ISablierV2MerkleLockupLL } from "./interfaces/ISablierV2MerkleLockupLL.sol"; +import { MerkleLockup } from "./types/DataTypes.sol"; -/// @title SablierV2MerkleStreamerLL -/// @notice See the documentation in {ISablierV2MerkleStreamerLL}. -contract SablierV2MerkleStreamerLL is - ISablierV2MerkleStreamerLL, // 2 inherited components - SablierV2MerkleStreamer // 4 inherited components +/// @title SablierV2MerkleLockupLL +/// @notice See the documentation in {ISablierV2MerkleLockupLL}. +contract SablierV2MerkleLockupLL is + ISablierV2MerkleLockupLL, // 2 inherited components + SablierV2MerkleLockup // 4 inherited components { using BitMaps for BitMaps.BitMap; using SafeERC20 for IERC20; @@ -25,10 +25,10 @@ contract SablierV2MerkleStreamerLL is STATE VARIABLES //////////////////////////////////////////////////////////////////////////*/ - /// @inheritdoc ISablierV2MerkleStreamerLL + /// @inheritdoc ISablierV2MerkleLockupLL ISablierV2LockupLinear public immutable override LOCKUP_LINEAR; - /// @inheritdoc ISablierV2MerkleStreamerLL + /// @inheritdoc ISablierV2MerkleLockupLL LockupLinear.Durations public override streamDurations; /*////////////////////////////////////////////////////////////////////////// @@ -38,16 +38,16 @@ contract SablierV2MerkleStreamerLL is /// @dev Constructs the contract by initializing the immutable state variables, and max approving the Sablier /// contract. constructor( - MerkleStreamer.ConstructorParams memory baseParams, + MerkleLockup.ConstructorParams memory baseParams, ISablierV2LockupLinear lockupLinear, LockupLinear.Durations memory streamDurations_ ) - SablierV2MerkleStreamer(baseParams) + SablierV2MerkleLockup(baseParams) { LOCKUP_LINEAR = lockupLinear; streamDurations = streamDurations_; - // Max approve the Sablier contract to spend funds from the Merkle streamer. + // Max approve the Sablier contract to spend funds from the Merkle Lockup contract. ASSET.forceApprove(address(LOCKUP_LINEAR), type(uint256).max); } @@ -55,7 +55,7 @@ contract SablierV2MerkleStreamerLL is USER-FACING NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @inheritdoc ISablierV2MerkleStreamerLL + /// @inheritdoc ISablierV2MerkleLockupLL function claim( uint256 index, address recipient, diff --git a/src/abstracts/SablierV2MerkleStreamer.sol b/src/abstracts/SablierV2MerkleLockup.sol similarity index 76% rename from src/abstracts/SablierV2MerkleStreamer.sol rename to src/abstracts/SablierV2MerkleLockup.sol index a25b3bba..16c27ce6 100644 --- a/src/abstracts/SablierV2MerkleStreamer.sol +++ b/src/abstracts/SablierV2MerkleLockup.sol @@ -7,14 +7,14 @@ import { MerkleProof } from "@openzeppelin/contracts/utils/cryptography/MerklePr import { BitMaps } from "@openzeppelin/contracts/utils/structs/BitMaps.sol"; import { Adminable } from "@sablier/v2-core/src/abstracts/Adminable.sol"; -import { ISablierV2MerkleStreamer } from "../interfaces/ISablierV2MerkleStreamer.sol"; -import { MerkleStreamer } from "../types/DataTypes.sol"; +import { ISablierV2MerkleLockup } from "../interfaces/ISablierV2MerkleLockup.sol"; +import { MerkleLockup } from "../types/DataTypes.sol"; import { Errors } from "../libraries/Errors.sol"; -/// @title SablierV2MerkleStreamer -/// @notice See the documentation in {ISablierV2MerkleStreamer}. -abstract contract SablierV2MerkleStreamer is - ISablierV2MerkleStreamer, // 2 inherited component +/// @title SablierV2MerkleLockup +/// @notice See the documentation in {ISablierV2MerkleLockup}. +abstract contract SablierV2MerkleLockup is + ISablierV2MerkleLockup, // 2 inherited component Adminable // 1 inherited component { using BitMaps for BitMaps.BitMap; @@ -24,22 +24,22 @@ abstract contract SablierV2MerkleStreamer is STATE VARIABLES //////////////////////////////////////////////////////////////////////////*/ - /// @inheritdoc ISablierV2MerkleStreamer + /// @inheritdoc ISablierV2MerkleLockup IERC20 public immutable override ASSET; - /// @inheritdoc ISablierV2MerkleStreamer + /// @inheritdoc ISablierV2MerkleLockup bool public immutable override CANCELABLE; - /// @inheritdoc ISablierV2MerkleStreamer + /// @inheritdoc ISablierV2MerkleLockup uint40 public immutable override EXPIRATION; - /// @inheritdoc ISablierV2MerkleStreamer + /// @inheritdoc ISablierV2MerkleLockup bytes32 public immutable override MERKLE_ROOT; /// @dev The name of the campaign stored as bytes32. bytes32 internal immutable NAME; - /// @inheritdoc ISablierV2MerkleStreamer + /// @inheritdoc ISablierV2MerkleLockup bool public immutable override TRANSFERABLE; /// @dev Packed booleans that record the history of claims. @@ -50,10 +50,10 @@ abstract contract SablierV2MerkleStreamer is //////////////////////////////////////////////////////////////////////////*/ /// @dev Constructs the contract by initializing the immutable state variables. - constructor(MerkleStreamer.ConstructorParams memory params) { + constructor(MerkleLockup.ConstructorParams memory params) { // Checks: the campaign name is not greater than 32 bytes if (bytes(params.name).length > 32) { - revert Errors.SablierV2MerkleStreamer_CampaignNameTooLong({ + revert Errors.SablierV2MerkleLockup_CampaignNameTooLong({ nameLength: bytes(params.name).length, maxLength: 32 }); @@ -72,17 +72,17 @@ abstract contract SablierV2MerkleStreamer is USER-FACING CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @inheritdoc ISablierV2MerkleStreamer + /// @inheritdoc ISablierV2MerkleLockup function hasClaimed(uint256 index) public view override returns (bool) { return _claimedBitMap.get(index); } - /// @inheritdoc ISablierV2MerkleStreamer + /// @inheritdoc ISablierV2MerkleLockup function hasExpired() public view override returns (bool) { return EXPIRATION > 0 && EXPIRATION <= block.timestamp; } - /// @inheritdoc ISablierV2MerkleStreamer + /// @inheritdoc ISablierV2MerkleLockup function name() external view override returns (string memory) { return string(abi.encodePacked(NAME)); } @@ -91,11 +91,11 @@ abstract contract SablierV2MerkleStreamer is USER-FACING NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @inheritdoc ISablierV2MerkleStreamer + /// @inheritdoc ISablierV2MerkleLockup function clawback(address to, uint128 amount) external override onlyAdmin { // Checks: the campaign is not expired. if (!hasExpired()) { - revert Errors.SablierV2MerkleStreamer_CampaignNotExpired({ + revert Errors.SablierV2MerkleLockup_CampaignNotExpired({ currentTime: block.timestamp, expiration: EXPIRATION }); @@ -116,20 +116,17 @@ abstract contract SablierV2MerkleStreamer is function _checkClaim(uint256 index, bytes32 leaf, bytes32[] calldata merkleProof) internal view { // Checks: the campaign has not expired. if (hasExpired()) { - revert Errors.SablierV2MerkleStreamer_CampaignExpired({ - currentTime: block.timestamp, - expiration: EXPIRATION - }); + revert Errors.SablierV2MerkleLockup_CampaignExpired({ currentTime: block.timestamp, expiration: EXPIRATION }); } // Checks: the index has not been claimed. if (_claimedBitMap.get(index)) { - revert Errors.SablierV2MerkleStreamer_StreamClaimed(index); + revert Errors.SablierV2MerkleLockup_StreamClaimed(index); } // Checks: the input claim is included in the Merkle tree. if (!MerkleProof.verify(merkleProof, MERKLE_ROOT, leaf)) { - revert Errors.SablierV2MerkleStreamer_InvalidProof(); + revert Errors.SablierV2MerkleLockup_InvalidProof(); } } } diff --git a/src/interfaces/ISablierV2MerkleStreamer.sol b/src/interfaces/ISablierV2MerkleLockup.sol similarity index 88% rename from src/interfaces/ISablierV2MerkleStreamer.sol rename to src/interfaces/ISablierV2MerkleLockup.sol index 0079e149..b5ee5414 100644 --- a/src/interfaces/ISablierV2MerkleStreamer.sol +++ b/src/interfaces/ISablierV2MerkleLockup.sol @@ -4,13 +4,13 @@ pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IAdminable } from "@sablier/v2-core/src/interfaces/IAdminable.sol"; -/// @title ISablierV2MerkleStreamer +/// @title ISablierV2MerkleLockup /// @notice A contract that lets user claim Sablier streams using Merkle proofs. An interesting use case for /// MerkleStream is airstreams, which is a portmanteau of "airdrop" and "stream". This is an airdrop model where the /// tokens are distributed over time, as opposed to all at once. -/// @dev This is the base interface for MerkleStreamer contracts. See the Sablier docs for more guidance on how +/// @dev This is the base interface for MerkleLockup contracts. See the Sablier docs for more guidance on how /// streaming works: https://docs.sablier.com/. -interface ISablierV2MerkleStreamer is IAdminable { +interface ISablierV2MerkleLockup is IAdminable { /*////////////////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////////////////*/ @@ -33,7 +33,7 @@ interface ISablierV2MerkleStreamer is IAdminable { /// @dev This is an immutable state variable. function CANCELABLE() external returns (bool); - /// @notice The cut-off point for the Merkle streamer, as a Unix timestamp. A value of zero means there + /// @notice The cut-off point for the Merkle Lockup contract, as a Unix timestamp. A value of zero means there /// is no expiration. /// @dev This is an immutable state variable. function EXPIRATION() external returns (uint40); @@ -46,7 +46,7 @@ interface ISablierV2MerkleStreamer is IAdminable { /// @param index The index of the recipient to check. function hasClaimed(uint256 index) external returns (bool); - /// @notice Returns a flag indicating whether the Merkle streamer has expired. + /// @notice Returns a flag indicating whether the campaign has expired. function hasExpired() external view returns (bool); /// @notice The root of the Merkle tree used to validate the claims. @@ -61,7 +61,7 @@ interface ISablierV2MerkleStreamer is IAdminable { NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Claws back the unclaimed tokens from the Merkle streamer. + /// @notice Claws back the unclaimed tokens from the Merkle Lockup. /// /// @dev Emits a {Clawback} event. /// diff --git a/src/interfaces/ISablierV2MerkleStreamerFactory.sol b/src/interfaces/ISablierV2MerkleLockupFactory.sol similarity index 62% rename from src/interfaces/ISablierV2MerkleStreamerFactory.sol rename to src/interfaces/ISablierV2MerkleLockupFactory.sol index 46deefaa..ba55bb8c 100644 --- a/src/interfaces/ISablierV2MerkleStreamerFactory.sol +++ b/src/interfaces/ISablierV2MerkleLockupFactory.sol @@ -4,20 +4,20 @@ pragma solidity >=0.8.22; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { ISablierV2MerkleStreamerLL } from "./ISablierV2MerkleStreamerLL.sol"; -import { MerkleStreamer } from "../types/DataTypes.sol"; +import { ISablierV2MerkleLockupLL } from "./ISablierV2MerkleLockupLL.sol"; +import { MerkleLockup } from "../types/DataTypes.sol"; -/// @title ISablierV2MerkleStreamerFactory -/// @notice Deploys new Lockup Linear Merkle streamers via CREATE2. -interface ISablierV2MerkleStreamerFactory { +/// @title ISablierV2MerkleLockupFactory +/// @notice Deploys new Lockup Linear Merkle lockups via CREATE2. +interface ISablierV2MerkleLockupFactory { /*////////////////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Emitted when a Sablier V2 Lockup Linear Merkle streamer is created. - event CreateMerkleStreamerLL( - ISablierV2MerkleStreamerLL indexed merkleStreamerLL, - MerkleStreamer.ConstructorParams indexed baseParams, + /// @notice Emitted when a Sablier V2 Lockup Linear Merkle Lockup is created. + event CreateMerkleLockupLL( + ISablierV2MerkleLockupLL indexed merkleLockupLL, + MerkleLockup.ConstructorParams indexed baseParams, ISablierV2LockupLinear lockupLinear, LockupLinear.Durations streamDurations, string ipfsCID, @@ -29,18 +29,18 @@ interface ISablierV2MerkleStreamerFactory { NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Creates a new Merkle streamer that uses Lockup Linear. - /// @dev Emits a {CreateMerkleStreamerLL} event. - /// @param baseParams Struct encapsulating the {SablierV2MerkleStreamer} parameters, which are documented in + /// @notice Creates a new Merkle Lockup that uses Lockup Linear. + /// @dev Emits a {CreateMerkleLockupLL} event. + /// @param baseParams Struct encapsulating the {SablierV2MerkleLockup} parameters, which are documented in /// {DataTypes}. /// @param lockupLinear The address of the {SablierV2LockupLinear} contract. /// @param streamDurations The durations for each stream due to the recipient. /// @param ipfsCID Metadata parameter emitted for indexing purposes. /// @param aggregateAmount Total amount of ERC-20 assets to be streamed to all recipients. /// @param recipientsCount Total number of recipients eligible to claim. - /// @return merkleStreamerLL The address of the newly created Merkle streamer contract. - function createMerkleStreamerLL( - MerkleStreamer.ConstructorParams memory baseParams, + /// @return merkleLockupLL The address of the newly created Merkle Lockup contract. + function createMerkleLockupLL( + MerkleLockup.ConstructorParams memory baseParams, ISablierV2LockupLinear lockupLinear, LockupLinear.Durations memory streamDurations, string memory ipfsCID, @@ -48,5 +48,5 @@ interface ISablierV2MerkleStreamerFactory { uint256 recipientsCount ) external - returns (ISablierV2MerkleStreamerLL merkleStreamerLL); + returns (ISablierV2MerkleLockupLL merkleLockupLL); } diff --git a/src/interfaces/ISablierV2MerkleStreamerLL.sol b/src/interfaces/ISablierV2MerkleLockupLL.sol similarity index 87% rename from src/interfaces/ISablierV2MerkleStreamerLL.sol rename to src/interfaces/ISablierV2MerkleLockupLL.sol index d69cbd01..f901ec5d 100644 --- a/src/interfaces/ISablierV2MerkleStreamerLL.sol +++ b/src/interfaces/ISablierV2MerkleLockupLL.sol @@ -3,11 +3,11 @@ pragma solidity >=0.8.22; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; -import { ISablierV2MerkleStreamer } from "./ISablierV2MerkleStreamer.sol"; +import { ISablierV2MerkleLockup } from "./ISablierV2MerkleLockup.sol"; -/// @title ISablierV2MerkleStreamerLL -/// @notice Merkle streamer that creates Lockup Linear streams. -interface ISablierV2MerkleStreamerLL is ISablierV2MerkleStreamer { +/// @title ISablierV2MerkleLockupLL +/// @notice Merkle Lockup that creates Lockup Linear streams. +interface ISablierV2MerkleLockupLL is ISablierV2MerkleLockup { /*////////////////////////////////////////////////////////////////////////// CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index 1c343584..0477df58 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -11,21 +11,21 @@ library Errors { error SablierV2Batch_BatchSizeZero(); /*////////////////////////////////////////////////////////////////////////// - SABLIER-V2-MERKLE-STREAMER + SABLIER-V2-MERKLE-LOCKUP //////////////////////////////////////////////////////////////////////////*/ /// @notice Thrown when trying to claim after the campaign has expired. - error SablierV2MerkleStreamer_CampaignExpired(uint256 currentTime, uint40 expiration); + error SablierV2MerkleLockup_CampaignExpired(uint256 currentTime, uint40 expiration); /// @notice Thrown when trying to create a campaign with a name that is too long. - error SablierV2MerkleStreamer_CampaignNameTooLong(uint256 nameLength, uint256 maxLength); + error SablierV2MerkleLockup_CampaignNameTooLong(uint256 nameLength, uint256 maxLength); /// @notice Thrown when trying to clawback when the campaign has not expired. - error SablierV2MerkleStreamer_CampaignNotExpired(uint256 currentTime, uint40 expiration); + error SablierV2MerkleLockup_CampaignNotExpired(uint256 currentTime, uint40 expiration); /// @notice Thrown when trying to claim with an invalid Merkle proof. - error SablierV2MerkleStreamer_InvalidProof(); + error SablierV2MerkleLockup_InvalidProof(); /// @notice Thrown when trying to claim the same stream more than once. - error SablierV2MerkleStreamer_StreamClaimed(uint256 index); + error SablierV2MerkleLockup_StreamClaimed(uint256 index); } diff --git a/src/types/DataTypes.sol b/src/types/DataTypes.sol index 02549065..f6d5a4a2 100644 --- a/src/types/DataTypes.sol +++ b/src/types/DataTypes.sol @@ -62,11 +62,11 @@ library Batch { } } -library MerkleStreamer { - /// @notice Struct encapsulating the base constructor parameter of a {SablierV2MerkleStreamer} contract. - /// @param initialAdmin The initial admin of the Merkle streamer contract. +library MerkleLockup { + /// @notice Struct encapsulating the base constructor parameter of a {SablierV2MerkleLockup} contract. + /// @param initialAdmin The initial admin of the Merkle Lockup contract. /// @param asset The address of the streamed ERC-20 asset. - /// @param name The name of the Merkle streamer contract. + /// @param name The name of the campaign. /// @param merkleRoot The Merkle root of the claim data. /// @param expiration The expiration of the streaming campaign, as a Unix timestamp. /// @param cancelable Indicates if each stream will be cancelable. diff --git a/test/Base.t.sol b/test/Base.t.sol index 792277e6..84909168 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -14,11 +14,11 @@ import { Assertions as V2CoreAssertions } from "@sablier/v2-core/test/utils/Asse import { Utils as V2CoreUtils } from "@sablier/v2-core/test/utils/Utils.sol"; import { ISablierV2Batch } from "src/interfaces/ISablierV2Batch.sol"; -import { ISablierV2MerkleStreamerFactory } from "src/interfaces/ISablierV2MerkleStreamerFactory.sol"; -import { ISablierV2MerkleStreamerLL } from "src/interfaces/ISablierV2MerkleStreamerLL.sol"; +import { ISablierV2MerkleLockupFactory } from "src/interfaces/ISablierV2MerkleLockupFactory.sol"; +import { ISablierV2MerkleLockupLL } from "src/interfaces/ISablierV2MerkleLockupLL.sol"; import { SablierV2Batch } from "src/SablierV2Batch.sol"; -import { SablierV2MerkleStreamerFactory } from "src/SablierV2MerkleStreamerFactory.sol"; -import { SablierV2MerkleStreamerLL } from "src/SablierV2MerkleStreamerLL.sol"; +import { SablierV2MerkleLockupFactory } from "src/SablierV2MerkleLockupFactory.sol"; +import { SablierV2MerkleLockupLL } from "src/SablierV2MerkleLockupLL.sol"; import { Defaults } from "./utils/Defaults.sol"; import { DeployOptimized } from "./utils/DeployOptimized.sol"; @@ -44,8 +44,8 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions Defaults internal defaults; ISablierV2LockupDynamic internal lockupDynamic; ISablierV2LockupLinear internal lockupLinear; - ISablierV2MerkleStreamerFactory internal merkleStreamerFactory; - ISablierV2MerkleStreamerLL internal merkleStreamerLL; + ISablierV2MerkleLockupFactory internal merkleLockupFactory; + ISablierV2MerkleLockupLL internal merkleLockupLL; /*////////////////////////////////////////////////////////////////////////// SET-UP FUNCTION @@ -90,17 +90,17 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions function deployPeripheryConditionally() internal { if (!isTestOptimizedProfile()) { batch = new SablierV2Batch(); - merkleStreamerFactory = new SablierV2MerkleStreamerFactory(); + merkleLockupFactory = new SablierV2MerkleLockupFactory(); } else { - (batch, merkleStreamerFactory) = deployOptimizedPeriphery(); + (batch, merkleLockupFactory) = deployOptimizedPeriphery(); } } /// @dev Labels the most relevant contracts. function labelContracts() internal { vm.label({ account: address(asset), newLabel: IERC20Metadata(address(asset)).symbol() }); - vm.label({ account: address(merkleStreamerFactory), newLabel: "MerkleStreamerFactory" }); - vm.label({ account: address(merkleStreamerLL), newLabel: "MerkleStreamerLL" }); + vm.label({ account: address(merkleLockupFactory), newLabel: "MerkleLockupFactory" }); + vm.label({ account: address(merkleLockupLL), newLabel: "MerkleLockupLL" }); vm.label({ account: address(defaults), newLabel: "Defaults" }); vm.label({ account: address(comptroller), newLabel: "Comptroller" }); vm.label({ account: address(lockupDynamic), newLabel: "LockupDynamic" }); @@ -247,10 +247,10 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions } /*////////////////////////////////////////////////////////////////////////// - MERKLE-STREAMER + MERKLE-LOCKUP //////////////////////////////////////////////////////////////////////////*/ - function computeMerkleStreamerLLAddress( + function computeMerkleLockupLLAddress( address admin, bytes32 merkleRoot, uint40 expiration @@ -271,15 +271,15 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions abi.encode(defaults.durations()) ) ); - bytes32 creationBytecodeHash = keccak256(getMerkleStreamerLLBytecode(admin, merkleRoot, expiration)); + bytes32 creationBytecodeHash = keccak256(getMerkleLockupLLBytecode(admin, merkleRoot, expiration)); return computeCreate2Address({ salt: salt, initcodeHash: creationBytecodeHash, - deployer: address(merkleStreamerFactory) + deployer: address(merkleLockupFactory) }); } - function getMerkleStreamerLLBytecode( + function getMerkleLockupLLBytecode( address admin, bytes32 merkleRoot, uint40 expiration @@ -290,11 +290,10 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions bytes memory constructorArgs = abi.encode(defaults.baseParams(admin, merkleRoot, expiration), lockupLinear, defaults.durations()); if (!isTestOptimizedProfile()) { - return bytes.concat(type(SablierV2MerkleStreamerLL).creationCode, constructorArgs); + return bytes.concat(type(SablierV2MerkleLockupLL).creationCode, constructorArgs); } else { return bytes.concat( - vm.getCode("out-optimized/SablierV2MerkleStreamerLL.sol/SablierV2MerkleStreamerLL.json"), - constructorArgs + vm.getCode("out-optimized/SablierV2MerkleLockupLL.sol/SablierV2MerkleLockupLL.json"), constructorArgs ); } } diff --git a/test/fork/assets/USDC.t.sol b/test/fork/assets/USDC.t.sol index ef356d0c..1d58e000 100644 --- a/test/fork/assets/USDC.t.sol +++ b/test/fork/assets/USDC.t.sol @@ -5,7 +5,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { CreateWithTimestamps_LockupDynamic_Batch_Fork_Test } from "../batch/createWithTimestampsLD.t.sol"; import { CreateWithTimestamps_LockupLinear_Batch_Fork_Test } from "../batch/createWithTimestampsLL.t.sol"; -import { MerkleStreamerLL_Fork_Test } from "../merkle-streamer/MerkleStreamerLL.t.sol"; +import { MerkleLockupLL_Fork_Test } from "../merkle-lockup/MerkleLockupLL.t.sol"; IERC20 constant usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); @@ -17,4 +17,4 @@ contract USDC_CreateWithTimestamps_LockupLinear_Batch_Fork_Test is CreateWithTimestamps_LockupLinear_Batch_Fork_Test(usdc) { } -contract USDC_MerkleStreamerLL_Fork_Test is MerkleStreamerLL_Fork_Test(usdc) { } +contract USDC_MerkleLockupLL_Fork_Test is MerkleLockupLL_Fork_Test(usdc) { } diff --git a/test/fork/assets/USDT.t.sol b/test/fork/assets/USDT.t.sol index 4b03564a..5be49fce 100644 --- a/test/fork/assets/USDT.t.sol +++ b/test/fork/assets/USDT.t.sol @@ -5,7 +5,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { CreateWithTimestamps_LockupDynamic_Batch_Fork_Test } from "../batch/createWithTimestampsLD.t.sol"; import { CreateWithTimestamps_LockupLinear_Batch_Fork_Test } from "../batch/createWithTimestampsLL.t.sol"; -import { MerkleStreamerLL_Fork_Test } from "../merkle-streamer/MerkleStreamerLL.t.sol"; +import { MerkleLockupLL_Fork_Test } from "../merkle-lockup/MerkleLockupLL.t.sol"; IERC20 constant usdt = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7); @@ -17,4 +17,4 @@ contract USDT_CreateWithTimestamps_LockupLinear_Batch_Fork_Test is CreateWithTimestamps_LockupLinear_Batch_Fork_Test(usdt) { } -contract USDT_MerkleStreamerLL_Fork_Test is MerkleStreamerLL_Fork_Test(usdt) { } +contract USDT_MerkleLockupLL_Fork_Test is MerkleLockupLL_Fork_Test(usdt) { } diff --git a/test/fork/merkle-streamer/MerkleStreamerLL.t.sol b/test/fork/merkle-lockup/MerkleLockupLL.t.sol similarity index 80% rename from test/fork/merkle-streamer/MerkleStreamerLL.t.sol rename to test/fork/merkle-lockup/MerkleLockupLL.t.sol index c2741211..3f81fd2b 100644 --- a/test/fork/merkle-streamer/MerkleStreamerLL.t.sol +++ b/test/fork/merkle-lockup/MerkleLockupLL.t.sol @@ -5,13 +5,13 @@ import { Arrays } from "@openzeppelin/contracts/utils/Arrays.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { Lockup, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { ISablierV2MerkleStreamerLL } from "src/interfaces/ISablierV2MerkleStreamerLL.sol"; -import { MerkleStreamer } from "src/types/DataTypes.sol"; +import { ISablierV2MerkleLockupLL } from "src/interfaces/ISablierV2MerkleLockupLL.sol"; +import { MerkleLockup } from "src/types/DataTypes.sol"; import { MerkleBuilder } from "../../utils/MerkleBuilder.sol"; import { Fork_Test } from "../Fork.t.sol"; -abstract contract MerkleStreamerLL_Fork_Test is Fork_Test { +abstract contract MerkleLockupLL_Fork_Test is Fork_Test { using MerkleBuilder for uint256[]; constructor(IERC20 asset_) Fork_Test(asset_) { } @@ -40,14 +40,14 @@ abstract contract MerkleStreamerLL_Fork_Test is Fork_Test { uint128[] amounts; uint256 aggregateAmount; uint128 clawbackAmount; - address expectedStreamerLL; - MerkleStreamer.ConstructorParams baseParams; + address expectedLockupLL; + MerkleLockup.ConstructorParams baseParams; LockupLinear.Stream expectedStream; uint256 expectedStreamId; uint256[] indexes; uint256 leafPos; uint256 leafToClaim; - ISablierV2MerkleStreamerLL merkleStreamerLL; + ISablierV2MerkleLockupLL merkleLockupLL; bytes32 merkleRoot; address[] recipients; uint256 recipientsCount; @@ -56,7 +56,7 @@ abstract contract MerkleStreamerLL_Fork_Test is Fork_Test { // We need the leaves as a storage variable so that we can use OpenZeppelin's {Arrays.findUpperBound}. uint256[] public leaves; - function testForkFuzz_MerkleStreamerLL(Params memory params) external { + function testForkFuzz_MerkleLockupLL(Params memory params) external { vm.assume(params.admin != address(0) && params.admin != users.admin); vm.assume(params.expiration == 0 || params.expiration > block.timestamp); vm.assume(params.leafData.length > 1); @@ -93,14 +93,14 @@ abstract contract MerkleStreamerLL_Fork_Test is Fork_Test { MerkleBuilder.sortLeaves(leaves); vars.merkleRoot = getRoot(leaves.toBytes32()); - vars.expectedStreamerLL = computeMerkleStreamerLLAddress(params.admin, vars.merkleRoot, params.expiration); + vars.expectedLockupLL = computeMerkleLockupLLAddress(params.admin, vars.merkleRoot, params.expiration); vars.baseParams = defaults.baseParams({ admin: params.admin, merkleRoot: vars.merkleRoot, expiration: params.expiration }); - vm.expectEmit({ emitter: address(merkleStreamerFactory) }); - emit CreateMerkleStreamerLL({ - merkleStreamerLL: ISablierV2MerkleStreamerLL(vars.expectedStreamerLL), + vm.expectEmit({ emitter: address(merkleLockupFactory) }); + emit CreateMerkleLockupLL({ + merkleLockupLL: ISablierV2MerkleLockupLL(vars.expectedLockupLL), baseParams: vars.baseParams, lockupLinear: lockupLinear, streamDurations: defaults.durations(), @@ -109,7 +109,7 @@ abstract contract MerkleStreamerLL_Fork_Test is Fork_Test { recipientsCount: vars.recipientsCount }); - vars.merkleStreamerLL = merkleStreamerFactory.createMerkleStreamerLL({ + vars.merkleLockupLL = merkleLockupFactory.createMerkleLockupLL({ baseParams: vars.baseParams, lockupLinear: lockupLinear, streamDurations: defaults.durations(), @@ -118,21 +118,21 @@ abstract contract MerkleStreamerLL_Fork_Test is Fork_Test { recipientsCount: vars.recipientsCount }); - // Fund the Merkle streamer. - deal({ token: address(asset), to: address(vars.merkleStreamerLL), give: vars.aggregateAmount }); + // Fund the Merkle Lockup contract. + deal({ token: address(asset), to: address(vars.merkleLockupLL), give: vars.aggregateAmount }); - assertGt(address(vars.merkleStreamerLL).code.length, 0, "MerkleStreamerLL contract not created"); + assertGt(address(vars.merkleLockupLL).code.length, 0, "MerkleLockupLL contract not created"); assertEq( - address(vars.merkleStreamerLL), - vars.expectedStreamerLL, - "MerkleStreamerLL contract does not match computed address" + address(vars.merkleLockupLL), + vars.expectedLockupLL, + "MerkleLockupLL contract does not match computed address" ); /*////////////////////////////////////////////////////////////////////////// CLAIM //////////////////////////////////////////////////////////////////////////*/ - assertFalse(vars.merkleStreamerLL.hasClaimed(vars.indexes[params.posBeforeSort])); + assertFalse(vars.merkleLockupLL.hasClaimed(vars.indexes[params.posBeforeSort])); vars.leafToClaim = MerkleBuilder.computeLeaf( vars.indexes[params.posBeforeSort], @@ -148,7 +148,7 @@ abstract contract MerkleStreamerLL_Fork_Test is Fork_Test { vars.amounts[params.posBeforeSort], vars.expectedStreamId ); - vars.actualStreamId = vars.merkleStreamerLL.claim({ + vars.actualStreamId = vars.merkleLockupLL.claim({ index: vars.indexes[params.posBeforeSort], recipient: vars.recipients[params.posBeforeSort], amount: vars.amounts[params.posBeforeSort], @@ -170,7 +170,7 @@ abstract contract MerkleStreamerLL_Fork_Test is Fork_Test { wasCanceled: false }); - assertTrue(vars.merkleStreamerLL.hasClaimed(vars.indexes[params.posBeforeSort])); + assertTrue(vars.merkleLockupLL.hasClaimed(vars.indexes[params.posBeforeSort])); assertEq(vars.actualStreamId, vars.expectedStreamId); assertEq(vars.actualStream, vars.expectedStream); @@ -179,14 +179,14 @@ abstract contract MerkleStreamerLL_Fork_Test is Fork_Test { //////////////////////////////////////////////////////////////////////////*/ if (params.expiration > 0) { - vars.clawbackAmount = uint128(asset.balanceOf(address(vars.merkleStreamerLL))); + vars.clawbackAmount = uint128(asset.balanceOf(address(vars.merkleLockupLL))); vm.warp({ timestamp: uint256(params.expiration) + 1 seconds }); changePrank({ msgSender: params.admin }); expectCallToTransfer({ to: params.admin, amount: vars.clawbackAmount }); - vm.expectEmit({ emitter: address(vars.merkleStreamerLL) }); + vm.expectEmit({ emitter: address(vars.merkleLockupLL) }); emit Clawback({ to: params.admin, admin: params.admin, amount: vars.clawbackAmount }); - vars.merkleStreamerLL.clawback({ to: params.admin, amount: vars.clawbackAmount }); + vars.merkleLockupLL.clawback({ to: params.admin, amount: vars.clawbackAmount }); } } } diff --git a/test/integration/merkle-lockup/MerkleLockup.t.sol b/test/integration/merkle-lockup/MerkleLockup.t.sol new file mode 100644 index 00000000..16a2c43b --- /dev/null +++ b/test/integration/merkle-lockup/MerkleLockup.t.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22; + +import { ISablierV2MerkleLockupLL } from "src/interfaces/ISablierV2MerkleLockupLL.sol"; + +import { Integration_Test } from "../Integration.t.sol"; + +abstract contract MerkleLockup_Integration_Test is Integration_Test { + function setUp() public virtual override { + Integration_Test.setUp(); + + // Create the default Merkle Lockup. + merkleLockupLL = createMerkleLockupLL(); + + // Fund the Merkle Lockup contract. + deal({ token: address(asset), to: address(merkleLockupLL), give: defaults.AGGREGATE_AMOUNT() }); + } + + function claimLL() internal returns (uint256) { + return merkleLockupLL.claim({ + index: defaults.INDEX1(), + recipient: users.recipient1, + amount: defaults.CLAIM_AMOUNT(), + merkleProof: defaults.index1Proof() + }); + } + + function computeMerkleLockupLLAddress() internal returns (address) { + return computeMerkleLockupLLAddress(users.admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); + } + + function computeMerkleLockupLLAddress(address admin) internal returns (address) { + return computeMerkleLockupLLAddress(admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); + } + + function computeMerkleLockupLLAddress(address admin, uint40 expiration) internal returns (address) { + return computeMerkleLockupLLAddress(admin, defaults.MERKLE_ROOT(), expiration); + } + + function computeMerkleLockupLLAddress(address admin, bytes32 merkleRoot) internal returns (address) { + return computeMerkleLockupLLAddress(admin, merkleRoot, defaults.EXPIRATION()); + } + + function createMerkleLockupLL() internal returns (ISablierV2MerkleLockupLL) { + return createMerkleLockupLL(users.admin, defaults.EXPIRATION()); + } + + function createMerkleLockupLL(address admin) internal returns (ISablierV2MerkleLockupLL) { + return createMerkleLockupLL(admin, defaults.EXPIRATION()); + } + + function createMerkleLockupLL(uint40 expiration) internal returns (ISablierV2MerkleLockupLL) { + return createMerkleLockupLL(users.admin, expiration); + } + + function createMerkleLockupLL(address admin, uint40 expiration) internal returns (ISablierV2MerkleLockupLL) { + return merkleLockupFactory.createMerkleLockupLL({ + baseParams: defaults.baseParams(admin, defaults.MERKLE_ROOT(), expiration), + lockupLinear: lockupLinear, + streamDurations: defaults.durations(), + ipfsCID: defaults.IPFS_CID(), + aggregateAmount: defaults.AGGREGATE_AMOUNT(), + recipientsCount: defaults.RECIPIENTS_COUNT() + }); + } +} diff --git a/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.t.sol b/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol similarity index 56% rename from test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.t.sol rename to test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol index 936cc02f..60f35400 100644 --- a/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.t.sol +++ b/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol @@ -4,18 +4,18 @@ pragma solidity >=0.8.22 <0.9.0; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; import { Errors } from "src/libraries/Errors.sol"; -import { ISablierV2MerkleStreamerLL } from "src/interfaces/ISablierV2MerkleStreamerLL.sol"; -import { MerkleStreamer } from "src/types/DataTypes.sol"; +import { ISablierV2MerkleLockupLL } from "src/interfaces/ISablierV2MerkleLockupLL.sol"; +import { MerkleLockup } from "src/types/DataTypes.sol"; -import { MerkleStreamer_Integration_Test } from "../../MerkleStreamer.t.sol"; +import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; -contract CreateMerkleStreamerLL_Integration_Test is MerkleStreamer_Integration_Test { +contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test { function setUp() public override { - MerkleStreamer_Integration_Test.setUp(); + MerkleLockup_Integration_Test.setUp(); } function test_RevertWhen_CampaignNameTooLong() external { - MerkleStreamer.ConstructorParams memory baseParams = defaults.baseParams(); + MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); LockupLinear.Durations memory streamDurations = defaults.durations(); string memory ipfsCID = defaults.IPFS_CID(); uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); @@ -25,11 +25,11 @@ contract CreateMerkleStreamerLL_Integration_Test is MerkleStreamer_Integration_T vm.expectRevert( abi.encodeWithSelector( - Errors.SablierV2MerkleStreamer_CampaignNameTooLong.selector, bytes(baseParams.name).length, 32 + Errors.SablierV2MerkleLockup_CampaignNameTooLong.selector, bytes(baseParams.name).length, 32 ) ); - merkleStreamerFactory.createMerkleStreamerLL({ + merkleLockupFactory.createMerkleLockupLL({ baseParams: baseParams, lockupLinear: lockupLinear, streamDurations: streamDurations, @@ -43,16 +43,16 @@ contract CreateMerkleStreamerLL_Integration_Test is MerkleStreamer_Integration_T _; } - /// @dev This test works because a default Merkle streamer is deployed in {Integration_Test.setUp} - function test_RevertGiven_AlreadyDeployed() external whenCampaignNameIsNotTooLong { - MerkleStreamer.ConstructorParams memory baseParams = defaults.baseParams(); + /// @dev This test works because a default Merkle Lockup contract is deployed in {Integration_Test.setUp} + function test_RevertGiven_AlreadyCreated() external whenCampaignNameIsNotTooLong { + MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); LockupLinear.Durations memory streamDurations = defaults.durations(); string memory ipfsCID = defaults.IPFS_CID(); uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); uint256 recipientsCount = defaults.RECIPIENTS_COUNT(); vm.expectRevert(); - merkleStreamerFactory.createMerkleStreamerLL({ + merkleLockupFactory.createMerkleLockupLL({ baseParams: baseParams, lockupLinear: lockupLinear, streamDurations: streamDurations, @@ -62,27 +62,27 @@ contract CreateMerkleStreamerLL_Integration_Test is MerkleStreamer_Integration_T }); } - modifier givenNotAlreadyDeployed() { + modifier givenNotAlreadyCreated() { _; } - function testFuzz_CreateMerkleStreamerLL( + function testFuzz_CreateMerkleLockupLL( address admin, uint40 expiration ) external - givenNotAlreadyDeployed whenCampaignNameIsNotTooLong + givenNotAlreadyCreated { vm.assume(admin != users.admin); - address expectedStreamerLL = computeMerkleStreamerLLAddress(admin, expiration); + address expectedLockupLL = computeMerkleLockupLLAddress(admin, expiration); - MerkleStreamer.ConstructorParams memory baseParams = + MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams({ admin: admin, merkleRoot: defaults.MERKLE_ROOT(), expiration: expiration }); - vm.expectEmit({ emitter: address(merkleStreamerFactory) }); - emit CreateMerkleStreamerLL({ - merkleStreamerLL: ISablierV2MerkleStreamerLL(expectedStreamerLL), + vm.expectEmit({ emitter: address(merkleLockupFactory) }); + emit CreateMerkleLockupLL({ + merkleLockupLL: ISablierV2MerkleLockupLL(expectedLockupLL), baseParams: baseParams, lockupLinear: lockupLinear, streamDurations: defaults.durations(), @@ -91,9 +91,9 @@ contract CreateMerkleStreamerLL_Integration_Test is MerkleStreamer_Integration_T recipientsCount: defaults.RECIPIENTS_COUNT() }); - address actualStreamerLL = address(createMerkleStreamerLL(admin, expiration)); + address actualLockupLL = address(createMerkleLockupLL(admin, expiration)); - assertGt(actualStreamerLL.code.length, 0, "MerkleStreamerLL contract not created"); - assertEq(actualStreamerLL, expectedStreamerLL, "MerkleStreamerLL contract does not match computed address"); + assertGt(actualLockupLL.code.length, 0, "MerkleLockupLL contract not created"); + assertEq(actualLockupLL, expectedLockupLL, "MerkleLockupLL contract does not match computed address"); } } diff --git a/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.tree b/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.tree new file mode 100644 index 00000000..82eaa983 --- /dev/null +++ b/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.tree @@ -0,0 +1,9 @@ +createMerkleLockupLL.t.sol +├── when the campaign name is too long +│ └── it should revert +└── when the campaign name is not too long + ├── given the campaign has been already created + │ └── it should revert + └── given the campaign has not been already created + ├── it should create the campaign + └── it should emit a {CreateMerkleLockupLL} event diff --git a/test/integration/merkle-streamer/ll/claim/claim.t.sol b/test/integration/merkle-lockup/ll/claim/claim.t.sol similarity index 79% rename from test/integration/merkle-streamer/ll/claim/claim.t.sol rename to test/integration/merkle-lockup/ll/claim/claim.t.sol index d2404229..c9543ce2 100644 --- a/test/integration/merkle-streamer/ll/claim/claim.t.sol +++ b/test/integration/merkle-lockup/ll/claim/claim.t.sol @@ -6,11 +6,11 @@ import { ud, UD60x18 } from "@prb/math/src/UD60x18.sol"; import { Errors } from "src/libraries/Errors.sol"; -import { MerkleStreamer_Integration_Test } from "../../MerkleStreamer.t.sol"; +import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; -contract Claim_Integration_Test is MerkleStreamer_Integration_Test { +contract Claim_Integration_Test is MerkleLockup_Integration_Test { function setUp() public virtual override { - MerkleStreamer_Integration_Test.setUp(); + MerkleLockup_Integration_Test.setUp(); } function test_RevertGiven_CampaignExpired() external { @@ -19,9 +19,9 @@ contract Claim_Integration_Test is MerkleStreamer_Integration_Test { bytes32[] memory merkleProof; vm.warp({ timestamp: warpTime }); vm.expectRevert( - abi.encodeWithSelector(Errors.SablierV2MerkleStreamer_CampaignExpired.selector, warpTime, expiration) + abi.encodeWithSelector(Errors.SablierV2MerkleLockup_CampaignExpired.selector, warpTime, expiration) ); - merkleStreamerLL.claim({ index: 1, recipient: users.recipient1, amount: 1, merkleProof: merkleProof }); + merkleLockupLL.claim({ index: 1, recipient: users.recipient1, amount: 1, merkleProof: merkleProof }); } modifier givenCampaignNotExpired() { @@ -33,8 +33,8 @@ contract Claim_Integration_Test is MerkleStreamer_Integration_Test { uint256 index1 = defaults.INDEX1(); uint128 amount = defaults.CLAIM_AMOUNT(); bytes32[] memory merkleProof = defaults.index1Proof(); - vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleStreamer_StreamClaimed.selector, index1)); - merkleStreamerLL.claim(index1, users.recipient1, amount, merkleProof); + vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_StreamClaimed.selector, index1)); + merkleLockupLL.claim(index1, users.recipient1, amount, merkleProof); } modifier givenNotClaimed() { @@ -54,8 +54,8 @@ contract Claim_Integration_Test is MerkleStreamer_Integration_Test { uint256 invalidIndex = 1337; uint128 amount = defaults.CLAIM_AMOUNT(); bytes32[] memory merkleProof = defaults.index1Proof(); - vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleStreamer_InvalidProof.selector)); - merkleStreamerLL.claim(invalidIndex, users.recipient1, amount, merkleProof); + vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_InvalidProof.selector)); + merkleLockupLL.claim(invalidIndex, users.recipient1, amount, merkleProof); } function test_RevertWhen_InvalidRecipient() @@ -68,8 +68,8 @@ contract Claim_Integration_Test is MerkleStreamer_Integration_Test { address invalidRecipient = address(1337); uint128 amount = defaults.CLAIM_AMOUNT(); bytes32[] memory merkleProof = defaults.index1Proof(); - vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleStreamer_InvalidProof.selector)); - merkleStreamerLL.claim(index1, invalidRecipient, amount, merkleProof); + vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_InvalidProof.selector)); + merkleLockupLL.claim(index1, invalidRecipient, amount, merkleProof); } function test_RevertWhen_InvalidAmount() @@ -81,8 +81,8 @@ contract Claim_Integration_Test is MerkleStreamer_Integration_Test { uint256 index1 = defaults.INDEX1(); uint128 invalidAmount = 1337; bytes32[] memory merkleProof = defaults.index1Proof(); - vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleStreamer_InvalidProof.selector)); - merkleStreamerLL.claim(index1, users.recipient1, invalidAmount, merkleProof); + vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_InvalidProof.selector)); + merkleLockupLL.claim(index1, users.recipient1, invalidAmount, merkleProof); } function test_RevertWhen_InvalidMerkleProof() @@ -94,8 +94,8 @@ contract Claim_Integration_Test is MerkleStreamer_Integration_Test { uint256 index1 = defaults.INDEX1(); uint128 amount = defaults.CLAIM_AMOUNT(); bytes32[] memory invalidMerkleProof = defaults.index2Proof(); - vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleStreamer_InvalidProof.selector)); - merkleStreamerLL.claim(index1, users.recipient1, amount, invalidMerkleProof); + vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_InvalidProof.selector)); + merkleLockupLL.claim(index1, users.recipient1, amount, invalidMerkleProof); } modifier givenIncludedInMerkleTree() { @@ -132,7 +132,7 @@ contract Claim_Integration_Test is MerkleStreamer_Integration_Test { uint256 expectedStreamId = lockupLinear.nextStreamId(); uint128 feeAmount = uint128(ud(defaults.CLAIM_AMOUNT()).mul(protocolFee).intoUint256()); - vm.expectEmit({ emitter: address(merkleStreamerLL) }); + vm.expectEmit({ emitter: address(merkleLockupLL) }); emit Claim(defaults.INDEX1(), users.recipient1, defaults.CLAIM_AMOUNT(), expectedStreamId); uint256 actualStreamId = claimLL(); @@ -151,7 +151,7 @@ contract Claim_Integration_Test is MerkleStreamer_Integration_Test { wasCanceled: false }); - assertTrue(merkleStreamerLL.hasClaimed(defaults.INDEX1()), "not claimed"); + assertTrue(merkleLockupLL.hasClaimed(defaults.INDEX1()), "not claimed"); assertEq(actualStreamId, expectedStreamId, "invalid stream id"); assertEq(actualStream, expectedStream); } diff --git a/test/integration/merkle-streamer/ll/claim/claim.tree b/test/integration/merkle-lockup/ll/claim/claim.tree similarity index 100% rename from test/integration/merkle-streamer/ll/claim/claim.tree rename to test/integration/merkle-lockup/ll/claim/claim.tree diff --git a/test/integration/merkle-streamer/ll/clawback/clawback.t.sol b/test/integration/merkle-lockup/ll/clawback/clawback.t.sol similarity index 71% rename from test/integration/merkle-streamer/ll/clawback/clawback.t.sol rename to test/integration/merkle-lockup/ll/clawback/clawback.t.sol index fdaea6e9..1ea18bed 100644 --- a/test/integration/merkle-streamer/ll/clawback/clawback.t.sol +++ b/test/integration/merkle-lockup/ll/clawback/clawback.t.sol @@ -5,17 +5,17 @@ import { Errors as V2CoreErrors } from "@sablier/v2-core/src/libraries/Errors.so import { Errors } from "src/libraries/Errors.sol"; -import { MerkleStreamer_Integration_Test } from "../../MerkleStreamer.t.sol"; +import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; -contract Clawback_Integration_Test is MerkleStreamer_Integration_Test { +contract Clawback_Integration_Test is MerkleLockup_Integration_Test { function setUp() public virtual override { - MerkleStreamer_Integration_Test.setUp(); + MerkleLockup_Integration_Test.setUp(); } function test_RevertWhen_CallerNotAdmin() external { changePrank({ msgSender: users.eve }); vm.expectRevert(abi.encodeWithSelector(V2CoreErrors.CallerNotAdmin.selector, users.admin, users.eve)); - merkleStreamerLL.clawback({ to: users.eve, amount: 1 }); + merkleLockupLL.clawback({ to: users.eve, amount: 1 }); } modifier whenCallerAdmin() { @@ -26,10 +26,10 @@ contract Clawback_Integration_Test is MerkleStreamer_Integration_Test { function test_RevertGiven_CampaignNotExpired() external whenCallerAdmin { vm.expectRevert( abi.encodeWithSelector( - Errors.SablierV2MerkleStreamer_CampaignNotExpired.selector, block.timestamp, defaults.EXPIRATION() + Errors.SablierV2MerkleLockup_CampaignNotExpired.selector, block.timestamp, defaults.EXPIRATION() ) ); - merkleStreamerLL.clawback({ to: users.admin, amount: 1 }); + merkleLockupLL.clawback({ to: users.admin, amount: 1 }); } modifier givenCampaignExpired() { @@ -49,10 +49,10 @@ contract Clawback_Integration_Test is MerkleStreamer_Integration_Test { } function test_Clawback(address to) internal { - uint128 clawbackAmount = uint128(asset.balanceOf(address(merkleStreamerLL))); + uint128 clawbackAmount = uint128(asset.balanceOf(address(merkleLockupLL))); expectCallToTransfer({ to: to, amount: clawbackAmount }); - vm.expectEmit({ emitter: address(merkleStreamerLL) }); + vm.expectEmit({ emitter: address(merkleLockupLL) }); emit Clawback({ admin: users.admin, to: to, amount: clawbackAmount }); - merkleStreamerLL.clawback({ to: to, amount: clawbackAmount }); + merkleLockupLL.clawback({ to: to, amount: clawbackAmount }); } } diff --git a/test/integration/merkle-streamer/ll/clawback/clawback.tree b/test/integration/merkle-lockup/ll/clawback/clawback.tree similarity index 100% rename from test/integration/merkle-streamer/ll/clawback/clawback.tree rename to test/integration/merkle-lockup/ll/clawback/clawback.tree diff --git a/test/integration/merkle-streamer/ll/constructor/constructor.t.sol b/test/integration/merkle-lockup/ll/constructor/constructor.t.sol similarity index 71% rename from test/integration/merkle-streamer/ll/constructor/constructor.t.sol rename to test/integration/merkle-lockup/ll/constructor/constructor.t.sol index 2f0d3ea2..2d96f94c 100644 --- a/test/integration/merkle-streamer/ll/constructor/constructor.t.sol +++ b/test/integration/merkle-lockup/ll/constructor/constructor.t.sol @@ -3,11 +3,11 @@ pragma solidity >=0.8.22 <0.9.0; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { SablierV2MerkleStreamerLL } from "src/SablierV2MerkleStreamerLL.sol"; +import { SablierV2MerkleLockupLL } from "src/SablierV2MerkleLockupLL.sol"; -import { MerkleStreamer_Integration_Test } from "../../MerkleStreamer.t.sol"; +import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; -contract Constructor_MerkleStreamerLL_Integration_Test is MerkleStreamer_Integration_Test { +contract Constructor_MerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test { /// @dev Needed to prevent "Stack too deep" error struct Vars { address actualAdmin; @@ -33,49 +33,49 @@ contract Constructor_MerkleStreamerLL_Integration_Test is MerkleStreamer_Integra } function test_Constructor() external { - SablierV2MerkleStreamerLL constructedStreamerLL = - new SablierV2MerkleStreamerLL(defaults.baseParams(), lockupLinear, defaults.durations()); + SablierV2MerkleLockupLL constructedLockupLL = + new SablierV2MerkleLockupLL(defaults.baseParams(), lockupLinear, defaults.durations()); Vars memory vars; - vars.actualAdmin = constructedStreamerLL.admin(); + vars.actualAdmin = constructedLockupLL.admin(); vars.expectedAdmin = users.admin; assertEq(vars.actualAdmin, vars.expectedAdmin, "admin"); - vars.actualAsset = address(constructedStreamerLL.ASSET()); + vars.actualAsset = address(constructedLockupLL.ASSET()); vars.expectedAsset = address(asset); assertEq(vars.actualAsset, vars.expectedAsset, "asset"); - vars.actualName = constructedStreamerLL.name(); + vars.actualName = constructedLockupLL.name(); vars.expectedName = defaults.NAME_BYTES32(); assertEq(bytes32(abi.encodePacked(vars.actualName)), vars.expectedName, "name"); - vars.actualMerkleRoot = constructedStreamerLL.MERKLE_ROOT(); + vars.actualMerkleRoot = constructedLockupLL.MERKLE_ROOT(); vars.expectedMerkleRoot = defaults.MERKLE_ROOT(); assertEq(vars.actualMerkleRoot, vars.expectedMerkleRoot, "merkleRoot"); - vars.actualCancelable = constructedStreamerLL.CANCELABLE(); + vars.actualCancelable = constructedLockupLL.CANCELABLE(); vars.expectedCancelable = defaults.CANCELABLE(); assertEq(vars.actualCancelable, vars.expectedCancelable, "cancelable"); - vars.actualTransferable = constructedStreamerLL.TRANSFERABLE(); + vars.actualTransferable = constructedLockupLL.TRANSFERABLE(); vars.expectedTransferable = defaults.TRANSFERABLE(); assertEq(vars.actualTransferable, vars.expectedTransferable, "transferable"); - vars.actualExpiration = constructedStreamerLL.EXPIRATION(); + vars.actualExpiration = constructedLockupLL.EXPIRATION(); vars.expectedExpiration = defaults.EXPIRATION(); assertEq(vars.actualExpiration, vars.expectedExpiration, "expiration"); - vars.actualLockupLinear = address(constructedStreamerLL.LOCKUP_LINEAR()); + vars.actualLockupLinear = address(constructedLockupLL.LOCKUP_LINEAR()); vars.expectedLockupLinear = address(lockupLinear); assertEq(vars.actualLockupLinear, vars.expectedLockupLinear, "lockupLinear"); - (vars.actualDurations.cliff, vars.actualDurations.total) = constructedStreamerLL.streamDurations(); + (vars.actualDurations.cliff, vars.actualDurations.total) = constructedLockupLL.streamDurations(); vars.expectedDurations = defaults.durations(); assertEq(vars.actualDurations.cliff, vars.expectedDurations.cliff, "durations.cliff"); assertEq(vars.actualDurations.total, vars.expectedDurations.total, "durations.total"); - vars.actualAllowance = asset.allowance(address(constructedStreamerLL), address(lockupLinear)); + vars.actualAllowance = asset.allowance(address(constructedLockupLL), address(lockupLinear)); vars.expectedAllowance = MAX_UINT256; assertEq(vars.actualAllowance, vars.expectedAllowance, "allowance"); } diff --git a/test/integration/merkle-streamer/ll/has-claimed/hasClaimed.t.sol b/test/integration/merkle-lockup/ll/has-claimed/hasClaimed.t.sol similarity index 54% rename from test/integration/merkle-streamer/ll/has-claimed/hasClaimed.t.sol rename to test/integration/merkle-lockup/ll/has-claimed/hasClaimed.t.sol index 6802efed..e7aafaef 100644 --- a/test/integration/merkle-streamer/ll/has-claimed/hasClaimed.t.sol +++ b/test/integration/merkle-lockup/ll/has-claimed/hasClaimed.t.sol @@ -1,16 +1,16 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.22 <0.9.0; -import { MerkleStreamer_Integration_Test } from "../../MerkleStreamer.t.sol"; +import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; -contract HasClaimed_Integration_Test is MerkleStreamer_Integration_Test { +contract HasClaimed_Integration_Test is MerkleLockup_Integration_Test { function setUp() public virtual override { - MerkleStreamer_Integration_Test.setUp(); + MerkleLockup_Integration_Test.setUp(); } function test_HasClaimed_IndexNotInTree() external { uint256 indexNotInTree = 1337e18; - assertFalse(merkleStreamerLL.hasClaimed(indexNotInTree), "claimed"); + assertFalse(merkleLockupLL.hasClaimed(indexNotInTree), "claimed"); } modifier whenIndexInTree() { @@ -18,7 +18,7 @@ contract HasClaimed_Integration_Test is MerkleStreamer_Integration_Test { } function test_HasClaimed_NotClaimed() external whenIndexInTree { - assertFalse(merkleStreamerLL.hasClaimed(defaults.INDEX1()), "claimed"); + assertFalse(merkleLockupLL.hasClaimed(defaults.INDEX1()), "claimed"); } modifier givenRecipientHasClaimed() { @@ -27,6 +27,6 @@ contract HasClaimed_Integration_Test is MerkleStreamer_Integration_Test { } function test_HasClaimed() external whenIndexInTree givenRecipientHasClaimed { - assertTrue(merkleStreamerLL.hasClaimed(defaults.INDEX1()), "not claimed"); + assertTrue(merkleLockupLL.hasClaimed(defaults.INDEX1()), "not claimed"); } } diff --git a/test/integration/merkle-streamer/ll/has-claimed/hasClaimed.tree b/test/integration/merkle-lockup/ll/has-claimed/hasClaimed.tree similarity index 100% rename from test/integration/merkle-streamer/ll/has-claimed/hasClaimed.tree rename to test/integration/merkle-lockup/ll/has-claimed/hasClaimed.tree diff --git a/test/integration/merkle-streamer/ll/has-expired/hasExpired.t.sol b/test/integration/merkle-lockup/ll/has-expired/hasExpired.t.sol similarity index 50% rename from test/integration/merkle-streamer/ll/has-expired/hasExpired.t.sol rename to test/integration/merkle-lockup/ll/has-expired/hasExpired.t.sol index 6ce7b363..c185e38f 100644 --- a/test/integration/merkle-streamer/ll/has-expired/hasExpired.t.sol +++ b/test/integration/merkle-lockup/ll/has-expired/hasExpired.t.sol @@ -1,18 +1,18 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.22 <0.9.0; -import { ISablierV2MerkleStreamerLL } from "src/interfaces/ISablierV2MerkleStreamerLL.sol"; +import { ISablierV2MerkleLockupLL } from "src/interfaces/ISablierV2MerkleLockupLL.sol"; -import { MerkleStreamer_Integration_Test } from "../../MerkleStreamer.t.sol"; +import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; -contract HasExpired_Integration_Test is MerkleStreamer_Integration_Test { +contract HasExpired_Integration_Test is MerkleLockup_Integration_Test { function setUp() public virtual override { - MerkleStreamer_Integration_Test.setUp(); + MerkleLockup_Integration_Test.setUp(); } function test_HasExpired_ExpirationZero() external { - ISablierV2MerkleStreamerLL testStreamer = createMerkleStreamerLL({ expiration: 0 }); - assertFalse(testStreamer.hasExpired(), "campaign expired"); + ISablierV2MerkleLockupLL testLockup = createMerkleLockupLL({ expiration: 0 }); + assertFalse(testLockup.hasExpired(), "campaign expired"); } modifier whenExpirationNotZero() { @@ -20,16 +20,16 @@ contract HasExpired_Integration_Test is MerkleStreamer_Integration_Test { } function test_HasExpired_ExpirationLessThanCurrentTime() external whenExpirationNotZero { - assertFalse(merkleStreamerLL.hasExpired(), "campaign expired"); + assertFalse(merkleLockupLL.hasExpired(), "campaign expired"); } function test_HasExpired_ExpirationEqualToCurrentTime() external whenExpirationNotZero { vm.warp({ timestamp: defaults.EXPIRATION() }); - assertTrue(merkleStreamerLL.hasExpired(), "campaign not expired"); + assertTrue(merkleLockupLL.hasExpired(), "campaign not expired"); } function test_HasExpired_ExpirationGreaterThanCurrentTime() external whenExpirationNotZero { vm.warp({ timestamp: defaults.EXPIRATION() + 1 seconds }); - assertTrue(merkleStreamerLL.hasExpired(), "campaign not expired"); + assertTrue(merkleLockupLL.hasExpired(), "campaign not expired"); } } diff --git a/test/integration/merkle-streamer/ll/has-expired/hasExpired.tree b/test/integration/merkle-lockup/ll/has-expired/hasExpired.tree similarity index 100% rename from test/integration/merkle-streamer/ll/has-expired/hasExpired.tree rename to test/integration/merkle-lockup/ll/has-expired/hasExpired.tree diff --git a/test/integration/merkle-streamer/MerkleStreamer.t.sol b/test/integration/merkle-streamer/MerkleStreamer.t.sol deleted file mode 100644 index 63adb001..00000000 --- a/test/integration/merkle-streamer/MerkleStreamer.t.sol +++ /dev/null @@ -1,66 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.22; - -import { ISablierV2MerkleStreamerLL } from "src/interfaces/ISablierV2MerkleStreamerLL.sol"; - -import { Integration_Test } from "../Integration.t.sol"; - -abstract contract MerkleStreamer_Integration_Test is Integration_Test { - function setUp() public virtual override { - Integration_Test.setUp(); - - // Create the default Merkle streamer. - merkleStreamerLL = createMerkleStreamerLL(); - - // Fund the Merkle streamer. - deal({ token: address(asset), to: address(merkleStreamerLL), give: defaults.AGGREGATE_AMOUNT() }); - } - - function claimLL() internal returns (uint256) { - return merkleStreamerLL.claim({ - index: defaults.INDEX1(), - recipient: users.recipient1, - amount: defaults.CLAIM_AMOUNT(), - merkleProof: defaults.index1Proof() - }); - } - - function computeMerkleStreamerLLAddress() internal returns (address) { - return computeMerkleStreamerLLAddress(users.admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); - } - - function computeMerkleStreamerLLAddress(address admin) internal returns (address) { - return computeMerkleStreamerLLAddress(admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); - } - - function computeMerkleStreamerLLAddress(address admin, uint40 expiration) internal returns (address) { - return computeMerkleStreamerLLAddress(admin, defaults.MERKLE_ROOT(), expiration); - } - - function computeMerkleStreamerLLAddress(address admin, bytes32 merkleRoot) internal returns (address) { - return computeMerkleStreamerLLAddress(admin, merkleRoot, defaults.EXPIRATION()); - } - - function createMerkleStreamerLL() internal returns (ISablierV2MerkleStreamerLL) { - return createMerkleStreamerLL(users.admin, defaults.EXPIRATION()); - } - - function createMerkleStreamerLL(address admin) internal returns (ISablierV2MerkleStreamerLL) { - return createMerkleStreamerLL(admin, defaults.EXPIRATION()); - } - - function createMerkleStreamerLL(uint40 expiration) internal returns (ISablierV2MerkleStreamerLL) { - return createMerkleStreamerLL(users.admin, expiration); - } - - function createMerkleStreamerLL(address admin, uint40 expiration) internal returns (ISablierV2MerkleStreamerLL) { - return merkleStreamerFactory.createMerkleStreamerLL({ - baseParams: defaults.baseParams(admin, defaults.MERKLE_ROOT(), expiration), - lockupLinear: lockupLinear, - streamDurations: defaults.durations(), - ipfsCID: defaults.IPFS_CID(), - aggregateAmount: defaults.AGGREGATE_AMOUNT(), - recipientsCount: defaults.RECIPIENTS_COUNT() - }); - } -} diff --git a/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.tree b/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.tree deleted file mode 100644 index 0ca44455..00000000 --- a/test/integration/merkle-streamer/factory/create-merkle-streamer-ll/createMerkleStreamerLL.tree +++ /dev/null @@ -1,9 +0,0 @@ -createMerkleStreamerLL.t.sol -├── when the campaign name is too long -│ └── it should revert -└── when the campaign name is not too long - ├── given the Merkle streamer has been deployed with CREATE2 - │ └── it should revert - └── given the Merkle streamer has not been deployed - ├── it should deploy the Merkle streamer - └── it should emit a {CreateMerkleStreamerLL} event diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index bb9e395f..3d951f30 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -7,7 +7,7 @@ import { ud2x18 } from "@prb/math/src/UD2x18.sol"; import { UD60x18 } from "@prb/math/src/UD60x18.sol"; import { Broker, LockupDynamic, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { Batch, MerkleStreamer } from "src/types/DataTypes.sol"; +import { Batch, MerkleLockup } from "src/types/DataTypes.sol"; import { ArrayBuilder } from "./ArrayBuilder.sol"; import { BatchBuilder } from "./BatchBuilder.sol"; @@ -39,7 +39,7 @@ contract Defaults is Merkle { uint128 public constant WITHDRAW_AMOUNT = 2500e18; /*////////////////////////////////////////////////////////////////////////// - MERKLE-STREAMER + MERKLE-LOCKUP //////////////////////////////////////////////////////////////////////////*/ uint256 public constant AGGREGATE_AMOUNT = CLAIM_AMOUNT * RECIPIENTS_COUNT; @@ -89,7 +89,7 @@ contract Defaults is Merkle { } /*////////////////////////////////////////////////////////////////////////// - MERKLE-STREAMER + MERKLE-LOCKUP //////////////////////////////////////////////////////////////////////////*/ function index1Proof() public view returns (bytes32[] memory) { @@ -116,7 +116,7 @@ contract Defaults is Merkle { return getProof(LEAVES.toBytes32(), pos); } - function baseParams() public view returns (MerkleStreamer.ConstructorParams memory) { + function baseParams() public view returns (MerkleLockup.ConstructorParams memory) { return baseParams(users.admin, MERKLE_ROOT, EXPIRATION); } @@ -127,9 +127,9 @@ contract Defaults is Merkle { ) public view - returns (MerkleStreamer.ConstructorParams memory) + returns (MerkleLockup.ConstructorParams memory) { - return MerkleStreamer.ConstructorParams({ + return MerkleLockup.ConstructorParams({ initialAdmin: admin, asset: asset, name: NAME, diff --git a/test/utils/DeployOptimized.sol b/test/utils/DeployOptimized.sol index 46b0ae52..068a9ded 100644 --- a/test/utils/DeployOptimized.sol +++ b/test/utils/DeployOptimized.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.22 <0.9.0; import { StdCheats } from "forge-std/src/StdCheats.sol"; import { ISablierV2Batch } from "../../src/interfaces/ISablierV2Batch.sol"; -import { ISablierV2MerkleStreamerFactory } from "../../src/interfaces/ISablierV2MerkleStreamerFactory.sol"; +import { ISablierV2MerkleLockupFactory } from "../../src/interfaces/ISablierV2MerkleLockupFactory.sol"; abstract contract DeployOptimized is StdCheats { /// @dev Deploys {SablierV2Batch} from an optimized source compiled with `--via-ir`. @@ -12,18 +12,18 @@ abstract contract DeployOptimized is StdCheats { return ISablierV2Batch(deployCode("out-optimized/SablierV2Batch.sol/SablierV2Batch.json")); } - /// @dev Deploys {SablierV2MerkleStreamerFactory} from an optimized source compiled with `--via-ir`. - function deployOptimizedMerkleStreamerFactory() internal returns (ISablierV2MerkleStreamerFactory) { - return ISablierV2MerkleStreamerFactory( - deployCode("out-optimized/SablierV2MerkleStreamerFactory.sol/SablierV2MerkleStreamerFactory.json") + /// @dev Deploys {SablierV2MerkleLockupFactory} from an optimized source compiled with `--via-ir`. + function deployOptimizedMerkleLockupFactory() internal returns (ISablierV2MerkleLockupFactory) { + return ISablierV2MerkleLockupFactory( + deployCode("out-optimized/SablierV2MerkleLockupFactory.sol/SablierV2MerkleLockupFactory.json") ); } /// @notice Deploys all V2 Periphery contracts from a optimized source in the following order: /// /// 1. {SablierV2Batch} - /// 2. {SablierV2MerkleStreamerFactory} - function deployOptimizedPeriphery() internal returns (ISablierV2Batch, ISablierV2MerkleStreamerFactory) { - return (deployOptimizedBatch(), deployOptimizedMerkleStreamerFactory()); + /// 2. {SablierV2MerkleLockupFactory} + function deployOptimizedPeriphery() internal returns (ISablierV2Batch, ISablierV2MerkleLockupFactory) { + return (deployOptimizedBatch(), deployOptimizedMerkleLockupFactory()); } } diff --git a/test/utils/Events.sol b/test/utils/Events.sol index 9a8c2a96..f54ee425 100644 --- a/test/utils/Events.sol +++ b/test/utils/Events.sol @@ -4,16 +4,16 @@ pragma solidity >=0.8.22; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; -import { ISablierV2MerkleStreamerLL } from "src/interfaces/ISablierV2MerkleStreamerLL.sol"; -import { MerkleStreamer } from "src/types/DataTypes.sol"; +import { ISablierV2MerkleLockupLL } from "src/interfaces/ISablierV2MerkleLockupLL.sol"; +import { MerkleLockup } from "src/types/DataTypes.sol"; /// @notice Abstract contract containing all the events emitted by the protocol. abstract contract Events { event Claim(uint256 index, address indexed recipient, uint128 amount, uint256 indexed streamId); event Clawback(address indexed admin, address indexed to, uint128 amount); - event CreateMerkleStreamerLL( - ISablierV2MerkleStreamerLL indexed merkleStreamerLL, - MerkleStreamer.ConstructorParams indexed baseParams, + event CreateMerkleLockupLL( + ISablierV2MerkleLockupLL indexed merkleLockupLL, + MerkleLockup.ConstructorParams indexed baseParams, ISablierV2LockupLinear lockupLinear, LockupLinear.Durations streamDurations, string ipfsCID, diff --git a/test/utils/Precompiles.sol b/test/utils/Precompiles.sol index c2775276..1d8cbe5a 100644 --- a/test/utils/Precompiles.sol +++ b/test/utils/Precompiles.sol @@ -3,7 +3,7 @@ pragma solidity >=0.8.22; import { ISablierV2Batch } from "../../src/interfaces/ISablierV2Batch.sol"; -import { ISablierV2MerkleStreamerFactory } from "../../src/interfaces/ISablierV2MerkleStreamerFactory.sol"; +import { ISablierV2MerkleLockupFactory } from "../../src/interfaces/ISablierV2MerkleLockupFactory.sol"; contract Precompiles { /*////////////////////////////////////////////////////////////////////////// @@ -12,7 +12,7 @@ contract Precompiles { bytes public constant BYTECODE_BATCH = hex"6080806040523461001657611a49908161001c8239f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c9081632b754bb014610c7d575080639b38b39a146108645780639b675ad6146104ac5763e8d349611461004b57600080fd5b346104345760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610434576100826111c0565b61008a6110c6565b9060443567ffffffffffffffff808211610434573660238301121561043457816004013511610434573660246101208360040135028301011161043457806004013515610482576000805b8260040135821061044d5761010291508473ffffffffffffffffffffffffffffffffffffffff851661158a565b61010f8160040135611339565b9160005b82600401358110610130576040518061012c8682611184565b0390f35b8060e061014582866004013560248801611579565b01610163606061015d84886004013560248a01611579565b01611388565b9061017683876004013560248901611579565b91610194602061018e868a6004013560248c01611579565b01611395565b6101ae6101a9868a6004013560248c01611579565b611395565b916fffffffffffffffffffffffffffffffff6101dd60406101d78960048e013560248f01611579565b01611252565b73ffffffffffffffffffffffffffffffffffffffff61020c8c61015d60809c8260248f94600401359101611579565b94816040519761021b8961126f565b16875216602086015216604084015273ffffffffffffffffffffffffffffffffffffffff8b166060840152151586830152151560a082015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff608436030112610434576103c4926102c060e093604051610295816112a8565b6102a160a08501611450565b81526102b060c0809501611450565b602082015283850152369061140a565b83830152604051957fab167ccc00000000000000000000000000000000000000000000000000000000875273ffffffffffffffffffffffffffffffffffffffff835116600488015273ffffffffffffffffffffffffffffffffffffffff60208401511660248801526fffffffffffffffffffffffffffffffff604084015116604488015273ffffffffffffffffffffffffffffffffffffffff60608401511660648801528201511515608487015260a0820151151560a4870152810151602064ffffffffff918281511660c489015201511660e486015201516101048401906020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b60208261014481600073ffffffffffffffffffffffffffffffffffffffff88165af1801561044157600090610409575b600192506104028287611514565b5201610113565b506020823d602011610439575b81610423602093836112e0565b8101031261043457600191516103f4565b600080fd5b3d9150610416565b6040513d6000823e3d90fd5b6001906fffffffffffffffffffffffffffffffff61047860406101d786886004013560248a01611579565b16019101906100d5565b60046040517f763e559d000000000000000000000000000000000000000000000000000000008152fd5b34610434576104ba366110e9565b909281156104825760009060005b838110610836575073ffffffffffffffffffffffffffffffffffffffff6104f2911691848361158a565b6104fb82611339565b9260005b838110610514576040518061012c8782611184565b61051f818588611539565b60c001908685610530838284611539565b60600161053c90611388565b9381610549858286611539565b60200161055590611395565b85610561818488611539565b60a0810161056e916113b6565b9561057a929197611539565b61058390611395565b968c87610591818684611539565b60400161059d90611252565b946105a792611539565b6080016105b390611388565b90604051986105c18a61126f565b73ffffffffffffffffffffffffffffffffffffffff168952151560208901521515604088015273ffffffffffffffffffffffffffffffffffffffff1660608701526fffffffffffffffffffffffffffffffff16608086015273ffffffffffffffffffffffffffffffffffffffff861660a08601523661063f9161140a565b60c0850152369061064f92611462565b60e083015260405180927f168444560000000000000000000000000000000000000000000000000000000082526004820160209052610144820190805173ffffffffffffffffffffffffffffffffffffffff166024840152602081015115156044840152604081015115156064840152606081015173ffffffffffffffffffffffffffffffffffffffff16608484015260808101516fffffffffffffffffffffffffffffffff1660a484015260a081015173ffffffffffffffffffffffffffffffffffffffff1660c484015260c081015160e4840161074d916020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b60e0015190610124830161012090528151809152610164830191602001906000905b8082106107db57505050908060209203816000885af18015610441576000906107a8575b600192506107a18288611514565b52016104ff565b506020823d6020116107d3575b816107c2602093836112e0565b810103126104345760019151610793565b3d91506107b5565b919350916020606082610828600194885164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b01940192018593929161076f565b916001906fffffffffffffffffffffffffffffffff61085b60406101d787898c611539565b160192016104c8565b346104345760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104345761089b6111c0565b6108a36110c6565b6044359167ffffffffffffffff8084116104345736602385011215610434578360040135116104345760248301903660246101408660040135028601011161043457836004013515610482576000805b85600401358210610c4b5761092091508473ffffffffffffffffffffffffffffffffffffffff841661158a565b61092d8460040135611339565b9260005b8560040135811061094a576040518061012c8782611184565b808661010061095f8794836004013586611528565b0183610975606061015d86866004013585611528565b610998602061018e8761098d81896004013588611528565b976004013586611528565b906fffffffffffffffffffffffffffffffff8c73ffffffffffffffffffffffffffffffffffffffff6109fe6109ea60406101d78c6109de6101a98260048a01358e611528565b9a876004013590611528565b9261015d8b60809d8e936004013590611528565b948160405197610a0d8961126f565b16875216602086015216604084015273ffffffffffffffffffffffffffffffffffffffff88166060840152151586830152151560a082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60843603011261043457610bd392610ac260e093604051610a878161128c565b610a9360a08501611450565b8152610ab28660c095610aa7878201611450565b602085015201611450565b604082015283850152369061140a565b83830152604051957f96ce143100000000000000000000000000000000000000000000000000000000875273ffffffffffffffffffffffffffffffffffffffff835116600488015273ffffffffffffffffffffffffffffffffffffffff60208401511660248801526fffffffffffffffffffffffffffffffff604084015116604488015273ffffffffffffffffffffffffffffffffffffffff60608401511660648801528201511515608487015260a0820151151560a4870152810151604064ffffffffff918281511660c48901528260208201511660e489015201511661010486015201516101248401906020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b60208261016481600073ffffffffffffffffffffffffffffffffffffffff89165af1801561044157600090610c18575b60019250610c118288611514565b5201610931565b506020823d602011610c43575b81610c32602093836112e0565b810103126104345760019151610c03565b3d9150610c25565b6001906fffffffffffffffffffffffffffffffff610c7360406101d7868b600401358a611528565b16019101906108f3565b3461043457610c8b366110e9565b929093831561109e57506000805b84821061107057610cc291508373ffffffffffffffffffffffffffffffffffffffff841661158a565b610ccb83611339565b9360005b848110610ce4576040518061012c8882611184565b60e0610cf18287856111e3565b0190610d03608061015d8389876111e3565b91610d14602061018e848a886111e3565b92610d2d610d23848a886111e3565b60c08101906113b6565b9091610d3d6101a9868c8a6111e3565b936060610d4b878d8b6111e3565b01359464ffffffffff861686036104345788610d7e60a061015d8f80610d7860406101d78f80958a6111e3565b956111e3565b96604051998a61012081011067ffffffffffffffff6101208d0111176110415773ffffffffffffffffffffffffffffffffffffffff908b99610e3999989764ffffffffff6fffffffffffffffffffffffffffffffff96956101009f86610e2d9b9a61012083016040521690521660208d0152151560408c0152151560608b01521660808901521660a087015273ffffffffffffffffffffffffffffffffffffffff8b1660c0870152369061140a565b60e08501523691611462565b838201526040519283917fc33cd35e0000000000000000000000000000000000000000000000000000000083526020600484015273ffffffffffffffffffffffffffffffffffffffff815116602484015264ffffffffff602082015116604484015260408101511515606484015260608101511515608484015273ffffffffffffffffffffffffffffffffffffffff60808201511660a48401526fffffffffffffffffffffffffffffffff60a08201511660c484015273ffffffffffffffffffffffffffffffffffffffff60c08201511660e4840152610f4260e08201516101048501906020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b0151610140610144830152805180610164840152602061018484019201906000905b808210610fe65750505090806020920381600073ffffffffffffffffffffffffffffffffffffffff89165af1801561044157600090610fb3575b60019250610fac8289611514565b5201610ccf565b506020823d602011610fde575b81610fcd602093836112e0565b810103126104345760019151610f9e565b3d9150610fc0565b919350916020606082611033600194885164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b019401920185939291610f64565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6001906fffffffffffffffffffffffffffffffff61109460406101d7868a8c6111e3565b1601910190610c99565b807f763e559d0000000000000000000000000000000000000000000000000000000060049252fd5b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361043457565b9060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126104345773ffffffffffffffffffffffffffffffffffffffff91600435838116810361043457926024359081168103610434579160443567ffffffffffffffff9283821161043457806023830112156104345781600401359384116104345760248460051b83010111610434576024019190565b602090602060408183019282815285518094520193019160005b8281106111ac575050505090565b83518552938101939281019260010161119e565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361043457565b91908110156112235760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee181360301821215610434570190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b356fffffffffffffffffffffffffffffffff811681036104345790565b610100810190811067ffffffffffffffff82111761104157604052565b6060810190811067ffffffffffffffff82111761104157604052565b6040810190811067ffffffffffffffff82111761104157604052565b6080810190811067ffffffffffffffff82111761104157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761104157604052565b67ffffffffffffffff81116110415760051b60200190565b9061134382611321565b61135060405191826112e0565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061137e8294611321565b0190602036910137565b3580151581036104345790565b3573ffffffffffffffffffffffffffffffffffffffff811681036104345790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610434570180359067ffffffffffffffff82116104345760200191606082023603831361043457565b919082604091031261043457604051611422816112a8565b8092803573ffffffffffffffffffffffffffffffffffffffff81168103610434578252602090810135910152565b359064ffffffffff8216820361043457565b92919261146e82611321565b60409461147e60405192836112e0565b8195848352602080930191606080960285019481861161043457925b8584106114aa5750505050505050565b8684830312610434578251906114bf8261128c565b84356fffffffffffffffffffffffffffffffff81168103610434578252858501359067ffffffffffffffff8216820361043457828792838b950152611505868801611450565b8682015281520193019261149a565b80518210156112235760209160051b010190565b919081101561122357610140020190565b91908110156112235760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0181360301821215610434570190565b919081101561122357610120020190565b90604080516020907f23b872dd000000000000000000000000000000000000000000000000000000008282015233602482015260449030828201528660648201526064815260a081019080821067ffffffffffffffff831117611041576115f39185528561179c565b73ffffffffffffffffffffffffffffffffffffffff94858516958451917fdd62ed3e0000000000000000000000000000000000000000000000000000000083523060048401521690816024820152838184818a5afa90811561177957908891600091611748575b501061166a575b50505050505050565b8351956000808589017f095ea7b3000000000000000000000000000000000000000000000000000000009a8b82528560248c0152868b0152858a526116ae8a6112c4565b89519082855af1906116be6118bc565b82611715575b508161170a575b50611661576116fe966116f9945193840152602483015260008183015281526116f3816112c4565b8261179c565b61179c565b38808080808080611661565b90503b1515386116cb565b809192505190858215928315611730575b50505090386116c4565b6117409350820181019101611784565b388581611726565b809250858092503d8311611772575b61176181836112e0565b81010312610434578790513861165a565b503d611757565b85513d6000823e3d90fd5b90816020910312610434575180151581036104345790565b6040516118079173ffffffffffffffffffffffffffffffffffffffff166117c2826112a8565b6000806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af16118016118bc565b9161191a565b8051908282159283156118a4575b505050156118205750565b608490604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b6118b49350820181019101611784565b388281611815565b3d15611915573d9067ffffffffffffffff8211611041576040519161190960207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846112e0565b82523d6000602084013e565b606090565b91929015611995575081511561192e575090565b3b156119375790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b8251909150156119a85750805190602001fd5b604051907f08c379a000000000000000000000000000000000000000000000000000000000825281602080600483015282519283602484015260005b848110611a25575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f836000604480968601015201168101030190fd5b8181018301518682016044015285935082016119e456fea164736f6c6343000817000a"; - bytes public constant BYTECODE_MERKLE_STREAMER_FACTORY = + bytes public constant BYTECODE_MERKLE_LOCKUP_FACTORY = hex"6080806040523461001657611d39908161001c8239f35b600080fdfe600436101561000d57600080fd5b60003560e01c6373b01dbb1461002257600080fd5b3461053b576101807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261053b5773ffffffffffffffffffffffffffffffffffffffff600435166004350361053b5773ffffffffffffffffffffffffffffffffffffffff602435166024350361053b5773ffffffffffffffffffffffffffffffffffffffff604435166044350361053b5764ffffffffff608435166084350361053b5760407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5c36011261053b5760c060405260a43564ffffffffff8116810361053b5760805264ffffffffff60c4351660c4350361053b5760c43560a05260e435801515810361053b5761010435801515810361053b5767ffffffffffffffff610124351161053b573660236101243501121561053b5767ffffffffffffffff61012435600401351161050c57604051906101ae60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f610124356004013501160183610540565b600461012435908101358084523691016024011161053b5761012435600401356024610124350160208401376000602061012435600401358401015260405161020f6020820160806020908164ffffffffff91828151168552015116910152565b6040815280606081011067ffffffffffffffff60608301111761050c5760608101604052606060808201917fffffffffffffffffffffffffffffffffffffffff00000000000000000000000080600435841b16845280602435841b166094830152604435831b1660a882015260643560bc8201527fffffffffff00000000000000000000000000000000000000000000000000000060843560d81b1660dc8201526102f56023838380516102ca8160e1840160208501610581565b81018a151560f81b60e182015288151560f81b60e28201520301600381018585015201838301610540565b015190206040518061178881011067ffffffffffffffff6117888301111761050c576117886105a5823973ffffffffffffffffffffffffffffffffffffffff60043581166117888301908152602435821660208201526044359091166040820152606435606082015264ffffffffff608435811660808084019190915251811660a080840191909152511660c082015285151560e08201528315156101008201526101209082900301906000f591821561050057602093604051926101409173ffffffffffffffffffffffffffffffffffffffff861685526064358786015264ffffffffff6084351660408601526104056060860160806020908164ffffffffff91828151168552015116910152565b151560a0850152151560c08401528060e0840152815180918401526104338161016093878587019101610581565b61014435610100840152610164356101208401527fb2f6bc588a802f2ce7f8dec57a5096c107d2fc000d5b4cc65745a2c0e232349c73ffffffffffffffffffffffffffffffffffffffff604435169373ffffffffffffffffffffffffffffffffffffffff6024351693817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f73ffffffffffffffffffffffffffffffffffffffff600435169601168101030190a473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761050c57604052565b60005b8381106105945750506000910152565b818101518382015260200161058456fe610160604081815234620002e0578162001788803803809162000023828562000301565b833981010361012091828212620002e05783516001600160a01b038082169391849003620002e057602090818701519481861693848703620002e0578589015192831692838103620002e05760608a015190876200008460808d0162000325565b93609f190112620002e0578751918289016001600160401b03811184821017620002ca578952620000b860a08d0162000325565b8352620000c860c08d0162000325565b83880190815293620000dd60e08e0162000338565b91610100809e01620000ef9062000338565b93600097600160a01b60019003198954161788556080528960e0528d5260c05260a05289526101409788525164ffffffffff16600254915160281b69ffffffffff00000000001691600160501b600190031916171760025584519280840191808063095ea7b360e01b948581528860248901526000196044890152604488526200017988620002e5565b87519082885af16200018a62000346565b8162000288575b50806200027d575b156200023b575b505050505050519161121b93846200056d8539608051848181610377015281816106460152610cc4015260a0518481816107320152610bb0015260c05184818161015101528181610afc01528181610f1b01526110b7015260e05184818161020e015281816105d00152610c63015251838181610327015261056801525182818161075a0152610b730152518181816101a201526108cd0152f35b62000271956200026b9388519384015260248301526044820152604481526200026481620002e5565b8262000389565b62000389565b388080808080620001a0565b50833b151562000199565b80915051838115918215620002a3575b505090503862000191565b8380929350010312620002c65782620002bd910162000338565b80833862000298565b5080fd5b634e487b7160e01b600052604160045260246000fd5b600080fd5b608081019081106001600160401b03821117620002ca57604052565b601f909101601f19168101906001600160401b03821190821017620002ca57604052565b519064ffffffffff82168203620002e057565b51908115158203620002e057565b3d1562000384573d906001600160401b038211620002ca576040519162000378601f8201601f19166020018462000301565b82523d6000602084013e565b606090565b60408051908101916001600160a01b03166001600160401b03831182841017620002ca57620003fd926040526000806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af1620003f662000346565b9162000492565b80518281159182156200046f575b5050905015620004185750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b8380929350010312620002e0578162000489910162000338565b8082386200040b565b91929015620004f75750815115620004a8575090565b3b15620004b25790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b8251909150156200050b5750805190602001fd5b6040519062461bcd60e51b82528160208060048301528251908160248401526000935b82851062000552575050604492506000838284010152601f80199101168101030190fd5b84810182015186860160440152938101938593506200052e56fe608080604052600436101561001357600080fd5b60003560e01c9081631686c90914610bd55750806316c3549d14610b985780631bfd681414610b5b5780633bfe03a814610b2c5780633f31ae3f1461039b5780634800d97f1461034a57806351e75e8b1461030f57806375829def14610232578063845aef4b146101e157806390e64d13146101c65780639e93e57714610175578063bb4b573414610133578063ce516507146100f25763f851a440146100b957600080fd5b346100ed5760006003193601126100ed57602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100ed5760206003193601126100ed57602061012960043560ff6001918060081c60005282602052161b60406000205416151590565b6040519015158152f35b346100ed5760006003193601126100ed57602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100ed5760006003193601126100ed57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100ed5760006003193601126100ed5760206101296110af565b346100ed5760006003193601126100ed57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100ed5760206003193601126100ed5761024b610fe7565b60005473ffffffffffffffffffffffffffffffffffffffff808216923384036102c2577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100ed5760006003193601126100ed5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100ed5760006003193601126100ed57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100ed5760806003193601126100ed5760243573ffffffffffffffffffffffffffffffffffffffff811681036100ed57604435906fffffffffffffffffffffffffffffffff821682036100ed5767ffffffffffffffff606435116100ed573660236064350112156100ed5767ffffffffffffffff60643560040135116100ed573660246064356004013560051b6064350101116100ed576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff831660408201526fffffffffffffffffffffffffffffffff841660608201526060815261048181611026565b519020604051602081019182526020815261049b8161100a565b519020916104a76110af565b610ace576104cf60043560ff6001918060081c60005282602052161b60406000205416151590565b610a9c57604051926104ed60206064356004013560051b0185611042565b60643560048101358552602401602085015b60246064356004013560051b60643501018210610a8c575050906000915b84518310156105645760208360051b860101519081811060001461055157600052602052600160406000205b92019161051d565b9060005260205260016040600020610549565b83907f000000000000000000000000000000000000000000000000000000000000000003610a6257604051907f5fe3b56700000000000000000000000000000000000000000000000000000000825260208260048173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa91821561099a57600092610a31575b506040517fdcf844a700000000000000000000000000000000000000000000000000000000815260208160248173ffffffffffffffffffffffffffffffffffffffff807f00000000000000000000000000000000000000000000000000000000000000001697886004840152165afa90811561099a576000916109ff575b506109d55760043560081c60005260016020526040600020600160ff600435161b81541790556040516106b18161100a565b600081526000602082015273ffffffffffffffffffffffffffffffffffffffff600054169260405193610100850185811067ffffffffffffffff8211176109a657604052845273ffffffffffffffffffffffffffffffffffffffff831660208501526fffffffffffffffffffffffffffffffff8516604085015260608401527f0000000000000000000000000000000000000000000000000000000000000000151560808401527f0000000000000000000000000000000000000000000000000000000000000000151560a084015260405161078c8161100a565b64ffffffffff600254818116835260281c16602082015260c084015260e0830152602060e0604051937fab167ccc00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff815116600486015273ffffffffffffffffffffffffffffffffffffffff838201511660248601526fffffffffffffffffffffffffffffffff604082015116604486015273ffffffffffffffffffffffffffffffffffffffff606082015116606486015260808101511515608486015260a0810151151560a486015264ffffffffff8360c08301518281511660c489015201511660e4860152015173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015260208261014481600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af191821561099a57600092610965575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d602011610992575b8161098060209383611042565b810103126100ed5790519060206108fe565b3d9150610973565b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60046040517fa4432b51000000000000000000000000000000000000000000000000000000008152fd5b90506020813d602011610a29575b81610a1a60209383611042565b810103126100ed57518461067f565b3d9150610a0d565b610a5491925060203d602011610a5b575b610a4c8183611042565b810190611083565b9083610601565b503d610a42565b60046040517fb3f3b2a6000000000000000000000000000000000000000000000000000000008152fd5b81358152602091820191016104ff565b60246040517f3548783b0000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f74b43bd00000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100ed5760006003193601126100ed57604060025464ffffffffff825191818116835260281c166020820152f35b346100ed5760006003193601126100ed5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100ed5760006003193601126100ed5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100ed5760406003193601126100ed57610bee610fe7565b90602435906fffffffffffffffffffffffffffffffff82168092036100ed5773ffffffffffffffffffffffffffffffffffffffff908160005416338103610f9e575050604051907f5fe3b5670000000000000000000000000000000000000000000000000000000082526020918281600481857f0000000000000000000000000000000000000000000000000000000000000000165afa90811561099a57600091610f81575b506040517fdcf844a7000000000000000000000000000000000000000000000000000000008152838160248186807f00000000000000000000000000000000000000000000000000000000000000001696876004840152165afa90811561099a57600091610f54575b50610d066110af565b159081610f4b575b50610eed5760405160008084868401987fa9059cbb000000000000000000000000000000000000000000000000000000008a52169788602485015287604485015260448452610d5c84611026565b60405193610d698561100a565b8785527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656488860152519082865af13d15610ee0573d9067ffffffffffffffff82116109a657610df59360405192610de7887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160185611042565b83523d60008885013e6110ec565b8051838115918215610ec0575b5050905015610e3c57907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b608482604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b83809293500103126100ed5782015180151581036100ed57808387610e02565b91610df5926060916110ec565b6040517f1351f21d0000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b90501586610d0e565b90508381813d8311610f7a575b610f6b8183611042565b810103126100ed575186610cfd565b503d610f61565b610f989150833d8511610a5b57610a4c8183611042565b85610c94565b7fc6cce6a400000000000000000000000000000000000000000000000000000000825273ffffffffffffffffffffffffffffffffffffffff166004820152336024820152604490fd5b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100ed57565b6040810190811067ffffffffffffffff8211176109a657604052565b6080810190811067ffffffffffffffff8211176109a657604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176109a657604052565b908160209103126100ed575173ffffffffffffffffffffffffffffffffffffffff811681036100ed5790565b64ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001680151590816110e4575090565b905042101590565b919290156111675750815115611100575090565b3b156111095790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b82519091501561117a5750805190602001fd5b604051907f08c379a000000000000000000000000000000000000000000000000000000000825281602080600483015282519283602484015260005b8481106111f7575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f836000604480968601015201168101030190fd5b8181018301518682016044015285935082016111b656fea164736f6c6343000817000aa164736f6c6343000817000a"; /*////////////////////////////////////////////////////////////////////////// @@ -28,27 +28,26 @@ contract Precompiles { require(address(batch) != address(0), "Sablier V2 Precompiles: deployment failed for Batch contract"); } - /// @notice Deploys {SablierV2MerkleStreamerFactory} from precompiled bytecode. - function deployMerkleStreamerFactory() public returns (ISablierV2MerkleStreamerFactory factory) { - bytes memory creationBytecode = BYTECODE_MERKLE_STREAMER_FACTORY; + /// @notice Deploys {SablierV2MerkleLockupFactory} from precompiled bytecode. + function deployMerkleLockupFactory() public returns (ISablierV2MerkleLockupFactory factory) { + bytes memory creationBytecode = BYTECODE_MERKLE_LOCKUP_FACTORY; assembly { factory := create(0, add(creationBytecode, 0x20), mload(creationBytecode)) } require( - address(factory) != address(0), - "Sablier V2 Precompiles: deployment failed for MerkleStreamerFactory contract" + address(factory) != address(0), "Sablier V2 Precompiles: deployment failed for MerkleLockupFactory contract" ); } /// @notice Deploys all V2 Periphery contracts in the following order: /// /// 1. {SablierV2Batch} - /// 2. {SablierV2MerkleStreamerFactory} + /// 2. {SablierV2MerkleLockupFactory} function deployPeriphery() public - returns (ISablierV2Batch batch, ISablierV2MerkleStreamerFactory merkleStreamerFactory) + returns (ISablierV2Batch batch, ISablierV2MerkleLockupFactory merkleLockupFactory) { batch = deployBatch(); - merkleStreamerFactory = deployMerkleStreamerFactory(); + merkleLockupFactory = deployMerkleLockupFactory(); } } diff --git a/test/utils/Precompiles.t.sol b/test/utils/Precompiles.t.sol index 00595365..2ca1fe9b 100644 --- a/test/utils/Precompiles.t.sol +++ b/test/utils/Precompiles.t.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.22 <0.9.0; import { LibString } from "solady/src/utils/LibString.sol"; import { ISablierV2Batch } from "../../src/interfaces/ISablierV2Batch.sol"; -import { ISablierV2MerkleStreamerFactory } from "../../src/interfaces/ISablierV2MerkleStreamerFactory.sol"; +import { ISablierV2MerkleLockupFactory } from "../../src/interfaces/ISablierV2MerkleLockupFactory.sol"; import { Base_Test } from "../Base.t.sol"; import { Precompiles } from "./Precompiles.sol"; @@ -27,22 +27,22 @@ contract Precompiles_Test is Base_Test { assertEq(actualBatch.code, expectedBatch.code, "bytecodes mismatch"); } - function test_DeployMerkleStreamerFactory() external onlyTestOptimizedProfile { - address actualFactory = address(precompiles.deployMerkleStreamerFactory()); - address expectedFactory = address(deployOptimizedMerkleStreamerFactory()); + function test_DeployMerkleLockupFactory() external onlyTestOptimizedProfile { + address actualFactory = address(precompiles.deployMerkleLockupFactory()); + address expectedFactory = address(deployOptimizedMerkleLockupFactory()); assertEq(actualFactory.code, expectedFactory.code, "bytecodes mismatch"); } function test_DeployPeriphery() external onlyTestOptimizedProfile { - (ISablierV2Batch actualBatch, ISablierV2MerkleStreamerFactory actualMerkleStreamerFactory) = + (ISablierV2Batch actualBatch, ISablierV2MerkleLockupFactory actualMerkleLockupFactory) = precompiles.deployPeriphery(); - (ISablierV2Batch expectedBatch, ISablierV2MerkleStreamerFactory expectedMerkleStreamerFactory) = + (ISablierV2Batch expectedBatch, ISablierV2MerkleLockupFactory expectedMerkleLockupFactory) = deployOptimizedPeriphery(); assertEq(address(actualBatch).code, address(expectedBatch).code, "bytecodes mismatch"); assertEq( - address(actualMerkleStreamerFactory).code, address(expectedMerkleStreamerFactory).code, "bytecodes mismatch" + address(actualMerkleLockupFactory).code, address(expectedMerkleLockupFactory).code, "bytecodes mismatch" ); } } From 9b24fb61218b599e3b13e7d4c4e02fed6e59d050 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Wed, 31 Jan 2024 12:25:56 +0530 Subject: [PATCH 11/61] refactor: rename constructorParams to baseParams in CreateMerkleLockupLL.s.sol --- script/CreateMerkleLockupLL.s.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/CreateMerkleLockupLL.s.sol b/script/CreateMerkleLockupLL.s.sol index 57fc23e1..a1ecde08 100644 --- a/script/CreateMerkleLockupLL.s.sol +++ b/script/CreateMerkleLockupLL.s.sol @@ -12,7 +12,7 @@ import { MerkleLockup } from "../src/types/DataTypes.sol"; contract CreateMerkleLockupLL is BaseScript { struct Params { - MerkleLockup.ConstructorParams constructorParams; + MerkleLockup.ConstructorParams baseParams; ISablierV2LockupLinear lockupLinear; LockupLinear.Durations streamDurations; string ipfsCID; @@ -29,7 +29,7 @@ contract CreateMerkleLockupLL is BaseScript { returns (ISablierV2MerkleLockupLL merkleLockupLL) { merkleLockupLL = merkleLockupFactory.createMerkleLockupLL( - params.constructorParams, + params.baseParams, params.lockupLinear, params.streamDurations, params.ipfsCID, From 77a0982cb137118d2abc8bd5e43eab143e8c7dfa Mon Sep 17 00:00:00 2001 From: andreivladbrg Date: Tue, 6 Feb 2024 13:55:24 +0200 Subject: [PATCH 12/61] build: update openzeppelin to v5.0 test: add ERC20Mock --- bun.lockb | Bin 43329 -> 43329 bytes package.json | 4 ++-- test/Base.t.sol | 6 +++--- test/fork/batch/createWithTimestampsLD.t.sol | 2 +- test/fork/batch/createWithTimestampsLL.t.sol | 2 +- test/mocks/erc20/ERC20Mock.sol | 8 ++++++++ 6 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 test/mocks/erc20/ERC20Mock.sol diff --git a/bun.lockb b/bun.lockb index c0526ba6953ea0e9ad57efbb655de3d65d5c4c78..ccf12e7f5f790f4f3903cbc8029bcef51346b887 100755 GIT binary patch delta 384 zcmX?jiRs`arU`lqpI9&V-&-ZRC*@zL`;N4=J`41J3aIRxIX70^(!F}SZ1Y4vD?tke z1_l8JhK9*NniELZY@Aun%vlWOd2E(sDQ971oqUnYeDev84r#{X&4DUsI2`i+t|`^) z)>;~Bu*veb)pE1VA%7I?UfVUzW(=SG*?7j|gRaUu*FNMl-}6Os&-=~(xA%XROf62C z_Uz?h-S@YjZQi^&RyWvv@|qHb%@;~OFtRWL?c02$%!f%D<{q89D$R=D?_DgfU3lSs)E@(0wl}d&17OU)icmD0E$kY ztY0OGVJ;Kg+{u%FS4!g3zgfS^j(76KcIC;r?J1MLv@5bFnwlo3rWj2Q?2wr}VFk-% Qn~o4xuyvcKcC1kV0Nf{pLjV8( delta 390 zcmX?jiRs`arU`lq`;(NykJ&BKQWEaUwzAcjn0P+#jmJsrR9T_NIy1IsWKZ<75)@@% zU=UznXea>EoIpBqT9~)tk4v)GgK#QS4s-Q+*jRx-ILLx!-0cCZ*q8LsB|Pyg*ea`!E7D@lI8DaGBKLyS%UQy zOxCZG#4wkMqY|h?8RY-Tzbhs2>EEnhWyd>tLVL>O+;-*3U)mMflT4E=EKE`+2X@Fz Vp0I*tvQ0+_E7-ctQ#;lu006l4eiQ%z diff --git a/package.json b/package.json index 619373c5..87999a0d 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "url": "https://github.com/sablier-labs/v2-periphery/issues" }, "dependencies": { - "@openzeppelin/contracts": "4.9.2", + "@openzeppelin/contracts": "5.0.0", "@prb/math": "4.0.2", "@sablier/v2-core": "github:sablier-labs/v2-core#staging" }, @@ -49,7 +49,7 @@ "web3" ], "peerDependencies": { - "@sablier/v2-core": "1.1.2" + "@sablier/v2-core": "github:sablier-labs/v2-core#staging" }, "publishConfig": { "access": "public" diff --git a/test/Base.t.sol b/test/Base.t.sol index 84909168..fc57d187 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -2,7 +2,6 @@ // solhint-disable max-states-count pragma solidity >=0.8.22 <0.9.0; -import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { ISablierV2Comptroller } from "@sablier/v2-core/src/interfaces/ISablierV2Comptroller.sol"; @@ -20,6 +19,7 @@ import { SablierV2Batch } from "src/SablierV2Batch.sol"; import { SablierV2MerkleLockupFactory } from "src/SablierV2MerkleLockupFactory.sol"; import { SablierV2MerkleLockupLL } from "src/SablierV2MerkleLockupLL.sol"; +import { ERC20Mock } from "./mocks/erc20/ERC20Mock.sol"; import { Defaults } from "./utils/Defaults.sol"; import { DeployOptimized } from "./utils/DeployOptimized.sol"; import { Events } from "./utils/Events.sol"; @@ -53,7 +53,7 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions function setUp() public virtual { // Deploy the default test asset. - asset = new ERC20("DAI Stablecoin", "DAI"); + asset = new ERC20Mock("DAI Stablecoin", "DAI"); // Create users for testing. users.alice = createUser("Alice"); @@ -75,7 +75,7 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions function approveContracts() internal { // Approve Batch to spend assets from Alice. changePrank({ msgSender: users.alice }); - asset.approve({ spender: address(batch), amount: MAX_UINT256 }); + asset.approve({ spender: address(batch), value: MAX_UINT256 }); } /// @dev Generates a user, labels its address, and funds it with ETH. diff --git a/test/fork/batch/createWithTimestampsLD.t.sol b/test/fork/batch/createWithTimestampsLD.t.sol index 81cbe2c8..394bc39d 100644 --- a/test/fork/batch/createWithTimestampsLD.t.sol +++ b/test/fork/batch/createWithTimestampsLD.t.sol @@ -50,7 +50,7 @@ abstract contract CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is Fork_Tes deal({ token: address(asset), to: params.sender, give: uint256(totalTransferAmount) }); changePrank({ msgSender: params.sender }); - asset.approve({ spender: address(batch), amount: totalTransferAmount }); + asset.approve({ spender: address(batch), value: totalTransferAmount }); LockupDynamic.CreateWithTimestamps memory createWithTimestamps = LockupDynamic.CreateWithTimestamps({ sender: params.sender, diff --git a/test/fork/batch/createWithTimestampsLL.t.sol b/test/fork/batch/createWithTimestampsLL.t.sol index 179b20e4..c0dce04c 100644 --- a/test/fork/batch/createWithTimestampsLL.t.sol +++ b/test/fork/batch/createWithTimestampsLL.t.sol @@ -44,7 +44,7 @@ abstract contract CreateWithTimestamps_LockupLinear_Batch_Fork_Test is Fork_Test deal({ token: address(asset), to: params.sender, give: uint256(totalTransferAmount) }); changePrank({ msgSender: params.sender }); - asset.approve({ spender: address(batch), amount: totalTransferAmount }); + asset.approve({ spender: address(batch), value: totalTransferAmount }); LockupLinear.CreateWithTimestamps memory createParams = LockupLinear.CreateWithTimestamps({ sender: params.sender, diff --git a/test/mocks/erc20/ERC20Mock.sol b/test/mocks/erc20/ERC20Mock.sol new file mode 100644 index 00000000..8cf2389d --- /dev/null +++ b/test/mocks/erc20/ERC20Mock.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity >=0.8.22; + +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract ERC20Mock is ERC20 { + constructor(string memory name, string memory symbol) ERC20(name, symbol) { } +} From f82777b9230d8712b43d3ce46599d9d48517f439 Mon Sep 17 00:00:00 2001 From: andreivladbrg Date: Tue, 13 Feb 2024 18:35:49 +0200 Subject: [PATCH 13/61] style: order functions alphabetically in ISablierV2MerkleLockup --- src/interfaces/ISablierV2MerkleLockup.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/interfaces/ISablierV2MerkleLockup.sol b/src/interfaces/ISablierV2MerkleLockup.sol index b5ee5414..4df858d6 100644 --- a/src/interfaces/ISablierV2MerkleLockup.sol +++ b/src/interfaces/ISablierV2MerkleLockup.sol @@ -38,9 +38,6 @@ interface ISablierV2MerkleLockup is IAdminable { /// @dev This is an immutable state variable. function EXPIRATION() external returns (uint40); - /// @notice Retrieves the name of the campaign. - function name() external returns (string memory); - /// @notice Returns a flag indicating whether a claim has been made for a given index. /// @dev Uses a bitmap to save gas. /// @param index The index of the recipient to check. @@ -53,6 +50,9 @@ interface ISablierV2MerkleLockup is IAdminable { /// @dev This is an immutable state variable. function MERKLE_ROOT() external returns (bytes32); + /// @notice Retrieves the name of the campaign. + function name() external returns (string memory); + /// @notice A flag indicating whether the stream NFTs are transferable. /// @dev This is an immutable state variable. function TRANSFERABLE() external returns (bool); From b310ed09f664d84b9ebb98cecbebdf7074dc1152 Mon Sep 17 00:00:00 2001 From: andreivladbrg Date: Tue, 13 Feb 2024 18:38:11 +0200 Subject: [PATCH 14/61] ci: remove deployment workflow --- .../deploy-merkle-lockup-factory.yml | 39 ------------------- 1 file changed, 39 deletions(-) delete mode 100644 .github/workflows/deploy-merkle-lockup-factory.yml diff --git a/.github/workflows/deploy-merkle-lockup-factory.yml b/.github/workflows/deploy-merkle-lockup-factory.yml deleted file mode 100644 index 793eafeb..00000000 --- a/.github/workflows/deploy-merkle-lockup-factory.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: "Deploy Merkle Lockup Factory" - -env: - API_KEY_INFURA: ${{ secrets.API_KEY_INFURA }} - FOUNDRY_PROFILE: "optimized" - MNEMONIC: ${{ secrets.MNEMONIC }} - RPC_URL_MAINNET: ${{ secrets.RPC_URL_MAINNET }} - -on: - workflow_dispatch: - inputs: - chain: - default: "sepolia" - description: "Chain name as defined in the Foundry config." - required: false - -jobs: - deploy-merkle-lockup-factory: - runs-on: "ubuntu-latest" - steps: - - name: "Check out the repo" - uses: "actions/checkout@v4" - - - name: "Install Foundry" - uses: "foundry-rs/foundry-toolchain@v1" - - - name: "Deploy the SablierV2MerkleLockupFactory contract" - run: >- - forge script script/DeployMerkleLockupFactory.s.sol - --broadcast - --rpc-url "${{ inputs.chain }}" - --sig "run()" - --verify - -vvvv - - - name: "Add workflow summary" - run: | - echo "## Result" >> $GITHUB_STEP_SUMMARY - echo "✅ Done" >> $GITHUB_STEP_SUMMARY From bd483e2b12c7fda59354076d9542498352572d4d Mon Sep 17 00:00:00 2001 From: andreivladbrg Date: Tue, 13 Feb 2024 18:46:43 +0200 Subject: [PATCH 15/61] test: use pragma solidity >=0.8.22 in BaseScript test --- test/utils/BaseScript.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils/BaseScript.t.sol b/test/utils/BaseScript.t.sol index 28c15073..0f033749 100644 --- a/test/utils/BaseScript.t.sol +++ b/test/utils/BaseScript.t.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.19 <0.9.0; +pragma solidity >=0.8.22 <0.9.0; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { PRBTest } from "@prb/test/src/PRBTest.sol"; From ffe78442dddfa09f0cbe076c17755a9964ef5ca6 Mon Sep 17 00:00:00 2001 From: andreivladbrg Date: Tue, 13 Feb 2024 19:01:20 +0200 Subject: [PATCH 16/61] build: update bun.lockb file --- bun.lockb | Bin 43329 -> 43329 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bun.lockb b/bun.lockb index ccf12e7f5f790f4f3903cbc8029bcef51346b887..dc1099b81c731c60fb8ca8063f107deff17e56f3 100755 GIT binary patch delta 280 zcmV+z0q6d~(gMNK0+22s_P^XxaSM6BWdMJsh13dpD2uq3{GPxh6b0@7V!aPQOFtbP*Wg9>ed>4kfa9qhffUCLv`ElmU|7w1F0z_$)ZQ)F=yj!tR z4Bw>q<$;f`Oz*vYIzT+xa>2&87to}KkLCFY=uTF%?KI#HK;&Gl&+;p1$m*vD-QS5G z+r!gT$k~jMwlMkmx3h%dC_E_7#l eY;tuiV_{=xWs^~e8?#%Fy$qA0ksz};oCyuR7>fY_ delta 278 zcmV+x0qOq1(gMNK0+22s@&(h6-l`S6Wd2o0xMrOwK?C0(6X)tiJ?KI#HK!dktrpR*o0Q;IBJUA~+ z!6B0>hc}ZVz-0tf)@ym(3_rem-(GgY_+R@Mv?yUxn^)qEyqtW%3f{o1wY_sKma`2+ zqcD@KbRe_PbnpQL0RR91v&42$5eH#4HDhIDGLun=9Fvf!1hYJd^ac<$E;KH5XL@XM cbuD9IV`ycQQHUF}TaUdAlTwQyvpAdy4WR;lq5uE@ From 536be9ef7f87beddfe21b509316bba375c472905 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Wed, 14 Feb 2024 10:16:24 +0000 Subject: [PATCH 17/61] test: add `ASSET` variable in `Fork.t.sol` to store forked asset (#287) * test: add "ASSET" variable in Fork.t.sol to store forked asset refactor: name default test asset to "dai" test: use low-level call to approve contract via approveContract() doc: add dev comment to USDC.t.sol and USDT.t.sole * test: define a computeMerkleLockupLLAddress without asset_ parameter --- test/Base.t.sol | 47 ++++++++++++------- test/fork/Fork.t.sol | 20 +++++--- test/fork/assets/USDC.t.sol | 1 + test/fork/assets/USDT.t.sol | 1 + test/fork/batch/createWithTimestampsLD.t.sol | 13 +++-- test/fork/batch/createWithTimestampsLL.t.sol | 13 +++-- test/fork/merkle-lockup/MerkleLockupLL.t.sol | 20 ++++---- test/integration/Integration.t.sol | 8 ++-- .../createWithDurations.t.sol | 4 +- .../createWithTimestamps.t.sol | 4 +- .../createWithDurations.t.sol | 4 +- .../createWithTimestamps.t.sol | 4 +- .../merkle-lockup/MerkleLockup.t.sol | 4 +- .../createMerkleLockupLL.t.sol | 8 +++- .../merkle-lockup/ll/claim/claim.t.sol | 4 +- .../merkle-lockup/ll/clawback/clawback.t.sol | 2 +- .../ll/constructor/constructor.t.sol | 4 +- test/utils/Defaults.sol | 7 +-- 18 files changed, 98 insertions(+), 70 deletions(-) diff --git a/test/Base.t.sol b/test/Base.t.sol index fc57d187..2231bfd4 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -38,7 +38,7 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions TEST CONTRACTS //////////////////////////////////////////////////////////////////////////*/ - IERC20 internal asset; + IERC20 internal dai; ISablierV2Batch internal batch; ISablierV2Comptroller internal comptroller; Defaults internal defaults; @@ -53,7 +53,7 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions function setUp() public virtual { // Deploy the default test asset. - asset = new ERC20Mock("DAI Stablecoin", "DAI"); + dai = new ERC20Mock("DAI Stablecoin", "DAI"); // Create users for testing. users.alice = createUser("Alice"); @@ -71,18 +71,18 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions HELPERS //////////////////////////////////////////////////////////////////////////*/ - /// @dev Approves relevant contracts to spend assets from some users. - function approveContracts() internal { - // Approve Batch to spend assets from Alice. - changePrank({ msgSender: users.alice }); - asset.approve({ spender: address(batch), value: MAX_UINT256 }); + /// @dev Approve contract to spend asset from some users. + function approveContract(IERC20 asset_, address from, address spender) internal { + changePrank({ msgSender: from }); + (bool success,) = address(asset_).call(abi.encodeCall(IERC20.approve, (spender, MAX_UINT256))); + success; } /// @dev Generates a user, labels its address, and funds it with ETH. function createUser(string memory name) internal returns (address payable) { address user = makeAddr(name); vm.deal({ account: user, newBalance: 100_000 ether }); - deal({ token: address(asset), to: user, give: 1_000_000e18 }); + deal({ token: address(dai), to: user, give: 1_000_000e18 }); return payable(user); } @@ -97,8 +97,8 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions } /// @dev Labels the most relevant contracts. - function labelContracts() internal { - vm.label({ account: address(asset), newLabel: IERC20Metadata(address(asset)).symbol() }); + function labelContracts(IERC20 asset_) internal { + vm.label({ account: address(asset_), newLabel: IERC20Metadata(address(asset_)).symbol() }); vm.label({ account: address(merkleLockupFactory), newLabel: "MerkleLockupFactory" }); vm.label({ account: address(merkleLockupLL), newLabel: "MerkleLockupLL" }); vm.label({ account: address(defaults), newLabel: "Defaults" }); @@ -145,7 +145,7 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions /// @dev Expects a call to {IERC20.transfer}. function expectCallToTransfer(address to, uint256 amount) internal { - expectCallToTransfer(address(asset), to, amount); + expectCallToTransfer(address(dai), to, amount); } /// @dev Expects a call to {IERC20.transfer}. @@ -155,7 +155,7 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions /// @dev Expects a call to {IERC20.transferFrom}. function expectCallToTransferFrom(address from, address to, uint256 amount) internal { - expectCallToTransferFrom(address(asset), from, to, amount); + expectCallToTransferFrom(address(dai), from, to, amount); } /// @dev Expects a call to {IERC20.transferFrom}. @@ -225,12 +225,12 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions /// @dev Expects multiple calls to {IERC20.transfer}. function expectMultipleCallsToTransfer(uint64 count, address to, uint256 amount) internal { - vm.expectCall({ callee: address(asset), count: count, data: abi.encodeCall(IERC20.transfer, (to, amount)) }); + vm.expectCall({ callee: address(dai), count: count, data: abi.encodeCall(IERC20.transfer, (to, amount)) }); } /// @dev Expects multiple calls to {IERC20.transferFrom}. function expectMultipleCallsToTransferFrom(uint64 count, address from, address to, uint256 amount) internal { - expectMultipleCallsToTransferFrom(address(asset), count, from, to, amount); + expectMultipleCallsToTransferFrom(address(dai), count, from, to, amount); } /// @dev Expects multiple calls to {IERC20.transferFrom}. @@ -257,11 +257,23 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions ) internal returns (address) + { + return computeMerkleLockupLLAddress(admin, dai, merkleRoot, expiration); + } + + function computeMerkleLockupLLAddress( + address admin, + IERC20 asset_, + bytes32 merkleRoot, + uint40 expiration + ) + internal + returns (address) { bytes32 salt = keccak256( abi.encodePacked( admin, - asset, + address(asset_), defaults.NAME_BYTES32(), merkleRoot, expiration, @@ -271,7 +283,7 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions abi.encode(defaults.durations()) ) ); - bytes32 creationBytecodeHash = keccak256(getMerkleLockupLLBytecode(admin, merkleRoot, expiration)); + bytes32 creationBytecodeHash = keccak256(getMerkleLockupLLBytecode(admin, asset_, merkleRoot, expiration)); return computeCreate2Address({ salt: salt, initcodeHash: creationBytecodeHash, @@ -281,6 +293,7 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions function getMerkleLockupLLBytecode( address admin, + IERC20 asset_, bytes32 merkleRoot, uint40 expiration ) @@ -288,7 +301,7 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions returns (bytes memory) { bytes memory constructorArgs = - abi.encode(defaults.baseParams(admin, merkleRoot, expiration), lockupLinear, defaults.durations()); + abi.encode(defaults.baseParams(admin, asset_, merkleRoot, expiration), lockupLinear, defaults.durations()); if (!isTestOptimizedProfile()) { return bytes.concat(type(SablierV2MerkleLockupLL).creationCode, constructorArgs); } else { diff --git a/test/fork/Fork.t.sol b/test/fork/Fork.t.sol index 7d1ad224..d0730733 100644 --- a/test/fork/Fork.t.sol +++ b/test/fork/Fork.t.sol @@ -13,12 +13,18 @@ import { Base_Test } from "../Base.t.sol"; /// @notice Common logic needed by all fork tests. abstract contract Fork_Test is Base_Test, V2CoreFuzzers { + /*////////////////////////////////////////////////////////////////////////// + STATE VARIABLES + //////////////////////////////////////////////////////////////////////////*/ + + IERC20 internal immutable ASSET; + /*////////////////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////////////////*/ constructor(IERC20 asset_) { - asset = asset_; + ASSET = asset_; } /*////////////////////////////////////////////////////////////////////////// @@ -38,17 +44,17 @@ abstract contract Fork_Test is Base_Test, V2CoreFuzzers { deployDependencies(); // Deploy the defaults contract and allow it to access cheatcodes. - defaults = new Defaults(users, asset); + defaults = new Defaults(users, ASSET); vm.allowCheatcodes(address(defaults)); // Deploy V2 Periphery. deployPeripheryConditionally(); // Label the contracts. - labelContracts(); + labelContracts(ASSET); - // Approve the relevant contracts. - approveContracts(); + // Approve the relevant contract. + approveContract({ asset_: ASSET, from: users.alice, spender: address(batch) }); } /*////////////////////////////////////////////////////////////////////////// @@ -66,8 +72,8 @@ abstract contract Fork_Test is Base_Test, V2CoreFuzzers { vm.assume(user != address(lockupLinear) && recipient != address(lockupLinear)); // Avoid users blacklisted by USDC or USDT. - assumeNoBlacklisted(address(asset), user); - assumeNoBlacklisted(address(asset), recipient); + assumeNoBlacklisted(address(ASSET), user); + assumeNoBlacklisted(address(ASSET), recipient); } /// @dev Loads all dependencies pre-deployed on Mainnet. diff --git a/test/fork/assets/USDC.t.sol b/test/fork/assets/USDC.t.sol index 1d58e000..2b47c4d5 100644 --- a/test/fork/assets/USDC.t.sol +++ b/test/fork/assets/USDC.t.sol @@ -7,6 +7,7 @@ import { CreateWithTimestamps_LockupDynamic_Batch_Fork_Test } from "../batch/cre import { CreateWithTimestamps_LockupLinear_Batch_Fork_Test } from "../batch/createWithTimestampsLL.t.sol"; import { MerkleLockupLL_Fork_Test } from "../merkle-lockup/MerkleLockupLL.t.sol"; +/// @dev An ERC-20 asset with 6 decimals. IERC20 constant usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); contract USDC_CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is diff --git a/test/fork/assets/USDT.t.sol b/test/fork/assets/USDT.t.sol index 5be49fce..83c76459 100644 --- a/test/fork/assets/USDT.t.sol +++ b/test/fork/assets/USDT.t.sol @@ -7,6 +7,7 @@ import { CreateWithTimestamps_LockupDynamic_Batch_Fork_Test } from "../batch/cre import { CreateWithTimestamps_LockupLinear_Batch_Fork_Test } from "../batch/createWithTimestampsLL.t.sol"; import { MerkleLockupLL_Fork_Test } from "../merkle-lockup/MerkleLockupLL.t.sol"; +/// @dev An ERC-20 asset that suffers from the missing return value bug. IERC20 constant usdt = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7); contract USDT_CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is diff --git a/test/fork/batch/createWithTimestampsLD.t.sol b/test/fork/batch/createWithTimestampsLD.t.sol index 394bc39d..ba9e1fbf 100644 --- a/test/fork/batch/createWithTimestampsLD.t.sol +++ b/test/fork/batch/createWithTimestampsLD.t.sol @@ -48,15 +48,14 @@ abstract contract CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is Fork_Tes uint256 firstStreamId = lockupDynamic.nextStreamId(); uint128 totalTransferAmount = params.perStreamAmount * params.batchSize; - deal({ token: address(asset), to: params.sender, give: uint256(totalTransferAmount) }); - changePrank({ msgSender: params.sender }); - asset.approve({ spender: address(batch), value: totalTransferAmount }); + deal({ token: address(ASSET), to: params.sender, give: uint256(totalTransferAmount) }); + approveContract({ asset_: ASSET, from: params.sender, spender: address(batch) }); LockupDynamic.CreateWithTimestamps memory createWithTimestamps = LockupDynamic.CreateWithTimestamps({ sender: params.sender, recipient: params.recipient, totalAmount: params.perStreamAmount, - asset: asset, + asset: ASSET, cancelable: true, transferable: true, startTime: params.startTime, @@ -67,21 +66,21 @@ abstract contract CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is Fork_Tes BatchBuilder.fillBatch(createWithTimestamps, params.batchSize); expectCallToTransferFrom({ - asset_: address(asset), + asset_: address(ASSET), from: params.sender, to: address(batch), amount: totalTransferAmount }); expectMultipleCallsToCreateWithTimestampsLD({ count: uint64(params.batchSize), params: createWithTimestamps }); expectMultipleCallsToTransferFrom({ - asset_: address(asset), + asset_: address(ASSET), count: uint64(params.batchSize), from: address(batch), to: address(lockupDynamic), amount: params.perStreamAmount }); - uint256[] memory actualStreamIds = batch.createWithTimestampsLD(lockupDynamic, asset, batchParams); + uint256[] memory actualStreamIds = batch.createWithTimestampsLD(lockupDynamic, ASSET, batchParams); uint256[] memory expectedStreamIds = ArrayBuilder.fillStreamIds(firstStreamId, params.batchSize); assertEq(actualStreamIds, expectedStreamIds); } diff --git a/test/fork/batch/createWithTimestampsLL.t.sol b/test/fork/batch/createWithTimestampsLL.t.sol index c0dce04c..eb02f311 100644 --- a/test/fork/batch/createWithTimestampsLL.t.sol +++ b/test/fork/batch/createWithTimestampsLL.t.sol @@ -42,15 +42,14 @@ abstract contract CreateWithTimestamps_LockupLinear_Batch_Fork_Test is Fork_Test uint256 firstStreamId = lockupLinear.nextStreamId(); uint128 totalTransferAmount = params.perStreamAmount * params.batchSize; - deal({ token: address(asset), to: params.sender, give: uint256(totalTransferAmount) }); - changePrank({ msgSender: params.sender }); - asset.approve({ spender: address(batch), value: totalTransferAmount }); + deal({ token: address(ASSET), to: params.sender, give: uint256(totalTransferAmount) }); + approveContract({ asset_: ASSET, from: params.sender, spender: address(batch) }); LockupLinear.CreateWithTimestamps memory createParams = LockupLinear.CreateWithTimestamps({ sender: params.sender, recipient: params.recipient, totalAmount: params.perStreamAmount, - asset: asset, + asset: ASSET, cancelable: true, transferable: true, range: params.range, @@ -60,21 +59,21 @@ abstract contract CreateWithTimestamps_LockupLinear_Batch_Fork_Test is Fork_Test // Asset flow: sender → batch → Sablier expectCallToTransferFrom({ - asset_: address(asset), + asset_: address(ASSET), from: params.sender, to: address(batch), amount: totalTransferAmount }); expectMultipleCallsToCreateWithTimestampsLL({ count: uint64(params.batchSize), params: createParams }); expectMultipleCallsToTransferFrom({ - asset_: address(asset), + asset_: address(ASSET), count: uint64(params.batchSize), from: address(batch), to: address(lockupLinear), amount: params.perStreamAmount }); - uint256[] memory actualStreamIds = batch.createWithTimestampsLL(lockupLinear, asset, batchParams); + uint256[] memory actualStreamIds = batch.createWithTimestampsLL(lockupLinear, ASSET, batchParams); uint256[] memory expectedStreamIds = ArrayBuilder.fillStreamIds(firstStreamId, params.batchSize); assertEq(actualStreamIds, expectedStreamIds); } diff --git a/test/fork/merkle-lockup/MerkleLockupLL.t.sol b/test/fork/merkle-lockup/MerkleLockupLL.t.sol index 3f81fd2b..7292bb91 100644 --- a/test/fork/merkle-lockup/MerkleLockupLL.t.sol +++ b/test/fork/merkle-lockup/MerkleLockupLL.t.sol @@ -61,7 +61,7 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { vm.assume(params.expiration == 0 || params.expiration > block.timestamp); vm.assume(params.leafData.length > 1); params.posBeforeSort = _bound(params.posBeforeSort, 0, params.leafData.length - 1); - assumeNoBlacklisted({ token: address(asset), addr: params.admin }); + assumeNoBlacklisted({ token: address(ASSET), addr: params.admin }); /*////////////////////////////////////////////////////////////////////////// CREATE @@ -93,10 +93,14 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { MerkleBuilder.sortLeaves(leaves); vars.merkleRoot = getRoot(leaves.toBytes32()); - vars.expectedLockupLL = computeMerkleLockupLLAddress(params.admin, vars.merkleRoot, params.expiration); + vars.expectedLockupLL = computeMerkleLockupLLAddress(params.admin, ASSET, vars.merkleRoot, params.expiration); - vars.baseParams = - defaults.baseParams({ admin: params.admin, merkleRoot: vars.merkleRoot, expiration: params.expiration }); + vars.baseParams = defaults.baseParams({ + admin: params.admin, + asset_: ASSET, + merkleRoot: vars.merkleRoot, + expiration: params.expiration + }); vm.expectEmit({ emitter: address(merkleLockupFactory) }); emit CreateMerkleLockupLL({ @@ -119,7 +123,7 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { }); // Fund the Merkle Lockup contract. - deal({ token: address(asset), to: address(vars.merkleLockupLL), give: vars.aggregateAmount }); + deal({ token: address(ASSET), to: address(vars.merkleLockupLL), give: vars.aggregateAmount }); assertGt(address(vars.merkleLockupLL).code.length, 0, "MerkleLockupLL contract not created"); assertEq( @@ -158,7 +162,7 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { vars.actualStream = lockupLinear.getStream(vars.actualStreamId); vars.expectedStream = LockupLinear.Stream({ amounts: Lockup.Amounts({ deposited: vars.amounts[params.posBeforeSort], refunded: 0, withdrawn: 0 }), - asset: asset, + asset: ASSET, cliffTime: uint40(block.timestamp) + defaults.CLIFF_DURATION(), endTime: uint40(block.timestamp) + defaults.TOTAL_DURATION(), isCancelable: defaults.CANCELABLE(), @@ -179,11 +183,11 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { //////////////////////////////////////////////////////////////////////////*/ if (params.expiration > 0) { - vars.clawbackAmount = uint128(asset.balanceOf(address(vars.merkleLockupLL))); + vars.clawbackAmount = uint128(ASSET.balanceOf(address(vars.merkleLockupLL))); vm.warp({ timestamp: uint256(params.expiration) + 1 seconds }); changePrank({ msgSender: params.admin }); - expectCallToTransfer({ to: params.admin, amount: vars.clawbackAmount }); + expectCallToTransfer({ asset_: address(ASSET), to: params.admin, amount: vars.clawbackAmount }); vm.expectEmit({ emitter: address(vars.merkleLockupLL) }); emit Clawback({ to: params.admin, admin: params.admin, amount: vars.clawbackAmount }); vars.merkleLockupLL.clawback({ to: params.admin, amount: vars.clawbackAmount }); diff --git a/test/integration/Integration.t.sol b/test/integration/Integration.t.sol index f5bc9cdb..470b575a 100644 --- a/test/integration/Integration.t.sol +++ b/test/integration/Integration.t.sol @@ -20,16 +20,16 @@ abstract contract Integration_Test is Base_Test { deployDependencies(); // Deploy the defaults contract. - defaults = new Defaults(users, asset); + defaults = new Defaults(users, dai); // Deploy V2 Periphery. deployPeripheryConditionally(); // Label the contracts. - labelContracts(); + labelContracts(dai); - // Approve the relevant contracts. - approveContracts(); + // Approve the relevant contract. + approveContract({ asset_: dai, from: users.alice, spender: address(batch) }); } /*////////////////////////////////////////////////////////////////////////// diff --git a/test/integration/batch/lockup-dynamic/create-with-durations/createWithDurations.t.sol b/test/integration/batch/lockup-dynamic/create-with-durations/createWithDurations.t.sol index 1af6d955..f1d4bf27 100644 --- a/test/integration/batch/lockup-dynamic/create-with-durations/createWithDurations.t.sol +++ b/test/integration/batch/lockup-dynamic/create-with-durations/createWithDurations.t.sol @@ -14,7 +14,7 @@ contract CreateWithDurations_LockupDynamic_Integration_Test is Integration_Test function test_RevertWhen_BatchSizeZero() external { Batch.CreateWithDurationsLD[] memory batchParams = new Batch.CreateWithDurationsLD[](0); vm.expectRevert(Errors.SablierV2Batch_BatchSizeZero.selector); - batch.createWithDurationsLD(lockupDynamic, asset, batchParams); + batch.createWithDurationsLD(lockupDynamic, dai, batchParams); } modifier whenBatchSizeNotZero() { @@ -38,7 +38,7 @@ contract CreateWithDurations_LockupDynamic_Integration_Test is Integration_Test // Assert that the batch of streams has been created successfully. uint256[] memory actualStreamIds = - batch.createWithDurationsLD(lockupDynamic, asset, defaults.batchCreateWithDurationsLD()); + batch.createWithDurationsLD(lockupDynamic, dai, defaults.batchCreateWithDurationsLD()); uint256[] memory expectedStreamIds = defaults.incrementalStreamIds(); assertEq(actualStreamIds, expectedStreamIds, "stream ids mismatch"); } diff --git a/test/integration/batch/lockup-dynamic/create-with-timestamps/createWithTimestamps.t.sol b/test/integration/batch/lockup-dynamic/create-with-timestamps/createWithTimestamps.t.sol index 81500065..96213c43 100644 --- a/test/integration/batch/lockup-dynamic/create-with-timestamps/createWithTimestamps.t.sol +++ b/test/integration/batch/lockup-dynamic/create-with-timestamps/createWithTimestamps.t.sol @@ -14,7 +14,7 @@ contract CreateWithTimestamps_LockupDynamic_Integration_Test is Integration_Test function test_RevertWhen_BatchSizeZero() external { Batch.CreateWithTimestampsLD[] memory batchParams = new Batch.CreateWithTimestampsLD[](0); vm.expectRevert(Errors.SablierV2Batch_BatchSizeZero.selector); - batch.createWithTimestampsLD(lockupDynamic, asset, batchParams); + batch.createWithTimestampsLD(lockupDynamic, dai, batchParams); } modifier whenBatchSizeNotZero() { @@ -38,7 +38,7 @@ contract CreateWithTimestamps_LockupDynamic_Integration_Test is Integration_Test // Assert that the batch of streams has been created successfully. uint256[] memory actualStreamIds = - batch.createWithTimestampsLD(lockupDynamic, asset, defaults.batchCreateWithTimestampsLD()); + batch.createWithTimestampsLD(lockupDynamic, dai, defaults.batchCreateWithTimestampsLD()); uint256[] memory expectedStreamIds = defaults.incrementalStreamIds(); assertEq(actualStreamIds, expectedStreamIds, "stream ids mismatch"); } diff --git a/test/integration/batch/lockup-linear/create-with-durations/createWithDurations.t.sol b/test/integration/batch/lockup-linear/create-with-durations/createWithDurations.t.sol index f3f4523a..a3dd26ee 100644 --- a/test/integration/batch/lockup-linear/create-with-durations/createWithDurations.t.sol +++ b/test/integration/batch/lockup-linear/create-with-durations/createWithDurations.t.sol @@ -14,7 +14,7 @@ contract CreateWithDurations_LockupLinear_Integration_Test is Integration_Test { function test_RevertWhen_BatchSizeZero() external { Batch.CreateWithDurationsLL[] memory batchParams = new Batch.CreateWithDurationsLL[](0); vm.expectRevert(Errors.SablierV2Batch_BatchSizeZero.selector); - batch.createWithDurationsLL(lockupLinear, asset, batchParams); + batch.createWithDurationsLL(lockupLinear, dai, batchParams); } modifier whenBatchSizeNotZero() { @@ -38,7 +38,7 @@ contract CreateWithDurations_LockupLinear_Integration_Test is Integration_Test { // Assert that the batch of streams has been created successfully. uint256[] memory actualStreamIds = - batch.createWithDurationsLL(lockupLinear, asset, defaults.batchCreateWithDurationsLL()); + batch.createWithDurationsLL(lockupLinear, dai, defaults.batchCreateWithDurationsLL()); uint256[] memory expectedStreamIds = defaults.incrementalStreamIds(); assertEq(actualStreamIds, expectedStreamIds, "stream ids mismatch"); } diff --git a/test/integration/batch/lockup-linear/create-with-timestamps/createWithTimestamps.t.sol b/test/integration/batch/lockup-linear/create-with-timestamps/createWithTimestamps.t.sol index 66639b2f..f4834a9d 100644 --- a/test/integration/batch/lockup-linear/create-with-timestamps/createWithTimestamps.t.sol +++ b/test/integration/batch/lockup-linear/create-with-timestamps/createWithTimestamps.t.sol @@ -14,7 +14,7 @@ contract CreateWithTimestamps_LockupLinear_Integration_Test is Integration_Test function test_RevertWhen_BatchSizeZero() external { Batch.CreateWithTimestampsLL[] memory batchParams = new Batch.CreateWithTimestampsLL[](0); vm.expectRevert(Errors.SablierV2Batch_BatchSizeZero.selector); - batch.createWithTimestampsLL(lockupLinear, asset, batchParams); + batch.createWithTimestampsLL(lockupLinear, dai, batchParams); } modifier whenBatchSizeNotZero() { @@ -38,7 +38,7 @@ contract CreateWithTimestamps_LockupLinear_Integration_Test is Integration_Test // Assert that the batch of streams has been created successfully. uint256[] memory actualStreamIds = - batch.createWithTimestampsLL(lockupLinear, asset, defaults.batchCreateWithTimestampsLL()); + batch.createWithTimestampsLL(lockupLinear, dai, defaults.batchCreateWithTimestampsLL()); uint256[] memory expectedStreamIds = defaults.incrementalStreamIds(); assertEq(actualStreamIds, expectedStreamIds, "stream ids mismatch"); } diff --git a/test/integration/merkle-lockup/MerkleLockup.t.sol b/test/integration/merkle-lockup/MerkleLockup.t.sol index 16a2c43b..e9b120b7 100644 --- a/test/integration/merkle-lockup/MerkleLockup.t.sol +++ b/test/integration/merkle-lockup/MerkleLockup.t.sol @@ -13,7 +13,7 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { merkleLockupLL = createMerkleLockupLL(); // Fund the Merkle Lockup contract. - deal({ token: address(asset), to: address(merkleLockupLL), give: defaults.AGGREGATE_AMOUNT() }); + deal({ token: address(dai), to: address(merkleLockupLL), give: defaults.AGGREGATE_AMOUNT() }); } function claimLL() internal returns (uint256) { @@ -55,7 +55,7 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { function createMerkleLockupLL(address admin, uint40 expiration) internal returns (ISablierV2MerkleLockupLL) { return merkleLockupFactory.createMerkleLockupLL({ - baseParams: defaults.baseParams(admin, defaults.MERKLE_ROOT(), expiration), + baseParams: defaults.baseParams(admin, dai, defaults.MERKLE_ROOT(), expiration), lockupLinear: lockupLinear, streamDurations: defaults.durations(), ipfsCID: defaults.IPFS_CID(), diff --git a/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol b/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol index 60f35400..e3b9bca6 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol +++ b/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol @@ -77,8 +77,12 @@ contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test vm.assume(admin != users.admin); address expectedLockupLL = computeMerkleLockupLLAddress(admin, expiration); - MerkleLockup.ConstructorParams memory baseParams = - defaults.baseParams({ admin: admin, merkleRoot: defaults.MERKLE_ROOT(), expiration: expiration }); + MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams({ + admin: admin, + asset_: dai, + merkleRoot: defaults.MERKLE_ROOT(), + expiration: expiration + }); vm.expectEmit({ emitter: address(merkleLockupFactory) }); emit CreateMerkleLockupLL({ diff --git a/test/integration/merkle-lockup/ll/claim/claim.t.sol b/test/integration/merkle-lockup/ll/claim/claim.t.sol index c9543ce2..f3a50227 100644 --- a/test/integration/merkle-lockup/ll/claim/claim.t.sol +++ b/test/integration/merkle-lockup/ll/claim/claim.t.sol @@ -109,7 +109,7 @@ contract Claim_Integration_Test is MerkleLockup_Integration_Test { givenIncludedInMerkleTree { changePrank({ msgSender: users.admin }); - comptroller.setProtocolFee({ asset: asset, newProtocolFee: ud(0.1e18) }); + comptroller.setProtocolFee({ asset: dai, newProtocolFee: ud(0.1e18) }); test_Claim({ protocolFee: ud(0.1e18) }); } @@ -139,7 +139,7 @@ contract Claim_Integration_Test is MerkleLockup_Integration_Test { LockupLinear.Stream memory actualStream = lockupLinear.getStream(actualStreamId); LockupLinear.Stream memory expectedStream = LockupLinear.Stream({ amounts: Lockup.Amounts({ deposited: defaults.CLAIM_AMOUNT() - feeAmount, refunded: 0, withdrawn: 0 }), - asset: asset, + asset: dai, cliffTime: uint40(block.timestamp) + defaults.CLIFF_DURATION(), endTime: uint40(block.timestamp) + defaults.TOTAL_DURATION(), isCancelable: defaults.CANCELABLE(), diff --git a/test/integration/merkle-lockup/ll/clawback/clawback.t.sol b/test/integration/merkle-lockup/ll/clawback/clawback.t.sol index 1ea18bed..e5fa4cb6 100644 --- a/test/integration/merkle-lockup/ll/clawback/clawback.t.sol +++ b/test/integration/merkle-lockup/ll/clawback/clawback.t.sol @@ -49,7 +49,7 @@ contract Clawback_Integration_Test is MerkleLockup_Integration_Test { } function test_Clawback(address to) internal { - uint128 clawbackAmount = uint128(asset.balanceOf(address(merkleLockupLL))); + uint128 clawbackAmount = uint128(dai.balanceOf(address(merkleLockupLL))); expectCallToTransfer({ to: to, amount: clawbackAmount }); vm.expectEmit({ emitter: address(merkleLockupLL) }); emit Clawback({ admin: users.admin, to: to, amount: clawbackAmount }); diff --git a/test/integration/merkle-lockup/ll/constructor/constructor.t.sol b/test/integration/merkle-lockup/ll/constructor/constructor.t.sol index 2d96f94c..1af5dad4 100644 --- a/test/integration/merkle-lockup/ll/constructor/constructor.t.sol +++ b/test/integration/merkle-lockup/ll/constructor/constructor.t.sol @@ -43,7 +43,7 @@ contract Constructor_MerkleLockupLL_Integration_Test is MerkleLockup_Integration assertEq(vars.actualAdmin, vars.expectedAdmin, "admin"); vars.actualAsset = address(constructedLockupLL.ASSET()); - vars.expectedAsset = address(asset); + vars.expectedAsset = address(dai); assertEq(vars.actualAsset, vars.expectedAsset, "asset"); vars.actualName = constructedLockupLL.name(); @@ -75,7 +75,7 @@ contract Constructor_MerkleLockupLL_Integration_Test is MerkleLockup_Integration assertEq(vars.actualDurations.cliff, vars.expectedDurations.cliff, "durations.cliff"); assertEq(vars.actualDurations.total, vars.expectedDurations.total, "durations.total"); - vars.actualAllowance = asset.allowance(address(constructedLockupLL), address(lockupLinear)); + vars.actualAllowance = dai.allowance(address(constructedLockupLL), address(lockupLinear)); vars.expectedAllowance = MAX_UINT256; assertEq(vars.actualAllowance, vars.expectedAllowance, "allowance"); } diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index 3d951f30..73f795ed 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -117,21 +117,22 @@ contract Defaults is Merkle { } function baseParams() public view returns (MerkleLockup.ConstructorParams memory) { - return baseParams(users.admin, MERKLE_ROOT, EXPIRATION); + return baseParams(users.admin, asset, MERKLE_ROOT, EXPIRATION); } function baseParams( address admin, + IERC20 asset_, bytes32 merkleRoot, uint40 expiration ) public - view + pure returns (MerkleLockup.ConstructorParams memory) { return MerkleLockup.ConstructorParams({ initialAdmin: admin, - asset: asset, + asset: asset_, name: NAME, merkleRoot: merkleRoot, expiration: expiration, From 77a119182f7afdf298c61e2c8d3c6f29e9d8472d Mon Sep 17 00:00:00 2001 From: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com> Date: Wed, 21 Feb 2024 14:39:50 +0200 Subject: [PATCH 18/61] build: update bun lock file (#294) test: use StreamLL struct --- bun.lockb | Bin 43329 -> 43329 bytes test/fork/batch/createWithTimestampsLL.t.sol | 2 +- test/fork/merkle-lockup/MerkleLockupLL.t.sol | 6 +++--- .../merkle-lockup/ll/claim/claim.t.sol | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bun.lockb b/bun.lockb index dc1099b81c731c60fb8ca8063f107deff17e56f3..a3330ee91bc8cd86ae4404cb70d23ddc8b96efc2 100755 GIT binary patch delta 85 zcmV-b0IL7N(gMNK0+22sCA5^Pm~iXioNuoc_DUolE54{7FN&n5#<8CE?^ZP8u})%i rldN Date: Wed, 7 Feb 2024 10:40:28 +0530 Subject: [PATCH 19/61] feat: store ipfsCID as a public variable fix: remove additional implementation of name() --- script/CreateMerkleLockupLL.s.sol | 2 -- src/SablierV2MerkleLockupFactory.sol | 4 ++-- src/abstracts/SablierV2MerkleLockup.sol | 4 ++++ src/interfaces/ISablierV2MerkleLockup.sol | 3 +++ src/interfaces/ISablierV2MerkleLockupFactory.sol | 3 --- src/types/DataTypes.sol | 2 ++ test/Base.t.sol | 1 + test/fork/merkle-lockup/MerkleLockupLL.t.sol | 2 -- test/integration/merkle-lockup/MerkleLockup.t.sol | 1 - .../create-merkle-lockup-ll/createMerkleLockupLL.t.sol | 5 ----- .../merkle-lockup/ll/constructor/constructor.t.sol | 6 ++++++ test/utils/Defaults.sol | 1 + test/utils/Events.sol | 1 - 13 files changed, 19 insertions(+), 16 deletions(-) diff --git a/script/CreateMerkleLockupLL.s.sol b/script/CreateMerkleLockupLL.s.sol index a1ecde08..0ff2339c 100644 --- a/script/CreateMerkleLockupLL.s.sol +++ b/script/CreateMerkleLockupLL.s.sol @@ -15,7 +15,6 @@ contract CreateMerkleLockupLL is BaseScript { MerkleLockup.ConstructorParams baseParams; ISablierV2LockupLinear lockupLinear; LockupLinear.Durations streamDurations; - string ipfsCID; uint256 campaignTotalAmount; uint256 recipientsCount; } @@ -32,7 +31,6 @@ contract CreateMerkleLockupLL is BaseScript { params.baseParams, params.lockupLinear, params.streamDurations, - params.ipfsCID, params.campaignTotalAmount, params.recipientsCount ); diff --git a/src/SablierV2MerkleLockupFactory.sol b/src/SablierV2MerkleLockupFactory.sol index a3ced2b0..1964e0dd 100644 --- a/src/SablierV2MerkleLockupFactory.sol +++ b/src/SablierV2MerkleLockupFactory.sol @@ -21,7 +21,6 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { MerkleLockup.ConstructorParams memory baseParams, ISablierV2LockupLinear lockupLinear, LockupLinear.Durations memory streamDurations, - string memory ipfsCID, uint256 aggregateAmount, uint256 recipientsCount ) @@ -33,6 +32,7 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { abi.encodePacked( baseParams.initialAdmin, baseParams.asset, + abi.encode(baseParams.ipfsCID), bytes32(abi.encodePacked(baseParams.name)), baseParams.merkleRoot, baseParams.expiration, @@ -48,7 +48,7 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { // Using a different function to emit the event to avoid stack too deep error. emit CreateMerkleLockupLL( - merkleLockupLL, baseParams, lockupLinear, streamDurations, ipfsCID, aggregateAmount, recipientsCount + merkleLockupLL, baseParams, lockupLinear, streamDurations, aggregateAmount, recipientsCount ); } } diff --git a/src/abstracts/SablierV2MerkleLockup.sol b/src/abstracts/SablierV2MerkleLockup.sol index 16c27ce6..7f0e342c 100644 --- a/src/abstracts/SablierV2MerkleLockup.sol +++ b/src/abstracts/SablierV2MerkleLockup.sol @@ -42,6 +42,9 @@ abstract contract SablierV2MerkleLockup is /// @inheritdoc ISablierV2MerkleLockup bool public immutable override TRANSFERABLE; + /// @inheritdoc ISablierV2MerkleLockup + string public ipfsCID; + /// @dev Packed booleans that record the history of claims. BitMaps.BitMap internal _claimedBitMap; @@ -63,6 +66,7 @@ abstract contract SablierV2MerkleLockup is ASSET = params.asset; CANCELABLE = params.cancelable; EXPIRATION = params.expiration; + ipfsCID = params.ipfsCID; MERKLE_ROOT = params.merkleRoot; NAME = bytes32(abi.encodePacked(params.name)); TRANSFERABLE = params.transferable; diff --git a/src/interfaces/ISablierV2MerkleLockup.sol b/src/interfaces/ISablierV2MerkleLockup.sol index 4df858d6..92688563 100644 --- a/src/interfaces/ISablierV2MerkleLockup.sol +++ b/src/interfaces/ISablierV2MerkleLockup.sol @@ -46,6 +46,9 @@ interface ISablierV2MerkleLockup is IAdminable { /// @notice Returns a flag indicating whether the campaign has expired. function hasExpired() external view returns (bool); + /// @notice The content identifier for indexing the contract on IPFS. + function ipfsCID() external view returns (string memory); + /// @notice The root of the Merkle tree used to validate the claims. /// @dev This is an immutable state variable. function MERKLE_ROOT() external returns (bytes32); diff --git a/src/interfaces/ISablierV2MerkleLockupFactory.sol b/src/interfaces/ISablierV2MerkleLockupFactory.sol index ba55bb8c..497e7afc 100644 --- a/src/interfaces/ISablierV2MerkleLockupFactory.sol +++ b/src/interfaces/ISablierV2MerkleLockupFactory.sol @@ -20,7 +20,6 @@ interface ISablierV2MerkleLockupFactory { MerkleLockup.ConstructorParams indexed baseParams, ISablierV2LockupLinear lockupLinear, LockupLinear.Durations streamDurations, - string ipfsCID, uint256 aggregateAmount, uint256 recipientsCount ); @@ -35,7 +34,6 @@ interface ISablierV2MerkleLockupFactory { /// {DataTypes}. /// @param lockupLinear The address of the {SablierV2LockupLinear} contract. /// @param streamDurations The durations for each stream due to the recipient. - /// @param ipfsCID Metadata parameter emitted for indexing purposes. /// @param aggregateAmount Total amount of ERC-20 assets to be streamed to all recipients. /// @param recipientsCount Total number of recipients eligible to claim. /// @return merkleLockupLL The address of the newly created Merkle Lockup contract. @@ -43,7 +41,6 @@ interface ISablierV2MerkleLockupFactory { MerkleLockup.ConstructorParams memory baseParams, ISablierV2LockupLinear lockupLinear, LockupLinear.Durations memory streamDurations, - string memory ipfsCID, uint256 aggregateAmount, uint256 recipientsCount ) diff --git a/src/types/DataTypes.sol b/src/types/DataTypes.sol index f6d5a4a2..85ba726a 100644 --- a/src/types/DataTypes.sol +++ b/src/types/DataTypes.sol @@ -66,6 +66,7 @@ library MerkleLockup { /// @notice Struct encapsulating the base constructor parameter of a {SablierV2MerkleLockup} contract. /// @param initialAdmin The initial admin of the Merkle Lockup contract. /// @param asset The address of the streamed ERC-20 asset. + /// @param ipfsCID The content identifier for indexing the contract on IPFS. /// @param name The name of the campaign. /// @param merkleRoot The Merkle root of the claim data. /// @param expiration The expiration of the streaming campaign, as a Unix timestamp. @@ -74,6 +75,7 @@ library MerkleLockup { struct ConstructorParams { address initialAdmin; IERC20 asset; + string ipfsCID; string name; bytes32 merkleRoot; uint40 expiration; diff --git a/test/Base.t.sol b/test/Base.t.sol index 2231bfd4..e363a2cf 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -274,6 +274,7 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions abi.encodePacked( admin, address(asset_), + abi.encode(defaults.IPFS_CID()), defaults.NAME_BYTES32(), merkleRoot, expiration, diff --git a/test/fork/merkle-lockup/MerkleLockupLL.t.sol b/test/fork/merkle-lockup/MerkleLockupLL.t.sol index 311cf4c4..5f556679 100644 --- a/test/fork/merkle-lockup/MerkleLockupLL.t.sol +++ b/test/fork/merkle-lockup/MerkleLockupLL.t.sol @@ -108,7 +108,6 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { baseParams: vars.baseParams, lockupLinear: lockupLinear, streamDurations: defaults.durations(), - ipfsCID: defaults.IPFS_CID(), aggregateAmount: vars.aggregateAmount, recipientsCount: vars.recipientsCount }); @@ -117,7 +116,6 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { baseParams: vars.baseParams, lockupLinear: lockupLinear, streamDurations: defaults.durations(), - ipfsCID: defaults.IPFS_CID(), aggregateAmount: vars.aggregateAmount, recipientsCount: vars.recipientsCount }); diff --git a/test/integration/merkle-lockup/MerkleLockup.t.sol b/test/integration/merkle-lockup/MerkleLockup.t.sol index e9b120b7..85004f77 100644 --- a/test/integration/merkle-lockup/MerkleLockup.t.sol +++ b/test/integration/merkle-lockup/MerkleLockup.t.sol @@ -58,7 +58,6 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { baseParams: defaults.baseParams(admin, dai, defaults.MERKLE_ROOT(), expiration), lockupLinear: lockupLinear, streamDurations: defaults.durations(), - ipfsCID: defaults.IPFS_CID(), aggregateAmount: defaults.AGGREGATE_AMOUNT(), recipientsCount: defaults.RECIPIENTS_COUNT() }); diff --git a/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol b/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol index e3b9bca6..3bed45f8 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol +++ b/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol @@ -17,7 +17,6 @@ contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test function test_RevertWhen_CampaignNameTooLong() external { MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); LockupLinear.Durations memory streamDurations = defaults.durations(); - string memory ipfsCID = defaults.IPFS_CID(); uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); uint256 recipientsCount = defaults.RECIPIENTS_COUNT(); @@ -33,7 +32,6 @@ contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test baseParams: baseParams, lockupLinear: lockupLinear, streamDurations: streamDurations, - ipfsCID: ipfsCID, aggregateAmount: aggregateAmount, recipientsCount: recipientsCount }); @@ -47,7 +45,6 @@ contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test function test_RevertGiven_AlreadyCreated() external whenCampaignNameIsNotTooLong { MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); LockupLinear.Durations memory streamDurations = defaults.durations(); - string memory ipfsCID = defaults.IPFS_CID(); uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); uint256 recipientsCount = defaults.RECIPIENTS_COUNT(); @@ -56,7 +53,6 @@ contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test baseParams: baseParams, lockupLinear: lockupLinear, streamDurations: streamDurations, - ipfsCID: ipfsCID, aggregateAmount: aggregateAmount, recipientsCount: recipientsCount }); @@ -90,7 +86,6 @@ contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test baseParams: baseParams, lockupLinear: lockupLinear, streamDurations: defaults.durations(), - ipfsCID: defaults.IPFS_CID(), aggregateAmount: defaults.AGGREGATE_AMOUNT(), recipientsCount: defaults.RECIPIENTS_COUNT() }); diff --git a/test/integration/merkle-lockup/ll/constructor/constructor.t.sol b/test/integration/merkle-lockup/ll/constructor/constructor.t.sol index 1af5dad4..fcf2aba6 100644 --- a/test/integration/merkle-lockup/ll/constructor/constructor.t.sol +++ b/test/integration/merkle-lockup/ll/constructor/constructor.t.sol @@ -13,6 +13,7 @@ contract Constructor_MerkleLockupLL_Integration_Test is MerkleLockup_Integration address actualAdmin; uint256 actualAllowance; address actualAsset; + string actualIpfsCID; string actualName; bool actualCancelable; bool actualTransferable; @@ -23,6 +24,7 @@ contract Constructor_MerkleLockupLL_Integration_Test is MerkleLockup_Integration address expectedAdmin; uint256 expectedAllowance; address expectedAsset; + string expectedIpfsCID; bytes32 expectedName; bool expectedCancelable; bool expectedTransferable; @@ -78,5 +80,9 @@ contract Constructor_MerkleLockupLL_Integration_Test is MerkleLockup_Integration vars.actualAllowance = dai.allowance(address(constructedLockupLL), address(lockupLinear)); vars.expectedAllowance = MAX_UINT256; assertEq(vars.actualAllowance, vars.expectedAllowance, "allowance"); + + vars.actualIpfsCID = constructedLockupLL.ipfsCID(); + vars.expectedIpfsCID = defaults.IPFS_CID(); + assertEq(vars.actualIpfsCID, vars.expectedIpfsCID, "ipfsCID"); } } diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index 73f795ed..24e2559e 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -133,6 +133,7 @@ contract Defaults is Merkle { return MerkleLockup.ConstructorParams({ initialAdmin: admin, asset: asset_, + ipfsCID: IPFS_CID, name: NAME, merkleRoot: merkleRoot, expiration: expiration, diff --git a/test/utils/Events.sol b/test/utils/Events.sol index f54ee425..3ca28a3d 100644 --- a/test/utils/Events.sol +++ b/test/utils/Events.sol @@ -16,7 +16,6 @@ abstract contract Events { MerkleLockup.ConstructorParams indexed baseParams, ISablierV2LockupLinear lockupLinear, LockupLinear.Durations streamDurations, - string ipfsCID, uint256 aggregateAmount, uint256 recipientsCount ); From 7f9f2d6673e08e1f6898f373ab3317df36824482 Mon Sep 17 00:00:00 2001 From: andreivladbrg Date: Thu, 29 Feb 2024 01:05:45 +0200 Subject: [PATCH 20/61] chore: correct explantory comment --- src/SablierV2MerkleLockupFactory.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SablierV2MerkleLockupFactory.sol b/src/SablierV2MerkleLockupFactory.sol index 1964e0dd..3f513f4b 100644 --- a/src/SablierV2MerkleLockupFactory.sol +++ b/src/SablierV2MerkleLockupFactory.sol @@ -46,7 +46,7 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { // Deploy the Merkle Lockup contract with CREATE2. merkleLockupLL = new SablierV2MerkleLockupLL{ salt: salt }(baseParams, lockupLinear, streamDurations); - // Using a different function to emit the event to avoid stack too deep error. + // Log the creation of the Merkle Lockup, including some metadata that is not stored on-chain. emit CreateMerkleLockupLL( merkleLockupLL, baseParams, lockupLinear, streamDurations, aggregateAmount, recipientsCount ); From 2282afcd0e10a4b079096e4cb10dd25b52a50f99 Mon Sep 17 00:00:00 2001 From: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com> Date: Mon, 11 Mar 2024 13:48:35 +0200 Subject: [PATCH 21/61] MerkleLockup for LockupTranched (#297) * feat: implement SablierV2MerkleLockupLT contract feat: add createMerkleLockupLT in SablierV2MerkleLockupFactory contract * build: include Tranched contracts in prepare-artifacts * chore: use plural for tranches percantages docs: add requirements in createMerkleLockupLT natspec chore: improve wording in explanatory comments * fix: handle the case when protocol fee is greater than zero test: add Merkle Lockup LT integration tests * refactor: rename variables test: add fork tests for Merkle Lockup LT * test: remove unused import test: use pragma >=0.8.22 * chore: add named arg in custom error test: createMerkleLockupLT in factory test: update Precompiles bytecode * refactor: use plural in getter function test: getTranchesWithPercentages function test: update Precompiles bytecode * feat: add CreateMerkleLockupLT in script refactor: use maxCount in DeployProtocol script * test: add LT fork tests for USDC and USDT * ci: add fork tests to ci with 20 runs * refactor: order imports alphabetically (#301) * refactor: remove protocol fee handle in _calculateTranches build: bump core test: dry-fy fork tests test: remove protocol fee tests in claim test: add headers in MerkleLockup test: emit the correct element in the assertEq test: * refactor: remove getTranchesWithPercentages test * refactor: rename struct variable to unlockPercentage chore: improve explanatory comments test: fix fork test for MerkleLockupLT * build: update lockfile * docs: add unlock with percentages in Lockup Tranche test: remove protocol feee branch from claim.tree * refactor: rename variable to totalPercentage * test: increase coverage by testing when there is a rounding error in tranches' amounts calculation * test: improve rounding error test in claim * chore: be more precise in explanatory comment --------- Co-authored-by: smol-ninja --- .github/workflows/ci.yml | 11 + bun.lockb | Bin 43329 -> 43329 bytes script/CreateMerkleLockupLT.s.sol | 37 ++++ script/DeployProtocol.s.sol | 7 +- shell/prepare-artifacts.sh | 2 + src/SablierV2MerkleLockupFactory.sol | 60 +++++- src/SablierV2MerkleLockupLL.sol | 2 +- src/SablierV2MerkleLockupLT.sol | 160 ++++++++++++++ .../ISablierV2MerkleLockupFactory.sol | 37 +++- src/interfaces/ISablierV2MerkleLockupLT.sol | 48 +++++ src/libraries/Errors.sol | 7 + src/types/DataTypes.sol | 15 ++ test/Base.t.sol | 74 ++++++- test/fork/Fork.t.sol | 5 +- test/fork/assets/USDC.t.sol | 3 + test/fork/assets/USDT.t.sol | 3 + test/fork/merkle-lockup/MerkleLockupLL.t.sol | 6 +- test/fork/merkle-lockup/MerkleLockupLT.t.sol | 193 +++++++++++++++++ test/integration/Integration.t.sol | 2 +- .../merkle-lockup/MerkleLockup.t.sol | 62 +++++- .../createMerkleLockupLT.t.sol | 159 ++++++++++++++ .../createMerkleLockupLT.tree | 15 ++ .../merkle-lockup/ll/claim/claim.t.sol | 32 +-- .../merkle-lockup/ll/claim/claim.tree | 11 +- .../merkle-lockup/lt/claim/claim.t.sol | 202 ++++++++++++++++++ .../merkle-lockup/lt/claim/claim.tree | 26 +++ .../merkle-lockup/lt/clawback/clawback.t.sol | 58 +++++ .../merkle-lockup/lt/clawback/clawback.tree | 9 + .../lt/constructor/constructor.t.sol | 86 ++++++++ .../lt/has-claimed/hasClaimed.t.sol | 32 +++ .../lt/has-claimed/hasClaimed.tree | 8 + .../lt/has-expired/hasExpired.t.sol | 35 +++ .../lt/has-expired/hasExpired.tree | 10 + test/utils/Assertions.sol | 26 +++ test/utils/Defaults.sol | 49 ++++- test/utils/Events.sol | 12 +- test/utils/Precompiles.sol | 4 +- 37 files changed, 1448 insertions(+), 60 deletions(-) create mode 100644 script/CreateMerkleLockupLT.s.sol create mode 100644 src/SablierV2MerkleLockupLT.sol create mode 100644 src/interfaces/ISablierV2MerkleLockupLT.sol create mode 100644 test/fork/merkle-lockup/MerkleLockupLT.t.sol create mode 100644 test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.t.sol create mode 100644 test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.tree create mode 100644 test/integration/merkle-lockup/lt/claim/claim.t.sol create mode 100644 test/integration/merkle-lockup/lt/claim/claim.tree create mode 100644 test/integration/merkle-lockup/lt/clawback/clawback.t.sol create mode 100644 test/integration/merkle-lockup/lt/clawback/clawback.tree create mode 100644 test/integration/merkle-lockup/lt/constructor/constructor.t.sol create mode 100644 test/integration/merkle-lockup/lt/has-claimed/hasClaimed.t.sol create mode 100644 test/integration/merkle-lockup/lt/has-claimed/hasClaimed.tree create mode 100644 test/integration/merkle-lockup/lt/has-expired/hasExpired.t.sol create mode 100644 test/integration/merkle-lockup/lt/has-expired/hasExpired.tree create mode 100644 test/utils/Assertions.sol diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1347654d..cb451da2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,17 @@ jobs: match-path: "test/utils/**/*.sol" name: "Utils tests" + test-fork: + needs: ["lint", "build"] + secrets: + RPC_URL_MAINNET: ${{ secrets.RPC_URL_MAINNET }} + uses: "sablier-labs/reusable-workflows/.github/workflows/forge-test.yml@main" + with: + foundry-fuzz-runs: 20 + foundry-profile: "test-optimized" + match-path: "test/fork/**/*.sol" + name: "Fork tests" + coverage: needs: ["lint", "build"] uses: "sablier-labs/reusable-workflows/.github/workflows/forge-coverage.yml@main" diff --git a/bun.lockb b/bun.lockb index a3330ee91bc8cd86ae4404cb70d23ddc8b96efc2..83a2c5301c10ff805d17191a0375988c6f554ea7 100755 GIT binary patch delta 87 zcmV-d0I2`L(gMNK0+22s*>F9pNv2{yaJhyIjhi~ZR}&6h_*`e)|J&p-GC|q`u})%i tleBak0SL3rbn^iO|NsC0v&42$5eGD4Gh#I{HIq?@9Fvf!1hYJd1t9=VCj9^a delta 85 zcmV-b0IL7N(gMNK0+22sCA5^Pm~iXioNuoc_DUolE54{7FN&n5#<8CE?^ZP8u})%i rldN=0.8.22 <0.9.0; + +import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; + +import { BaseScript } from "./Base.s.sol"; + +import { ISablierV2MerkleLockupFactory } from "../src/interfaces/ISablierV2MerkleLockupFactory.sol"; +import { ISablierV2MerkleLockupLT } from "../src/interfaces/ISablierV2MerkleLockupLT.sol"; +import { MerkleLockup, MerkleLockupLT } from "../src/types/DataTypes.sol"; + +contract CreateMerkleLockupLT is BaseScript { + struct Params { + MerkleLockup.ConstructorParams baseParams; + ISablierV2LockupTranched lockupTranched; + MerkleLockupLT.TrancheWithPercentage[] tranchesWithPercentages; + uint256 campaignTotalAmount; + uint256 recipientsCount; + } + + function run( + ISablierV2MerkleLockupFactory merkleLockupFactory, + Params calldata params + ) + public + broadcast + returns (ISablierV2MerkleLockupLT merkleLockupLT) + { + merkleLockupLT = merkleLockupFactory.createMerkleLockupLT( + params.baseParams, + params.lockupTranched, + params.tranchesWithPercentages, + params.campaignTotalAmount, + params.recipientsCount + ); + } +} diff --git a/script/DeployProtocol.s.sol b/script/DeployProtocol.s.sol index 46f1ec6a..270805f7 100644 --- a/script/DeployProtocol.s.sol +++ b/script/DeployProtocol.s.sol @@ -4,6 +4,7 @@ pragma solidity >=0.8.22 <0.9.0; import { SablierV2Comptroller } from "@sablier/v2-core/src/SablierV2Comptroller.sol"; import { SablierV2LockupDynamic } from "@sablier/v2-core/src/SablierV2LockupDynamic.sol"; import { SablierV2LockupLinear } from "@sablier/v2-core/src/SablierV2LockupLinear.sol"; +import { SablierV2LockupTranched } from "@sablier/v2-core/src/SablierV2LockupTranched.sol"; import { SablierV2NFTDescriptor } from "@sablier/v2-core/src/SablierV2NFTDescriptor.sol"; import { BaseScript } from "./Base.s.sol"; @@ -14,7 +15,7 @@ import { SablierV2Batch } from "../src/SablierV2Batch.sol"; contract DeployProtocol is BaseScript { function run( address initialAdmin, - uint256 maxSegmentCount + uint256 maxCount ) public virtual @@ -23,6 +24,7 @@ contract DeployProtocol is BaseScript { SablierV2Comptroller comptroller, SablierV2LockupDynamic lockupDynamic, SablierV2LockupLinear lockupLinear, + SablierV2LockupTranched lockupTranched, SablierV2NFTDescriptor nftDescriptor, SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory @@ -31,8 +33,9 @@ contract DeployProtocol is BaseScript { // Deploy V2 Core. comptroller = new SablierV2Comptroller(initialAdmin); nftDescriptor = new SablierV2NFTDescriptor(); - lockupDynamic = new SablierV2LockupDynamic(initialAdmin, comptroller, nftDescriptor, maxSegmentCount); + lockupDynamic = new SablierV2LockupDynamic(initialAdmin, comptroller, nftDescriptor, maxCount); lockupLinear = new SablierV2LockupLinear(initialAdmin, comptroller, nftDescriptor); + lockupTranched = new SablierV2LockupTranched(initialAdmin, comptroller, nftDescriptor, maxCount); batch = new SablierV2Batch(); merkleLockupFactory = new SablierV2MerkleLockupFactory(); diff --git a/shell/prepare-artifacts.sh b/shell/prepare-artifacts.sh index 91fdb475..3b7e9378 100755 --- a/shell/prepare-artifacts.sh +++ b/shell/prepare-artifacts.sh @@ -27,11 +27,13 @@ FOUNDRY_PROFILE=optimized forge build cp out-optimized/SablierV2Batch.sol/SablierV2Batch.json $artifacts cp out-optimized/SablierV2MerkleLockupFactory.sol/SablierV2MerkleLockupFactory.json $artifacts cp out-optimized/SablierV2MerkleLockupLL.sol/SablierV2MerkleLockupLL.json $artifacts +cp out-optimized/SablierV2MerkleLockupLT.sol/SablierV2MerkleLockupLT.json $artifacts interfaces=./artifacts/interfaces cp out-optimized/ISablierV2Batch.sol/ISablierV2Batch.json $interfaces cp out-optimized/ISablierV2MerkleLockupFactory.sol/ISablierV2MerkleLockupFactory.json $interfaces cp out-optimized/ISablierV2MerkleLockupLL.sol/ISablierV2MerkleLockupLL.json $interfaces +cp out-optimized/ISablierV2MerkleLockupLT.sol/ISablierV2MerkleLockupLT.json $interfaces erc20=./artifacts/interfaces/erc20 cp out-optimized/IERC20.sol/IERC20.json $erc20 diff --git a/src/SablierV2MerkleLockupFactory.sol b/src/SablierV2MerkleLockupFactory.sol index 3f513f4b..28101727 100644 --- a/src/SablierV2MerkleLockupFactory.sol +++ b/src/SablierV2MerkleLockupFactory.sol @@ -1,13 +1,18 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; -import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; +import { UD60x18, ud } from "@prb/math/src/UD60x18.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; +import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; +import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { SablierV2MerkleLockupLL } from "./SablierV2MerkleLockupLL.sol"; import { ISablierV2MerkleLockupFactory } from "./interfaces/ISablierV2MerkleLockupFactory.sol"; import { ISablierV2MerkleLockupLL } from "./interfaces/ISablierV2MerkleLockupLL.sol"; -import { MerkleLockup } from "./types/DataTypes.sol"; +import { ISablierV2MerkleLockupLT } from "./interfaces/ISablierV2MerkleLockupLT.sol"; +import { Errors } from "./libraries/Errors.sol"; +import { SablierV2MerkleLockupLL } from "./SablierV2MerkleLockupLL.sol"; +import { SablierV2MerkleLockupLT } from "./SablierV2MerkleLockupLT.sol"; +import { MerkleLockup, MerkleLockupLT } from "./types/DataTypes.sol"; /// @title SablierV2MerkleLockupFactory /// @notice See the documentation in {ISablierV2MerkleLockupFactory}. @@ -51,4 +56,53 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { merkleLockupLL, baseParams, lockupLinear, streamDurations, aggregateAmount, recipientsCount ); } + + /// @notice inheritdoc ISablierV2MerkleLockupFactory + function createMerkleLockupLT( + MerkleLockup.ConstructorParams memory baseParams, + ISablierV2LockupTranched lockupTranched, + MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages, + uint256 aggregateAmount, + uint256 recipientsCount + ) + external + returns (ISablierV2MerkleLockupLT merkleLockupLT) + { + // Calculate the sum of percentages across all tranches. + UD60x18 totalPercentage; + uint256 trancheCount = tranchesWithPercentages.length; + for (uint256 i = 0; i < trancheCount; ++i) { + UD60x18 percentage = (tranchesWithPercentages[i].unlockPercentage).intoUD60x18(); + totalPercentage = totalPercentage.add(percentage); + } + + // Checks: the sum of percentages equals 100%. + if (!totalPercentage.eq(ud(1e18))) { + revert Errors.SablierV2MerkleLockupFactory_TotalPercentageNotEqualOneHundred(totalPercentage.intoUint256()); + } + + // Hash the parameters to generate a salt. + bytes32 salt = keccak256( + abi.encodePacked( + baseParams.initialAdmin, + baseParams.asset, + abi.encode(baseParams.ipfsCID), + bytes32(abi.encodePacked(baseParams.name)), + baseParams.merkleRoot, + baseParams.expiration, + baseParams.cancelable, + baseParams.transferable, + lockupTranched, + abi.encode(tranchesWithPercentages) + ) + ); + + // Deploy the Merkle Lockup contract with CREATE2. + merkleLockupLT = new SablierV2MerkleLockupLT{ salt: salt }(baseParams, lockupTranched, tranchesWithPercentages); + + // Log the creation of the Merkle Lockup, including some metadata that is not stored on-chain. + emit CreateMerkleLockupLT( + merkleLockupLT, baseParams, lockupTranched, tranchesWithPercentages, aggregateAmount, recipientsCount + ); + } } diff --git a/src/SablierV2MerkleLockupLL.sol b/src/SablierV2MerkleLockupLL.sol index 3046612c..6214875e 100644 --- a/src/SablierV2MerkleLockupLL.sol +++ b/src/SablierV2MerkleLockupLL.sol @@ -4,9 +4,9 @@ pragma solidity >=0.8.22; import { BitMaps } from "@openzeppelin/contracts/utils/structs/BitMaps.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { ud } from "@prb/math/src/UD60x18.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; import { Broker, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { ud } from "@prb/math/src/UD60x18.sol"; import { SablierV2MerkleLockup } from "./abstracts/SablierV2MerkleLockup.sol"; import { ISablierV2MerkleLockupLL } from "./interfaces/ISablierV2MerkleLockupLL.sol"; diff --git a/src/SablierV2MerkleLockupLT.sol b/src/SablierV2MerkleLockupLT.sol new file mode 100644 index 00000000..ffd243e9 --- /dev/null +++ b/src/SablierV2MerkleLockupLT.sol @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity >=0.8.22; + +import { BitMaps } from "@openzeppelin/contracts/utils/structs/BitMaps.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { ud, UD60x18 } from "@prb/math/src/UD60x18.sol"; +import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; +import { Broker, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; + +import { SablierV2MerkleLockup } from "./abstracts/SablierV2MerkleLockup.sol"; +import { ISablierV2MerkleLockupLT } from "./interfaces/ISablierV2MerkleLockupLT.sol"; +import { MerkleLockup, MerkleLockupLT } from "./types/DataTypes.sol"; + +/// @title SablierV2MerkleLockupLT +/// @notice See the documentation in {ISablierV2MerkleLockupLT}. +contract SablierV2MerkleLockupLT is + ISablierV2MerkleLockupLT, // 2 inherited components + SablierV2MerkleLockup // 4 inherited components +{ + using BitMaps for BitMaps.BitMap; + using SafeERC20 for IERC20; + + /*////////////////////////////////////////////////////////////////////////// + STATE VARIABLES + //////////////////////////////////////////////////////////////////////////*/ + + /// @inheritdoc ISablierV2MerkleLockupLT + ISablierV2LockupTranched public immutable override LOCKUP_TRANCHED; + + /// @dev The tranches with their respective unlock percentages and durations. + MerkleLockupLT.TrancheWithPercentage[] internal _tranchesWithPercentages; + + /*////////////////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev Constructs the contract by initializing the immutable state variables, and max approving the Sablier + /// contract. + constructor( + MerkleLockup.ConstructorParams memory baseParams, + ISablierV2LockupTranched lockupTranched, + MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages + ) + SablierV2MerkleLockup(baseParams) + { + LOCKUP_TRANCHED = lockupTranched; + + // Since Solidity lacks a syntax for copying arrays directly from memory to storage, + // a manual approach is necessary. See https://github.com/ethereum/solidity/issues/12783. + uint256 count = tranchesWithPercentages.length; + for (uint256 i = 0; i < count; ++i) { + _tranchesWithPercentages.push(tranchesWithPercentages[i]); + } + + // Max approve the Sablier contract to spend funds from the Merkle Lockup contract. + ASSET.forceApprove(address(LOCKUP_TRANCHED), type(uint256).max); + } + + /*////////////////////////////////////////////////////////////////////////// + USER-FACING CONSTANT FUNCTIONS + //////////////////////////////////////////////////////////////////////////*/ + + /// @inheritdoc ISablierV2MerkleLockupLT + function getTranchesWithPercentages() + external + view + override + returns (MerkleLockupLT.TrancheWithPercentage[] memory) + { + return _tranchesWithPercentages; + } + + /*////////////////////////////////////////////////////////////////////////// + USER-FACING NON-CONSTANT FUNCTIONS + //////////////////////////////////////////////////////////////////////////*/ + + /// @inheritdoc ISablierV2MerkleLockupLT + function claim( + uint256 index, + address recipient, + uint128 amount, + bytes32[] calldata merkleProof + ) + external + override + returns (uint256 streamId) + { + // Generate the Merkle tree leaf by hashing the corresponding parameters. Hashing twice prevents second + // preimage attacks. + bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(index, recipient, amount)))); + + // Checks: validate the function. + _checkClaim(index, leaf, merkleProof); + + // Calculate the tranches based on the `amount`. + LockupTranched.TrancheWithDuration[] memory tranches = _calculateTranches(amount); + + // Effects: mark the index as claimed. + _claimedBitMap.set(index); + + // Interactions: create the stream via {SablierV2LockupTranched}. + streamId = LOCKUP_TRANCHED.createWithDurations( + LockupTranched.CreateWithDurations({ + sender: admin, + recipient: recipient, + totalAmount: amount, + asset: ASSET, + cancelable: CANCELABLE, + transferable: TRANSFERABLE, + tranches: tranches, + broker: Broker({ account: address(0), fee: ud(0) }) + }) + ); + + // Log the claim. + emit Claim(index, recipient, amount, streamId); + } + + /// @dev Calculates the stream tranches based on Merkle tree amount and unlock percentage for each tranche. + function _calculateTranches(uint128 amount) + internal + view + returns (LockupTranched.TrancheWithDuration[] memory tranches) + { + // Load the tranches in memory to save gas. + MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages = _tranchesWithPercentages; + + // Declare the variables need for calculation. + UD60x18 trancheAmountsSum; + uint256 trancheCount = tranchesWithPercentages.length; + tranches = new LockupTranched.TrancheWithDuration[](trancheCount); + + UD60x18 udAmount = ud(amount); + + // Iterate over each tranche to calculate its amount based on its percentage. + for (uint256 i = 0; i < trancheCount; ++i) { + // Convert the tranche's percentage to `UD60x18` for calculation. + UD60x18 percentage = (tranchesWithPercentages[i].unlockPercentage).intoUD60x18(); + + // Calculate the tranche's amount by applying its percentage to the `amount`. + UD60x18 trancheAmount = udAmount.mul(percentage); + + // Sum all tranche amounts. + trancheAmountsSum = trancheAmountsSum.add(trancheAmount); + + // Assign calculated amount and duration to the tranche. + tranches[i] = LockupTranched.TrancheWithDuration({ + amount: trancheAmount.intoUint128(), + duration: tranchesWithPercentages[i].duration + }); + } + + // Adjust the last tranche amount to prevent claim failure due to rounding differences during calculations. We + // need to ensure the core protocol invariant: the sum of all tranches' amounts equals the deposit amount. + if (!udAmount.eq(trancheAmountsSum)) { + tranches[trancheCount - 1].amount += udAmount.intoUint128() - trancheAmountsSum.intoUint128(); + } + } +} diff --git a/src/interfaces/ISablierV2MerkleLockupFactory.sol b/src/interfaces/ISablierV2MerkleLockupFactory.sol index 497e7afc..0b99e466 100644 --- a/src/interfaces/ISablierV2MerkleLockupFactory.sol +++ b/src/interfaces/ISablierV2MerkleLockupFactory.sol @@ -2,10 +2,12 @@ pragma solidity >=0.8.22; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; +import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; import { ISablierV2MerkleLockupLL } from "./ISablierV2MerkleLockupLL.sol"; -import { MerkleLockup } from "../types/DataTypes.sol"; +import { ISablierV2MerkleLockupLT } from "./ISablierV2MerkleLockupLT.sol"; +import { MerkleLockup, MerkleLockupLT } from "../types/DataTypes.sol"; /// @title ISablierV2MerkleLockupFactory /// @notice Deploys new Lockup Linear Merkle lockups via CREATE2. @@ -24,6 +26,16 @@ interface ISablierV2MerkleLockupFactory { uint256 recipientsCount ); + /// @notice Emitted when a Sablier V2 Lockup Tranched Merkle Lockup is created. + event CreateMerkleLockupLT( + ISablierV2MerkleLockupLT indexed merkleLockupLT, + MerkleLockup.ConstructorParams indexed baseParams, + ISablierV2LockupTranched lockupTranched, + MerkleLockupLT.TrancheWithPercentage[] tranchesWithPercentages, + uint256 aggregateAmount, + uint256 recipientsCount + ); + /*////////////////////////////////////////////////////////////////////////// NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ @@ -46,4 +58,27 @@ interface ISablierV2MerkleLockupFactory { ) external returns (ISablierV2MerkleLockupLL merkleLockupLL); + + /// @notice Creates a new Merkle Lockup that uses Lockup Tranched. + /// @dev Emits a {CreateMerkleLockupLT} event. + /// + /// Requirements: + /// - The sum of the tranches' unlock percentages must equal 100% = 1e18. + /// + /// @param baseParams Struct encapsulating the {SablierV2MerkleLockup} parameters, which are documented in + /// {DataTypes}. + /// @param lockupTranched The address of the {SablierV2LockupTranched} contract. + /// @param tranchesWithPercentages The tranches with their respective unlock percentages. + /// @param aggregateAmount Total amount of ERC-20 assets to be streamed to all recipients. + /// @param recipientsCount Total number of recipients eligible to claim. + /// @return merkleLockupLT The address of the newly created Merkle Lockup contract. + function createMerkleLockupLT( + MerkleLockup.ConstructorParams memory baseParams, + ISablierV2LockupTranched lockupTranched, + MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages, + uint256 aggregateAmount, + uint256 recipientsCount + ) + external + returns (ISablierV2MerkleLockupLT merkleLockupLT); } diff --git a/src/interfaces/ISablierV2MerkleLockupLT.sol b/src/interfaces/ISablierV2MerkleLockupLT.sol new file mode 100644 index 00000000..e34d53fe --- /dev/null +++ b/src/interfaces/ISablierV2MerkleLockupLT.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity >=0.8.22; + +import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; + +import { ISablierV2MerkleLockup } from "./ISablierV2MerkleLockup.sol"; +import { MerkleLockupLT } from "./../types/DataTypes.sol"; + +/// @title ISablierV2MerkleLockupLT +/// @notice Merkle Lockup that creates Lockup Tranched streams. +interface ISablierV2MerkleLockupLT is ISablierV2MerkleLockup { + /*////////////////////////////////////////////////////////////////////////// + CONSTANT FUNCTIONS + //////////////////////////////////////////////////////////////////////////*/ + + /// @notice Retrieves the tranches with their respective unlock percentages and durations. + function getTranchesWithPercentages() external view returns (MerkleLockupLT.TrancheWithPercentage[] memory); + + /// @notice The address of the {SablierV2LockupTranched} contract. + function LOCKUP_TRANCHED() external view returns (ISablierV2LockupTranched); + + /*////////////////////////////////////////////////////////////////////////// + NON-CONSTANT FUNCTIONS + //////////////////////////////////////////////////////////////////////////*/ + + /// @notice Makes the claim by creating a Lockup Tranched stream to the recipient. + /// + /// @dev Emits a {Claim} event. + /// + /// Requirements: + /// - The campaign must not have expired. + /// - The stream must not have been claimed already. + /// - The Merkle proof must be valid. + /// + /// @param index The index of the recipient in the Merkle tree. + /// @param recipient The address of the stream holder. + /// @param amount The amount of tokens to be streamed. + /// @param merkleProof The Merkle proof of inclusion in the stream. + /// @return streamId The id of the newly created stream. + function claim( + uint256 index, + address recipient, + uint128 amount, + bytes32[] calldata merkleProof + ) + external + returns (uint256 streamId); +} diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index 0477df58..bf8bec32 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -28,4 +28,11 @@ library Errors { /// @notice Thrown when trying to claim the same stream more than once. error SablierV2MerkleLockup_StreamClaimed(uint256 index); + + /*////////////////////////////////////////////////////////////////////////// + SABLIER-V2-MERKLE-LOCKUP-FACTORY + //////////////////////////////////////////////////////////////////////////*/ + + /// @notice Thrown when the sum of the tranches' unlock percentages does not equal 100%. + error SablierV2MerkleLockupFactory_TotalPercentageNotEqualOneHundred(uint256 totalPercentage); } diff --git a/src/types/DataTypes.sol b/src/types/DataTypes.sol index 85ba726a..21cdcffb 100644 --- a/src/types/DataTypes.sol +++ b/src/types/DataTypes.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { UD2x18 } from "@prb/math/src/UD2x18.sol"; import { ISablierV2Lockup } from "@sablier/v2-core/src/interfaces/ISablierV2Lockup.sol"; import { Broker, LockupDynamic, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; @@ -83,3 +84,17 @@ library MerkleLockup { bool transferable; } } + +library MerkleLockupLT { + /// @notice Struct encapsulating the amount percentage and the tranche duration of the stream. + /// @dev Each recipient may have a different amount allocated, this struct stores the percentage of the + /// amount designated for each duration unlock. We use a 18 decimals format to represent percentages: + /// 100% = 1e18. + /// @param unlockPercentage The percentage of the amount designated to be unlocked in this tranche. + /// @param duration The time difference in seconds between this tranche and the previous one. + struct TrancheWithPercentage { + // slot 0 + UD2x18 unlockPercentage; + uint40 duration; + } +} diff --git a/test/Base.t.sol b/test/Base.t.sol index e363a2cf..23fecf6d 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -7,6 +7,7 @@ import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/I import { ISablierV2Comptroller } from "@sablier/v2-core/src/interfaces/ISablierV2Comptroller.sol"; import { ISablierV2LockupDynamic } from "@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; +import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; import { LockupDynamic, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; import { Assertions as V2CoreAssertions } from "@sablier/v2-core/test/utils/Assertions.sol"; @@ -15,11 +16,14 @@ import { Utils as V2CoreUtils } from "@sablier/v2-core/test/utils/Utils.sol"; import { ISablierV2Batch } from "src/interfaces/ISablierV2Batch.sol"; import { ISablierV2MerkleLockupFactory } from "src/interfaces/ISablierV2MerkleLockupFactory.sol"; import { ISablierV2MerkleLockupLL } from "src/interfaces/ISablierV2MerkleLockupLL.sol"; +import { ISablierV2MerkleLockupLT } from "src/interfaces/ISablierV2MerkleLockupLT.sol"; import { SablierV2Batch } from "src/SablierV2Batch.sol"; import { SablierV2MerkleLockupFactory } from "src/SablierV2MerkleLockupFactory.sol"; import { SablierV2MerkleLockupLL } from "src/SablierV2MerkleLockupLL.sol"; +import { SablierV2MerkleLockupLT } from "src/SablierV2MerkleLockupLT.sol"; import { ERC20Mock } from "./mocks/erc20/ERC20Mock.sol"; +import { Assertions } from "./utils/Assertions.sol"; import { Defaults } from "./utils/Defaults.sol"; import { DeployOptimized } from "./utils/DeployOptimized.sol"; import { Events } from "./utils/Events.sol"; @@ -27,7 +31,7 @@ import { Merkle } from "./utils/Murky.sol"; import { Users } from "./utils/Types.sol"; /// @notice Base test contract with common logic needed by all tests. -abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions, V2CoreUtils { +abstract contract Base_Test is Assertions, DeployOptimized, Events, Merkle, V2CoreAssertions, V2CoreUtils { /*////////////////////////////////////////////////////////////////////////// VARIABLES //////////////////////////////////////////////////////////////////////////*/ @@ -44,8 +48,10 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions Defaults internal defaults; ISablierV2LockupDynamic internal lockupDynamic; ISablierV2LockupLinear internal lockupLinear; + ISablierV2LockupTranched internal lockupTranched; ISablierV2MerkleLockupFactory internal merkleLockupFactory; ISablierV2MerkleLockupLL internal merkleLockupLL; + ISablierV2MerkleLockupLT internal merkleLockupLT; /*////////////////////////////////////////////////////////////////////////// SET-UP FUNCTION @@ -105,6 +111,7 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions vm.label({ account: address(comptroller), newLabel: "Comptroller" }); vm.label({ account: address(lockupDynamic), newLabel: "LockupDynamic" }); vm.label({ account: address(lockupLinear), newLabel: "LockupLinear" }); + vm.label({ account: address(lockupTranched), newLabel: "LockupTranched" }); } /*////////////////////////////////////////////////////////////////////////// @@ -292,6 +299,48 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions }); } + function computeMerkleLockupLTAddress( + address admin, + bytes32 merkleRoot, + uint40 expiration + ) + internal + returns (address) + { + return computeMerkleLockupLTAddress(admin, dai, merkleRoot, expiration); + } + + function computeMerkleLockupLTAddress( + address admin, + IERC20 asset_, + bytes32 merkleRoot, + uint40 expiration + ) + internal + returns (address) + { + bytes32 salt = keccak256( + abi.encodePacked( + admin, + address(asset_), + abi.encode(defaults.IPFS_CID()), + defaults.NAME_BYTES32(), + merkleRoot, + expiration, + defaults.CANCELABLE(), + defaults.TRANSFERABLE(), + lockupTranched, + abi.encode(defaults.tranchesWithPercentages()) + ) + ); + bytes32 creationBytecodeHash = keccak256(getMerkleLockupLTBytecode(admin, asset_, merkleRoot, expiration)); + return computeCreate2Address({ + salt: salt, + initcodeHash: creationBytecodeHash, + deployer: address(merkleLockupFactory) + }); + } + function getMerkleLockupLLBytecode( address admin, IERC20 asset_, @@ -311,4 +360,27 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, V2CoreAssertions ); } } + + function getMerkleLockupLTBytecode( + address admin, + IERC20 asset_, + bytes32 merkleRoot, + uint40 expiration + ) + internal + returns (bytes memory) + { + bytes memory constructorArgs = abi.encode( + defaults.baseParams(admin, asset_, merkleRoot, expiration), + lockupTranched, + defaults.tranchesWithPercentages() + ); + if (!isTestOptimizedProfile()) { + return bytes.concat(type(SablierV2MerkleLockupLT).creationCode, constructorArgs); + } else { + return bytes.concat( + vm.getCode("out-optimized/SablierV2MerkleLockupLT.sol/SablierV2MerkleLockupLT.json"), constructorArgs + ); + } + } } diff --git a/test/fork/Fork.t.sol b/test/fork/Fork.t.sol index d0730733..a106242a 100644 --- a/test/fork/Fork.t.sol +++ b/test/fork/Fork.t.sol @@ -4,6 +4,7 @@ pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2LockupDynamic } from "@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; +import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; import { Precompiles as V2CorePrecompiles } from "@sablier/v2-core/test/utils/Precompiles.sol"; import { Fuzzers as V2CoreFuzzers } from "@sablier/v2-core/test/utils/Fuzzers.sol"; @@ -70,6 +71,7 @@ abstract contract Fork_Test is Base_Test, V2CoreFuzzers { vm.assume(user != recipient); vm.assume(user != address(lockupDynamic) && recipient != address(lockupDynamic)); vm.assume(user != address(lockupLinear) && recipient != address(lockupLinear)); + vm.assume(user != address(lockupTranched) && recipient != address(lockupTranched)); // Avoid users blacklisted by USDC or USDT. assumeNoBlacklisted(address(ASSET), user); @@ -80,11 +82,12 @@ abstract contract Fork_Test is Base_Test, V2CoreFuzzers { function loadDependencies() private { lockupDynamic = ISablierV2LockupDynamic(0x7CC7e125d83A581ff438608490Cc0f7bDff79127); lockupLinear = ISablierV2LockupLinear(0xAFb979d9afAd1aD27C5eFf4E27226E3AB9e5dCC9); + lockupTranched = ISablierV2LockupTranched(0xAFb979d9afAd1aD27C5eFf4E27226E3AB9e5dCC9); } /// @dev Deploys the v2 core dependencies. // TODO: Remove this function once the v2 core contracts are deployed on Mainnet. function deployDependencies() private { - (, lockupDynamic, lockupLinear,) = new V2CorePrecompiles().deployCore(users.admin); + (, lockupDynamic, lockupLinear, lockupTranched,) = new V2CorePrecompiles().deployCore(users.admin); } } diff --git a/test/fork/assets/USDC.t.sol b/test/fork/assets/USDC.t.sol index 2b47c4d5..76b283c3 100644 --- a/test/fork/assets/USDC.t.sol +++ b/test/fork/assets/USDC.t.sol @@ -6,6 +6,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { CreateWithTimestamps_LockupDynamic_Batch_Fork_Test } from "../batch/createWithTimestampsLD.t.sol"; import { CreateWithTimestamps_LockupLinear_Batch_Fork_Test } from "../batch/createWithTimestampsLL.t.sol"; import { MerkleLockupLL_Fork_Test } from "../merkle-lockup/MerkleLockupLL.t.sol"; +import { MerkleLockupLT_Fork_Test } from "../merkle-lockup/MerkleLockupLT.t.sol"; /// @dev An ERC-20 asset with 6 decimals. IERC20 constant usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); @@ -19,3 +20,5 @@ contract USDC_CreateWithTimestamps_LockupLinear_Batch_Fork_Test is { } contract USDC_MerkleLockupLL_Fork_Test is MerkleLockupLL_Fork_Test(usdc) { } + +contract USDC_MerkleLockupLT_Fork_Test is MerkleLockupLT_Fork_Test(usdc) { } diff --git a/test/fork/assets/USDT.t.sol b/test/fork/assets/USDT.t.sol index 83c76459..0e60fea1 100644 --- a/test/fork/assets/USDT.t.sol +++ b/test/fork/assets/USDT.t.sol @@ -6,6 +6,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { CreateWithTimestamps_LockupDynamic_Batch_Fork_Test } from "../batch/createWithTimestampsLD.t.sol"; import { CreateWithTimestamps_LockupLinear_Batch_Fork_Test } from "../batch/createWithTimestampsLL.t.sol"; import { MerkleLockupLL_Fork_Test } from "../merkle-lockup/MerkleLockupLL.t.sol"; +import { MerkleLockupLT_Fork_Test } from "../merkle-lockup/MerkleLockupLT.t.sol"; /// @dev An ERC-20 asset that suffers from the missing return value bug. IERC20 constant usdt = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7); @@ -19,3 +20,5 @@ contract USDT_CreateWithTimestamps_LockupLinear_Batch_Fork_Test is { } contract USDT_MerkleLockupLL_Fork_Test is MerkleLockupLL_Fork_Test(usdt) { } + +contract USDT_MerkleLockupLT_Fork_Test is MerkleLockupLT_Fork_Test(usdt) { } diff --git a/test/fork/merkle-lockup/MerkleLockupLL.t.sol b/test/fork/merkle-lockup/MerkleLockupLL.t.sol index 5f556679..c154d672 100644 --- a/test/fork/merkle-lockup/MerkleLockupLL.t.sol +++ b/test/fork/merkle-lockup/MerkleLockupLL.t.sol @@ -76,10 +76,8 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { vars.indexes[i] = params.leafData[i].index; // Bound each leaf amount so that `aggregateAmount` does not overflow. - params.leafData[i].amount = - uint128(_bound(params.leafData[i].amount, 1, MAX_UINT256 / vars.recipientsCount - 1)); - vars.amounts[i] = params.leafData[i].amount; - vars.aggregateAmount += params.leafData[i].amount; + vars.amounts[i] = uint128(_bound(params.leafData[i].amount, 1, MAX_UINT256 / vars.recipientsCount - 1)); + vars.aggregateAmount += vars.amounts[i]; // Avoid zero recipient addresses. uint256 boundedRecipientSeed = _bound(params.leafData[i].recipientSeed, 1, type(uint160).max); diff --git a/test/fork/merkle-lockup/MerkleLockupLT.t.sol b/test/fork/merkle-lockup/MerkleLockupLT.t.sol new file mode 100644 index 00000000..20ddf82c --- /dev/null +++ b/test/fork/merkle-lockup/MerkleLockupLT.t.sol @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22 <0.9.0; + +import { Arrays } from "@openzeppelin/contracts/utils/Arrays.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { Lockup, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; + +import { ISablierV2MerkleLockupLT } from "src/interfaces/ISablierV2MerkleLockupLT.sol"; +import { MerkleLockup } from "src/types/DataTypes.sol"; + +import { MerkleBuilder } from "../../utils/MerkleBuilder.sol"; +import { Fork_Test } from "../Fork.t.sol"; + +abstract contract MerkleLockupLT_Fork_Test is Fork_Test { + using MerkleBuilder for uint256[]; + + constructor(IERC20 asset_) Fork_Test(asset_) { } + + function setUp() public virtual override { + Fork_Test.setUp(); + } + + /// @dev Encapsulates the data needed to compute a Merkle tree leaf. + struct LeafData { + uint256 index; + uint256 recipientSeed; + uint128 amount; + } + + struct Params { + address admin; + uint40 expiration; + LeafData[] leafData; + uint256 posBeforeSort; + } + + struct Vars { + uint256 actualStreamId; + LockupTranched.Tranche[] actualTranches; + LockupTranched.StreamLT actualStream; + uint128[] amounts; + uint256 aggregateAmount; + uint128 clawbackAmount; + address expectedLockupLT; + MerkleLockup.ConstructorParams baseParams; + LockupTranched.StreamLT expectedStream; + uint256 expectedStreamId; + uint256[] indexes; + uint256 leafPos; + uint256 leafToClaim; + ISablierV2MerkleLockupLT merkleLockupLT; + bytes32 merkleRoot; + address[] recipients; + uint256 recipientsCount; + } + + // We need the leaves as a storage variable so that we can use OpenZeppelin's {Arrays.findUpperBound}. + uint256[] public leaves; + + function testForkFuzz_MerkleLockupLT(Params memory params) external { + vm.assume(params.admin != address(0) && params.admin != users.admin); + vm.assume(params.expiration == 0 || params.expiration > block.timestamp); + vm.assume(params.leafData.length > 1); + params.posBeforeSort = _bound(params.posBeforeSort, 0, params.leafData.length - 1); + assumeNoBlacklisted({ token: address(ASSET), addr: params.admin }); + + /*////////////////////////////////////////////////////////////////////////// + CREATE + //////////////////////////////////////////////////////////////////////////*/ + + Vars memory vars; + vars.recipientsCount = params.leafData.length; + vars.amounts = new uint128[](vars.recipientsCount); + vars.indexes = new uint256[](vars.recipientsCount); + vars.recipients = new address[](vars.recipientsCount); + for (uint256 i = 0; i < vars.recipientsCount; ++i) { + vars.indexes[i] = params.leafData[i].index; + + // Bound each leaf amount so that `aggregateAmount` does not overflow. + vars.amounts[i] = uint128(_bound(params.leafData[i].amount, 1, MAX_UINT256 / vars.recipientsCount - 1)); + vars.aggregateAmount += vars.amounts[i]; + + // Avoid zero recipient addresses. + uint256 boundedRecipientSeed = _bound(params.leafData[i].recipientSeed, 1, type(uint160).max); + vars.recipients[i] = address(uint160(boundedRecipientSeed)); + } + + leaves = new uint256[](vars.recipientsCount); + leaves = MerkleBuilder.computeLeaves(vars.indexes, vars.recipients, vars.amounts); + + // Sort the leaves in ascending order to match the production environment. + MerkleBuilder.sortLeaves(leaves); + vars.merkleRoot = getRoot(leaves.toBytes32()); + + vars.expectedLockupLT = computeMerkleLockupLTAddress(params.admin, ASSET, vars.merkleRoot, params.expiration); + + vars.baseParams = defaults.baseParams({ + admin: params.admin, + asset_: ASSET, + merkleRoot: vars.merkleRoot, + expiration: params.expiration + }); + + vm.expectEmit({ emitter: address(merkleLockupFactory) }); + emit CreateMerkleLockupLT({ + merkleLockupLT: ISablierV2MerkleLockupLT(vars.expectedLockupLT), + baseParams: vars.baseParams, + lockupTranched: lockupTranched, + tranchesWithPercentages: defaults.tranchesWithPercentages(), + aggregateAmount: vars.aggregateAmount, + recipientsCount: vars.recipientsCount + }); + + vars.merkleLockupLT = merkleLockupFactory.createMerkleLockupLT({ + baseParams: vars.baseParams, + lockupTranched: lockupTranched, + tranchesWithPercentages: defaults.tranchesWithPercentages(), + aggregateAmount: vars.aggregateAmount, + recipientsCount: vars.recipientsCount + }); + + // Fund the Merkle Lockup contract. + deal({ token: address(ASSET), to: address(vars.merkleLockupLT), give: vars.aggregateAmount }); + + assertGt(address(vars.merkleLockupLT).code.length, 0, "MerkleLockupLT contract not created"); + assertEq( + address(vars.merkleLockupLT), + vars.expectedLockupLT, + "MerkleLockupLT contract does not match computed address" + ); + + /*////////////////////////////////////////////////////////////////////////// + CLAIM + //////////////////////////////////////////////////////////////////////////*/ + + assertFalse(vars.merkleLockupLT.hasClaimed(vars.indexes[params.posBeforeSort])); + + vars.leafToClaim = MerkleBuilder.computeLeaf( + vars.indexes[params.posBeforeSort], + vars.recipients[params.posBeforeSort], + vars.amounts[params.posBeforeSort] + ); + vars.leafPos = Arrays.findUpperBound(leaves, vars.leafToClaim); + + vars.expectedStreamId = lockupTranched.nextStreamId(); + emit Claim( + vars.indexes[params.posBeforeSort], + vars.recipients[params.posBeforeSort], + vars.amounts[params.posBeforeSort], + vars.expectedStreamId + ); + vars.actualStreamId = vars.merkleLockupLT.claim({ + index: vars.indexes[params.posBeforeSort], + recipient: vars.recipients[params.posBeforeSort], + amount: vars.amounts[params.posBeforeSort], + merkleProof: getProof(leaves.toBytes32(), vars.leafPos) + }); + + vars.actualStream = lockupTranched.getStream(vars.actualStreamId); + vars.expectedStream = LockupTranched.StreamLT({ + amounts: Lockup.Amounts({ deposited: vars.amounts[params.posBeforeSort], refunded: 0, withdrawn: 0 }), + asset: ASSET, + endTime: uint40(block.timestamp) + defaults.TOTAL_DURATION(), + isCancelable: defaults.CANCELABLE(), + isDepleted: false, + isStream: true, + isTransferable: defaults.TRANSFERABLE(), + sender: params.admin, + startTime: uint40(block.timestamp), + tranches: defaults.tranches({ totalAmount: vars.amounts[params.posBeforeSort] }), + wasCanceled: false + }); + + assertTrue(vars.merkleLockupLT.hasClaimed(vars.indexes[params.posBeforeSort])); + assertEq(vars.actualStreamId, vars.expectedStreamId); + assertEq(vars.actualStream, vars.expectedStream); + + /*////////////////////////////////////////////////////////////////////////// + CLAWBACK + //////////////////////////////////////////////////////////////////////////*/ + + if (params.expiration > 0) { + vars.clawbackAmount = uint128(ASSET.balanceOf(address(vars.merkleLockupLT))); + vm.warp({ timestamp: uint256(params.expiration) + 1 seconds }); + + changePrank({ msgSender: params.admin }); + expectCallToTransfer({ asset_: address(ASSET), to: params.admin, amount: vars.clawbackAmount }); + vm.expectEmit({ emitter: address(vars.merkleLockupLT) }); + emit Clawback({ to: params.admin, admin: params.admin, amount: vars.clawbackAmount }); + vars.merkleLockupLT.clawback({ to: params.admin, amount: vars.clawbackAmount }); + } + } +} diff --git a/test/integration/Integration.t.sol b/test/integration/Integration.t.sol index 470b575a..9d81ea0c 100644 --- a/test/integration/Integration.t.sol +++ b/test/integration/Integration.t.sol @@ -37,6 +37,6 @@ abstract contract Integration_Test is Base_Test { //////////////////////////////////////////////////////////////////////////*/ function deployDependencies() private { - (comptroller, lockupDynamic, lockupLinear,) = new V2CorePrecompiles().deployCore(users.admin); + (comptroller, lockupDynamic, lockupLinear, lockupTranched,) = new V2CorePrecompiles().deployCore(users.admin); } } diff --git a/test/integration/merkle-lockup/MerkleLockup.t.sol b/test/integration/merkle-lockup/MerkleLockup.t.sol index 85004f77..7d5ce8c1 100644 --- a/test/integration/merkle-lockup/MerkleLockup.t.sol +++ b/test/integration/merkle-lockup/MerkleLockup.t.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.22; import { ISablierV2MerkleLockupLL } from "src/interfaces/ISablierV2MerkleLockupLL.sol"; +import { ISablierV2MerkleLockupLT } from "src/interfaces/ISablierV2MerkleLockupLT.sol"; import { Integration_Test } from "../Integration.t.sol"; @@ -9,13 +10,19 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { function setUp() public virtual override { Integration_Test.setUp(); - // Create the default Merkle Lockup. + // Create the default Merkle Lockup contracts. merkleLockupLL = createMerkleLockupLL(); + merkleLockupLT = createMerkleLockupLT(); - // Fund the Merkle Lockup contract. + // Fund the Merkle Lockup contracts. deal({ token: address(dai), to: address(merkleLockupLL), give: defaults.AGGREGATE_AMOUNT() }); + deal({ token: address(dai), to: address(merkleLockupLT), give: defaults.AGGREGATE_AMOUNT() }); } + /*////////////////////////////////////////////////////////////////////////// + MERKLE-LOCKUP-LL + //////////////////////////////////////////////////////////////////////////*/ + function claimLL() internal returns (uint256) { return merkleLockupLL.claim({ index: defaults.INDEX1(), @@ -62,4 +69,55 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { recipientsCount: defaults.RECIPIENTS_COUNT() }); } + + /*////////////////////////////////////////////////////////////////////////// + MERKLE-LOCKUP-LT + //////////////////////////////////////////////////////////////////////////*/ + + function claimLT() internal returns (uint256) { + return merkleLockupLT.claim({ + index: defaults.INDEX1(), + recipient: users.recipient1, + amount: defaults.CLAIM_AMOUNT(), + merkleProof: defaults.index1Proof() + }); + } + + function computeMerkleLockupLTAddress() internal returns (address) { + return computeMerkleLockupLTAddress(users.admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); + } + + function computeMerkleLockupLTAddress(address admin) internal returns (address) { + return computeMerkleLockupLTAddress(admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); + } + + function computeMerkleLockupLTAddress(address admin, uint40 expiration) internal returns (address) { + return computeMerkleLockupLTAddress(admin, defaults.MERKLE_ROOT(), expiration); + } + + function computeMerkleLockupLTAddress(address admin, bytes32 merkleRoot) internal returns (address) { + return computeMerkleLockupLTAddress(admin, merkleRoot, defaults.EXPIRATION()); + } + + function createMerkleLockupLT() internal returns (ISablierV2MerkleLockupLT) { + return createMerkleLockupLT(users.admin, defaults.EXPIRATION()); + } + + function createMerkleLockupLT(address admin) internal returns (ISablierV2MerkleLockupLT) { + return createMerkleLockupLT(admin, defaults.EXPIRATION()); + } + + function createMerkleLockupLT(uint40 expiration) internal returns (ISablierV2MerkleLockupLT) { + return createMerkleLockupLT(users.admin, expiration); + } + + function createMerkleLockupLT(address admin, uint40 expiration) internal returns (ISablierV2MerkleLockupLT) { + return merkleLockupFactory.createMerkleLockupLT({ + baseParams: defaults.baseParams(admin, dai, defaults.MERKLE_ROOT(), expiration), + lockupTranched: lockupTranched, + tranchesWithPercentages: defaults.tranchesWithPercentages(), + aggregateAmount: defaults.AGGREGATE_AMOUNT(), + recipientsCount: defaults.RECIPIENTS_COUNT() + }); + } } diff --git a/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.t.sol b/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.t.sol new file mode 100644 index 00000000..e16db62c --- /dev/null +++ b/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.t.sol @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22 <0.9.0; + +import { ud2x18 } from "@prb/math/src/UD2x18.sol"; + +import { Errors } from "src/libraries/Errors.sol"; +import { ISablierV2MerkleLockupLT } from "src/interfaces/ISablierV2MerkleLockupLT.sol"; +import { MerkleLockup, MerkleLockupLT } from "src/types/DataTypes.sol"; + +import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; + +contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test { + function setUp() public override { + MerkleLockup_Integration_Test.setUp(); + } + + modifier whenTotalPercentageIsNotOneHundred() { + _; + } + + function test_RevertWhen_TotalPercentageLessThanOneHundred() external whenTotalPercentageIsNotOneHundred { + MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); + uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); + uint256 recipientsCount = defaults.RECIPIENTS_COUNT(); + + MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); + tranchesWithPercentages[0].unlockPercentage = ud2x18(0.05e18); + + uint256 totalPercentage = tranchesWithPercentages[0].unlockPercentage.intoUint256() + + tranchesWithPercentages[1].unlockPercentage.intoUint256(); + + vm.expectRevert( + abi.encodeWithSelector( + Errors.SablierV2MerkleLockupFactory_TotalPercentageNotEqualOneHundred.selector, totalPercentage + ) + ); + + merkleLockupFactory.createMerkleLockupLT({ + baseParams: baseParams, + lockupTranched: lockupTranched, + tranchesWithPercentages: tranchesWithPercentages, + aggregateAmount: aggregateAmount, + recipientsCount: recipientsCount + }); + } + + function test_RevertWhen_TotalPercentageGreaterThanOneHundred() external whenTotalPercentageIsNotOneHundred { + MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); + uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); + uint256 recipientsCount = defaults.RECIPIENTS_COUNT(); + + MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); + tranchesWithPercentages[0].unlockPercentage = ud2x18(0.75e18); + + uint256 totalPercentage = tranchesWithPercentages[0].unlockPercentage.intoUint256() + + tranchesWithPercentages[1].unlockPercentage.intoUint256(); + + vm.expectRevert( + abi.encodeWithSelector( + Errors.SablierV2MerkleLockupFactory_TotalPercentageNotEqualOneHundred.selector, totalPercentage + ) + ); + + merkleLockupFactory.createMerkleLockupLT({ + baseParams: baseParams, + lockupTranched: lockupTranched, + tranchesWithPercentages: tranchesWithPercentages, + aggregateAmount: aggregateAmount, + recipientsCount: recipientsCount + }); + } + + modifier whenTotalPercentageIsOneHundred() { + _; + } + + function test_RevertWhen_CampaignNameTooLong() external whenTotalPercentageIsOneHundred { + MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); + MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); + uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); + uint256 recipientsCount = defaults.RECIPIENTS_COUNT(); + + baseParams.name = "this string is longer than 32 characters"; + + vm.expectRevert( + abi.encodeWithSelector( + Errors.SablierV2MerkleLockup_CampaignNameTooLong.selector, bytes(baseParams.name).length, 32 + ) + ); + + merkleLockupFactory.createMerkleLockupLT({ + baseParams: baseParams, + lockupTranched: lockupTranched, + tranchesWithPercentages: tranchesWithPercentages, + aggregateAmount: aggregateAmount, + recipientsCount: recipientsCount + }); + } + + modifier whenCampaignNameIsNotTooLong() { + _; + } + + /// @dev This test works because a default Merkle Lockup contract is deployed in {Integration_Test.setUp} + function test_RevertGiven_AlreadyCreated() external whenTotalPercentageIsOneHundred whenCampaignNameIsNotTooLong { + MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); + MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); + uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); + uint256 recipientsCount = defaults.RECIPIENTS_COUNT(); + + vm.expectRevert(); + merkleLockupFactory.createMerkleLockupLT({ + baseParams: baseParams, + lockupTranched: lockupTranched, + tranchesWithPercentages: tranchesWithPercentages, + aggregateAmount: aggregateAmount, + recipientsCount: recipientsCount + }); + } + + modifier givenNotAlreadyCreated() { + _; + } + + function testFuzz_CreateMerkleLockupLT( + address admin, + uint40 expiration + ) + external + whenTotalPercentageIsOneHundred + whenCampaignNameIsNotTooLong + givenNotAlreadyCreated + { + vm.assume(admin != users.admin); + address expectedLockupLT = computeMerkleLockupLTAddress(admin, expiration); + + MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams({ + admin: admin, + asset_: dai, + merkleRoot: defaults.MERKLE_ROOT(), + expiration: expiration + }); + + vm.expectEmit({ emitter: address(merkleLockupFactory) }); + emit CreateMerkleLockupLT({ + merkleLockupLT: ISablierV2MerkleLockupLT(expectedLockupLT), + baseParams: baseParams, + lockupTranched: lockupTranched, + tranchesWithPercentages: defaults.tranchesWithPercentages(), + aggregateAmount: defaults.AGGREGATE_AMOUNT(), + recipientsCount: defaults.RECIPIENTS_COUNT() + }); + + address actualLockupLT = address(createMerkleLockupLT(admin, expiration)); + + assertGt(actualLockupLT.code.length, 0, "MerkleLockupLT contract not created"); + assertEq(actualLockupLT, expectedLockupLT, "MerkleLockupLT contract does not match computed address"); + } +} diff --git a/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.tree b/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.tree new file mode 100644 index 00000000..e319653d --- /dev/null +++ b/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.tree @@ -0,0 +1,15 @@ +createMerkleLockupLL.t.sol +├── when the total percentage does not equal 100% +│ ├── when the total percentage is less than 100% +│ │ └── it should revert +│ └── when the total percentage is greater than 100% +│ └── it should revert +└── when the total percentage equals 100% + ├── when the campaign name is too long + │ └── it should revert + └── when the campaign name is not too long + ├── given the campaign has been already created + │ └── it should revert + └── given the campaign has not been already created + ├── it should create the campaign + └── it should emit a {CreateMerkleLockupLT} event diff --git a/test/integration/merkle-lockup/ll/claim/claim.t.sol b/test/integration/merkle-lockup/ll/claim/claim.t.sol index 3d133a22..765a3419 100644 --- a/test/integration/merkle-lockup/ll/claim/claim.t.sol +++ b/test/integration/merkle-lockup/ll/claim/claim.t.sol @@ -2,7 +2,6 @@ pragma solidity >=0.8.22 <0.9.0; import { Lockup, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { ud, UD60x18 } from "@prb/math/src/UD60x18.sol"; import { Errors } from "src/libraries/Errors.sol"; @@ -102,35 +101,8 @@ contract Claim_Integration_Test is MerkleLockup_Integration_Test { _; } - function test_Claim_ProtocolFeeNotZero() - external - givenCampaignNotExpired - givenNotClaimed - givenIncludedInMerkleTree - { - changePrank({ msgSender: users.admin }); - comptroller.setProtocolFee({ asset: dai, newProtocolFee: ud(0.1e18) }); - - test_Claim({ protocolFee: ud(0.1e18) }); - } - - modifier givenProtocolFeeZero() { - _; - } - - function test_Claim() - external - givenCampaignNotExpired - givenNotClaimed - givenIncludedInMerkleTree - givenProtocolFeeZero - { - test_Claim({ protocolFee: ud(0) }); - } - - function test_Claim(UD60x18 protocolFee) internal { + function test_Claim() external givenCampaignNotExpired givenNotClaimed givenIncludedInMerkleTree { uint256 expectedStreamId = lockupLinear.nextStreamId(); - uint128 feeAmount = uint128(ud(defaults.CLAIM_AMOUNT()).mul(protocolFee).intoUint256()); vm.expectEmit({ emitter: address(merkleLockupLL) }); emit Claim(defaults.INDEX1(), users.recipient1, defaults.CLAIM_AMOUNT(), expectedStreamId); @@ -138,7 +110,7 @@ contract Claim_Integration_Test is MerkleLockup_Integration_Test { LockupLinear.StreamLL memory actualStream = lockupLinear.getStream(actualStreamId); LockupLinear.StreamLL memory expectedStream = LockupLinear.StreamLL({ - amounts: Lockup.Amounts({ deposited: defaults.CLAIM_AMOUNT() - feeAmount, refunded: 0, withdrawn: 0 }), + amounts: Lockup.Amounts({ deposited: defaults.CLAIM_AMOUNT(), refunded: 0, withdrawn: 0 }), asset: dai, cliffTime: uint40(block.timestamp) + defaults.CLIFF_DURATION(), endTime: uint40(block.timestamp) + defaults.TOTAL_DURATION(), diff --git a/test/integration/merkle-lockup/ll/claim/claim.tree b/test/integration/merkle-lockup/ll/claim/claim.tree index 00fd2f61..35461bb2 100644 --- a/test/integration/merkle-lockup/ll/claim/claim.tree +++ b/test/integration/merkle-lockup/ll/claim/claim.tree @@ -15,11 +15,6 @@ claim.t.sol │ └── when the Merkle proof is not valid │ └── it should revert └── given the claim is included in the Merkle tree - ├── given the protocol fee is greater than zero - │ ├── it should mark the index as claimed - │ ├── it should create a LockupLinear stream - │ └── it should emit a {Claim} event - └── given the protocol fee is not greater than zero - ├── it should mark the index as claimed - ├── it should create a LockupLinear stream - └── it should emit a {Claim} event + ├── it should mark the index as claimed + ├── it should create a LockupLinear stream + └── it should emit a {Claim} event diff --git a/test/integration/merkle-lockup/lt/claim/claim.t.sol b/test/integration/merkle-lockup/lt/claim/claim.t.sol new file mode 100644 index 00000000..271042e6 --- /dev/null +++ b/test/integration/merkle-lockup/lt/claim/claim.t.sol @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22 <0.9.0; + +import { Arrays } from "@openzeppelin/contracts/utils/Arrays.sol"; +import { Lockup, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; + +import { ISablierV2MerkleLockupLT } from "src/interfaces/ISablierV2MerkleLockupLT.sol"; +import { Errors } from "src/libraries/Errors.sol"; +import { MerkleLockup } from "src/types/DataTypes.sol"; + +import { MerkleBuilder } from "../../../../utils/MerkleBuilder.sol"; +import { Merkle } from "../../../../utils/Murky.sol"; + +import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; + +contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { + using MerkleBuilder for uint256[]; + + function setUp() public virtual override { + MerkleLockup_Integration_Test.setUp(); + } + + function test_RevertGiven_CampaignExpired() external { + uint40 expiration = defaults.EXPIRATION(); + uint256 warpTime = expiration + 1 seconds; + bytes32[] memory merkleProof; + vm.warp({ timestamp: warpTime }); + vm.expectRevert( + abi.encodeWithSelector(Errors.SablierV2MerkleLockup_CampaignExpired.selector, warpTime, expiration) + ); + merkleLockupLT.claim({ index: 1, recipient: users.recipient1, amount: 1, merkleProof: merkleProof }); + } + + modifier givenCampaignNotExpired() { + _; + } + + function test_RevertGiven_AlreadyClaimed() external givenCampaignNotExpired { + claimLT(); + uint256 index1 = defaults.INDEX1(); + uint128 amount = defaults.CLAIM_AMOUNT(); + bytes32[] memory merkleProof = defaults.index1Proof(); + vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_StreamClaimed.selector, index1)); + merkleLockupLT.claim(index1, users.recipient1, amount, merkleProof); + } + + modifier givenNotClaimed() { + _; + } + + modifier givenNotIncludedInMerkleTree() { + _; + } + + function test_RevertWhen_InvalidIndex() + external + givenCampaignNotExpired + givenNotClaimed + givenNotIncludedInMerkleTree + { + uint256 invalidIndex = 1337; + uint128 amount = defaults.CLAIM_AMOUNT(); + bytes32[] memory merkleProof = defaults.index1Proof(); + vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_InvalidProof.selector)); + merkleLockupLT.claim(invalidIndex, users.recipient1, amount, merkleProof); + } + + function test_RevertWhen_InvalidRecipient() + external + givenCampaignNotExpired + givenNotClaimed + givenNotIncludedInMerkleTree + { + uint256 index1 = defaults.INDEX1(); + address invalidRecipient = address(1337); + uint128 amount = defaults.CLAIM_AMOUNT(); + bytes32[] memory merkleProof = defaults.index1Proof(); + vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_InvalidProof.selector)); + merkleLockupLT.claim(index1, invalidRecipient, amount, merkleProof); + } + + function test_RevertWhen_InvalidAmount() + external + givenCampaignNotExpired + givenNotClaimed + givenNotIncludedInMerkleTree + { + uint256 index1 = defaults.INDEX1(); + uint128 invalidAmount = 1337; + bytes32[] memory merkleProof = defaults.index1Proof(); + vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_InvalidProof.selector)); + merkleLockupLT.claim(index1, users.recipient1, invalidAmount, merkleProof); + } + + function test_RevertWhen_InvalidMerkleProof() + external + givenCampaignNotExpired + givenNotClaimed + givenNotIncludedInMerkleTree + { + uint256 index1 = defaults.INDEX1(); + uint128 amount = defaults.CLAIM_AMOUNT(); + bytes32[] memory invalidMerkleProof = defaults.index2Proof(); + vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_InvalidProof.selector)); + merkleLockupLT.claim(index1, users.recipient1, amount, invalidMerkleProof); + } + + modifier givenIncludedInMerkleTree() { + _; + } + + // Needed this variable in storage due to how the imported libaries work. + uint256[] public leaves = new uint256[](4); // same number of recipients as in Defaults + + function test_Claim_TrancheAmountsSumNotEqualClaimAmount() + external + givenCampaignNotExpired + givenNotClaimed + givenIncludedInMerkleTree + { + // Declare an amount that will cause a rounding error. + uint128 claimAmount = defaults.CLAIM_AMOUNT() + 1; + uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT() + 1; + + // Compute the Merkle tree. + leaves = defaults.getLeaves(); + uint256 leaf = MerkleBuilder.computeLeaf(defaults.INDEX1(), users.recipient1, claimAmount); + leaves[0] = leaf; + MerkleBuilder.sortLeaves(leaves); + bytes32 merkleRoot = getRoot(leaves.toBytes32()); + + // Compute the Merkle proof. + uint256 pos = Arrays.findUpperBound(leaves, leaf); + bytes32[] memory proof = getProof(leaves.toBytes32(), pos); + + /// Declare the constructor params. + MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); + baseParams.merkleRoot = merkleRoot; + + // Deploy the new MerkleLockupLT contract. + ISablierV2MerkleLockupLT _merkleLockupLT = merkleLockupFactory.createMerkleLockupLT( + baseParams, lockupTranched, defaults.tranchesWithPercentages(), aggregateAmount, defaults.RECIPIENTS_COUNT() + ); + + // Fund the MerkleLockupLT contract. + deal({ token: address(dai), to: address(_merkleLockupLT), give: aggregateAmount }); + + uint256 expectedStreamId = lockupTranched.nextStreamId(); + + vm.expectEmit({ emitter: address(_merkleLockupLT) }); + emit Claim(defaults.INDEX1(), users.recipient1, claimAmount, expectedStreamId); + uint256 actualStreamId = _merkleLockupLT.claim(defaults.INDEX1(), users.recipient1, claimAmount, proof); + + LockupTranched.StreamLT memory actualStream = lockupTranched.getStream(actualStreamId); + LockupTranched.StreamLT memory expectedStream = LockupTranched.StreamLT({ + amounts: Lockup.Amounts({ deposited: claimAmount, refunded: 0, withdrawn: 0 }), + asset: dai, + endTime: uint40(block.timestamp) + defaults.TOTAL_DURATION(), + isCancelable: defaults.CANCELABLE(), + isDepleted: false, + isStream: true, + isTransferable: defaults.TRANSFERABLE(), + sender: users.admin, + startTime: uint40(block.timestamp), + tranches: defaults.tranches(claimAmount), + wasCanceled: false + }); + + assertTrue(_merkleLockupLT.hasClaimed(defaults.INDEX1()), "not claimed"); + assertEq(actualStreamId, expectedStreamId, "invalid stream id"); + assertEq(actualStream, expectedStream); + } + + function test_Claim() external givenCampaignNotExpired givenNotClaimed givenIncludedInMerkleTree { + uint256 expectedStreamId = lockupTranched.nextStreamId(); + + vm.expectEmit({ emitter: address(merkleLockupLT) }); + emit Claim(defaults.INDEX1(), users.recipient1, defaults.CLAIM_AMOUNT(), expectedStreamId); + uint256 actualStreamId = claimLT(); + + LockupTranched.Tranche[] memory tranches = defaults.tranches(); + + LockupTranched.StreamLT memory actualStream = lockupTranched.getStream(actualStreamId); + LockupTranched.StreamLT memory expectedStream = LockupTranched.StreamLT({ + amounts: Lockup.Amounts({ deposited: defaults.CLAIM_AMOUNT(), refunded: 0, withdrawn: 0 }), + asset: dai, + endTime: uint40(block.timestamp) + defaults.TOTAL_DURATION(), + isCancelable: defaults.CANCELABLE(), + isDepleted: false, + isStream: true, + isTransferable: defaults.TRANSFERABLE(), + sender: users.admin, + startTime: uint40(block.timestamp), + tranches: tranches, + wasCanceled: false + }); + + assertTrue(merkleLockupLT.hasClaimed(defaults.INDEX1()), "not claimed"); + assertEq(actualStreamId, expectedStreamId, "invalid stream id"); + assertEq(actualStream, expectedStream); + } +} diff --git a/test/integration/merkle-lockup/lt/claim/claim.tree b/test/integration/merkle-lockup/lt/claim/claim.tree new file mode 100644 index 00000000..ae9d8682 --- /dev/null +++ b/test/integration/merkle-lockup/lt/claim/claim.tree @@ -0,0 +1,26 @@ +claim.t.sol +├── given the campaign has expired +│ └── it should revert +└── given the campaign has not expired + ├── given the recipient has claimed + │ └── it should revert + └── given the recipient has not claimed + ├── given the claim is not included in the Merkle tree + │ ├── when the index is not valid + │ │ └── it should revert + │ ├── when the recipient address is not valid + │ │ └── it should revert + │ ├── when the amount is not valid + │ │ └── it should revert + │ └── when the Merkle proof is not valid + │ └── it should revert + └── given the claim is included in the Merkle tree + ├── when the sum of the tranches' amounts does not equal the claim amount + │ ├── it should adjust the last tranche amount + │ ├── it should mark the index as claimed + │ ├── it should create a LockupTranched stream + │ └── it should emit a {Claim} event + └── when the sum of the tranches' amounts equals the claim amount + ├── it should mark the index as claimed + ├── it should create a LockupTranched stream + └── it should emit a {Claim} event diff --git a/test/integration/merkle-lockup/lt/clawback/clawback.t.sol b/test/integration/merkle-lockup/lt/clawback/clawback.t.sol new file mode 100644 index 00000000..c8247109 --- /dev/null +++ b/test/integration/merkle-lockup/lt/clawback/clawback.t.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22 <0.9.0; + +import { Errors as V2CoreErrors } from "@sablier/v2-core/src/libraries/Errors.sol"; + +import { Errors } from "src/libraries/Errors.sol"; + +import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; + +contract Clawback_Integration_Test is MerkleLockup_Integration_Test { + function setUp() public virtual override { + MerkleLockup_Integration_Test.setUp(); + } + + function test_RevertWhen_CallerNotAdmin() external { + changePrank({ msgSender: users.eve }); + vm.expectRevert(abi.encodeWithSelector(V2CoreErrors.CallerNotAdmin.selector, users.admin, users.eve)); + merkleLockupLT.clawback({ to: users.eve, amount: 1 }); + } + + modifier whenCallerAdmin() { + changePrank({ msgSender: users.admin }); + _; + } + + function test_RevertGiven_CampaignNotExpired() external whenCallerAdmin { + vm.expectRevert( + abi.encodeWithSelector( + Errors.SablierV2MerkleLockup_CampaignNotExpired.selector, block.timestamp, defaults.EXPIRATION() + ) + ); + merkleLockupLT.clawback({ to: users.admin, amount: 1 }); + } + + modifier givenCampaignExpired() { + // Make a claim to have a different contract balance. + claimLT(); + vm.warp({ timestamp: defaults.EXPIRATION() + 1 seconds }); + _; + } + + function test_Clawback() external whenCallerAdmin givenCampaignExpired { + test_Clawback(users.admin); + } + + function testFuzz_Clawback(address to) external whenCallerAdmin givenCampaignExpired { + vm.assume(to != address(0)); + test_Clawback(to); + } + + function test_Clawback(address to) internal { + uint128 clawbackAmount = uint128(dai.balanceOf(address(merkleLockupLT))); + expectCallToTransfer({ to: to, amount: clawbackAmount }); + vm.expectEmit({ emitter: address(merkleLockupLT) }); + emit Clawback({ admin: users.admin, to: to, amount: clawbackAmount }); + merkleLockupLT.clawback({ to: to, amount: clawbackAmount }); + } +} diff --git a/test/integration/merkle-lockup/lt/clawback/clawback.tree b/test/integration/merkle-lockup/lt/clawback/clawback.tree new file mode 100644 index 00000000..feef74bc --- /dev/null +++ b/test/integration/merkle-lockup/lt/clawback/clawback.tree @@ -0,0 +1,9 @@ +clawback.t.sol +├── when the caller is not the admin +│ └── it should revert +└── when the caller is the admin + ├── given the campaign has not expired + │ └── it should revert + └── given the campaign has expired + ├── it should perform the ERC-20 transfer + └── it should emit a {Clawback} event diff --git a/test/integration/merkle-lockup/lt/constructor/constructor.t.sol b/test/integration/merkle-lockup/lt/constructor/constructor.t.sol new file mode 100644 index 00000000..7ab96e41 --- /dev/null +++ b/test/integration/merkle-lockup/lt/constructor/constructor.t.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22 <0.9.0; + +import { SablierV2MerkleLockupLT } from "src/SablierV2MerkleLockupLT.sol"; +import { MerkleLockupLT } from "src/types/DataTypes.sol"; + +import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; + +contract Constructor_MerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test { + /// @dev Needed to prevent "Stack too deep" error + struct Vars { + address actualAdmin; + uint256 actualAllowance; + address actualAsset; + string actualIpfsCID; + string actualName; + bool actualCancelable; + bool actualTransferable; + MerkleLockupLT.TrancheWithPercentage[] actualTranchesWithPercentages; + uint40 actualExpiration; + address actualLockupTranched; + bytes32 actualMerkleRoot; + address expectedAdmin; + uint256 expectedAllowance; + address expectedAsset; + string expectedIpfsCID; + bytes32 expectedName; + bool expectedCancelable; + bool expectedTransferable; + MerkleLockupLT.TrancheWithPercentage[] expectedTranchesWithPercentages; + uint40 expectedExpiration; + address expectedLockupTranched; + bytes32 expectedMerkleRoot; + } + + function test_Constructor() external { + SablierV2MerkleLockupLT constructedLockupLT = + new SablierV2MerkleLockupLT(defaults.baseParams(), lockupTranched, defaults.tranchesWithPercentages()); + + Vars memory vars; + + vars.actualAdmin = constructedLockupLT.admin(); + vars.expectedAdmin = users.admin; + assertEq(vars.actualAdmin, vars.expectedAdmin, "admin"); + + vars.actualAsset = address(constructedLockupLT.ASSET()); + vars.expectedAsset = address(dai); + assertEq(vars.actualAsset, vars.expectedAsset, "asset"); + + vars.actualName = constructedLockupLT.name(); + vars.expectedName = defaults.NAME_BYTES32(); + assertEq(bytes32(abi.encodePacked(vars.actualName)), vars.expectedName, "name"); + + vars.actualMerkleRoot = constructedLockupLT.MERKLE_ROOT(); + vars.expectedMerkleRoot = defaults.MERKLE_ROOT(); + assertEq(vars.actualMerkleRoot, vars.expectedMerkleRoot, "merkleRoot"); + + vars.actualCancelable = constructedLockupLT.CANCELABLE(); + vars.expectedCancelable = defaults.CANCELABLE(); + assertEq(vars.actualCancelable, vars.expectedCancelable, "cancelable"); + + vars.actualTransferable = constructedLockupLT.TRANSFERABLE(); + vars.expectedTransferable = defaults.TRANSFERABLE(); + assertEq(vars.actualTransferable, vars.expectedTransferable, "transferable"); + + vars.actualExpiration = constructedLockupLT.EXPIRATION(); + vars.expectedExpiration = defaults.EXPIRATION(); + assertEq(vars.actualExpiration, vars.expectedExpiration, "expiration"); + + vars.actualLockupTranched = address(constructedLockupLT.LOCKUP_TRANCHED()); + vars.expectedLockupTranched = address(lockupTranched); + assertEq(vars.actualLockupTranched, vars.expectedLockupTranched, "LockupTranched"); + + vars.actualTranchesWithPercentages = constructedLockupLT.getTranchesWithPercentages(); + vars.expectedTranchesWithPercentages = defaults.tranchesWithPercentages(); + assertEq(vars.actualTranchesWithPercentages, vars.expectedTranchesWithPercentages); + + vars.actualAllowance = dai.allowance(address(constructedLockupLT), address(lockupTranched)); + vars.expectedAllowance = MAX_UINT256; + assertEq(vars.actualAllowance, vars.expectedAllowance, "allowance"); + + vars.actualIpfsCID = constructedLockupLT.ipfsCID(); + vars.expectedIpfsCID = defaults.IPFS_CID(); + assertEq(vars.actualIpfsCID, vars.expectedIpfsCID, "ipfsCID"); + } +} diff --git a/test/integration/merkle-lockup/lt/has-claimed/hasClaimed.t.sol b/test/integration/merkle-lockup/lt/has-claimed/hasClaimed.t.sol new file mode 100644 index 00000000..a46bd722 --- /dev/null +++ b/test/integration/merkle-lockup/lt/has-claimed/hasClaimed.t.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22 <0.9.0; + +import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; + +contract HasClaimed_Integration_Test is MerkleLockup_Integration_Test { + function setUp() public virtual override { + MerkleLockup_Integration_Test.setUp(); + } + + function test_HasClaimed_IndexNotInTree() external { + uint256 indexNotInTree = 1337e18; + assertFalse(merkleLockupLT.hasClaimed(indexNotInTree), "claimed"); + } + + modifier whenIndexInTree() { + _; + } + + function test_HasClaimed_NotClaimed() external whenIndexInTree { + assertFalse(merkleLockupLT.hasClaimed(defaults.INDEX1()), "claimed"); + } + + modifier givenRecipientHasClaimed() { + claimLT(); + _; + } + + function test_HasClaimed() external whenIndexInTree givenRecipientHasClaimed { + assertTrue(merkleLockupLT.hasClaimed(defaults.INDEX1()), "not claimed"); + } +} diff --git a/test/integration/merkle-lockup/lt/has-claimed/hasClaimed.tree b/test/integration/merkle-lockup/lt/has-claimed/hasClaimed.tree new file mode 100644 index 00000000..dcbcafe7 --- /dev/null +++ b/test/integration/merkle-lockup/lt/has-claimed/hasClaimed.tree @@ -0,0 +1,8 @@ +hasClaimed.t.sol +├── when the index is not in the Merkle tree +│ └── it should return false +└── when the index is in the Merkle tree + ├── given the recipient has not claimed + │ └── it should return false + └── given the recipient has claimed + └── it should return true diff --git a/test/integration/merkle-lockup/lt/has-expired/hasExpired.t.sol b/test/integration/merkle-lockup/lt/has-expired/hasExpired.t.sol new file mode 100644 index 00000000..a1b4998c --- /dev/null +++ b/test/integration/merkle-lockup/lt/has-expired/hasExpired.t.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22 <0.9.0; + +import { ISablierV2MerkleLockupLT } from "src/interfaces/ISablierV2MerkleLockupLT.sol"; + +import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; + +contract HasExpired_Integration_Test is MerkleLockup_Integration_Test { + function setUp() public virtual override { + MerkleLockup_Integration_Test.setUp(); + } + + function test_HasExpired_ExpirationZero() external { + ISablierV2MerkleLockupLT testLockup = createMerkleLockupLT({ expiration: 0 }); + assertFalse(testLockup.hasExpired(), "campaign expired"); + } + + modifier whenExpirationNotZero() { + _; + } + + function test_HasExpired_ExpirationLessThanCurrentTime() external whenExpirationNotZero { + assertFalse(merkleLockupLT.hasExpired(), "campaign expired"); + } + + function test_HasExpired_ExpirationEqualToCurrentTime() external whenExpirationNotZero { + vm.warp({ timestamp: defaults.EXPIRATION() }); + assertTrue(merkleLockupLT.hasExpired(), "campaign not expired"); + } + + function test_HasExpired_ExpirationGreaterThanCurrentTime() external whenExpirationNotZero { + vm.warp({ timestamp: defaults.EXPIRATION() + 1 seconds }); + assertTrue(merkleLockupLT.hasExpired(), "campaign not expired"); + } +} diff --git a/test/integration/merkle-lockup/lt/has-expired/hasExpired.tree b/test/integration/merkle-lockup/lt/has-expired/hasExpired.tree new file mode 100644 index 00000000..18fd5766 --- /dev/null +++ b/test/integration/merkle-lockup/lt/has-expired/hasExpired.tree @@ -0,0 +1,10 @@ +hasExpired.t.sol +├── when the expiration is zero +│ └── it should return false +└── when the expiration is not zero + ├── when the expiration is less than current time + │ └── it should return false + ├── when the expiration is equal to the current time + │ └── it should return true + └── when the expiration is greater than current time + └── it should return true diff --git a/test/utils/Assertions.sol b/test/utils/Assertions.sol new file mode 100644 index 00000000..066650b2 --- /dev/null +++ b/test/utils/Assertions.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity >=0.8.22; + +import { PRBMathAssertions } from "@prb/math/test/utils/Assertions.sol"; +import { PRBTest } from "@prb/test/src/PRBTest.sol"; + +import { MerkleLockupLT } from "src/types/DataTypes.sol"; + +abstract contract Assertions is PRBTest, PRBMathAssertions { + event LogNamedArray(string key, MerkleLockupLT.TrancheWithPercentage[] tranchesWithPercentages); + + /// @dev Compares two {MerkleLockupLT.TrancheWithPercentage[]} arrays. + function assertEq( + MerkleLockupLT.TrancheWithPercentage[] memory a, + MerkleLockupLT.TrancheWithPercentage[] memory b + ) + internal + { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit Log("Error: a == b not satisfied [MerkleLockupLT.TrancheWithPercentage[]]"); + emit LogNamedArray(" Left", a); + emit LogNamedArray(" Right", b); + fail(); + } + } +} diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index 24e2559e..7e7aa8ee 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -4,10 +4,10 @@ pragma solidity >=0.8.22 <0.9.0; import { Arrays } from "@openzeppelin/contracts/utils/Arrays.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ud2x18 } from "@prb/math/src/UD2x18.sol"; -import { UD60x18 } from "@prb/math/src/UD60x18.sol"; -import { Broker, LockupDynamic, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; +import { UD60x18, ud } from "@prb/math/src/UD60x18.sol"; +import { Broker, LockupDynamic, LockupLinear, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { Batch, MerkleLockup } from "src/types/DataTypes.sol"; +import { Batch, MerkleLockup, MerkleLockupLT } from "src/types/DataTypes.sol"; import { ArrayBuilder } from "./ArrayBuilder.sol"; import { BatchBuilder } from "./BatchBuilder.sol"; @@ -88,6 +88,10 @@ contract Defaults is Merkle { MERKLE_ROOT = getRoot(LEAVES.toBytes32()); } + function getLeaves() public view returns (uint256[] memory) { + return LEAVES; + } + /*////////////////////////////////////////////////////////////////////////// MERKLE-LOCKUP //////////////////////////////////////////////////////////////////////////*/ @@ -142,6 +146,18 @@ contract Defaults is Merkle { }); } + function tranchesWithPercentages() + public + pure + returns (MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages_) + { + tranchesWithPercentages_ = new MerkleLockupLT.TrancheWithPercentage[](2); + tranchesWithPercentages_[0] = + MerkleLockupLT.TrancheWithPercentage({ unlockPercentage: ud2x18(0.25e18), duration: 2500 seconds }); + tranchesWithPercentages_[1] = + MerkleLockupLT.TrancheWithPercentage({ unlockPercentage: ud2x18(0.75e18), duration: 7500 seconds }); + } + /*////////////////////////////////////////////////////////////////////////// SABLIER-V2-LOCKUP //////////////////////////////////////////////////////////////////////////*/ @@ -284,6 +300,33 @@ contract Defaults is Merkle { return LockupLinear.Range({ start: START_TIME, cliff: CLIFF_TIME, end: END_TIME }); } + /*////////////////////////////////////////////////////////////////////////// + SABLIER-V2-LOCKUP-TRANCHED + //////////////////////////////////////////////////////////////////////////*/ + + function tranches() public view returns (LockupTranched.Tranche[] memory tranches_) { + tranches_ = new LockupTranched.Tranche[](2); + tranches_[0] = LockupTranched.Tranche({ amount: 2500e18, timestamp: uint40(block.timestamp) + CLIFF_DURATION }); + tranches_[1] = LockupTranched.Tranche({ amount: 7500e18, timestamp: uint40(block.timestamp) + TOTAL_DURATION }); + } + + /// @dev Mirros the logic from {SablierV2MerkleLockupLT._calculateTranches}. + function tranches(uint128 totalAmount) public view returns (LockupTranched.Tranche[] memory tranches_) { + tranches_ = tranches(); + + uint128 amount0 = ud(totalAmount).mul(tranchesWithPercentages()[0].unlockPercentage.intoUD60x18()).intoUint128(); + uint128 amount1 = ud(totalAmount).mul(tranchesWithPercentages()[1].unlockPercentage.intoUD60x18()).intoUint128(); + + tranches_[0].amount = amount0; + tranches_[1].amount = amount1; + + uint128 amountsSum = amount0 + amount1; + + if (amountsSum != totalAmount) { + tranches_[1].amount += totalAmount - amountsSum; + } + } + /*////////////////////////////////////////////////////////////////////////// BATCH //////////////////////////////////////////////////////////////////////////*/ diff --git a/test/utils/Events.sol b/test/utils/Events.sol index 3ca28a3d..1b5d13c7 100644 --- a/test/utils/Events.sol +++ b/test/utils/Events.sol @@ -3,9 +3,11 @@ pragma solidity >=0.8.22; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; +import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; import { ISablierV2MerkleLockupLL } from "src/interfaces/ISablierV2MerkleLockupLL.sol"; -import { MerkleLockup } from "src/types/DataTypes.sol"; +import { ISablierV2MerkleLockupLT } from "src/interfaces/ISablierV2MerkleLockupLT.sol"; +import { MerkleLockup, MerkleLockupLT } from "src/types/DataTypes.sol"; /// @notice Abstract contract containing all the events emitted by the protocol. abstract contract Events { @@ -19,4 +21,12 @@ abstract contract Events { uint256 aggregateAmount, uint256 recipientsCount ); + event CreateMerkleLockupLT( + ISablierV2MerkleLockupLT indexed merkleLockupLT, + MerkleLockup.ConstructorParams indexed baseParams, + ISablierV2LockupTranched lockupTranched, + MerkleLockupLT.TrancheWithPercentage[] tranchesWithPercentages, + uint256 aggregateAmount, + uint256 recipientsCount + ); } diff --git a/test/utils/Precompiles.sol b/test/utils/Precompiles.sol index 1d8cbe5a..bb712314 100644 --- a/test/utils/Precompiles.sol +++ b/test/utils/Precompiles.sol @@ -11,9 +11,9 @@ contract Precompiles { //////////////////////////////////////////////////////////////////////////*/ bytes public constant BYTECODE_BATCH = - hex"6080806040523461001657611a49908161001c8239f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c9081632b754bb014610c7d575080639b38b39a146108645780639b675ad6146104ac5763e8d349611461004b57600080fd5b346104345760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610434576100826111c0565b61008a6110c6565b9060443567ffffffffffffffff808211610434573660238301121561043457816004013511610434573660246101208360040135028301011161043457806004013515610482576000805b8260040135821061044d5761010291508473ffffffffffffffffffffffffffffffffffffffff851661158a565b61010f8160040135611339565b9160005b82600401358110610130576040518061012c8682611184565b0390f35b8060e061014582866004013560248801611579565b01610163606061015d84886004013560248a01611579565b01611388565b9061017683876004013560248901611579565b91610194602061018e868a6004013560248c01611579565b01611395565b6101ae6101a9868a6004013560248c01611579565b611395565b916fffffffffffffffffffffffffffffffff6101dd60406101d78960048e013560248f01611579565b01611252565b73ffffffffffffffffffffffffffffffffffffffff61020c8c61015d60809c8260248f94600401359101611579565b94816040519761021b8961126f565b16875216602086015216604084015273ffffffffffffffffffffffffffffffffffffffff8b166060840152151586830152151560a082015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff608436030112610434576103c4926102c060e093604051610295816112a8565b6102a160a08501611450565b81526102b060c0809501611450565b602082015283850152369061140a565b83830152604051957fab167ccc00000000000000000000000000000000000000000000000000000000875273ffffffffffffffffffffffffffffffffffffffff835116600488015273ffffffffffffffffffffffffffffffffffffffff60208401511660248801526fffffffffffffffffffffffffffffffff604084015116604488015273ffffffffffffffffffffffffffffffffffffffff60608401511660648801528201511515608487015260a0820151151560a4870152810151602064ffffffffff918281511660c489015201511660e486015201516101048401906020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b60208261014481600073ffffffffffffffffffffffffffffffffffffffff88165af1801561044157600090610409575b600192506104028287611514565b5201610113565b506020823d602011610439575b81610423602093836112e0565b8101031261043457600191516103f4565b600080fd5b3d9150610416565b6040513d6000823e3d90fd5b6001906fffffffffffffffffffffffffffffffff61047860406101d786886004013560248a01611579565b16019101906100d5565b60046040517f763e559d000000000000000000000000000000000000000000000000000000008152fd5b34610434576104ba366110e9565b909281156104825760009060005b838110610836575073ffffffffffffffffffffffffffffffffffffffff6104f2911691848361158a565b6104fb82611339565b9260005b838110610514576040518061012c8782611184565b61051f818588611539565b60c001908685610530838284611539565b60600161053c90611388565b9381610549858286611539565b60200161055590611395565b85610561818488611539565b60a0810161056e916113b6565b9561057a929197611539565b61058390611395565b968c87610591818684611539565b60400161059d90611252565b946105a792611539565b6080016105b390611388565b90604051986105c18a61126f565b73ffffffffffffffffffffffffffffffffffffffff168952151560208901521515604088015273ffffffffffffffffffffffffffffffffffffffff1660608701526fffffffffffffffffffffffffffffffff16608086015273ffffffffffffffffffffffffffffffffffffffff861660a08601523661063f9161140a565b60c0850152369061064f92611462565b60e083015260405180927f168444560000000000000000000000000000000000000000000000000000000082526004820160209052610144820190805173ffffffffffffffffffffffffffffffffffffffff166024840152602081015115156044840152604081015115156064840152606081015173ffffffffffffffffffffffffffffffffffffffff16608484015260808101516fffffffffffffffffffffffffffffffff1660a484015260a081015173ffffffffffffffffffffffffffffffffffffffff1660c484015260c081015160e4840161074d916020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b60e0015190610124830161012090528151809152610164830191602001906000905b8082106107db57505050908060209203816000885af18015610441576000906107a8575b600192506107a18288611514565b52016104ff565b506020823d6020116107d3575b816107c2602093836112e0565b810103126104345760019151610793565b3d91506107b5565b919350916020606082610828600194885164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b01940192018593929161076f565b916001906fffffffffffffffffffffffffffffffff61085b60406101d787898c611539565b160192016104c8565b346104345760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104345761089b6111c0565b6108a36110c6565b6044359167ffffffffffffffff8084116104345736602385011215610434578360040135116104345760248301903660246101408660040135028601011161043457836004013515610482576000805b85600401358210610c4b5761092091508473ffffffffffffffffffffffffffffffffffffffff841661158a565b61092d8460040135611339565b9260005b8560040135811061094a576040518061012c8782611184565b808661010061095f8794836004013586611528565b0183610975606061015d86866004013585611528565b610998602061018e8761098d81896004013588611528565b976004013586611528565b906fffffffffffffffffffffffffffffffff8c73ffffffffffffffffffffffffffffffffffffffff6109fe6109ea60406101d78c6109de6101a98260048a01358e611528565b9a876004013590611528565b9261015d8b60809d8e936004013590611528565b948160405197610a0d8961126f565b16875216602086015216604084015273ffffffffffffffffffffffffffffffffffffffff88166060840152151586830152151560a082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60843603011261043457610bd392610ac260e093604051610a878161128c565b610a9360a08501611450565b8152610ab28660c095610aa7878201611450565b602085015201611450565b604082015283850152369061140a565b83830152604051957f96ce143100000000000000000000000000000000000000000000000000000000875273ffffffffffffffffffffffffffffffffffffffff835116600488015273ffffffffffffffffffffffffffffffffffffffff60208401511660248801526fffffffffffffffffffffffffffffffff604084015116604488015273ffffffffffffffffffffffffffffffffffffffff60608401511660648801528201511515608487015260a0820151151560a4870152810151604064ffffffffff918281511660c48901528260208201511660e489015201511661010486015201516101248401906020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b60208261016481600073ffffffffffffffffffffffffffffffffffffffff89165af1801561044157600090610c18575b60019250610c118288611514565b5201610931565b506020823d602011610c43575b81610c32602093836112e0565b810103126104345760019151610c03565b3d9150610c25565b6001906fffffffffffffffffffffffffffffffff610c7360406101d7868b600401358a611528565b16019101906108f3565b3461043457610c8b366110e9565b929093831561109e57506000805b84821061107057610cc291508373ffffffffffffffffffffffffffffffffffffffff841661158a565b610ccb83611339565b9360005b848110610ce4576040518061012c8882611184565b60e0610cf18287856111e3565b0190610d03608061015d8389876111e3565b91610d14602061018e848a886111e3565b92610d2d610d23848a886111e3565b60c08101906113b6565b9091610d3d6101a9868c8a6111e3565b936060610d4b878d8b6111e3565b01359464ffffffffff861686036104345788610d7e60a061015d8f80610d7860406101d78f80958a6111e3565b956111e3565b96604051998a61012081011067ffffffffffffffff6101208d0111176110415773ffffffffffffffffffffffffffffffffffffffff908b99610e3999989764ffffffffff6fffffffffffffffffffffffffffffffff96956101009f86610e2d9b9a61012083016040521690521660208d0152151560408c0152151560608b01521660808901521660a087015273ffffffffffffffffffffffffffffffffffffffff8b1660c0870152369061140a565b60e08501523691611462565b838201526040519283917fc33cd35e0000000000000000000000000000000000000000000000000000000083526020600484015273ffffffffffffffffffffffffffffffffffffffff815116602484015264ffffffffff602082015116604484015260408101511515606484015260608101511515608484015273ffffffffffffffffffffffffffffffffffffffff60808201511660a48401526fffffffffffffffffffffffffffffffff60a08201511660c484015273ffffffffffffffffffffffffffffffffffffffff60c08201511660e4840152610f4260e08201516101048501906020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b0151610140610144830152805180610164840152602061018484019201906000905b808210610fe65750505090806020920381600073ffffffffffffffffffffffffffffffffffffffff89165af1801561044157600090610fb3575b60019250610fac8289611514565b5201610ccf565b506020823d602011610fde575b81610fcd602093836112e0565b810103126104345760019151610f9e565b3d9150610fc0565b919350916020606082611033600194885164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b019401920185939291610f64565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6001906fffffffffffffffffffffffffffffffff61109460406101d7868a8c6111e3565b1601910190610c99565b807f763e559d0000000000000000000000000000000000000000000000000000000060049252fd5b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361043457565b9060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126104345773ffffffffffffffffffffffffffffffffffffffff91600435838116810361043457926024359081168103610434579160443567ffffffffffffffff9283821161043457806023830112156104345781600401359384116104345760248460051b83010111610434576024019190565b602090602060408183019282815285518094520193019160005b8281106111ac575050505090565b83518552938101939281019260010161119e565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361043457565b91908110156112235760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee181360301821215610434570190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b356fffffffffffffffffffffffffffffffff811681036104345790565b610100810190811067ffffffffffffffff82111761104157604052565b6060810190811067ffffffffffffffff82111761104157604052565b6040810190811067ffffffffffffffff82111761104157604052565b6080810190811067ffffffffffffffff82111761104157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761104157604052565b67ffffffffffffffff81116110415760051b60200190565b9061134382611321565b61135060405191826112e0565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061137e8294611321565b0190602036910137565b3580151581036104345790565b3573ffffffffffffffffffffffffffffffffffffffff811681036104345790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610434570180359067ffffffffffffffff82116104345760200191606082023603831361043457565b919082604091031261043457604051611422816112a8565b8092803573ffffffffffffffffffffffffffffffffffffffff81168103610434578252602090810135910152565b359064ffffffffff8216820361043457565b92919261146e82611321565b60409461147e60405192836112e0565b8195848352602080930191606080960285019481861161043457925b8584106114aa5750505050505050565b8684830312610434578251906114bf8261128c565b84356fffffffffffffffffffffffffffffffff81168103610434578252858501359067ffffffffffffffff8216820361043457828792838b950152611505868801611450565b8682015281520193019261149a565b80518210156112235760209160051b010190565b919081101561122357610140020190565b91908110156112235760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0181360301821215610434570190565b919081101561122357610120020190565b90604080516020907f23b872dd000000000000000000000000000000000000000000000000000000008282015233602482015260449030828201528660648201526064815260a081019080821067ffffffffffffffff831117611041576115f39185528561179c565b73ffffffffffffffffffffffffffffffffffffffff94858516958451917fdd62ed3e0000000000000000000000000000000000000000000000000000000083523060048401521690816024820152838184818a5afa90811561177957908891600091611748575b501061166a575b50505050505050565b8351956000808589017f095ea7b3000000000000000000000000000000000000000000000000000000009a8b82528560248c0152868b0152858a526116ae8a6112c4565b89519082855af1906116be6118bc565b82611715575b508161170a575b50611661576116fe966116f9945193840152602483015260008183015281526116f3816112c4565b8261179c565b61179c565b38808080808080611661565b90503b1515386116cb565b809192505190858215928315611730575b50505090386116c4565b6117409350820181019101611784565b388581611726565b809250858092503d8311611772575b61176181836112e0565b81010312610434578790513861165a565b503d611757565b85513d6000823e3d90fd5b90816020910312610434575180151581036104345790565b6040516118079173ffffffffffffffffffffffffffffffffffffffff166117c2826112a8565b6000806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af16118016118bc565b9161191a565b8051908282159283156118a4575b505050156118205750565b608490604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b6118b49350820181019101611784565b388281611815565b3d15611915573d9067ffffffffffffffff8211611041576040519161190960207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846112e0565b82523d6000602084013e565b606090565b91929015611995575081511561192e575090565b3b156119375790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b8251909150156119a85750805190602001fd5b604051907f08c379a000000000000000000000000000000000000000000000000000000000825281602080600483015282519283602484015260005b848110611a25575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f836000604480968601015201168101030190fd5b8181018301518682016044015285935082016119e456fea164736f6c6343000817000a"; + hex"6080806040523461001657611936908161001c8239f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c90816349a32c4014610c8657508063606ef875146108d2578063a514f83e146104a85763f7ca34eb1461004b57600080fd5b346103ab576100593661113e565b919092821561047e576000805b8482106104505761008f91508373ffffffffffffffffffffffffffffffffffffffff8416611580565b61009883611300565b9260005b8181106100b557604051806100b18782611102565b0390f35b6100c86100c3828489611540565b61134f565b906100df60206100d983868b611540565b0161134f565b876100f660406100f0858885611540565b01611219565b9361010d6060610107868986611540565b01611370565b9460a061012b8689610125608061010784848b611540565b96611540565b013564ffffffffff811681036103ab578b91889160e06101658961015d61015382888a611540565b60c0810190611429565b969097611540565b0195604051998a61012081011067ffffffffffffffff6101208d0111176104215773ffffffffffffffffffffffffffffffffffffffff8e8c60406fffffffffffffffffffffffffffffffff64ffffffffff98856102029f816101f69f61012088018752168652166020850152169101521660608c0152151560808b0152151560a08a01521660c0880152369161147d565b60e0850152369061138f565b610100830152604051917f31df3d480000000000000000000000000000000000000000000000000000000083526020600484015273ffffffffffffffffffffffffffffffffffffffff815116602484015273ffffffffffffffffffffffffffffffffffffffff60208201511660448401526fffffffffffffffffffffffffffffffff604082015116606484015273ffffffffffffffffffffffffffffffffffffffff60608201511660848401526080810151151560a484015260a0810151151560c484015264ffffffffff60c08201511660e48401528260e0820151610140610104830152805180610164840152602061018484019201906000905b8082106103c457505050819061034061010060209501516101248401906020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b0381600073ffffffffffffffffffffffffffffffffffffffff89165af180156103b857600090610380575b6001925061037982886113d5565b520161009c565b506020823d6020116103b0575b8161039a602093836112a7565b810103126103ab576001915161036b565b600080fd5b3d915061038d565b6040513d6000823e3d90fd5b91949350916020606082610412600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b019501920186939492916102fe565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6001906fffffffffffffffffffffffffffffffff61047460406100f0868a8c611540565b1601910190610066565b60046040517f763e559d000000000000000000000000000000000000000000000000000000008152fd5b346103ab5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103ab576104df6110bc565b6104e76110df565b9060443567ffffffffffffffff8082116103ab57366023830112156103ab578160040135116103ab57366024610140836004013502830101116103ab5780600401351561047e576000805b8260040135821061089d5761055f91508473ffffffffffffffffffffffffffffffffffffffff8516611580565b61056c8160040135611300565b9160005b8260040135811061058957604051806100b18682611102565b61059e6100c38285600401356024870161152f565b90836105b760206100d98484600401356024860161152f565b6105ce60406100f08585600401356024870161152f565b936105e660606101078686600401356024880161152f565b946080956fffffffffffffffffffffffffffffffff6106118861010789896004013560248b0161152f565b9273ffffffffffffffffffffffffffffffffffffffff61010061064f8a6106408160048d013560248e0161152f565b9a60248160040135910161152f565b0196816040519761065f89611236565b16875216602086015216604084015273ffffffffffffffffffffffffffffffffffffffff8b166060840152151586830152151560a082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6084360301126103ab576108259261071460e0936040516106d98161126f565b6106e560a0850161137d565b81526107048660c0956106f987820161137d565b60208501520161137d565b604082015283850152369061138f565b83830152604051957f53b1572700000000000000000000000000000000000000000000000000000000875273ffffffffffffffffffffffffffffffffffffffff835116600488015273ffffffffffffffffffffffffffffffffffffffff60208401511660248801526fffffffffffffffffffffffffffffffff604084015116604488015273ffffffffffffffffffffffffffffffffffffffff60608401511660648801528201511515608487015260a0820151151560a4870152810151604064ffffffffff918281511660c48901528260208201511660e489015201511661010486015201516101248401906020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b60208261016481600073ffffffffffffffffffffffffffffffffffffffff88165af180156103b85760009061086a575b6001925061086382876113d5565b5201610570565b506020823d602011610895575b81610884602093836112a7565b810103126103ab5760019151610855565b3d9150610877565b6001906fffffffffffffffffffffffffffffffff6108c860406100f086886004013560248a0161152f565b1601910190610532565b346103ab576108e03661113e565b92831561047e5760009060005b858110610c58575073ffffffffffffffffffffffffffffffffffffffff6109179116918483611580565b61092084611300565b9260005b85811061093957604051806100b18782611102565b6109448187866113e9565b61094d9061134f565b906109598188876113e9565b6020016109659061134f565b878261097281838a6113e9565b60400161097e90611219565b61098982848b6113e9565b60600161099590611370565b6109a083858c6113e9565b6080016109ac90611370565b916109b884868d6113e9565b60a081016109c591611429565b946109d191968d6113e9565b60c00195604051986109e28a611236565b73ffffffffffffffffffffffffffffffffffffffff16895273ffffffffffffffffffffffffffffffffffffffff1660208901526fffffffffffffffffffffffffffffffff16604088015273ffffffffffffffffffffffffffffffffffffffff8816606088015215156080870152151560a08601523690610a619261147d565b60c084015236610a709161138f565b60e0830152604051917f54c022920000000000000000000000000000000000000000000000000000000083526004830160209052826101448101825173ffffffffffffffffffffffffffffffffffffffff166024830152602083015173ffffffffffffffffffffffffffffffffffffffff16604483015260408301516fffffffffffffffffffffffffffffffff166064830152606083015173ffffffffffffffffffffffffffffffffffffffff1660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001906000905b808210610bfb575050508190610b9e60e060209501516101048401906020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b03816000885af180156103b857600090610bc8575b60019250610bc182886113d5565b5201610924565b506020823d602011610bf3575b81610be2602093836112a7565b810103126103ab5760019151610bb3565b3d9150610bd5565b91949350916020606082610c49600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b01950192018693949291610b5d565b916001906fffffffffffffffffffffffffffffffff610c7d60406100f0878b8a6113e9565b160192016108ed565b346103ab5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103ab57610cbd6110bc565b610cc56110df565b916044359067ffffffffffffffff8083116103ab57366023840112156103ab578260040135116103ab57366024610120846004013502840101116103ab5781600401351561109457506000805b8260040135821061105f57610d3f91508473ffffffffffffffffffffffffffffffffffffffff8516611580565b610d4c8160040135611300565b9160005b82600401358110610d6957604051806100b18682611102565b610d7e6100c3828560040135602487016111d9565b9083610d9760206100d9848460040135602486016111d9565b610dae60406100f0858560040135602487016111d9565b93610dc66060610107868660040135602488016111d9565b946080956fffffffffffffffffffffffffffffffff610df18861010789896004013560248b016111d9565b9273ffffffffffffffffffffffffffffffffffffffff60e0610e2e8a610e1f8160048d013560248e016111d9565b9a6024816004013591016111d9565b01968160405197610e3e89611236565b16875216602086015216604084015273ffffffffffffffffffffffffffffffffffffffff8b166060840152151586830152151560a082015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6084360301126103ab57610fe792610ee360e093604051610eb881611253565b610ec460a0850161137d565b8152610ed360c080950161137d565b602082015283850152369061138f565b83830152604051957fab167ccc00000000000000000000000000000000000000000000000000000000875273ffffffffffffffffffffffffffffffffffffffff835116600488015273ffffffffffffffffffffffffffffffffffffffff60208401511660248801526fffffffffffffffffffffffffffffffff604084015116604488015273ffffffffffffffffffffffffffffffffffffffff60608401511660648801528201511515608487015260a0820151151560a4870152810151602064ffffffffff918281511660c489015201511660e486015201516101048401906020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b60208261014481600073ffffffffffffffffffffffffffffffffffffffff88165af180156103b85760009061102c575b6001925061102582876113d5565b5201610d50565b506020823d602011611057575b81611046602093836112a7565b810103126103ab5760019151611017565b3d9150611039565b6001906fffffffffffffffffffffffffffffffff61108a60406100f086886004013560248a016111d9565b1601910190610d12565b807f763e559d0000000000000000000000000000000000000000000000000000000060049252fd5b6004359073ffffffffffffffffffffffffffffffffffffffff821682036103ab57565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036103ab57565b602090602060408183019282815285518094520193019160005b82811061112a575050505090565b83518552938101939281019260010161111c565b9060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126103ab5773ffffffffffffffffffffffffffffffffffffffff9160043583811681036103ab579260243590811681036103ab579160443567ffffffffffffffff928382116103ab57806023830112156103ab5781600401359384116103ab5760248460051b830101116103ab576024019190565b91908110156111ea57610120020190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b356fffffffffffffffffffffffffffffffff811681036103ab5790565b610100810190811067ffffffffffffffff82111761042157604052565b6040810190811067ffffffffffffffff82111761042157604052565b6060810190811067ffffffffffffffff82111761042157604052565b6080810190811067ffffffffffffffff82111761042157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761042157604052565b67ffffffffffffffff81116104215760051b60200190565b9061130a826112e8565b61131760405191826112a7565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061134582946112e8565b0190602036910137565b3573ffffffffffffffffffffffffffffffffffffffff811681036103ab5790565b3580151581036103ab5790565b359064ffffffffff821682036103ab57565b91908260409103126103ab576040516113a781611253565b8092803573ffffffffffffffffffffffffffffffffffffffff811681036103ab578252602090810135910152565b80518210156111ea5760209160051b010190565b91908110156111ea5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01813603018212156103ab570190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156103ab570180359067ffffffffffffffff82116103ab576020019160608202360383136103ab57565b929192611489826112e8565b60409461149960405192836112a7565b819584835260208093019160608096028501948186116103ab57925b8584106114c55750505050505050565b86848303126103ab578251906114da8261126f565b84356fffffffffffffffffffffffffffffffff811681036103ab578252858501359067ffffffffffffffff821682036103ab57828792838b95015261152086880161137d565b868201528152019301926114b5565b91908110156111ea57610140020190565b91908110156111ea5760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee1813603018212156103ab570190565b90604080516020907f23b872dd000000000000000000000000000000000000000000000000000000008282015233602482015260449030828201528660648201526064815260a081019080821067ffffffffffffffff831117610421576115e991855285611792565b73ffffffffffffffffffffffffffffffffffffffff94858516958451917fdd62ed3e0000000000000000000000000000000000000000000000000000000083523060048401521690816024820152838184818a5afa90811561176f5790889160009161173e575b5010611660575b50505050505050565b8351956000808589017f095ea7b3000000000000000000000000000000000000000000000000000000009a8b82528560248c0152868b0152858a526116a48a61128b565b89519082855af1906116b461182b565b8261170b575b5081611700575b50611657576116f4966116ef945193840152602483015260008183015281526116e98161128b565b82611792565b611792565b38808080808080611657565b90503b1515386116c1565b809192505190858215928315611726575b50505090386116ba565b611736935082018101910161177a565b38858161171c565b809250858092503d8311611768575b61175781836112a7565b810103126103ab5787905138611650565b503d61174d565b85513d6000823e3d90fd5b908160209103126103ab575180151581036103ab5790565b60008073ffffffffffffffffffffffffffffffffffffffff6117c993169360208151910182865af16117c261182b565b9083611889565b8051908115159182611810575b50506117df5750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b611823925060208091830101910161177a565b1538806117d6565b3d15611884573d9067ffffffffffffffff8211610421576040519161187860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846112a7565b82523d6000602084013e565b606090565b906118c8575080511561189e57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580611920575b6118d9575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b156118d156fea164736f6c6343000817000a"; bytes public constant BYTECODE_MERKLE_LOCKUP_FACTORY = - hex"6080806040523461001657611d39908161001c8239f35b600080fdfe600436101561000d57600080fd5b60003560e01c6373b01dbb1461002257600080fd5b3461053b576101807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261053b5773ffffffffffffffffffffffffffffffffffffffff600435166004350361053b5773ffffffffffffffffffffffffffffffffffffffff602435166024350361053b5773ffffffffffffffffffffffffffffffffffffffff604435166044350361053b5764ffffffffff608435166084350361053b5760407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5c36011261053b5760c060405260a43564ffffffffff8116810361053b5760805264ffffffffff60c4351660c4350361053b5760c43560a05260e435801515810361053b5761010435801515810361053b5767ffffffffffffffff610124351161053b573660236101243501121561053b5767ffffffffffffffff61012435600401351161050c57604051906101ae60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f610124356004013501160183610540565b600461012435908101358084523691016024011161053b5761012435600401356024610124350160208401376000602061012435600401358401015260405161020f6020820160806020908164ffffffffff91828151168552015116910152565b6040815280606081011067ffffffffffffffff60608301111761050c5760608101604052606060808201917fffffffffffffffffffffffffffffffffffffffff00000000000000000000000080600435841b16845280602435841b166094830152604435831b1660a882015260643560bc8201527fffffffffff00000000000000000000000000000000000000000000000000000060843560d81b1660dc8201526102f56023838380516102ca8160e1840160208501610581565b81018a151560f81b60e182015288151560f81b60e28201520301600381018585015201838301610540565b015190206040518061178881011067ffffffffffffffff6117888301111761050c576117886105a5823973ffffffffffffffffffffffffffffffffffffffff60043581166117888301908152602435821660208201526044359091166040820152606435606082015264ffffffffff608435811660808084019190915251811660a080840191909152511660c082015285151560e08201528315156101008201526101209082900301906000f591821561050057602093604051926101409173ffffffffffffffffffffffffffffffffffffffff861685526064358786015264ffffffffff6084351660408601526104056060860160806020908164ffffffffff91828151168552015116910152565b151560a0850152151560c08401528060e0840152815180918401526104338161016093878587019101610581565b61014435610100840152610164356101208401527fb2f6bc588a802f2ce7f8dec57a5096c107d2fc000d5b4cc65745a2c0e232349c73ffffffffffffffffffffffffffffffffffffffff604435169373ffffffffffffffffffffffffffffffffffffffff6024351693817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f73ffffffffffffffffffffffffffffffffffffffff600435169601168101030190a473ffffffffffffffffffffffffffffffffffffffff60405191168152f35b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761050c57604052565b60005b8381106105945750506000910152565b818101518382015260200161058456fe610160604081815234620002e0578162001788803803809162000023828562000301565b833981010361012091828212620002e05783516001600160a01b038082169391849003620002e057602090818701519481861693848703620002e0578589015192831692838103620002e05760608a015190876200008460808d0162000325565b93609f190112620002e0578751918289016001600160401b03811184821017620002ca578952620000b860a08d0162000325565b8352620000c860c08d0162000325565b83880190815293620000dd60e08e0162000338565b91610100809e01620000ef9062000338565b93600097600160a01b60019003198954161788556080528960e0528d5260c05260a05289526101409788525164ffffffffff16600254915160281b69ffffffffff00000000001691600160501b600190031916171760025584519280840191808063095ea7b360e01b948581528860248901526000196044890152604488526200017988620002e5565b87519082885af16200018a62000346565b8162000288575b50806200027d575b156200023b575b505050505050519161121b93846200056d8539608051848181610377015281816106460152610cc4015260a0518481816107320152610bb0015260c05184818161015101528181610afc01528181610f1b01526110b7015260e05184818161020e015281816105d00152610c63015251838181610327015261056801525182818161075a0152610b730152518181816101a201526108cd0152f35b62000271956200026b9388519384015260248301526044820152604481526200026481620002e5565b8262000389565b62000389565b388080808080620001a0565b50833b151562000199565b80915051838115918215620002a3575b505090503862000191565b8380929350010312620002c65782620002bd910162000338565b80833862000298565b5080fd5b634e487b7160e01b600052604160045260246000fd5b600080fd5b608081019081106001600160401b03821117620002ca57604052565b601f909101601f19168101906001600160401b03821190821017620002ca57604052565b519064ffffffffff82168203620002e057565b51908115158203620002e057565b3d1562000384573d906001600160401b038211620002ca576040519162000378601f8201601f19166020018462000301565b82523d6000602084013e565b606090565b60408051908101916001600160a01b03166001600160401b03831182841017620002ca57620003fd926040526000806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af1620003f662000346565b9162000492565b80518281159182156200046f575b5050905015620004185750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b8380929350010312620002e0578162000489910162000338565b8082386200040b565b91929015620004f75750815115620004a8575090565b3b15620004b25790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b8251909150156200050b5750805190602001fd5b6040519062461bcd60e51b82528160208060048301528251908160248401526000935b82851062000552575050604492506000838284010152601f80199101168101030190fd5b84810182015186860160440152938101938593506200052e56fe608080604052600436101561001357600080fd5b60003560e01c9081631686c90914610bd55750806316c3549d14610b985780631bfd681414610b5b5780633bfe03a814610b2c5780633f31ae3f1461039b5780634800d97f1461034a57806351e75e8b1461030f57806375829def14610232578063845aef4b146101e157806390e64d13146101c65780639e93e57714610175578063bb4b573414610133578063ce516507146100f25763f851a440146100b957600080fd5b346100ed5760006003193601126100ed57602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100ed5760206003193601126100ed57602061012960043560ff6001918060081c60005282602052161b60406000205416151590565b6040519015158152f35b346100ed5760006003193601126100ed57602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100ed5760006003193601126100ed57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100ed5760006003193601126100ed5760206101296110af565b346100ed5760006003193601126100ed57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100ed5760206003193601126100ed5761024b610fe7565b60005473ffffffffffffffffffffffffffffffffffffffff808216923384036102c2577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100ed5760006003193601126100ed5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100ed5760006003193601126100ed57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100ed5760806003193601126100ed5760243573ffffffffffffffffffffffffffffffffffffffff811681036100ed57604435906fffffffffffffffffffffffffffffffff821682036100ed5767ffffffffffffffff606435116100ed573660236064350112156100ed5767ffffffffffffffff60643560040135116100ed573660246064356004013560051b6064350101116100ed576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff831660408201526fffffffffffffffffffffffffffffffff841660608201526060815261048181611026565b519020604051602081019182526020815261049b8161100a565b519020916104a76110af565b610ace576104cf60043560ff6001918060081c60005282602052161b60406000205416151590565b610a9c57604051926104ed60206064356004013560051b0185611042565b60643560048101358552602401602085015b60246064356004013560051b60643501018210610a8c575050906000915b84518310156105645760208360051b860101519081811060001461055157600052602052600160406000205b92019161051d565b9060005260205260016040600020610549565b83907f000000000000000000000000000000000000000000000000000000000000000003610a6257604051907f5fe3b56700000000000000000000000000000000000000000000000000000000825260208260048173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa91821561099a57600092610a31575b506040517fdcf844a700000000000000000000000000000000000000000000000000000000815260208160248173ffffffffffffffffffffffffffffffffffffffff807f00000000000000000000000000000000000000000000000000000000000000001697886004840152165afa90811561099a576000916109ff575b506109d55760043560081c60005260016020526040600020600160ff600435161b81541790556040516106b18161100a565b600081526000602082015273ffffffffffffffffffffffffffffffffffffffff600054169260405193610100850185811067ffffffffffffffff8211176109a657604052845273ffffffffffffffffffffffffffffffffffffffff831660208501526fffffffffffffffffffffffffffffffff8516604085015260608401527f0000000000000000000000000000000000000000000000000000000000000000151560808401527f0000000000000000000000000000000000000000000000000000000000000000151560a084015260405161078c8161100a565b64ffffffffff600254818116835260281c16602082015260c084015260e0830152602060e0604051937fab167ccc00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff815116600486015273ffffffffffffffffffffffffffffffffffffffff838201511660248601526fffffffffffffffffffffffffffffffff604082015116604486015273ffffffffffffffffffffffffffffffffffffffff606082015116606486015260808101511515608486015260a0810151151560a486015264ffffffffff8360c08301518281511660c489015201511660e4860152015173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015260208261014481600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af191821561099a57600092610965575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d602011610992575b8161098060209383611042565b810103126100ed5790519060206108fe565b3d9150610973565b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60046040517fa4432b51000000000000000000000000000000000000000000000000000000008152fd5b90506020813d602011610a29575b81610a1a60209383611042565b810103126100ed57518461067f565b3d9150610a0d565b610a5491925060203d602011610a5b575b610a4c8183611042565b810190611083565b9083610601565b503d610a42565b60046040517fb3f3b2a6000000000000000000000000000000000000000000000000000000008152fd5b81358152602091820191016104ff565b60246040517f3548783b0000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f74b43bd00000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100ed5760006003193601126100ed57604060025464ffffffffff825191818116835260281c166020820152f35b346100ed5760006003193601126100ed5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100ed5760006003193601126100ed5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100ed5760406003193601126100ed57610bee610fe7565b90602435906fffffffffffffffffffffffffffffffff82168092036100ed5773ffffffffffffffffffffffffffffffffffffffff908160005416338103610f9e575050604051907f5fe3b5670000000000000000000000000000000000000000000000000000000082526020918281600481857f0000000000000000000000000000000000000000000000000000000000000000165afa90811561099a57600091610f81575b506040517fdcf844a7000000000000000000000000000000000000000000000000000000008152838160248186807f00000000000000000000000000000000000000000000000000000000000000001696876004840152165afa90811561099a57600091610f54575b50610d066110af565b159081610f4b575b50610eed5760405160008084868401987fa9059cbb000000000000000000000000000000000000000000000000000000008a52169788602485015287604485015260448452610d5c84611026565b60405193610d698561100a565b8785527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656488860152519082865af13d15610ee0573d9067ffffffffffffffff82116109a657610df59360405192610de7887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160185611042565b83523d60008885013e6110ec565b8051838115918215610ec0575b5050905015610e3c57907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b608482604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b83809293500103126100ed5782015180151581036100ed57808387610e02565b91610df5926060916110ec565b6040517f1351f21d0000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b90501586610d0e565b90508381813d8311610f7a575b610f6b8183611042565b810103126100ed575186610cfd565b503d610f61565b610f989150833d8511610a5b57610a4c8183611042565b85610c94565b7fc6cce6a400000000000000000000000000000000000000000000000000000000825273ffffffffffffffffffffffffffffffffffffffff166004820152336024820152604490fd5b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100ed57565b6040810190811067ffffffffffffffff8211176109a657604052565b6080810190811067ffffffffffffffff8211176109a657604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176109a657604052565b908160209103126100ed575173ffffffffffffffffffffffffffffffffffffffff811681036100ed5790565b64ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001680151590816110e4575090565b905042101590565b919290156111675750815115611100575090565b3b156111095790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b82519091501561117a5750805190602001fd5b604051907f08c379a000000000000000000000000000000000000000000000000000000000825281602080600483015282519283602484015260005b8481106111f7575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f836000604480968601015201168101030190fd5b8181018301518682016044015285935082016111b656fea164736f6c6343000817000aa164736f6c6343000817000a"; + hex"60808060405234610016576144fb908161001c8239f35b600080fdfe608060405260043610156200001357600080fd5b60003560e01c806324496a1514620003fc576360d18fed146200003557600080fd5b34620003f75760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112620003f75760043567ffffffffffffffff8111620003f757620000899036906004016200097d565b73ffffffffffffffffffffffffffffffffffffffff602435168060243503620003f75760407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc360112620003f757604051620000e581620008bf565b64ffffffffff926044358481168103620003f75782526064359384168403620003f75760209384830152805184820151620001386200014760408501516040519283918a80840152604083019062000a91565b03601f198101835282620008dc565b6200018260608501516200017c89604051836200016e829551809285808601910162000a6c565b8101038084520182620008dc565b62000ab8565b60808501519260a086015160c0870151151560e088015115159160405196620001c18d89018c6020908164ffffffffff91828151168552015116910152565b6040885287606081011067ffffffffffffffff60608a011117620003c8578c889760608901604052608089019a7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009788809260601b168d5260601b1660948a01528051809260a88b01920191620002389262000a6c565b87019560a887015260c886015260d81b7fffffffffff0000000000000000000000000000000000000000000000000000001660e885015260f81b60ed84015260f81b60ee83015260243560601b1660ef8201528151906101038101828a850191620002a39262000a6c565b010360838101606083015260a3016060820190620002c191620008dc565b60600151902060405161180c80820182811067ffffffffffffffff821117620003c857829162000c8a83396080815262000326604062000305608084018862000b4c565b92898b82015201876020908164ffffffffff91828151168552015116910152565b03906000f5928315620003bc5760a07f3ce4d6bb8b94ee36e2248c8057e53b793a45b37e4d58038ec3b5dc21c56e925491620003a373ffffffffffffffffffffffffffffffffffffffff6200037f971696879562000bdb565b95604051928352888301906020908164ffffffffff91828151168552015116910152565b608435606082015260a4356080820152a3604051908152f35b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b34620003f75760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112620003f75760043567ffffffffffffffff8111620003f757620004509036906004016200097d565b73ffffffffffffffffffffffffffffffffffffffff6024351660243503620003f75760443567ffffffffffffffff8111620003f75736602382011215620003f757806004013567ffffffffffffffff8111620003c85760059060405192620004bf60208360051b0185620008dc565b8184526024602085019260061b82010190368211620003f757602401915b8183106200086b5750505060009082516000915b818310620007d657505050670de0b6b3a76400008103620007a55750620005426200068b60a38451620005d16020870151620005516040890151604051978891602080840152604083019062000a91565b03601f198101885287620008dc565b6200057960608901516200017c6020604051836200016e829551809285808601910162000a6c565b90608089015160a08a01517fffffffffff00000000000000000000000000000000000000000000000000000060c08c015115159160e08d0151151593620005e08d604051998a91602080840152604083019062000afa565b03601f1981018a5289620008dc565b604862000634604051809e819c7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009a8b809260601b16602085015260601b1660348301526020815194859301910162000a6c565b8901966048880152606887015260d81b16608885015260f81b608d84015260f81b608e83015260243560601b16608f8201526200067b825180936020878501910162000a6c565b01036083810184520182620008dc565b602081519101206040516120598082019082821067ffffffffffffffff831117620003c8578291620006fe9162002496843960608152620006d0606082018862000b4c565b9073ffffffffffffffffffffffffffffffffffffffff60243516602082015260408183039101528562000afa565b03906000f5908115620003bc576020927f431b4603d07471c22cc6557755bfeb55c0f0c1163d5df05ee2b54701ab6674ab6200078a73ffffffffffffffffffffffffffffffffffffffff62000757951694859362000bdb565b9360405191829173ffffffffffffffffffffffffffffffffffffffff602435168352608089840152608083019062000afa565b606435604083015260843560608301520390a3604051908152f35b602490604051907f5484abf20000000000000000000000000000000000000000000000000000000082526004820152fd5b90919284518410156200083c5767ffffffffffffffff602085841b87010151511681018091116200080d57926001019190620004f1565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b604083360312620003f757604051906200088582620008bf565b83359067ffffffffffffffff82168203620003f7578260209260409452620008af8387016200095c565b83820152815201920191620004dd565b6040810190811067ffffffffffffffff821117620003c857604052565b90601f601f19910116810190811067ffffffffffffffff821117620003c857604052565b81601f82011215620003f75780359067ffffffffffffffff8211620003c85760405192620009396020601f19601f8601160185620008dc565b82845260208383010111620003f757816000926020809301838601378301015290565b359064ffffffffff82168203620003f757565b35908115158203620003f757565b919091610100908181850312620003f75760405191820167ffffffffffffffff9083811082821117620003c8576040528294823573ffffffffffffffffffffffffffffffffffffffff908181168103620003f757855260208401359081168103620003f75760208501526040830135828111620003f7578162000a0291850162000900565b60408501526060830135918211620003f7578262000a2b60e0949262000a679486940162000900565b60608601526080810135608086015262000a4860a082016200095c565b60a086015262000a5b60c082016200096f565b60c0860152016200096f565b910152565b60005b83811062000a805750506000910152565b818101518382015260200162000a6f565b90601f19601f60209362000ab18151809281875287808801910162000a6c565b0116010190565b60208151910151906020811062000acd575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b90815180825260208080930193019160005b82811062000b1b575050505090565b8351805167ffffffffffffffff16865282015164ffffffffff16858301526040909401939281019260010162000b0c565b9060e08062000ba962000b9661010073ffffffffffffffffffffffffffffffffffffffff808851168752602088015116602087015260408701519080604088015286019062000a91565b6060860151858203606087015262000a91565b936080810151608085015264ffffffffff60a08201511660a085015260c0810151151560c08501520151151591015290565b60c0604051809273ffffffffffffffffffffffffffffffffffffffff808251168352602082015116602083015260e0601f604083015162000c2781518092602060408901910162000a6c565b601f199182910116840190601f606085015162000c4f81518092602060408801910162000a6c565b011601916080810151604084015264ffffffffff60a082015116606084015284810151151560808401520151151560a0820152030190209056fe6101608060405234620005b4576200180c8038038091620000218285620005d5565b83398101908082039160808312620005b45781516001600160401b038111620005b45782019261010084830312620005b4576040519361010085016001600160401b038111868210176200059e5760405280516001600160a01b0381168103620005b45785526020810151926001600160a01b0384168403620005b4576020860193845260408201516001600160401b038111620005b45781620000c79184016200063a565b60408701908152606083015190916001600160401b038211620005b457620000f19184016200063a565b6060870152608082015195608081019687526200011160a0840162000687565b9660a082019788526200013c60e06200012d60c087016200069a565b9560c08501968752016200069a565b60e083019081526020880151959093906001600160a01b0387168703620005b457604090603f190112620005b45760408051989089016001600160401b0381118a8210176200059e57620001a7916060916040526200019e6040820162000687565b8b520162000687565b9460208901958652606084015151602081116200057f57508351600080546001600160a01b0319166001600160a01b0392831617815598511660805251151560a052975164ffffffffff90811660c052975180516001600160401b0381116200056b5760019182548381811c9116801562000560575b60208210146200054c57601f8111620004ff575b50602090601f83116001146200049357606095949392918a918362000487575b5050600019600383901b1c191690821b1790555b5160e052015160405162000299602082816200028b818301968781519384920162000615565b8101038084520182620005d5565b519051906020811062000474575b50610100525115159461012095865261014094838652511669ffffffffff0000000000600354925160281b169160018060501b031916171760035560018060a01b03608051166040519160208301848063095ea7b360e01b9283815260018060a01b03851660248801526000196044880152604487526200032887620005b9565b86519082875af162000339620006a8565b8162000433575b508062000428575b15620003dc575b60405161104290878983620007ca8439608051838181610470015281816107440152610c36015260a05183818161076b0152610b5c015260c05183818161015d01528181610aa801528181610d8f0152610f60015260e0518381816102e2015261066201526101005183610e250152518281816107930152610b1f0152518181816101ae01526109060152f35b6200041d94620004179260405192602084015260018060a01b031660248301526044820152604481526200041081620005b9565b82620006dd565b620006dd565b38808080806200034f565b50823b151562000348565b80518015925082156200044a575b50503862000340565b8192509060209181010312620004705760206200046891016200069a565b388062000441565b8580fd5b6000199060200360031b1b1638620002a7565b01519050388062000251565b838a5260208a209190601f1984168b5b818110620004e857509185949291836060999897959310620004ce575b505050811b01905562000265565b015160001960f88460031b161c19169055388080620004c0565b8284015185559386019360209384019301620004a3565b838a5260208a20601f840160051c8101916020851062000541575b601f0160051c019084905b8281106200053557505062000231565b8b815501849062000525565b90915081906200051a565b634e487b7160e01b8a52602260045260248afd5b90607f16906200021d565b634e487b7160e01b88526041600452602488fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b634e487b7160e01b600052604160045260246000fd5b600080fd5b608081019081106001600160401b038211176200059e57604052565b601f909101601f19168101906001600160401b038211908210176200059e57604052565b6001600160401b0381116200059e57601f01601f191660200190565b60005b838110620006295750506000910152565b818101518382015260200162000618565b81601f82011215620005b45780516200065381620005f9565b92620006636040519485620005d5565b81845260208284010111620005b45762000684916020808501910162000615565b90565b519064ffffffffff82168203620005b457565b51908115158203620005b457565b3d15620006d8573d90620006bc82620005f9565b91620006cc6040519384620005d5565b82523d6000602084013e565b606090565b6000806200070a9260018060a01b03169360208151910182865af162000702620006a8565b908362000761565b80519081151591826200073a575b5050620007225750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620005b45760206200075891016200069a565b15388062000718565b906200078a57508051156200077857805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580620007bf575b6200079d575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156200079456fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde0314610e0e575080631686c90914610b8157806316c3549d14610b445780631bfd681414610b075780633bfe03a814610ad85780633f31ae3f146104945780634800d97f1461044357806349fc73dd1461030557806351e75e8b146102ca57806375829def146101ed57806390e64d13146101d25780639e93e57714610181578063bb4b57341461013f578063ce516507146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760206003193601126100f857602061013560043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f8576020610135610f58565b346100f85760206003193601126100f857610206610ebc565b60005473ffffffffffffffffffffffffffffffffffffffff8082169233840361027d577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c9060018316928315610439575b602093848410811461040a578386529081156103cc5750600114610371575b61036d8461036181880382610f17565b60405191829182610e56565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8284106103b9575050508161036d936103619282010193610351565b805485850187015292850192810161039d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103618161036d610351565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691610332565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f857604435906fffffffffffffffffffffffffffffffff821682036100f85767ffffffffffffffff606435116100f8573660236064350112156100f85767ffffffffffffffff60643560040135116100f8573660246064356004013560051b6064350101116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff831660408201526fffffffffffffffffffffffffffffffff841660608201526060815261057a81610efb565b519020604051602081019182526020815261059481610edf565b519020916105a0610f58565b610a7a576105c960043560ff6001918060081c6000526002602052161b60406000205416151590565b610a4857604051926105e760206064356004013560051b0185610f17565b60643560048101358552602401602085015b60246064356004013560051b60643501018210610a38575050906000915b845183101561065e5760208360051b860101519081811060001461064b57600052602052600160406000205b920191610617565b9060005260205260016040600020610643565b83907f000000000000000000000000000000000000000000000000000000000000000003610a0e5760043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff60005416906040516106cd81610edf565b600081526000602082015260405192610100840184811067ffffffffffffffff8211176109df57604052835273ffffffffffffffffffffffffffffffffffffffff821660208401526fffffffffffffffffffffffffffffffff8416604084015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608401527f0000000000000000000000000000000000000000000000000000000000000000151560808401527f0000000000000000000000000000000000000000000000000000000000000000151560a08401526040516107c581610edf565b64ffffffffff600354818116835260281c16602082015260c084015260e0830152602060e0604051937fab167ccc00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff815116600486015273ffffffffffffffffffffffffffffffffffffffff838201511660248601526fffffffffffffffffffffffffffffffff604082015116604486015273ffffffffffffffffffffffffffffffffffffffff606082015116606486015260808101511515608486015260a0810151151560a486015264ffffffffff8360c08301518281511660c489015201511660e4860152015173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015260208261014481600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af19182156109d35760009261099e575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d6020116109cb575b816109b960209383610f17565b810103126100f8579051906020610937565b3d91506109ac565b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b81358152602091820191016105f9565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f857604060035464ffffffffff825191818116835260281c166020820152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857610b9a610ebc565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff8060005416338103610dbf5750610be1610f58565b15610d61576040519060209160008083858401977fa9059cbb000000000000000000000000000000000000000000000000000000008952169687602485015286604485015260448452610c3384610efb565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610d55573d67ffffffffffffffff81116109df57610cbc9160405191610cac877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610f17565b82523d60008784013e5b83610f95565b8051848115159182610d34575b50509050610d035750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488610cc9565b610cbc90606090610cb6565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f85761036d907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261036181610edf565b60208082528251818301819052939260005b858110610ea8575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201610e68565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff8211176109df57604052565b6080810190811067ffffffffffffffff8211176109df57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176109df57604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081610f8d575090565b905042101590565b90610fd45750805115610faa57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b8151158061102c575b610fe5575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15610fdd56fea164736f6c6343000817000a6101608060405234620006f7576200205990813803809262000022828462000743565b82396060818381010312620006f75780516001600160401b038111620006f7578101916101009283818385010312620006f757604051908482016001600160401b03811183821017620007115760405280516001600160a01b0381168103620006f757825260208101516001600160a01b0381168103620006f757602083015260408101516001600160401b038111620006f757620000c790848601908301620007a8565b604083015260608101516001600160401b038111620006f7576200013491620000f860e092868801908301620007a8565b6060850152608081015160808501526200011560a08201620007f5565b60a08501526200012860c0820162000808565b60c08501520162000808565b60e08201526020830151916001600160a01b0383168303620006f7576040840151906001600160401b038211620006f757808501601f838701011215620006f75784820151906001600160401b0382116200071157604051956200019f60208460051b018862000743565b828752602087019382820160208560061b838501010111620006f75793602085830101945b60208560061b828501010186106200069157505050505050606081015151602081116200067257508051600080546001600160a01b0319166001600160a01b03928316178155602083015190911660805260c080830151151560a090815283015164ffffffffff16905260408201518051919290916001600160401b0381116200065e5760019283548481811c9116801562000653575b60208210146200063f57601f8111620005f2575b50602090601f8311600114620005885760e09392918691836200057c575b5050600019600383901b1c191690841b1783555b608081015182526060810151604051620002db60208281620002cd818301968781519384920162000783565b810103808452018262000743565b519051906020811062000569575b5087520151151593610120948552610140938452805190835b828110620004a7575050505060018060a01b036080511660018060a01b03835116906040519160208301848063095ea7b360e01b92838152846024880152600019604488015260448752620003578762000727565b86519082875af16200036862000816565b8162000466575b50806200045b575b1562000417575b6040516117219087898b846200093885396080518481816104e2015281816107fe0152818161092001526110e2015260a0518481816109470152611008015260c05184818161022001528181610f830152818161123b015261140c015260e05184818161035401526106b2015251836112d101525182818161096f0152610fcb01525181818161012a015281816107850152610ad90152f35b62000450946200044a926040519260208401526024830152604482015260448152620004438162000727565b826200084b565b6200084b565b38808080806200037e565b50823b151562000377565b80518015925082156200047d575b5050386200036f565b8192509060209181010312620004a35760206200049b910162000808565b388062000474565b8580fd5b8151811015620005555760208160051b8301015160038054906801000000000000000082101562000541578682018082558210156200052d578752602080882083519201805493909101516cffffffffff000000000000000060409190911b166001600160401b039092166001600160681b031990931692909217179055830162000302565b634e487b7160e01b88526032600452602488fd5b634e487b7160e01b88526041600452602488fd5b634e487b7160e01b85526032600452602485fd5b6000199060200360031b1b1638620002e9565b0151905038806200028d565b92918491601f198216908388526020882091885b818110620005d95750958360e09710620005bf575b505050811b018355620002a1565b015160001960f88460031b161c19169055388080620005b1565b828801518455889590930192602092830192016200059c565b84865260208620601f840160051c8101916020851062000634575b601f0160051c019085905b828110620006285750506200026f565b87815501859062000618565b90915081906200060d565b634e487b7160e01b86526022600452602486fd5b90607f16906200025b565b634e487b7160e01b84526041600452602484fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b6040868585010312620006f757604080519081016001600160401b03811182821017620006fc5760405286516001600160401b0381168103620006f75760209382859260409452620006e5838b01620007f5565b838201528152019601959150620001c4565b600080fd5b60246000634e487b7160e01b81526041600452fd5b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b038211176200071157604052565b601f909101601f19168101906001600160401b038211908210176200071157604052565b6001600160401b0381116200071157601f01601f191660200190565b60005b838110620007975750506000910152565b818101518382015260200162000786565b81601f82011215620006f7578051620007c18162000767565b92620007d1604051948562000743565b81845260208284010111620006f757620007f2916020808501910162000783565b90565b519064ffffffffff82168203620006f757565b51908115158203620006f757565b3d1562000846573d906200082a8262000767565b916200083a604051938462000743565b82523d6000602084013e565b606090565b600080620008789260018060a01b03169360208151910182865af16200087062000816565b9083620008cf565b8051908115159182620008a8575b5050620008905750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620006f7576020620008c6910162000808565b15388062000886565b90620008f85750805115620008e657805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806200092d575b6200090b575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156200090256fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde03146112ba575080631686c9091461102d57806316c3549d14610ff05780631bfd681414610fb35780633f31ae3f146105065780634800d97f146104b557806349fc73dd1461037757806351e75e8b1461033c57806375829def1461025f57806390e64d1314610244578063bb4b573414610202578063bf4ed03f14610190578063ce5165071461014e578063da792468146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760206003193601126100f857602061018660043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f8576101a9611459565b6040516020918282018383528151809152836040840192019360005b8281106101d25784840385f35b8551805167ffffffffffffffff16855282015164ffffffffff1684830152948101946040909301926001016101c5565b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f8576020610186611404565b346100f85760206003193601126100f857610278611368565b60005473ffffffffffffffffffffffffffffffffffffffff808216923384036102ef577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c90600183169283156104ab575b602093848410811461047c5783865290811561043e57506001146103e3575b6103df846103d3818803826113c3565b60405191829182611302565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b82841061042b57505050816103df936103d392820101936103c3565b805485850187015292850192810161040f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103d3816103df6103c3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f16916103a4565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f857604435906fffffffffffffffffffffffffffffffff821682036100f8576064359167ffffffffffffffff83116100f857366023840112156100f85782600401359267ffffffffffffffff84116100f85760248460051b8201013681116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff861660408201526fffffffffffffffffffffffffffffffff85166060820152606081526105e5816113a7565b51902060405160208101918252602081526105ff8161138b565b5190209161060b611404565b610f555761063460043560ff6001918060081c6000526002602052161b60406000205416151590565b610f235761064186611441565b9561064f60405197886113c3565b8652602401602086015b828210610f1357505050906000915b84518310156106ae5761067b83866114f4565b51908181101561069b57600052602052600160406000205b920191610668565b9060005260205260016040600020610693565b83907f000000000000000000000000000000000000000000000000000000000000000003610ee9576106de611459565b9060008251906106ed82611441565b936106fb60405195866113c3565b8285527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061072884611441565b0160005b818110610ec45750506000905b838210610e205750506040517f5fe3b56700000000000000000000000000000000000000000000000000000000815260208160048173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa908115610ba657600091610dd0575b50602073ffffffffffffffffffffffffffffffffffffffff916024604051809481937fdcf844a7000000000000000000000000000000000000000000000000000000008352817f0000000000000000000000000000000000000000000000000000000000000000166004840152165afa908115610ba657600091610d9e575b5080610cfd575b50806fffffffffffffffffffffffffffffffff861603610c1f575b505060043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff6000541691604051906108a18261138b565b6000825260006020830152604051938461010081011067ffffffffffffffff61010087011117610bf0576101008501604052845273ffffffffffffffffffffffffffffffffffffffff831660208501526fffffffffffffffffffffffffffffffff8516604085015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608501527f0000000000000000000000000000000000000000000000000000000000000000151560808501527f0000000000000000000000000000000000000000000000000000000000000000151560a085015260c084015260e0830152604051917f897f362b0000000000000000000000000000000000000000000000000000000083526020600484015282610144810173ffffffffffffffffffffffffffffffffffffffff835116602483015273ffffffffffffffffffffffffffffffffffffffff60208401511660448301526fffffffffffffffffffffffffffffffff604084015116606483015273ffffffffffffffffffffffffffffffffffffffff60608401511660848301526080830151151560a483015260a0830151151560c483015260c08301519061012060e484015281518091526020610164840192019060005b818110610bb2575050508190602060e08195015173ffffffffffffffffffffffffffffffffffffffff81511661010485015201516101248301520381600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1918215610ba657600092610b71575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d602011610b9e575b81610b8c602093836113c3565b810103126100f8579051906020610b0a565b3d9150610b7f565b6040513d6000823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff168186015288955060409094019390920191600101610a84565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610c4d90610c47610c416fffffffffffffffffffffffffffffffff8816611559565b91611559565b90611537565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101908111610cce57610c8290846114f4565b51906fffffffffffffffffffffffffffffffff808351169116016fffffffffffffffffffffffffffffffff8111610cce576fffffffffffffffffffffffffffffffff1690528380610857565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b610d1c610d21916fffffffffffffffffffffffffffffffff88166115a7565b611559565b827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810111610cce576fffffffffffffffffffffffffffffffff610d95610d8a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8601886114f4565b519282845116611537565b1690528561083c565b90506020813d602011610dc8575b81610db9602093836113c3565b810103126100f8575186610835565b3d9150610dac565b90506020813d602011610e18575b81610deb602093836113c3565b810103126100f8575173ffffffffffffffffffffffffffffffffffffffff811681036100f85760206107b6565b3d9150610dde565b9091610e5367ffffffffffffffff610e3885856114f4565b5151166fffffffffffffffffffffffffffffffff89166115a7565b928382018211610cce57610e6b846001930194611559565b64ffffffffff6020610e7d84876114f4565b510151166fffffffffffffffffffffffffffffffff60405192610e9f8461138b565b1682526020820152610eb182896114f4565b52610ebc81886114f4565b500190610739565b602090604051610ed38161138b565b6000815260008382015282828a0101520161072c565b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b8135815260209182019101610659565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857611046611368565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff806000541633810361126b575061108d611404565b1561120d576040519060209160008083858401977fa9059cbb0000000000000000000000000000000000000000000000000000000089521696876024850152866044850152604484526110df846113a7565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15611201573d67ffffffffffffffff8111610bf0576111689160405191611158877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846113c3565b82523d60008784013e5b83611674565b80518481151591826111e0575b505090506111af5750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488611175565b61116890606090611162565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f8576103df907f00000000000000000000000000000000000000000000000000000000000000006020820152602081526103d38161138b565b60208082528251818301819052939260005b858110611354575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201611314565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff821117610bf057604052565b6080810190811067ffffffffffffffff821117610bf057604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610bf057604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081611439575090565b905042101590565b67ffffffffffffffff8111610bf05760051b60200190565b6003549061146682611441565b91604061147660405194856113c3565b8184528360208091019160036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b906000935b8585106114ba57505050505050565b600184819284516114ca8161138b565b64ffffffffff875467ffffffffffffffff81168352871c16838201528152019301940193916114ab565b80518210156115085760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6fffffffffffffffffffffffffffffffff9182169082160391908211610cce57565b6fffffffffffffffffffffffffffffffff90818111611576571690565b602490604051907f4916adce0000000000000000000000000000000000000000000000000000000082526004820152fd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8382098382029182808310920391808303921461166357670de0b6b3a7640000908183101561162c57947faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac1066994950990828211900360ee1b910360121c170290565b60449086604051917f5173648d00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5050670de0b6b3a764000090049150565b906116b3575080511561168957805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b8151158061170b575b6116c4575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b156116bc56fea164736f6c6343000817000aa164736f6c6343000817000a"; /*////////////////////////////////////////////////////////////////////////// DEPLOYERS From caa7a2994859d2a901e91f122bb27f100c2a906d Mon Sep 17 00:00:00 2001 From: andreivladbrg Date: Thu, 7 Mar 2024 13:47:05 +0200 Subject: [PATCH 22/61] feat: add batch function for lockup tranched test: lockup tranched batch functions --- src/SablierV2Batch.sol | 106 +++++++++++++++++- src/interfaces/ISablierV2Batch.sol | 43 +++++++ src/types/DataTypes.sol | 27 ++++- test/Base.t.sol | 48 +++++++- test/fork/assets/USDC.t.sol | 5 + test/fork/assets/USDT.t.sol | 5 + test/fork/batch/createWithTimestampsLT.t.sol | 87 ++++++++++++++ .../createWithDurations.t.sol | 45 ++++++++ .../createWithDurations.tree | 6 + .../createWithTimestamps.t.sol | 45 ++++++++ .../createWithTimestamps.tree | 6 + test/utils/BatchBuilder.sol | 77 ++++++++++++- test/utils/Defaults.sol | 77 ++++++++++++- 13 files changed, 568 insertions(+), 9 deletions(-) create mode 100644 test/fork/batch/createWithTimestampsLT.t.sol create mode 100644 test/integration/batch/lockup-tranched/create-with-durations/createWithDurations.t.sol create mode 100644 test/integration/batch/lockup-tranched/create-with-durations/createWithDurations.tree create mode 100644 test/integration/batch/lockup-tranched/create-with-timestamps/createWithTimestamps.t.sol create mode 100644 test/integration/batch/lockup-tranched/create-with-timestamps/createWithTimestamps.tree diff --git a/src/SablierV2Batch.sol b/src/SablierV2Batch.sol index 12ff58e6..0b15fb74 100644 --- a/src/SablierV2Batch.sol +++ b/src/SablierV2Batch.sol @@ -3,9 +3,10 @@ pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; import { ISablierV2LockupDynamic } from "@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol"; -import { LockupDynamic, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; +import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; +import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; +import { LockupDynamic, LockupLinear, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; import { ISablierV2Batch } from "./interfaces/ISablierV2Batch.sol"; import { Errors } from "./libraries/Errors.sol"; @@ -217,6 +218,107 @@ contract SablierV2Batch is ISablierV2Batch { } } + /*////////////////////////////////////////////////////////////////////////// + SABLIER-V2-LOCKUP-TRANCHED + //////////////////////////////////////////////////////////////////////////*/ + + /// @inheritdoc ISablierV2Batch + function createWithDurationsLT( + ISablierV2LockupTranched lockupTranched, + IERC20 asset, + Batch.CreateWithDurationsLT[] calldata batch + ) + external + override + returns (uint256[] memory streamIds) + { + // Check that the batch size is not zero. + uint256 batchSize = batch.length; + if (batchSize == 0) { + revert Errors.SablierV2Batch_BatchSizeZero(); + } + + // Calculate the sum of all of stream amounts. It is safe to use unchecked addition because one of the create + // transactions will revert if there is overflow. + uint256 i; + uint256 transferAmount; + for (i = 0; i < batchSize; ++i) { + unchecked { + transferAmount += batch[i].totalAmount; + } + } + + // Perform the ERC-20 transfer and approve {SablierV2LockupTranched} to spend the amount of assets. + _handleTransfer(address(lockupTranched), asset, transferAmount); + + // Create a stream for each element in the parameter array. + streamIds = new uint256[](batchSize); + for (i = 0; i < batchSize; ++i) { + // Create the stream. + streamIds[i] = lockupTranched.createWithDurations( + LockupTranched.CreateWithDurations({ + sender: batch[i].sender, + recipient: batch[i].recipient, + totalAmount: batch[i].totalAmount, + asset: asset, + cancelable: batch[i].cancelable, + transferable: batch[i].transferable, + tranches: batch[i].tranches, + broker: batch[i].broker + }) + ); + } + } + + /// @inheritdoc ISablierV2Batch + function createWithTimestampsLT( + ISablierV2LockupTranched lockupTranched, + IERC20 asset, + Batch.CreateWithTimestampsLT[] calldata batch + ) + external + override + returns (uint256[] memory streamIds) + { + // Check that the batch size is not zero. + uint256 batchSize = batch.length; + if (batchSize == 0) { + revert Errors.SablierV2Batch_BatchSizeZero(); + } + + // Calculate the sum of all of stream amounts. It is safe to use unchecked addition because one of the create + // transactions will revert if there is overflow. + uint256 i; + uint256 transferAmount; + for (i = 0; i < batchSize; ++i) { + unchecked { + transferAmount += batch[i].totalAmount; + } + } + + // Perform the ERC-20 transfer and approve {SablierV2LockupTranched} to spend the amount of assets. + _handleTransfer(address(lockupTranched), asset, transferAmount); + + // Create a stream for each element in the parameter array. + streamIds = new uint256[](batchSize); + for (i = 0; i < batchSize; ++i) { + // Create the stream. + streamIds[i] = lockupTranched.createWithTimestamps( + LockupTranched.CreateWithTimestamps({ + sender: batch[i].sender, + recipient: batch[i].recipient, + totalAmount: batch[i].totalAmount, + asset: asset, + cancelable: batch[i].cancelable, + transferable: batch[i].transferable, + startTime: batch[i].startTime, + tranches: batch[i].tranches, + broker: batch[i].broker + }) + ); + } + } + /*////////////////////////////////////////////////////////////////////////// HELPER FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ diff --git a/src/interfaces/ISablierV2Batch.sol b/src/interfaces/ISablierV2Batch.sol index 7b3512a4..32ec6e95 100644 --- a/src/interfaces/ISablierV2Batch.sol +++ b/src/interfaces/ISablierV2Batch.sol @@ -4,6 +4,7 @@ pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ISablierV2LockupDynamic } from "@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; +import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; import { Batch } from "../types/DataTypes.sol"; @@ -93,4 +94,46 @@ interface ISablierV2Batch { ) external returns (uint256[] memory streamIds); + + /*////////////////////////////////////////////////////////////////////////// + SABLIER-V2-LOCKUP-TRANCHED + //////////////////////////////////////////////////////////////////////////*/ + + /// @notice Creates a batch of Lockup Tranched streams using `createWithDurations`. + /// + /// @dev Requirements: + /// - There must be at least one element in `batch`. + /// - All requirements from {ISablierV2LockupTranched.createWithDurations} must be met for each stream. + /// + /// @param lockupTranched The address of the {SablierV2LockupTranched} contract. + /// @param asset The contract address of the ERC-20 asset used for streaming. + /// @param batch An array of structs, each encapsulating a subset of the parameters of + /// {SablierV2LockupTranched.createWithDurations}. + /// @return streamIds The ids of the newly created streams. + function createWithDurationsLT( + ISablierV2LockupTranched lockupTranched, + IERC20 asset, + Batch.CreateWithDurationsLT[] calldata batch + ) + external + returns (uint256[] memory streamIds); + + /// @notice Creates a batch of Lockup Tranched streams using `createWithTimestamps`. + /// + /// @dev Requirements: + /// - There must be at least one element in `batch`. + /// - All requirements from {ISablierV2LockupTranched.createWithTimestamps} must be met for each stream. + /// + /// @param lockupTranched The address of the {SablierV2LockupTranched} contract. + /// @param asset The contract address of the ERC-20 asset used for streaming. + /// @param batch An array of structs, each encapsulating a subset of the parameters of + /// {SablierV2LockupTranched.createWithTimestamps}. + /// @return streamIds The ids of the newly created streams. + function createWithTimestampsLT( + ISablierV2LockupTranched lockupTranched, + IERC20 asset, + Batch.CreateWithTimestampsLT[] calldata batch + ) + external + returns (uint256[] memory streamIds); } diff --git a/src/types/DataTypes.sol b/src/types/DataTypes.sol index 21cdcffb..9b84ad03 100644 --- a/src/types/DataTypes.sol +++ b/src/types/DataTypes.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.22; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { UD2x18 } from "@prb/math/src/UD2x18.sol"; import { ISablierV2Lockup } from "@sablier/v2-core/src/interfaces/ISablierV2Lockup.sol"; -import { Broker, LockupDynamic, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; +import { Broker, LockupDynamic, LockupLinear, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; library Batch { /// @notice A struct encapsulating the lockup contract's address and the stream ids to cancel. @@ -37,6 +37,18 @@ library Batch { Broker broker; } + /// @notice A struct encapsulating all parameters of {SablierV2LockupTranched.createWithDurations} except for the + /// asset. + struct CreateWithDurationsLT { + address sender; + address recipient; + uint128 totalAmount; + bool cancelable; + bool transferable; + LockupTranched.TrancheWithDuration[] tranches; + Broker broker; + } + /// @notice A struct encapsulating all parameters of {SablierV2LockupDynamic.createWithTimestamps} except for the /// asset. struct CreateWithTimestampsLD { @@ -61,6 +73,19 @@ library Batch { LockupLinear.Range range; Broker broker; } + + /// @notice A struct encapsulating all parameters of {SablierV2LockupTranched.createWithTimestamps} except for the + /// asset. + struct CreateWithTimestampsLT { + address sender; + address recipient; + uint128 totalAmount; + bool cancelable; + bool transferable; + uint40 startTime; + LockupTranched.Tranche[] tranches; + Broker broker; + } } library MerkleLockup { diff --git a/test/Base.t.sol b/test/Base.t.sol index 23fecf6d..21831039 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -8,7 +8,7 @@ import { ISablierV2Comptroller } from "@sablier/v2-core/src/interfaces/ISablierV import { ISablierV2LockupDynamic } from "@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; -import { LockupDynamic, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; +import { LockupDynamic, LockupLinear, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; import { Assertions as V2CoreAssertions } from "@sablier/v2-core/test/utils/Assertions.sol"; import { Utils as V2CoreUtils } from "@sablier/v2-core/test/utils/Utils.sol"; @@ -134,6 +134,14 @@ abstract contract Base_Test is Assertions, DeployOptimized, Events, Merkle, V2Co }); } + /// @dev Expects a call to {ISablierV2LockupTranched.createWithDurations}. + function expectCallToCreateWithDurationsLT(LockupTranched.CreateWithDurations memory params) internal { + vm.expectCall({ + callee: address(lockupTranched), + data: abi.encodeCall(ISablierV2LockupTranched.createWithDurations, (params)) + }); + } + /// @dev Expects a call to {ISablierV2LockupDynamic.createWithTimestamps}. function expectCallToCreateWithTimestampsLD(LockupDynamic.CreateWithTimestamps memory params) internal { vm.expectCall({ @@ -150,6 +158,14 @@ abstract contract Base_Test is Assertions, DeployOptimized, Events, Merkle, V2Co }); } + /// @dev Expects a call to {ISablierV2LockupTranched.createWithTimestamps}. + function expectCallToCreateWithTimestampsLT(LockupTranched.CreateWithTimestamps memory params) internal { + vm.expectCall({ + callee: address(lockupTranched), + data: abi.encodeCall(ISablierV2LockupTranched.createWithTimestamps, (params)) + }); + } + /// @dev Expects a call to {IERC20.transfer}. function expectCallToTransfer(address to, uint256 amount) internal { expectCallToTransfer(address(dai), to, amount); @@ -200,6 +216,21 @@ abstract contract Base_Test is Assertions, DeployOptimized, Events, Merkle, V2Co }); } + /// @dev Expects multiple calls to {ISablierV2LockupTranched.createWithDurations}, each with the specified + /// `params`. + function expectMultipleCallsToCreateWithDurationsLT( + uint64 count, + LockupTranched.CreateWithDurations memory params + ) + internal + { + vm.expectCall({ + callee: address(lockupTranched), + count: count, + data: abi.encodeCall(ISablierV2LockupTranched.createWithDurations, (params)) + }); + } + /// @dev Expects multiple calls to {ISablierV2LockupDynamic.createWithTimestamps}, each with the specified /// `params`. function expectMultipleCallsToCreateWithTimestampsLD( @@ -230,6 +261,21 @@ abstract contract Base_Test is Assertions, DeployOptimized, Events, Merkle, V2Co }); } + /// @dev Expects multiple calls to {ISablierV2LockupTranched.createWithTimestamps}, each with the specified + /// `params`. + function expectMultipleCallsToCreateWithTimestampsLT( + uint64 count, + LockupTranched.CreateWithTimestamps memory params + ) + internal + { + vm.expectCall({ + callee: address(lockupTranched), + count: count, + data: abi.encodeCall(ISablierV2LockupTranched.createWithTimestamps, (params)) + }); + } + /// @dev Expects multiple calls to {IERC20.transfer}. function expectMultipleCallsToTransfer(uint64 count, address to, uint256 amount) internal { vm.expectCall({ callee: address(dai), count: count, data: abi.encodeCall(IERC20.transfer, (to, amount)) }); diff --git a/test/fork/assets/USDC.t.sol b/test/fork/assets/USDC.t.sol index 76b283c3..342d7f56 100644 --- a/test/fork/assets/USDC.t.sol +++ b/test/fork/assets/USDC.t.sol @@ -5,6 +5,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { CreateWithTimestamps_LockupDynamic_Batch_Fork_Test } from "../batch/createWithTimestampsLD.t.sol"; import { CreateWithTimestamps_LockupLinear_Batch_Fork_Test } from "../batch/createWithTimestampsLL.t.sol"; +import { CreateWithTimestamps_LockupTranched_Batch_Fork_Test } from "../batch/createWithTimestampsLT.t.sol"; import { MerkleLockupLL_Fork_Test } from "../merkle-lockup/MerkleLockupLL.t.sol"; import { MerkleLockupLT_Fork_Test } from "../merkle-lockup/MerkleLockupLT.t.sol"; @@ -19,6 +20,10 @@ contract USDC_CreateWithTimestamps_LockupLinear_Batch_Fork_Test is CreateWithTimestamps_LockupLinear_Batch_Fork_Test(usdc) { } +contract USDC_CreateWithTimestamps_LockupTranched_Batch_Fork_Test is + CreateWithTimestamps_LockupTranched_Batch_Fork_Test(usdc) +{ } + contract USDC_MerkleLockupLL_Fork_Test is MerkleLockupLL_Fork_Test(usdc) { } contract USDC_MerkleLockupLT_Fork_Test is MerkleLockupLT_Fork_Test(usdc) { } diff --git a/test/fork/assets/USDT.t.sol b/test/fork/assets/USDT.t.sol index 0e60fea1..75c4434e 100644 --- a/test/fork/assets/USDT.t.sol +++ b/test/fork/assets/USDT.t.sol @@ -5,6 +5,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { CreateWithTimestamps_LockupDynamic_Batch_Fork_Test } from "../batch/createWithTimestampsLD.t.sol"; import { CreateWithTimestamps_LockupLinear_Batch_Fork_Test } from "../batch/createWithTimestampsLL.t.sol"; +import { CreateWithTimestamps_LockupTranched_Batch_Fork_Test } from "../batch/createWithTimestampsLT.t.sol"; import { MerkleLockupLL_Fork_Test } from "../merkle-lockup/MerkleLockupLL.t.sol"; import { MerkleLockupLT_Fork_Test } from "../merkle-lockup/MerkleLockupLT.t.sol"; @@ -19,6 +20,10 @@ contract USDT_CreateWithTimestamps_LockupLinear_Batch_Fork_Test is CreateWithTimestamps_LockupLinear_Batch_Fork_Test(usdt) { } +contract USDT_CreateWithTimestamps_LockupTranched_Batch_Fork_Test is + CreateWithTimestamps_LockupTranched_Batch_Fork_Test(usdt) +{ } + contract USDT_MerkleLockupLL_Fork_Test is MerkleLockupLL_Fork_Test(usdt) { } contract USDT_MerkleLockupLT_Fork_Test is MerkleLockupLT_Fork_Test(usdt) { } diff --git a/test/fork/batch/createWithTimestampsLT.t.sol b/test/fork/batch/createWithTimestampsLT.t.sol new file mode 100644 index 00000000..ec4417aa --- /dev/null +++ b/test/fork/batch/createWithTimestampsLT.t.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22 <0.9.0; + +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; + +import { Batch } from "src/types/DataTypes.sol"; + +import { Fork_Test } from "../Fork.t.sol"; +import { ArrayBuilder } from "../../utils/ArrayBuilder.sol"; +import { BatchBuilder } from "../../utils/BatchBuilder.sol"; + +/// @dev Runs against multiple fork assets. +abstract contract CreateWithTimestamps_LockupTranched_Batch_Fork_Test is Fork_Test { + constructor(IERC20 asset_) Fork_Test(asset_) { } + + function setUp() public virtual override { + Fork_Test.setUp(); + } + + /*////////////////////////////////////////////////////////////////////////// + BATCH-CREATE-WITH-TIMESTAMPS + //////////////////////////////////////////////////////////////////////////*/ + + struct CreateWithTimestampsParams { + uint128 batchSize; + address sender; + address recipient; + uint128 perStreamAmount; + uint40 startTime; + LockupTranched.Tranche[] tranches; + } + + function testForkFuzz_CreateWithTimestamps(CreateWithTimestampsParams memory params) external { + vm.assume(params.tranches.length != 0); + params.batchSize = boundUint128(params.batchSize, 1, 20); + params.startTime = boundUint40(params.startTime, getBlockTimestamp(), getBlockTimestamp() + 24 hours); + fuzzTrancheTimestamps(params.tranches, params.startTime); + (params.perStreamAmount,) = fuzzTranchedStreamAmounts({ + upperBound: MAX_UINT128 / params.batchSize, + tranches: params.tranches, + protocolFee: defaults.PROTOCOL_FEE(), + brokerFee: defaults.BROKER_FEE() + }); + + checkUsers(params.sender, params.recipient); + + uint256 firstStreamId = lockupTranched.nextStreamId(); + uint128 totalTransferAmount = params.perStreamAmount * params.batchSize; + + deal({ token: address(ASSET), to: params.sender, give: uint256(totalTransferAmount) }); + approveContract({ asset_: ASSET, from: params.sender, spender: address(batch) }); + + LockupTranched.CreateWithTimestamps memory createWithTimestamps = LockupTranched.CreateWithTimestamps({ + sender: params.sender, + recipient: params.recipient, + totalAmount: params.perStreamAmount, + asset: ASSET, + cancelable: true, + transferable: true, + startTime: params.startTime, + tranches: params.tranches, + broker: defaults.broker() + }); + Batch.CreateWithTimestampsLT[] memory batchParams = + BatchBuilder.fillBatch(createWithTimestamps, params.batchSize); + + expectCallToTransferFrom({ + asset_: address(ASSET), + from: params.sender, + to: address(batch), + amount: totalTransferAmount + }); + expectMultipleCallsToCreateWithTimestampsLT({ count: uint64(params.batchSize), params: createWithTimestamps }); + expectMultipleCallsToTransferFrom({ + asset_: address(ASSET), + count: uint64(params.batchSize), + from: address(batch), + to: address(lockupTranched), + amount: params.perStreamAmount + }); + + uint256[] memory actualStreamIds = batch.createWithTimestampsLT(lockupTranched, ASSET, batchParams); + uint256[] memory expectedStreamIds = ArrayBuilder.fillStreamIds(firstStreamId, params.batchSize); + assertEq(actualStreamIds, expectedStreamIds); + } +} diff --git a/test/integration/batch/lockup-tranched/create-with-durations/createWithDurations.t.sol b/test/integration/batch/lockup-tranched/create-with-durations/createWithDurations.t.sol new file mode 100644 index 00000000..88ac0984 --- /dev/null +++ b/test/integration/batch/lockup-tranched/create-with-durations/createWithDurations.t.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22 <0.9.0; + +import { Errors } from "src/libraries/Errors.sol"; +import { Batch } from "src/types/DataTypes.sol"; + +import { Integration_Test } from "../../../Integration.t.sol"; + +contract CreateWithDurations_LockupTranched_Integration_Test is Integration_Test { + function setUp() public virtual override { + Integration_Test.setUp(); + } + + function test_RevertWhen_BatchSizeZero() external { + Batch.CreateWithDurationsLT[] memory batchParams = new Batch.CreateWithDurationsLT[](0); + vm.expectRevert(Errors.SablierV2Batch_BatchSizeZero.selector); + batch.createWithDurationsLT(lockupTranched, dai, batchParams); + } + + modifier whenBatchSizeNotZero() { + _; + } + + function test_BatchCreateWithDurations() external whenBatchSizeNotZero { + // Asset flow: Alice → batch → Sablier + // Expect transfers from Alice to the batch, and then from the batch to the Sablier contract. + expectCallToTransferFrom({ from: users.alice, to: address(batch), amount: defaults.TOTAL_TRANSFER_AMOUNT() }); + expectMultipleCallsToCreateWithDurationsLT({ + count: defaults.BATCH_SIZE(), + params: defaults.createWithDurationsLT() + }); + expectMultipleCallsToTransferFrom({ + count: defaults.BATCH_SIZE(), + from: address(batch), + to: address(lockupTranched), + amount: defaults.PER_STREAM_AMOUNT() + }); + + // Assert that the batch of streams has been created successfully. + uint256[] memory actualStreamIds = + batch.createWithDurationsLT(lockupTranched, dai, defaults.batchCreateWithDurationsLT()); + uint256[] memory expectedStreamIds = defaults.incrementalStreamIds(); + assertEq(actualStreamIds, expectedStreamIds, "stream ids mismatch"); + } +} diff --git a/test/integration/batch/lockup-tranched/create-with-durations/createWithDurations.tree b/test/integration/batch/lockup-tranched/create-with-durations/createWithDurations.tree new file mode 100644 index 00000000..377110d8 --- /dev/null +++ b/test/integration/batch/lockup-tranched/create-with-durations/createWithDurations.tree @@ -0,0 +1,6 @@ +createWithDurations.t.sol +├── when the batch size is zero +│ └── it should revert +└── when the batch size is not zero + ├── it should create a batch of streams with durations + └── it should perform the ERC-20 transfers diff --git a/test/integration/batch/lockup-tranched/create-with-timestamps/createWithTimestamps.t.sol b/test/integration/batch/lockup-tranched/create-with-timestamps/createWithTimestamps.t.sol new file mode 100644 index 00000000..28528ac9 --- /dev/null +++ b/test/integration/batch/lockup-tranched/create-with-timestamps/createWithTimestamps.t.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22 <0.9.0; + +import { Errors } from "src/libraries/Errors.sol"; +import { Batch } from "src/types/DataTypes.sol"; + +import { Integration_Test } from "../../../Integration.t.sol"; + +contract CreateWithTimestamps_LockupTranched_Integration_Test is Integration_Test { + function setUp() public virtual override { + Integration_Test.setUp(); + } + + function test_RevertWhen_BatchSizeZero() external { + Batch.CreateWithTimestampsLT[] memory batchParams = new Batch.CreateWithTimestampsLT[](0); + vm.expectRevert(Errors.SablierV2Batch_BatchSizeZero.selector); + batch.createWithTimestampsLT(lockupTranched, dai, batchParams); + } + + modifier whenBatchSizeNotZero() { + _; + } + + function test_BatchCreateWithTimestamps() external whenBatchSizeNotZero { + // Asset flow: Alice → batch → Sablier + // Expect transfers from Alice to the batch, and then from the batch to the Sablier contract. + expectCallToTransferFrom({ from: users.alice, to: address(batch), amount: defaults.TOTAL_TRANSFER_AMOUNT() }); + expectMultipleCallsToCreateWithTimestampsLT({ + count: defaults.BATCH_SIZE(), + params: defaults.createWithTimestampsLT() + }); + expectMultipleCallsToTransferFrom({ + count: defaults.BATCH_SIZE(), + from: address(batch), + to: address(lockupTranched), + amount: defaults.PER_STREAM_AMOUNT() + }); + + // Assert that the batch of streams has been created successfully. + uint256[] memory actualStreamIds = + batch.createWithTimestampsLT(lockupTranched, dai, defaults.batchCreateWithTimestampsLT()); + uint256[] memory expectedStreamIds = defaults.incrementalStreamIds(); + assertEq(actualStreamIds, expectedStreamIds, "stream ids mismatch"); + } +} diff --git a/test/integration/batch/lockup-tranched/create-with-timestamps/createWithTimestamps.tree b/test/integration/batch/lockup-tranched/create-with-timestamps/createWithTimestamps.tree new file mode 100644 index 00000000..7769ecd1 --- /dev/null +++ b/test/integration/batch/lockup-tranched/create-with-timestamps/createWithTimestamps.tree @@ -0,0 +1,6 @@ +createWithTimestamps.t.sol +├── when the batch size is zero +│ └── it should revert +└── when the batch size is not zero + ├── it should create a batch of streams with timestamps + └── it should perform the ERC-20 transfers diff --git a/test/utils/BatchBuilder.sol b/test/utils/BatchBuilder.sol index d52e3dff..6567d193 100644 --- a/test/utils/BatchBuilder.sol +++ b/test/utils/BatchBuilder.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; -import { LockupDynamic, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; +import { LockupDynamic, LockupLinear, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; import { Batch } from "../../src/types/DataTypes.sol"; @@ -80,6 +80,43 @@ library BatchBuilder { batch = fillBatch(batchSingle, batchSize); } + /// @notice Generates an array containing `batchSize` copies of `batchSingle`. + function fillBatch( + Batch.CreateWithDurationsLT memory batchSingle, + uint256 batchSize + ) + internal + pure + returns (Batch.CreateWithDurationsLT[] memory batch) + { + batch = new Batch.CreateWithDurationsLT[](batchSize); + for (uint256 i = 0; i < batchSize; ++i) { + batch[i] = batchSingle; + } + } + + /// @notice Turns the `params` into an array of `Batch.CreateWithDurationsLT` structs. + function fillBatch( + LockupTranched.CreateWithDurations memory params, + uint256 batchSize + ) + internal + pure + returns (Batch.CreateWithDurationsLT[] memory batch) + { + batch = new Batch.CreateWithDurationsLT[](batchSize); + Batch.CreateWithDurationsLT memory batchSingle = Batch.CreateWithDurationsLT({ + sender: params.sender, + recipient: params.recipient, + totalAmount: params.totalAmount, + cancelable: params.cancelable, + transferable: params.transferable, + tranches: params.tranches, + broker: params.broker + }); + batch = fillBatch(batchSingle, batchSize); + } + /// @notice Generates an array containing `batchSize` copies of `batchSingle`. function fillBatch( Batch.CreateWithTimestampsLD memory batchSingle, @@ -154,4 +191,42 @@ library BatchBuilder { }); batch = fillBatch(batchSingle, batchSize); } + + /// @notice Generates an array containing `batchSize` copies of `batchSingle`. + function fillBatch( + Batch.CreateWithTimestampsLT memory batchSingle, + uint256 batchSize + ) + internal + pure + returns (Batch.CreateWithTimestampsLT[] memory batch) + { + batch = new Batch.CreateWithTimestampsLT[](batchSize); + for (uint256 i = 0; i < batchSize; ++i) { + batch[i] = batchSingle; + } + } + + /// @notice Turns the `params` into an array of `Batch.CreateWithTimestampsLT` structs. + function fillBatch( + LockupTranched.CreateWithTimestamps memory params, + uint256 batchSize + ) + internal + pure + returns (Batch.CreateWithTimestampsLT[] memory batch) + { + batch = new Batch.CreateWithTimestampsLT[](batchSize); + Batch.CreateWithTimestampsLT memory batchSingle = Batch.CreateWithTimestampsLT({ + sender: params.sender, + recipient: params.recipient, + totalAmount: params.totalAmount, + cancelable: params.cancelable, + transferable: params.transferable, + startTime: params.startTime, + tranches: params.tranches, + broker: params.broker + }); + batch = fillBatch(batchSingle, batchSize); + } } diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index 7e7aa8ee..46d128c7 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -214,10 +214,6 @@ contract Defaults is Merkle { }); } - function dynamicRange() public view returns (LockupDynamic.Range memory) { - return LockupDynamic.Range({ start: START_TIME, end: END_TIME }); - } - /// @dev Returns a batch of `LockupDynamic.Segment` parameters. function segments() private view returns (LockupDynamic.Segment[] memory segments_) { segments_ = new LockupDynamic.Segment[](2); @@ -304,6 +300,41 @@ contract Defaults is Merkle { SABLIER-V2-LOCKUP-TRANCHED //////////////////////////////////////////////////////////////////////////*/ + function createWithDurationsLT() public view returns (LockupTranched.CreateWithDurations memory) { + return createWithDurationsLT(asset); + } + + function createWithDurationsLT(IERC20 asset_) public view returns (LockupTranched.CreateWithDurations memory) { + return LockupTranched.CreateWithDurations({ + sender: users.alice, + recipient: users.recipient0, + totalAmount: PER_STREAM_AMOUNT, + asset: asset_, + cancelable: true, + transferable: true, + tranches: tranchesWithDurations(), + broker: broker() + }); + } + + function createWithTimestampsLT() public view returns (LockupTranched.CreateWithTimestamps memory) { + return createWithTimestampsLT(asset); + } + + function createWithTimestampsLT(IERC20 asset_) public view returns (LockupTranched.CreateWithTimestamps memory) { + return LockupTranched.CreateWithTimestamps({ + sender: users.alice, + recipient: users.recipient0, + totalAmount: PER_STREAM_AMOUNT, + asset: asset_, + cancelable: true, + transferable: true, + startTime: START_TIME, + tranches: tranches(), + broker: broker() + }); + } + function tranches() public view returns (LockupTranched.Tranche[] memory tranches_) { tranches_ = new LockupTranched.Tranche[](2); tranches_[0] = LockupTranched.Tranche({ amount: 2500e18, timestamp: uint40(block.timestamp) + CLIFF_DURATION }); @@ -327,6 +358,25 @@ contract Defaults is Merkle { } } + /// @dev Returns a batch of `LockupTranched.TrancheWithDuration` parameters. + function tranchesWithDurations() public pure returns (LockupTranched.TrancheWithDuration[] memory) { + return tranchesWithDurations({ amount0: 2500e18, amount1: 7500e18 }); + } + + /// @dev Returns a batch of `LockupTranched.TrancheWithDuration` parameters. + function tranchesWithDurations( + uint128 amount0, + uint128 amount1 + ) + public + pure + returns (LockupTranched.TrancheWithDuration[] memory segments_) + { + segments_ = new LockupTranched.TrancheWithDuration[](2); + segments_[0] = LockupTranched.TrancheWithDuration({ amount: amount0, duration: 2500 seconds }); + segments_[1] = LockupTranched.TrancheWithDuration({ amount: amount1, duration: 7500 seconds }); + } + /*////////////////////////////////////////////////////////////////////////// BATCH //////////////////////////////////////////////////////////////////////////*/ @@ -341,6 +391,11 @@ contract Defaults is Merkle { batch = BatchBuilder.fillBatch(createWithDurationsLL(), BATCH_SIZE); } + /// @dev Returns a default-size batch of `Batch.CreateWithDurationsLT` parameters. + function batchCreateWithDurationsLT() public view returns (Batch.CreateWithDurationsLT[] memory batch) { + batch = BatchBuilder.fillBatch(createWithDurationsLT(), BATCH_SIZE); + } + /// @dev Returns a default-size batch of `Batch.CreateWithTimestampsLD` parameters. function batchCreateWithTimestampsLD() public view returns (Batch.CreateWithTimestampsLD[] memory batch) { batch = batchCreateWithTimestampsLD(BATCH_SIZE); @@ -368,4 +423,18 @@ contract Defaults is Merkle { { batch = BatchBuilder.fillBatch(createWithTimestampsLL(), batchSize); } + + /// @dev Returns a default-size batch of `Batch.CreateWithTimestampsLT` parameters. + function batchCreateWithTimestampsLT() public view returns (Batch.CreateWithTimestampsLT[] memory batch) { + batch = batchCreateWithTimestampsLT(BATCH_SIZE); + } + + /// @dev Returns a batch of `Batch.CreateWithTimestampsLL` parameters. + function batchCreateWithTimestampsLT(uint256 batchSize) + public + view + returns (Batch.CreateWithTimestampsLT[] memory batch) + { + batch = BatchBuilder.fillBatch(createWithTimestampsLT(), batchSize); + } } From 6a86e7fa3bc427c1f6eadf717d479f6f1a2d7a0b Mon Sep 17 00:00:00 2001 From: andreivladbrg Date: Mon, 11 Mar 2024 17:05:07 +0200 Subject: [PATCH 23/61] build: bump core contracts refactor: remove protocol fee and comptroller test: update Precompiles bytecode --- bun.lockb | Bin 43329 -> 43329 bytes script/DeployProtocol.s.sol | 9 +++------ test/Base.t.sol | 3 --- test/fork/Fork.t.sol | 2 +- test/fork/batch/createWithTimestampsLD.t.sol | 1 - test/fork/batch/createWithTimestampsLT.t.sol | 1 - test/integration/Integration.t.sol | 2 +- test/utils/Defaults.sol | 1 - test/utils/Precompiles.sol | 4 ++-- 9 files changed, 7 insertions(+), 16 deletions(-) diff --git a/bun.lockb b/bun.lockb index 83a2c5301c10ff805d17191a0375988c6f554ea7..8041c00adb186e6f2b94a027882167130d362866 100755 GIT binary patch delta 84 zcmV-a0IUDO(gMNK0+25sxk#b)?dI@6RCdf7RxUiV6?T7>S4;t52z$oeodDpmPGWSE qtaKo=&~)$t1OWg50JFq)Q4t3-G-EenW;T;ih#ZrUs06b-hy@{9gCgnx delta 86 zcmV-c0IC1M(gMNK0+25sa6PL@reZ&ExrPjln>xQ&6AoSYTxZ<>+vG7aLD~YbPGWSE sv~(N+2(!&}^8p0^|NsB9#CA~;2Q*?cVl^=}lTnBqlaQzcvpk3eA-WkRBme*a diff --git a/script/DeployProtocol.s.sol b/script/DeployProtocol.s.sol index 270805f7..0e8741e4 100644 --- a/script/DeployProtocol.s.sol +++ b/script/DeployProtocol.s.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22 <0.9.0; -import { SablierV2Comptroller } from "@sablier/v2-core/src/SablierV2Comptroller.sol"; import { SablierV2LockupDynamic } from "@sablier/v2-core/src/SablierV2LockupDynamic.sol"; import { SablierV2LockupLinear } from "@sablier/v2-core/src/SablierV2LockupLinear.sol"; import { SablierV2LockupTranched } from "@sablier/v2-core/src/SablierV2LockupTranched.sol"; @@ -21,7 +20,6 @@ contract DeployProtocol is BaseScript { virtual broadcast returns ( - SablierV2Comptroller comptroller, SablierV2LockupDynamic lockupDynamic, SablierV2LockupLinear lockupLinear, SablierV2LockupTranched lockupTranched, @@ -31,11 +29,10 @@ contract DeployProtocol is BaseScript { ) { // Deploy V2 Core. - comptroller = new SablierV2Comptroller(initialAdmin); nftDescriptor = new SablierV2NFTDescriptor(); - lockupDynamic = new SablierV2LockupDynamic(initialAdmin, comptroller, nftDescriptor, maxCount); - lockupLinear = new SablierV2LockupLinear(initialAdmin, comptroller, nftDescriptor); - lockupTranched = new SablierV2LockupTranched(initialAdmin, comptroller, nftDescriptor, maxCount); + lockupDynamic = new SablierV2LockupDynamic(initialAdmin, nftDescriptor, maxCount); + lockupLinear = new SablierV2LockupLinear(initialAdmin, nftDescriptor); + lockupTranched = new SablierV2LockupTranched(initialAdmin, nftDescriptor, maxCount); batch = new SablierV2Batch(); merkleLockupFactory = new SablierV2MerkleLockupFactory(); diff --git a/test/Base.t.sol b/test/Base.t.sol index 21831039..8868f0dd 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; -import { ISablierV2Comptroller } from "@sablier/v2-core/src/interfaces/ISablierV2Comptroller.sol"; import { ISablierV2LockupDynamic } from "@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; @@ -44,7 +43,6 @@ abstract contract Base_Test is Assertions, DeployOptimized, Events, Merkle, V2Co IERC20 internal dai; ISablierV2Batch internal batch; - ISablierV2Comptroller internal comptroller; Defaults internal defaults; ISablierV2LockupDynamic internal lockupDynamic; ISablierV2LockupLinear internal lockupLinear; @@ -108,7 +106,6 @@ abstract contract Base_Test is Assertions, DeployOptimized, Events, Merkle, V2Co vm.label({ account: address(merkleLockupFactory), newLabel: "MerkleLockupFactory" }); vm.label({ account: address(merkleLockupLL), newLabel: "MerkleLockupLL" }); vm.label({ account: address(defaults), newLabel: "Defaults" }); - vm.label({ account: address(comptroller), newLabel: "Comptroller" }); vm.label({ account: address(lockupDynamic), newLabel: "LockupDynamic" }); vm.label({ account: address(lockupLinear), newLabel: "LockupLinear" }); vm.label({ account: address(lockupTranched), newLabel: "LockupTranched" }); diff --git a/test/fork/Fork.t.sol b/test/fork/Fork.t.sol index a106242a..69ecf8d2 100644 --- a/test/fork/Fork.t.sol +++ b/test/fork/Fork.t.sol @@ -88,6 +88,6 @@ abstract contract Fork_Test is Base_Test, V2CoreFuzzers { /// @dev Deploys the v2 core dependencies. // TODO: Remove this function once the v2 core contracts are deployed on Mainnet. function deployDependencies() private { - (, lockupDynamic, lockupLinear, lockupTranched,) = new V2CorePrecompiles().deployCore(users.admin); + (lockupDynamic, lockupLinear, lockupTranched,) = new V2CorePrecompiles().deployCore(users.admin); } } diff --git a/test/fork/batch/createWithTimestampsLD.t.sol b/test/fork/batch/createWithTimestampsLD.t.sol index ba9e1fbf..51adc556 100644 --- a/test/fork/batch/createWithTimestampsLD.t.sol +++ b/test/fork/batch/createWithTimestampsLD.t.sol @@ -39,7 +39,6 @@ abstract contract CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is Fork_Tes (params.perStreamAmount,) = fuzzDynamicStreamAmounts({ upperBound: MAX_UINT128 / params.batchSize, segments: params.segments, - protocolFee: defaults.PROTOCOL_FEE(), brokerFee: defaults.BROKER_FEE() }); diff --git a/test/fork/batch/createWithTimestampsLT.t.sol b/test/fork/batch/createWithTimestampsLT.t.sol index ec4417aa..a441fe45 100644 --- a/test/fork/batch/createWithTimestampsLT.t.sol +++ b/test/fork/batch/createWithTimestampsLT.t.sol @@ -39,7 +39,6 @@ abstract contract CreateWithTimestamps_LockupTranched_Batch_Fork_Test is Fork_Te (params.perStreamAmount,) = fuzzTranchedStreamAmounts({ upperBound: MAX_UINT128 / params.batchSize, tranches: params.tranches, - protocolFee: defaults.PROTOCOL_FEE(), brokerFee: defaults.BROKER_FEE() }); diff --git a/test/integration/Integration.t.sol b/test/integration/Integration.t.sol index 9d81ea0c..d26b4044 100644 --- a/test/integration/Integration.t.sol +++ b/test/integration/Integration.t.sol @@ -37,6 +37,6 @@ abstract contract Integration_Test is Base_Test { //////////////////////////////////////////////////////////////////////////*/ function deployDependencies() private { - (comptroller, lockupDynamic, lockupLinear, lockupTranched,) = new V2CorePrecompiles().deployCore(users.admin); + (lockupDynamic, lockupLinear, lockupTranched,) = new V2CorePrecompiles().deployCore(users.admin); } } diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index 46d128c7..2261efac 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -31,7 +31,6 @@ contract Defaults is Merkle { uint256 public constant ETHER_AMOUNT = 10_000 ether; uint256 public constant MAX_SEGMENT_COUNT = 1000; uint128 public constant PER_STREAM_AMOUNT = 10_000e18; - UD60x18 public constant PROTOCOL_FEE = UD60x18.wrap(0); uint128 public constant REFUND_AMOUNT = 7500e18; // deposit - cliff amount uint40 public immutable START_TIME; uint40 public constant TOTAL_DURATION = 10_000 seconds; diff --git a/test/utils/Precompiles.sol b/test/utils/Precompiles.sol index bb712314..0e5247b6 100644 --- a/test/utils/Precompiles.sol +++ b/test/utils/Precompiles.sol @@ -11,9 +11,9 @@ contract Precompiles { //////////////////////////////////////////////////////////////////////////*/ bytes public constant BYTECODE_BATCH = - hex"6080806040523461001657611936908161001c8239f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c90816349a32c4014610c8657508063606ef875146108d2578063a514f83e146104a85763f7ca34eb1461004b57600080fd5b346103ab576100593661113e565b919092821561047e576000805b8482106104505761008f91508373ffffffffffffffffffffffffffffffffffffffff8416611580565b61009883611300565b9260005b8181106100b557604051806100b18782611102565b0390f35b6100c86100c3828489611540565b61134f565b906100df60206100d983868b611540565b0161134f565b876100f660406100f0858885611540565b01611219565b9361010d6060610107868986611540565b01611370565b9460a061012b8689610125608061010784848b611540565b96611540565b013564ffffffffff811681036103ab578b91889160e06101658961015d61015382888a611540565b60c0810190611429565b969097611540565b0195604051998a61012081011067ffffffffffffffff6101208d0111176104215773ffffffffffffffffffffffffffffffffffffffff8e8c60406fffffffffffffffffffffffffffffffff64ffffffffff98856102029f816101f69f61012088018752168652166020850152169101521660608c0152151560808b0152151560a08a01521660c0880152369161147d565b60e0850152369061138f565b610100830152604051917f31df3d480000000000000000000000000000000000000000000000000000000083526020600484015273ffffffffffffffffffffffffffffffffffffffff815116602484015273ffffffffffffffffffffffffffffffffffffffff60208201511660448401526fffffffffffffffffffffffffffffffff604082015116606484015273ffffffffffffffffffffffffffffffffffffffff60608201511660848401526080810151151560a484015260a0810151151560c484015264ffffffffff60c08201511660e48401528260e0820151610140610104830152805180610164840152602061018484019201906000905b8082106103c457505050819061034061010060209501516101248401906020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b0381600073ffffffffffffffffffffffffffffffffffffffff89165af180156103b857600090610380575b6001925061037982886113d5565b520161009c565b506020823d6020116103b0575b8161039a602093836112a7565b810103126103ab576001915161036b565b600080fd5b3d915061038d565b6040513d6000823e3d90fd5b91949350916020606082610412600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b019501920186939492916102fe565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6001906fffffffffffffffffffffffffffffffff61047460406100f0868a8c611540565b1601910190610066565b60046040517f763e559d000000000000000000000000000000000000000000000000000000008152fd5b346103ab5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103ab576104df6110bc565b6104e76110df565b9060443567ffffffffffffffff8082116103ab57366023830112156103ab578160040135116103ab57366024610140836004013502830101116103ab5780600401351561047e576000805b8260040135821061089d5761055f91508473ffffffffffffffffffffffffffffffffffffffff8516611580565b61056c8160040135611300565b9160005b8260040135811061058957604051806100b18682611102565b61059e6100c38285600401356024870161152f565b90836105b760206100d98484600401356024860161152f565b6105ce60406100f08585600401356024870161152f565b936105e660606101078686600401356024880161152f565b946080956fffffffffffffffffffffffffffffffff6106118861010789896004013560248b0161152f565b9273ffffffffffffffffffffffffffffffffffffffff61010061064f8a6106408160048d013560248e0161152f565b9a60248160040135910161152f565b0196816040519761065f89611236565b16875216602086015216604084015273ffffffffffffffffffffffffffffffffffffffff8b166060840152151586830152151560a082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6084360301126103ab576108259261071460e0936040516106d98161126f565b6106e560a0850161137d565b81526107048660c0956106f987820161137d565b60208501520161137d565b604082015283850152369061138f565b83830152604051957f53b1572700000000000000000000000000000000000000000000000000000000875273ffffffffffffffffffffffffffffffffffffffff835116600488015273ffffffffffffffffffffffffffffffffffffffff60208401511660248801526fffffffffffffffffffffffffffffffff604084015116604488015273ffffffffffffffffffffffffffffffffffffffff60608401511660648801528201511515608487015260a0820151151560a4870152810151604064ffffffffff918281511660c48901528260208201511660e489015201511661010486015201516101248401906020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b60208261016481600073ffffffffffffffffffffffffffffffffffffffff88165af180156103b85760009061086a575b6001925061086382876113d5565b5201610570565b506020823d602011610895575b81610884602093836112a7565b810103126103ab5760019151610855565b3d9150610877565b6001906fffffffffffffffffffffffffffffffff6108c860406100f086886004013560248a0161152f565b1601910190610532565b346103ab576108e03661113e565b92831561047e5760009060005b858110610c58575073ffffffffffffffffffffffffffffffffffffffff6109179116918483611580565b61092084611300565b9260005b85811061093957604051806100b18782611102565b6109448187866113e9565b61094d9061134f565b906109598188876113e9565b6020016109659061134f565b878261097281838a6113e9565b60400161097e90611219565b61098982848b6113e9565b60600161099590611370565b6109a083858c6113e9565b6080016109ac90611370565b916109b884868d6113e9565b60a081016109c591611429565b946109d191968d6113e9565b60c00195604051986109e28a611236565b73ffffffffffffffffffffffffffffffffffffffff16895273ffffffffffffffffffffffffffffffffffffffff1660208901526fffffffffffffffffffffffffffffffff16604088015273ffffffffffffffffffffffffffffffffffffffff8816606088015215156080870152151560a08601523690610a619261147d565b60c084015236610a709161138f565b60e0830152604051917f54c022920000000000000000000000000000000000000000000000000000000083526004830160209052826101448101825173ffffffffffffffffffffffffffffffffffffffff166024830152602083015173ffffffffffffffffffffffffffffffffffffffff16604483015260408301516fffffffffffffffffffffffffffffffff166064830152606083015173ffffffffffffffffffffffffffffffffffffffff1660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001906000905b808210610bfb575050508190610b9e60e060209501516101048401906020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b03816000885af180156103b857600090610bc8575b60019250610bc182886113d5565b5201610924565b506020823d602011610bf3575b81610be2602093836112a7565b810103126103ab5760019151610bb3565b3d9150610bd5565b91949350916020606082610c49600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b01950192018693949291610b5d565b916001906fffffffffffffffffffffffffffffffff610c7d60406100f0878b8a6113e9565b160192016108ed565b346103ab5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103ab57610cbd6110bc565b610cc56110df565b916044359067ffffffffffffffff8083116103ab57366023840112156103ab578260040135116103ab57366024610120846004013502840101116103ab5781600401351561109457506000805b8260040135821061105f57610d3f91508473ffffffffffffffffffffffffffffffffffffffff8516611580565b610d4c8160040135611300565b9160005b82600401358110610d6957604051806100b18682611102565b610d7e6100c3828560040135602487016111d9565b9083610d9760206100d9848460040135602486016111d9565b610dae60406100f0858560040135602487016111d9565b93610dc66060610107868660040135602488016111d9565b946080956fffffffffffffffffffffffffffffffff610df18861010789896004013560248b016111d9565b9273ffffffffffffffffffffffffffffffffffffffff60e0610e2e8a610e1f8160048d013560248e016111d9565b9a6024816004013591016111d9565b01968160405197610e3e89611236565b16875216602086015216604084015273ffffffffffffffffffffffffffffffffffffffff8b166060840152151586830152151560a082015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6084360301126103ab57610fe792610ee360e093604051610eb881611253565b610ec460a0850161137d565b8152610ed360c080950161137d565b602082015283850152369061138f565b83830152604051957fab167ccc00000000000000000000000000000000000000000000000000000000875273ffffffffffffffffffffffffffffffffffffffff835116600488015273ffffffffffffffffffffffffffffffffffffffff60208401511660248801526fffffffffffffffffffffffffffffffff604084015116604488015273ffffffffffffffffffffffffffffffffffffffff60608401511660648801528201511515608487015260a0820151151560a4870152810151602064ffffffffff918281511660c489015201511660e486015201516101048401906020809173ffffffffffffffffffffffffffffffffffffffff81511684520151910152565b60208261014481600073ffffffffffffffffffffffffffffffffffffffff88165af180156103b85760009061102c575b6001925061102582876113d5565b5201610d50565b506020823d602011611057575b81611046602093836112a7565b810103126103ab5760019151611017565b3d9150611039565b6001906fffffffffffffffffffffffffffffffff61108a60406100f086886004013560248a016111d9565b1601910190610d12565b807f763e559d0000000000000000000000000000000000000000000000000000000060049252fd5b6004359073ffffffffffffffffffffffffffffffffffffffff821682036103ab57565b6024359073ffffffffffffffffffffffffffffffffffffffff821682036103ab57565b602090602060408183019282815285518094520193019160005b82811061112a575050505090565b83518552938101939281019260010161111c565b9060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc8301126103ab5773ffffffffffffffffffffffffffffffffffffffff9160043583811681036103ab579260243590811681036103ab579160443567ffffffffffffffff928382116103ab57806023830112156103ab5781600401359384116103ab5760248460051b830101116103ab576024019190565b91908110156111ea57610120020190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b356fffffffffffffffffffffffffffffffff811681036103ab5790565b610100810190811067ffffffffffffffff82111761042157604052565b6040810190811067ffffffffffffffff82111761042157604052565b6060810190811067ffffffffffffffff82111761042157604052565b6080810190811067ffffffffffffffff82111761042157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761042157604052565b67ffffffffffffffff81116104215760051b60200190565b9061130a826112e8565b61131760405191826112a7565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061134582946112e8565b0190602036910137565b3573ffffffffffffffffffffffffffffffffffffffff811681036103ab5790565b3580151581036103ab5790565b359064ffffffffff821682036103ab57565b91908260409103126103ab576040516113a781611253565b8092803573ffffffffffffffffffffffffffffffffffffffff811681036103ab578252602090810135910152565b80518210156111ea5760209160051b010190565b91908110156111ea5760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01813603018212156103ab570190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe1813603018212156103ab570180359067ffffffffffffffff82116103ab576020019160608202360383136103ab57565b929192611489826112e8565b60409461149960405192836112a7565b819584835260208093019160608096028501948186116103ab57925b8584106114c55750505050505050565b86848303126103ab578251906114da8261126f565b84356fffffffffffffffffffffffffffffffff811681036103ab578252858501359067ffffffffffffffff821682036103ab57828792838b95015261152086880161137d565b868201528152019301926114b5565b91908110156111ea57610140020190565b91908110156111ea5760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee1813603018212156103ab570190565b90604080516020907f23b872dd000000000000000000000000000000000000000000000000000000008282015233602482015260449030828201528660648201526064815260a081019080821067ffffffffffffffff831117610421576115e991855285611792565b73ffffffffffffffffffffffffffffffffffffffff94858516958451917fdd62ed3e0000000000000000000000000000000000000000000000000000000083523060048401521690816024820152838184818a5afa90811561176f5790889160009161173e575b5010611660575b50505050505050565b8351956000808589017f095ea7b3000000000000000000000000000000000000000000000000000000009a8b82528560248c0152868b0152858a526116a48a61128b565b89519082855af1906116b461182b565b8261170b575b5081611700575b50611657576116f4966116ef945193840152602483015260008183015281526116e98161128b565b82611792565b611792565b38808080808080611657565b90503b1515386116c1565b809192505190858215928315611726575b50505090386116ba565b611736935082018101910161177a565b38858161171c565b809250858092503d8311611768575b61175781836112a7565b810103126103ab5787905138611650565b503d61174d565b85513d6000823e3d90fd5b908160209103126103ab575180151581036103ab5790565b60008073ffffffffffffffffffffffffffffffffffffffff6117c993169360208151910182865af16117c261182b565b9083611889565b8051908115159182611810575b50506117df5750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b611823925060208091830101910161177a565b1538806117d6565b3d15611884573d9067ffffffffffffffff8211610421576040519161187860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846112a7565b82523d6000602084013e565b606090565b906118c8575080511561189e57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580611920575b6118d9575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b156118d156fea164736f6c6343000817000a"; + hex"6080806040523461001657611f10908161001c8239f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c90816337266dd31461125b5750806349a32c4014610eb7578063606ef87514610b6b5780639e743f29146107fc578063a514f83e146104295763f7ca34eb1461006157600080fd5b346103555761006f366115d6565b9283156103ff5760009060005b8581106103cb57506001600160a01b036100999116918483611b81565b6100a284611858565b9260005b8581106100bf57604051806100bb8782611664565b0390f35b6100ca818786611b1e565b6100d3906118a7565b906100df818887611b1e565b6020016100eb906118a7565b85886100f8848284611b1e565b60400161010490611725565b61010f858385611b1e565b60600161011b906118bb565b610126868486611b1e565b608001610132906118bb565b9061013e878587611b1e565b60a00161014a90611b5e565b9287610157818789611b1e565b60c0810161016491611a2a565b96610170929198611b1e565b60e00196604051996101818b6117aa565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c086015236906101e592611a7e565b60e0840152366101f4916119cc565b610100830152604051917f31df3d48000000000000000000000000000000000000000000000000000000008352600483016020905280516001600160a01b0316602484015260208101516001600160a01b0316604484015260408101516fffffffffffffffffffffffffffffffff16606484015260608101516001600160a01b031660848401526080810151151560a484015260a0810151151560c484015260c081015164ffffffffff1660e48401528260e082015161010482016101409052805180610164840152610184830191602001906000905b80821061036e5750505081906103006101006020950151610124840190602080916001600160a01b0381511684520151910152565b03816000885af180156103625760009061032a575b600192506103238288611a05565b52016100a6565b506020823d60201161035a575b81610344602093836117ff565b810103126103555760019151610315565b600080fd5b3d9150610337565b6040513d6000823e3d90fd5b919493509160206060826103bc600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b019501920186939492916102cb565b916001906fffffffffffffffffffffffffffffffff6103f660406103f0878b8a611b1e565b01611725565b1601920161007c565b60046040517f763e559d000000000000000000000000000000000000000000000000000000008152fd5b346103555760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610355576104606116a0565b6104686115c0565b9060443567ffffffffffffffff8082116103555736602383011215610355578160040135116103555736602461014083600401350283010111610355578060040135156103ff576000805b826004013582106107c7576104d39150846001600160a01b038516611b81565b6104e08160040135611858565b9160005b826004013581106104fd57604051806100bb8682611664565b61051761051282856004013560248701611b70565b6118a7565b9083610536602061053084846004013560248601611b70565b016118a7565b61054d60406103f085856004013560248701611b70565b9361056b606061056586866004013560248801611b70565b016118bb565b946080956fffffffffffffffffffffffffffffffff6105968861056589896004013560248b01611b70565b926001600160a01b036101006105c78a6105b88160048d013560248e01611b70565b9a602481600401359101611b70565b019681604051976105d789611742565b1687521660208601521660408401526001600160a01b038b166060840152151586830152151560a082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6084360301126103555761075c9261067f60e093604051610644816117c7565b61065060a08501611939565b815261066f8660c095610664878201611939565b602085015201611939565b60408201528385015236906119cc565b83830152604051957f53b157270000000000000000000000000000000000000000000000000000000087526001600160a01b0383511660048801526001600160a01b0360208401511660248801526fffffffffffffffffffffffffffffffff60408401511660448801526001600160a01b0360608401511660648801528201511515608487015260a0820151151560a4870152810151604064ffffffffff918281511660c48901528260208201511660e48901520151166101048601520151610124840190602080916001600160a01b0381511684520151910152565b6020826101648160006001600160a01b0388165af1801561036257600090610794575b6001925061078d8287611a05565b52016104e4565b506020823d6020116107bf575b816107ae602093836117ff565b81010312610355576001915161077f565b3d91506107a1565b6001906fffffffffffffffffffffffffffffffff6107f260406103f086886004013560248a01611b70565b16019101906104b3565b346103555761080a366115d6565b9283156103ff5760009060005b858110610b3d57506001600160a01b036108349116918483611b81565b61083d84611858565b9260005b85811061085657604051806100bb8782611664565b610861818786611b1e565b61086a906118a7565b90610876818887611b1e565b602001610882906118a7565b858861088f848284611b1e565b60400161089b90611725565b6108a6858385611b1e565b6060016108b2906118bb565b6108bd868486611b1e565b6080016108c9906118bb565b906108d5878587611b1e565b60a0016108e190611b5e565b92876108ee818789611b1e565b60c081016108fb916118c8565b96610907929198611b1e565b60e00196604051996109188b6117aa565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c0860152369061097c9261194b565b60e08401523661098b916119cc565b610100830152604051917f32fbe22b000000000000000000000000000000000000000000000000000000008352600483016020905280516001600160a01b0316602484015260208101516001600160a01b0316604484015260408101516fffffffffffffffffffffffffffffffff16606484015260608101516001600160a01b031660848401526080810151151560a484015260a0810151151560c484015260c081015164ffffffffff1660e48401528260e082015161010482016101409052805180610164840152610184830191602001906000905b808210610af4575050508190610a976101006020950151610124840190602080916001600160a01b0381511684520151910152565b03816000885af1801561036257600090610ac1575b60019250610aba8288611a05565b5201610841565b506020823d602011610aec575b81610adb602093836117ff565b810103126103555760019151610aac565b3d9150610ace565b91949350916020604082610b2e600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291610a62565b916001906fffffffffffffffffffffffffffffffff610b6260406103f0878b8a611b1e565b16019201610817565b3461035557610b79366115d6565b9283156103ff5760009060005b858110610e8957506001600160a01b03610ba39116918483611b81565b610bac84611858565b9260005b858110610bc557604051806100bb8782611664565b610bd08187866116b6565b610bd9906118a7565b90610be58188876116b6565b602001610bf1906118a7565b8782610bfe81838a6116b6565b604001610c0a90611725565b610c1582848b6116b6565b606001610c21906118bb565b610c2c83858c6116b6565b608001610c38906118bb565b91610c4484868d6116b6565b60a08101610c5191611a2a565b94610c5d91968d6116b6565b60c0019560405198610c6e8a611742565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a08601523690610cc692611a7e565b60c084015236610cd5916119cc565b60e0830152604051917f54c02292000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001906000905b808210610e2c575050508190610dcf60e06020950151610104840190602080916001600160a01b0381511684520151910152565b03816000885af1801561036257600090610df9575b60019250610df28288611a05565b5201610bb0565b506020823d602011610e24575b81610e13602093836117ff565b810103126103555760019151610de4565b3d9150610e06565b91949350916020606082610e7a600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b01950192018693949291610d9b565b916001906fffffffffffffffffffffffffffffffff610eae60406103f0878b8a6116b6565b16019201610b86565b346103555760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035557610eee6116a0565b610ef66115c0565b9060443567ffffffffffffffff8082116103555736602383011215610355578160040135116103555736602461012083600401350283010111610355578060040135156103ff576000805b8260040135821061122657610f619150846001600160a01b038516611b81565b610f6e8160040135611858565b9160005b82600401358110610f8b57604051806100bb8682611664565b610fa061051282856004013560248701611a19565b9083610fb9602061053084846004013560248601611a19565b610fd060406103f085856004013560248701611a19565b93610fe8606061056586866004013560248801611a19565b946080956fffffffffffffffffffffffffffffffff6110138861056589896004013560248b01611a19565b926001600160a01b0360e06110438a6110348160048d013560248e01611a19565b9a602481600401359101611a19565b0196816040519761105389611742565b1687521660208601521660408401526001600160a01b038b166060840152151586830152151560a082015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff608436030112610355576111bb926110eb60e0936040516110c08161178e565b6110cc60a08501611939565b81526110db60c0809501611939565b60208201528385015236906119cc565b83830152604051957fab167ccc0000000000000000000000000000000000000000000000000000000087526001600160a01b0383511660048801526001600160a01b0360208401511660248801526fffffffffffffffffffffffffffffffff60408401511660448801526001600160a01b0360608401511660648801528201511515608487015260a0820151151560a4870152810151602064ffffffffff918281511660c489015201511660e48601520151610104840190602080916001600160a01b0381511684520151910152565b6020826101448160006001600160a01b0388165af18015610362576000906111f3575b600192506111ec8287611a05565b5201610f72565b506020823d60201161121e575b8161120d602093836117ff565b8101031261035557600191516111de565b3d9150611200565b6001906fffffffffffffffffffffffffffffffff61125160406103f086886004013560248a01611a19565b1601910190610f41565b3461035557611269366115d6565b93849391929315611598575060009060005b85811061156a57506001600160a01b036112989116918483611b81565b6112a184611858565b9260005b8581106112ba57604051806100bb8782611664565b6112c58187866116b6565b6112ce906118a7565b906112da8188876116b6565b6020016112e6906118a7565b87826112f381838a6116b6565b6040016112ff90611725565b61130a82848b6116b6565b606001611316906118bb565b61132183858c6116b6565b60800161132d906118bb565b9161133984868d6116b6565b60a08101611346916118c8565b9461135291968d6116b6565b60c00195604051986113638a611742565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a086015236906113bb9261194b565b60c0840152366113ca916119cc565b60e0830152604051917f897f362b000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001906000905b8082106115215750505081906114c460e06020950151610104840190602080916001600160a01b0381511684520151910152565b03816000885af18015610362576000906114ee575b600192506114e78288611a05565b52016112a5565b506020823d602011611519575b81611508602093836117ff565b8101031261035557600191516114d9565b3d91506114fb565b9194935091602060408261155b600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291611490565b916001906fffffffffffffffffffffffffffffffff61158f60406103f0878b8a6116b6565b1601920161127b565b807f763e559d0000000000000000000000000000000000000000000000000000000060049252fd5b602435906001600160a01b038216820361035557565b9060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc830112610355576001600160a01b0391600435838116810361035557926024359081168103610355579160443567ffffffffffffffff9283821161035557806023830112156103555781600401359384116103555760248460051b83010111610355576024019190565b602090602060408183019282815285518094520193019160005b82811061168c575050505090565b83518552938101939281019260010161167e565b600435906001600160a01b038216820361035557565b91908110156116f65760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0181360301821215610355570190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b356fffffffffffffffffffffffffffffffff811681036103555790565b610100810190811067ffffffffffffffff82111761175f57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff82111761175f57604052565b610120810190811067ffffffffffffffff82111761175f57604052565b6060810190811067ffffffffffffffff82111761175f57604052565b6080810190811067ffffffffffffffff82111761175f57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761175f57604052565b67ffffffffffffffff811161175f5760051b60200190565b9061186282611840565b61186f60405191826117ff565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061189d8294611840565b0190602036910137565b356001600160a01b03811681036103555790565b3580151581036103555790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610355570180359067ffffffffffffffff821161035557602001918160061b3603831361035557565b35906fffffffffffffffffffffffffffffffff8216820361035557565b359064ffffffffff8216820361035557565b92919261195782611840565b60409261196760405192836117ff565b819581835260208093019160061b84019381851161035557915b84831061199057505050505050565b85838303126103555783869182516119a78161178e565b6119b08661191c565b81526119bd838701611939565b83820152815201920191611981565b9190826040910312610355576040516119e48161178e565b809280356001600160a01b0381168103610355578252602090810135910152565b80518210156116f65760209160051b010190565b91908110156116f657610120020190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610355570180359067ffffffffffffffff82116103555760200191606082023603831361035557565b929192611a8a82611840565b604094611a9a60405192836117ff565b8195848352602080930191606080960285019481861161035557925b858410611ac65750505050505050565b868483031261035557825190611adb826117c7565b611ae48561191c565b8252858501359067ffffffffffffffff8216820361035557828792838b950152611b0f868801611939565b86820152815201930192611ab6565b91908110156116f65760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee181360301821215610355570190565b3564ffffffffff811681036103555790565b91908110156116f657610140020190565b90604080516020907f23b872dd000000000000000000000000000000000000000000000000000000008282015233602482015260449030828201528660648201526064815260a081019080821067ffffffffffffffff83111761175f57611bea91855285611d86565b6001600160a01b0394858516958451917fdd62ed3e0000000000000000000000000000000000000000000000000000000083523060048401521690816024820152838184818a5afa908115611d6357908891600091611d32575b5010611c54575b50505050505050565b8351956000808589017f095ea7b3000000000000000000000000000000000000000000000000000000009a8b82528560248c0152868b0152858a52611c988a6117e3565b89519082855af190611ca8611e12565b82611cff575b5081611cf4575b50611c4b57611ce896611ce394519384015260248301526000818301528152611cdd816117e3565b82611d86565b611d86565b38808080808080611c4b565b90503b151538611cb5565b809192505190858215928315611d1a575b5050509038611cae565b611d2a9350820181019101611d6e565b388581611d10565b809250858092503d8311611d5c575b611d4b81836117ff565b810103126103555787905138611c44565b503d611d41565b85513d6000823e3d90fd5b90816020910312610355575180151581036103555790565b6000806001600160a01b03611db093169360208151910182865af1611da9611e12565b9083611e70565b8051908115159182611df7575b5050611dc65750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b611e0a9250602080918301019101611d6e565b153880611dbd565b3d15611e6b573d9067ffffffffffffffff821161175f5760405191611e5f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846117ff565b82523d6000602084013e565b606090565b90611eaf5750805115611e8557805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580611efa575b611ec0575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15611eb856fea164736f6c6343000817000a"; bytes public constant BYTECODE_MERKLE_LOCKUP_FACTORY = - hex"60808060405234610016576144fb908161001c8239f35b600080fdfe608060405260043610156200001357600080fd5b60003560e01c806324496a1514620003fc576360d18fed146200003557600080fd5b34620003f75760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112620003f75760043567ffffffffffffffff8111620003f757620000899036906004016200097d565b73ffffffffffffffffffffffffffffffffffffffff602435168060243503620003f75760407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc360112620003f757604051620000e581620008bf565b64ffffffffff926044358481168103620003f75782526064359384168403620003f75760209384830152805184820151620001386200014760408501516040519283918a80840152604083019062000a91565b03601f198101835282620008dc565b6200018260608501516200017c89604051836200016e829551809285808601910162000a6c565b8101038084520182620008dc565b62000ab8565b60808501519260a086015160c0870151151560e088015115159160405196620001c18d89018c6020908164ffffffffff91828151168552015116910152565b6040885287606081011067ffffffffffffffff60608a011117620003c8578c889760608901604052608089019a7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009788809260601b168d5260601b1660948a01528051809260a88b01920191620002389262000a6c565b87019560a887015260c886015260d81b7fffffffffff0000000000000000000000000000000000000000000000000000001660e885015260f81b60ed84015260f81b60ee83015260243560601b1660ef8201528151906101038101828a850191620002a39262000a6c565b010360838101606083015260a3016060820190620002c191620008dc565b60600151902060405161180c80820182811067ffffffffffffffff821117620003c857829162000c8a83396080815262000326604062000305608084018862000b4c565b92898b82015201876020908164ffffffffff91828151168552015116910152565b03906000f5928315620003bc5760a07f3ce4d6bb8b94ee36e2248c8057e53b793a45b37e4d58038ec3b5dc21c56e925491620003a373ffffffffffffffffffffffffffffffffffffffff6200037f971696879562000bdb565b95604051928352888301906020908164ffffffffff91828151168552015116910152565b608435606082015260a4356080820152a3604051908152f35b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b34620003f75760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112620003f75760043567ffffffffffffffff8111620003f757620004509036906004016200097d565b73ffffffffffffffffffffffffffffffffffffffff6024351660243503620003f75760443567ffffffffffffffff8111620003f75736602382011215620003f757806004013567ffffffffffffffff8111620003c85760059060405192620004bf60208360051b0185620008dc565b8184526024602085019260061b82010190368211620003f757602401915b8183106200086b5750505060009082516000915b818310620007d657505050670de0b6b3a76400008103620007a55750620005426200068b60a38451620005d16020870151620005516040890151604051978891602080840152604083019062000a91565b03601f198101885287620008dc565b6200057960608901516200017c6020604051836200016e829551809285808601910162000a6c565b90608089015160a08a01517fffffffffff00000000000000000000000000000000000000000000000000000060c08c015115159160e08d0151151593620005e08d604051998a91602080840152604083019062000afa565b03601f1981018a5289620008dc565b604862000634604051809e819c7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009a8b809260601b16602085015260601b1660348301526020815194859301910162000a6c565b8901966048880152606887015260d81b16608885015260f81b608d84015260f81b608e83015260243560601b16608f8201526200067b825180936020878501910162000a6c565b01036083810184520182620008dc565b602081519101206040516120598082019082821067ffffffffffffffff831117620003c8578291620006fe9162002496843960608152620006d0606082018862000b4c565b9073ffffffffffffffffffffffffffffffffffffffff60243516602082015260408183039101528562000afa565b03906000f5908115620003bc576020927f431b4603d07471c22cc6557755bfeb55c0f0c1163d5df05ee2b54701ab6674ab6200078a73ffffffffffffffffffffffffffffffffffffffff62000757951694859362000bdb565b9360405191829173ffffffffffffffffffffffffffffffffffffffff602435168352608089840152608083019062000afa565b606435604083015260843560608301520390a3604051908152f35b602490604051907f5484abf20000000000000000000000000000000000000000000000000000000082526004820152fd5b90919284518410156200083c5767ffffffffffffffff602085841b87010151511681018091116200080d57926001019190620004f1565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b604083360312620003f757604051906200088582620008bf565b83359067ffffffffffffffff82168203620003f7578260209260409452620008af8387016200095c565b83820152815201920191620004dd565b6040810190811067ffffffffffffffff821117620003c857604052565b90601f601f19910116810190811067ffffffffffffffff821117620003c857604052565b81601f82011215620003f75780359067ffffffffffffffff8211620003c85760405192620009396020601f19601f8601160185620008dc565b82845260208383010111620003f757816000926020809301838601378301015290565b359064ffffffffff82168203620003f757565b35908115158203620003f757565b919091610100908181850312620003f75760405191820167ffffffffffffffff9083811082821117620003c8576040528294823573ffffffffffffffffffffffffffffffffffffffff908181168103620003f757855260208401359081168103620003f75760208501526040830135828111620003f7578162000a0291850162000900565b60408501526060830135918211620003f7578262000a2b60e0949262000a679486940162000900565b60608601526080810135608086015262000a4860a082016200095c565b60a086015262000a5b60c082016200096f565b60c0860152016200096f565b910152565b60005b83811062000a805750506000910152565b818101518382015260200162000a6f565b90601f19601f60209362000ab18151809281875287808801910162000a6c565b0116010190565b60208151910151906020811062000acd575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b90815180825260208080930193019160005b82811062000b1b575050505090565b8351805167ffffffffffffffff16865282015164ffffffffff16858301526040909401939281019260010162000b0c565b9060e08062000ba962000b9661010073ffffffffffffffffffffffffffffffffffffffff808851168752602088015116602087015260408701519080604088015286019062000a91565b6060860151858203606087015262000a91565b936080810151608085015264ffffffffff60a08201511660a085015260c0810151151560c08501520151151591015290565b60c0604051809273ffffffffffffffffffffffffffffffffffffffff808251168352602082015116602083015260e0601f604083015162000c2781518092602060408901910162000a6c565b601f199182910116840190601f606085015162000c4f81518092602060408801910162000a6c565b011601916080810151604084015264ffffffffff60a082015116606084015284810151151560808401520151151560a0820152030190209056fe6101608060405234620005b4576200180c8038038091620000218285620005d5565b83398101908082039160808312620005b45781516001600160401b038111620005b45782019261010084830312620005b4576040519361010085016001600160401b038111868210176200059e5760405280516001600160a01b0381168103620005b45785526020810151926001600160a01b0384168403620005b4576020860193845260408201516001600160401b038111620005b45781620000c79184016200063a565b60408701908152606083015190916001600160401b038211620005b457620000f19184016200063a565b6060870152608082015195608081019687526200011160a0840162000687565b9660a082019788526200013c60e06200012d60c087016200069a565b9560c08501968752016200069a565b60e083019081526020880151959093906001600160a01b0387168703620005b457604090603f190112620005b45760408051989089016001600160401b0381118a8210176200059e57620001a7916060916040526200019e6040820162000687565b8b520162000687565b9460208901958652606084015151602081116200057f57508351600080546001600160a01b0319166001600160a01b0392831617815598511660805251151560a052975164ffffffffff90811660c052975180516001600160401b0381116200056b5760019182548381811c9116801562000560575b60208210146200054c57601f8111620004ff575b50602090601f83116001146200049357606095949392918a918362000487575b5050600019600383901b1c191690821b1790555b5160e052015160405162000299602082816200028b818301968781519384920162000615565b8101038084520182620005d5565b519051906020811062000474575b50610100525115159461012095865261014094838652511669ffffffffff0000000000600354925160281b169160018060501b031916171760035560018060a01b03608051166040519160208301848063095ea7b360e01b9283815260018060a01b03851660248801526000196044880152604487526200032887620005b9565b86519082875af162000339620006a8565b8162000433575b508062000428575b15620003dc575b60405161104290878983620007ca8439608051838181610470015281816107440152610c36015260a05183818161076b0152610b5c015260c05183818161015d01528181610aa801528181610d8f0152610f60015260e0518381816102e2015261066201526101005183610e250152518281816107930152610b1f0152518181816101ae01526109060152f35b6200041d94620004179260405192602084015260018060a01b031660248301526044820152604481526200041081620005b9565b82620006dd565b620006dd565b38808080806200034f565b50823b151562000348565b80518015925082156200044a575b50503862000340565b8192509060209181010312620004705760206200046891016200069a565b388062000441565b8580fd5b6000199060200360031b1b1638620002a7565b01519050388062000251565b838a5260208a209190601f1984168b5b818110620004e857509185949291836060999897959310620004ce575b505050811b01905562000265565b015160001960f88460031b161c19169055388080620004c0565b8284015185559386019360209384019301620004a3565b838a5260208a20601f840160051c8101916020851062000541575b601f0160051c019084905b8281106200053557505062000231565b8b815501849062000525565b90915081906200051a565b634e487b7160e01b8a52602260045260248afd5b90607f16906200021d565b634e487b7160e01b88526041600452602488fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b634e487b7160e01b600052604160045260246000fd5b600080fd5b608081019081106001600160401b038211176200059e57604052565b601f909101601f19168101906001600160401b038211908210176200059e57604052565b6001600160401b0381116200059e57601f01601f191660200190565b60005b838110620006295750506000910152565b818101518382015260200162000618565b81601f82011215620005b45780516200065381620005f9565b92620006636040519485620005d5565b81845260208284010111620005b45762000684916020808501910162000615565b90565b519064ffffffffff82168203620005b457565b51908115158203620005b457565b3d15620006d8573d90620006bc82620005f9565b91620006cc6040519384620005d5565b82523d6000602084013e565b606090565b6000806200070a9260018060a01b03169360208151910182865af162000702620006a8565b908362000761565b80519081151591826200073a575b5050620007225750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620005b45760206200075891016200069a565b15388062000718565b906200078a57508051156200077857805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580620007bf575b6200079d575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156200079456fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde0314610e0e575080631686c90914610b8157806316c3549d14610b445780631bfd681414610b075780633bfe03a814610ad85780633f31ae3f146104945780634800d97f1461044357806349fc73dd1461030557806351e75e8b146102ca57806375829def146101ed57806390e64d13146101d25780639e93e57714610181578063bb4b57341461013f578063ce516507146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760206003193601126100f857602061013560043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f8576020610135610f58565b346100f85760206003193601126100f857610206610ebc565b60005473ffffffffffffffffffffffffffffffffffffffff8082169233840361027d577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c9060018316928315610439575b602093848410811461040a578386529081156103cc5750600114610371575b61036d8461036181880382610f17565b60405191829182610e56565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8284106103b9575050508161036d936103619282010193610351565b805485850187015292850192810161039d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103618161036d610351565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691610332565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f857604435906fffffffffffffffffffffffffffffffff821682036100f85767ffffffffffffffff606435116100f8573660236064350112156100f85767ffffffffffffffff60643560040135116100f8573660246064356004013560051b6064350101116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff831660408201526fffffffffffffffffffffffffffffffff841660608201526060815261057a81610efb565b519020604051602081019182526020815261059481610edf565b519020916105a0610f58565b610a7a576105c960043560ff6001918060081c6000526002602052161b60406000205416151590565b610a4857604051926105e760206064356004013560051b0185610f17565b60643560048101358552602401602085015b60246064356004013560051b60643501018210610a38575050906000915b845183101561065e5760208360051b860101519081811060001461064b57600052602052600160406000205b920191610617565b9060005260205260016040600020610643565b83907f000000000000000000000000000000000000000000000000000000000000000003610a0e5760043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff60005416906040516106cd81610edf565b600081526000602082015260405192610100840184811067ffffffffffffffff8211176109df57604052835273ffffffffffffffffffffffffffffffffffffffff821660208401526fffffffffffffffffffffffffffffffff8416604084015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608401527f0000000000000000000000000000000000000000000000000000000000000000151560808401527f0000000000000000000000000000000000000000000000000000000000000000151560a08401526040516107c581610edf565b64ffffffffff600354818116835260281c16602082015260c084015260e0830152602060e0604051937fab167ccc00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff815116600486015273ffffffffffffffffffffffffffffffffffffffff838201511660248601526fffffffffffffffffffffffffffffffff604082015116604486015273ffffffffffffffffffffffffffffffffffffffff606082015116606486015260808101511515608486015260a0810151151560a486015264ffffffffff8360c08301518281511660c489015201511660e4860152015173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015260208261014481600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af19182156109d35760009261099e575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d6020116109cb575b816109b960209383610f17565b810103126100f8579051906020610937565b3d91506109ac565b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b81358152602091820191016105f9565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f857604060035464ffffffffff825191818116835260281c166020820152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857610b9a610ebc565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff8060005416338103610dbf5750610be1610f58565b15610d61576040519060209160008083858401977fa9059cbb000000000000000000000000000000000000000000000000000000008952169687602485015286604485015260448452610c3384610efb565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610d55573d67ffffffffffffffff81116109df57610cbc9160405191610cac877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610f17565b82523d60008784013e5b83610f95565b8051848115159182610d34575b50509050610d035750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488610cc9565b610cbc90606090610cb6565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f85761036d907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261036181610edf565b60208082528251818301819052939260005b858110610ea8575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201610e68565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff8211176109df57604052565b6080810190811067ffffffffffffffff8211176109df57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176109df57604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081610f8d575090565b905042101590565b90610fd45750805115610faa57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b8151158061102c575b610fe5575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15610fdd56fea164736f6c6343000817000a6101608060405234620006f7576200205990813803809262000022828462000743565b82396060818381010312620006f75780516001600160401b038111620006f7578101916101009283818385010312620006f757604051908482016001600160401b03811183821017620007115760405280516001600160a01b0381168103620006f757825260208101516001600160a01b0381168103620006f757602083015260408101516001600160401b038111620006f757620000c790848601908301620007a8565b604083015260608101516001600160401b038111620006f7576200013491620000f860e092868801908301620007a8565b6060850152608081015160808501526200011560a08201620007f5565b60a08501526200012860c0820162000808565b60c08501520162000808565b60e08201526020830151916001600160a01b0383168303620006f7576040840151906001600160401b038211620006f757808501601f838701011215620006f75784820151906001600160401b0382116200071157604051956200019f60208460051b018862000743565b828752602087019382820160208560061b838501010111620006f75793602085830101945b60208560061b828501010186106200069157505050505050606081015151602081116200067257508051600080546001600160a01b0319166001600160a01b03928316178155602083015190911660805260c080830151151560a090815283015164ffffffffff16905260408201518051919290916001600160401b0381116200065e5760019283548481811c9116801562000653575b60208210146200063f57601f8111620005f2575b50602090601f8311600114620005885760e09392918691836200057c575b5050600019600383901b1c191690841b1783555b608081015182526060810151604051620002db60208281620002cd818301968781519384920162000783565b810103808452018262000743565b519051906020811062000569575b5087520151151593610120948552610140938452805190835b828110620004a7575050505060018060a01b036080511660018060a01b03835116906040519160208301848063095ea7b360e01b92838152846024880152600019604488015260448752620003578762000727565b86519082875af16200036862000816565b8162000466575b50806200045b575b1562000417575b6040516117219087898b846200093885396080518481816104e2015281816107fe0152818161092001526110e2015260a0518481816109470152611008015260c05184818161022001528181610f830152818161123b015261140c015260e05184818161035401526106b2015251836112d101525182818161096f0152610fcb01525181818161012a015281816107850152610ad90152f35b62000450946200044a926040519260208401526024830152604482015260448152620004438162000727565b826200084b565b6200084b565b38808080806200037e565b50823b151562000377565b80518015925082156200047d575b5050386200036f565b8192509060209181010312620004a35760206200049b910162000808565b388062000474565b8580fd5b8151811015620005555760208160051b8301015160038054906801000000000000000082101562000541578682018082558210156200052d578752602080882083519201805493909101516cffffffffff000000000000000060409190911b166001600160401b039092166001600160681b031990931692909217179055830162000302565b634e487b7160e01b88526032600452602488fd5b634e487b7160e01b88526041600452602488fd5b634e487b7160e01b85526032600452602485fd5b6000199060200360031b1b1638620002e9565b0151905038806200028d565b92918491601f198216908388526020882091885b818110620005d95750958360e09710620005bf575b505050811b018355620002a1565b015160001960f88460031b161c19169055388080620005b1565b828801518455889590930192602092830192016200059c565b84865260208620601f840160051c8101916020851062000634575b601f0160051c019085905b828110620006285750506200026f565b87815501859062000618565b90915081906200060d565b634e487b7160e01b86526022600452602486fd5b90607f16906200025b565b634e487b7160e01b84526041600452602484fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b6040868585010312620006f757604080519081016001600160401b03811182821017620006fc5760405286516001600160401b0381168103620006f75760209382859260409452620006e5838b01620007f5565b838201528152019601959150620001c4565b600080fd5b60246000634e487b7160e01b81526041600452fd5b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b038211176200071157604052565b601f909101601f19168101906001600160401b038211908210176200071157604052565b6001600160401b0381116200071157601f01601f191660200190565b60005b838110620007975750506000910152565b818101518382015260200162000786565b81601f82011215620006f7578051620007c18162000767565b92620007d1604051948562000743565b81845260208284010111620006f757620007f2916020808501910162000783565b90565b519064ffffffffff82168203620006f757565b51908115158203620006f757565b3d1562000846573d906200082a8262000767565b916200083a604051938462000743565b82523d6000602084013e565b606090565b600080620008789260018060a01b03169360208151910182865af16200087062000816565b9083620008cf565b8051908115159182620008a8575b5050620008905750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620006f7576020620008c6910162000808565b15388062000886565b90620008f85750805115620008e657805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806200092d575b6200090b575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156200090256fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde03146112ba575080631686c9091461102d57806316c3549d14610ff05780631bfd681414610fb35780633f31ae3f146105065780634800d97f146104b557806349fc73dd1461037757806351e75e8b1461033c57806375829def1461025f57806390e64d1314610244578063bb4b573414610202578063bf4ed03f14610190578063ce5165071461014e578063da792468146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760206003193601126100f857602061018660043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f8576101a9611459565b6040516020918282018383528151809152836040840192019360005b8281106101d25784840385f35b8551805167ffffffffffffffff16855282015164ffffffffff1684830152948101946040909301926001016101c5565b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f8576020610186611404565b346100f85760206003193601126100f857610278611368565b60005473ffffffffffffffffffffffffffffffffffffffff808216923384036102ef577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c90600183169283156104ab575b602093848410811461047c5783865290811561043e57506001146103e3575b6103df846103d3818803826113c3565b60405191829182611302565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b82841061042b57505050816103df936103d392820101936103c3565b805485850187015292850192810161040f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103d3816103df6103c3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f16916103a4565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f857604435906fffffffffffffffffffffffffffffffff821682036100f8576064359167ffffffffffffffff83116100f857366023840112156100f85782600401359267ffffffffffffffff84116100f85760248460051b8201013681116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff861660408201526fffffffffffffffffffffffffffffffff85166060820152606081526105e5816113a7565b51902060405160208101918252602081526105ff8161138b565b5190209161060b611404565b610f555761063460043560ff6001918060081c6000526002602052161b60406000205416151590565b610f235761064186611441565b9561064f60405197886113c3565b8652602401602086015b828210610f1357505050906000915b84518310156106ae5761067b83866114f4565b51908181101561069b57600052602052600160406000205b920191610668565b9060005260205260016040600020610693565b83907f000000000000000000000000000000000000000000000000000000000000000003610ee9576106de611459565b9060008251906106ed82611441565b936106fb60405195866113c3565b8285527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061072884611441565b0160005b818110610ec45750506000905b838210610e205750506040517f5fe3b56700000000000000000000000000000000000000000000000000000000815260208160048173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa908115610ba657600091610dd0575b50602073ffffffffffffffffffffffffffffffffffffffff916024604051809481937fdcf844a7000000000000000000000000000000000000000000000000000000008352817f0000000000000000000000000000000000000000000000000000000000000000166004840152165afa908115610ba657600091610d9e575b5080610cfd575b50806fffffffffffffffffffffffffffffffff861603610c1f575b505060043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff6000541691604051906108a18261138b565b6000825260006020830152604051938461010081011067ffffffffffffffff61010087011117610bf0576101008501604052845273ffffffffffffffffffffffffffffffffffffffff831660208501526fffffffffffffffffffffffffffffffff8516604085015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608501527f0000000000000000000000000000000000000000000000000000000000000000151560808501527f0000000000000000000000000000000000000000000000000000000000000000151560a085015260c084015260e0830152604051917f897f362b0000000000000000000000000000000000000000000000000000000083526020600484015282610144810173ffffffffffffffffffffffffffffffffffffffff835116602483015273ffffffffffffffffffffffffffffffffffffffff60208401511660448301526fffffffffffffffffffffffffffffffff604084015116606483015273ffffffffffffffffffffffffffffffffffffffff60608401511660848301526080830151151560a483015260a0830151151560c483015260c08301519061012060e484015281518091526020610164840192019060005b818110610bb2575050508190602060e08195015173ffffffffffffffffffffffffffffffffffffffff81511661010485015201516101248301520381600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1918215610ba657600092610b71575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d602011610b9e575b81610b8c602093836113c3565b810103126100f8579051906020610b0a565b3d9150610b7f565b6040513d6000823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff168186015288955060409094019390920191600101610a84565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b610c4d90610c47610c416fffffffffffffffffffffffffffffffff8816611559565b91611559565b90611537565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101908111610cce57610c8290846114f4565b51906fffffffffffffffffffffffffffffffff808351169116016fffffffffffffffffffffffffffffffff8111610cce576fffffffffffffffffffffffffffffffff1690528380610857565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b610d1c610d21916fffffffffffffffffffffffffffffffff88166115a7565b611559565b827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810111610cce576fffffffffffffffffffffffffffffffff610d95610d8a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8601886114f4565b519282845116611537565b1690528561083c565b90506020813d602011610dc8575b81610db9602093836113c3565b810103126100f8575186610835565b3d9150610dac565b90506020813d602011610e18575b81610deb602093836113c3565b810103126100f8575173ffffffffffffffffffffffffffffffffffffffff811681036100f85760206107b6565b3d9150610dde565b9091610e5367ffffffffffffffff610e3885856114f4565b5151166fffffffffffffffffffffffffffffffff89166115a7565b928382018211610cce57610e6b846001930194611559565b64ffffffffff6020610e7d84876114f4565b510151166fffffffffffffffffffffffffffffffff60405192610e9f8461138b565b1682526020820152610eb182896114f4565b52610ebc81886114f4565b500190610739565b602090604051610ed38161138b565b6000815260008382015282828a0101520161072c565b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b8135815260209182019101610659565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857611046611368565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff806000541633810361126b575061108d611404565b1561120d576040519060209160008083858401977fa9059cbb0000000000000000000000000000000000000000000000000000000089521696876024850152866044850152604484526110df846113a7565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15611201573d67ffffffffffffffff8111610bf0576111689160405191611158877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846113c3565b82523d60008784013e5b83611674565b80518481151591826111e0575b505090506111af5750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488611175565b61116890606090611162565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f8576103df907f00000000000000000000000000000000000000000000000000000000000000006020820152602081526103d38161138b565b60208082528251818301819052939260005b858110611354575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201611314565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff821117610bf057604052565b6080810190811067ffffffffffffffff821117610bf057604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610bf057604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081611439575090565b905042101590565b67ffffffffffffffff8111610bf05760051b60200190565b6003549061146682611441565b91604061147660405194856113c3565b8184528360208091019160036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b906000935b8585106114ba57505050505050565b600184819284516114ca8161138b565b64ffffffffff875467ffffffffffffffff81168352871c16838201528152019301940193916114ab565b80518210156115085760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6fffffffffffffffffffffffffffffffff9182169082160391908211610cce57565b6fffffffffffffffffffffffffffffffff90818111611576571690565b602490604051907f4916adce0000000000000000000000000000000000000000000000000000000082526004820152fd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8382098382029182808310920391808303921461166357670de0b6b3a7640000908183101561162c57947faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac1066994950990828211900360ee1b910360121c170290565b60449086604051917f5173648d00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5050670de0b6b3a764000090049150565b906116b3575080511561168957805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b8151158061170b575b6116c4575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b156116bc56fea164736f6c6343000817000aa164736f6c6343000817000a"; + hex"6080806040523461001657614245908161001c8239f35b600080fdfe608060405260043610156200001357600080fd5b60003560e01c806324496a1514620003fc576360d18fed146200003557600080fd5b34620003f75760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112620003f75760043567ffffffffffffffff8111620003f757620000899036906004016200097d565b73ffffffffffffffffffffffffffffffffffffffff602435168060243503620003f75760407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc360112620003f757604051620000e581620008bf565b64ffffffffff926044358481168103620003f75782526064359384168403620003f75760209384830152805184820151620001386200014760408501516040519283918a80840152604083019062000a91565b03601f198101835282620008dc565b6200018260608501516200017c89604051836200016e829551809285808601910162000a6c565b8101038084520182620008dc565b62000ab8565b60808501519260a086015160c0870151151560e088015115159160405196620001c18d89018c6020908164ffffffffff91828151168552015116910152565b6040885287606081011067ffffffffffffffff60608a011117620003c8578c889760608901604052608089019a7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009788809260601b168d5260601b1660948a01528051809260a88b01920191620002389262000a6c565b87019560a887015260c886015260d81b7fffffffffff0000000000000000000000000000000000000000000000000000001660e885015260f81b60ed84015260f81b60ee83015260243560601b1660ef8201528151906101038101828a850191620002a39262000a6c565b010360838101606083015260a3016060820190620002c191620008dc565b60600151902060405161180c80820182811067ffffffffffffffff821117620003c857829162000c8a83396080815262000326604062000305608084018862000b4c565b92898b82015201876020908164ffffffffff91828151168552015116910152565b03906000f5928315620003bc5760a07f3ce4d6bb8b94ee36e2248c8057e53b793a45b37e4d58038ec3b5dc21c56e925491620003a373ffffffffffffffffffffffffffffffffffffffff6200037f971696879562000bdb565b95604051928352888301906020908164ffffffffff91828151168552015116910152565b608435606082015260a4356080820152a3604051908152f35b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080fd5b34620003f75760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112620003f75760043567ffffffffffffffff8111620003f757620004509036906004016200097d565b73ffffffffffffffffffffffffffffffffffffffff6024351660243503620003f75760443567ffffffffffffffff8111620003f75736602382011215620003f757806004013567ffffffffffffffff8111620003c85760059060405192620004bf60208360051b0185620008dc565b8184526024602085019260061b82010190368211620003f757602401915b8183106200086b5750505060009082516000915b818310620007d657505050670de0b6b3a76400008103620007a55750620005426200068b60a38451620005d16020870151620005516040890151604051978891602080840152604083019062000a91565b03601f198101885287620008dc565b6200057960608901516200017c6020604051836200016e829551809285808601910162000a6c565b90608089015160a08a01517fffffffffff00000000000000000000000000000000000000000000000000000060c08c015115159160e08d0151151593620005e08d604051998a91602080840152604083019062000afa565b03601f1981018a5289620008dc565b604862000634604051809e819c7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009a8b809260601b16602085015260601b1660348301526020815194859301910162000a6c565b8901966048880152606887015260d81b16608885015260f81b608d84015260f81b608e83015260243560601b16608f8201526200067b825180936020878501910162000a6c565b01036083810184520182620008dc565b60208151910120604051611da38082019082821067ffffffffffffffff831117620003c8578291620006fe9162002496843960608152620006d0606082018862000b4c565b9073ffffffffffffffffffffffffffffffffffffffff60243516602082015260408183039101528562000afa565b03906000f5908115620003bc576020927f431b4603d07471c22cc6557755bfeb55c0f0c1163d5df05ee2b54701ab6674ab6200078a73ffffffffffffffffffffffffffffffffffffffff62000757951694859362000bdb565b9360405191829173ffffffffffffffffffffffffffffffffffffffff602435168352608089840152608083019062000afa565b606435604083015260843560608301520390a3604051908152f35b602490604051907ff1cd938a0000000000000000000000000000000000000000000000000000000082526004820152fd5b90919284518410156200083c5767ffffffffffffffff602085841b87010151511681018091116200080d57926001019190620004f1565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b604083360312620003f757604051906200088582620008bf565b83359067ffffffffffffffff82168203620003f7578260209260409452620008af8387016200095c565b83820152815201920191620004dd565b6040810190811067ffffffffffffffff821117620003c857604052565b90601f601f19910116810190811067ffffffffffffffff821117620003c857604052565b81601f82011215620003f75780359067ffffffffffffffff8211620003c85760405192620009396020601f19601f8601160185620008dc565b82845260208383010111620003f757816000926020809301838601378301015290565b359064ffffffffff82168203620003f757565b35908115158203620003f757565b919091610100908181850312620003f75760405191820167ffffffffffffffff9083811082821117620003c8576040528294823573ffffffffffffffffffffffffffffffffffffffff908181168103620003f757855260208401359081168103620003f75760208501526040830135828111620003f7578162000a0291850162000900565b60408501526060830135918211620003f7578262000a2b60e0949262000a679486940162000900565b60608601526080810135608086015262000a4860a082016200095c565b60a086015262000a5b60c082016200096f565b60c0860152016200096f565b910152565b60005b83811062000a805750506000910152565b818101518382015260200162000a6f565b90601f19601f60209362000ab18151809281875287808801910162000a6c565b0116010190565b60208151910151906020811062000acd575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b90815180825260208080930193019160005b82811062000b1b575050505090565b8351805167ffffffffffffffff16865282015164ffffffffff16858301526040909401939281019260010162000b0c565b9060e08062000ba962000b9661010073ffffffffffffffffffffffffffffffffffffffff808851168752602088015116602087015260408701519080604088015286019062000a91565b6060860151858203606087015262000a91565b936080810151608085015264ffffffffff60a08201511660a085015260c0810151151560c08501520151151591015290565b60c0604051809273ffffffffffffffffffffffffffffffffffffffff808251168352602082015116602083015260e0601f604083015162000c2781518092602060408901910162000a6c565b601f199182910116840190601f606085015162000c4f81518092602060408801910162000a6c565b011601916080810151604084015264ffffffffff60a082015116606084015284810151151560808401520151151560a0820152030190209056fe6101608060405234620005b4576200180c8038038091620000218285620005d5565b83398101908082039160808312620005b45781516001600160401b038111620005b45782019261010084830312620005b4576040519361010085016001600160401b038111868210176200059e5760405280516001600160a01b0381168103620005b45785526020810151926001600160a01b0384168403620005b4576020860193845260408201516001600160401b038111620005b45781620000c79184016200063a565b60408701908152606083015190916001600160401b038211620005b457620000f19184016200063a565b6060870152608082015195608081019687526200011160a0840162000687565b9660a082019788526200013c60e06200012d60c087016200069a565b9560c08501968752016200069a565b60e083019081526020880151959093906001600160a01b0387168703620005b457604090603f190112620005b45760408051989089016001600160401b0381118a8210176200059e57620001a7916060916040526200019e6040820162000687565b8b520162000687565b9460208901958652606084015151602081116200057f57508351600080546001600160a01b0319166001600160a01b0392831617815598511660805251151560a052975164ffffffffff90811660c052975180516001600160401b0381116200056b5760019182548381811c9116801562000560575b60208210146200054c57601f8111620004ff575b50602090601f83116001146200049357606095949392918a918362000487575b5050600019600383901b1c191690821b1790555b5160e052015160405162000299602082816200028b818301968781519384920162000615565b8101038084520182620005d5565b519051906020811062000474575b50610100525115159461012095865261014094838652511669ffffffffff0000000000600354925160281b169160018060501b031916171760035560018060a01b03608051166040519160208301848063095ea7b360e01b9283815260018060a01b03851660248801526000196044880152604487526200032887620005b9565b86519082875af162000339620006a8565b8162000433575b508062000428575b15620003dc575b60405161104290878983620007ca8439608051838181610470015281816107440152610c36015260a05183818161076b0152610b5c015260c05183818161015d01528181610aa801528181610d8f0152610f60015260e0518381816102e2015261066201526101005183610e250152518281816107930152610b1f0152518181816101ae01526109060152f35b6200041d94620004179260405192602084015260018060a01b031660248301526044820152604481526200041081620005b9565b82620006dd565b620006dd565b38808080806200034f565b50823b151562000348565b80518015925082156200044a575b50503862000340565b8192509060209181010312620004705760206200046891016200069a565b388062000441565b8580fd5b6000199060200360031b1b1638620002a7565b01519050388062000251565b838a5260208a209190601f1984168b5b818110620004e857509185949291836060999897959310620004ce575b505050811b01905562000265565b015160001960f88460031b161c19169055388080620004c0565b8284015185559386019360209384019301620004a3565b838a5260208a20601f840160051c8101916020851062000541575b601f0160051c019084905b8281106200053557505062000231565b8b815501849062000525565b90915081906200051a565b634e487b7160e01b8a52602260045260248afd5b90607f16906200021d565b634e487b7160e01b88526041600452602488fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b634e487b7160e01b600052604160045260246000fd5b600080fd5b608081019081106001600160401b038211176200059e57604052565b601f909101601f19168101906001600160401b038211908210176200059e57604052565b6001600160401b0381116200059e57601f01601f191660200190565b60005b838110620006295750506000910152565b818101518382015260200162000618565b81601f82011215620005b45780516200065381620005f9565b92620006636040519485620005d5565b81845260208284010111620005b45762000684916020808501910162000615565b90565b519064ffffffffff82168203620005b457565b51908115158203620005b457565b3d15620006d8573d90620006bc82620005f9565b91620006cc6040519384620005d5565b82523d6000602084013e565b606090565b6000806200070a9260018060a01b03169360208151910182865af162000702620006a8565b908362000761565b80519081151591826200073a575b5050620007225750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620005b45760206200075891016200069a565b15388062000718565b906200078a57508051156200077857805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580620007bf575b6200079d575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156200079456fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde0314610e0e575080631686c90914610b8157806316c3549d14610b445780631bfd681414610b075780633bfe03a814610ad85780633f31ae3f146104945780634800d97f1461044357806349fc73dd1461030557806351e75e8b146102ca57806375829def146101ed57806390e64d13146101d25780639e93e57714610181578063bb4b57341461013f578063ce516507146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760206003193601126100f857602061013560043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f8576020610135610f58565b346100f85760206003193601126100f857610206610ebc565b60005473ffffffffffffffffffffffffffffffffffffffff8082169233840361027d577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c9060018316928315610439575b602093848410811461040a578386529081156103cc5750600114610371575b61036d8461036181880382610f17565b60405191829182610e56565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8284106103b9575050508161036d936103619282010193610351565b805485850187015292850192810161039d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103618161036d610351565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691610332565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f857604435906fffffffffffffffffffffffffffffffff821682036100f85767ffffffffffffffff606435116100f8573660236064350112156100f85767ffffffffffffffff60643560040135116100f8573660246064356004013560051b6064350101116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff831660408201526fffffffffffffffffffffffffffffffff841660608201526060815261057a81610efb565b519020604051602081019182526020815261059481610edf565b519020916105a0610f58565b610a7a576105c960043560ff6001918060081c6000526002602052161b60406000205416151590565b610a4857604051926105e760206064356004013560051b0185610f17565b60643560048101358552602401602085015b60246064356004013560051b60643501018210610a38575050906000915b845183101561065e5760208360051b860101519081811060001461064b57600052602052600160406000205b920191610617565b9060005260205260016040600020610643565b83907f000000000000000000000000000000000000000000000000000000000000000003610a0e5760043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff60005416906040516106cd81610edf565b600081526000602082015260405192610100840184811067ffffffffffffffff8211176109df57604052835273ffffffffffffffffffffffffffffffffffffffff821660208401526fffffffffffffffffffffffffffffffff8416604084015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608401527f0000000000000000000000000000000000000000000000000000000000000000151560808401527f0000000000000000000000000000000000000000000000000000000000000000151560a08401526040516107c581610edf565b64ffffffffff600354818116835260281c16602082015260c084015260e0830152602060e0604051937fab167ccc00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff815116600486015273ffffffffffffffffffffffffffffffffffffffff838201511660248601526fffffffffffffffffffffffffffffffff604082015116604486015273ffffffffffffffffffffffffffffffffffffffff606082015116606486015260808101511515608486015260a0810151151560a486015264ffffffffff8360c08301518281511660c489015201511660e4860152015173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015260208261014481600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af19182156109d35760009261099e575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d6020116109cb575b816109b960209383610f17565b810103126100f8579051906020610937565b3d91506109ac565b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b81358152602091820191016105f9565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f857604060035464ffffffffff825191818116835260281c166020820152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857610b9a610ebc565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff8060005416338103610dbf5750610be1610f58565b15610d61576040519060209160008083858401977fa9059cbb000000000000000000000000000000000000000000000000000000008952169687602485015286604485015260448452610c3384610efb565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610d55573d67ffffffffffffffff81116109df57610cbc9160405191610cac877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610f17565b82523d60008784013e5b83610f95565b8051848115159182610d34575b50509050610d035750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488610cc9565b610cbc90606090610cb6565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f85761036d907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261036181610edf565b60208082528251818301819052939260005b858110610ea8575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201610e68565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff8211176109df57604052565b6080810190811067ffffffffffffffff8211176109df57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176109df57604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081610f8d575090565b905042101590565b90610fd45750805115610faa57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b8151158061102c575b610fe5575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15610fdd56fea164736f6c6343000817000a6101608060405234620006e95762001da390813803809262000022828462000735565b82396060818381010312620006e95780516001600160401b038111620006e9578101916101009283818385010312620006e957604051908482016001600160401b03811183821017620007035760405280516001600160a01b0381168103620006e957825260208101516001600160a01b0381168103620006e957602083015260408101516001600160401b038111620006e957620000c7908486019083016200079a565b604083015260608101516001600160401b038111620006e9576200013491620000f860e0928688019083016200079a565b6060850152608081015160808501526200011560a08201620007e7565b60a08501526200012860c08201620007fa565b60c085015201620007fa565b60e08201526020830151916001600160a01b0383168303620006e9576040840151906001600160401b038211620006e957808501601f838701011215620006e95784820151906001600160401b0382116200070357604051956200019f60208460051b018862000735565b828752602087019382820160208560061b838501010111620006e95793602085830101945b60208560061b828501010186106200068357505050505050606081015151602081116200066457508051600080546001600160a01b0319166001600160a01b03928316178155602083015190911660805260c080830151151560a090815283015164ffffffffff16905260408201518051919290916001600160401b038111620006505760019283548481811c9116801562000645575b60208210146200063157601f8111620005e4575b50602090601f83116001146200057a5760e09392918691836200056e575b5050600019600383901b1c191690841b1783555b608081015182526060810151604051620002db60208281620002cd818301968781519384920162000775565b810103808452018262000735565b51905190602081106200055b575b5087520151151593610120948552610140938452805190835b82811062000499575050505060018060a01b036080511660018060a01b03835116906040519160208301848063095ea7b360e01b92838152846024880152600019604488015260448752620003578762000719565b86519082875af16200036862000808565b8162000458575b50806200044d575b1562000409575b6040516114799087898b846200092a85396080518481816104f4015281816107e60152610e5f015260a05184818161080d0152610d84015260c05184818161022c01528181610cff01528181610fb40152611186015260e05184818161036601526106b00152518361104b0152518281816108350152610d4701525181818161012c01526109a00152f35b62000442946200043c926040519260208401526024830152604482015260448152620004358162000719565b826200083d565b6200083d565b38808080806200037e565b50823b151562000377565b80518015925082156200046f575b5050386200036f565b8192509060209181010312620004955760206200048d9101620007fa565b388062000466565b8580fd5b8151811015620005475760208160051b8301015160038054906801000000000000000082101562000533578682018082558210156200051f578752602080882083519201805493909101516cffffffffff000000000000000060409190911b166001600160401b039092166001600160681b031990931692909217179055830162000302565b634e487b7160e01b88526032600452602488fd5b634e487b7160e01b88526041600452602488fd5b634e487b7160e01b85526032600452602485fd5b6000199060200360031b1b1638620002e9565b0151905038806200028d565b92918491601f198216908388526020882091885b818110620005cb5750958360e09710620005b1575b505050811b018355620002a1565b015160001960f88460031b161c19169055388080620005a3565b828801518455889590930192602092830192016200058e565b84865260208620601f840160051c8101916020851062000626575b601f0160051c019085905b8281106200061a5750506200026f565b8781550185906200060a565b9091508190620005ff565b634e487b7160e01b86526022600452602486fd5b90607f16906200025b565b634e487b7160e01b84526041600452602484fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b6040868585010312620006e957604080519081016001600160401b03811182821017620006ee5760405286516001600160401b0381168103620006e95760209382859260409452620006d7838b01620007e7565b838201528152019601959150620001c4565b600080fd5b60246000634e487b7160e01b81526041600452fd5b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b038211176200070357604052565b601f909101601f19168101906001600160401b038211908210176200070357604052565b6001600160401b0381116200070357601f01601f191660200190565b60005b838110620007895750506000910152565b818101518382015260200162000778565b81601f82011215620006e9578051620007b38162000759565b92620007c3604051948562000735565b81845260208284010111620006e957620007e4916020808501910162000775565b90565b519064ffffffffff82168203620006e957565b51908115158203620006e957565b3d1562000838573d906200081c8262000759565b916200082c604051938462000735565b82523d6000602084013e565b606090565b6000806200086a9260018060a01b03169360208151910182865af16200086262000808565b9083620008c1565b80519081151591826200089a575b5050620008825750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620006e9576020620008b89101620007fa565b15388062000878565b90620008ea5750805115620008d857805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806200091f575b620008fd575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15620008f456fe6080604090808252600436101561001557600080fd5b60003560e01c90816306fdde0314611032575080631686c90914610da957806316c3549d14610d6c5780631bfd681414610d2f5780633f31ae3f146105185780634800d97f146104c757806349fc73dd1461038957806351e75e8b1461034e57806375829def1461026d57806390e64d1314610250578063bb4b57341461020e578063bf4ed03f14610193578063ce51650714610150578063da792468146100ff5763f851a440146100c657600080fd5b346100fa5760006003193601126100fa5760209073ffffffffffffffffffffffffffffffffffffffff600054169051908152f35b600080fd5b50346100fa5760006003193601126100fa576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346100fa5760206003193601126100fa5760209061018a60043560ff6001918060081c6000526002602052161b60406000205416151590565b90519015158152f35b50346100fa5760006003193601126100fa576101ad6111d3565b9080519182916020808401908085528351809252808386019401926000905b8382106101d95786860387f35b8451805167ffffffffffffffff16875283015164ffffffffff16868401528796509485019493820193600191909101906101cc565b50346100fa5760006003193601126100fa576020905164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346100fa5760006003193601126100fa5760209061018a61117e565b50346100fa5760206003193601126100fa576102876110e2565b6000549073ffffffffffffffffffffffffffffffffffffffff9081831633810361030057507fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b84517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b50346100fa5760006003193601126100fa57602090517f00000000000000000000000000000000000000000000000000000000000000008152f35b50346100fa5760006003193601126100fa57805160009160018054908160011c90600183169283156104bd575b602093848410811461048e5783875290811561045057506001146103f5575b5050506103e7826103f194038361113d565b519182918261107c565b0390f35b600160009081529295507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b82841061043d57505050826103f1946103e792820101946103d5565b8054868501880152928601928101610421565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016868501525050151560051b83010192506103e7826103f16103d5565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f16916103b6565b50346100fa5760006003193601126100fa576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346100fa5760806003193601126100fa5760243573ffffffffffffffffffffffffffffffffffffffff811681036100fa57604435916fffffffffffffffffffffffffffffffff83168093036100fa5767ffffffffffffffff9283606435116100fa573660236064350112156100fa5760643560040135908482116100fa5760248260051b60643501013681116100fa5783516020810190600435825273ffffffffffffffffffffffffffffffffffffffff871686820152836060820152606081526105e381611121565b519020845160208101918252602081526105fc81611105565b5190209061060861117e565b610cd25761063160043560ff6001918060081c6000526002602052161b60406000205416151590565b610ca15761063e846111bb565b9361064b8651958661113d565b8452606435602401602085015b828210610c9157505050946000955b83518710156106ab5761067a878561126e565b519081811015610699576000526020526001846000205b960195610667565b90600052602052600184600020610691565b8585917f000000000000000000000000000000000000000000000000000000000000000003610c68576106dc6111d3565b805160006106e9826111bb565b926106f68651948561113d565b8284527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610723846111bb565b0160005b818110610c445750506000905b838210610bbc575050808703610acd575b505060043560081c600052600260205282600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff600054169083519161078d83611105565b60008084526020840152845195610100870190811187821017610a9e578552855273ffffffffffffffffffffffffffffffffffffffff83166020860152858486015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608601527f0000000000000000000000000000000000000000000000000000000000000000151560808601527f0000000000000000000000000000000000000000000000000000000000000000151560a086015260c085015260e08401528151927f897f362b000000000000000000000000000000000000000000000000000000008452602060048501528361014481019173ffffffffffffffffffffffffffffffffffffffff815116602483015273ffffffffffffffffffffffffffffffffffffffff60208201511660448301526fffffffffffffffffffffffffffffffff8582015116606483015273ffffffffffffffffffffffffffffffffffffffff60608201511660848301526080810151151560a483015260a0810151151560c483015260c08101519261012060e484015283518091526020610164840194019060005b87828210610a6357505050506020928360e08493015173ffffffffffffffffffffffffffffffffffffffff81511661010485015201516101248301520381600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1928315610a5857600093610a23575b506020937f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d8373ffffffffffffffffffffffffffffffffffffffff86948251946004358652898601521692a351908152f35b92506020833d602011610a50575b81610a3e6020938361113d565b810103126100fa5791519160206109d1565b3d9150610a31565b82513d6000823e3d90fd5b835180516fffffffffffffffffffffffffffffffff16885260209081015164ffffffffff168189015296019589955090920191600101610949565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6fffffffffffffffffffffffffffffffff80610af1610aeb8a6112b1565b936112b1565b16911603906fffffffffffffffffffffffffffffffff8211610b8d577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101908111610b8d57610b41908361126e565b51906fffffffffffffffffffffffffffffffff808351169116016fffffffffffffffffffffffffffffffff8111610b8d576fffffffffffffffffffffffffffffffff1690528580610745565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b9091610bd588610bcc858561126e565b5151168a6112ff565b92838201809211610b8d57610bec600192946112b1565b64ffffffffff6020610bfe848761126e565b510151166fffffffffffffffffffffffffffffffff8a5192610c1f84611105565b1682526020820152610c31828861126e565b52610c3c818761126e565b500190610734565b6020908851610c5281611105565b6000815260008382015282828901015201610727565b600482517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b8135815260209182019101610658565b602485517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b84517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b50346100fa5760006003193601126100fa57602090517f000000000000000000000000000000000000000000000000000000000000000015158152f35b50346100fa5760006003193601126100fa57602090517f000000000000000000000000000000000000000000000000000000000000000015158152f35b50346100fa57806003193601126100fa57610dc26110e2565b602435916fffffffffffffffffffffffffffffffff83168093036100fa5773ffffffffffffffffffffffffffffffffffffffff928360005416338103610fe45750610e0b61117e565b15610f875781519060209160008087858401977fa9059cbb000000000000000000000000000000000000000000000000000000008952169687602485015284604485015260448452610e5c84611121565b887f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610f7b573d67ffffffffffffffff8111610a9e57610ee491865191610ed4877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116018461113d565b82523d60008784013e5b836113cc565b8051848115159182610f5a575b50509050610f2a57507f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f929394600054169351908152a3005b6024908451907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100fa578301518015908115036100fa57808438610ef1565b610ee490606090610ede565b50517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b82517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b9050346100fa5760006003193601126100fa576103f1917f00000000000000000000000000000000000000000000000000000000000000006020830152602082526103e782611105565b60208082528251818301819052939260005b8581106110ce575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b81810183015184820160400152820161108e565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100fa57565b6040810190811067ffffffffffffffff821117610a9e57604052565b6080810190811067ffffffffffffffff821117610a9e57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610a9e57604052565b64ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001680151590816111b3575090565b905042101590565b67ffffffffffffffff8111610a9e5760051b60200190565b600354906111e0826111bb565b9160406111f0604051948561113d565b8184528360208091019160036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b906000935b85851061123457505050505050565b6001848192845161124481611105565b64ffffffffff875467ffffffffffffffff81168352871c1683820152815201930194019391611225565b80518210156112825760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6fffffffffffffffffffffffffffffffff908181116112ce571690565b602490604051907f4916adce0000000000000000000000000000000000000000000000000000000082526004820152fd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff838209838202918280831092039180830392146113bb57670de0b6b3a7640000908183101561138457947faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac1066994950990828211900360ee1b910360121c170290565b60449086604051917f5173648d00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5050670de0b6b3a764000090049150565b9061140b57508051156113e157805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580611463575b61141c575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b1561141456fea164736f6c6343000817000aa164736f6c6343000817000a"; /*////////////////////////////////////////////////////////////////////////// DEPLOYERS From bcd435d333c2cc2c6675c51846f88f26d5980022 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Mon, 11 Mar 2024 16:06:54 +0000 Subject: [PATCH 24/61] refactor: reorder imports in fork tests --- test/fork/batch/createWithTimestampsLD.t.sol | 2 +- test/fork/batch/createWithTimestampsLL.t.sol | 2 +- test/fork/batch/createWithTimestampsLT.t.sol | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/fork/batch/createWithTimestampsLD.t.sol b/test/fork/batch/createWithTimestampsLD.t.sol index 51adc556..e2704d24 100644 --- a/test/fork/batch/createWithTimestampsLD.t.sol +++ b/test/fork/batch/createWithTimestampsLD.t.sol @@ -6,9 +6,9 @@ import { LockupDynamic } from "@sablier/v2-core/src/types/DataTypes.sol"; import { Batch } from "src/types/DataTypes.sol"; -import { Fork_Test } from "../Fork.t.sol"; import { ArrayBuilder } from "../../utils/ArrayBuilder.sol"; import { BatchBuilder } from "../../utils/BatchBuilder.sol"; +import { Fork_Test } from "../Fork.t.sol"; /// @dev Runs against multiple fork assets. abstract contract CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is Fork_Test { diff --git a/test/fork/batch/createWithTimestampsLL.t.sol b/test/fork/batch/createWithTimestampsLL.t.sol index d01c1562..8dc3705d 100644 --- a/test/fork/batch/createWithTimestampsLL.t.sol +++ b/test/fork/batch/createWithTimestampsLL.t.sol @@ -6,9 +6,9 @@ import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; import { Batch } from "src/types/DataTypes.sol"; -import { Fork_Test } from "../Fork.t.sol"; import { ArrayBuilder } from "../../utils/ArrayBuilder.sol"; import { BatchBuilder } from "../../utils/BatchBuilder.sol"; +import { Fork_Test } from "../Fork.t.sol"; /// @dev Runs against multiple fork assets. abstract contract CreateWithTimestamps_LockupLinear_Batch_Fork_Test is Fork_Test { diff --git a/test/fork/batch/createWithTimestampsLT.t.sol b/test/fork/batch/createWithTimestampsLT.t.sol index a441fe45..a745dd48 100644 --- a/test/fork/batch/createWithTimestampsLT.t.sol +++ b/test/fork/batch/createWithTimestampsLT.t.sol @@ -6,9 +6,9 @@ import { LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; import { Batch } from "src/types/DataTypes.sol"; -import { Fork_Test } from "../Fork.t.sol"; import { ArrayBuilder } from "../../utils/ArrayBuilder.sol"; import { BatchBuilder } from "../../utils/BatchBuilder.sol"; +import { Fork_Test } from "../Fork.t.sol"; /// @dev Runs against multiple fork assets. abstract contract CreateWithTimestamps_LockupTranched_Batch_Fork_Test is Fork_Test { From f0f7b36bef1b7e9a823c004340896168fb62ad67 Mon Sep 17 00:00:00 2001 From: andreivladbrg Date: Mon, 11 Mar 2024 18:35:08 +0200 Subject: [PATCH 25/61] test: remove unused functions from Base_Test --- test/Base.t.sol | 48 ------------------------------------------------ 1 file changed, 48 deletions(-) diff --git a/test/Base.t.sol b/test/Base.t.sol index 8868f0dd..a39bd388 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -115,54 +115,6 @@ abstract contract Base_Test is Assertions, DeployOptimized, Events, Merkle, V2Co CALL EXPECTS //////////////////////////////////////////////////////////////////////////*/ - /// @dev Expects a call to {ISablierV2LockupDynamic.createWithDurations}. - function expectCallToCreateWithDurationsLD(LockupDynamic.CreateWithDurations memory params) internal { - vm.expectCall({ - callee: address(lockupDynamic), - data: abi.encodeCall(ISablierV2LockupDynamic.createWithDurations, (params)) - }); - } - - /// @dev Expects a call to {ISablierV2LockupLinear.createWithDurations}. - function expectCallToCreateWithDurationsLL(LockupLinear.CreateWithDurations memory params) internal { - vm.expectCall({ - callee: address(lockupLinear), - data: abi.encodeCall(ISablierV2LockupLinear.createWithDurations, (params)) - }); - } - - /// @dev Expects a call to {ISablierV2LockupTranched.createWithDurations}. - function expectCallToCreateWithDurationsLT(LockupTranched.CreateWithDurations memory params) internal { - vm.expectCall({ - callee: address(lockupTranched), - data: abi.encodeCall(ISablierV2LockupTranched.createWithDurations, (params)) - }); - } - - /// @dev Expects a call to {ISablierV2LockupDynamic.createWithTimestamps}. - function expectCallToCreateWithTimestampsLD(LockupDynamic.CreateWithTimestamps memory params) internal { - vm.expectCall({ - callee: address(lockupDynamic), - data: abi.encodeCall(ISablierV2LockupDynamic.createWithTimestamps, (params)) - }); - } - - /// @dev Expects a call to {ISablierV2LockupLinear.createWithTimestamps}. - function expectCallToCreateWithTimestampsLL(LockupLinear.CreateWithTimestamps memory params) internal { - vm.expectCall({ - callee: address(lockupLinear), - data: abi.encodeCall(ISablierV2LockupLinear.createWithTimestamps, (params)) - }); - } - - /// @dev Expects a call to {ISablierV2LockupTranched.createWithTimestamps}. - function expectCallToCreateWithTimestampsLT(LockupTranched.CreateWithTimestamps memory params) internal { - vm.expectCall({ - callee: address(lockupTranched), - data: abi.encodeCall(ISablierV2LockupTranched.createWithTimestamps, (params)) - }); - } - /// @dev Expects a call to {IERC20.transfer}. function expectCallToTransfer(address to, uint256 amount) internal { expectCallToTransfer(address(dai), to, amount); From f862f5fcf3689dcc4872885353f77de6a9bb0015 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Thu, 14 Mar 2024 12:21:33 +0000 Subject: [PATCH 26/61] feat: move precompiles to precompiles directory (#302) feat: new deployProtocol function in Precompiles contract chore: ignore precompiles in codecov --- .codecov.yml | 1 + bun.lockb | Bin 43329 -> 43329 bytes package.json | 1 + {test/utils => precompiles}/Precompiles.sol | 36 ++++++++++++++++++-- shell/update-precompiles.sh | 2 +- test/fork/Fork.t.sol | 2 +- test/integration/Integration.t.sol | 2 +- test/utils/Defaults.sol | 1 - test/utils/Precompiles.t.sol | 2 +- 9 files changed, 40 insertions(+), 7 deletions(-) rename {test/utils => precompiles}/Precompiles.sol (96%) diff --git a/.codecov.yml b/.codecov.yml index 5cf31dcd..9e7248e0 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -5,5 +5,6 @@ coverage: status: patch: off ignore: + - "precompiles" - "script" - "test" diff --git a/bun.lockb b/bun.lockb index 8041c00adb186e6f2b94a027882167130d362866..c5827cb058ea5fa8e53f7505bb2cbe3daabdfc59 100755 GIT binary patch delta 179 zcmV;k08IbE(gMNK0+22sD|!Vz1w#@m`LS|3R)EqB^BqIs8CNGjhR@v8(jj~Lu};br zlh_F`vq%a1uGc`3kzk@{G?bGz*BE7erudkbSJz?7 hH)dmIlTnBqlaQzcvpk5W5R>GaAd~f%AhRQx9uWU%R6YOz delta 177 zcmV;i08anG(gMNK0+22s*||ue_3h^HKvZ_j8dfemvlVuKl~+swU=0.8.22; -import { ISablierV2Batch } from "../../src/interfaces/ISablierV2Batch.sol"; -import { ISablierV2MerkleLockupFactory } from "../../src/interfaces/ISablierV2MerkleLockupFactory.sol"; +import { Precompiles as V2CorePrecompiles } from "@sablier/v2-core/precompiles/Precompiles.sol"; +import { ISablierV2LockupDynamic } from "@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol"; +import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; +import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; +import { ISablierV2NFTDescriptor } from "@sablier/v2-core/src/interfaces/ISablierV2NFTDescriptor.sol"; + +import { ISablierV2Batch } from "../src/interfaces/ISablierV2Batch.sol"; +import { ISablierV2MerkleLockupFactory } from "../src/interfaces/ISablierV2MerkleLockupFactory.sol"; contract Precompiles { /*////////////////////////////////////////////////////////////////////////// @@ -50,4 +56,30 @@ contract Precompiles { batch = deployBatch(); merkleLockupFactory = deployMerkleLockupFactory(); } + + /// @notice Deploys the entire Sablier V2 Protocol from precompiled bytecode. + /// + /// 1. {SablierV2NFTDescriptor} + /// 2. {SablierV2LockupDynamic} + /// 3. {SablierV2LockupLinear} + /// 4. {SablierV2LockupTranched} + /// 5. {SablierV2Batch} + /// 6. {SablierV2MerkleLockupFactory} + function deployProtocol(address initialAdmin) + public + returns ( + ISablierV2LockupDynamic lockupDynamic, + ISablierV2LockupLinear lockupLinear, + ISablierV2LockupTranched lockupTranched, + ISablierV2NFTDescriptor nftDescriptor, + ISablierV2Batch batch, + ISablierV2MerkleLockupFactory merkleLockupFactory + ) + { + // Deploy V2 Core. + (lockupDynamic, lockupLinear, lockupTranched, nftDescriptor) = new V2CorePrecompiles().deployCore(initialAdmin); + + // Deploy V2 Periphery. + (batch, merkleLockupFactory) = deployPeriphery(); + } } diff --git a/shell/update-precompiles.sh b/shell/update-precompiles.sh index 0842169e..4e05f9eb 100755 --- a/shell/update-precompiles.sh +++ b/shell/update-precompiles.sh @@ -15,7 +15,7 @@ FOUNDRY_PROFILE=optimized forge build batch=$(cat out-optimized/SablierV2Batch.sol/SablierV2Batch.json | jq -r '.bytecode.object' | cut -c 3-) merkle_lockup_factory=$(cat out-optimized/SablierV2MerkleLockupFactory.sol/SablierV2MerkleLockupFactory.json | jq -r '.bytecode.object' | cut -c 3-) -precompiles_path="test/utils/Precompiles.sol" +precompiles_path="precompiles/Precompiles.sol" if [ ! -f $precompiles_path ]; then echo "Precompiles file does not exist" exit 1 diff --git a/test/fork/Fork.t.sol b/test/fork/Fork.t.sol index 69ecf8d2..1cda8b71 100644 --- a/test/fork/Fork.t.sol +++ b/test/fork/Fork.t.sol @@ -2,10 +2,10 @@ pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { Precompiles as V2CorePrecompiles } from "@sablier/v2-core/precompiles/Precompiles.sol"; import { ISablierV2LockupDynamic } from "@sablier/v2-core/src/interfaces/ISablierV2LockupDynamic.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; -import { Precompiles as V2CorePrecompiles } from "@sablier/v2-core/test/utils/Precompiles.sol"; import { Fuzzers as V2CoreFuzzers } from "@sablier/v2-core/test/utils/Fuzzers.sol"; diff --git a/test/integration/Integration.t.sol b/test/integration/Integration.t.sol index d26b4044..6cd60157 100644 --- a/test/integration/Integration.t.sol +++ b/test/integration/Integration.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.22 <0.9.0; -import { Precompiles as V2CorePrecompiles } from "@sablier/v2-core/test/utils/Precompiles.sol"; +import { Precompiles as V2CorePrecompiles } from "@sablier/v2-core/precompiles/Precompiles.sol"; import { Defaults } from "../utils/Defaults.sol"; import { Base_Test } from "../Base.t.sol"; diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index 2261efac..0d583cd0 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -29,7 +29,6 @@ contract Defaults is Merkle { uint40 public immutable CLIFF_TIME; uint40 public immutable END_TIME; uint256 public constant ETHER_AMOUNT = 10_000 ether; - uint256 public constant MAX_SEGMENT_COUNT = 1000; uint128 public constant PER_STREAM_AMOUNT = 10_000e18; uint128 public constant REFUND_AMOUNT = 7500e18; // deposit - cliff amount uint40 public immutable START_TIME; diff --git a/test/utils/Precompiles.t.sol b/test/utils/Precompiles.t.sol index 2ca1fe9b..54f57356 100644 --- a/test/utils/Precompiles.t.sol +++ b/test/utils/Precompiles.t.sol @@ -3,11 +3,11 @@ pragma solidity >=0.8.22 <0.9.0; import { LibString } from "solady/src/utils/LibString.sol"; +import { Precompiles } from "../../precompiles/Precompiles.sol"; import { ISablierV2Batch } from "../../src/interfaces/ISablierV2Batch.sol"; import { ISablierV2MerkleLockupFactory } from "../../src/interfaces/ISablierV2MerkleLockupFactory.sol"; import { Base_Test } from "../Base.t.sol"; -import { Precompiles } from "./Precompiles.sol"; contract Precompiles_Test is Base_Test { using LibString for address; From 002cad9fbd49d65b1569e524ec0a2a5729bc0e66 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Thu, 14 Mar 2024 15:28:06 +0000 Subject: [PATCH 27/61] test: include recipient in StreamLT, StreamLL structs (#304) --- bun.lockb | Bin 43329 -> 43329 bytes test/fork/merkle-lockup/MerkleLockupLL.t.sol | 1 + test/fork/merkle-lockup/MerkleLockupLT.t.sol | 1 + .../merkle-lockup/ll/claim/claim.t.sol | 1 + .../merkle-lockup/lt/claim/claim.t.sol | 2 ++ 5 files changed, 5 insertions(+) diff --git a/bun.lockb b/bun.lockb index c5827cb058ea5fa8e53f7505bb2cbe3daabdfc59..b2ffc73d237bdf968467623057acc153b1dfb5ee 100755 GIT binary patch delta 66 zcmV-I0KNag(gMNK0+22s3f+tDgR^Y<)VBR@mBrrH)dmIlTnBqlaQzcvpk3eA( Date: Sat, 16 Mar 2024 22:12:36 +0000 Subject: [PATCH 28/61] chore: rename custom-errors to gas-custom-errors in solhint --- .solhint.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.solhint.json b/.solhint.json index c73668ef..2e1b56a5 100644 --- a/.solhint.json +++ b/.solhint.json @@ -6,9 +6,9 @@ "compiler-version": ["error", ">=0.8.22"], "contract-name-camelcase": "off", "const-name-snakecase": "off", - "custom-errors": "off", "func-name-mixedcase": "off", "func-visibility": ["error", { "ignoreConstructors": true }], + "gas-custom-errors": "off", "max-line-length": ["error", 123], "named-parameters-mapping": "warn", "no-empty-blocks": "off", From c16396e06938a8b45ab0e4d08f4ca5554f469f89 Mon Sep 17 00:00:00 2001 From: Paul Razvan Berg Date: Wed, 20 Mar 2024 19:13:21 +0000 Subject: [PATCH 29/61] docs: update security assumptions --- SECURITY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/SECURITY.md b/SECURITY.md index 70c9e3ba..aff203e8 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -47,6 +47,7 @@ vulnerability, it must adhere to these assumptions as well: - [All assumptions](https://github.com/sablier-labs/v2-core/blob/main/SECURITY.md) in Sablier V2 Core apply to Sablier V2 Periphery as well. +- In `SablierV2MerkleLockupLT`, the tranche unlock percentages and the durations will be same for all airdrop claimers. ### Rewards From 73831c7dcaa5ec4e2fed6caa0f8040154e53030a Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Thu, 21 Mar 2024 14:39:04 +0000 Subject: [PATCH 30/61] Integrate sphinx for deployment (#305) * build: integrate sphinx for deployment * chore: add SPHINX_PROJECT_NAME in .env.example * build: add base chain in foundry endpoints * build: update lockfile --------- Co-authored-by: andreivladbrg --- .env.example | 4 +++ bun.lockb | Bin 43329 -> 308439 bytes foundry.toml | 13 +++++-- package.json | 1 + remappings.txt | 3 +- script/Base.s.sol | 32 ++++++++++++++--- script/CreateMerkleLockupLL.s.sol | 27 +++++++++++++- script/CreateMerkleLockupLT.s.sol | 27 +++++++++++++- script/DeployBatch.t.sol | 12 ++++++- script/DeployDeterministicBatch.s.sol | 12 ++++++- script/DeployDeterministicPeriphery.s.sol | 17 ++++++++- script/DeployMerkleLockupFactory.s.sol | 12 ++++++- script/DeployPeriphery.s.sol | 22 +++++++++++- script/DeployProtocol.s.sol | 42 +++++++++++++++++++++- 14 files changed, 207 insertions(+), 17 deletions(-) diff --git a/.env.example b/.env.example index 5c5efc6e..5a628011 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,8 @@ export API_KEY_INFURA="YOUR_API_KEY_INFURA" +export EOA="YOUR_EOA_ADDRESS" export FOUNDRY_PROFILE="lite" export MNEMONIC="YOUR_MNEMONIC" export RPC_URL_MAINNET="YOUR_RPC_URL_MAINNET" +export SPHINX_API_KEY="YOUR_API_KEY" +export SPHINX_ORG_ID="YOUR_ORG_ID" +export SPHINX_PROJECT_NAME="YOUR_PROJECT_NAME" diff --git a/bun.lockb b/bun.lockb index b2ffc73d237bdf968467623057acc153b1dfb5ee..26ce36de77f38f81f1e6b2f9189721d3028a9533 100755 GIT binary patch literal 308439 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%0DDzdA{^=vb)O?J(s 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 Bi~axr diff --git a/foundry.toml b/foundry.toml index 910202c1..e984add9 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,8 +3,13 @@ block_timestamp = 1_682_899_200 # May 1, 2023 at 00:00 GMT bytecode_hash = "none" evm_version = "paris" + extra_output = ['storageLayout'] ffi = true - fs_permissions = [{ access = "read", path = "out-optimized" }] + fs_permissions = [ + { access = "read", path = "out-optimized" }, + { access = "read", path = "./out" }, + { access = "read-write", path = "./cache" } + ] gas_reports = [ "SablierV2Batch", "SablierV2MerkleLockupFactory", @@ -56,9 +61,11 @@ [rpc_endpoints] arbitrum = "https://arbitrum-mainnet.infura.io/v3/${API_KEY_INFURA}" avalanche = "https://avalanche-mainnet.infura.io/v3/${API_KEY_INFURA}" - bnb_smart_chain = "https://bsc-dataseed.binance.org" - gnosis_chain = "https://rpc.gnosischain.com" + base = "https://mainnet.base.org" + bnb = "https://bsc-dataseed.binance.org" + gnosis = "https://rpc.gnosischain.com" localhost = "http://localhost:8545" + ethereum = "${RPC_URL_MAINNET}" mainnet = "${RPC_URL_MAINNET}" optimism = "https://optimism-mainnet.infura.io/v3/${API_KEY_INFURA}" polygon = "https://polygon-mainnet.infura.io/v3/${API_KEY_INFURA}" diff --git a/package.json b/package.json index 3f18eaf7..c88a1b05 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ }, "devDependencies": { "@prb/test": "0.6.4", + "@sphinx-labs/plugins": "^0.30.6", "forge-std": "github:foundry-rs/forge-std#v1.5.6", "prettier": "^2.8.8", "solady": "0.0.129", diff --git a/remappings.txt b/remappings.txt index e5aca86c..fcc102b1 100644 --- a/remappings.txt +++ b/remappings.txt @@ -2,5 +2,6 @@ @prb/math/=node_modules/@prb/math/ @prb/test/=node_modules/@prb/test/ @sablier/v2-core/=node_modules/@sablier/v2-core/ +@sphinx-labs/contracts/=node_modules/@sphinx-labs/contracts/contracts/foundry forge-std/=node_modules/forge-std/ -solady/=node_modules/solady/ \ No newline at end of file +solady/=node_modules/solady/ diff --git a/script/Base.s.sol b/script/Base.s.sol index 4b5f36ee..fa871c79 100644 --- a/script/Base.s.sol +++ b/script/Base.s.sol @@ -3,40 +3,49 @@ pragma solidity >=0.8.22 <0.9.0; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; +import { Sphinx } from "@sphinx-labs/contracts/SphinxPlugin.sol"; import { console2 } from "forge-std/src/console2.sol"; import { Script } from "forge-std/src/Script.sol"; -contract BaseScript is Script { +contract BaseScript is Script, Sphinx { using Strings for uint256; /// @dev Included to enable compilation of the script without a $MNEMONIC environment variable. string internal constant TEST_MNEMONIC = "test test test test test test test test test test test junk"; + /// @dev The default project name for the Sphinx plugin. + string internal constant SPHINX_PROJECT_NAME = "test-test"; + /// @dev Needed for the deterministic deployments. bytes32 internal constant ZERO_SALT = bytes32(0); /// @dev The address of the transaction broadcaster. address internal broadcaster; - /// @dev Used to derive the broadcaster's address if $ETH_FROM is not defined. + /// @dev Used to derive the broadcaster's address if $EOA is not defined. string internal mnemonic; + /// @dev The project name for the Sphinx plugin. + string internal sphinxProjectName; + /// @dev Initializes the transaction broadcaster like this: /// - /// - If $ETH_FROM is defined, use it. + /// - If $EOA is defined, use it. /// - Otherwise, derive the broadcaster address from $MNEMONIC. /// - If $MNEMONIC is not defined, default to a test mnemonic. + /// - If $SPHINX_PROJECT_NAME is not defined, default to a test project name. /// - /// The use case for $ETH_FROM is to specify the broadcaster key and its address via the command line. + /// The use case for $EOA is to specify the broadcaster key and its address via the command line. constructor() { - address from = vm.envOr({ name: "ETH_FROM", defaultValue: address(0) }); + address from = vm.envOr({ name: "EOA", defaultValue: address(0) }); if (from != address(0)) { broadcaster = from; } else { mnemonic = vm.envOr({ name: "MNEMONIC", defaultValue: TEST_MNEMONIC }); (broadcaster,) = deriveRememberKey({ mnemonic: mnemonic, index: 0 }); } + sphinxProjectName = vm.envOr({ name: "SPHINX_PROJECT_NAME", defaultValue: SPHINX_PROJECT_NAME }); } modifier broadcast() { @@ -45,6 +54,19 @@ contract BaseScript is Script { vm.stopBroadcast(); } + /// @dev Configures the Sphinx plugin to use Sphinx managed deployment for smart contracts. + /// Refer to https://github.com/sphinx-labs/sphinx/tree/main/docs. + /// CLI example: + /// - bun sphinx propose script/DeployBatch.s.sol --networks testnets --sig "runSphinx()" + function configureSphinx() public override { + sphinxConfig.mainnets = ["arbitrum", "avalanche", "base", "bnb", "gnosis", "ethereum", "optimism", "polygon"]; + sphinxConfig.orgId = vm.envOr({ name: "SPHINX_ORG_ID", defaultValue: TEST_MNEMONIC }); + sphinxConfig.owners = [broadcaster]; + sphinxConfig.projectName = sphinxProjectName; + sphinxConfig.testnets = ["sepolia"]; + sphinxConfig.threshold = 1; + } + /// @dev The presence of the salt instructs Forge to deploy contracts via this deterministic CREATE2 factory: /// https://github.com/Arachnid/deterministic-deployment-proxy /// diff --git a/script/CreateMerkleLockupLL.s.sol b/script/CreateMerkleLockupLL.s.sol index 0ff2339c..2a8a4bf8 100644 --- a/script/CreateMerkleLockupLL.s.sol +++ b/script/CreateMerkleLockupLL.s.sol @@ -19,13 +19,38 @@ contract CreateMerkleLockupLL is BaseScript { uint256 recipientsCount; } - function run( + /// @dev Deploy using Forge CLI. + function runBroadcast( ISablierV2MerkleLockupFactory merkleLockupFactory, Params calldata params ) public + virtual broadcast returns (ISablierV2MerkleLockupLL merkleLockupLL) + { + merkleLockupLL = _run(merkleLockupFactory, params); + } + + /// @dev Deploy using Sphinx CLI. + function runSphinx( + ISablierV2MerkleLockupFactory merkleLockupFactory, + Params calldata params + ) + public + virtual + sphinx + returns (ISablierV2MerkleLockupLL merkleLockupLL) + { + merkleLockupLL = _run(merkleLockupFactory, params); + } + + function _run( + ISablierV2MerkleLockupFactory merkleLockupFactory, + Params calldata params + ) + internal + returns (ISablierV2MerkleLockupLL merkleLockupLL) { merkleLockupLL = merkleLockupFactory.createMerkleLockupLL( params.baseParams, diff --git a/script/CreateMerkleLockupLT.s.sol b/script/CreateMerkleLockupLT.s.sol index a5c12c7d..4e028621 100644 --- a/script/CreateMerkleLockupLT.s.sol +++ b/script/CreateMerkleLockupLT.s.sol @@ -18,13 +18,38 @@ contract CreateMerkleLockupLT is BaseScript { uint256 recipientsCount; } - function run( + /// @dev Deploy using Forge CLI. + function runBroadcast( ISablierV2MerkleLockupFactory merkleLockupFactory, Params calldata params ) public + virtual broadcast returns (ISablierV2MerkleLockupLT merkleLockupLT) + { + merkleLockupLT = _run(merkleLockupFactory, params); + } + + /// @dev Deploy using Sphinx CLI. + function runSphinx( + ISablierV2MerkleLockupFactory merkleLockupFactory, + Params calldata params + ) + public + virtual + sphinx + returns (ISablierV2MerkleLockupLT merkleLockupLT) + { + merkleLockupLT = _run(merkleLockupFactory, params); + } + + function _run( + ISablierV2MerkleLockupFactory merkleLockupFactory, + Params calldata params + ) + internal + returns (ISablierV2MerkleLockupLT merkleLockupLT) { merkleLockupLT = merkleLockupFactory.createMerkleLockupLT( params.baseParams, diff --git a/script/DeployBatch.t.sol b/script/DeployBatch.t.sol index bbf92056..0a32ebc5 100644 --- a/script/DeployBatch.t.sol +++ b/script/DeployBatch.t.sol @@ -6,7 +6,17 @@ import { BaseScript } from "./Base.s.sol"; import { SablierV2Batch } from "../src/SablierV2Batch.sol"; contract DeployBatch is BaseScript { - function run() public broadcast returns (SablierV2Batch batch) { + /// @dev Deploy using Forge CLI. + function runBroadcast() public virtual broadcast returns (SablierV2Batch batch) { + batch = _run(); + } + + /// @dev Deploy using Sphinx CLI. + function runSphinx() public virtual sphinx returns (SablierV2Batch batch) { + batch = _run(); + } + + function _run() internal returns (SablierV2Batch batch) { batch = new SablierV2Batch(); } } diff --git a/script/DeployDeterministicBatch.s.sol b/script/DeployDeterministicBatch.s.sol index 2f953528..632ca4de 100644 --- a/script/DeployDeterministicBatch.s.sol +++ b/script/DeployDeterministicBatch.s.sol @@ -8,7 +8,17 @@ import { SablierV2Batch } from "../src/SablierV2Batch.sol"; /// @notice Deploys {SablierV2Batch} at a deterministic address across chains. /// @dev Reverts if the contract has already been deployed. contract DeployDeterministicBatch is BaseScript { - function run() public virtual broadcast returns (SablierV2Batch batch) { + /// @dev Deploy using Forge CLI. + function runBroadcast() public virtual broadcast returns (SablierV2Batch batch) { + batch = _run(); + } + + /// @dev Deploy using Sphinx CLI. + function runSphinx() public virtual sphinx returns (SablierV2Batch batch) { + batch = _run(); + } + + function _run() internal returns (SablierV2Batch batch) { bytes32 salt = constructCreate2Salt(); batch = new SablierV2Batch{ salt: salt }(); } diff --git a/script/DeployDeterministicPeriphery.s.sol b/script/DeployDeterministicPeriphery.s.sol index 0720c382..70b17bb1 100644 --- a/script/DeployDeterministicPeriphery.s.sol +++ b/script/DeployDeterministicPeriphery.s.sol @@ -13,12 +13,27 @@ import { SablierV2MerkleLockupFactory } from "../src/SablierV2MerkleLockupFactor /// /// @dev Reverts if any contract has already been deployed. contract DeployDeterministicPeriphery is BaseScript { - function run() + /// @dev Deploy using Forge CLI. + function runBroadcast() public virtual broadcast returns (SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory) { + (batch, merkleLockupFactory) = _run(); + } + + /// @dev Deploy using Sphinx CLI. + function runSphinx() + public + virtual + sphinx + returns (SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory) + { + (batch, merkleLockupFactory) = _run(); + } + + function _run() internal returns (SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory) { bytes32 salt = constructCreate2Salt(); batch = new SablierV2Batch{ salt: salt }(); merkleLockupFactory = new SablierV2MerkleLockupFactory{ salt: salt }(); diff --git a/script/DeployMerkleLockupFactory.s.sol b/script/DeployMerkleLockupFactory.s.sol index 0b0864e7..d0a90429 100644 --- a/script/DeployMerkleLockupFactory.s.sol +++ b/script/DeployMerkleLockupFactory.s.sol @@ -6,7 +6,17 @@ import { BaseScript } from "./Base.s.sol"; import { SablierV2MerkleLockupFactory } from "../src/SablierV2MerkleLockupFactory.sol"; contract DeployMerkleLockupFactory is BaseScript { - function run() public broadcast returns (SablierV2MerkleLockupFactory merkleLockupFactory) { + /// @dev Deploy using Forge CLI. + function runBroadcast() public virtual broadcast returns (SablierV2MerkleLockupFactory merkleLockupFactory) { + merkleLockupFactory = _run(); + } + + /// @dev Deploy using Sphinx CLI. + function runSphinx() public virtual sphinx returns (SablierV2MerkleLockupFactory merkleLockupFactory) { + merkleLockupFactory = _run(); + } + + function _run() internal returns (SablierV2MerkleLockupFactory merkleLockupFactory) { merkleLockupFactory = new SablierV2MerkleLockupFactory(); } } diff --git a/script/DeployPeriphery.s.sol b/script/DeployPeriphery.s.sol index 375d1099..a8576c1d 100644 --- a/script/DeployPeriphery.s.sol +++ b/script/DeployPeriphery.s.sol @@ -11,7 +11,27 @@ import { SablierV2Batch } from "../src/SablierV2Batch.sol"; /// 1. {SablierV2Batch} /// 2. {SablierV2MerkleLockupFactory} contract DeployPeriphery is BaseScript { - function run() public broadcast returns (SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory) { + /// @dev Deploy using Forge CLI. + function runBroadcast() + public + virtual + broadcast + returns (SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory) + { + (batch, merkleLockupFactory) = _run(); + } + + /// @dev Deploy using Sphinx CLI. + function runSphinx() + public + virtual + sphinx + returns (SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory) + { + (batch, merkleLockupFactory) = _run(); + } + + function _run() internal returns (SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory) { batch = new SablierV2Batch(); merkleLockupFactory = new SablierV2MerkleLockupFactory(); } diff --git a/script/DeployProtocol.s.sol b/script/DeployProtocol.s.sol index 0e8741e4..27df1d40 100644 --- a/script/DeployProtocol.s.sol +++ b/script/DeployProtocol.s.sol @@ -12,7 +12,8 @@ import { SablierV2Batch } from "../src/SablierV2Batch.sol"; /// @notice Deploys the Sablier V2 Protocol. contract DeployProtocol is BaseScript { - function run( + /// @dev Deploy using Forge CLI. + function runBroadcast( address initialAdmin, uint256 maxCount ) @@ -27,6 +28,45 @@ contract DeployProtocol is BaseScript { SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory ) + { + (lockupDynamic, lockupLinear, lockupTranched, nftDescriptor, batch, merkleLockupFactory) = + _run(initialAdmin, maxCount); + } + + /// @dev Deploy using Sphinx CLI. + function runSphinx( + address initialAdmin, + uint256 maxCount + ) + public + virtual + sphinx + returns ( + SablierV2LockupDynamic lockupDynamic, + SablierV2LockupLinear lockupLinear, + SablierV2LockupTranched lockupTranched, + SablierV2NFTDescriptor nftDescriptor, + SablierV2Batch batch, + SablierV2MerkleLockupFactory merkleLockupFactory + ) + { + (lockupDynamic, lockupLinear, lockupTranched, nftDescriptor, batch, merkleLockupFactory) = + _run(initialAdmin, maxCount); + } + + function _run( + address initialAdmin, + uint256 maxCount + ) + internal + returns ( + SablierV2LockupDynamic lockupDynamic, + SablierV2LockupLinear lockupLinear, + SablierV2LockupTranched lockupTranched, + SablierV2NFTDescriptor nftDescriptor, + SablierV2Batch batch, + SablierV2MerkleLockupFactory merkleLockupFactory + ) { // Deploy V2 Core. nftDescriptor = new SablierV2NFTDescriptor(); From a3131838ec731b38b1e2e03735fba874ab66f5e2 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Thu, 28 Mar 2024 18:06:19 +0000 Subject: [PATCH 31/61] Update forge-std to v1.8.1 (#307) * build: update forge-std to v1.8.1 build: remove @prb/test dep test: rename `timestamp` to `newTimestamp` in `vm.warp` test: declare some functions as `pure` and `view` test: use StdAssertions log events test: inherit V2 core's Constants.sol in Base_Test test: test(refactor): rename log names in Assertions * test: remove unnecessary import * build: add commit hash to prb-math dep * chore: use short commit hash * test: deprecate changePrank in favor of resetPrank --------- Co-authored-by: andreivladbrg --- bun.lockb | Bin 308439 -> 308798 bytes package.json | 5 ++--- remappings.txt | 1 - test/Base.t.sol | 19 ++++++++++++++++-- test/fork/merkle-lockup/MerkleLockupLL.t.sol | 4 ++-- test/fork/merkle-lockup/MerkleLockupLT.t.sol | 4 ++-- .../merkle-lockup/MerkleLockup.t.sol | 16 +++++++-------- .../merkle-lockup/ll/claim/claim.t.sol | 2 +- .../merkle-lockup/ll/clawback/clawback.t.sol | 6 +++--- .../ll/has-expired/hasExpired.t.sol | 6 +++--- .../merkle-lockup/lt/claim/claim.t.sol | 2 +- .../merkle-lockup/lt/clawback/clawback.t.sol | 6 +++--- .../lt/has-expired/hasExpired.t.sol | 6 +++--- test/utils/Assertions.sol | 11 +++++----- test/utils/BaseScript.t.sol | 4 ++-- test/utils/MerkleBuilder.t.sol | 8 ++++---- test/utils/Precompiles.t.sol | 5 ----- 17 files changed, 56 insertions(+), 49 deletions(-) diff --git a/bun.lockb b/bun.lockb index 26ce36de77f38f81f1e6b2f9189721d3028a9533..0576556979089cf339ff7187da2a5bb911bee43b 100755 GIT binary patch delta 60696 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.8.22; import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; import { ISablierV2MerkleLockup } from "./ISablierV2MerkleLockup.sol"; -import { MerkleLockupLT } from "./../types/DataTypes.sol"; +import { MerkleLT } from "./../types/DataTypes.sol"; -/// @title ISablierV2MerkleLockupLT +/// @title ISablierV2MerkleLT /// @notice MerkleLockup campaign that creates LockupTranched streams. -interface ISablierV2MerkleLockupLT is ISablierV2MerkleLockup { +interface ISablierV2MerkleLT is ISablierV2MerkleLockup { /*////////////////////////////////////////////////////////////////////////// CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ /// @notice Retrieves the tranches with their respective unlock percentages and durations. - function getTranchesWithPercentages() external view returns (MerkleLockupLT.TrancheWithPercentage[] memory); + function getTranchesWithPercentages() external view returns (MerkleLT.TrancheWithPercentage[] memory); /// @notice The address of the {SablierV2LockupTranched} contract. function LOCKUP_TRANCHED() external view returns (ISablierV2LockupTranched); diff --git a/src/interfaces/ISablierV2MerkleLockupFactory.sol b/src/interfaces/ISablierV2MerkleLockupFactory.sol index 6f09f0bc..29b84d2a 100644 --- a/src/interfaces/ISablierV2MerkleLockupFactory.sol +++ b/src/interfaces/ISablierV2MerkleLockupFactory.sol @@ -5,9 +5,9 @@ import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablier import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { ISablierV2MerkleLockupLL } from "./ISablierV2MerkleLockupLL.sol"; -import { ISablierV2MerkleLockupLT } from "./ISablierV2MerkleLockupLT.sol"; -import { MerkleLockup, MerkleLockupLT } from "../types/DataTypes.sol"; +import { ISablierV2MerkleLL } from "./ISablierV2MerkleLL.sol"; +import { ISablierV2MerkleLT } from "./ISablierV2MerkleLT.sol"; +import { MerkleLockup, MerkleLT } from "../types/DataTypes.sol"; /// @title ISablierV2MerkleLockupFactory /// @notice Deploys MerkleLockup campaigns with CREATE2. @@ -16,9 +16,9 @@ interface ISablierV2MerkleLockupFactory { EVENTS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Emitted when a {SablierV2MerkleLockupLL} campaign is created. - event CreateMerkleLockupLL( - ISablierV2MerkleLockupLL indexed merkleLockupLL, + /// @notice Emitted when a {SablierV2MerkleLL} campaign is created. + event CreateMerkleLL( + ISablierV2MerkleLL indexed merkleLL, MerkleLockup.ConstructorParams baseParams, ISablierV2LockupLinear lockupLinear, LockupLinear.Durations streamDurations, @@ -26,12 +26,12 @@ interface ISablierV2MerkleLockupFactory { uint256 recipientCount ); - /// @notice Emitted when a {SablierV2MerkleLockupLT} campaign is created. - event CreateMerkleLockupLT( - ISablierV2MerkleLockupLT indexed merkleLockupLT, + /// @notice Emitted when a {SablierV2MerkleLT} campaign is created. + event CreateMerkleLT( + ISablierV2MerkleLT indexed merkleLT, MerkleLockup.ConstructorParams baseParams, ISablierV2LockupTranched lockupTranched, - MerkleLockupLT.TrancheWithPercentage[] tranchesWithPercentages, + MerkleLT.TrancheWithPercentage[] tranchesWithPercentages, uint256 totalDuration, uint256 aggregateAmount, uint256 recipientCount @@ -42,15 +42,15 @@ interface ISablierV2MerkleLockupFactory { //////////////////////////////////////////////////////////////////////////*/ /// @notice Creates a new MerkleLockup campaign with a LockupLinear distribution. - /// @dev Emits a {CreateMerkleLockupLL} event. + /// @dev Emits a {CreateMerkleLL} event. /// @param baseParams Struct encapsulating the {SablierV2MerkleLockup} parameters, which are documented in /// {DataTypes}. /// @param lockupLinear The address of the {SablierV2LockupLinear} contract. /// @param streamDurations The durations for each stream. /// @param aggregateAmount The total amount of ERC-20 assets to be distributed to all recipients. /// @param recipientCount The total number of recipients who are eligible to claim. - /// @return merkleLockupLL The address of the newly created MerkleLockup contract. - function createMerkleLockupLL( + /// @return merkleLL The address of the newly created MerkleLockup contract. + function createMerkleLL( MerkleLockup.ConstructorParams memory baseParams, ISablierV2LockupLinear lockupLinear, LockupLinear.Durations memory streamDurations, @@ -58,10 +58,10 @@ interface ISablierV2MerkleLockupFactory { uint256 recipientCount ) external - returns (ISablierV2MerkleLockupLL merkleLockupLL); + returns (ISablierV2MerkleLL merkleLL); /// @notice Creates a new MerkleLockup campaign with a LockupTranched distribution. - /// @dev Emits a {CreateMerkleLockupLT} event. + /// @dev Emits a {CreateMerkleLT} event. /// /// Requirements: /// - The sum of the tranches' unlock percentages must equal 100% = 1e18. @@ -72,14 +72,14 @@ interface ISablierV2MerkleLockupFactory { /// @param tranchesWithPercentages The tranches with their respective unlock percentages. /// @param aggregateAmount The total amount of ERC-20 assets to be distributed to all recipients. /// @param recipientCount The total number of recipients who are eligible to claim. - /// @return merkleLockupLT The address of the newly created MerkleLockup contract. - function createMerkleLockupLT( + /// @return merkleLT The address of the newly created MerkleLockup contract. + function createMerkleLT( MerkleLockup.ConstructorParams memory baseParams, ISablierV2LockupTranched lockupTranched, - MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages, + MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages, uint256 aggregateAmount, uint256 recipientCount ) external - returns (ISablierV2MerkleLockupLT merkleLockupLT); + returns (ISablierV2MerkleLT merkleLT); } diff --git a/src/types/DataTypes.sol b/src/types/DataTypes.sol index dd9bcbfb..91ca4c1d 100644 --- a/src/types/DataTypes.sol +++ b/src/types/DataTypes.sol @@ -110,7 +110,7 @@ library MerkleLockup { } } -library MerkleLockupLT { +library MerkleLT { /// @notice Struct encapsulating the unlock percentage and duration of a tranche. /// @dev Since users may have different amounts allocated, this struct makes it possible to calculate the amounts /// at claim time. An 18-decimal format is used to represent percentages: 100% = 1e18. For more information, see diff --git a/test/Base.t.sol b/test/Base.t.sol index 290c95ba..d6c826ea 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -14,13 +14,13 @@ import { Constants as V2CoreConstants } from "@sablier/v2-core/test/utils/Consta import { Utils as V2CoreUtils } from "@sablier/v2-core/test/utils/Utils.sol"; import { ISablierV2Batch } from "src/interfaces/ISablierV2Batch.sol"; +import { ISablierV2MerkleLL } from "src/interfaces/ISablierV2MerkleLL.sol"; import { ISablierV2MerkleLockupFactory } from "src/interfaces/ISablierV2MerkleLockupFactory.sol"; -import { ISablierV2MerkleLockupLL } from "src/interfaces/ISablierV2MerkleLockupLL.sol"; -import { ISablierV2MerkleLockupLT } from "src/interfaces/ISablierV2MerkleLockupLT.sol"; +import { ISablierV2MerkleLT } from "src/interfaces/ISablierV2MerkleLT.sol"; import { SablierV2Batch } from "src/SablierV2Batch.sol"; +import { SablierV2MerkleLL } from "src/SablierV2MerkleLL.sol"; import { SablierV2MerkleLockupFactory } from "src/SablierV2MerkleLockupFactory.sol"; -import { SablierV2MerkleLockupLL } from "src/SablierV2MerkleLockupLL.sol"; -import { SablierV2MerkleLockupLT } from "src/SablierV2MerkleLockupLT.sol"; +import { SablierV2MerkleLT } from "src/SablierV2MerkleLT.sol"; import { ERC20Mock } from "./mocks/erc20/ERC20Mock.sol"; import { Assertions } from "./utils/Assertions.sol"; @@ -57,8 +57,8 @@ abstract contract Base_Test is ISablierV2LockupLinear internal lockupLinear; ISablierV2LockupTranched internal lockupTranched; ISablierV2MerkleLockupFactory internal merkleLockupFactory; - ISablierV2MerkleLockupLL internal merkleLockupLL; - ISablierV2MerkleLockupLT internal merkleLockupLT; + ISablierV2MerkleLL internal merkleLL; + ISablierV2MerkleLT internal merkleLT; /*////////////////////////////////////////////////////////////////////////// SET-UP FUNCTION @@ -116,9 +116,9 @@ abstract contract Base_Test is vm.label({ account: address(lockupDynamic), newLabel: "LockupDynamic" }); vm.label({ account: address(lockupLinear), newLabel: "LockupLinear" }); vm.label({ account: address(lockupTranched), newLabel: "LockupTranched" }); + vm.label({ account: address(merkleLL), newLabel: "MerkleLL" }); vm.label({ account: address(merkleLockupFactory), newLabel: "MerkleLockupFactory" }); - vm.label({ account: address(merkleLockupLL), newLabel: "MerkleLockupLL" }); - vm.label({ account: address(merkleLockupLT), newLabel: "MerkleLockupLT" }); + vm.label({ account: address(merkleLT), newLabel: "MerkleLT" }); } /*////////////////////////////////////////////////////////////////////////// @@ -262,7 +262,7 @@ abstract contract Base_Test is MERKLE-LOCKUP //////////////////////////////////////////////////////////////////////////*/ - function computeMerkleLockupLLAddress( + function computeMerkleLLAddress( address admin, bytes32 merkleRoot, uint40 expiration @@ -271,10 +271,10 @@ abstract contract Base_Test is view returns (address) { - return computeMerkleLockupLLAddress(admin, dai, merkleRoot, expiration); + return computeMerkleLLAddress(admin, dai, merkleRoot, expiration); } - function computeMerkleLockupLLAddress( + function computeMerkleLLAddress( address admin, IERC20 asset_, bytes32 merkleRoot, @@ -298,7 +298,7 @@ abstract contract Base_Test is abi.encode(defaults.durations()) ) ); - bytes32 creationBytecodeHash = keccak256(getMerkleLockupLLBytecode(admin, asset_, merkleRoot, expiration)); + bytes32 creationBytecodeHash = keccak256(getMerkleLLBytecode(admin, asset_, merkleRoot, expiration)); return computeCreate2Address({ salt: salt, initcodeHash: creationBytecodeHash, @@ -306,7 +306,7 @@ abstract contract Base_Test is }); } - function computeMerkleLockupLTAddress( + function computeMerkleLTAddress( address admin, bytes32 merkleRoot, uint40 expiration @@ -315,10 +315,10 @@ abstract contract Base_Test is view returns (address) { - return computeMerkleLockupLTAddress(admin, dai, merkleRoot, expiration); + return computeMerkleLTAddress(admin, dai, merkleRoot, expiration); } - function computeMerkleLockupLTAddress( + function computeMerkleLTAddress( address admin, IERC20 asset_, bytes32 merkleRoot, @@ -342,7 +342,7 @@ abstract contract Base_Test is abi.encode(defaults.tranchesWithPercentages()) ) ); - bytes32 creationBytecodeHash = keccak256(getMerkleLockupLTBytecode(admin, asset_, merkleRoot, expiration)); + bytes32 creationBytecodeHash = keccak256(getMerkleLTBytecode(admin, asset_, merkleRoot, expiration)); return computeCreate2Address({ salt: salt, initcodeHash: creationBytecodeHash, @@ -350,7 +350,7 @@ abstract contract Base_Test is }); } - function getMerkleLockupLLBytecode( + function getMerkleLLBytecode( address admin, IERC20 asset_, bytes32 merkleRoot, @@ -363,15 +363,14 @@ abstract contract Base_Test is bytes memory constructorArgs = abi.encode(defaults.baseParams(admin, asset_, merkleRoot, expiration), lockupLinear, defaults.durations()); if (!isTestOptimizedProfile()) { - return bytes.concat(type(SablierV2MerkleLockupLL).creationCode, constructorArgs); + return bytes.concat(type(SablierV2MerkleLL).creationCode, constructorArgs); } else { - return bytes.concat( - vm.getCode("out-optimized/SablierV2MerkleLockupLL.sol/SablierV2MerkleLockupLL.json"), constructorArgs - ); + return + bytes.concat(vm.getCode("out-optimized/SablierV2MerkleLL.sol/SablierV2MerkleLL.json"), constructorArgs); } } - function getMerkleLockupLTBytecode( + function getMerkleLTBytecode( address admin, IERC20 asset_, bytes32 merkleRoot, @@ -387,11 +386,10 @@ abstract contract Base_Test is defaults.tranchesWithPercentages() ); if (!isTestOptimizedProfile()) { - return bytes.concat(type(SablierV2MerkleLockupLT).creationCode, constructorArgs); + return bytes.concat(type(SablierV2MerkleLT).creationCode, constructorArgs); } else { - return bytes.concat( - vm.getCode("out-optimized/SablierV2MerkleLockupLT.sol/SablierV2MerkleLockupLT.json"), constructorArgs - ); + return + bytes.concat(vm.getCode("out-optimized/SablierV2MerkleLT.sol/SablierV2MerkleLT.json"), constructorArgs); } } } diff --git a/test/fork/assets/USDC.t.sol b/test/fork/assets/USDC.t.sol index 342d7f56..5589fb15 100644 --- a/test/fork/assets/USDC.t.sol +++ b/test/fork/assets/USDC.t.sol @@ -6,8 +6,8 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { CreateWithTimestamps_LockupDynamic_Batch_Fork_Test } from "../batch/createWithTimestampsLD.t.sol"; import { CreateWithTimestamps_LockupLinear_Batch_Fork_Test } from "../batch/createWithTimestampsLL.t.sol"; import { CreateWithTimestamps_LockupTranched_Batch_Fork_Test } from "../batch/createWithTimestampsLT.t.sol"; -import { MerkleLockupLL_Fork_Test } from "../merkle-lockup/MerkleLockupLL.t.sol"; -import { MerkleLockupLT_Fork_Test } from "../merkle-lockup/MerkleLockupLT.t.sol"; +import { MerkleLL_Fork_Test } from "../merkle-lockup/MerkleLL.t.sol"; +import { MerkleLT_Fork_Test } from "../merkle-lockup/MerkleLT.t.sol"; /// @dev An ERC-20 asset with 6 decimals. IERC20 constant usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); @@ -24,6 +24,6 @@ contract USDC_CreateWithTimestamps_LockupTranched_Batch_Fork_Test is CreateWithTimestamps_LockupTranched_Batch_Fork_Test(usdc) { } -contract USDC_MerkleLockupLL_Fork_Test is MerkleLockupLL_Fork_Test(usdc) { } +contract USDC_MerkleLL_Fork_Test is MerkleLL_Fork_Test(usdc) { } -contract USDC_MerkleLockupLT_Fork_Test is MerkleLockupLT_Fork_Test(usdc) { } +contract USDC_MerkleLT_Fork_Test is MerkleLT_Fork_Test(usdc) { } diff --git a/test/fork/assets/USDT.t.sol b/test/fork/assets/USDT.t.sol index 75c4434e..de70935f 100644 --- a/test/fork/assets/USDT.t.sol +++ b/test/fork/assets/USDT.t.sol @@ -6,8 +6,8 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { CreateWithTimestamps_LockupDynamic_Batch_Fork_Test } from "../batch/createWithTimestampsLD.t.sol"; import { CreateWithTimestamps_LockupLinear_Batch_Fork_Test } from "../batch/createWithTimestampsLL.t.sol"; import { CreateWithTimestamps_LockupTranched_Batch_Fork_Test } from "../batch/createWithTimestampsLT.t.sol"; -import { MerkleLockupLL_Fork_Test } from "../merkle-lockup/MerkleLockupLL.t.sol"; -import { MerkleLockupLT_Fork_Test } from "../merkle-lockup/MerkleLockupLT.t.sol"; +import { MerkleLL_Fork_Test } from "../merkle-lockup/MerkleLL.t.sol"; +import { MerkleLT_Fork_Test } from "../merkle-lockup/MerkleLT.t.sol"; /// @dev An ERC-20 asset that suffers from the missing return value bug. IERC20 constant usdt = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7); @@ -24,6 +24,6 @@ contract USDT_CreateWithTimestamps_LockupTranched_Batch_Fork_Test is CreateWithTimestamps_LockupTranched_Batch_Fork_Test(usdt) { } -contract USDT_MerkleLockupLL_Fork_Test is MerkleLockupLL_Fork_Test(usdt) { } +contract USDT_MerkleLL_Fork_Test is MerkleLL_Fork_Test(usdt) { } -contract USDT_MerkleLockupLT_Fork_Test is MerkleLockupLT_Fork_Test(usdt) { } +contract USDT_MerkleLT_Fork_Test is MerkleLT_Fork_Test(usdt) { } diff --git a/test/fork/merkle-lockup/MerkleLockupLL.t.sol b/test/fork/merkle-lockup/MerkleLL.t.sol similarity index 83% rename from test/fork/merkle-lockup/MerkleLockupLL.t.sol rename to test/fork/merkle-lockup/MerkleLL.t.sol index cea3dd79..1cad3e13 100644 --- a/test/fork/merkle-lockup/MerkleLockupLL.t.sol +++ b/test/fork/merkle-lockup/MerkleLL.t.sol @@ -5,13 +5,13 @@ import { Arrays } from "@openzeppelin/contracts/utils/Arrays.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { Lockup, LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { ISablierV2MerkleLockupLL } from "src/interfaces/ISablierV2MerkleLockupLL.sol"; +import { ISablierV2MerkleLL } from "src/interfaces/ISablierV2MerkleLL.sol"; import { MerkleLockup } from "src/types/DataTypes.sol"; import { MerkleBuilder } from "../../utils/MerkleBuilder.sol"; import { Fork_Test } from "../Fork.t.sol"; -abstract contract MerkleLockupLL_Fork_Test is Fork_Test { +abstract contract MerkleLL_Fork_Test is Fork_Test { using MerkleBuilder for uint256[]; constructor(IERC20 asset_) Fork_Test(asset_) { } @@ -41,13 +41,13 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { uint128[] amounts; MerkleLockup.ConstructorParams baseParams; uint128 clawbackAmount; - address expectedLockupLL; + address expectedLL; LockupLinear.StreamLL expectedStream; uint256 expectedStreamId; uint256[] indexes; uint256 leafPos; uint256 leafToClaim; - ISablierV2MerkleLockupLL merkleLockupLL; + ISablierV2MerkleLL merkleLL; bytes32 merkleRoot; address[] recipients; uint256 recipientCount; @@ -56,7 +56,7 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { // We need the leaves as a storage variable so that we can use OpenZeppelin's {Arrays.findUpperBound}. uint256[] public leaves; - function testForkFuzz_MerkleLockupLL(Params memory params) external { + function testForkFuzz_MerkleLL(Params memory params) external { vm.assume(params.admin != address(0) && params.admin != users.admin); vm.assume(params.leafData.length > 1); assumeNoBlacklisted({ token: address(FORK_ASSET), addr: params.admin }); @@ -95,8 +95,7 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { MerkleBuilder.sortLeaves(leaves); vars.merkleRoot = getRoot(leaves.toBytes32()); - vars.expectedLockupLL = - computeMerkleLockupLLAddress(params.admin, FORK_ASSET, vars.merkleRoot, params.expiration); + vars.expectedLL = computeMerkleLLAddress(params.admin, FORK_ASSET, vars.merkleRoot, params.expiration); vars.baseParams = defaults.baseParams({ admin: params.admin, @@ -106,8 +105,8 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { }); vm.expectEmit({ emitter: address(merkleLockupFactory) }); - emit CreateMerkleLockupLL({ - merkleLockupLL: ISablierV2MerkleLockupLL(vars.expectedLockupLL), + emit CreateMerkleLL({ + merkleLL: ISablierV2MerkleLL(vars.expectedLL), baseParams: vars.baseParams, lockupLinear: lockupLinear, streamDurations: defaults.durations(), @@ -115,7 +114,7 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { recipientCount: vars.recipientCount }); - vars.merkleLockupLL = merkleLockupFactory.createMerkleLockupLL({ + vars.merkleLL = merkleLockupFactory.createMerkleLL({ baseParams: vars.baseParams, lockupLinear: lockupLinear, streamDurations: defaults.durations(), @@ -124,20 +123,16 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { }); // Fund the MerkleLockup contract. - deal({ token: address(FORK_ASSET), to: address(vars.merkleLockupLL), give: vars.aggregateAmount }); + deal({ token: address(FORK_ASSET), to: address(vars.merkleLL), give: vars.aggregateAmount }); - assertGt(address(vars.merkleLockupLL).code.length, 0, "MerkleLockupLL contract not created"); - assertEq( - address(vars.merkleLockupLL), - vars.expectedLockupLL, - "MerkleLockupLL contract does not match computed address" - ); + assertGt(address(vars.merkleLL).code.length, 0, "MerkleLL contract not created"); + assertEq(address(vars.merkleLL), vars.expectedLL, "MerkleLL contract does not match computed address"); /*////////////////////////////////////////////////////////////////////////// CLAIM //////////////////////////////////////////////////////////////////////////*/ - assertFalse(vars.merkleLockupLL.hasClaimed(vars.indexes[params.posBeforeSort])); + assertFalse(vars.merkleLL.hasClaimed(vars.indexes[params.posBeforeSort])); vars.leafToClaim = MerkleBuilder.computeLeaf( vars.indexes[params.posBeforeSort], @@ -153,7 +148,7 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { vars.amounts[params.posBeforeSort], vars.expectedStreamId ); - vars.actualStreamId = vars.merkleLockupLL.claim({ + vars.actualStreamId = vars.merkleLL.claim({ index: vars.indexes[params.posBeforeSort], recipient: vars.recipients[params.posBeforeSort], amount: vars.amounts[params.posBeforeSort], @@ -176,7 +171,7 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { wasCanceled: false }); - assertTrue(vars.merkleLockupLL.hasClaimed(vars.indexes[params.posBeforeSort])); + assertTrue(vars.merkleLL.hasClaimed(vars.indexes[params.posBeforeSort])); assertEq(vars.actualStreamId, vars.expectedStreamId); assertEq(vars.actualStream, vars.expectedStream); @@ -185,14 +180,14 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { //////////////////////////////////////////////////////////////////////////*/ if (params.expiration > 0) { - vars.clawbackAmount = uint128(FORK_ASSET.balanceOf(address(vars.merkleLockupLL))); + vars.clawbackAmount = uint128(FORK_ASSET.balanceOf(address(vars.merkleLL))); vm.warp({ newTimestamp: uint256(params.expiration) + 1 seconds }); resetPrank({ msgSender: params.admin }); expectCallToTransfer({ asset_: address(FORK_ASSET), to: params.admin, amount: vars.clawbackAmount }); - vm.expectEmit({ emitter: address(vars.merkleLockupLL) }); + vm.expectEmit({ emitter: address(vars.merkleLL) }); emit Clawback({ to: params.admin, admin: params.admin, amount: vars.clawbackAmount }); - vars.merkleLockupLL.clawback({ to: params.admin, amount: vars.clawbackAmount }); + vars.merkleLL.clawback({ to: params.admin, amount: vars.clawbackAmount }); } } } diff --git a/test/fork/merkle-lockup/MerkleLockupLT.t.sol b/test/fork/merkle-lockup/MerkleLT.t.sol similarity index 83% rename from test/fork/merkle-lockup/MerkleLockupLT.t.sol rename to test/fork/merkle-lockup/MerkleLT.t.sol index f5d7fb61..ec8ec664 100644 --- a/test/fork/merkle-lockup/MerkleLockupLT.t.sol +++ b/test/fork/merkle-lockup/MerkleLT.t.sol @@ -5,13 +5,13 @@ import { Arrays } from "@openzeppelin/contracts/utils/Arrays.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { Lockup, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { ISablierV2MerkleLockupLT } from "src/interfaces/ISablierV2MerkleLockupLT.sol"; +import { ISablierV2MerkleLT } from "src/interfaces/ISablierV2MerkleLT.sol"; import { MerkleLockup } from "src/types/DataTypes.sol"; import { MerkleBuilder } from "../../utils/MerkleBuilder.sol"; import { Fork_Test } from "../Fork.t.sol"; -abstract contract MerkleLockupLT_Fork_Test is Fork_Test { +abstract contract MerkleLT_Fork_Test is Fork_Test { using MerkleBuilder for uint256[]; constructor(IERC20 asset_) Fork_Test(asset_) { } @@ -42,13 +42,13 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { uint256 aggregateAmount; MerkleLockup.ConstructorParams baseParams; uint128 clawbackAmount; - address expectedLockupLT; + address expectedLT; LockupTranched.StreamLT expectedStream; uint256 expectedStreamId; uint256[] indexes; uint256 leafPos; uint256 leafToClaim; - ISablierV2MerkleLockupLT merkleLockupLT; + ISablierV2MerkleLT merkleLT; bytes32 merkleRoot; address[] recipients; uint256 recipientCount; @@ -57,7 +57,7 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { // We need the leaves as a storage variable so that we can use OpenZeppelin's {Arrays.findUpperBound}. uint256[] public leaves; - function testForkFuzz_MerkleLockupLT(Params memory params) external { + function testForkFuzz_MerkleLT(Params memory params) external { vm.assume(params.admin != address(0) && params.admin != users.admin); vm.assume(params.leafData.length > 1); assumeNoBlacklisted({ token: address(FORK_ASSET), addr: params.admin }); @@ -96,8 +96,7 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { MerkleBuilder.sortLeaves(leaves); vars.merkleRoot = getRoot(leaves.toBytes32()); - vars.expectedLockupLT = - computeMerkleLockupLTAddress(params.admin, FORK_ASSET, vars.merkleRoot, params.expiration); + vars.expectedLT = computeMerkleLTAddress(params.admin, FORK_ASSET, vars.merkleRoot, params.expiration); vars.baseParams = defaults.baseParams({ admin: params.admin, @@ -107,8 +106,8 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { }); vm.expectEmit({ emitter: address(merkleLockupFactory) }); - emit CreateMerkleLockupLT({ - merkleLockupLT: ISablierV2MerkleLockupLT(vars.expectedLockupLT), + emit CreateMerkleLT({ + merkleLT: ISablierV2MerkleLT(vars.expectedLT), baseParams: vars.baseParams, lockupTranched: lockupTranched, tranchesWithPercentages: defaults.tranchesWithPercentages(), @@ -117,7 +116,7 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { recipientCount: vars.recipientCount }); - vars.merkleLockupLT = merkleLockupFactory.createMerkleLockupLT({ + vars.merkleLT = merkleLockupFactory.createMerkleLT({ baseParams: vars.baseParams, lockupTranched: lockupTranched, tranchesWithPercentages: defaults.tranchesWithPercentages(), @@ -126,20 +125,16 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { }); // Fund the MerkleLockup contract. - deal({ token: address(FORK_ASSET), to: address(vars.merkleLockupLT), give: vars.aggregateAmount }); + deal({ token: address(FORK_ASSET), to: address(vars.merkleLT), give: vars.aggregateAmount }); - assertGt(address(vars.merkleLockupLT).code.length, 0, "MerkleLockupLT contract not created"); - assertEq( - address(vars.merkleLockupLT), - vars.expectedLockupLT, - "MerkleLockupLT contract does not match computed address" - ); + assertGt(address(vars.merkleLT).code.length, 0, "MerkleLT contract not created"); + assertEq(address(vars.merkleLT), vars.expectedLT, "MerkleLT contract does not match computed address"); /*////////////////////////////////////////////////////////////////////////// CLAIM //////////////////////////////////////////////////////////////////////////*/ - assertFalse(vars.merkleLockupLT.hasClaimed(vars.indexes[params.posBeforeSort])); + assertFalse(vars.merkleLT.hasClaimed(vars.indexes[params.posBeforeSort])); vars.leafToClaim = MerkleBuilder.computeLeaf( vars.indexes[params.posBeforeSort], @@ -155,7 +150,7 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { vars.amounts[params.posBeforeSort], vars.expectedStreamId ); - vars.actualStreamId = vars.merkleLockupLT.claim({ + vars.actualStreamId = vars.merkleLT.claim({ index: vars.indexes[params.posBeforeSort], recipient: vars.recipients[params.posBeforeSort], amount: vars.amounts[params.posBeforeSort], @@ -178,7 +173,7 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { wasCanceled: false }); - assertTrue(vars.merkleLockupLT.hasClaimed(vars.indexes[params.posBeforeSort])); + assertTrue(vars.merkleLT.hasClaimed(vars.indexes[params.posBeforeSort])); assertEq(vars.actualStreamId, vars.expectedStreamId); assertEq(vars.actualStream, vars.expectedStream); @@ -187,14 +182,14 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { //////////////////////////////////////////////////////////////////////////*/ if (params.expiration > 0) { - vars.clawbackAmount = uint128(FORK_ASSET.balanceOf(address(vars.merkleLockupLT))); + vars.clawbackAmount = uint128(FORK_ASSET.balanceOf(address(vars.merkleLT))); vm.warp({ newTimestamp: uint256(params.expiration) + 1 seconds }); resetPrank({ msgSender: params.admin }); expectCallToTransfer({ asset_: address(FORK_ASSET), to: params.admin, amount: vars.clawbackAmount }); - vm.expectEmit({ emitter: address(vars.merkleLockupLT) }); + vm.expectEmit({ emitter: address(vars.merkleLT) }); emit Clawback({ to: params.admin, admin: params.admin, amount: vars.clawbackAmount }); - vars.merkleLockupLT.clawback({ to: params.admin, amount: vars.clawbackAmount }); + vars.merkleLT.clawback({ to: params.admin, amount: vars.clawbackAmount }); } } } diff --git a/test/integration/merkle-lockup/MerkleLockup.t.sol b/test/integration/merkle-lockup/MerkleLockup.t.sol index 832e91c7..8d6be352 100644 --- a/test/integration/merkle-lockup/MerkleLockup.t.sol +++ b/test/integration/merkle-lockup/MerkleLockup.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.22; -import { ISablierV2MerkleLockupLL } from "src/interfaces/ISablierV2MerkleLockupLL.sol"; -import { ISablierV2MerkleLockupLT } from "src/interfaces/ISablierV2MerkleLockupLT.sol"; +import { ISablierV2MerkleLL } from "src/interfaces/ISablierV2MerkleLL.sol"; +import { ISablierV2MerkleLT } from "src/interfaces/ISablierV2MerkleLT.sol"; import { Integration_Test } from "../Integration.t.sol"; @@ -11,20 +11,20 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { Integration_Test.setUp(); // Create the default MerkleLockup contracts. - merkleLockupLL = createMerkleLockupLL(); - merkleLockupLT = createMerkleLockupLT(); + merkleLL = createMerkleLL(); + merkleLT = createMerkleLT(); // Fund the MerkleLockup contracts. - deal({ token: address(dai), to: address(merkleLockupLL), give: defaults.AGGREGATE_AMOUNT() }); - deal({ token: address(dai), to: address(merkleLockupLT), give: defaults.AGGREGATE_AMOUNT() }); + deal({ token: address(dai), to: address(merkleLL), give: defaults.AGGREGATE_AMOUNT() }); + deal({ token: address(dai), to: address(merkleLT), give: defaults.AGGREGATE_AMOUNT() }); } /*////////////////////////////////////////////////////////////////////////// - MERKLE-LOCKUP-LL + MERKLE-LL //////////////////////////////////////////////////////////////////////////*/ function claimLL() internal returns (uint256) { - return merkleLockupLL.claim({ + return merkleLL.claim({ index: defaults.INDEX1(), recipient: users.recipient1, amount: defaults.CLAIM_AMOUNT(), @@ -32,36 +32,36 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { }); } - function computeMerkleLockupLLAddress() internal view returns (address) { - return computeMerkleLockupLLAddress(users.admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); + function computeMerkleLLAddress() internal view returns (address) { + return computeMerkleLLAddress(users.admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); } - function computeMerkleLockupLLAddress(address admin) internal view returns (address) { - return computeMerkleLockupLLAddress(admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); + function computeMerkleLLAddress(address admin) internal view returns (address) { + return computeMerkleLLAddress(admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); } - function computeMerkleLockupLLAddress(address admin, uint40 expiration) internal view returns (address) { - return computeMerkleLockupLLAddress(admin, defaults.MERKLE_ROOT(), expiration); + function computeMerkleLLAddress(address admin, uint40 expiration) internal view returns (address) { + return computeMerkleLLAddress(admin, defaults.MERKLE_ROOT(), expiration); } - function computeMerkleLockupLLAddress(address admin, bytes32 merkleRoot) internal view returns (address) { - return computeMerkleLockupLLAddress(admin, merkleRoot, defaults.EXPIRATION()); + function computeMerkleLLAddress(address admin, bytes32 merkleRoot) internal view returns (address) { + return computeMerkleLLAddress(admin, merkleRoot, defaults.EXPIRATION()); } - function createMerkleLockupLL() internal returns (ISablierV2MerkleLockupLL) { - return createMerkleLockupLL(users.admin, defaults.EXPIRATION()); + function createMerkleLL() internal returns (ISablierV2MerkleLL) { + return createMerkleLL(users.admin, defaults.EXPIRATION()); } - function createMerkleLockupLL(address admin) internal returns (ISablierV2MerkleLockupLL) { - return createMerkleLockupLL(admin, defaults.EXPIRATION()); + function createMerkleLL(address admin) internal returns (ISablierV2MerkleLL) { + return createMerkleLL(admin, defaults.EXPIRATION()); } - function createMerkleLockupLL(uint40 expiration) internal returns (ISablierV2MerkleLockupLL) { - return createMerkleLockupLL(users.admin, expiration); + function createMerkleLL(uint40 expiration) internal returns (ISablierV2MerkleLL) { + return createMerkleLL(users.admin, expiration); } - function createMerkleLockupLL(address admin, uint40 expiration) internal returns (ISablierV2MerkleLockupLL) { - return merkleLockupFactory.createMerkleLockupLL({ + function createMerkleLL(address admin, uint40 expiration) internal returns (ISablierV2MerkleLL) { + return merkleLockupFactory.createMerkleLL({ baseParams: defaults.baseParams(admin, dai, defaults.MERKLE_ROOT(), expiration), lockupLinear: lockupLinear, streamDurations: defaults.durations(), @@ -71,11 +71,11 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { } /*////////////////////////////////////////////////////////////////////////// - MERKLE-LOCKUP-LT + MERKLE-LT //////////////////////////////////////////////////////////////////////////*/ function claimLT() internal returns (uint256) { - return merkleLockupLT.claim({ + return merkleLT.claim({ index: defaults.INDEX1(), recipient: users.recipient1, amount: defaults.CLAIM_AMOUNT(), @@ -83,36 +83,36 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { }); } - function computeMerkleLockupLTAddress() internal view returns (address) { - return computeMerkleLockupLTAddress(users.admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); + function computeMerkleLTAddress() internal view returns (address) { + return computeMerkleLTAddress(users.admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); } - function computeMerkleLockupLTAddress(address admin) internal view returns (address) { - return computeMerkleLockupLTAddress(admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); + function computeMerkleLTAddress(address admin) internal view returns (address) { + return computeMerkleLTAddress(admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); } - function computeMerkleLockupLTAddress(address admin, uint40 expiration) internal view returns (address) { - return computeMerkleLockupLTAddress(admin, defaults.MERKLE_ROOT(), expiration); + function computeMerkleLTAddress(address admin, uint40 expiration) internal view returns (address) { + return computeMerkleLTAddress(admin, defaults.MERKLE_ROOT(), expiration); } - function computeMerkleLockupLTAddress(address admin, bytes32 merkleRoot) internal view returns (address) { - return computeMerkleLockupLTAddress(admin, merkleRoot, defaults.EXPIRATION()); + function computeMerkleLTAddress(address admin, bytes32 merkleRoot) internal view returns (address) { + return computeMerkleLTAddress(admin, merkleRoot, defaults.EXPIRATION()); } - function createMerkleLockupLT() internal returns (ISablierV2MerkleLockupLT) { - return createMerkleLockupLT(users.admin, defaults.EXPIRATION()); + function createMerkleLT() internal returns (ISablierV2MerkleLT) { + return createMerkleLT(users.admin, defaults.EXPIRATION()); } - function createMerkleLockupLT(address admin) internal returns (ISablierV2MerkleLockupLT) { - return createMerkleLockupLT(admin, defaults.EXPIRATION()); + function createMerkleLT(address admin) internal returns (ISablierV2MerkleLT) { + return createMerkleLT(admin, defaults.EXPIRATION()); } - function createMerkleLockupLT(uint40 expiration) internal returns (ISablierV2MerkleLockupLT) { - return createMerkleLockupLT(users.admin, expiration); + function createMerkleLT(uint40 expiration) internal returns (ISablierV2MerkleLT) { + return createMerkleLT(users.admin, expiration); } - function createMerkleLockupLT(address admin, uint40 expiration) internal returns (ISablierV2MerkleLockupLT) { - return merkleLockupFactory.createMerkleLockupLT({ + function createMerkleLT(address admin, uint40 expiration) internal returns (ISablierV2MerkleLT) { + return merkleLockupFactory.createMerkleLT({ baseParams: defaults.baseParams(admin, dai, defaults.MERKLE_ROOT(), expiration), lockupTranched: lockupTranched, tranchesWithPercentages: defaults.tranchesWithPercentages(), diff --git a/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol b/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.t.sol similarity index 78% rename from test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol rename to test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.t.sol index 616fcfad..f4e3f268 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol +++ b/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.t.sol @@ -4,12 +4,12 @@ pragma solidity >=0.8.22 <0.9.0; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; import { Errors } from "src/libraries/Errors.sol"; -import { ISablierV2MerkleLockupLL } from "src/interfaces/ISablierV2MerkleLockupLL.sol"; +import { ISablierV2MerkleLL } from "src/interfaces/ISablierV2MerkleLL.sol"; import { MerkleLockup } from "src/types/DataTypes.sol"; import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; -contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test { +contract CreateMerkleLL_Integration_Test is MerkleLockup_Integration_Test { function setUp() public override { MerkleLockup_Integration_Test.setUp(); } @@ -28,7 +28,7 @@ contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test ) ); - merkleLockupFactory.createMerkleLockupLL({ + merkleLockupFactory.createMerkleLL({ baseParams: baseParams, lockupLinear: lockupLinear, streamDurations: streamDurations, @@ -50,7 +50,7 @@ contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test // Expect a revert due to CREATE2. vm.expectRevert(); - merkleLockupFactory.createMerkleLockupLL({ + merkleLockupFactory.createMerkleLL({ baseParams: baseParams, lockupLinear: lockupLinear, streamDurations: streamDurations, @@ -63,7 +63,7 @@ contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test _; } - function testFuzz_CreateMerkleLockupLL( + function testFuzz_CreateMerkleLL( address admin, uint40 expiration ) @@ -72,7 +72,7 @@ contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test givenNotCreatedAlready { vm.assume(admin != users.admin); - address expectedLockupLL = computeMerkleLockupLLAddress(admin, expiration); + address expectedLL = computeMerkleLLAddress(admin, expiration); MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams({ admin: admin, @@ -82,8 +82,8 @@ contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test }); vm.expectEmit({ emitter: address(merkleLockupFactory) }); - emit CreateMerkleLockupLL({ - merkleLockupLL: ISablierV2MerkleLockupLL(expectedLockupLL), + emit CreateMerkleLL({ + merkleLL: ISablierV2MerkleLL(expectedLL), baseParams: baseParams, lockupLinear: lockupLinear, streamDurations: defaults.durations(), @@ -91,8 +91,8 @@ contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test recipientCount: defaults.RECIPIENT_COUNT() }); - address actualLockupLL = address(createMerkleLockupLL(admin, expiration)); - assertGt(actualLockupLL.code.length, 0, "MerkleLockupLL contract not created"); - assertEq(actualLockupLL, expectedLockupLL, "MerkleLockupLL contract does not match computed address"); + address actualLL = address(createMerkleLL(admin, expiration)); + assertGt(actualLL.code.length, 0, "MerkleLL contract not created"); + assertEq(actualLL, expectedLL, "MerkleLL contract does not match computed address"); } } diff --git a/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.tree b/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.tree similarity index 78% rename from test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.tree rename to test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.tree index a504f83e..81de5f39 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.tree +++ b/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.tree @@ -1,4 +1,4 @@ -createMerkleLockupLL.t.sol +createMerkleLL.t.sol ├── when the campaign name is too long │ └── it should revert └── when the campaign name is not too long @@ -6,4 +6,4 @@ createMerkleLockupLL.t.sol │ └── it should revert └── given the campaign has not been created already ├── it should create the campaign - └── it should emit a {CreateMerkleLockupLL} event + └── it should emit a {CreateMerkleLL} event diff --git a/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.t.sol b/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.t.sol similarity index 76% rename from test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.t.sol rename to test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.t.sol index dbe768bc..8cae789a 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.t.sol +++ b/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.t.sol @@ -4,12 +4,12 @@ pragma solidity >=0.8.22 <0.9.0; import { ud2x18 } from "@prb/math/src/UD2x18.sol"; import { Errors } from "src/libraries/Errors.sol"; -import { ISablierV2MerkleLockupLT } from "src/interfaces/ISablierV2MerkleLockupLT.sol"; -import { MerkleLockup, MerkleLockupLT } from "src/types/DataTypes.sol"; +import { ISablierV2MerkleLT } from "src/interfaces/ISablierV2MerkleLT.sol"; +import { MerkleLockup, MerkleLT } from "src/types/DataTypes.sol"; import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; -contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test { +contract CreateMerkleLT_Integration_Test is MerkleLockup_Integration_Test { function setUp() public override { MerkleLockup_Integration_Test.setUp(); } @@ -23,7 +23,7 @@ contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); uint256 recipientCount = defaults.RECIPIENT_COUNT(); - MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); + MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); tranchesWithPercentages[0].unlockPercentage = ud2x18(0.05e18); tranchesWithPercentages[1].unlockPercentage = ud2x18(0.2e18); @@ -36,7 +36,7 @@ contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test ) ); - merkleLockupFactory.createMerkleLockupLT( + merkleLockupFactory.createMerkleLT( baseParams, lockupTranched, tranchesWithPercentages, aggregateAmount, recipientCount ); } @@ -46,7 +46,7 @@ contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); uint256 recipientCount = defaults.RECIPIENT_COUNT(); - MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); + MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); tranchesWithPercentages[0].unlockPercentage = ud2x18(0.75e18); tranchesWithPercentages[1].unlockPercentage = ud2x18(0.8e18); @@ -59,7 +59,7 @@ contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test ) ); - merkleLockupFactory.createMerkleLockupLT( + merkleLockupFactory.createMerkleLT( baseParams, lockupTranched, tranchesWithPercentages, aggregateAmount, recipientCount ); } @@ -70,7 +70,7 @@ contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test function test_RevertWhen_CampaignNameTooLong() external whenTotalPercentageOneHundred { MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); - MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); + MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); uint256 recipientCount = defaults.RECIPIENT_COUNT(); @@ -82,7 +82,7 @@ contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test ) ); - merkleLockupFactory.createMerkleLockupLT( + merkleLockupFactory.createMerkleLT( baseParams, lockupTranched, tranchesWithPercentages, aggregateAmount, recipientCount ); } @@ -94,13 +94,13 @@ contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test /// @dev This test works because a default MerkleLockup contract is deployed in {Integration_Test.setUp} function test_RevertGiven_CreatedAlready() external whenTotalPercentageOneHundred whenCampaignNameNotTooLong { MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); - MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); + MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); uint256 recipientCount = defaults.RECIPIENT_COUNT(); // Expect a revert due to CREATE2. vm.expectRevert(); - merkleLockupFactory.createMerkleLockupLT( + merkleLockupFactory.createMerkleLT( baseParams, lockupTranched, tranchesWithPercentages, aggregateAmount, recipientCount ); } @@ -109,7 +109,7 @@ contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test _; } - function testFuzz_CreateMerkleLockupLT( + function testFuzz_CreateMerkleLT( address admin, uint40 expiration ) @@ -119,7 +119,7 @@ contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test givenNotCreatedAlready { vm.assume(admin != users.admin); - address expectedLockupLT = computeMerkleLockupLTAddress(admin, expiration); + address expectedLT = computeMerkleLTAddress(admin, expiration); MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams({ admin: admin, @@ -129,8 +129,8 @@ contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test }); vm.expectEmit({ emitter: address(merkleLockupFactory) }); - emit CreateMerkleLockupLT({ - merkleLockupLT: ISablierV2MerkleLockupLT(expectedLockupLT), + emit CreateMerkleLT({ + merkleLT: ISablierV2MerkleLT(expectedLT), baseParams: baseParams, lockupTranched: lockupTranched, tranchesWithPercentages: defaults.tranchesWithPercentages(), @@ -139,8 +139,8 @@ contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test recipientCount: defaults.RECIPIENT_COUNT() }); - address actualLockupLT = address(createMerkleLockupLT(admin, expiration)); - assertGt(actualLockupLT.code.length, 0, "MerkleLockupLT contract not created"); - assertEq(actualLockupLT, expectedLockupLT, "MerkleLockupLT contract does not match computed address"); + address actualLT = address(createMerkleLT(admin, expiration)); + assertGt(actualLT.code.length, 0, "MerkleLT contract not created"); + assertEq(actualLT, expectedLT, "MerkleLT contract does not match computed address"); } } diff --git a/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.tree b/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.tree similarity index 87% rename from test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.tree rename to test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.tree index 108a4625..215638cb 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.tree +++ b/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.tree @@ -1,4 +1,4 @@ -createMerkleLockupLL.t.sol +createMerkleLL.t.sol ├── when the total percentage does not equal 100% │ ├── when the total percentage is less than 100% │ │ └── it should revert @@ -12,4 +12,4 @@ createMerkleLockupLL.t.sol │ └── it should revert └── given the campaign has not been already created ├── it should create the campaign - └── it should emit a {CreateMerkleLockupLT} event + └── it should emit a {CreateMerkleLT} event diff --git a/test/integration/merkle-lockup/ll/claim/claim.t.sol b/test/integration/merkle-lockup/ll/claim/claim.t.sol index 7e2bf72b..30d9441d 100644 --- a/test/integration/merkle-lockup/ll/claim/claim.t.sol +++ b/test/integration/merkle-lockup/ll/claim/claim.t.sol @@ -20,7 +20,7 @@ contract Claim_Integration_Test is MerkleLockup_Integration_Test { vm.expectRevert( abi.encodeWithSelector(Errors.SablierV2MerkleLockup_CampaignExpired.selector, warpTime, expiration) ); - merkleLockupLL.claim({ index: 1, recipient: users.recipient1, amount: 1, merkleProof: merkleProof }); + merkleLL.claim({ index: 1, recipient: users.recipient1, amount: 1, merkleProof: merkleProof }); } modifier givenCampaignNotExpired() { @@ -33,7 +33,7 @@ contract Claim_Integration_Test is MerkleLockup_Integration_Test { uint128 amount = defaults.CLAIM_AMOUNT(); bytes32[] memory merkleProof = defaults.index1Proof(); vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_StreamClaimed.selector, index1)); - merkleLockupLL.claim(index1, users.recipient1, amount, merkleProof); + merkleLL.claim(index1, users.recipient1, amount, merkleProof); } modifier givenNotClaimed() { @@ -54,7 +54,7 @@ contract Claim_Integration_Test is MerkleLockup_Integration_Test { uint128 amount = defaults.CLAIM_AMOUNT(); bytes32[] memory merkleProof = defaults.index1Proof(); vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_InvalidProof.selector)); - merkleLockupLL.claim(invalidIndex, users.recipient1, amount, merkleProof); + merkleLL.claim(invalidIndex, users.recipient1, amount, merkleProof); } function test_RevertWhen_InvalidRecipient() @@ -68,7 +68,7 @@ contract Claim_Integration_Test is MerkleLockup_Integration_Test { uint128 amount = defaults.CLAIM_AMOUNT(); bytes32[] memory merkleProof = defaults.index1Proof(); vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_InvalidProof.selector)); - merkleLockupLL.claim(index1, invalidRecipient, amount, merkleProof); + merkleLL.claim(index1, invalidRecipient, amount, merkleProof); } function test_RevertWhen_InvalidAmount() @@ -81,7 +81,7 @@ contract Claim_Integration_Test is MerkleLockup_Integration_Test { uint128 invalidAmount = 1337; bytes32[] memory merkleProof = defaults.index1Proof(); vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_InvalidProof.selector)); - merkleLockupLL.claim(index1, users.recipient1, invalidAmount, merkleProof); + merkleLL.claim(index1, users.recipient1, invalidAmount, merkleProof); } function test_RevertWhen_InvalidMerkleProof() @@ -94,7 +94,7 @@ contract Claim_Integration_Test is MerkleLockup_Integration_Test { uint128 amount = defaults.CLAIM_AMOUNT(); bytes32[] memory invalidMerkleProof = defaults.index2Proof(); vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_InvalidProof.selector)); - merkleLockupLL.claim(index1, users.recipient1, amount, invalidMerkleProof); + merkleLL.claim(index1, users.recipient1, amount, invalidMerkleProof); } modifier givenIncludedInMerkleTree() { @@ -104,7 +104,7 @@ contract Claim_Integration_Test is MerkleLockup_Integration_Test { function test_Claim() external givenCampaignNotExpired givenNotClaimed givenIncludedInMerkleTree { uint256 expectedStreamId = lockupLinear.nextStreamId(); - vm.expectEmit({ emitter: address(merkleLockupLL) }); + vm.expectEmit({ emitter: address(merkleLL) }); emit Claim(defaults.INDEX1(), users.recipient1, defaults.CLAIM_AMOUNT(), expectedStreamId); uint256 actualStreamId = claimLL(); @@ -124,7 +124,7 @@ contract Claim_Integration_Test is MerkleLockup_Integration_Test { wasCanceled: false }); - assertTrue(merkleLockupLL.hasClaimed(defaults.INDEX1()), "not claimed"); + assertTrue(merkleLL.hasClaimed(defaults.INDEX1()), "not claimed"); assertEq(actualStreamId, expectedStreamId, "invalid stream id"); assertEq(actualStream, expectedStream); } diff --git a/test/integration/merkle-lockup/ll/clawback/clawback.t.sol b/test/integration/merkle-lockup/ll/clawback/clawback.t.sol index 21110c7e..c0079bd7 100644 --- a/test/integration/merkle-lockup/ll/clawback/clawback.t.sol +++ b/test/integration/merkle-lockup/ll/clawback/clawback.t.sol @@ -15,7 +15,7 @@ contract Clawback_Integration_Test is MerkleLockup_Integration_Test { function test_RevertWhen_CallerNotAdmin() external { resetPrank({ msgSender: users.eve }); vm.expectRevert(abi.encodeWithSelector(V2CoreErrors.CallerNotAdmin.selector, users.admin, users.eve)); - merkleLockupLL.clawback({ to: users.eve, amount: 1 }); + merkleLL.clawback({ to: users.eve, amount: 1 }); } modifier whenCallerAdmin() { @@ -29,7 +29,7 @@ contract Clawback_Integration_Test is MerkleLockup_Integration_Test { Errors.SablierV2MerkleLockup_CampaignNotExpired.selector, block.timestamp, defaults.EXPIRATION() ) ); - merkleLockupLL.clawback({ to: users.admin, amount: 1 }); + merkleLL.clawback({ to: users.admin, amount: 1 }); } modifier givenCampaignExpired() { @@ -49,10 +49,10 @@ contract Clawback_Integration_Test is MerkleLockup_Integration_Test { } function test_Clawback(address to) internal { - uint128 clawbackAmount = uint128(dai.balanceOf(address(merkleLockupLL))); + uint128 clawbackAmount = uint128(dai.balanceOf(address(merkleLL))); expectCallToTransfer({ to: to, amount: clawbackAmount }); - vm.expectEmit({ emitter: address(merkleLockupLL) }); + vm.expectEmit({ emitter: address(merkleLL) }); emit Clawback({ admin: users.admin, to: to, amount: clawbackAmount }); - merkleLockupLL.clawback({ to: to, amount: clawbackAmount }); + merkleLL.clawback({ to: to, amount: clawbackAmount }); } } diff --git a/test/integration/merkle-lockup/ll/constructor/constructor.t.sol b/test/integration/merkle-lockup/ll/constructor/constructor.t.sol index 59ae4711..4ae23a8e 100644 --- a/test/integration/merkle-lockup/ll/constructor/constructor.t.sol +++ b/test/integration/merkle-lockup/ll/constructor/constructor.t.sol @@ -3,11 +3,11 @@ pragma solidity >=0.8.22 <0.9.0; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { SablierV2MerkleLockupLL } from "src/SablierV2MerkleLockupLL.sol"; +import { SablierV2MerkleLL } from "src/SablierV2MerkleLL.sol"; import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; -contract Constructor_MerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test { +contract Constructor_MerkleLL_Integration_Test is MerkleLockup_Integration_Test { /// @dev Needed to prevent "Stack too deep" error struct Vars { address actualAdmin; @@ -35,53 +35,53 @@ contract Constructor_MerkleLockupLL_Integration_Test is MerkleLockup_Integration } function test_Constructor() external { - SablierV2MerkleLockupLL constructedLockupLL = - new SablierV2MerkleLockupLL(defaults.baseParams(), lockupLinear, defaults.durations()); + SablierV2MerkleLL constructedLL = + new SablierV2MerkleLL(defaults.baseParams(), lockupLinear, defaults.durations()); Vars memory vars; - vars.actualAdmin = constructedLockupLL.admin(); + vars.actualAdmin = constructedLL.admin(); vars.expectedAdmin = users.admin; assertEq(vars.actualAdmin, vars.expectedAdmin, "admin"); - vars.actualAllowance = dai.allowance(address(constructedLockupLL), address(lockupLinear)); + vars.actualAllowance = dai.allowance(address(constructedLL), address(lockupLinear)); vars.expectedAllowance = MAX_UINT256; assertEq(vars.actualAllowance, vars.expectedAllowance, "allowance"); - vars.actualAsset = address(constructedLockupLL.ASSET()); + vars.actualAsset = address(constructedLL.ASSET()); vars.expectedAsset = address(dai); assertEq(vars.actualAsset, vars.expectedAsset, "asset"); - vars.actualCancelable = constructedLockupLL.CANCELABLE(); + vars.actualCancelable = constructedLL.CANCELABLE(); vars.expectedCancelable = defaults.CANCELABLE(); assertEq(vars.actualCancelable, vars.expectedCancelable, "cancelable"); - (vars.actualDurations.cliff, vars.actualDurations.total) = constructedLockupLL.streamDurations(); + (vars.actualDurations.cliff, vars.actualDurations.total) = constructedLL.streamDurations(); vars.expectedDurations = defaults.durations(); assertEq(vars.actualDurations.cliff, vars.expectedDurations.cliff, "durations.cliff"); assertEq(vars.actualDurations.total, vars.expectedDurations.total, "durations.total"); - vars.actualExpiration = constructedLockupLL.EXPIRATION(); + vars.actualExpiration = constructedLL.EXPIRATION(); vars.expectedExpiration = defaults.EXPIRATION(); assertEq(vars.actualExpiration, vars.expectedExpiration, "expiration"); - vars.actualIpfsCID = constructedLockupLL.ipfsCID(); + vars.actualIpfsCID = constructedLL.ipfsCID(); vars.expectedIpfsCID = defaults.IPFS_CID(); assertEq(vars.actualIpfsCID, vars.expectedIpfsCID, "ipfsCID"); - vars.actualLockupLinear = address(constructedLockupLL.LOCKUP_LINEAR()); + vars.actualLockupLinear = address(constructedLL.LOCKUP_LINEAR()); vars.expectedLockupLinear = address(lockupLinear); assertEq(vars.actualLockupLinear, vars.expectedLockupLinear, "lockupLinear"); - vars.actualMerkleRoot = constructedLockupLL.MERKLE_ROOT(); + vars.actualMerkleRoot = constructedLL.MERKLE_ROOT(); vars.expectedMerkleRoot = defaults.MERKLE_ROOT(); assertEq(vars.actualMerkleRoot, vars.expectedMerkleRoot, "merkleRoot"); - vars.actualName = constructedLockupLL.name(); + vars.actualName = constructedLL.name(); vars.expectedName = defaults.NAME_BYTES32(); assertEq(bytes32(abi.encodePacked(vars.actualName)), vars.expectedName, "name"); - vars.actualTransferable = constructedLockupLL.TRANSFERABLE(); + vars.actualTransferable = constructedLL.TRANSFERABLE(); vars.expectedTransferable = defaults.TRANSFERABLE(); assertEq(vars.actualTransferable, vars.expectedTransferable, "transferable"); } diff --git a/test/integration/merkle-lockup/ll/has-claimed/hasClaimed.t.sol b/test/integration/merkle-lockup/ll/has-claimed/hasClaimed.t.sol index e7aafaef..0b07e916 100644 --- a/test/integration/merkle-lockup/ll/has-claimed/hasClaimed.t.sol +++ b/test/integration/merkle-lockup/ll/has-claimed/hasClaimed.t.sol @@ -10,7 +10,7 @@ contract HasClaimed_Integration_Test is MerkleLockup_Integration_Test { function test_HasClaimed_IndexNotInTree() external { uint256 indexNotInTree = 1337e18; - assertFalse(merkleLockupLL.hasClaimed(indexNotInTree), "claimed"); + assertFalse(merkleLL.hasClaimed(indexNotInTree), "claimed"); } modifier whenIndexInTree() { @@ -18,7 +18,7 @@ contract HasClaimed_Integration_Test is MerkleLockup_Integration_Test { } function test_HasClaimed_NotClaimed() external whenIndexInTree { - assertFalse(merkleLockupLL.hasClaimed(defaults.INDEX1()), "claimed"); + assertFalse(merkleLL.hasClaimed(defaults.INDEX1()), "claimed"); } modifier givenRecipientHasClaimed() { @@ -27,6 +27,6 @@ contract HasClaimed_Integration_Test is MerkleLockup_Integration_Test { } function test_HasClaimed() external whenIndexInTree givenRecipientHasClaimed { - assertTrue(merkleLockupLL.hasClaimed(defaults.INDEX1()), "not claimed"); + assertTrue(merkleLL.hasClaimed(defaults.INDEX1()), "not claimed"); } } diff --git a/test/integration/merkle-lockup/ll/has-expired/hasExpired.t.sol b/test/integration/merkle-lockup/ll/has-expired/hasExpired.t.sol index 5b463a88..85de0fa2 100644 --- a/test/integration/merkle-lockup/ll/has-expired/hasExpired.t.sol +++ b/test/integration/merkle-lockup/ll/has-expired/hasExpired.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.22 <0.9.0; -import { ISablierV2MerkleLockupLL } from "src/interfaces/ISablierV2MerkleLockupLL.sol"; +import { ISablierV2MerkleLL } from "src/interfaces/ISablierV2MerkleLL.sol"; import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; @@ -11,7 +11,7 @@ contract HasExpired_Integration_Test is MerkleLockup_Integration_Test { } function test_HasExpired_ExpirationZero() external { - ISablierV2MerkleLockupLL testLockup = createMerkleLockupLL({ expiration: 0 }); + ISablierV2MerkleLL testLockup = createMerkleLL({ expiration: 0 }); assertFalse(testLockup.hasExpired(), "campaign expired"); } @@ -20,16 +20,16 @@ contract HasExpired_Integration_Test is MerkleLockup_Integration_Test { } function test_HasExpired_ExpirationLessThanBlockTimestamp() external view givenExpirationNotZero { - assertFalse(merkleLockupLL.hasExpired(), "campaign expired"); + assertFalse(merkleLL.hasExpired(), "campaign expired"); } function test_HasExpired_ExpirationEqualToBlockTimestamp() external givenExpirationNotZero { vm.warp({ newTimestamp: defaults.EXPIRATION() }); - assertTrue(merkleLockupLL.hasExpired(), "campaign not expired"); + assertTrue(merkleLL.hasExpired(), "campaign not expired"); } function test_HasExpired_ExpirationGreaterThanBlockTimestamp() external givenExpirationNotZero { vm.warp({ newTimestamp: defaults.EXPIRATION() + 1 seconds }); - assertTrue(merkleLockupLL.hasExpired(), "campaign not expired"); + assertTrue(merkleLL.hasExpired(), "campaign not expired"); } } diff --git a/test/integration/merkle-lockup/lt/claim/claim.t.sol b/test/integration/merkle-lockup/lt/claim/claim.t.sol index 86083471..243fc995 100644 --- a/test/integration/merkle-lockup/lt/claim/claim.t.sol +++ b/test/integration/merkle-lockup/lt/claim/claim.t.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.22 <0.9.0; import { Arrays } from "@openzeppelin/contracts/utils/Arrays.sol"; import { Lockup, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { ISablierV2MerkleLockupLT } from "src/interfaces/ISablierV2MerkleLockupLT.sol"; +import { ISablierV2MerkleLT } from "src/interfaces/ISablierV2MerkleLT.sol"; import { Errors } from "src/libraries/Errors.sol"; import { MerkleLockup } from "src/types/DataTypes.sol"; @@ -28,7 +28,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { vm.expectRevert( abi.encodeWithSelector(Errors.SablierV2MerkleLockup_CampaignExpired.selector, warpTime, expiration) ); - merkleLockupLT.claim({ index: 1, recipient: users.recipient1, amount: 1, merkleProof: merkleProof }); + merkleLT.claim({ index: 1, recipient: users.recipient1, amount: 1, merkleProof: merkleProof }); } modifier givenCampaignNotExpired() { @@ -41,7 +41,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { uint128 amount = defaults.CLAIM_AMOUNT(); bytes32[] memory merkleProof = defaults.index1Proof(); vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_StreamClaimed.selector, index1)); - merkleLockupLT.claim(index1, users.recipient1, amount, merkleProof); + merkleLT.claim(index1, users.recipient1, amount, merkleProof); } modifier givenNotClaimed() { @@ -62,7 +62,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { uint128 amount = defaults.CLAIM_AMOUNT(); bytes32[] memory merkleProof = defaults.index1Proof(); vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_InvalidProof.selector)); - merkleLockupLT.claim(invalidIndex, users.recipient1, amount, merkleProof); + merkleLT.claim(invalidIndex, users.recipient1, amount, merkleProof); } function test_RevertWhen_InvalidRecipient() @@ -76,7 +76,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { uint128 amount = defaults.CLAIM_AMOUNT(); bytes32[] memory merkleProof = defaults.index1Proof(); vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_InvalidProof.selector)); - merkleLockupLT.claim(index1, invalidRecipient, amount, merkleProof); + merkleLT.claim(index1, invalidRecipient, amount, merkleProof); } function test_RevertWhen_InvalidAmount() @@ -89,7 +89,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { uint128 invalidAmount = 1337; bytes32[] memory merkleProof = defaults.index1Proof(); vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_InvalidProof.selector)); - merkleLockupLT.claim(index1, users.recipient1, invalidAmount, merkleProof); + merkleLT.claim(index1, users.recipient1, invalidAmount, merkleProof); } function test_RevertWhen_InvalidMerkleProof() @@ -102,7 +102,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { uint128 amount = defaults.CLAIM_AMOUNT(); bytes32[] memory invalidMerkleProof = defaults.index2Proof(); vm.expectRevert(abi.encodeWithSelector(Errors.SablierV2MerkleLockup_InvalidProof.selector)); - merkleLockupLT.claim(index1, users.recipient1, amount, invalidMerkleProof); + merkleLT.claim(index1, users.recipient1, amount, invalidMerkleProof); } modifier givenIncludedInMerkleTree() { @@ -135,8 +135,8 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); baseParams.merkleRoot = getRoot(leaves.toBytes32()); - // Deploy a test MerkleLockupLT contract. - ISablierV2MerkleLockupLT testMerkleLT = merkleLockupFactory.createMerkleLockupLT( + // Deploy a test MerkleLT contract. + ISablierV2MerkleLT testMerkleLT = merkleLockupFactory.createMerkleLT( baseParams, lockupTranched, defaults.tranchesWithPercentages(), @@ -144,7 +144,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { defaults.RECIPIENT_COUNT() ); - // Fund the MerkleLockupLT contract. + // Fund the MerkleLT contract. deal({ token: address(dai), to: address(testMerkleLT), give: defaults.AGGREGATE_AMOUNT() }); uint256 expectedStreamId = lockupTranched.nextStreamId(); @@ -185,7 +185,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { whenCalculatedAmountsSumEqualsClaimAmount { uint256 expectedStreamId = lockupTranched.nextStreamId(); - vm.expectEmit({ emitter: address(merkleLockupLT) }); + vm.expectEmit({ emitter: address(merkleLT) }); emit Claim(defaults.INDEX1(), users.recipient1, defaults.CLAIM_AMOUNT(), expectedStreamId); uint256 actualStreamId = claimLT(); @@ -205,7 +205,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { wasCanceled: false }); - assertTrue(merkleLockupLT.hasClaimed(defaults.INDEX1()), "not claimed"); + assertTrue(merkleLT.hasClaimed(defaults.INDEX1()), "not claimed"); assertEq(actualStreamId, expectedStreamId, "invalid stream id"); assertEq(actualStream, expectedStream); } diff --git a/test/integration/merkle-lockup/lt/clawback/clawback.t.sol b/test/integration/merkle-lockup/lt/clawback/clawback.t.sol index e0b8cb0a..119e2836 100644 --- a/test/integration/merkle-lockup/lt/clawback/clawback.t.sol +++ b/test/integration/merkle-lockup/lt/clawback/clawback.t.sol @@ -15,7 +15,7 @@ contract Clawback_Integration_Test is MerkleLockup_Integration_Test { function test_RevertWhen_CallerNotAdmin() external { resetPrank({ msgSender: users.eve }); vm.expectRevert(abi.encodeWithSelector(V2CoreErrors.CallerNotAdmin.selector, users.admin, users.eve)); - merkleLockupLT.clawback({ to: users.eve, amount: 1 }); + merkleLT.clawback({ to: users.eve, amount: 1 }); } modifier whenCallerAdmin() { @@ -29,7 +29,7 @@ contract Clawback_Integration_Test is MerkleLockup_Integration_Test { Errors.SablierV2MerkleLockup_CampaignNotExpired.selector, block.timestamp, defaults.EXPIRATION() ) ); - merkleLockupLT.clawback({ to: users.admin, amount: 1 }); + merkleLT.clawback({ to: users.admin, amount: 1 }); } modifier givenCampaignExpired() { @@ -49,10 +49,10 @@ contract Clawback_Integration_Test is MerkleLockup_Integration_Test { } function test_Clawback(address to) internal { - uint128 clawbackAmount = uint128(dai.balanceOf(address(merkleLockupLT))); + uint128 clawbackAmount = uint128(dai.balanceOf(address(merkleLT))); expectCallToTransfer({ to: to, amount: clawbackAmount }); - vm.expectEmit({ emitter: address(merkleLockupLT) }); + vm.expectEmit({ emitter: address(merkleLT) }); emit Clawback({ admin: users.admin, to: to, amount: clawbackAmount }); - merkleLockupLT.clawback({ to: to, amount: clawbackAmount }); + merkleLT.clawback({ to: to, amount: clawbackAmount }); } } diff --git a/test/integration/merkle-lockup/lt/constructor/constructor.t.sol b/test/integration/merkle-lockup/lt/constructor/constructor.t.sol index f8de89ac..660ba300 100644 --- a/test/integration/merkle-lockup/lt/constructor/constructor.t.sol +++ b/test/integration/merkle-lockup/lt/constructor/constructor.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.22 <0.9.0; -import { SablierV2MerkleLockupLT } from "src/SablierV2MerkleLockupLT.sol"; -import { MerkleLockupLT } from "src/types/DataTypes.sol"; +import { SablierV2MerkleLT } from "src/SablierV2MerkleLT.sol"; +import { MerkleLT } from "src/types/DataTypes.sol"; import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; -contract Constructor_MerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test { +contract Constructor_MerkleLT_Integration_Test is MerkleLockup_Integration_Test { /// @dev Needed to prevent "Stack too deep" error struct Vars { address actualAdmin; @@ -18,7 +18,7 @@ contract Constructor_MerkleLockupLT_Integration_Test is MerkleLockup_Integration uint40 actualExpiration; address actualLockupTranched; bytes32 actualMerkleRoot; - MerkleLockupLT.TrancheWithPercentage[] actualTranchesWithPercentages; + MerkleLT.TrancheWithPercentage[] actualTranchesWithPercentages; bool actualTransferable; address expectedAdmin; uint256 expectedAllowance; @@ -29,57 +29,57 @@ contract Constructor_MerkleLockupLT_Integration_Test is MerkleLockup_Integration address expectedLockupTranched; bytes32 expectedMerkleRoot; bytes32 expectedName; - MerkleLockupLT.TrancheWithPercentage[] expectedTranchesWithPercentages; + MerkleLT.TrancheWithPercentage[] expectedTranchesWithPercentages; bool expectedTransferable; } function test_Constructor() external { - SablierV2MerkleLockupLT constructedLockupLT = - new SablierV2MerkleLockupLT(defaults.baseParams(), lockupTranched, defaults.tranchesWithPercentages()); + SablierV2MerkleLT constructedLT = + new SablierV2MerkleLT(defaults.baseParams(), lockupTranched, defaults.tranchesWithPercentages()); Vars memory vars; - vars.actualAdmin = constructedLockupLT.admin(); + vars.actualAdmin = constructedLT.admin(); vars.expectedAdmin = users.admin; assertEq(vars.actualAdmin, vars.expectedAdmin, "admin"); - vars.actualAllowance = dai.allowance(address(constructedLockupLT), address(lockupTranched)); + vars.actualAllowance = dai.allowance(address(constructedLT), address(lockupTranched)); vars.expectedAllowance = MAX_UINT256; assertEq(vars.actualAllowance, vars.expectedAllowance, "allowance"); - vars.actualAsset = address(constructedLockupLT.ASSET()); + vars.actualAsset = address(constructedLT.ASSET()); vars.expectedAsset = address(dai); assertEq(vars.actualAsset, vars.expectedAsset, "asset"); - vars.actualCancelable = constructedLockupLT.CANCELABLE(); + vars.actualCancelable = constructedLT.CANCELABLE(); vars.expectedCancelable = defaults.CANCELABLE(); assertEq(vars.actualCancelable, vars.expectedCancelable, "cancelable"); - vars.actualExpiration = constructedLockupLT.EXPIRATION(); + vars.actualExpiration = constructedLT.EXPIRATION(); vars.expectedExpiration = defaults.EXPIRATION(); assertEq(vars.actualExpiration, vars.expectedExpiration, "expiration"); - vars.actualIpfsCID = constructedLockupLT.ipfsCID(); + vars.actualIpfsCID = constructedLT.ipfsCID(); vars.expectedIpfsCID = defaults.IPFS_CID(); assertEq(vars.actualIpfsCID, vars.expectedIpfsCID, "ipfsCID"); - vars.actualLockupTranched = address(constructedLockupLT.LOCKUP_TRANCHED()); + vars.actualLockupTranched = address(constructedLT.LOCKUP_TRANCHED()); vars.expectedLockupTranched = address(lockupTranched); assertEq(vars.actualLockupTranched, vars.expectedLockupTranched, "lockupTranched"); - vars.actualName = constructedLockupLT.name(); + vars.actualName = constructedLT.name(); vars.expectedName = defaults.NAME_BYTES32(); assertEq(bytes32(abi.encodePacked(vars.actualName)), vars.expectedName, "name"); - vars.actualMerkleRoot = constructedLockupLT.MERKLE_ROOT(); + vars.actualMerkleRoot = constructedLT.MERKLE_ROOT(); vars.expectedMerkleRoot = defaults.MERKLE_ROOT(); assertEq(vars.actualMerkleRoot, vars.expectedMerkleRoot, "merkleRoot"); - vars.actualTranchesWithPercentages = constructedLockupLT.getTranchesWithPercentages(); + vars.actualTranchesWithPercentages = constructedLT.getTranchesWithPercentages(); vars.expectedTranchesWithPercentages = defaults.tranchesWithPercentages(); assertEq(vars.actualTranchesWithPercentages, vars.expectedTranchesWithPercentages, "tranchesWithPercentages"); - vars.actualTransferable = constructedLockupLT.TRANSFERABLE(); + vars.actualTransferable = constructedLT.TRANSFERABLE(); vars.expectedTransferable = defaults.TRANSFERABLE(); assertEq(vars.actualTransferable, vars.expectedTransferable, "transferable"); } diff --git a/test/integration/merkle-lockup/lt/has-claimed/hasClaimed.t.sol b/test/integration/merkle-lockup/lt/has-claimed/hasClaimed.t.sol index a46bd722..54b13ac6 100644 --- a/test/integration/merkle-lockup/lt/has-claimed/hasClaimed.t.sol +++ b/test/integration/merkle-lockup/lt/has-claimed/hasClaimed.t.sol @@ -10,7 +10,7 @@ contract HasClaimed_Integration_Test is MerkleLockup_Integration_Test { function test_HasClaimed_IndexNotInTree() external { uint256 indexNotInTree = 1337e18; - assertFalse(merkleLockupLT.hasClaimed(indexNotInTree), "claimed"); + assertFalse(merkleLT.hasClaimed(indexNotInTree), "claimed"); } modifier whenIndexInTree() { @@ -18,7 +18,7 @@ contract HasClaimed_Integration_Test is MerkleLockup_Integration_Test { } function test_HasClaimed_NotClaimed() external whenIndexInTree { - assertFalse(merkleLockupLT.hasClaimed(defaults.INDEX1()), "claimed"); + assertFalse(merkleLT.hasClaimed(defaults.INDEX1()), "claimed"); } modifier givenRecipientHasClaimed() { @@ -27,6 +27,6 @@ contract HasClaimed_Integration_Test is MerkleLockup_Integration_Test { } function test_HasClaimed() external whenIndexInTree givenRecipientHasClaimed { - assertTrue(merkleLockupLT.hasClaimed(defaults.INDEX1()), "not claimed"); + assertTrue(merkleLT.hasClaimed(defaults.INDEX1()), "not claimed"); } } diff --git a/test/integration/merkle-lockup/lt/has-expired/hasExpired.t.sol b/test/integration/merkle-lockup/lt/has-expired/hasExpired.t.sol index c1775927..93a47eaa 100644 --- a/test/integration/merkle-lockup/lt/has-expired/hasExpired.t.sol +++ b/test/integration/merkle-lockup/lt/has-expired/hasExpired.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.22 <0.9.0; -import { ISablierV2MerkleLockupLT } from "src/interfaces/ISablierV2MerkleLockupLT.sol"; +import { ISablierV2MerkleLT } from "src/interfaces/ISablierV2MerkleLT.sol"; import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; @@ -11,7 +11,7 @@ contract HasExpired_Integration_Test is MerkleLockup_Integration_Test { } function test_HasExpired_ExpirationZero() external { - ISablierV2MerkleLockupLT testLockup = createMerkleLockupLT({ expiration: 0 }); + ISablierV2MerkleLT testLockup = createMerkleLT({ expiration: 0 }); assertFalse(testLockup.hasExpired(), "campaign expired"); } @@ -20,16 +20,16 @@ contract HasExpired_Integration_Test is MerkleLockup_Integration_Test { } function test_HasExpired_ExpirationLessThanBlockTimestamp() external view givenExpirationNotZero { - assertFalse(merkleLockupLT.hasExpired(), "campaign expired"); + assertFalse(merkleLT.hasExpired(), "campaign expired"); } function test_HasExpired_ExpirationEqualToBlockTimestamp() external givenExpirationNotZero { vm.warp({ newTimestamp: defaults.EXPIRATION() }); - assertTrue(merkleLockupLT.hasExpired(), "campaign not expired"); + assertTrue(merkleLT.hasExpired(), "campaign not expired"); } function test_HasExpired_ExpirationGreaterThanBlockTimestamp() external givenExpirationNotZero { vm.warp({ newTimestamp: defaults.EXPIRATION() + 1 seconds }); - assertTrue(merkleLockupLT.hasExpired(), "campaign not expired"); + assertTrue(merkleLT.hasExpired(), "campaign not expired"); } } diff --git a/test/utils/Assertions.sol b/test/utils/Assertions.sol index 16866028..d4fa320d 100644 --- a/test/utils/Assertions.sol +++ b/test/utils/Assertions.sol @@ -4,30 +4,25 @@ pragma solidity >=0.8.22; import { PRBMathAssertions } from "@prb/math/test/utils/Assertions.sol"; -import { MerkleLockupLT } from "src/types/DataTypes.sol"; +import { MerkleLT } from "src/types/DataTypes.sol"; abstract contract Assertions is PRBMathAssertions { - event log_named_array(string key, MerkleLockupLT.TrancheWithPercentage[] tranchesWithPercentages); + event log_named_array(string key, MerkleLT.TrancheWithPercentage[] tranchesWithPercentages); - /// @dev Compares two {MerkleLockupLT.TrancheWithPercentage} arrays. - function assertEq( - MerkleLockupLT.TrancheWithPercentage[] memory a, - MerkleLockupLT.TrancheWithPercentage[] memory b - ) - internal - { + /// @dev Compares two {MerkleLT.TrancheWithPercentage} arrays. + function assertEq(MerkleLT.TrancheWithPercentage[] memory a, MerkleLT.TrancheWithPercentage[] memory b) internal { if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { - emit log("Error: a == b not satisfied [MerkleLockupLT.TrancheWithPercentage[]]"); + emit log("Error: a == b not satisfied [MerkleLT.TrancheWithPercentage[]]"); emit log_named_array(" Left", a); emit log_named_array(" Right", b); fail(); } } - /// @dev Compares two {MerkleLockupLT.TrancheWithPercentage} arrays. + /// @dev Compares two {MerkleLT.TrancheWithPercentage} arrays. function assertEq( - MerkleLockupLT.TrancheWithPercentage[] memory a, - MerkleLockupLT.TrancheWithPercentage[] memory b, + MerkleLT.TrancheWithPercentage[] memory a, + MerkleLT.TrancheWithPercentage[] memory b, string memory err ) internal diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index 2bfba0f4..4a3dba1f 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -7,7 +7,7 @@ import { ud2x18 } from "@prb/math/src/UD2x18.sol"; import { UD60x18, ud } from "@prb/math/src/UD60x18.sol"; import { Broker, LockupDynamic, LockupLinear, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { Batch, MerkleLockup, MerkleLockupLT } from "src/types/DataTypes.sol"; +import { Batch, MerkleLockup, MerkleLT } from "src/types/DataTypes.sol"; import { ArrayBuilder } from "./ArrayBuilder.sol"; import { BatchBuilder } from "./BatchBuilder.sol"; @@ -147,13 +147,13 @@ contract Defaults is Merkle { function tranchesWithPercentages() public pure - returns (MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages_) + returns (MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages_) { - tranchesWithPercentages_ = new MerkleLockupLT.TrancheWithPercentage[](2); + tranchesWithPercentages_ = new MerkleLT.TrancheWithPercentage[](2); tranchesWithPercentages_[0] = - MerkleLockupLT.TrancheWithPercentage({ unlockPercentage: ud2x18(0.25e18), duration: 2500 seconds }); + MerkleLT.TrancheWithPercentage({ unlockPercentage: ud2x18(0.25e18), duration: 2500 seconds }); tranchesWithPercentages_[1] = - MerkleLockupLT.TrancheWithPercentage({ unlockPercentage: ud2x18(0.75e18), duration: 7500 seconds }); + MerkleLT.TrancheWithPercentage({ unlockPercentage: ud2x18(0.75e18), duration: 7500 seconds }); } /*////////////////////////////////////////////////////////////////////////// @@ -339,7 +339,7 @@ contract Defaults is Merkle { tranches_[1] = LockupTranched.Tranche({ amount: 7500e18, timestamp: uint40(block.timestamp) + TOTAL_DURATION }); } - /// @dev Mirros the logic from {SablierV2MerkleLockupLT._calculateTranches}. + /// @dev Mirros the logic from {SablierV2MerkleLT._calculateTranches}. function tranches(uint128 totalAmount) public view returns (LockupTranched.Tranche[] memory tranches_) { tranches_ = tranches(); diff --git a/test/utils/Events.sol b/test/utils/Events.sol index f27d8d13..9910c0a4 100644 --- a/test/utils/Events.sol +++ b/test/utils/Events.sol @@ -5,27 +5,27 @@ import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; -import { ISablierV2MerkleLockupLL } from "src/interfaces/ISablierV2MerkleLockupLL.sol"; -import { ISablierV2MerkleLockupLT } from "src/interfaces/ISablierV2MerkleLockupLT.sol"; -import { MerkleLockup, MerkleLockupLT } from "src/types/DataTypes.sol"; +import { ISablierV2MerkleLL } from "src/interfaces/ISablierV2MerkleLL.sol"; +import { ISablierV2MerkleLT } from "src/interfaces/ISablierV2MerkleLT.sol"; +import { MerkleLockup, MerkleLT } from "src/types/DataTypes.sol"; /// @notice Abstract contract containing all the events emitted by the protocol. abstract contract Events { event Claim(uint256 index, address indexed recipient, uint128 amount, uint256 indexed streamId); event Clawback(address indexed admin, address indexed to, uint128 amount); - event CreateMerkleLockupLL( - ISablierV2MerkleLockupLL indexed merkleLockupLL, + event CreateMerkleLL( + ISablierV2MerkleLL indexed merkleLL, MerkleLockup.ConstructorParams baseParams, ISablierV2LockupLinear lockupLinear, LockupLinear.Durations streamDurations, uint256 aggregateAmount, uint256 recipientCount ); - event CreateMerkleLockupLT( - ISablierV2MerkleLockupLT indexed merkleLockupLT, + event CreateMerkleLT( + ISablierV2MerkleLT indexed merkleLT, MerkleLockup.ConstructorParams baseParams, ISablierV2LockupTranched lockupTranched, - MerkleLockupLT.TrancheWithPercentage[] tranchesWithPercentages, + MerkleLT.TrancheWithPercentage[] tranchesWithPercentages, uint256 totalDuration, uint256 aggregateAmount, uint256 recipientCount From 470aea1d97186466062bb42f8676ad183c908435 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Thu, 11 Apr 2024 23:01:12 +0100 Subject: [PATCH 38/61] build: bump sphinx --- bun.lockb | Bin 307932 -> 307932 bytes package.json | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bun.lockb b/bun.lockb index 7b09b4318df7b209e70832bf3f7c9527c3952fa4..fbf1247899869434e4fd68677dd0d1224df23551 100755 GIT binary patch delta 475 zcmccfROrrAp$U2lS~s6Bd+T|)@J7nXtH&Iq^c;67N=UV>vM5@2{9jLNJljUUTfUQ1 zwrWhi@RoCPq@RDn%~~m;=tj0swgOSw(ql;eyJ;yYu$>^Bp3&0OlvKjcJ^E$E1Un?;y;zo9>(wUtxF7z zd2?{-*8H_*n?F0hU#4~0QOV;L|rYn{l2wZ*!JU#*4(7!y#@GoAWVROs}?KmSi;BKGlZ#eh{O@_R@al4-PSAX$BVNmKK(VNd_inX~~8r zDV8RNNk)mL78V93hL*-jMy4rgX~q`CiAgz`sYSXuiAlx8>Y0A<0<$PPRQ2?K=b06^ k|GU7EzS72LtcYp5B z8>e>f4c2?g@3wJH!i4`<<2O9Dzq%%i-DK9n-kH{^Z_jwG&TC7LEt*mz`?7uRZ^rF& ze={vLVl>+BtjBE5$7nXa+J;$@(PaBn8|M2#K)SS_`GZ4Ds;RlDsj;bnd6IFWv4usl zNus%tiD{~VNt&@)Qi@?pvVoyVa#FHEabi+VW@?dcPGV9qv3jN-yud8V4plw<-+5-m l?f)(?Gbu8fFK7J3Tn=;tQzpwo#p%qdEDGBXcd Date: Fri, 12 Apr 2024 12:06:26 +0100 Subject: [PATCH 39/61] refactor: rename Batch to BatchLockup (#322) * refactor: rename Batch to BatchLockup * refactor: rename 'batchLockup' to 'batch' --------- Co-authored-by: Paul Razvan Berg --- .gas-snapshot | 88 ++++++++++++------- foundry.toml | 2 +- precompiles/Precompiles.sol | 30 ++++--- script/Base.s.sol | 2 +- script/DeployBatch.t.sol | 22 ----- script/DeployBatchLockup.t.sol | 22 +++++ script/DeployDeterministicBatch.s.sol | 25 ------ script/DeployDeterministicBatchLockup.s.sol | 25 ++++++ script/DeployDeterministicPeriphery.s.sol | 19 ++-- script/DeployPeriphery.s.sol | 19 ++-- script/DeployProtocol.s.sol | 14 +-- shell/prepare-artifacts.sh | 4 +- shell/update-precompiles.sh | 4 +- ...erV2Batch.sol => SablierV2BatchLockup.sol} | 53 +++++------ ...rV2Batch.sol => ISablierV2BatchLockup.sol} | 18 ++-- src/libraries/Errors.sol | 4 +- src/types/DataTypes.sol | 2 +- test/Base.t.sol | 10 +-- test/fork/Fork.t.sol | 4 +- test/fork/assets/USDC.t.sol | 18 ++-- test/fork/assets/USDT.t.sol | 18 ++-- .../createWithTimestampsLD.t.sol | 18 ++-- .../createWithTimestampsLL.t.sol | 17 ++-- .../createWithTimestampsLT.t.sol | 18 ++-- test/integration/Integration.t.sol | 4 +- .../createWithDurationsLD.t.sol | 22 +++-- .../createWithDurationsLD.tree | 0 .../createWithDurationsLL.t.sol | 22 +++-- .../createWithDurationsLL.tree | 0 .../createWithDurationsLT.t.sol | 22 +++-- .../createWithDurationsLT.tree | 0 .../createWithTimestampsLD.t.sol | 22 +++-- .../createWithTimestampsLD.tree | 0 .../createWithTimestamps.t.sol | 22 +++-- .../createWithTimestamps.tree | 0 .../createWithTimestampsLT.t.sol | 22 +++-- .../createWithTimestampsLT.tree | 0 ...atchBuilder.sol => BatchLockupBuilder.sol} | 88 +++++++++---------- test/utils/Defaults.sol | 54 ++++++------ test/utils/DeployOptimized.sol | 16 ++-- test/utils/Precompiles.t.sol | 16 ++-- 41 files changed, 411 insertions(+), 355 deletions(-) delete mode 100644 script/DeployBatch.t.sol create mode 100644 script/DeployBatchLockup.t.sol delete mode 100644 script/DeployDeterministicBatch.s.sol create mode 100644 script/DeployDeterministicBatchLockup.s.sol rename src/{SablierV2Batch.sol => SablierV2BatchLockup.sol} (89%) rename src/interfaces/{ISablierV2Batch.sol => ISablierV2BatchLockup.sol} (92%) rename test/fork/{batch => batch-lockup}/createWithTimestampsLD.t.sol (82%) rename test/fork/{batch => batch-lockup}/createWithTimestampsLL.t.sol (82%) rename test/fork/{batch => batch-lockup}/createWithTimestampsLT.t.sol (82%) rename test/integration/{batch => batch-lockup}/create-with-durations-ld/createWithDurationsLD.t.sol (59%) rename test/integration/{batch => batch-lockup}/create-with-durations-ld/createWithDurationsLD.tree (100%) rename test/integration/{batch => batch-lockup}/create-with-durations-ll/createWithDurationsLL.t.sol (59%) rename test/integration/{batch => batch-lockup}/create-with-durations-ll/createWithDurationsLL.tree (100%) rename test/integration/{batch => batch-lockup}/create-with-durations-lt/createWithDurationsLT.t.sol (59%) rename test/integration/{batch => batch-lockup}/create-with-durations-lt/createWithDurationsLT.tree (100%) rename test/integration/{batch => batch-lockup}/create-with-timestamps-ld/createWithTimestampsLD.t.sol (59%) rename test/integration/{batch => batch-lockup}/create-with-timestamps-ld/createWithTimestampsLD.tree (100%) rename test/integration/{batch => batch-lockup}/create-with-timestamps-ll/createWithTimestamps.t.sol (59%) rename test/integration/{batch => batch-lockup}/create-with-timestamps-ll/createWithTimestamps.tree (100%) rename test/integration/{batch => batch-lockup}/create-with-timestamps-lt/createWithTimestampsLT.t.sol (59%) rename test/integration/{batch => batch-lockup}/create-with-timestamps-lt/createWithTimestampsLT.tree (100%) rename test/utils/{BatchBuilder.sol => BatchLockupBuilder.sol} (60%) diff --git a/.gas-snapshot b/.gas-snapshot index 97828581..ec228115 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,33 +1,55 @@ -Claim_Integration_Test:test_Claim() (gas: 278641) -Claim_Integration_Test:test_RevertGiven_AlreadyClaimed() (gas: 266327) -Claim_Integration_Test:test_RevertGiven_CampaignExpired() (gas: 17745) -Claim_Integration_Test:test_RevertGiven_ProtocolFeeNotZero() (gas: 84510) -Clawback_Integration_Test:testFuzz_Clawback(address) (runs: 20, μ: 316725, ~: 316725) -Clawback_Integration_Test:testFuzz_Clawback_CampaignNotExpired(address) (runs: 20, μ: 88746, ~: 88746) -Clawback_Integration_Test:test_Clawback() (gas: 271907) -Clawback_Integration_Test:test_RevertGiven_CampaignNotExpired() (gas: 30429) -Constructor_MerkleStreamerLL_Integration_Test:test_Constructor() (gas: 1198502) -CreateMerkleStreamerLL_Integration_Test:testFuzz_CreateMerkleStreamerLL(address,uint40) (runs: 20, μ: 1126334, ~: 1126334) -CreateMerkleStreamerLL_Integration_Test:test_RevertGiven_AlreadyDeployed() (gas: 8937393460516730624) -CreateWithDeltas_Integration_Test:test_BatchCreateWithDeltas() (gas: 2070068) -CreateWithDurations_Integration_Test:test_BatchCreateWithDurations() (gas: 1333914) -CreateWithMilestones_Integration_Test:test_BatchCreateWithMilestones() (gas: 2063258) -CreateWithRange_Integration_Test:test_CreateWithRange() (gas: 1336083) -HasClaimed_Integration_Test:test_HasClaimed() (gas: 251704) -HasClaimed_Integration_Test:test_HasClaimed_IndexNotInTree() (gas: 7997) -HasClaimed_Integration_Test:test_HasClaimed_NotClaimed() (gas: 13254) -HasExpired_Integration_Test:test_HasExpired_ExpirationEqualToCurrentTime() (gas: 13957) -HasExpired_Integration_Test:test_HasExpired_ExpirationGreaterThanCurrentTime() (gas: 14051) -HasExpired_Integration_Test:test_HasExpired_ExpirationLessThanCurrentTime() (gas: 5746) -HasExpired_Integration_Test:test_HasExpired_ExpirationZero() (gas: 1068623) -MerkleBuilder_Test:testFuzz_ComputeLeaf(uint256,address,uint128) (runs: 20, μ: 1176, ~: 1176) -MerkleBuilder_Test:testFuzz_ComputeLeaves((uint256,address,uint128)[]) (runs: 20, μ: 328702, ~: 368679) -Precompiles_Test:test_DeployBatch() (gas: 2794084) -Precompiles_Test:test_DeployMerkleStreamerFactory() (gas: 3098051) -Precompiles_Test:test_DeployPeriphery() (gas: 5886439) -USDC_CreateWithMilestones_Batch_Fork_Test:testForkFuzz_CreateWithMilestones((uint128,address,address,uint128,uint40,(uint128,uint64,uint40)[])) (runs: 20, μ: 35758280, ~: 21718385) -USDC_CreateWithRange_Batch_Fork_Test:testForkFuzz_CreateWithRange((uint128,(uint40,uint40,uint40),address,address,uint128)) (runs: 20, μ: 1353143, ~: 1508161) -USDC_MerkleStreamerLL_Fork_Test:testForkFuzz_MerkleStreamerLL((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 20, μ: 4504409, ~: 3860802) -USDT_CreateWithMilestones_Batch_Fork_Test:testForkFuzz_CreateWithMilestones((uint128,address,address,uint128,uint40,(uint128,uint64,uint40)[])) (runs: 20, μ: 35758280, ~: 21718385) -USDT_CreateWithRange_Batch_Fork_Test:testForkFuzz_CreateWithRange((uint128,(uint40,uint40,uint40),address,address,uint128)) (runs: 20, μ: 1353143, ~: 1508161) -USDT_MerkleStreamerLL_Fork_Test:testForkFuzz_MerkleStreamerLL((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 20, μ: 4504409, ~: 3860802) \ No newline at end of file +BaseScript_Test:test_ConstructCreate2Salt() (gas: 18701) +Claim_Integration_Test:test_Claim() (gas: 296792) +Claim_Integration_Test:test_Claim() (gas: 359541) +Claim_Integration_Test:test_Claim_CalculatedAmountsSumNotEqualClaimAmount() (gas: 1906979) +Claim_Integration_Test:test_RevertGiven_AlreadyClaimed() (gas: 274624) +Claim_Integration_Test:test_RevertGiven_AlreadyClaimed() (gas: 329502) +Claim_Integration_Test:test_RevertGiven_CampaignExpired() (gas: 17786) +Claim_Integration_Test:test_RevertGiven_CampaignExpired() (gas: 17811) +Clawback_Integration_Test:testFuzz_Clawback(address) (runs: 38, μ: 295825, ~: 295825) +Clawback_Integration_Test:testFuzz_Clawback(address) (runs: 38, μ: 350750, ~: 350750) +Clawback_Integration_Test:test_Clawback() (gas: 278339) +Clawback_Integration_Test:test_Clawback() (gas: 333264) +Clawback_Integration_Test:test_RevertGiven_CampaignNotExpired() (gas: 19523) +Clawback_Integration_Test:test_RevertGiven_CampaignNotExpired() (gas: 19523) +Constructor_MerkleLL_Integration_Test:test_Constructor() (gas: 1150963) +Constructor_MerkleLT_Integration_Test:test_Constructor() (gas: 1495600) +CreateMerkleLL_Integration_Test:testFuzz_CreateMerkleLL(address,uint40) (runs: 38, μ: 1122843, ~: 1123367) +CreateMerkleLL_Integration_Test:test_RevertGiven_CreatedAlready() (gas: 8937393460516730662) +CreateMerkleLT_Integration_Test:testFuzz_CreateMerkleLT(address,uint40) (runs: 38, μ: 1431123, ~: 1431123) +CreateMerkleLT_Integration_Test:test_RevertGiven_CreatedAlready() (gas: 8937393460516730766) +CreateWithDurationsLD_Integration_Test:test_BatchCreateWithDurations() (gas: 2021956) +CreateWithDurationsLL_Integration_Test:test_BatchCreateWithDurations() (gas: 1498705) +CreateWithDurationsLT_Integration_Test:test_BatchCreateWithDurations() (gas: 2007334) +CreateWithTimestampsLD_Integration_Test:test_BatchCreateWithTimestamps() (gas: 2005042) +CreateWithTimestampsLL_Integration_Test:test_BatchCreateWithTimestamps() (gas: 1497928) +CreateWithTimestampsLT_Integration_Test:test_BatchCreateWithTimestamps() (gas: 1991536) +HasClaimed_Integration_Test:test_HasClaimed() (gas: 263197) +HasClaimed_Integration_Test:test_HasClaimed() (gas: 318100) +HasClaimed_Integration_Test:test_HasClaimed_IndexNotInTree() (gas: 11174) +HasClaimed_Integration_Test:test_HasClaimed_IndexNotInTree() (gas: 11196) +HasClaimed_Integration_Test:test_HasClaimed_NotClaimed() (gas: 16409) +HasClaimed_Integration_Test:test_HasClaimed_NotClaimed() (gas: 16431) +HasExpired_Integration_Test:test_HasExpired_ExpirationEqualToBlockTimestamp() (gas: 14692) +HasExpired_Integration_Test:test_HasExpired_ExpirationEqualToBlockTimestamp() (gas: 14714) +HasExpired_Integration_Test:test_HasExpired_ExpirationGreaterThanBlockTimestamp() (gas: 14788) +HasExpired_Integration_Test:test_HasExpired_ExpirationGreaterThanBlockTimestamp() (gas: 14810) +HasExpired_Integration_Test:test_HasExpired_ExpirationLessThanBlockTimestamp() (gas: 8902) +HasExpired_Integration_Test:test_HasExpired_ExpirationLessThanBlockTimestamp() (gas: 8924) +HasExpired_Integration_Test:test_HasExpired_ExpirationZero() (gas: 1051899) +HasExpired_Integration_Test:test_HasExpired_ExpirationZero() (gas: 1343143) +MerkleBuilder_Test:testFuzz_ComputeLeaf(uint256,address,uint128) (runs: 38, μ: 4323, ~: 4323) +MerkleBuilder_Test:testFuzz_ComputeLeaves((uint256,address,uint128)[]) (runs: 38, μ: 366364, ~: 398189) +Precompiles_Test:test_DeployBatchLockup() (gas: 3323914) +Precompiles_Test:test_DeployMerkleLockupFactory() (gas: 6986949) +Precompiles_Test:test_DeployPeriphery() (gas: 10310682) +USDC_CreateWithTimestamps_LockupDynamic_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLD((uint128,address,address,uint128,uint40,(uint128,uint64,uint40)[])) (runs: 38, μ: 34210026, ~: 30903106) +USDC_CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLL((uint128,(uint40,uint40,uint40),address,address,uint128)) (runs: 38, μ: 1592674, ~: 1502438) +USDC_CreateWithTimestamps_LockupTranched_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLT((uint128,address,address,uint128,uint40,(uint128,uint40)[])) (runs: 36, μ: 24927469, ~: 23804750) +USDC_MerkleLL_Fork_Test:testForkFuzz_MerkleLL((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 38, μ: 5247542, ~: 5027798) +USDC_MerkleLT_Fork_Test:testForkFuzz_MerkleLT((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 38, μ: 5622048, ~: 5399736) +USDT_CreateWithTimestamps_LockupDynamic_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLD((uint128,address,address,uint128,uint40,(uint128,uint64,uint40)[])) (runs: 38, μ: 30978443, ~: 21899958) +USDT_CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLL((uint128,(uint40,uint40,uint40),address,address,uint128)) (runs: 38, μ: 1953484, ~: 2211406) +USDT_CreateWithTimestamps_LockupTranched_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLT((uint128,address,address,uint128,uint40,(uint128,uint40)[])) (runs: 38, μ: 27146173, ~: 17675525) +USDT_MerkleLL_Fork_Test:testForkFuzz_MerkleLL((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 38, μ: 5214786, ~: 4997326) +USDT_MerkleLT_Fork_Test:testForkFuzz_MerkleLT((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 38, μ: 5585576, ~: 5365241) \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 56ea645a..fa3849d6 100644 --- a/foundry.toml +++ b/foundry.toml @@ -11,7 +11,7 @@ { access = "read-write", path = "./cache" }, ] gas_reports = [ - "SablierV2Batch", + "SablierV2BatchLockup", "SablierV2MerkleLL", "SablierV2MerkleLockupFactory", "SablierV2MerkleLT", diff --git a/precompiles/Precompiles.sol b/precompiles/Precompiles.sol index 5146f8fa..5cb05410 100644 --- a/precompiles/Precompiles.sol +++ b/precompiles/Precompiles.sol @@ -8,7 +8,7 @@ import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablier import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; import { ISablierV2NFTDescriptor } from "@sablier/v2-core/src/interfaces/ISablierV2NFTDescriptor.sol"; -import { ISablierV2Batch } from "../src/interfaces/ISablierV2Batch.sol"; +import { ISablierV2BatchLockup } from "../src/interfaces/ISablierV2BatchLockup.sol"; import { ISablierV2MerkleLockupFactory } from "../src/interfaces/ISablierV2MerkleLockupFactory.sol"; contract Precompiles { @@ -16,8 +16,8 @@ contract Precompiles { BYTECODES //////////////////////////////////////////////////////////////////////////*/ - bytes public constant BYTECODE_BATCH = - hex"6080806040523461001657611f10908161001c8239f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c90816337266dd31461125b5750806349a32c4014610eb7578063606ef87514610b6b5780639e743f29146107fc578063a514f83e146104295763f7ca34eb1461006157600080fd5b346103555761006f366115d6565b9283156103ff5760009060005b8581106103cb57506001600160a01b036100999116918483611b81565b6100a284611858565b9260005b8581106100bf57604051806100bb8782611664565b0390f35b6100ca818786611b1e565b6100d3906118a7565b906100df818887611b1e565b6020016100eb906118a7565b85886100f8848284611b1e565b60400161010490611725565b61010f858385611b1e565b60600161011b906118bb565b610126868486611b1e565b608001610132906118bb565b9061013e878587611b1e565b60a00161014a90611b5e565b9287610157818789611b1e565b60c0810161016491611a2a565b96610170929198611b1e565b60e00196604051996101818b6117aa565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c086015236906101e592611a7e565b60e0840152366101f4916119cc565b610100830152604051917f31df3d48000000000000000000000000000000000000000000000000000000008352600483016020905280516001600160a01b0316602484015260208101516001600160a01b0316604484015260408101516fffffffffffffffffffffffffffffffff16606484015260608101516001600160a01b031660848401526080810151151560a484015260a0810151151560c484015260c081015164ffffffffff1660e48401528260e082015161010482016101409052805180610164840152610184830191602001906000905b80821061036e5750505081906103006101006020950151610124840190602080916001600160a01b0381511684520151910152565b03816000885af180156103625760009061032a575b600192506103238288611a05565b52016100a6565b506020823d60201161035a575b81610344602093836117ff565b810103126103555760019151610315565b600080fd5b3d9150610337565b6040513d6000823e3d90fd5b919493509160206060826103bc600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b019501920186939492916102cb565b916001906fffffffffffffffffffffffffffffffff6103f660406103f0878b8a611b1e565b01611725565b1601920161007c565b60046040517f763e559d000000000000000000000000000000000000000000000000000000008152fd5b346103555760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610355576104606116a0565b6104686115c0565b9060443567ffffffffffffffff8082116103555736602383011215610355578160040135116103555736602461014083600401350283010111610355578060040135156103ff576000805b826004013582106107c7576104d39150846001600160a01b038516611b81565b6104e08160040135611858565b9160005b826004013581106104fd57604051806100bb8682611664565b61051761051282856004013560248701611b70565b6118a7565b9083610536602061053084846004013560248601611b70565b016118a7565b61054d60406103f085856004013560248701611b70565b9361056b606061056586866004013560248801611b70565b016118bb565b946080956fffffffffffffffffffffffffffffffff6105968861056589896004013560248b01611b70565b926001600160a01b036101006105c78a6105b88160048d013560248e01611b70565b9a602481600401359101611b70565b019681604051976105d789611742565b1687521660208601521660408401526001600160a01b038b166060840152151586830152151560a082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6084360301126103555761075c9261067f60e093604051610644816117c7565b61065060a08501611939565b815261066f8660c095610664878201611939565b602085015201611939565b60408201528385015236906119cc565b83830152604051957f53b157270000000000000000000000000000000000000000000000000000000087526001600160a01b0383511660048801526001600160a01b0360208401511660248801526fffffffffffffffffffffffffffffffff60408401511660448801526001600160a01b0360608401511660648801528201511515608487015260a0820151151560a4870152810151604064ffffffffff918281511660c48901528260208201511660e48901520151166101048601520151610124840190602080916001600160a01b0381511684520151910152565b6020826101648160006001600160a01b0388165af1801561036257600090610794575b6001925061078d8287611a05565b52016104e4565b506020823d6020116107bf575b816107ae602093836117ff565b81010312610355576001915161077f565b3d91506107a1565b6001906fffffffffffffffffffffffffffffffff6107f260406103f086886004013560248a01611b70565b16019101906104b3565b346103555761080a366115d6565b9283156103ff5760009060005b858110610b3d57506001600160a01b036108349116918483611b81565b61083d84611858565b9260005b85811061085657604051806100bb8782611664565b610861818786611b1e565b61086a906118a7565b90610876818887611b1e565b602001610882906118a7565b858861088f848284611b1e565b60400161089b90611725565b6108a6858385611b1e565b6060016108b2906118bb565b6108bd868486611b1e565b6080016108c9906118bb565b906108d5878587611b1e565b60a0016108e190611b5e565b92876108ee818789611b1e565b60c081016108fb916118c8565b96610907929198611b1e565b60e00196604051996109188b6117aa565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c0860152369061097c9261194b565b60e08401523661098b916119cc565b610100830152604051917f32fbe22b000000000000000000000000000000000000000000000000000000008352600483016020905280516001600160a01b0316602484015260208101516001600160a01b0316604484015260408101516fffffffffffffffffffffffffffffffff16606484015260608101516001600160a01b031660848401526080810151151560a484015260a0810151151560c484015260c081015164ffffffffff1660e48401528260e082015161010482016101409052805180610164840152610184830191602001906000905b808210610af4575050508190610a976101006020950151610124840190602080916001600160a01b0381511684520151910152565b03816000885af1801561036257600090610ac1575b60019250610aba8288611a05565b5201610841565b506020823d602011610aec575b81610adb602093836117ff565b810103126103555760019151610aac565b3d9150610ace565b91949350916020604082610b2e600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291610a62565b916001906fffffffffffffffffffffffffffffffff610b6260406103f0878b8a611b1e565b16019201610817565b3461035557610b79366115d6565b9283156103ff5760009060005b858110610e8957506001600160a01b03610ba39116918483611b81565b610bac84611858565b9260005b858110610bc557604051806100bb8782611664565b610bd08187866116b6565b610bd9906118a7565b90610be58188876116b6565b602001610bf1906118a7565b8782610bfe81838a6116b6565b604001610c0a90611725565b610c1582848b6116b6565b606001610c21906118bb565b610c2c83858c6116b6565b608001610c38906118bb565b91610c4484868d6116b6565b60a08101610c5191611a2a565b94610c5d91968d6116b6565b60c0019560405198610c6e8a611742565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a08601523690610cc692611a7e565b60c084015236610cd5916119cc565b60e0830152604051917f54c02292000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001906000905b808210610e2c575050508190610dcf60e06020950151610104840190602080916001600160a01b0381511684520151910152565b03816000885af1801561036257600090610df9575b60019250610df28288611a05565b5201610bb0565b506020823d602011610e24575b81610e13602093836117ff565b810103126103555760019151610de4565b3d9150610e06565b91949350916020606082610e7a600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b01950192018693949291610d9b565b916001906fffffffffffffffffffffffffffffffff610eae60406103f0878b8a6116b6565b16019201610b86565b346103555760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035557610eee6116a0565b610ef66115c0565b9060443567ffffffffffffffff8082116103555736602383011215610355578160040135116103555736602461012083600401350283010111610355578060040135156103ff576000805b8260040135821061122657610f619150846001600160a01b038516611b81565b610f6e8160040135611858565b9160005b82600401358110610f8b57604051806100bb8682611664565b610fa061051282856004013560248701611a19565b9083610fb9602061053084846004013560248601611a19565b610fd060406103f085856004013560248701611a19565b93610fe8606061056586866004013560248801611a19565b946080956fffffffffffffffffffffffffffffffff6110138861056589896004013560248b01611a19565b926001600160a01b0360e06110438a6110348160048d013560248e01611a19565b9a602481600401359101611a19565b0196816040519761105389611742565b1687521660208601521660408401526001600160a01b038b166060840152151586830152151560a082015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff608436030112610355576111bb926110eb60e0936040516110c08161178e565b6110cc60a08501611939565b81526110db60c0809501611939565b60208201528385015236906119cc565b83830152604051957fab167ccc0000000000000000000000000000000000000000000000000000000087526001600160a01b0383511660048801526001600160a01b0360208401511660248801526fffffffffffffffffffffffffffffffff60408401511660448801526001600160a01b0360608401511660648801528201511515608487015260a0820151151560a4870152810151602064ffffffffff918281511660c489015201511660e48601520151610104840190602080916001600160a01b0381511684520151910152565b6020826101448160006001600160a01b0388165af18015610362576000906111f3575b600192506111ec8287611a05565b5201610f72565b506020823d60201161121e575b8161120d602093836117ff565b8101031261035557600191516111de565b3d9150611200565b6001906fffffffffffffffffffffffffffffffff61125160406103f086886004013560248a01611a19565b1601910190610f41565b3461035557611269366115d6565b93849391929315611598575060009060005b85811061156a57506001600160a01b036112989116918483611b81565b6112a184611858565b9260005b8581106112ba57604051806100bb8782611664565b6112c58187866116b6565b6112ce906118a7565b906112da8188876116b6565b6020016112e6906118a7565b87826112f381838a6116b6565b6040016112ff90611725565b61130a82848b6116b6565b606001611316906118bb565b61132183858c6116b6565b60800161132d906118bb565b9161133984868d6116b6565b60a08101611346916118c8565b9461135291968d6116b6565b60c00195604051986113638a611742565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a086015236906113bb9261194b565b60c0840152366113ca916119cc565b60e0830152604051917f897f362b000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001906000905b8082106115215750505081906114c460e06020950151610104840190602080916001600160a01b0381511684520151910152565b03816000885af18015610362576000906114ee575b600192506114e78288611a05565b52016112a5565b506020823d602011611519575b81611508602093836117ff565b8101031261035557600191516114d9565b3d91506114fb565b9194935091602060408261155b600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291611490565b916001906fffffffffffffffffffffffffffffffff61158f60406103f0878b8a6116b6565b1601920161127b565b807f763e559d0000000000000000000000000000000000000000000000000000000060049252fd5b602435906001600160a01b038216820361035557565b9060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc830112610355576001600160a01b0391600435838116810361035557926024359081168103610355579160443567ffffffffffffffff9283821161035557806023830112156103555781600401359384116103555760248460051b83010111610355576024019190565b602090602060408183019282815285518094520193019160005b82811061168c575050505090565b83518552938101939281019260010161167e565b600435906001600160a01b038216820361035557565b91908110156116f65760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0181360301821215610355570190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b356fffffffffffffffffffffffffffffffff811681036103555790565b610100810190811067ffffffffffffffff82111761175f57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff82111761175f57604052565b610120810190811067ffffffffffffffff82111761175f57604052565b6060810190811067ffffffffffffffff82111761175f57604052565b6080810190811067ffffffffffffffff82111761175f57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761175f57604052565b67ffffffffffffffff811161175f5760051b60200190565b9061186282611840565b61186f60405191826117ff565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061189d8294611840565b0190602036910137565b356001600160a01b03811681036103555790565b3580151581036103555790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610355570180359067ffffffffffffffff821161035557602001918160061b3603831361035557565b35906fffffffffffffffffffffffffffffffff8216820361035557565b359064ffffffffff8216820361035557565b92919261195782611840565b60409261196760405192836117ff565b819581835260208093019160061b84019381851161035557915b84831061199057505050505050565b85838303126103555783869182516119a78161178e565b6119b08661191c565b81526119bd838701611939565b83820152815201920191611981565b9190826040910312610355576040516119e48161178e565b809280356001600160a01b0381168103610355578252602090810135910152565b80518210156116f65760209160051b010190565b91908110156116f657610120020190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610355570180359067ffffffffffffffff82116103555760200191606082023603831361035557565b929192611a8a82611840565b604094611a9a60405192836117ff565b8195848352602080930191606080960285019481861161035557925b858410611ac65750505050505050565b868483031261035557825190611adb826117c7565b611ae48561191c565b8252858501359067ffffffffffffffff8216820361035557828792838b950152611b0f868801611939565b86820152815201930192611ab6565b91908110156116f65760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee181360301821215610355570190565b3564ffffffffff811681036103555790565b91908110156116f657610140020190565b90604080516020907f23b872dd000000000000000000000000000000000000000000000000000000008282015233602482015260449030828201528660648201526064815260a081019080821067ffffffffffffffff83111761175f57611bea91855285611d86565b6001600160a01b0394858516958451917fdd62ed3e0000000000000000000000000000000000000000000000000000000083523060048401521690816024820152838184818a5afa908115611d6357908891600091611d32575b5010611c54575b50505050505050565b8351956000808589017f095ea7b3000000000000000000000000000000000000000000000000000000009a8b82528560248c0152868b0152858a52611c988a6117e3565b89519082855af190611ca8611e12565b82611cff575b5081611cf4575b50611c4b57611ce896611ce394519384015260248301526000818301528152611cdd816117e3565b82611d86565b611d86565b38808080808080611c4b565b90503b151538611cb5565b809192505190858215928315611d1a575b5050509038611cae565b611d2a9350820181019101611d6e565b388581611d10565b809250858092503d8311611d5c575b611d4b81836117ff565b810103126103555787905138611c44565b503d611d41565b85513d6000823e3d90fd5b90816020910312610355575180151581036103555790565b6000806001600160a01b03611db093169360208151910182865af1611da9611e12565b9083611e70565b8051908115159182611df7575b5050611dc65750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b611e0a9250602080918301019101611d6e565b153880611dbd565b3d15611e6b573d9067ffffffffffffffff821161175f5760405191611e5f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846117ff565b82523d6000602084013e565b606090565b90611eaf5750805115611e8557805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580611efa575b611ec0575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15611eb856fea164736f6c6343000817000a"; + bytes public constant BYTECODE_BATCH_LOCKUP = + hex"6080806040523461001657611f10908161001c8239f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c90816337266dd31461125b5750806349a32c4014610eb7578063606ef87514610b6b5780639e743f29146107fc578063a514f83e146104295763f7ca34eb1461006157600080fd5b346103555761006f366115d6565b9283156103ff5760009060005b8581106103cb57506001600160a01b036100999116918483611b81565b6100a284611858565b9260005b8581106100bf57604051806100bb8782611664565b0390f35b6100ca818786611b1e565b6100d3906118a7565b906100df818887611b1e565b6020016100eb906118a7565b85886100f8848284611b1e565b60400161010490611725565b61010f858385611b1e565b60600161011b906118bb565b610126868486611b1e565b608001610132906118bb565b9061013e878587611b1e565b60a00161014a90611b5e565b9287610157818789611b1e565b60c0810161016491611a2a565b96610170929198611b1e565b60e00196604051996101818b6117aa565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c086015236906101e592611a7e565b60e0840152366101f4916119cc565b610100830152604051917f31df3d48000000000000000000000000000000000000000000000000000000008352600483016020905280516001600160a01b0316602484015260208101516001600160a01b0316604484015260408101516fffffffffffffffffffffffffffffffff16606484015260608101516001600160a01b031660848401526080810151151560a484015260a0810151151560c484015260c081015164ffffffffff1660e48401528260e082015161010482016101409052805180610164840152610184830191602001906000905b80821061036e5750505081906103006101006020950151610124840190602080916001600160a01b0381511684520151910152565b03816000885af180156103625760009061032a575b600192506103238288611a05565b52016100a6565b506020823d60201161035a575b81610344602093836117ff565b810103126103555760019151610315565b600080fd5b3d9150610337565b6040513d6000823e3d90fd5b919493509160206060826103bc600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b019501920186939492916102cb565b916001906fffffffffffffffffffffffffffffffff6103f660406103f0878b8a611b1e565b01611725565b1601920161007c565b60046040517ff8bf106c000000000000000000000000000000000000000000000000000000008152fd5b346103555760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610355576104606116a0565b6104686115c0565b9060443567ffffffffffffffff8082116103555736602383011215610355578160040135116103555736602461014083600401350283010111610355578060040135156103ff576000805b826004013582106107c7576104d39150846001600160a01b038516611b81565b6104e08160040135611858565b9160005b826004013581106104fd57604051806100bb8682611664565b61051761051282856004013560248701611b70565b6118a7565b9083610536602061053084846004013560248601611b70565b016118a7565b61054d60406103f085856004013560248701611b70565b9361056b606061056586866004013560248801611b70565b016118bb565b946080956fffffffffffffffffffffffffffffffff6105968861056589896004013560248b01611b70565b926001600160a01b036101006105c78a6105b88160048d013560248e01611b70565b9a602481600401359101611b70565b019681604051976105d789611742565b1687521660208601521660408401526001600160a01b038b166060840152151586830152151560a082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6084360301126103555761075c9261067f60e093604051610644816117c7565b61065060a08501611939565b815261066f8660c095610664878201611939565b602085015201611939565b60408201528385015236906119cc565b83830152604051957f53b157270000000000000000000000000000000000000000000000000000000087526001600160a01b0383511660048801526001600160a01b0360208401511660248801526fffffffffffffffffffffffffffffffff60408401511660448801526001600160a01b0360608401511660648801528201511515608487015260a0820151151560a4870152810151604064ffffffffff918281511660c48901528260208201511660e48901520151166101048601520151610124840190602080916001600160a01b0381511684520151910152565b6020826101648160006001600160a01b0388165af1801561036257600090610794575b6001925061078d8287611a05565b52016104e4565b506020823d6020116107bf575b816107ae602093836117ff565b81010312610355576001915161077f565b3d91506107a1565b6001906fffffffffffffffffffffffffffffffff6107f260406103f086886004013560248a01611b70565b16019101906104b3565b346103555761080a366115d6565b9283156103ff5760009060005b858110610b3d57506001600160a01b036108349116918483611b81565b61083d84611858565b9260005b85811061085657604051806100bb8782611664565b610861818786611b1e565b61086a906118a7565b90610876818887611b1e565b602001610882906118a7565b858861088f848284611b1e565b60400161089b90611725565b6108a6858385611b1e565b6060016108b2906118bb565b6108bd868486611b1e565b6080016108c9906118bb565b906108d5878587611b1e565b60a0016108e190611b5e565b92876108ee818789611b1e565b60c081016108fb916118c8565b96610907929198611b1e565b60e00196604051996109188b6117aa565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c0860152369061097c9261194b565b60e08401523661098b916119cc565b610100830152604051917f32fbe22b000000000000000000000000000000000000000000000000000000008352600483016020905280516001600160a01b0316602484015260208101516001600160a01b0316604484015260408101516fffffffffffffffffffffffffffffffff16606484015260608101516001600160a01b031660848401526080810151151560a484015260a0810151151560c484015260c081015164ffffffffff1660e48401528260e082015161010482016101409052805180610164840152610184830191602001906000905b808210610af4575050508190610a976101006020950151610124840190602080916001600160a01b0381511684520151910152565b03816000885af1801561036257600090610ac1575b60019250610aba8288611a05565b5201610841565b506020823d602011610aec575b81610adb602093836117ff565b810103126103555760019151610aac565b3d9150610ace565b91949350916020604082610b2e600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291610a62565b916001906fffffffffffffffffffffffffffffffff610b6260406103f0878b8a611b1e565b16019201610817565b3461035557610b79366115d6565b9283156103ff5760009060005b858110610e8957506001600160a01b03610ba39116918483611b81565b610bac84611858565b9260005b858110610bc557604051806100bb8782611664565b610bd08187866116b6565b610bd9906118a7565b90610be58188876116b6565b602001610bf1906118a7565b8782610bfe81838a6116b6565b604001610c0a90611725565b610c1582848b6116b6565b606001610c21906118bb565b610c2c83858c6116b6565b608001610c38906118bb565b91610c4484868d6116b6565b60a08101610c5191611a2a565b94610c5d91968d6116b6565b60c0019560405198610c6e8a611742565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a08601523690610cc692611a7e565b60c084015236610cd5916119cc565b60e0830152604051917f54c02292000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001906000905b808210610e2c575050508190610dcf60e06020950151610104840190602080916001600160a01b0381511684520151910152565b03816000885af1801561036257600090610df9575b60019250610df28288611a05565b5201610bb0565b506020823d602011610e24575b81610e13602093836117ff565b810103126103555760019151610de4565b3d9150610e06565b91949350916020606082610e7a600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b01950192018693949291610d9b565b916001906fffffffffffffffffffffffffffffffff610eae60406103f0878b8a6116b6565b16019201610b86565b346103555760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035557610eee6116a0565b610ef66115c0565b9060443567ffffffffffffffff8082116103555736602383011215610355578160040135116103555736602461012083600401350283010111610355578060040135156103ff576000805b8260040135821061122657610f619150846001600160a01b038516611b81565b610f6e8160040135611858565b9160005b82600401358110610f8b57604051806100bb8682611664565b610fa061051282856004013560248701611a19565b9083610fb9602061053084846004013560248601611a19565b610fd060406103f085856004013560248701611a19565b93610fe8606061056586866004013560248801611a19565b946080956fffffffffffffffffffffffffffffffff6110138861056589896004013560248b01611a19565b926001600160a01b0360e06110438a6110348160048d013560248e01611a19565b9a602481600401359101611a19565b0196816040519761105389611742565b1687521660208601521660408401526001600160a01b038b166060840152151586830152151560a082015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff608436030112610355576111bb926110eb60e0936040516110c08161178e565b6110cc60a08501611939565b81526110db60c0809501611939565b60208201528385015236906119cc565b83830152604051957fab167ccc0000000000000000000000000000000000000000000000000000000087526001600160a01b0383511660048801526001600160a01b0360208401511660248801526fffffffffffffffffffffffffffffffff60408401511660448801526001600160a01b0360608401511660648801528201511515608487015260a0820151151560a4870152810151602064ffffffffff918281511660c489015201511660e48601520151610104840190602080916001600160a01b0381511684520151910152565b6020826101448160006001600160a01b0388165af18015610362576000906111f3575b600192506111ec8287611a05565b5201610f72565b506020823d60201161121e575b8161120d602093836117ff565b8101031261035557600191516111de565b3d9150611200565b6001906fffffffffffffffffffffffffffffffff61125160406103f086886004013560248a01611a19565b1601910190610f41565b3461035557611269366115d6565b93849391929315611598575060009060005b85811061156a57506001600160a01b036112989116918483611b81565b6112a184611858565b9260005b8581106112ba57604051806100bb8782611664565b6112c58187866116b6565b6112ce906118a7565b906112da8188876116b6565b6020016112e6906118a7565b87826112f381838a6116b6565b6040016112ff90611725565b61130a82848b6116b6565b606001611316906118bb565b61132183858c6116b6565b60800161132d906118bb565b9161133984868d6116b6565b60a08101611346916118c8565b9461135291968d6116b6565b60c00195604051986113638a611742565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a086015236906113bb9261194b565b60c0840152366113ca916119cc565b60e0830152604051917f897f362b000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001906000905b8082106115215750505081906114c460e06020950151610104840190602080916001600160a01b0381511684520151910152565b03816000885af18015610362576000906114ee575b600192506114e78288611a05565b52016112a5565b506020823d602011611519575b81611508602093836117ff565b8101031261035557600191516114d9565b3d91506114fb565b9194935091602060408261155b600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291611490565b916001906fffffffffffffffffffffffffffffffff61158f60406103f0878b8a6116b6565b1601920161127b565b807ff8bf106c0000000000000000000000000000000000000000000000000000000060049252fd5b602435906001600160a01b038216820361035557565b9060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc830112610355576001600160a01b0391600435838116810361035557926024359081168103610355579160443567ffffffffffffffff9283821161035557806023830112156103555781600401359384116103555760248460051b83010111610355576024019190565b602090602060408183019282815285518094520193019160005b82811061168c575050505090565b83518552938101939281019260010161167e565b600435906001600160a01b038216820361035557565b91908110156116f65760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0181360301821215610355570190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b356fffffffffffffffffffffffffffffffff811681036103555790565b610100810190811067ffffffffffffffff82111761175f57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff82111761175f57604052565b610120810190811067ffffffffffffffff82111761175f57604052565b6060810190811067ffffffffffffffff82111761175f57604052565b6080810190811067ffffffffffffffff82111761175f57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761175f57604052565b67ffffffffffffffff811161175f5760051b60200190565b9061186282611840565b61186f60405191826117ff565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061189d8294611840565b0190602036910137565b356001600160a01b03811681036103555790565b3580151581036103555790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610355570180359067ffffffffffffffff821161035557602001918160061b3603831361035557565b35906fffffffffffffffffffffffffffffffff8216820361035557565b359064ffffffffff8216820361035557565b92919261195782611840565b60409261196760405192836117ff565b819581835260208093019160061b84019381851161035557915b84831061199057505050505050565b85838303126103555783869182516119a78161178e565b6119b08661191c565b81526119bd838701611939565b83820152815201920191611981565b9190826040910312610355576040516119e48161178e565b809280356001600160a01b0381168103610355578252602090810135910152565b80518210156116f65760209160051b010190565b91908110156116f657610120020190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610355570180359067ffffffffffffffff82116103555760200191606082023603831361035557565b929192611a8a82611840565b604094611a9a60405192836117ff565b8195848352602080930191606080960285019481861161035557925b858410611ac65750505050505050565b868483031261035557825190611adb826117c7565b611ae48561191c565b8252858501359067ffffffffffffffff8216820361035557828792838b950152611b0f868801611939565b86820152815201930192611ab6565b91908110156116f65760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee181360301821215610355570190565b3564ffffffffff811681036103555790565b91908110156116f657610140020190565b90604080516020907f23b872dd000000000000000000000000000000000000000000000000000000008282015233602482015260449030828201528660648201526064815260a081019080821067ffffffffffffffff83111761175f57611bea91855285611d86565b6001600160a01b0394858516958451917fdd62ed3e0000000000000000000000000000000000000000000000000000000083523060048401521690816024820152838184818a5afa908115611d6357908891600091611d32575b5010611c54575b50505050505050565b8351956000808589017f095ea7b3000000000000000000000000000000000000000000000000000000009a8b82528560248c0152868b0152858a52611c988a6117e3565b89519082855af190611ca8611e12565b82611cff575b5081611cf4575b50611c4b57611ce896611ce394519384015260248301526000818301528152611cdd816117e3565b82611d86565b611d86565b38808080808080611c4b565b90503b151538611cb5565b809192505190858215928315611d1a575b5050509038611cae565b611d2a9350820181019101611d6e565b388581611d10565b809250858092503d8311611d5c575b611d4b81836117ff565b810103126103555787905138611c44565b503d611d41565b85513d6000823e3d90fd5b90816020910312610355575180151581036103555790565b6000806001600160a01b03611db093169360208151910182865af1611da9611e12565b9083611e70565b8051908115159182611df7575b5050611dc65750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b611e0a9250602080918301019101611d6e565b153880611dbd565b3d15611e6b573d9067ffffffffffffffff821161175f5760405191611e5f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846117ff565b82523d6000602084013e565b606090565b90611eaf5750805115611e8557805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580611efa575b611ec0575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15611eb856fea164736f6c6343000817000a"; bytes public constant BYTECODE_MERKLE_LOCKUP_FACTORY = hex"6080806040523461001657614200908161001c8239f35b600080fdfe608060405260043610156200001357600080fd5b60003560e01c806389a40c0814620005245763a729a319146200003557600080fd5b346200051f5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126200051f5760043567ffffffffffffffff81116200051f576200008990369060040162000930565b73ffffffffffffffffffffffffffffffffffffffff6024351680602435036200051f576044359167ffffffffffffffff83116200051f57366023840112156200051f57826004013567ffffffffffffffff81116200046b5760405193620000f760208360051b01866200088f565b8185526024602086019260061b820101903682116200051f57602401915b818310620004cb575050506000906000906000925b8551841015620001b65767ffffffffffffffff806200014a868962000b3c565b51511691160167ffffffffffffffff811162000187576001909264ffffffffff602062000178878a62000b3c565b5101511601930192916200012a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b67ffffffffffffffff9195935016670de0b6b3a764000081036200049a57506200020a6200036860a38651620002ae60208901516200021960408b0151604051978891602080840152604083019062000a44565b03601f1981018852876200088f565b6200025560608b01516200024f60206040518362000241829551809285808601910162000a1f565b81010380845201826200088f565b62000a6b565b90888b7fffffffffff000000000000000000000000000000000000000000000000000000608082015160a083015190620002bd60e060c08601511515950151151595604051998a91602080840152604083019062000b80565b03601f1981018a52896200088f565b604862000311604051809e819c7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009a8b809260601b16602085015260601b1660348301526020815194859301910162000a1f565b8901966048880152606887015260d81b16608885015260f81b608d84015260f81b608e83015260243560601b16608f82015262000358825180936020878501910162000a1f565b010360838101845201826200088f565b60208151910120604051611e158082019082821067ffffffffffffffff8311176200046b578291620003c391620023df843960608152620003ad606082018a62000aad565b9088602082015260408183039101528662000b80565b03906000f580156200045f576020947fee52c9be756b04ff87545717a099143dd3f3a3df3d30ed320884849c5769c22e926200043e73ffffffffffffffffffffffffffffffffffffffff6200042b941696879660405195869560c0875260c087019062000aad565b918a860152848203604086015262000b80565b906060830152606435608083015260843560a08301520390a2604051908152f35b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b602490604051907f1b05170a0000000000000000000000000000000000000000000000000000000082526004820152fd5b6040833603126200051f5760405190620004e58262000872565b83359067ffffffffffffffff821682036200051f5782602092604094526200050f8387016200090f565b8382015281520192019162000115565b600080fd5b346200051f5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126200051f5767ffffffffffffffff6004358181116200051f576200057990369060040162000930565b73ffffffffffffffffffffffffffffffffffffffff8060243516602435036200051f5760407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126200051f5760405190620005d68262000872565b64ffffffffff9360443585811681036200051f57835260643594851685036200051f5760209485840152835162000629868601516200063860408801516040519384918b80840152604083019062000a44565b03601f1981018452836200088f565b6200065f60608801516200024f8a6040518362000241829551809285808601910162000a1f565b8860808901519160a08a01519360c08b0151151560e08c01511515906200069f6040519586018d6020908164ffffffffff91828151168552015116910152565b6040855260608501978589108b8a11176200046b576200077d978f977fffffffffff000000000000000000000000000000000000000000000000000000926200073460a39a8d60405260808b019e8f997fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009a8b809360601b16905260601b1660948c0152825192839160a88d01910162000a1f565b88019660a888015260c887015260d81b1660e885015260f81b60ed84015260f81b60ee83015260243560601b1660ef82015281519062000358828d610103840190860162000a1f565b519020604051909161180c90818301908111838210176200046b57829162000bd3833960808152620007de6040620007b9608084018a62000aad565b9287602435168b82015201876020908164ffffffffff91828151168552015116910152565b03906000f580156200045f576200085793827fb905fb2b79e37b53ad42b4bbb04afcfc7c13d2771efe396ad8c687a844662e959362000830931695869560405194859460c0865260c086019062000aad565b92602435168985015260408401906020908164ffffffffff91828151168552015116910152565b608435608083015260a43560a08301520390a2604051908152f35b6040810190811067ffffffffffffffff8211176200046b57604052565b90601f601f19910116810190811067ffffffffffffffff8211176200046b57604052565b81601f820112156200051f5780359067ffffffffffffffff82116200046b5760405192620008ec6020601f19601f86011601856200088f565b828452602083830101116200051f57816000926020809301838601378301015290565b359064ffffffffff821682036200051f57565b359081151582036200051f57565b9190916101009081818503126200051f5760405191820167ffffffffffffffff90838110828211176200046b576040528294823573ffffffffffffffffffffffffffffffffffffffff9081811681036200051f578552602084013590811681036200051f57602085015260408301358281116200051f5781620009b5918501620008b3565b604085015260608301359182116200051f5782620009de60e0949262000a1a94869401620008b3565b606086015260808101356080860152620009fb60a082016200090f565b60a086015262000a0e60c0820162000922565b60c08601520162000922565b910152565b60005b83811062000a335750506000910152565b818101518382015260200162000a22565b90601f19601f60209362000a648151809281875287808801910162000a1f565b0116010190565b60208151910151906020811062000a80575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b9060e08062000b0a62000af761010073ffffffffffffffffffffffffffffffffffffffff808851168752602088015116602087015260408701519080604088015286019062000a44565b6060860151858203606087015262000a44565b936080810151608085015264ffffffffff60a08201511660a085015260c0810151151560c08501520151151591015290565b805182101562000b515760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90815180825260208080930193019160005b82811062000ba1575050505090565b8351805167ffffffffffffffff16865282015164ffffffffff16858301526040909401939281019260010162000b9256fe6101608060405234620005b4576200180c8038038091620000218285620005d5565b83398101908082039160808312620005b45781516001600160401b038111620005b45782019261010084830312620005b4576040519361010085016001600160401b038111868210176200059e5760405280516001600160a01b0381168103620005b45785526020810151926001600160a01b0384168403620005b4576020860193845260408201516001600160401b038111620005b45781620000c79184016200063a565b60408701908152606083015190916001600160401b038211620005b457620000f19184016200063a565b6060870152608082015195608081019687526200011160a0840162000687565b9660a082019788526200013c60e06200012d60c087016200069a565b9560c08501968752016200069a565b60e083019081526020880151959093906001600160a01b0387168703620005b457604090603f190112620005b45760408051989089016001600160401b0381118a8210176200059e57620001a7916060916040526200019e6040820162000687565b8b520162000687565b9460208901958652606084015151602081116200057f57508351600080546001600160a01b0319166001600160a01b0392831617815598511660805251151560a052975164ffffffffff90811660c052975180516001600160401b0381116200056b5760019182548381811c9116801562000560575b60208210146200054c57601f8111620004ff575b50602090601f83116001146200049357606095949392918a918362000487575b5050600019600383901b1c191690821b1790555b5160e052015160405162000299602082816200028b818301968781519384920162000615565b8101038084520182620005d5565b519051906020811062000474575b50610100525115159461012095865261014094838652511669ffffffffff0000000000600354925160281b169160018060501b031916171760035560018060a01b03608051166040519160208301848063095ea7b360e01b9283815260018060a01b03851660248801526000196044880152604487526200032887620005b9565b86519082875af162000339620006a8565b8162000433575b508062000428575b15620003dc575b60405161104290878983620007ca8439608051838181610470015281816107440152610c36015260a05183818161076b0152610b5c015260c05183818161015d01528181610aa801528181610d8f0152610f60015260e0518381816102e2015261066201526101005183610e250152518281816107930152610b1f0152518181816101ae01526109060152f35b6200041d94620004179260405192602084015260018060a01b031660248301526044820152604481526200041081620005b9565b82620006dd565b620006dd565b38808080806200034f565b50823b151562000348565b80518015925082156200044a575b50503862000340565b8192509060209181010312620004705760206200046891016200069a565b388062000441565b8580fd5b6000199060200360031b1b1638620002a7565b01519050388062000251565b838a5260208a209190601f1984168b5b818110620004e857509185949291836060999897959310620004ce575b505050811b01905562000265565b015160001960f88460031b161c19169055388080620004c0565b8284015185559386019360209384019301620004a3565b838a5260208a20601f840160051c8101916020851062000541575b601f0160051c019084905b8281106200053557505062000231565b8b815501849062000525565b90915081906200051a565b634e487b7160e01b8a52602260045260248afd5b90607f16906200021d565b634e487b7160e01b88526041600452602488fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b634e487b7160e01b600052604160045260246000fd5b600080fd5b608081019081106001600160401b038211176200059e57604052565b601f909101601f19168101906001600160401b038211908210176200059e57604052565b6001600160401b0381116200059e57601f01601f191660200190565b60005b838110620006295750506000910152565b818101518382015260200162000618565b81601f82011215620005b45780516200065381620005f9565b92620006636040519485620005d5565b81845260208284010111620005b45762000684916020808501910162000615565b90565b519064ffffffffff82168203620005b457565b51908115158203620005b457565b3d15620006d8573d90620006bc82620005f9565b91620006cc6040519384620005d5565b82523d6000602084013e565b606090565b6000806200070a9260018060a01b03169360208151910182865af162000702620006a8565b908362000761565b80519081151591826200073a575b5050620007225750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620005b45760206200075891016200069a565b15388062000718565b906200078a57508051156200077857805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580620007bf575b6200079d575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156200079456fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde0314610e0e575080631686c90914610b8157806316c3549d14610b445780631bfd681414610b075780633bfe03a814610ad85780633f31ae3f146104945780634800d97f1461044357806349fc73dd1461030557806351e75e8b146102ca57806375829def146101ed57806390e64d13146101d25780639e93e57714610181578063bb4b57341461013f578063ce516507146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760206003193601126100f857602061013560043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f8576020610135610f58565b346100f85760206003193601126100f857610206610ebc565b60005473ffffffffffffffffffffffffffffffffffffffff8082169233840361027d577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c9060018316928315610439575b602093848410811461040a578386529081156103cc5750600114610371575b61036d8461036181880382610f17565b60405191829182610e56565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8284106103b9575050508161036d936103619282010193610351565b805485850187015292850192810161039d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103618161036d610351565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691610332565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f857604435906fffffffffffffffffffffffffffffffff821682036100f85767ffffffffffffffff606435116100f8573660236064350112156100f85767ffffffffffffffff60643560040135116100f8573660246064356004013560051b6064350101116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff831660408201526fffffffffffffffffffffffffffffffff841660608201526060815261057a81610efb565b519020604051602081019182526020815261059481610edf565b519020916105a0610f58565b610a7a576105c960043560ff6001918060081c6000526002602052161b60406000205416151590565b610a4857604051926105e760206064356004013560051b0185610f17565b60643560048101358552602401602085015b60246064356004013560051b60643501018210610a38575050906000915b845183101561065e5760208360051b860101519081811060001461064b57600052602052600160406000205b920191610617565b9060005260205260016040600020610643565b83907f000000000000000000000000000000000000000000000000000000000000000003610a0e5760043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff60005416906040516106cd81610edf565b600081526000602082015260405192610100840184811067ffffffffffffffff8211176109df57604052835273ffffffffffffffffffffffffffffffffffffffff821660208401526fffffffffffffffffffffffffffffffff8416604084015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608401527f0000000000000000000000000000000000000000000000000000000000000000151560808401527f0000000000000000000000000000000000000000000000000000000000000000151560a08401526040516107c581610edf565b64ffffffffff600354818116835260281c16602082015260c084015260e0830152602060e0604051937fab167ccc00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff815116600486015273ffffffffffffffffffffffffffffffffffffffff838201511660248601526fffffffffffffffffffffffffffffffff604082015116604486015273ffffffffffffffffffffffffffffffffffffffff606082015116606486015260808101511515608486015260a0810151151560a486015264ffffffffff8360c08301518281511660c489015201511660e4860152015173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015260208261014481600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af19182156109d35760009261099e575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d6020116109cb575b816109b960209383610f17565b810103126100f8579051906020610937565b3d91506109ac565b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b81358152602091820191016105f9565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f857604060035464ffffffffff825191818116835260281c166020820152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857610b9a610ebc565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff8060005416338103610dbf5750610be1610f58565b15610d61576040519060209160008083858401977fa9059cbb000000000000000000000000000000000000000000000000000000008952169687602485015286604485015260448452610c3384610efb565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610d55573d67ffffffffffffffff81116109df57610cbc9160405191610cac877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610f17565b82523d60008784013e5b83610f95565b8051848115159182610d34575b50509050610d035750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488610cc9565b610cbc90606090610cb6565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f85761036d907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261036181610edf565b60208082528251818301819052939260005b858110610ea8575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201610e68565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff8211176109df57604052565b6080810190811067ffffffffffffffff8211176109df57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176109df57604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081610f8d575090565b905042101590565b90610fd45750805115610faa57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b8151158061102c575b610fe5575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15610fdd56fea164736f6c6343000817000a6101608060405234620006e95762001e1590813803809262000022828462000735565b82396060818381010312620006e95780516001600160401b038111620006e9578101916101009283818385010312620006e957604051908482016001600160401b03811183821017620007035760405280516001600160a01b0381168103620006e957825260208101516001600160a01b0381168103620006e957602083015260408101516001600160401b038111620006e957620000c7908486019083016200079a565b604083015260608101516001600160401b038111620006e9576200013491620000f860e0928688019083016200079a565b6060850152608081015160808501526200011560a08201620007e7565b60a08501526200012860c08201620007fa565b60c085015201620007fa565b60e08201526020830151916001600160a01b0383168303620006e9576040840151906001600160401b038211620006e957808501601f838701011215620006e95784820151906001600160401b0382116200070357604051956200019f60208460051b018862000735565b828752602087019382820160208560061b838501010111620006e95793602085830101945b60208560061b828501010186106200068357505050505050606081015151602081116200066457508051600080546001600160a01b0319166001600160a01b03928316178155602083015190911660805260c080830151151560a090815283015164ffffffffff16905260408201518051919290916001600160401b038111620006505760019283548481811c9116801562000645575b60208210146200063157601f8111620005e4575b50602090601f83116001146200057a5760e09392918691836200056e575b5050600019600383901b1c191690841b1783555b608081015182526060810151604051620002db60208281620002cd818301968781519384920162000775565b810103808452018262000735565b51905190602081106200055b575b5087520151151593610120948552610140938452805190835b82811062000499575050505060018060a01b036080511660018060a01b03835116906040519160208301848063095ea7b360e01b92838152846024880152600019604488015260448752620003578762000719565b86519082875af16200036862000808565b8162000458575b50806200044d575b1562000409575b6040516114eb9087898b846200092a85396080518481816104e2015281816108550152610f1c015260a05184818161087c0152610e42015260c05184818161022001528181610dbd015281816110750152611246015260e05184818161035401526106ba0152518361110b0152518281816108a40152610e0501525181818161012a0152610a0e0152f35b62000442946200043c926040519260208401526024830152604482015260448152620004358162000719565b826200083d565b6200083d565b38808080806200037e565b50823b151562000377565b80518015925082156200046f575b5050386200036f565b8192509060209181010312620004955760206200048d9101620007fa565b388062000466565b8580fd5b8151811015620005475760208160051b8301015160038054906801000000000000000082101562000533578682018082558210156200051f578752602080882083519201805493909101516cffffffffff000000000000000060409190911b166001600160401b039092166001600160681b031990931692909217179055830162000302565b634e487b7160e01b88526032600452602488fd5b634e487b7160e01b88526041600452602488fd5b634e487b7160e01b85526032600452602485fd5b6000199060200360031b1b1638620002e9565b0151905038806200028d565b92918491601f198216908388526020882091885b818110620005cb5750958360e09710620005b1575b505050811b018355620002a1565b015160001960f88460031b161c19169055388080620005a3565b828801518455889590930192602092830192016200058e565b84865260208620601f840160051c8101916020851062000626575b601f0160051c019085905b8281106200061a5750506200026f565b8781550185906200060a565b9091508190620005ff565b634e487b7160e01b86526022600452602486fd5b90607f16906200025b565b634e487b7160e01b84526041600452602484fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b6040868585010312620006e957604080519081016001600160401b03811182821017620006ee5760405286516001600160401b0381168103620006e95760209382859260409452620006d7838b01620007e7565b838201528152019601959150620001c4565b600080fd5b60246000634e487b7160e01b81526041600452fd5b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b038211176200070357604052565b601f909101601f19168101906001600160401b038211908210176200070357604052565b6001600160401b0381116200070357601f01601f191660200190565b60005b838110620007895750506000910152565b818101518382015260200162000778565b81601f82011215620006e9578051620007b38162000759565b92620007c3604051948562000735565b81845260208284010111620006e957620007e4916020808501910162000775565b90565b519064ffffffffff82168203620006e957565b51908115158203620006e957565b3d1562000838573d906200081c8262000759565b916200082c604051938462000735565b82523d6000602084013e565b606090565b6000806200086a9260018060a01b03169360208151910182865af16200086262000808565b9083620008c1565b80519081151591826200089a575b5050620008825750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620006e9576020620008b89101620007fa565b15388062000878565b90620008ea5750805115620008d857805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806200091f575b620008fd575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15620008f456fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde03146110f4575080631686c90914610e6757806316c3549d14610e2a5780631bfd681414610ded5780633f31ae3f146105065780634800d97f146104b557806349fc73dd1461037757806351e75e8b1461033c57806375829def1461025f57806390e64d1314610244578063bb4b573414610202578063bf4ed03f14610190578063ce5165071461014e578063da792468146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760206003193601126100f857602061018660043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f8576101a9611293565b6040516020918282018383528151809152836040840192019360005b8281106101d25784840385f35b8551805167ffffffffffffffff16855282015164ffffffffff1684830152948101946040909301926001016101c5565b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f857602061018661123e565b346100f85760206003193601126100f8576102786111a2565b60005473ffffffffffffffffffffffffffffffffffffffff808216923384036102ef577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c90600183169283156104ab575b602093848410811461047c5783865290811561043e57506001146103e3575b6103df846103d3818803826111fd565b6040519182918261113c565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b82841061042b57505050816103df936103d392820101936103c3565b805485850187015292850192810161040f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103d3816103df6103c3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f16916103a4565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f8576fffffffffffffffffffffffffffffffff60443516604435036100f85767ffffffffffffffff606435116100f8573660236064350112156100f857606435600401359067ffffffffffffffff82116100f85760248260051b60643501013681116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff841660408201526fffffffffffffffffffffffffffffffff604435166060820152606081526105eb816111e1565b5190206040516020810191825260208152610605816111c5565b5190209061061161123e565b610d8f5761063a60043560ff6001918060081c6000526002602052161b60406000205416151590565b610d5d576106478461127b565b9361065560405195866111fd565b8452606435602401602085015b828210610d4d575050506000905b83518210156106b657610683828561132e565b5190818110156106a357600052602052600160406000205b910190610670565b906000526020526001604060002061069b565b90507f000000000000000000000000000000000000000000000000000000000000000003610d23576106e6611293565b90600082516106f48161127b565b9361070260405195866111fd565b8185527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061072f8361127b565b0160005b818110610cfe5750506000905b828210610bd95750506fffffffffffffffffffffffffffffffff82166fffffffffffffffffffffffffffffffff604435168111610baa576fffffffffffffffffffffffffffffffff6044351611610b56575b505060043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff6000541691604051906107dc826111c5565b600082526000602083015260405193610100850185811067ffffffffffffffff821117610b2757604052845273ffffffffffffffffffffffffffffffffffffffff831660208501526fffffffffffffffffffffffffffffffff60443516604085015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608501527f0000000000000000000000000000000000000000000000000000000000000000151560808501527f0000000000000000000000000000000000000000000000000000000000000000151560a085015260c084015260e0830152604051917f897f362b0000000000000000000000000000000000000000000000000000000083526020600484015282610144810173ffffffffffffffffffffffffffffffffffffffff835116602483015273ffffffffffffffffffffffffffffffffffffffff60208401511660448301526fffffffffffffffffffffffffffffffff604084015116606483015273ffffffffffffffffffffffffffffffffffffffff60608401511660848301526080830151151560a483015260a0830151151560c483015260c08301519061012060e484015281518091526020610164840192019060005b818110610ae9575050508190602060e08195015173ffffffffffffffffffffffffffffffffffffffff81511661010485015201516101248301520381600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1918215610add57600092610aa8575b60208380847f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff81519360043585526fffffffffffffffffffffffffffffffff60443516888601521692a3604051908152f35b91506020823d602011610ad5575b81610ac3602093836111fd565b810103126100f8576020915191610a3f565b3d9150610ab6565b6040513d6000823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff1681860152889550604090940193909201916001016109b9565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6fffffffffffffffffffffffffffffffff610b947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8293018661132e565b5192604435031681835116011690528280610792565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b9092610c0e67ffffffffffffffff610bf1868561132e565b5151166fffffffffffffffffffffffffffffffff60443516611371565b6fffffffffffffffffffffffffffffffff8111610ccd576fffffffffffffffffffffffffffffffff8091169164ffffffffff6020610c4c888761132e565b5101511660405190610c5d826111c5565b8482526020820152610c6f878a61132e565b52610c7a868961132e565b5016016fffffffffffffffffffffffffffffffff8111610c9e579260010190610740565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b602490604051907f4916adce0000000000000000000000000000000000000000000000000000000082526004820152fd5b602090604051610d0d816111c5565b6000815260008382015282828a01015201610733565b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b8135815260209182019101610662565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857610e806111a2565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff80600054163381036110a55750610ec761123e565b15611047576040519060209160008083858401977fa9059cbb000000000000000000000000000000000000000000000000000000008952169687602485015286604485015260448452610f19846111e1565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d1561103b573d67ffffffffffffffff8111610b2757610fa29160405191610f92877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846111fd565b82523d60008784013e5b8361143e565b805184811515918261101a575b50509050610fe95750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488610faf565b610fa290606090610f9c565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f8576103df907f00000000000000000000000000000000000000000000000000000000000000006020820152602081526103d3816111c5565b60208082528251818301819052939260005b85811061118e575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b81810183015184820160400152820161114e565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff821117610b2757604052565b6080810190811067ffffffffffffffff821117610b2757604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610b2757604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081611273575090565b905042101590565b67ffffffffffffffff8111610b275760051b60200190565b600354906112a08261127b565b9160406112b060405194856111fd565b8184528360208091019160036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b906000935b8585106112f457505050505050565b60018481928451611304816111c5565b64ffffffffff875467ffffffffffffffff81168352871c16838201528152019301940193916112e5565b80518210156113425760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8382098382029182808310920391808303921461142d57670de0b6b3a764000090818310156113f657947faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac1066994950990828211900360ee1b910360121c170290565b60449086604051917f5173648d00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5050670de0b6b3a764000090049150565b9061147d575080511561145357805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b815115806114d5575b61148e575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b1561148656fea164736f6c6343000817000aa164736f6c6343000817000a"; @@ -25,13 +25,15 @@ contract Precompiles { DEPLOYERS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Deploys {SablierV2Batch} from precompiled bytecode. - function deployBatch() public returns (ISablierV2Batch batch) { - bytes memory creationBytecode = BYTECODE_BATCH; + /// @notice Deploys {SablierV2BatchLockup} from precompiled bytecode. + function deployBatchLockup() public returns (ISablierV2BatchLockup batchLockup) { + bytes memory creationBytecode = BYTECODE_BATCH_LOCKUP; assembly { - batch := create(0, add(creationBytecode, 0x20), mload(creationBytecode)) + batchLockup := create(0, add(creationBytecode, 0x20), mload(creationBytecode)) } - require(address(batch) != address(0), "Sablier V2 Precompiles: deployment failed for Batch contract"); + require( + address(batchLockup) != address(0), "Sablier V2 Precompiles: deployment failed for BatchLockup contract" + ); } /// @notice Deploys {SablierV2MerkleLockupFactory} from precompiled bytecode. @@ -47,13 +49,13 @@ contract Precompiles { /// @notice Deploys all V2 Periphery contracts in the following order: /// - /// 1. {SablierV2Batch} + /// 1. {SablierV2BatchLockup} /// 2. {SablierV2MerkleLockupFactory} function deployPeriphery() public - returns (ISablierV2Batch batch, ISablierV2MerkleLockupFactory merkleLockupFactory) + returns (ISablierV2BatchLockup batchLockup, ISablierV2MerkleLockupFactory merkleLockupFactory) { - batch = deployBatch(); + batchLockup = deployBatchLockup(); merkleLockupFactory = deployMerkleLockupFactory(); } @@ -63,7 +65,7 @@ contract Precompiles { /// 2. {SablierV2LockupDynamic} /// 3. {SablierV2LockupLinear} /// 4. {SablierV2LockupTranched} - /// 5. {SablierV2Batch} + /// 5. {SablierV2BatchLockup} /// 6. {SablierV2MerkleLockupFactory} function deployProtocol(address initialAdmin) public @@ -72,7 +74,7 @@ contract Precompiles { ISablierV2LockupLinear lockupLinear, ISablierV2LockupTranched lockupTranched, ISablierV2NFTDescriptor nftDescriptor, - ISablierV2Batch batch, + ISablierV2BatchLockup batchLockup, ISablierV2MerkleLockupFactory merkleLockupFactory ) { @@ -80,6 +82,6 @@ contract Precompiles { (lockupDynamic, lockupLinear, lockupTranched, nftDescriptor) = new V2CorePrecompiles().deployCore(initialAdmin); // Deploy V2 Periphery. - (batch, merkleLockupFactory) = deployPeriphery(); + (batchLockup, merkleLockupFactory) = deployPeriphery(); } } diff --git a/script/Base.s.sol b/script/Base.s.sol index dda858b5..786566d2 100644 --- a/script/Base.s.sol +++ b/script/Base.s.sol @@ -58,7 +58,7 @@ contract BaseScript is Script, Sphinx { /// Refer to https://github.com/sphinx-labs/sphinx/tree/main/docs. /// /// CLI example: - /// bun sphinx propose script/DeployBatch.s.sol --networks testnets --sig "runSphinx()" + /// bun sphinx propose script/DeploybatchLockup.s.sol --networks testnets --sig "runSphinx()" function configureSphinx() public override { sphinxConfig.mainnets = ["arbitrum", "avalanche", "base", "bnb", "gnosis", "ethereum", "optimism", "polygon"]; sphinxConfig.orgId = vm.envOr({ name: "SPHINX_ORG_ID", defaultValue: TEST_MNEMONIC }); diff --git a/script/DeployBatch.t.sol b/script/DeployBatch.t.sol deleted file mode 100644 index 80e6e56d..00000000 --- a/script/DeployBatch.t.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.22 <0.9.0; - -import { BaseScript } from "./Base.s.sol"; - -import { SablierV2Batch } from "../src/SablierV2Batch.sol"; - -contract DeployBatch is BaseScript { - /// @dev Deploy via Forge. - function runBroadcast() public virtual broadcast returns (SablierV2Batch batch) { - batch = _run(); - } - - /// @dev Deploy via Sphinx. - function runSphinx() public virtual sphinx returns (SablierV2Batch batch) { - batch = _run(); - } - - function _run() internal returns (SablierV2Batch batch) { - batch = new SablierV2Batch(); - } -} diff --git a/script/DeployBatchLockup.t.sol b/script/DeployBatchLockup.t.sol new file mode 100644 index 00000000..2047bbc2 --- /dev/null +++ b/script/DeployBatchLockup.t.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity >=0.8.22 <0.9.0; + +import { BaseScript } from "./Base.s.sol"; + +import { SablierV2BatchLockup } from "../src/SablierV2BatchLockup.sol"; + +contract DeployBatchLockup is BaseScript { + /// @dev Deploy via Forge. + function runBroadcast() public virtual broadcast returns (SablierV2BatchLockup batchLockup) { + batchLockup = _run(); + } + + /// @dev Deploy via Sphinx. + function runSphinx() public virtual sphinx returns (SablierV2BatchLockup batchLockup) { + batchLockup = _run(); + } + + function _run() internal returns (SablierV2BatchLockup batchLockup) { + batchLockup = new SablierV2BatchLockup(); + } +} diff --git a/script/DeployDeterministicBatch.s.sol b/script/DeployDeterministicBatch.s.sol deleted file mode 100644 index d1728f53..00000000 --- a/script/DeployDeterministicBatch.s.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity >=0.8.22 <0.9.0; - -import { BaseScript } from "./Base.s.sol"; - -import { SablierV2Batch } from "../src/SablierV2Batch.sol"; - -/// @notice Deploys {SablierV2Batch} at a deterministic address across chains. -/// @dev Reverts if the contract has already been deployed. -contract DeployDeterministicBatch is BaseScript { - /// @dev Deploy via Forge. - function runBroadcast() public virtual broadcast returns (SablierV2Batch batch) { - batch = _run(); - } - - /// @dev Deploy via Sphinx. - function runSphinx() public virtual sphinx returns (SablierV2Batch batch) { - batch = _run(); - } - - function _run() internal returns (SablierV2Batch batch) { - bytes32 salt = constructCreate2Salt(); - batch = new SablierV2Batch{ salt: salt }(); - } -} diff --git a/script/DeployDeterministicBatchLockup.s.sol b/script/DeployDeterministicBatchLockup.s.sol new file mode 100644 index 00000000..0b4fd633 --- /dev/null +++ b/script/DeployDeterministicBatchLockup.s.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity >=0.8.22 <0.9.0; + +import { BaseScript } from "./Base.s.sol"; + +import { SablierV2BatchLockup } from "../src/SablierV2BatchLockup.sol"; + +/// @notice Deploys {SablierV2BatchLockup} at a deterministic address across chains. +/// @dev Reverts if the contract has already been deployed. +contract DeployDeterministicBatchLockup is BaseScript { + /// @dev Deploy via Forge. + function runBroadcast() public virtual broadcast returns (SablierV2BatchLockup batchLockup) { + batchLockup = _run(); + } + + /// @dev Deploy via Sphinx. + function runSphinx() public virtual sphinx returns (SablierV2BatchLockup batchLockup) { + batchLockup = _run(); + } + + function _run() internal returns (SablierV2BatchLockup batchLockup) { + bytes32 salt = constructCreate2Salt(); + batchLockup = new SablierV2BatchLockup{ salt: salt }(); + } +} diff --git a/script/DeployDeterministicPeriphery.s.sol b/script/DeployDeterministicPeriphery.s.sol index 77968c78..452509d7 100644 --- a/script/DeployDeterministicPeriphery.s.sol +++ b/script/DeployDeterministicPeriphery.s.sol @@ -3,12 +3,12 @@ pragma solidity >=0.8.22 <0.9.0; import { BaseScript } from "./Base.s.sol"; -import { SablierV2Batch } from "../src/SablierV2Batch.sol"; +import { SablierV2BatchLockup } from "../src/SablierV2BatchLockup.sol"; import { SablierV2MerkleLockupFactory } from "../src/SablierV2MerkleLockupFactory.sol"; /// @notice Deploys all V2 Periphery contracts at deterministic addresses across chains, in the following order: /// -/// 1. {SablierV2Batch} +/// 1. {SablierV2BatchLockup} /// 2. {SablierV2MerkleLockupFactory} /// /// @dev Reverts if any contract has already been deployed. @@ -18,9 +18,9 @@ contract DeployDeterministicPeriphery is BaseScript { public virtual broadcast - returns (SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory) + returns (SablierV2BatchLockup batchLockup, SablierV2MerkleLockupFactory merkleLockupFactory) { - (batch, merkleLockupFactory) = _run(); + (batchLockup, merkleLockupFactory) = _run(); } /// @dev Deploy via Sphinx. @@ -28,14 +28,17 @@ contract DeployDeterministicPeriphery is BaseScript { public virtual sphinx - returns (SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory) + returns (SablierV2BatchLockup batchLockup, SablierV2MerkleLockupFactory merkleLockupFactory) { - (batch, merkleLockupFactory) = _run(); + (batchLockup, merkleLockupFactory) = _run(); } - function _run() internal returns (SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory) { + function _run() + internal + returns (SablierV2BatchLockup batchLockup, SablierV2MerkleLockupFactory merkleLockupFactory) + { bytes32 salt = constructCreate2Salt(); - batch = new SablierV2Batch{ salt: salt }(); + batchLockup = new SablierV2BatchLockup{ salt: salt }(); merkleLockupFactory = new SablierV2MerkleLockupFactory{ salt: salt }(); } } diff --git a/script/DeployPeriphery.s.sol b/script/DeployPeriphery.s.sol index e8f0fee3..151fd81f 100644 --- a/script/DeployPeriphery.s.sol +++ b/script/DeployPeriphery.s.sol @@ -4,11 +4,11 @@ pragma solidity >=0.8.22 <0.9.0; import { BaseScript } from "./Base.s.sol"; import { SablierV2MerkleLockupFactory } from "../src/SablierV2MerkleLockupFactory.sol"; -import { SablierV2Batch } from "../src/SablierV2Batch.sol"; +import { SablierV2BatchLockup } from "../src/SablierV2BatchLockup.sol"; /// @notice Deploys all V2 Periphery contract in the following order: /// -/// 1. {SablierV2Batch} +/// 1. {SablierV2BatchLockup} /// 2. {SablierV2MerkleLockupFactory} contract DeployPeriphery is BaseScript { /// @dev Deploy via Forge. @@ -16,9 +16,9 @@ contract DeployPeriphery is BaseScript { public virtual broadcast - returns (SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory) + returns (SablierV2BatchLockup batchLockup, SablierV2MerkleLockupFactory merkleLockupFactory) { - (batch, merkleLockupFactory) = _run(); + (batchLockup, merkleLockupFactory) = _run(); } /// @dev Deploy via Sphinx. @@ -26,13 +26,16 @@ contract DeployPeriphery is BaseScript { public virtual sphinx - returns (SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory) + returns (SablierV2BatchLockup batchLockup, SablierV2MerkleLockupFactory merkleLockupFactory) { - (batch, merkleLockupFactory) = _run(); + (batchLockup, merkleLockupFactory) = _run(); } - function _run() internal returns (SablierV2Batch batch, SablierV2MerkleLockupFactory merkleLockupFactory) { - batch = new SablierV2Batch(); + function _run() + internal + returns (SablierV2BatchLockup batchLockup, SablierV2MerkleLockupFactory merkleLockupFactory) + { + batchLockup = new SablierV2BatchLockup(); merkleLockupFactory = new SablierV2MerkleLockupFactory(); } } diff --git a/script/DeployProtocol.s.sol b/script/DeployProtocol.s.sol index 3750c4be..c27c7f6a 100644 --- a/script/DeployProtocol.s.sol +++ b/script/DeployProtocol.s.sol @@ -8,7 +8,7 @@ import { SablierV2NFTDescriptor } from "@sablier/v2-core/src/SablierV2NFTDescrip import { BaseScript } from "./Base.s.sol"; import { SablierV2MerkleLockupFactory } from "../src/SablierV2MerkleLockupFactory.sol"; -import { SablierV2Batch } from "../src/SablierV2Batch.sol"; +import { SablierV2BatchLockup } from "../src/SablierV2BatchLockup.sol"; /// @notice Deploys the Sablier V2 Protocol. contract DeployProtocol is BaseScript { @@ -26,11 +26,11 @@ contract DeployProtocol is BaseScript { SablierV2LockupLinear lockupLinear, SablierV2LockupTranched lockupTranched, SablierV2NFTDescriptor nftDescriptor, - SablierV2Batch batch, + SablierV2BatchLockup batchLockup, SablierV2MerkleLockupFactory merkleLockupFactory ) { - (lockupDynamic, lockupLinear, lockupTranched, nftDescriptor, batch, merkleLockupFactory) = + (lockupDynamic, lockupLinear, lockupTranched, nftDescriptor, batchLockup, merkleLockupFactory) = _run(initialAdmin, maxSegmentCount, maxTrancheCount); } @@ -48,11 +48,11 @@ contract DeployProtocol is BaseScript { SablierV2LockupLinear lockupLinear, SablierV2LockupTranched lockupTranched, SablierV2NFTDescriptor nftDescriptor, - SablierV2Batch batch, + SablierV2BatchLockup batchLockup, SablierV2MerkleLockupFactory merkleLockupFactory ) { - (lockupDynamic, lockupLinear, lockupTranched, nftDescriptor, batch, merkleLockupFactory) = + (lockupDynamic, lockupLinear, lockupTranched, nftDescriptor, batchLockup, merkleLockupFactory) = _run(initialAdmin, maxSegmentCount, maxTrancheCount); } @@ -67,7 +67,7 @@ contract DeployProtocol is BaseScript { SablierV2LockupLinear lockupLinear, SablierV2LockupTranched lockupTranched, SablierV2NFTDescriptor nftDescriptor, - SablierV2Batch batch, + SablierV2BatchLockup batchLockup, SablierV2MerkleLockupFactory merkleLockupFactory ) { @@ -78,7 +78,7 @@ contract DeployProtocol is BaseScript { lockupTranched = new SablierV2LockupTranched(initialAdmin, nftDescriptor, maxTrancheCount); // Deploy V2 Periphery. - batch = new SablierV2Batch(); + batchLockup = new SablierV2BatchLockup(); merkleLockupFactory = new SablierV2MerkleLockupFactory(); } } diff --git a/shell/prepare-artifacts.sh b/shell/prepare-artifacts.sh index 93046687..74affdbb 100755 --- a/shell/prepare-artifacts.sh +++ b/shell/prepare-artifacts.sh @@ -24,13 +24,13 @@ mkdir $artifacts \ FOUNDRY_PROFILE=optimized forge build # Copy the production artifacts -cp out-optimized/SablierV2Batch.sol/SablierV2Batch.json $artifacts +cp out-optimized/SablierV2BatchLockup.sol/SablierV2BatchLockup.json $artifacts cp out-optimized/SablierV2MerkleLL.sol/SablierV2MerkleLL.json $artifacts cp out-optimized/SablierV2MerkleLockupFactory.sol/SablierV2MerkleLockupFactory.json $artifacts cp out-optimized/SablierV2MerkleLT.sol/SablierV2MerkleLT.json $artifacts interfaces=./artifacts/interfaces -cp out-optimized/ISablierV2Batch.sol/ISablierV2Batch.json $interfaces +cp out-optimized/ISablierV2BatchLockup.sol/ISablierV2BatchLockup.json $interfaces cp out-optimized/ISablierV2MerkleLL.sol/ISablierV2MerkleLL.json $interfaces cp out-optimized/ISablierV2MerkleLockupFactory.sol/ISablierV2MerkleLockupFactory.json $interfaces cp out-optimized/ISablierV2MerkleLT.sol/ISablierV2MerkleLT.json $interfaces diff --git a/shell/update-precompiles.sh b/shell/update-precompiles.sh index 4e05f9eb..ef98115f 100755 --- a/shell/update-precompiles.sh +++ b/shell/update-precompiles.sh @@ -12,7 +12,7 @@ set -euo pipefail FOUNDRY_PROFILE=optimized forge build # Retrieve the raw bytecodes, removing the "0x" prefix -batch=$(cat out-optimized/SablierV2Batch.sol/SablierV2Batch.json | jq -r '.bytecode.object' | cut -c 3-) +batch_lockup=$(cat out-optimized/SablierV2BatchLockup.sol/SablierV2BatchLockup.json | jq -r '.bytecode.object' | cut -c 3-) merkle_lockup_factory=$(cat out-optimized/SablierV2MerkleLockupFactory.sol/SablierV2MerkleLockupFactory.json | jq -r '.bytecode.object' | cut -c 3-) precompiles_path="precompiles/Precompiles.sol" @@ -22,7 +22,7 @@ if [ ! -f $precompiles_path ]; then fi # Replace the current bytecodes -sd "(BYTECODE_BATCH =)[^;]+;" "\$1 hex\"$batch\";" $precompiles_path +sd "(BYTECODE_BATCH_LOCKUP =)[^;]+;" "\$1 hex\"$batch_lockup\";" $precompiles_path sd "(BYTECODE_MERKLE_LOCKUP_FACTORY =)[^;]+;" "\$1 hex\"$merkle_lockup_factory\";" $precompiles_path # Reformat the code with Forge diff --git a/src/SablierV2Batch.sol b/src/SablierV2BatchLockup.sol similarity index 89% rename from src/SablierV2Batch.sol rename to src/SablierV2BatchLockup.sol index 926ff490..5c3d958c 100644 --- a/src/SablierV2Batch.sol +++ b/src/SablierV2BatchLockup.sol @@ -8,24 +8,24 @@ import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablier import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; import { LockupDynamic, LockupLinear, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { ISablierV2Batch } from "./interfaces/ISablierV2Batch.sol"; +import { ISablierV2BatchLockup } from "./interfaces/ISablierV2BatchLockup.sol"; import { Errors } from "./libraries/Errors.sol"; -import { Batch } from "./types/DataTypes.sol"; +import { BatchLockup } from "./types/DataTypes.sol"; -/// @title SablierV2Batch -/// @notice See the documentation in {ISablierV2Batch}. -contract SablierV2Batch is ISablierV2Batch { +/// @title SablierV2BatchLockup +/// @notice See the documentation in {ISablierV2BatchLockup}. +contract SablierV2BatchLockup is ISablierV2BatchLockup { using SafeERC20 for IERC20; /*////////////////////////////////////////////////////////////////////////// SABLIER-V2-LOCKUP-DYNAMIC //////////////////////////////////////////////////////////////////////////*/ - /// @inheritdoc ISablierV2Batch + /// @inheritdoc ISablierV2BatchLockup function createWithDurationsLD( ISablierV2LockupDynamic lockupDynamic, IERC20 asset, - Batch.CreateWithDurationsLD[] calldata batch + BatchLockup.CreateWithDurationsLD[] calldata batch ) external override @@ -34,7 +34,7 @@ contract SablierV2Batch is ISablierV2Batch { // Check that the batch size is not zero. uint256 batchSize = batch.length; if (batchSize == 0) { - revert Errors.SablierV2Batch_BatchSizeZero(); + revert Errors.SablierV2BatchLockup_BatchSizeZero(); } // Calculate the sum of all of stream amounts. It is safe to use unchecked addition because one of the create @@ -69,11 +69,11 @@ contract SablierV2Batch is ISablierV2Batch { } } - /// @inheritdoc ISablierV2Batch + /// @inheritdoc ISablierV2BatchLockup function createWithTimestampsLD( ISablierV2LockupDynamic lockupDynamic, IERC20 asset, - Batch.CreateWithTimestampsLD[] calldata batch + BatchLockup.CreateWithTimestampsLD[] calldata batch ) external override @@ -82,7 +82,7 @@ contract SablierV2Batch is ISablierV2Batch { // Check that the batch size is not zero. uint256 batchSize = batch.length; if (batchSize == 0) { - revert Errors.SablierV2Batch_BatchSizeZero(); + revert Errors.SablierV2BatchLockup_BatchSizeZero(); } // Calculate the sum of all of stream amounts. It is safe to use unchecked addition because one of the create @@ -122,11 +122,11 @@ contract SablierV2Batch is ISablierV2Batch { SABLIER-V2-LOCKUP-LINEAR //////////////////////////////////////////////////////////////////////////*/ - /// @inheritdoc ISablierV2Batch + /// @inheritdoc ISablierV2BatchLockup function createWithDurationsLL( ISablierV2LockupLinear lockupLinear, IERC20 asset, - Batch.CreateWithDurationsLL[] calldata batch + BatchLockup.CreateWithDurationsLL[] calldata batch ) external override @@ -135,7 +135,7 @@ contract SablierV2Batch is ISablierV2Batch { // Check that the batch size is not zero. uint256 batchSize = batch.length; if (batchSize == 0) { - revert Errors.SablierV2Batch_BatchSizeZero(); + revert Errors.SablierV2BatchLockup_BatchSizeZero(); } // Calculate the sum of all of stream amounts. It is safe to use unchecked addition because one of the create @@ -170,11 +170,11 @@ contract SablierV2Batch is ISablierV2Batch { } } - /// @inheritdoc ISablierV2Batch + /// @inheritdoc ISablierV2BatchLockup function createWithTimestampsLL( ISablierV2LockupLinear lockupLinear, IERC20 asset, - Batch.CreateWithTimestampsLL[] calldata batch + BatchLockup.CreateWithTimestampsLL[] calldata batch ) external override @@ -183,7 +183,7 @@ contract SablierV2Batch is ISablierV2Batch { // Check that the batch is not empty. uint256 batchSize = batch.length; if (batchSize == 0) { - revert Errors.SablierV2Batch_BatchSizeZero(); + revert Errors.SablierV2BatchLockup_BatchSizeZero(); } // Calculate the sum of all of stream amounts. It is safe to use unchecked addition because one of the create @@ -222,11 +222,11 @@ contract SablierV2Batch is ISablierV2Batch { SABLIER-V2-LOCKUP-TRANCHED //////////////////////////////////////////////////////////////////////////*/ - /// @inheritdoc ISablierV2Batch + /// @inheritdoc ISablierV2BatchLockup function createWithDurationsLT( ISablierV2LockupTranched lockupTranched, IERC20 asset, - Batch.CreateWithDurationsLT[] calldata batch + BatchLockup.CreateWithDurationsLT[] calldata batch ) external override @@ -235,7 +235,7 @@ contract SablierV2Batch is ISablierV2Batch { // Check that the batch size is not zero. uint256 batchSize = batch.length; if (batchSize == 0) { - revert Errors.SablierV2Batch_BatchSizeZero(); + revert Errors.SablierV2BatchLockup_BatchSizeZero(); } // Calculate the sum of all of stream amounts. It is safe to use unchecked addition because one of the create @@ -270,11 +270,11 @@ contract SablierV2Batch is ISablierV2Batch { } } - /// @inheritdoc ISablierV2Batch + /// @inheritdoc ISablierV2BatchLockup function createWithTimestampsLT( ISablierV2LockupTranched lockupTranched, IERC20 asset, - Batch.CreateWithTimestampsLT[] calldata batch + BatchLockup.CreateWithTimestampsLT[] calldata batch ) external override @@ -283,7 +283,7 @@ contract SablierV2Batch is ISablierV2Batch { // Check that the batch size is not zero. uint256 batchSize = batch.length; if (batchSize == 0) { - revert Errors.SablierV2Batch_BatchSizeZero(); + revert Errors.SablierV2BatchLockup_BatchSizeZero(); } // Calculate the sum of all of stream amounts. It is safe to use unchecked addition because one of the create @@ -323,7 +323,7 @@ contract SablierV2Batch is ISablierV2Batch { HELPER FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @dev Helper function to approve a Sablier contract to spend funds from the batch. If the current allowance + /// @dev Helper function to approve a Sablier contract to spend funds from the batchLockup. If the current allowance /// is insufficient, this function approves Sablier to spend the exact `amount`. /// The {SafeERC20.forceApprove} function is used to handle special ERC-20 assets (e.g. USDT) that require the /// current allowance to be zero before setting it to a non-zero value. @@ -334,9 +334,10 @@ contract SablierV2Batch is ISablierV2Batch { } } - /// @dev Helper function to transfer assets from the caller to the batch contract and approve the Sablier contract. + /// @dev Helper function to transfer assets from the caller to the batchLockup contract and approve the Sablier + /// contract. function _handleTransfer(address sablierContract, IERC20 asset, uint256 amount) internal { - // Transfer the assets to the batch contract. + // Transfer the assets to the batchLockup contract. asset.safeTransferFrom({ from: msg.sender, to: address(this), value: amount }); // Approve the Sablier contract to spend funds. diff --git a/src/interfaces/ISablierV2Batch.sol b/src/interfaces/ISablierV2BatchLockup.sol similarity index 92% rename from src/interfaces/ISablierV2Batch.sol rename to src/interfaces/ISablierV2BatchLockup.sol index 55d35863..4460fca3 100644 --- a/src/interfaces/ISablierV2Batch.sol +++ b/src/interfaces/ISablierV2BatchLockup.sol @@ -6,11 +6,11 @@ import { ISablierV2LockupDynamic } from "@sablier/v2-core/src/interfaces/ISablie import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; -import { Batch } from "../types/DataTypes.sol"; +import { BatchLockup } from "../types/DataTypes.sol"; -/// @title ISablierV2Batch +/// @title ISablierV2BatchLockup /// @notice Helper to batch create Sablier V2 Lockup streams. -interface ISablierV2Batch { +interface ISablierV2BatchLockup { /*////////////////////////////////////////////////////////////////////////// SABLIER-V2-LOCKUP-LINEAR //////////////////////////////////////////////////////////////////////////*/ @@ -29,7 +29,7 @@ interface ISablierV2Batch { function createWithDurationsLL( ISablierV2LockupLinear lockupLinear, IERC20 asset, - Batch.CreateWithDurationsLL[] calldata batch + BatchLockup.CreateWithDurationsLL[] calldata batch ) external returns (uint256[] memory streamIds); @@ -48,7 +48,7 @@ interface ISablierV2Batch { function createWithTimestampsLL( ISablierV2LockupLinear lockupLinear, IERC20 asset, - Batch.CreateWithTimestampsLL[] calldata batch + BatchLockup.CreateWithTimestampsLL[] calldata batch ) external returns (uint256[] memory streamIds); @@ -71,7 +71,7 @@ interface ISablierV2Batch { function createWithDurationsLD( ISablierV2LockupDynamic lockupDynamic, IERC20 asset, - Batch.CreateWithDurationsLD[] calldata batch + BatchLockup.CreateWithDurationsLD[] calldata batch ) external returns (uint256[] memory streamIds); @@ -90,7 +90,7 @@ interface ISablierV2Batch { function createWithTimestampsLD( ISablierV2LockupDynamic lockupDynamic, IERC20 asset, - Batch.CreateWithTimestampsLD[] calldata batch + BatchLockup.CreateWithTimestampsLD[] calldata batch ) external returns (uint256[] memory streamIds); @@ -113,7 +113,7 @@ interface ISablierV2Batch { function createWithDurationsLT( ISablierV2LockupTranched lockupTranched, IERC20 asset, - Batch.CreateWithDurationsLT[] calldata batch + BatchLockup.CreateWithDurationsLT[] calldata batch ) external returns (uint256[] memory streamIds); @@ -132,7 +132,7 @@ interface ISablierV2Batch { function createWithTimestampsLT( ISablierV2LockupTranched lockupTranched, IERC20 asset, - Batch.CreateWithTimestampsLT[] calldata batch + BatchLockup.CreateWithTimestampsLT[] calldata batch ) external returns (uint256[] memory streamIds); diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index dd14b0b0..a70d5285 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -5,10 +5,10 @@ pragma solidity >=0.8.22; /// @notice Library containing all custom errors the protocol may revert with. library Errors { /*////////////////////////////////////////////////////////////////////////// - SABLIER-V2-BATCH + SABLIER-V2-BATCH-LOCKUP //////////////////////////////////////////////////////////////////////////*/ - error SablierV2Batch_BatchSizeZero(); + error SablierV2BatchLockup_BatchSizeZero(); /*////////////////////////////////////////////////////////////////////////// SABLIER-V2-MERKLE-LOCKUP diff --git a/src/types/DataTypes.sol b/src/types/DataTypes.sol index 91ca4c1d..4acad74d 100644 --- a/src/types/DataTypes.sol +++ b/src/types/DataTypes.sol @@ -6,7 +6,7 @@ import { UD2x18 } from "@prb/math/src/UD2x18.sol"; import { ISablierV2Lockup } from "@sablier/v2-core/src/interfaces/ISablierV2Lockup.sol"; import { Broker, LockupDynamic, LockupLinear, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; -library Batch { +library BatchLockup { /// @notice A struct encapsulating the lockup contract's address and the stream ids to cancel. struct CancelMultiple { ISablierV2Lockup lockup; diff --git a/test/Base.t.sol b/test/Base.t.sol index d6c826ea..06483407 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -13,11 +13,11 @@ import { Assertions as V2CoreAssertions } from "@sablier/v2-core/test/utils/Asse import { Constants as V2CoreConstants } from "@sablier/v2-core/test/utils/Constants.sol"; import { Utils as V2CoreUtils } from "@sablier/v2-core/test/utils/Utils.sol"; -import { ISablierV2Batch } from "src/interfaces/ISablierV2Batch.sol"; +import { ISablierV2BatchLockup } from "src/interfaces/ISablierV2BatchLockup.sol"; import { ISablierV2MerkleLL } from "src/interfaces/ISablierV2MerkleLL.sol"; import { ISablierV2MerkleLockupFactory } from "src/interfaces/ISablierV2MerkleLockupFactory.sol"; import { ISablierV2MerkleLT } from "src/interfaces/ISablierV2MerkleLT.sol"; -import { SablierV2Batch } from "src/SablierV2Batch.sol"; +import { SablierV2BatchLockup } from "src/SablierV2BatchLockup.sol"; import { SablierV2MerkleLL } from "src/SablierV2MerkleLL.sol"; import { SablierV2MerkleLockupFactory } from "src/SablierV2MerkleLockupFactory.sol"; import { SablierV2MerkleLT } from "src/SablierV2MerkleLT.sol"; @@ -50,7 +50,7 @@ abstract contract Base_Test is TEST CONTRACTS //////////////////////////////////////////////////////////////////////////*/ - ISablierV2Batch internal batch; + ISablierV2BatchLockup internal batchLockup; IERC20 internal dai; Defaults internal defaults; ISablierV2LockupDynamic internal lockupDynamic; @@ -102,10 +102,10 @@ abstract contract Base_Test is /// @dev Conditionally deploy V2 Periphery normally or from an optimized source compiled with `--via-ir`. function deployPeripheryConditionally() internal { if (!isTestOptimizedProfile()) { - batch = new SablierV2Batch(); + batchLockup = new SablierV2BatchLockup(); merkleLockupFactory = new SablierV2MerkleLockupFactory(); } else { - (batch, merkleLockupFactory) = deployOptimizedPeriphery(); + (batchLockup, merkleLockupFactory) = deployOptimizedPeriphery(); } } diff --git a/test/fork/Fork.t.sol b/test/fork/Fork.t.sol index a8aed19d..8a165c7b 100644 --- a/test/fork/Fork.t.sol +++ b/test/fork/Fork.t.sol @@ -54,8 +54,8 @@ abstract contract Fork_Test is Base_Test, V2CoreFuzzers { // Label the contracts. labelContracts(FORK_ASSET); - // Approve the Batch contract. - approveContract({ asset_: FORK_ASSET, from: users.alice, spender: address(batch) }); + // Approve the BatchLockup contract. + approveContract({ asset_: FORK_ASSET, from: users.alice, spender: address(batchLockup) }); } /*////////////////////////////////////////////////////////////////////////// diff --git a/test/fork/assets/USDC.t.sol b/test/fork/assets/USDC.t.sol index 5589fb15..ca1467e0 100644 --- a/test/fork/assets/USDC.t.sol +++ b/test/fork/assets/USDC.t.sol @@ -3,25 +3,25 @@ pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { CreateWithTimestamps_LockupDynamic_Batch_Fork_Test } from "../batch/createWithTimestampsLD.t.sol"; -import { CreateWithTimestamps_LockupLinear_Batch_Fork_Test } from "../batch/createWithTimestampsLL.t.sol"; -import { CreateWithTimestamps_LockupTranched_Batch_Fork_Test } from "../batch/createWithTimestampsLT.t.sol"; +import { CreateWithTimestamps_LockupDynamic_BatchLockup_Fork_Test } from "../batch-lockup/createWithTimestampsLD.t.sol"; +import { CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test } from "../batch-lockup/createWithTimestampsLL.t.sol"; +import { CreateWithTimestamps_LockupTranched_BatchLockup_Fork_Test } from "../batch-lockup/createWithTimestampsLT.t.sol"; import { MerkleLL_Fork_Test } from "../merkle-lockup/MerkleLL.t.sol"; import { MerkleLT_Fork_Test } from "../merkle-lockup/MerkleLT.t.sol"; /// @dev An ERC-20 asset with 6 decimals. IERC20 constant usdc = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48); -contract USDC_CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is - CreateWithTimestamps_LockupDynamic_Batch_Fork_Test(usdc) +contract USDC_CreateWithTimestamps_LockupDynamic_BatchLockup_Fork_Test is + CreateWithTimestamps_LockupDynamic_BatchLockup_Fork_Test(usdc) { } -contract USDC_CreateWithTimestamps_LockupLinear_Batch_Fork_Test is - CreateWithTimestamps_LockupLinear_Batch_Fork_Test(usdc) +contract USDC_CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test is + CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test(usdc) { } -contract USDC_CreateWithTimestamps_LockupTranched_Batch_Fork_Test is - CreateWithTimestamps_LockupTranched_Batch_Fork_Test(usdc) +contract USDC_CreateWithTimestamps_LockupTranched_BatchLockup_Fork_Test is + CreateWithTimestamps_LockupTranched_BatchLockup_Fork_Test(usdc) { } contract USDC_MerkleLL_Fork_Test is MerkleLL_Fork_Test(usdc) { } diff --git a/test/fork/assets/USDT.t.sol b/test/fork/assets/USDT.t.sol index de70935f..bdc5ed85 100644 --- a/test/fork/assets/USDT.t.sol +++ b/test/fork/assets/USDT.t.sol @@ -3,25 +3,25 @@ pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { CreateWithTimestamps_LockupDynamic_Batch_Fork_Test } from "../batch/createWithTimestampsLD.t.sol"; -import { CreateWithTimestamps_LockupLinear_Batch_Fork_Test } from "../batch/createWithTimestampsLL.t.sol"; -import { CreateWithTimestamps_LockupTranched_Batch_Fork_Test } from "../batch/createWithTimestampsLT.t.sol"; +import { CreateWithTimestamps_LockupDynamic_BatchLockup_Fork_Test } from "../batch-lockup/createWithTimestampsLD.t.sol"; +import { CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test } from "../batch-lockup/createWithTimestampsLL.t.sol"; +import { CreateWithTimestamps_LockupTranched_BatchLockup_Fork_Test } from "../batch-lockup/createWithTimestampsLT.t.sol"; import { MerkleLL_Fork_Test } from "../merkle-lockup/MerkleLL.t.sol"; import { MerkleLT_Fork_Test } from "../merkle-lockup/MerkleLT.t.sol"; /// @dev An ERC-20 asset that suffers from the missing return value bug. IERC20 constant usdt = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7); -contract USDT_CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is - CreateWithTimestamps_LockupDynamic_Batch_Fork_Test(usdt) +contract USDT_CreateWithTimestamps_LockupDynamic_BatchLockup_Fork_Test is + CreateWithTimestamps_LockupDynamic_BatchLockup_Fork_Test(usdt) { } -contract USDT_CreateWithTimestamps_LockupLinear_Batch_Fork_Test is - CreateWithTimestamps_LockupLinear_Batch_Fork_Test(usdt) +contract USDT_CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test is + CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test(usdt) { } -contract USDT_CreateWithTimestamps_LockupTranched_Batch_Fork_Test is - CreateWithTimestamps_LockupTranched_Batch_Fork_Test(usdt) +contract USDT_CreateWithTimestamps_LockupTranched_BatchLockup_Fork_Test is + CreateWithTimestamps_LockupTranched_BatchLockup_Fork_Test(usdt) { } contract USDT_MerkleLL_Fork_Test is MerkleLL_Fork_Test(usdt) { } diff --git a/test/fork/batch/createWithTimestampsLD.t.sol b/test/fork/batch-lockup/createWithTimestampsLD.t.sol similarity index 82% rename from test/fork/batch/createWithTimestampsLD.t.sol rename to test/fork/batch-lockup/createWithTimestampsLD.t.sol index f7e47254..c36edc94 100644 --- a/test/fork/batch/createWithTimestampsLD.t.sol +++ b/test/fork/batch-lockup/createWithTimestampsLD.t.sol @@ -4,14 +4,14 @@ pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { LockupDynamic } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { Batch } from "src/types/DataTypes.sol"; +import { BatchLockup } from "src/types/DataTypes.sol"; import { ArrayBuilder } from "../../utils/ArrayBuilder.sol"; -import { BatchBuilder } from "../../utils/BatchBuilder.sol"; +import { BatchLockupBuilder } from "../../utils/BatchLockupBuilder.sol"; import { Fork_Test } from "../Fork.t.sol"; /// @dev Runs against multiple fork assets. -abstract contract CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is Fork_Test { +abstract contract CreateWithTimestamps_LockupDynamic_BatchLockup_Fork_Test is Fork_Test { constructor(IERC20 asset_) Fork_Test(asset_) { } function setUp() public virtual override { @@ -44,7 +44,7 @@ abstract contract CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is Fork_Tes uint128 totalTransferAmount = params.perStreamAmount * params.batchSize; deal({ token: address(FORK_ASSET), to: params.sender, give: uint256(totalTransferAmount) }); - approveContract({ asset_: FORK_ASSET, from: params.sender, spender: address(batch) }); + approveContract({ asset_: FORK_ASSET, from: params.sender, spender: address(batchLockup) }); LockupDynamic.CreateWithTimestamps memory createWithTimestamps = LockupDynamic.CreateWithTimestamps({ sender: params.sender, @@ -57,25 +57,25 @@ abstract contract CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is Fork_Tes segments: params.segments, broker: defaults.broker() }); - Batch.CreateWithTimestampsLD[] memory batchParams = - BatchBuilder.fillBatch(createWithTimestamps, params.batchSize); + BatchLockup.CreateWithTimestampsLD[] memory batchParams = + BatchLockupBuilder.fillBatch(createWithTimestamps, params.batchSize); expectCallToTransferFrom({ asset_: address(FORK_ASSET), from: params.sender, - to: address(batch), + to: address(batchLockup), amount: totalTransferAmount }); expectMultipleCallsToCreateWithTimestampsLD({ count: uint64(params.batchSize), params: createWithTimestamps }); expectMultipleCallsToTransferFrom({ asset_: address(FORK_ASSET), count: uint64(params.batchSize), - from: address(batch), + from: address(batchLockup), to: address(lockupDynamic), amount: params.perStreamAmount }); - uint256[] memory actualStreamIds = batch.createWithTimestampsLD(lockupDynamic, FORK_ASSET, batchParams); + uint256[] memory actualStreamIds = batchLockup.createWithTimestampsLD(lockupDynamic, FORK_ASSET, batchParams); uint256[] memory expectedStreamIds = ArrayBuilder.fillStreamIds(firstStreamId, params.batchSize); assertEq(actualStreamIds, expectedStreamIds); } diff --git a/test/fork/batch/createWithTimestampsLL.t.sol b/test/fork/batch-lockup/createWithTimestampsLL.t.sol similarity index 82% rename from test/fork/batch/createWithTimestampsLL.t.sol rename to test/fork/batch-lockup/createWithTimestampsLL.t.sol index ab3af65c..9b68add4 100644 --- a/test/fork/batch/createWithTimestampsLL.t.sol +++ b/test/fork/batch-lockup/createWithTimestampsLL.t.sol @@ -4,14 +4,14 @@ pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { Batch } from "src/types/DataTypes.sol"; +import { BatchLockup } from "src/types/DataTypes.sol"; import { ArrayBuilder } from "../../utils/ArrayBuilder.sol"; -import { BatchBuilder } from "../../utils/BatchBuilder.sol"; +import { BatchLockupBuilder } from "../../utils/BatchLockupBuilder.sol"; import { Fork_Test } from "../Fork.t.sol"; /// @dev Runs against multiple fork assets. -abstract contract CreateWithTimestamps_LockupLinear_Batch_Fork_Test is Fork_Test { +abstract contract CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test is Fork_Test { constructor(IERC20 asset_) Fork_Test(asset_) { } function setUp() public virtual override { @@ -40,7 +40,7 @@ abstract contract CreateWithTimestamps_LockupLinear_Batch_Fork_Test is Fork_Test uint128 totalTransferAmount = params.perStreamAmount * params.batchSize; deal({ token: address(FORK_ASSET), to: params.sender, give: uint256(totalTransferAmount) }); - approveContract({ asset_: FORK_ASSET, from: params.sender, spender: address(batch) }); + approveContract({ asset_: FORK_ASSET, from: params.sender, spender: address(batchLockup) }); LockupLinear.CreateWithTimestamps memory createParams = LockupLinear.CreateWithTimestamps({ sender: params.sender, @@ -52,25 +52,26 @@ abstract contract CreateWithTimestamps_LockupLinear_Batch_Fork_Test is Fork_Test range: params.range, broker: defaults.broker() }); - Batch.CreateWithTimestampsLL[] memory batchParams = BatchBuilder.fillBatch(createParams, params.batchSize); + BatchLockup.CreateWithTimestampsLL[] memory batchParams = + BatchLockupBuilder.fillBatch(createParams, params.batchSize); // Asset flow: sender → batch → Sablier expectCallToTransferFrom({ asset_: address(FORK_ASSET), from: params.sender, - to: address(batch), + to: address(batchLockup), amount: totalTransferAmount }); expectMultipleCallsToCreateWithTimestampsLL({ count: uint64(params.batchSize), params: createParams }); expectMultipleCallsToTransferFrom({ asset_: address(FORK_ASSET), count: uint64(params.batchSize), - from: address(batch), + from: address(batchLockup), to: address(lockupLinear), amount: params.perStreamAmount }); - uint256[] memory actualStreamIds = batch.createWithTimestampsLL(lockupLinear, FORK_ASSET, batchParams); + uint256[] memory actualStreamIds = batchLockup.createWithTimestampsLL(lockupLinear, FORK_ASSET, batchParams); uint256[] memory expectedStreamIds = ArrayBuilder.fillStreamIds(firstStreamId, params.batchSize); assertEq(actualStreamIds, expectedStreamIds); } diff --git a/test/fork/batch/createWithTimestampsLT.t.sol b/test/fork/batch-lockup/createWithTimestampsLT.t.sol similarity index 82% rename from test/fork/batch/createWithTimestampsLT.t.sol rename to test/fork/batch-lockup/createWithTimestampsLT.t.sol index 63d4d62a..30f758a8 100644 --- a/test/fork/batch/createWithTimestampsLT.t.sol +++ b/test/fork/batch-lockup/createWithTimestampsLT.t.sol @@ -4,14 +4,14 @@ pragma solidity >=0.8.22 <0.9.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { Batch } from "src/types/DataTypes.sol"; +import { BatchLockup } from "src/types/DataTypes.sol"; import { ArrayBuilder } from "../../utils/ArrayBuilder.sol"; -import { BatchBuilder } from "../../utils/BatchBuilder.sol"; +import { BatchLockupBuilder } from "../../utils/BatchLockupBuilder.sol"; import { Fork_Test } from "../Fork.t.sol"; /// @dev Runs against multiple fork assets. -abstract contract CreateWithTimestamps_LockupTranched_Batch_Fork_Test is Fork_Test { +abstract contract CreateWithTimestamps_LockupTranched_BatchLockup_Fork_Test is Fork_Test { constructor(IERC20 asset_) Fork_Test(asset_) { } function setUp() public virtual override { @@ -44,7 +44,7 @@ abstract contract CreateWithTimestamps_LockupTranched_Batch_Fork_Test is Fork_Te uint128 totalTransferAmount = params.perStreamAmount * params.batchSize; deal({ token: address(FORK_ASSET), to: params.sender, give: uint256(totalTransferAmount) }); - approveContract({ asset_: FORK_ASSET, from: params.sender, spender: address(batch) }); + approveContract({ asset_: FORK_ASSET, from: params.sender, spender: address(batchLockup) }); LockupTranched.CreateWithTimestamps memory createWithTimestamps = LockupTranched.CreateWithTimestamps({ sender: params.sender, @@ -57,25 +57,25 @@ abstract contract CreateWithTimestamps_LockupTranched_Batch_Fork_Test is Fork_Te tranches: params.tranches, broker: defaults.broker() }); - Batch.CreateWithTimestampsLT[] memory batchParams = - BatchBuilder.fillBatch(createWithTimestamps, params.batchSize); + BatchLockup.CreateWithTimestampsLT[] memory batchParams = + BatchLockupBuilder.fillBatch(createWithTimestamps, params.batchSize); expectCallToTransferFrom({ asset_: address(FORK_ASSET), from: params.sender, - to: address(batch), + to: address(batchLockup), amount: totalTransferAmount }); expectMultipleCallsToCreateWithTimestampsLT({ count: uint64(params.batchSize), params: createWithTimestamps }); expectMultipleCallsToTransferFrom({ asset_: address(FORK_ASSET), count: uint64(params.batchSize), - from: address(batch), + from: address(batchLockup), to: address(lockupTranched), amount: params.perStreamAmount }); - uint256[] memory actualStreamIds = batch.createWithTimestampsLT(lockupTranched, FORK_ASSET, batchParams); + uint256[] memory actualStreamIds = batchLockup.createWithTimestampsLT(lockupTranched, FORK_ASSET, batchParams); uint256[] memory expectedStreamIds = ArrayBuilder.fillStreamIds(firstStreamId, params.batchSize); assertEq(actualStreamIds, expectedStreamIds); } diff --git a/test/integration/Integration.t.sol b/test/integration/Integration.t.sol index 80c9d0f2..f889e27f 100644 --- a/test/integration/Integration.t.sol +++ b/test/integration/Integration.t.sol @@ -28,8 +28,8 @@ abstract contract Integration_Test is Base_Test { // Label the contracts. labelContracts(dai); - // Approve the Batch contract. - approveContract({ asset_: dai, from: users.alice, spender: address(batch) }); + // Approve the BatchLockup contract. + approveContract({ asset_: dai, from: users.alice, spender: address(batchLockup) }); } /*////////////////////////////////////////////////////////////////////////// diff --git a/test/integration/batch/create-with-durations-ld/createWithDurationsLD.t.sol b/test/integration/batch-lockup/create-with-durations-ld/createWithDurationsLD.t.sol similarity index 59% rename from test/integration/batch/create-with-durations-ld/createWithDurationsLD.t.sol rename to test/integration/batch-lockup/create-with-durations-ld/createWithDurationsLD.t.sol index a57072e4..fdcd5924 100644 --- a/test/integration/batch/create-with-durations-ld/createWithDurationsLD.t.sol +++ b/test/integration/batch-lockup/create-with-durations-ld/createWithDurationsLD.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; -import { Batch } from "src/types/DataTypes.sol"; +import { BatchLockup } from "src/types/DataTypes.sol"; import { Integration_Test } from "../../Integration.t.sol"; @@ -12,9 +12,9 @@ contract CreateWithDurationsLD_Integration_Test is Integration_Test { } function test_RevertWhen_BatchSizeZero() external { - Batch.CreateWithDurationsLD[] memory batchParams = new Batch.CreateWithDurationsLD[](0); - vm.expectRevert(Errors.SablierV2Batch_BatchSizeZero.selector); - batch.createWithDurationsLD(lockupDynamic, dai, batchParams); + BatchLockup.CreateWithDurationsLD[] memory batchParams = new BatchLockup.CreateWithDurationsLD[](0); + vm.expectRevert(Errors.SablierV2BatchLockup_BatchSizeZero.selector); + batchLockup.createWithDurationsLD(lockupDynamic, dai, batchParams); } modifier whenBatchSizeNotZero() { @@ -22,23 +22,27 @@ contract CreateWithDurationsLD_Integration_Test is Integration_Test { } function test_BatchCreateWithDurations() external whenBatchSizeNotZero { - // Asset flow: Alice → batch → Sablier - // Expect transfers from Alice to the batch, and then from the batch to the Sablier contract. - expectCallToTransferFrom({ from: users.alice, to: address(batch), amount: defaults.TOTAL_TRANSFER_AMOUNT() }); + // Asset flow: Alice → batchLockup → Sablier + // Expect transfers from Alice to the batchLockup, and then from the batchLockup to the Sablier contract. + expectCallToTransferFrom({ + from: users.alice, + to: address(batchLockup), + amount: defaults.TOTAL_TRANSFER_AMOUNT() + }); expectMultipleCallsToCreateWithDurationsLD({ count: defaults.BATCH_SIZE(), params: defaults.createWithDurationsLD() }); expectMultipleCallsToTransferFrom({ count: defaults.BATCH_SIZE(), - from: address(batch), + from: address(batchLockup), to: address(lockupDynamic), amount: defaults.PER_STREAM_AMOUNT() }); // Assert that the batch of streams has been created successfully. uint256[] memory actualStreamIds = - batch.createWithDurationsLD(lockupDynamic, dai, defaults.batchCreateWithDurationsLD()); + batchLockup.createWithDurationsLD(lockupDynamic, dai, defaults.batchCreateWithDurationsLD()); uint256[] memory expectedStreamIds = defaults.incrementalStreamIds(); assertEq(actualStreamIds, expectedStreamIds, "stream ids mismatch"); } diff --git a/test/integration/batch/create-with-durations-ld/createWithDurationsLD.tree b/test/integration/batch-lockup/create-with-durations-ld/createWithDurationsLD.tree similarity index 100% rename from test/integration/batch/create-with-durations-ld/createWithDurationsLD.tree rename to test/integration/batch-lockup/create-with-durations-ld/createWithDurationsLD.tree diff --git a/test/integration/batch/create-with-durations-ll/createWithDurationsLL.t.sol b/test/integration/batch-lockup/create-with-durations-ll/createWithDurationsLL.t.sol similarity index 59% rename from test/integration/batch/create-with-durations-ll/createWithDurationsLL.t.sol rename to test/integration/batch-lockup/create-with-durations-ll/createWithDurationsLL.t.sol index 404b04d2..ae18ca04 100644 --- a/test/integration/batch/create-with-durations-ll/createWithDurationsLL.t.sol +++ b/test/integration/batch-lockup/create-with-durations-ll/createWithDurationsLL.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; -import { Batch } from "src/types/DataTypes.sol"; +import { BatchLockup } from "src/types/DataTypes.sol"; import { Integration_Test } from "../../Integration.t.sol"; @@ -12,9 +12,9 @@ contract CreateWithDurationsLL_Integration_Test is Integration_Test { } function test_RevertWhen_BatchSizeZero() external { - Batch.CreateWithDurationsLL[] memory batchParams = new Batch.CreateWithDurationsLL[](0); - vm.expectRevert(Errors.SablierV2Batch_BatchSizeZero.selector); - batch.createWithDurationsLL(lockupLinear, dai, batchParams); + BatchLockup.CreateWithDurationsLL[] memory batchParams = new BatchLockup.CreateWithDurationsLL[](0); + vm.expectRevert(Errors.SablierV2BatchLockup_BatchSizeZero.selector); + batchLockup.createWithDurationsLL(lockupLinear, dai, batchParams); } modifier whenBatchSizeNotZero() { @@ -22,23 +22,27 @@ contract CreateWithDurationsLL_Integration_Test is Integration_Test { } function test_BatchCreateWithDurations() external whenBatchSizeNotZero { - // Asset flow: Alice → batch → Sablier - // Expect transfers from Alice to the batch, and then from the batch to the Sablier contract. - expectCallToTransferFrom({ from: users.alice, to: address(batch), amount: defaults.TOTAL_TRANSFER_AMOUNT() }); + // Asset flow: Alice → batchLockup → Sablier + // Expect transfers from Alice to the batchLockup, and then from the batchLockup to the Sablier contract. + expectCallToTransferFrom({ + from: users.alice, + to: address(batchLockup), + amount: defaults.TOTAL_TRANSFER_AMOUNT() + }); expectMultipleCallsToCreateWithDurationsLL({ count: defaults.BATCH_SIZE(), params: defaults.createWithDurationsLL() }); expectMultipleCallsToTransferFrom({ count: defaults.BATCH_SIZE(), - from: address(batch), + from: address(batchLockup), to: address(lockupLinear), amount: defaults.PER_STREAM_AMOUNT() }); // Assert that the batch of streams has been created successfully. uint256[] memory actualStreamIds = - batch.createWithDurationsLL(lockupLinear, dai, defaults.batchCreateWithDurationsLL()); + batchLockup.createWithDurationsLL(lockupLinear, dai, defaults.batchCreateWithDurationsLL()); uint256[] memory expectedStreamIds = defaults.incrementalStreamIds(); assertEq(actualStreamIds, expectedStreamIds, "stream ids mismatch"); } diff --git a/test/integration/batch/create-with-durations-ll/createWithDurationsLL.tree b/test/integration/batch-lockup/create-with-durations-ll/createWithDurationsLL.tree similarity index 100% rename from test/integration/batch/create-with-durations-ll/createWithDurationsLL.tree rename to test/integration/batch-lockup/create-with-durations-ll/createWithDurationsLL.tree diff --git a/test/integration/batch/create-with-durations-lt/createWithDurationsLT.t.sol b/test/integration/batch-lockup/create-with-durations-lt/createWithDurationsLT.t.sol similarity index 59% rename from test/integration/batch/create-with-durations-lt/createWithDurationsLT.t.sol rename to test/integration/batch-lockup/create-with-durations-lt/createWithDurationsLT.t.sol index 947a8578..86fd7153 100644 --- a/test/integration/batch/create-with-durations-lt/createWithDurationsLT.t.sol +++ b/test/integration/batch-lockup/create-with-durations-lt/createWithDurationsLT.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; -import { Batch } from "src/types/DataTypes.sol"; +import { BatchLockup } from "src/types/DataTypes.sol"; import { Integration_Test } from "../../Integration.t.sol"; @@ -12,9 +12,9 @@ contract CreateWithDurationsLT_Integration_Test is Integration_Test { } function test_RevertWhen_BatchSizeZero() external { - Batch.CreateWithDurationsLT[] memory batchParams = new Batch.CreateWithDurationsLT[](0); - vm.expectRevert(Errors.SablierV2Batch_BatchSizeZero.selector); - batch.createWithDurationsLT(lockupTranched, dai, batchParams); + BatchLockup.CreateWithDurationsLT[] memory batchParams = new BatchLockup.CreateWithDurationsLT[](0); + vm.expectRevert(Errors.SablierV2BatchLockup_BatchSizeZero.selector); + batchLockup.createWithDurationsLT(lockupTranched, dai, batchParams); } modifier whenBatchSizeNotZero() { @@ -22,23 +22,27 @@ contract CreateWithDurationsLT_Integration_Test is Integration_Test { } function test_BatchCreateWithDurations() external whenBatchSizeNotZero { - // Asset flow: Alice → batch → Sablier - // Expect transfers from Alice to the batch, and then from the batch to the Sablier contract. - expectCallToTransferFrom({ from: users.alice, to: address(batch), amount: defaults.TOTAL_TRANSFER_AMOUNT() }); + // Asset flow: Alice → batchLockup → Sablier + // Expect transfers from Alice to the batchLockup, and then from the batchLockup to the Sablier contract. + expectCallToTransferFrom({ + from: users.alice, + to: address(batchLockup), + amount: defaults.TOTAL_TRANSFER_AMOUNT() + }); expectMultipleCallsToCreateWithDurationsLT({ count: defaults.BATCH_SIZE(), params: defaults.createWithDurationsLT() }); expectMultipleCallsToTransferFrom({ count: defaults.BATCH_SIZE(), - from: address(batch), + from: address(batchLockup), to: address(lockupTranched), amount: defaults.PER_STREAM_AMOUNT() }); // Assert that the batch of streams has been created successfully. uint256[] memory actualStreamIds = - batch.createWithDurationsLT(lockupTranched, dai, defaults.batchCreateWithDurationsLT()); + batchLockup.createWithDurationsLT(lockupTranched, dai, defaults.batchCreateWithDurationsLT()); uint256[] memory expectedStreamIds = defaults.incrementalStreamIds(); assertEq(actualStreamIds, expectedStreamIds, "stream ids mismatch"); } diff --git a/test/integration/batch/create-with-durations-lt/createWithDurationsLT.tree b/test/integration/batch-lockup/create-with-durations-lt/createWithDurationsLT.tree similarity index 100% rename from test/integration/batch/create-with-durations-lt/createWithDurationsLT.tree rename to test/integration/batch-lockup/create-with-durations-lt/createWithDurationsLT.tree diff --git a/test/integration/batch/create-with-timestamps-ld/createWithTimestampsLD.t.sol b/test/integration/batch-lockup/create-with-timestamps-ld/createWithTimestampsLD.t.sol similarity index 59% rename from test/integration/batch/create-with-timestamps-ld/createWithTimestampsLD.t.sol rename to test/integration/batch-lockup/create-with-timestamps-ld/createWithTimestampsLD.t.sol index d8525d6c..fd15616d 100644 --- a/test/integration/batch/create-with-timestamps-ld/createWithTimestampsLD.t.sol +++ b/test/integration/batch-lockup/create-with-timestamps-ld/createWithTimestampsLD.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; -import { Batch } from "src/types/DataTypes.sol"; +import { BatchLockup } from "src/types/DataTypes.sol"; import { Integration_Test } from "../../Integration.t.sol"; @@ -12,9 +12,9 @@ contract CreateWithTimestampsLD_Integration_Test is Integration_Test { } function test_RevertWhen_BatchSizeZero() external { - Batch.CreateWithTimestampsLD[] memory batchParams = new Batch.CreateWithTimestampsLD[](0); - vm.expectRevert(Errors.SablierV2Batch_BatchSizeZero.selector); - batch.createWithTimestampsLD(lockupDynamic, dai, batchParams); + BatchLockup.CreateWithTimestampsLD[] memory batchParams = new BatchLockup.CreateWithTimestampsLD[](0); + vm.expectRevert(Errors.SablierV2BatchLockup_BatchSizeZero.selector); + batchLockup.createWithTimestampsLD(lockupDynamic, dai, batchParams); } modifier whenBatchSizeNotZero() { @@ -22,23 +22,27 @@ contract CreateWithTimestampsLD_Integration_Test is Integration_Test { } function test_BatchCreateWithTimestamps() external whenBatchSizeNotZero { - // Asset flow: Alice → batch → Sablier - // Expect transfers from Alice to the batch, and then from the batch to the Sablier contract. - expectCallToTransferFrom({ from: users.alice, to: address(batch), amount: defaults.TOTAL_TRANSFER_AMOUNT() }); + // Asset flow: Alice → batchLockup → Sablier + // Expect transfers from Alice to the batchLockup, and then from the batchLockup to the Sablier contract. + expectCallToTransferFrom({ + from: users.alice, + to: address(batchLockup), + amount: defaults.TOTAL_TRANSFER_AMOUNT() + }); expectMultipleCallsToCreateWithTimestampsLD({ count: defaults.BATCH_SIZE(), params: defaults.createWithTimestampsLD() }); expectMultipleCallsToTransferFrom({ count: defaults.BATCH_SIZE(), - from: address(batch), + from: address(batchLockup), to: address(lockupDynamic), amount: defaults.PER_STREAM_AMOUNT() }); // Assert that the batch of streams has been created successfully. uint256[] memory actualStreamIds = - batch.createWithTimestampsLD(lockupDynamic, dai, defaults.batchCreateWithTimestampsLD()); + batchLockup.createWithTimestampsLD(lockupDynamic, dai, defaults.batchCreateWithTimestampsLD()); uint256[] memory expectedStreamIds = defaults.incrementalStreamIds(); assertEq(actualStreamIds, expectedStreamIds, "stream ids mismatch"); } diff --git a/test/integration/batch/create-with-timestamps-ld/createWithTimestampsLD.tree b/test/integration/batch-lockup/create-with-timestamps-ld/createWithTimestampsLD.tree similarity index 100% rename from test/integration/batch/create-with-timestamps-ld/createWithTimestampsLD.tree rename to test/integration/batch-lockup/create-with-timestamps-ld/createWithTimestampsLD.tree diff --git a/test/integration/batch/create-with-timestamps-ll/createWithTimestamps.t.sol b/test/integration/batch-lockup/create-with-timestamps-ll/createWithTimestamps.t.sol similarity index 59% rename from test/integration/batch/create-with-timestamps-ll/createWithTimestamps.t.sol rename to test/integration/batch-lockup/create-with-timestamps-ll/createWithTimestamps.t.sol index 976e810b..39153116 100644 --- a/test/integration/batch/create-with-timestamps-ll/createWithTimestamps.t.sol +++ b/test/integration/batch-lockup/create-with-timestamps-ll/createWithTimestamps.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; -import { Batch } from "src/types/DataTypes.sol"; +import { BatchLockup } from "src/types/DataTypes.sol"; import { Integration_Test } from "../../Integration.t.sol"; @@ -12,9 +12,9 @@ contract CreateWithTimestampsLL_Integration_Test is Integration_Test { } function test_RevertWhen_BatchSizeZero() external { - Batch.CreateWithTimestampsLL[] memory batchParams = new Batch.CreateWithTimestampsLL[](0); - vm.expectRevert(Errors.SablierV2Batch_BatchSizeZero.selector); - batch.createWithTimestampsLL(lockupLinear, dai, batchParams); + BatchLockup.CreateWithTimestampsLL[] memory batchParams = new BatchLockup.CreateWithTimestampsLL[](0); + vm.expectRevert(Errors.SablierV2BatchLockup_BatchSizeZero.selector); + batchLockup.createWithTimestampsLL(lockupLinear, dai, batchParams); } modifier whenBatchSizeNotZero() { @@ -22,23 +22,27 @@ contract CreateWithTimestampsLL_Integration_Test is Integration_Test { } function test_BatchCreateWithTimestamps() external whenBatchSizeNotZero { - // Asset flow: Alice → batch → Sablier - // Expect transfers from Alice to the batch, and then from the batch to the Sablier contract. - expectCallToTransferFrom({ from: users.alice, to: address(batch), amount: defaults.TOTAL_TRANSFER_AMOUNT() }); + // Asset flow: Alice → batchLockup → Sablier + // Expect transfers from Alice to the batchLockup, and then from the batchLockup to the Sablier contract. + expectCallToTransferFrom({ + from: users.alice, + to: address(batchLockup), + amount: defaults.TOTAL_TRANSFER_AMOUNT() + }); expectMultipleCallsToCreateWithTimestampsLL({ count: defaults.BATCH_SIZE(), params: defaults.createWithTimestampsLL() }); expectMultipleCallsToTransferFrom({ count: defaults.BATCH_SIZE(), - from: address(batch), + from: address(batchLockup), to: address(lockupLinear), amount: defaults.PER_STREAM_AMOUNT() }); // Assert that the batch of streams has been created successfully. uint256[] memory actualStreamIds = - batch.createWithTimestampsLL(lockupLinear, dai, defaults.batchCreateWithTimestampsLL()); + batchLockup.createWithTimestampsLL(lockupLinear, dai, defaults.batchCreateWithTimestampsLL()); uint256[] memory expectedStreamIds = defaults.incrementalStreamIds(); assertEq(actualStreamIds, expectedStreamIds, "stream ids mismatch"); } diff --git a/test/integration/batch/create-with-timestamps-ll/createWithTimestamps.tree b/test/integration/batch-lockup/create-with-timestamps-ll/createWithTimestamps.tree similarity index 100% rename from test/integration/batch/create-with-timestamps-ll/createWithTimestamps.tree rename to test/integration/batch-lockup/create-with-timestamps-ll/createWithTimestamps.tree diff --git a/test/integration/batch/create-with-timestamps-lt/createWithTimestampsLT.t.sol b/test/integration/batch-lockup/create-with-timestamps-lt/createWithTimestampsLT.t.sol similarity index 59% rename from test/integration/batch/create-with-timestamps-lt/createWithTimestampsLT.t.sol rename to test/integration/batch-lockup/create-with-timestamps-lt/createWithTimestampsLT.t.sol index 2bd506c5..21aa2eed 100644 --- a/test/integration/batch/create-with-timestamps-lt/createWithTimestampsLT.t.sol +++ b/test/integration/batch-lockup/create-with-timestamps-lt/createWithTimestampsLT.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; -import { Batch } from "src/types/DataTypes.sol"; +import { BatchLockup } from "src/types/DataTypes.sol"; import { Integration_Test } from "../../Integration.t.sol"; @@ -12,9 +12,9 @@ contract CreateWithTimestampsLT_Integration_Test is Integration_Test { } function test_RevertWhen_BatchSizeZero() external { - Batch.CreateWithTimestampsLT[] memory batchParams = new Batch.CreateWithTimestampsLT[](0); - vm.expectRevert(Errors.SablierV2Batch_BatchSizeZero.selector); - batch.createWithTimestampsLT(lockupTranched, dai, batchParams); + BatchLockup.CreateWithTimestampsLT[] memory batchParams = new BatchLockup.CreateWithTimestampsLT[](0); + vm.expectRevert(Errors.SablierV2BatchLockup_BatchSizeZero.selector); + batchLockup.createWithTimestampsLT(lockupTranched, dai, batchParams); } modifier whenBatchSizeNotZero() { @@ -22,23 +22,27 @@ contract CreateWithTimestampsLT_Integration_Test is Integration_Test { } function test_BatchCreateWithTimestamps() external whenBatchSizeNotZero { - // Asset flow: Alice → batch → Sablier - // Expect transfers from Alice to the batch, and then from the batch to the Sablier contract. - expectCallToTransferFrom({ from: users.alice, to: address(batch), amount: defaults.TOTAL_TRANSFER_AMOUNT() }); + // Asset flow: Alice → batchLockup → Sablier + // Expect transfers from Alice to the batchLockup, and then from the batchLockup to the Sablier contract. + expectCallToTransferFrom({ + from: users.alice, + to: address(batchLockup), + amount: defaults.TOTAL_TRANSFER_AMOUNT() + }); expectMultipleCallsToCreateWithTimestampsLT({ count: defaults.BATCH_SIZE(), params: defaults.createWithTimestampsLT() }); expectMultipleCallsToTransferFrom({ count: defaults.BATCH_SIZE(), - from: address(batch), + from: address(batchLockup), to: address(lockupTranched), amount: defaults.PER_STREAM_AMOUNT() }); // Assert that the batch of streams has been created successfully. uint256[] memory actualStreamIds = - batch.createWithTimestampsLT(lockupTranched, dai, defaults.batchCreateWithTimestampsLT()); + batchLockup.createWithTimestampsLT(lockupTranched, dai, defaults.batchCreateWithTimestampsLT()); uint256[] memory expectedStreamIds = defaults.incrementalStreamIds(); assertEq(actualStreamIds, expectedStreamIds, "stream ids mismatch"); } diff --git a/test/integration/batch/create-with-timestamps-lt/createWithTimestampsLT.tree b/test/integration/batch-lockup/create-with-timestamps-lt/createWithTimestampsLT.tree similarity index 100% rename from test/integration/batch/create-with-timestamps-lt/createWithTimestampsLT.tree rename to test/integration/batch-lockup/create-with-timestamps-lt/createWithTimestampsLT.tree diff --git a/test/utils/BatchBuilder.sol b/test/utils/BatchLockupBuilder.sol similarity index 60% rename from test/utils/BatchBuilder.sol rename to test/utils/BatchLockupBuilder.sol index 6567d193..c0a31621 100644 --- a/test/utils/BatchBuilder.sol +++ b/test/utils/BatchLockupBuilder.sol @@ -3,35 +3,35 @@ pragma solidity >=0.8.22; import { LockupDynamic, LockupLinear, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { Batch } from "../../src/types/DataTypes.sol"; +import { BatchLockup } from "../../src/types/DataTypes.sol"; -library BatchBuilder { +library BatchLockupBuilder { /// @notice Generates an array containing `batchSize` copies of `batchSingle`. function fillBatch( - Batch.CreateWithDurationsLD memory batchSingle, + BatchLockup.CreateWithDurationsLD memory batchSingle, uint256 batchSize ) internal pure - returns (Batch.CreateWithDurationsLD[] memory batch) + returns (BatchLockup.CreateWithDurationsLD[] memory batch) { - batch = new Batch.CreateWithDurationsLD[](batchSize); + batch = new BatchLockup.CreateWithDurationsLD[](batchSize); for (uint256 i = 0; i < batchSize; ++i) { batch[i] = batchSingle; } } - /// @notice Turns the `params` into an array of `Batch.CreateWithDurationsLD` structs. + /// @notice Turns the `params` into an array of `BatchLockup.CreateWithDurationsLD` structs. function fillBatch( LockupDynamic.CreateWithDurations memory params, uint256 batchSize ) internal pure - returns (Batch.CreateWithDurationsLD[] memory batch) + returns (BatchLockup.CreateWithDurationsLD[] memory batch) { - batch = new Batch.CreateWithDurationsLD[](batchSize); - Batch.CreateWithDurationsLD memory batchSingle = Batch.CreateWithDurationsLD({ + batch = new BatchLockup.CreateWithDurationsLD[](batchSize); + BatchLockup.CreateWithDurationsLD memory batchSingle = BatchLockup.CreateWithDurationsLD({ sender: params.sender, recipient: params.recipient, totalAmount: params.totalAmount, @@ -45,30 +45,30 @@ library BatchBuilder { /// @notice Generates an array containing `batchSize` copies of `batchSingle`. function fillBatch( - Batch.CreateWithDurationsLL memory batchSingle, + BatchLockup.CreateWithDurationsLL memory batchSingle, uint256 batchSize ) internal pure - returns (Batch.CreateWithDurationsLL[] memory batch) + returns (BatchLockup.CreateWithDurationsLL[] memory batch) { - batch = new Batch.CreateWithDurationsLL[](batchSize); + batch = new BatchLockup.CreateWithDurationsLL[](batchSize); for (uint256 i = 0; i < batchSize; ++i) { batch[i] = batchSingle; } } - /// @notice Turns the `params` into an array of `Batch.CreateWithDurationsLL` structs. + /// @notice Turns the `params` into an array of `BatchLockup.CreateWithDurationsLL` structs. function fillBatch( LockupLinear.CreateWithDurations memory params, uint256 batchSize ) internal pure - returns (Batch.CreateWithDurationsLL[] memory batch) + returns (BatchLockup.CreateWithDurationsLL[] memory batch) { - batch = new Batch.CreateWithDurationsLL[](batchSize); - Batch.CreateWithDurationsLL memory batchSingle = Batch.CreateWithDurationsLL({ + batch = new BatchLockup.CreateWithDurationsLL[](batchSize); + BatchLockup.CreateWithDurationsLL memory batchSingle = BatchLockup.CreateWithDurationsLL({ sender: params.sender, recipient: params.recipient, totalAmount: params.totalAmount, @@ -82,30 +82,30 @@ library BatchBuilder { /// @notice Generates an array containing `batchSize` copies of `batchSingle`. function fillBatch( - Batch.CreateWithDurationsLT memory batchSingle, + BatchLockup.CreateWithDurationsLT memory batchSingle, uint256 batchSize ) internal pure - returns (Batch.CreateWithDurationsLT[] memory batch) + returns (BatchLockup.CreateWithDurationsLT[] memory batch) { - batch = new Batch.CreateWithDurationsLT[](batchSize); + batch = new BatchLockup.CreateWithDurationsLT[](batchSize); for (uint256 i = 0; i < batchSize; ++i) { batch[i] = batchSingle; } } - /// @notice Turns the `params` into an array of `Batch.CreateWithDurationsLT` structs. + /// @notice Turns the `params` into an array of `BatchLockup.CreateWithDurationsLT` structs. function fillBatch( LockupTranched.CreateWithDurations memory params, uint256 batchSize ) internal pure - returns (Batch.CreateWithDurationsLT[] memory batch) + returns (BatchLockup.CreateWithDurationsLT[] memory batch) { - batch = new Batch.CreateWithDurationsLT[](batchSize); - Batch.CreateWithDurationsLT memory batchSingle = Batch.CreateWithDurationsLT({ + batch = new BatchLockup.CreateWithDurationsLT[](batchSize); + BatchLockup.CreateWithDurationsLT memory batchSingle = BatchLockup.CreateWithDurationsLT({ sender: params.sender, recipient: params.recipient, totalAmount: params.totalAmount, @@ -119,30 +119,30 @@ library BatchBuilder { /// @notice Generates an array containing `batchSize` copies of `batchSingle`. function fillBatch( - Batch.CreateWithTimestampsLD memory batchSingle, + BatchLockup.CreateWithTimestampsLD memory batchSingle, uint256 batchSize ) internal pure - returns (Batch.CreateWithTimestampsLD[] memory batch) + returns (BatchLockup.CreateWithTimestampsLD[] memory batch) { - batch = new Batch.CreateWithTimestampsLD[](batchSize); + batch = new BatchLockup.CreateWithTimestampsLD[](batchSize); for (uint256 i = 0; i < batchSize; ++i) { batch[i] = batchSingle; } } - /// @notice Turns the `params` into an array of `Batch.CreateWithTimestampsLDs` structs. + /// @notice Turns the `params` into an array of `BatchLockup.CreateWithTimestampsLDs` structs. function fillBatch( LockupDynamic.CreateWithTimestamps memory params, uint256 batchSize ) internal pure - returns (Batch.CreateWithTimestampsLD[] memory batch) + returns (BatchLockup.CreateWithTimestampsLD[] memory batch) { - batch = new Batch.CreateWithTimestampsLD[](batchSize); - Batch.CreateWithTimestampsLD memory batchSingle = Batch.CreateWithTimestampsLD({ + batch = new BatchLockup.CreateWithTimestampsLD[](batchSize); + BatchLockup.CreateWithTimestampsLD memory batchSingle = BatchLockup.CreateWithTimestampsLD({ sender: params.sender, recipient: params.recipient, totalAmount: params.totalAmount, @@ -157,30 +157,30 @@ library BatchBuilder { /// @notice Generates an array containing `batchSize` copies of `batchSingle`. function fillBatch( - Batch.CreateWithTimestampsLL memory batchSingle, + BatchLockup.CreateWithTimestampsLL memory batchSingle, uint256 batchSize ) internal pure - returns (Batch.CreateWithTimestampsLL[] memory batch) + returns (BatchLockup.CreateWithTimestampsLL[] memory batch) { - batch = new Batch.CreateWithTimestampsLL[](batchSize); + batch = new BatchLockup.CreateWithTimestampsLL[](batchSize); for (uint256 i = 0; i < batchSize; ++i) { batch[i] = batchSingle; } } - /// @notice Turns the `params` into an array of `Batch.CreateWithTimestampsLL` structs. + /// @notice Turns the `params` into an array of `BatchLockup.CreateWithTimestampsLL` structs. function fillBatch( LockupLinear.CreateWithTimestamps memory params, uint256 batchSize ) internal pure - returns (Batch.CreateWithTimestampsLL[] memory batch) + returns (BatchLockup.CreateWithTimestampsLL[] memory batch) { - batch = new Batch.CreateWithTimestampsLL[](batchSize); - Batch.CreateWithTimestampsLL memory batchSingle = Batch.CreateWithTimestampsLL({ + batch = new BatchLockup.CreateWithTimestampsLL[](batchSize); + BatchLockup.CreateWithTimestampsLL memory batchSingle = BatchLockup.CreateWithTimestampsLL({ sender: params.sender, recipient: params.recipient, totalAmount: params.totalAmount, @@ -194,30 +194,30 @@ library BatchBuilder { /// @notice Generates an array containing `batchSize` copies of `batchSingle`. function fillBatch( - Batch.CreateWithTimestampsLT memory batchSingle, + BatchLockup.CreateWithTimestampsLT memory batchSingle, uint256 batchSize ) internal pure - returns (Batch.CreateWithTimestampsLT[] memory batch) + returns (BatchLockup.CreateWithTimestampsLT[] memory batch) { - batch = new Batch.CreateWithTimestampsLT[](batchSize); + batch = new BatchLockup.CreateWithTimestampsLT[](batchSize); for (uint256 i = 0; i < batchSize; ++i) { batch[i] = batchSingle; } } - /// @notice Turns the `params` into an array of `Batch.CreateWithTimestampsLT` structs. + /// @notice Turns the `params` into an array of `BatchLockup.CreateWithTimestampsLT` structs. function fillBatch( LockupTranched.CreateWithTimestamps memory params, uint256 batchSize ) internal pure - returns (Batch.CreateWithTimestampsLT[] memory batch) + returns (BatchLockup.CreateWithTimestampsLT[] memory batch) { - batch = new Batch.CreateWithTimestampsLT[](batchSize); - Batch.CreateWithTimestampsLT memory batchSingle = Batch.CreateWithTimestampsLT({ + batch = new BatchLockup.CreateWithTimestampsLT[](batchSize); + BatchLockup.CreateWithTimestampsLT memory batchSingle = BatchLockup.CreateWithTimestampsLT({ sender: params.sender, recipient: params.recipient, totalAmount: params.totalAmount, diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index 4a3dba1f..2dbc9c70 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -7,10 +7,10 @@ import { ud2x18 } from "@prb/math/src/UD2x18.sol"; import { UD60x18, ud } from "@prb/math/src/UD60x18.sol"; import { Broker, LockupDynamic, LockupLinear, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; -import { Batch, MerkleLockup, MerkleLT } from "src/types/DataTypes.sol"; +import { BatchLockup, MerkleLockup, MerkleLT } from "src/types/DataTypes.sol"; import { ArrayBuilder } from "./ArrayBuilder.sol"; -import { BatchBuilder } from "./BatchBuilder.sol"; +import { BatchLockupBuilder } from "./BatchLockupBuilder.sol"; import { Merkle } from "./Murky.sol"; import { MerkleBuilder } from "./MerkleBuilder.sol"; import { Users } from "./Types.sol"; @@ -376,63 +376,63 @@ contract Defaults is Merkle { } /*////////////////////////////////////////////////////////////////////////// - BATCH + BATCH-LOCKUP //////////////////////////////////////////////////////////////////////////*/ - /// @dev Returns a default-size batch of `Batch.CreateWithDurationsLD` parameters. - function batchCreateWithDurationsLD() public view returns (Batch.CreateWithDurationsLD[] memory batch) { - batch = BatchBuilder.fillBatch(createWithDurationsLD(), BATCH_SIZE); + /// @dev Returns a default-size batch of `BatchLockup.CreateWithDurationsLD` parameters. + function batchCreateWithDurationsLD() public view returns (BatchLockup.CreateWithDurationsLD[] memory batch) { + batch = BatchLockupBuilder.fillBatch(createWithDurationsLD(), BATCH_SIZE); } - /// @dev Returns a default-size batch of `Batch.CreateWithDurationsLL` parameters. - function batchCreateWithDurationsLL() public view returns (Batch.CreateWithDurationsLL[] memory batch) { - batch = BatchBuilder.fillBatch(createWithDurationsLL(), BATCH_SIZE); + /// @dev Returns a default-size batch of `BatchLockup.CreateWithDurationsLL` parameters. + function batchCreateWithDurationsLL() public view returns (BatchLockup.CreateWithDurationsLL[] memory batch) { + batch = BatchLockupBuilder.fillBatch(createWithDurationsLL(), BATCH_SIZE); } - /// @dev Returns a default-size batch of `Batch.CreateWithDurationsLT` parameters. - function batchCreateWithDurationsLT() public view returns (Batch.CreateWithDurationsLT[] memory batch) { - batch = BatchBuilder.fillBatch(createWithDurationsLT(), BATCH_SIZE); + /// @dev Returns a default-size batch of `BatchLockup.CreateWithDurationsLT` parameters. + function batchCreateWithDurationsLT() public view returns (BatchLockup.CreateWithDurationsLT[] memory batch) { + batch = BatchLockupBuilder.fillBatch(createWithDurationsLT(), BATCH_SIZE); } - /// @dev Returns a default-size batch of `Batch.CreateWithTimestampsLD` parameters. - function batchCreateWithTimestampsLD() public view returns (Batch.CreateWithTimestampsLD[] memory batch) { + /// @dev Returns a default-size batch of `BatchLockup.CreateWithTimestampsLD` parameters. + function batchCreateWithTimestampsLD() public view returns (BatchLockup.CreateWithTimestampsLD[] memory batch) { batch = batchCreateWithTimestampsLD(BATCH_SIZE); } - /// @dev Returns a batch of `Batch.CreateWithTimestampsLD` parameters. + /// @dev Returns a batch of `BatchLockup.CreateWithTimestampsLD` parameters. function batchCreateWithTimestampsLD(uint256 batchSize) public view - returns (Batch.CreateWithTimestampsLD[] memory batch) + returns (BatchLockup.CreateWithTimestampsLD[] memory batch) { - batch = BatchBuilder.fillBatch(createWithTimestampsLD(), batchSize); + batch = BatchLockupBuilder.fillBatch(createWithTimestampsLD(), batchSize); } - /// @dev Returns a default-size batch of `Batch.CreateWithTimestampsLL` parameters. - function batchCreateWithTimestampsLL() public view returns (Batch.CreateWithTimestampsLL[] memory batch) { + /// @dev Returns a default-size batch of `BatchLockup.CreateWithTimestampsLL` parameters. + function batchCreateWithTimestampsLL() public view returns (BatchLockup.CreateWithTimestampsLL[] memory batch) { batch = batchCreateWithTimestampsLL(BATCH_SIZE); } - /// @dev Returns a batch of `Batch.CreateWithTimestampsLL` parameters. + /// @dev Returns a batch of `BatchLockup.CreateWithTimestampsLL` parameters. function batchCreateWithTimestampsLL(uint256 batchSize) public view - returns (Batch.CreateWithTimestampsLL[] memory batch) + returns (BatchLockup.CreateWithTimestampsLL[] memory batch) { - batch = BatchBuilder.fillBatch(createWithTimestampsLL(), batchSize); + batch = BatchLockupBuilder.fillBatch(createWithTimestampsLL(), batchSize); } - /// @dev Returns a default-size batch of `Batch.CreateWithTimestampsLT` parameters. - function batchCreateWithTimestampsLT() public view returns (Batch.CreateWithTimestampsLT[] memory batch) { + /// @dev Returns a default-size batch of `BatchLockup.CreateWithTimestampsLT` parameters. + function batchCreateWithTimestampsLT() public view returns (BatchLockup.CreateWithTimestampsLT[] memory batch) { batch = batchCreateWithTimestampsLT(BATCH_SIZE); } - /// @dev Returns a batch of `Batch.CreateWithTimestampsLL` parameters. + /// @dev Returns a batch of `BatchLockup.CreateWithTimestampsLL` parameters. function batchCreateWithTimestampsLT(uint256 batchSize) public view - returns (Batch.CreateWithTimestampsLT[] memory batch) + returns (BatchLockup.CreateWithTimestampsLT[] memory batch) { - batch = BatchBuilder.fillBatch(createWithTimestampsLT(), batchSize); + batch = BatchLockupBuilder.fillBatch(createWithTimestampsLT(), batchSize); } } diff --git a/test/utils/DeployOptimized.sol b/test/utils/DeployOptimized.sol index 068a9ded..0b6c63e4 100644 --- a/test/utils/DeployOptimized.sol +++ b/test/utils/DeployOptimized.sol @@ -3,13 +3,13 @@ pragma solidity >=0.8.22 <0.9.0; import { StdCheats } from "forge-std/src/StdCheats.sol"; -import { ISablierV2Batch } from "../../src/interfaces/ISablierV2Batch.sol"; +import { ISablierV2BatchLockup } from "../../src/interfaces/ISablierV2BatchLockup.sol"; import { ISablierV2MerkleLockupFactory } from "../../src/interfaces/ISablierV2MerkleLockupFactory.sol"; abstract contract DeployOptimized is StdCheats { - /// @dev Deploys {SablierV2Batch} from an optimized source compiled with `--via-ir`. - function deployOptimizedBatch() internal returns (ISablierV2Batch) { - return ISablierV2Batch(deployCode("out-optimized/SablierV2Batch.sol/SablierV2Batch.json")); + /// @dev Deploys {SablierV2BatchLockup} from an optimized source compiled with `--via-ir`. + function deployOptimizedBatchLockup() internal returns (ISablierV2BatchLockup) { + return ISablierV2BatchLockup(deployCode("out-optimized/SablierV2BatchLockup.sol/SablierV2BatchLockup.json")); } /// @dev Deploys {SablierV2MerkleLockupFactory} from an optimized source compiled with `--via-ir`. @@ -19,11 +19,11 @@ abstract contract DeployOptimized is StdCheats { ); } - /// @notice Deploys all V2 Periphery contracts from a optimized source in the following order: + /// @notice Deploys all V2 Periphery contracts from an optimized source in the following order: /// - /// 1. {SablierV2Batch} + /// 1. {SablierV2BatchLockup} /// 2. {SablierV2MerkleLockupFactory} - function deployOptimizedPeriphery() internal returns (ISablierV2Batch, ISablierV2MerkleLockupFactory) { - return (deployOptimizedBatch(), deployOptimizedMerkleLockupFactory()); + function deployOptimizedPeriphery() internal returns (ISablierV2BatchLockup, ISablierV2MerkleLockupFactory) { + return (deployOptimizedBatchLockup(), deployOptimizedMerkleLockupFactory()); } } diff --git a/test/utils/Precompiles.t.sol b/test/utils/Precompiles.t.sol index c29a8f6a..88155421 100644 --- a/test/utils/Precompiles.t.sol +++ b/test/utils/Precompiles.t.sol @@ -2,7 +2,7 @@ pragma solidity >=0.8.22 <0.9.0; import { Precompiles } from "../../precompiles/Precompiles.sol"; -import { ISablierV2Batch } from "../../src/interfaces/ISablierV2Batch.sol"; +import { ISablierV2BatchLockup } from "../../src/interfaces/ISablierV2BatchLockup.sol"; import { ISablierV2MerkleLockupFactory } from "../../src/interfaces/ISablierV2MerkleLockupFactory.sol"; import { Base_Test } from "../Base.t.sol"; @@ -16,10 +16,10 @@ contract Precompiles_Test is Base_Test { } } - function test_DeployBatch() external onlyTestOptimizedProfile { - address actualBatch = address(precompiles.deployBatch()); - address expectedBatch = address(deployOptimizedBatch()); - assertEq(actualBatch.code, expectedBatch.code, "bytecodes mismatch"); + function test_DeployBatchLockup() external onlyTestOptimizedProfile { + address actualBatchLockup = address(precompiles.deployBatchLockup()); + address expectedBatchLockup = address(deployOptimizedBatchLockup()); + assertEq(actualBatchLockup.code, expectedBatchLockup.code, "bytecodes mismatch"); } function test_DeployMerkleLockupFactory() external onlyTestOptimizedProfile { @@ -29,13 +29,13 @@ contract Precompiles_Test is Base_Test { } function test_DeployPeriphery() external onlyTestOptimizedProfile { - (ISablierV2Batch actualBatch, ISablierV2MerkleLockupFactory actualMerkleLockupFactory) = + (ISablierV2BatchLockup actualBatchLockup, ISablierV2MerkleLockupFactory actualMerkleLockupFactory) = precompiles.deployPeriphery(); - (ISablierV2Batch expectedBatch, ISablierV2MerkleLockupFactory expectedMerkleLockupFactory) = + (ISablierV2BatchLockup expectedBatchLockup, ISablierV2MerkleLockupFactory expectedMerkleLockupFactory) = deployOptimizedPeriphery(); - assertEq(address(actualBatch).code, address(expectedBatch).code, "bytecodes mismatch"); + assertEq(address(actualBatchLockup).code, address(expectedBatchLockup).code, "bytecodes mismatch"); assertEq( address(actualMerkleLockupFactory).code, address(expectedMerkleLockupFactory).code, "bytecodes mismatch" ); From a57b2e1055f78e87d463b6c8349db37cb1af22a8 Mon Sep 17 00:00:00 2001 From: Paul Razvan Berg Date: Fri, 12 Apr 2024 15:35:28 +0300 Subject: [PATCH 40/61] refactor: constructor params (#326) * refactor: alphabetical constructor params * refactor: update precompiles test: update formatting in comments * test: dev comments --------- Co-authored-by: smol-ninja --- precompiles/Precompiles.sol | 2 +- src/SablierV2MerkleLockupFactory.sol | 16 +++---- src/types/DataTypes.sol | 18 ++++---- test/Base.t.sol | 20 ++++----- test/fork/merkle-lockup/MerkleLT.t.sol | 4 +- .../merkle-lockup/MerkleLockup.t.sol | 4 +- test/utils/BatchLockupBuilder.sol | 12 +++--- test/utils/Defaults.sol | 42 +++++++++---------- 8 files changed, 59 insertions(+), 59 deletions(-) diff --git a/precompiles/Precompiles.sol b/precompiles/Precompiles.sol index 5cb05410..0d938c30 100644 --- a/precompiles/Precompiles.sol +++ b/precompiles/Precompiles.sol @@ -19,7 +19,7 @@ contract Precompiles { bytes public constant BYTECODE_BATCH_LOCKUP = hex"6080806040523461001657611f10908161001c8239f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c90816337266dd31461125b5750806349a32c4014610eb7578063606ef87514610b6b5780639e743f29146107fc578063a514f83e146104295763f7ca34eb1461006157600080fd5b346103555761006f366115d6565b9283156103ff5760009060005b8581106103cb57506001600160a01b036100999116918483611b81565b6100a284611858565b9260005b8581106100bf57604051806100bb8782611664565b0390f35b6100ca818786611b1e565b6100d3906118a7565b906100df818887611b1e565b6020016100eb906118a7565b85886100f8848284611b1e565b60400161010490611725565b61010f858385611b1e565b60600161011b906118bb565b610126868486611b1e565b608001610132906118bb565b9061013e878587611b1e565b60a00161014a90611b5e565b9287610157818789611b1e565b60c0810161016491611a2a565b96610170929198611b1e565b60e00196604051996101818b6117aa565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c086015236906101e592611a7e565b60e0840152366101f4916119cc565b610100830152604051917f31df3d48000000000000000000000000000000000000000000000000000000008352600483016020905280516001600160a01b0316602484015260208101516001600160a01b0316604484015260408101516fffffffffffffffffffffffffffffffff16606484015260608101516001600160a01b031660848401526080810151151560a484015260a0810151151560c484015260c081015164ffffffffff1660e48401528260e082015161010482016101409052805180610164840152610184830191602001906000905b80821061036e5750505081906103006101006020950151610124840190602080916001600160a01b0381511684520151910152565b03816000885af180156103625760009061032a575b600192506103238288611a05565b52016100a6565b506020823d60201161035a575b81610344602093836117ff565b810103126103555760019151610315565b600080fd5b3d9150610337565b6040513d6000823e3d90fd5b919493509160206060826103bc600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b019501920186939492916102cb565b916001906fffffffffffffffffffffffffffffffff6103f660406103f0878b8a611b1e565b01611725565b1601920161007c565b60046040517ff8bf106c000000000000000000000000000000000000000000000000000000008152fd5b346103555760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610355576104606116a0565b6104686115c0565b9060443567ffffffffffffffff8082116103555736602383011215610355578160040135116103555736602461014083600401350283010111610355578060040135156103ff576000805b826004013582106107c7576104d39150846001600160a01b038516611b81565b6104e08160040135611858565b9160005b826004013581106104fd57604051806100bb8682611664565b61051761051282856004013560248701611b70565b6118a7565b9083610536602061053084846004013560248601611b70565b016118a7565b61054d60406103f085856004013560248701611b70565b9361056b606061056586866004013560248801611b70565b016118bb565b946080956fffffffffffffffffffffffffffffffff6105968861056589896004013560248b01611b70565b926001600160a01b036101006105c78a6105b88160048d013560248e01611b70565b9a602481600401359101611b70565b019681604051976105d789611742565b1687521660208601521660408401526001600160a01b038b166060840152151586830152151560a082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6084360301126103555761075c9261067f60e093604051610644816117c7565b61065060a08501611939565b815261066f8660c095610664878201611939565b602085015201611939565b60408201528385015236906119cc565b83830152604051957f53b157270000000000000000000000000000000000000000000000000000000087526001600160a01b0383511660048801526001600160a01b0360208401511660248801526fffffffffffffffffffffffffffffffff60408401511660448801526001600160a01b0360608401511660648801528201511515608487015260a0820151151560a4870152810151604064ffffffffff918281511660c48901528260208201511660e48901520151166101048601520151610124840190602080916001600160a01b0381511684520151910152565b6020826101648160006001600160a01b0388165af1801561036257600090610794575b6001925061078d8287611a05565b52016104e4565b506020823d6020116107bf575b816107ae602093836117ff565b81010312610355576001915161077f565b3d91506107a1565b6001906fffffffffffffffffffffffffffffffff6107f260406103f086886004013560248a01611b70565b16019101906104b3565b346103555761080a366115d6565b9283156103ff5760009060005b858110610b3d57506001600160a01b036108349116918483611b81565b61083d84611858565b9260005b85811061085657604051806100bb8782611664565b610861818786611b1e565b61086a906118a7565b90610876818887611b1e565b602001610882906118a7565b858861088f848284611b1e565b60400161089b90611725565b6108a6858385611b1e565b6060016108b2906118bb565b6108bd868486611b1e565b6080016108c9906118bb565b906108d5878587611b1e565b60a0016108e190611b5e565b92876108ee818789611b1e565b60c081016108fb916118c8565b96610907929198611b1e565b60e00196604051996109188b6117aa565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c0860152369061097c9261194b565b60e08401523661098b916119cc565b610100830152604051917f32fbe22b000000000000000000000000000000000000000000000000000000008352600483016020905280516001600160a01b0316602484015260208101516001600160a01b0316604484015260408101516fffffffffffffffffffffffffffffffff16606484015260608101516001600160a01b031660848401526080810151151560a484015260a0810151151560c484015260c081015164ffffffffff1660e48401528260e082015161010482016101409052805180610164840152610184830191602001906000905b808210610af4575050508190610a976101006020950151610124840190602080916001600160a01b0381511684520151910152565b03816000885af1801561036257600090610ac1575b60019250610aba8288611a05565b5201610841565b506020823d602011610aec575b81610adb602093836117ff565b810103126103555760019151610aac565b3d9150610ace565b91949350916020604082610b2e600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291610a62565b916001906fffffffffffffffffffffffffffffffff610b6260406103f0878b8a611b1e565b16019201610817565b3461035557610b79366115d6565b9283156103ff5760009060005b858110610e8957506001600160a01b03610ba39116918483611b81565b610bac84611858565b9260005b858110610bc557604051806100bb8782611664565b610bd08187866116b6565b610bd9906118a7565b90610be58188876116b6565b602001610bf1906118a7565b8782610bfe81838a6116b6565b604001610c0a90611725565b610c1582848b6116b6565b606001610c21906118bb565b610c2c83858c6116b6565b608001610c38906118bb565b91610c4484868d6116b6565b60a08101610c5191611a2a565b94610c5d91968d6116b6565b60c0019560405198610c6e8a611742565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a08601523690610cc692611a7e565b60c084015236610cd5916119cc565b60e0830152604051917f54c02292000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001906000905b808210610e2c575050508190610dcf60e06020950151610104840190602080916001600160a01b0381511684520151910152565b03816000885af1801561036257600090610df9575b60019250610df28288611a05565b5201610bb0565b506020823d602011610e24575b81610e13602093836117ff565b810103126103555760019151610de4565b3d9150610e06565b91949350916020606082610e7a600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b01950192018693949291610d9b565b916001906fffffffffffffffffffffffffffffffff610eae60406103f0878b8a6116b6565b16019201610b86565b346103555760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035557610eee6116a0565b610ef66115c0565b9060443567ffffffffffffffff8082116103555736602383011215610355578160040135116103555736602461012083600401350283010111610355578060040135156103ff576000805b8260040135821061122657610f619150846001600160a01b038516611b81565b610f6e8160040135611858565b9160005b82600401358110610f8b57604051806100bb8682611664565b610fa061051282856004013560248701611a19565b9083610fb9602061053084846004013560248601611a19565b610fd060406103f085856004013560248701611a19565b93610fe8606061056586866004013560248801611a19565b946080956fffffffffffffffffffffffffffffffff6110138861056589896004013560248b01611a19565b926001600160a01b0360e06110438a6110348160048d013560248e01611a19565b9a602481600401359101611a19565b0196816040519761105389611742565b1687521660208601521660408401526001600160a01b038b166060840152151586830152151560a082015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff608436030112610355576111bb926110eb60e0936040516110c08161178e565b6110cc60a08501611939565b81526110db60c0809501611939565b60208201528385015236906119cc565b83830152604051957fab167ccc0000000000000000000000000000000000000000000000000000000087526001600160a01b0383511660048801526001600160a01b0360208401511660248801526fffffffffffffffffffffffffffffffff60408401511660448801526001600160a01b0360608401511660648801528201511515608487015260a0820151151560a4870152810151602064ffffffffff918281511660c489015201511660e48601520151610104840190602080916001600160a01b0381511684520151910152565b6020826101448160006001600160a01b0388165af18015610362576000906111f3575b600192506111ec8287611a05565b5201610f72565b506020823d60201161121e575b8161120d602093836117ff565b8101031261035557600191516111de565b3d9150611200565b6001906fffffffffffffffffffffffffffffffff61125160406103f086886004013560248a01611a19565b1601910190610f41565b3461035557611269366115d6565b93849391929315611598575060009060005b85811061156a57506001600160a01b036112989116918483611b81565b6112a184611858565b9260005b8581106112ba57604051806100bb8782611664565b6112c58187866116b6565b6112ce906118a7565b906112da8188876116b6565b6020016112e6906118a7565b87826112f381838a6116b6565b6040016112ff90611725565b61130a82848b6116b6565b606001611316906118bb565b61132183858c6116b6565b60800161132d906118bb565b9161133984868d6116b6565b60a08101611346916118c8565b9461135291968d6116b6565b60c00195604051986113638a611742565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a086015236906113bb9261194b565b60c0840152366113ca916119cc565b60e0830152604051917f897f362b000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001906000905b8082106115215750505081906114c460e06020950151610104840190602080916001600160a01b0381511684520151910152565b03816000885af18015610362576000906114ee575b600192506114e78288611a05565b52016112a5565b506020823d602011611519575b81611508602093836117ff565b8101031261035557600191516114d9565b3d91506114fb565b9194935091602060408261155b600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291611490565b916001906fffffffffffffffffffffffffffffffff61158f60406103f0878b8a6116b6565b1601920161127b565b807ff8bf106c0000000000000000000000000000000000000000000000000000000060049252fd5b602435906001600160a01b038216820361035557565b9060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc830112610355576001600160a01b0391600435838116810361035557926024359081168103610355579160443567ffffffffffffffff9283821161035557806023830112156103555781600401359384116103555760248460051b83010111610355576024019190565b602090602060408183019282815285518094520193019160005b82811061168c575050505090565b83518552938101939281019260010161167e565b600435906001600160a01b038216820361035557565b91908110156116f65760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0181360301821215610355570190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b356fffffffffffffffffffffffffffffffff811681036103555790565b610100810190811067ffffffffffffffff82111761175f57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff82111761175f57604052565b610120810190811067ffffffffffffffff82111761175f57604052565b6060810190811067ffffffffffffffff82111761175f57604052565b6080810190811067ffffffffffffffff82111761175f57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761175f57604052565b67ffffffffffffffff811161175f5760051b60200190565b9061186282611840565b61186f60405191826117ff565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061189d8294611840565b0190602036910137565b356001600160a01b03811681036103555790565b3580151581036103555790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610355570180359067ffffffffffffffff821161035557602001918160061b3603831361035557565b35906fffffffffffffffffffffffffffffffff8216820361035557565b359064ffffffffff8216820361035557565b92919261195782611840565b60409261196760405192836117ff565b819581835260208093019160061b84019381851161035557915b84831061199057505050505050565b85838303126103555783869182516119a78161178e565b6119b08661191c565b81526119bd838701611939565b83820152815201920191611981565b9190826040910312610355576040516119e48161178e565b809280356001600160a01b0381168103610355578252602090810135910152565b80518210156116f65760209160051b010190565b91908110156116f657610120020190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610355570180359067ffffffffffffffff82116103555760200191606082023603831361035557565b929192611a8a82611840565b604094611a9a60405192836117ff565b8195848352602080930191606080960285019481861161035557925b858410611ac65750505050505050565b868483031261035557825190611adb826117c7565b611ae48561191c565b8252858501359067ffffffffffffffff8216820361035557828792838b950152611b0f868801611939565b86820152815201930192611ab6565b91908110156116f65760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee181360301821215610355570190565b3564ffffffffff811681036103555790565b91908110156116f657610140020190565b90604080516020907f23b872dd000000000000000000000000000000000000000000000000000000008282015233602482015260449030828201528660648201526064815260a081019080821067ffffffffffffffff83111761175f57611bea91855285611d86565b6001600160a01b0394858516958451917fdd62ed3e0000000000000000000000000000000000000000000000000000000083523060048401521690816024820152838184818a5afa908115611d6357908891600091611d32575b5010611c54575b50505050505050565b8351956000808589017f095ea7b3000000000000000000000000000000000000000000000000000000009a8b82528560248c0152868b0152858a52611c988a6117e3565b89519082855af190611ca8611e12565b82611cff575b5081611cf4575b50611c4b57611ce896611ce394519384015260248301526000818301528152611cdd816117e3565b82611d86565b611d86565b38808080808080611c4b565b90503b151538611cb5565b809192505190858215928315611d1a575b5050509038611cae565b611d2a9350820181019101611d6e565b388581611d10565b809250858092503d8311611d5c575b611d4b81836117ff565b810103126103555787905138611c44565b503d611d41565b85513d6000823e3d90fd5b90816020910312610355575180151581036103555790565b6000806001600160a01b03611db093169360208151910182865af1611da9611e12565b9083611e70565b8051908115159182611df7575b5050611dc65750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b611e0a9250602080918301019101611d6e565b153880611dbd565b3d15611e6b573d9067ffffffffffffffff821161175f5760405191611e5f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846117ff565b82523d6000602084013e565b606090565b90611eaf5750805115611e8557805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580611efa575b611ec0575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15611eb856fea164736f6c6343000817000a"; bytes public constant BYTECODE_MERKLE_LOCKUP_FACTORY = - hex"6080806040523461001657614200908161001c8239f35b600080fdfe608060405260043610156200001357600080fd5b60003560e01c806389a40c0814620005245763a729a319146200003557600080fd5b346200051f5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126200051f5760043567ffffffffffffffff81116200051f576200008990369060040162000930565b73ffffffffffffffffffffffffffffffffffffffff6024351680602435036200051f576044359167ffffffffffffffff83116200051f57366023840112156200051f57826004013567ffffffffffffffff81116200046b5760405193620000f760208360051b01866200088f565b8185526024602086019260061b820101903682116200051f57602401915b818310620004cb575050506000906000906000925b8551841015620001b65767ffffffffffffffff806200014a868962000b3c565b51511691160167ffffffffffffffff811162000187576001909264ffffffffff602062000178878a62000b3c565b5101511601930192916200012a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b67ffffffffffffffff9195935016670de0b6b3a764000081036200049a57506200020a6200036860a38651620002ae60208901516200021960408b0151604051978891602080840152604083019062000a44565b03601f1981018852876200088f565b6200025560608b01516200024f60206040518362000241829551809285808601910162000a1f565b81010380845201826200088f565b62000a6b565b90888b7fffffffffff000000000000000000000000000000000000000000000000000000608082015160a083015190620002bd60e060c08601511515950151151595604051998a91602080840152604083019062000b80565b03601f1981018a52896200088f565b604862000311604051809e819c7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009a8b809260601b16602085015260601b1660348301526020815194859301910162000a1f565b8901966048880152606887015260d81b16608885015260f81b608d84015260f81b608e83015260243560601b16608f82015262000358825180936020878501910162000a1f565b010360838101845201826200088f565b60208151910120604051611e158082019082821067ffffffffffffffff8311176200046b578291620003c391620023df843960608152620003ad606082018a62000aad565b9088602082015260408183039101528662000b80565b03906000f580156200045f576020947fee52c9be756b04ff87545717a099143dd3f3a3df3d30ed320884849c5769c22e926200043e73ffffffffffffffffffffffffffffffffffffffff6200042b941696879660405195869560c0875260c087019062000aad565b918a860152848203604086015262000b80565b906060830152606435608083015260843560a08301520390a2604051908152f35b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b602490604051907f1b05170a0000000000000000000000000000000000000000000000000000000082526004820152fd5b6040833603126200051f5760405190620004e58262000872565b83359067ffffffffffffffff821682036200051f5782602092604094526200050f8387016200090f565b8382015281520192019162000115565b600080fd5b346200051f5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126200051f5767ffffffffffffffff6004358181116200051f576200057990369060040162000930565b73ffffffffffffffffffffffffffffffffffffffff8060243516602435036200051f5760407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126200051f5760405190620005d68262000872565b64ffffffffff9360443585811681036200051f57835260643594851685036200051f5760209485840152835162000629868601516200063860408801516040519384918b80840152604083019062000a44565b03601f1981018452836200088f565b6200065f60608801516200024f8a6040518362000241829551809285808601910162000a1f565b8860808901519160a08a01519360c08b0151151560e08c01511515906200069f6040519586018d6020908164ffffffffff91828151168552015116910152565b6040855260608501978589108b8a11176200046b576200077d978f977fffffffffff000000000000000000000000000000000000000000000000000000926200073460a39a8d60405260808b019e8f997fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009a8b809360601b16905260601b1660948c0152825192839160a88d01910162000a1f565b88019660a888015260c887015260d81b1660e885015260f81b60ed84015260f81b60ee83015260243560601b1660ef82015281519062000358828d610103840190860162000a1f565b519020604051909161180c90818301908111838210176200046b57829162000bd3833960808152620007de6040620007b9608084018a62000aad565b9287602435168b82015201876020908164ffffffffff91828151168552015116910152565b03906000f580156200045f576200085793827fb905fb2b79e37b53ad42b4bbb04afcfc7c13d2771efe396ad8c687a844662e959362000830931695869560405194859460c0865260c086019062000aad565b92602435168985015260408401906020908164ffffffffff91828151168552015116910152565b608435608083015260a43560a08301520390a2604051908152f35b6040810190811067ffffffffffffffff8211176200046b57604052565b90601f601f19910116810190811067ffffffffffffffff8211176200046b57604052565b81601f820112156200051f5780359067ffffffffffffffff82116200046b5760405192620008ec6020601f19601f86011601856200088f565b828452602083830101116200051f57816000926020809301838601378301015290565b359064ffffffffff821682036200051f57565b359081151582036200051f57565b9190916101009081818503126200051f5760405191820167ffffffffffffffff90838110828211176200046b576040528294823573ffffffffffffffffffffffffffffffffffffffff9081811681036200051f578552602084013590811681036200051f57602085015260408301358281116200051f5781620009b5918501620008b3565b604085015260608301359182116200051f5782620009de60e0949262000a1a94869401620008b3565b606086015260808101356080860152620009fb60a082016200090f565b60a086015262000a0e60c0820162000922565b60c08601520162000922565b910152565b60005b83811062000a335750506000910152565b818101518382015260200162000a22565b90601f19601f60209362000a648151809281875287808801910162000a1f565b0116010190565b60208151910151906020811062000a80575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b9060e08062000b0a62000af761010073ffffffffffffffffffffffffffffffffffffffff808851168752602088015116602087015260408701519080604088015286019062000a44565b6060860151858203606087015262000a44565b936080810151608085015264ffffffffff60a08201511660a085015260c0810151151560c08501520151151591015290565b805182101562000b515760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90815180825260208080930193019160005b82811062000ba1575050505090565b8351805167ffffffffffffffff16865282015164ffffffffff16858301526040909401939281019260010162000b9256fe6101608060405234620005b4576200180c8038038091620000218285620005d5565b83398101908082039160808312620005b45781516001600160401b038111620005b45782019261010084830312620005b4576040519361010085016001600160401b038111868210176200059e5760405280516001600160a01b0381168103620005b45785526020810151926001600160a01b0384168403620005b4576020860193845260408201516001600160401b038111620005b45781620000c79184016200063a565b60408701908152606083015190916001600160401b038211620005b457620000f19184016200063a565b6060870152608082015195608081019687526200011160a0840162000687565b9660a082019788526200013c60e06200012d60c087016200069a565b9560c08501968752016200069a565b60e083019081526020880151959093906001600160a01b0387168703620005b457604090603f190112620005b45760408051989089016001600160401b0381118a8210176200059e57620001a7916060916040526200019e6040820162000687565b8b520162000687565b9460208901958652606084015151602081116200057f57508351600080546001600160a01b0319166001600160a01b0392831617815598511660805251151560a052975164ffffffffff90811660c052975180516001600160401b0381116200056b5760019182548381811c9116801562000560575b60208210146200054c57601f8111620004ff575b50602090601f83116001146200049357606095949392918a918362000487575b5050600019600383901b1c191690821b1790555b5160e052015160405162000299602082816200028b818301968781519384920162000615565b8101038084520182620005d5565b519051906020811062000474575b50610100525115159461012095865261014094838652511669ffffffffff0000000000600354925160281b169160018060501b031916171760035560018060a01b03608051166040519160208301848063095ea7b360e01b9283815260018060a01b03851660248801526000196044880152604487526200032887620005b9565b86519082875af162000339620006a8565b8162000433575b508062000428575b15620003dc575b60405161104290878983620007ca8439608051838181610470015281816107440152610c36015260a05183818161076b0152610b5c015260c05183818161015d01528181610aa801528181610d8f0152610f60015260e0518381816102e2015261066201526101005183610e250152518281816107930152610b1f0152518181816101ae01526109060152f35b6200041d94620004179260405192602084015260018060a01b031660248301526044820152604481526200041081620005b9565b82620006dd565b620006dd565b38808080806200034f565b50823b151562000348565b80518015925082156200044a575b50503862000340565b8192509060209181010312620004705760206200046891016200069a565b388062000441565b8580fd5b6000199060200360031b1b1638620002a7565b01519050388062000251565b838a5260208a209190601f1984168b5b818110620004e857509185949291836060999897959310620004ce575b505050811b01905562000265565b015160001960f88460031b161c19169055388080620004c0565b8284015185559386019360209384019301620004a3565b838a5260208a20601f840160051c8101916020851062000541575b601f0160051c019084905b8281106200053557505062000231565b8b815501849062000525565b90915081906200051a565b634e487b7160e01b8a52602260045260248afd5b90607f16906200021d565b634e487b7160e01b88526041600452602488fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b634e487b7160e01b600052604160045260246000fd5b600080fd5b608081019081106001600160401b038211176200059e57604052565b601f909101601f19168101906001600160401b038211908210176200059e57604052565b6001600160401b0381116200059e57601f01601f191660200190565b60005b838110620006295750506000910152565b818101518382015260200162000618565b81601f82011215620005b45780516200065381620005f9565b92620006636040519485620005d5565b81845260208284010111620005b45762000684916020808501910162000615565b90565b519064ffffffffff82168203620005b457565b51908115158203620005b457565b3d15620006d8573d90620006bc82620005f9565b91620006cc6040519384620005d5565b82523d6000602084013e565b606090565b6000806200070a9260018060a01b03169360208151910182865af162000702620006a8565b908362000761565b80519081151591826200073a575b5050620007225750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620005b45760206200075891016200069a565b15388062000718565b906200078a57508051156200077857805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580620007bf575b6200079d575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156200079456fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde0314610e0e575080631686c90914610b8157806316c3549d14610b445780631bfd681414610b075780633bfe03a814610ad85780633f31ae3f146104945780634800d97f1461044357806349fc73dd1461030557806351e75e8b146102ca57806375829def146101ed57806390e64d13146101d25780639e93e57714610181578063bb4b57341461013f578063ce516507146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760206003193601126100f857602061013560043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f8576020610135610f58565b346100f85760206003193601126100f857610206610ebc565b60005473ffffffffffffffffffffffffffffffffffffffff8082169233840361027d577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c9060018316928315610439575b602093848410811461040a578386529081156103cc5750600114610371575b61036d8461036181880382610f17565b60405191829182610e56565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8284106103b9575050508161036d936103619282010193610351565b805485850187015292850192810161039d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103618161036d610351565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691610332565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f857604435906fffffffffffffffffffffffffffffffff821682036100f85767ffffffffffffffff606435116100f8573660236064350112156100f85767ffffffffffffffff60643560040135116100f8573660246064356004013560051b6064350101116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff831660408201526fffffffffffffffffffffffffffffffff841660608201526060815261057a81610efb565b519020604051602081019182526020815261059481610edf565b519020916105a0610f58565b610a7a576105c960043560ff6001918060081c6000526002602052161b60406000205416151590565b610a4857604051926105e760206064356004013560051b0185610f17565b60643560048101358552602401602085015b60246064356004013560051b60643501018210610a38575050906000915b845183101561065e5760208360051b860101519081811060001461064b57600052602052600160406000205b920191610617565b9060005260205260016040600020610643565b83907f000000000000000000000000000000000000000000000000000000000000000003610a0e5760043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff60005416906040516106cd81610edf565b600081526000602082015260405192610100840184811067ffffffffffffffff8211176109df57604052835273ffffffffffffffffffffffffffffffffffffffff821660208401526fffffffffffffffffffffffffffffffff8416604084015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608401527f0000000000000000000000000000000000000000000000000000000000000000151560808401527f0000000000000000000000000000000000000000000000000000000000000000151560a08401526040516107c581610edf565b64ffffffffff600354818116835260281c16602082015260c084015260e0830152602060e0604051937fab167ccc00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff815116600486015273ffffffffffffffffffffffffffffffffffffffff838201511660248601526fffffffffffffffffffffffffffffffff604082015116604486015273ffffffffffffffffffffffffffffffffffffffff606082015116606486015260808101511515608486015260a0810151151560a486015264ffffffffff8360c08301518281511660c489015201511660e4860152015173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015260208261014481600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af19182156109d35760009261099e575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d6020116109cb575b816109b960209383610f17565b810103126100f8579051906020610937565b3d91506109ac565b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b81358152602091820191016105f9565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f857604060035464ffffffffff825191818116835260281c166020820152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857610b9a610ebc565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff8060005416338103610dbf5750610be1610f58565b15610d61576040519060209160008083858401977fa9059cbb000000000000000000000000000000000000000000000000000000008952169687602485015286604485015260448452610c3384610efb565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610d55573d67ffffffffffffffff81116109df57610cbc9160405191610cac877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610f17565b82523d60008784013e5b83610f95565b8051848115159182610d34575b50509050610d035750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488610cc9565b610cbc90606090610cb6565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f85761036d907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261036181610edf565b60208082528251818301819052939260005b858110610ea8575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201610e68565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff8211176109df57604052565b6080810190811067ffffffffffffffff8211176109df57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176109df57604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081610f8d575090565b905042101590565b90610fd45750805115610faa57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b8151158061102c575b610fe5575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15610fdd56fea164736f6c6343000817000a6101608060405234620006e95762001e1590813803809262000022828462000735565b82396060818381010312620006e95780516001600160401b038111620006e9578101916101009283818385010312620006e957604051908482016001600160401b03811183821017620007035760405280516001600160a01b0381168103620006e957825260208101516001600160a01b0381168103620006e957602083015260408101516001600160401b038111620006e957620000c7908486019083016200079a565b604083015260608101516001600160401b038111620006e9576200013491620000f860e0928688019083016200079a565b6060850152608081015160808501526200011560a08201620007e7565b60a08501526200012860c08201620007fa565b60c085015201620007fa565b60e08201526020830151916001600160a01b0383168303620006e9576040840151906001600160401b038211620006e957808501601f838701011215620006e95784820151906001600160401b0382116200070357604051956200019f60208460051b018862000735565b828752602087019382820160208560061b838501010111620006e95793602085830101945b60208560061b828501010186106200068357505050505050606081015151602081116200066457508051600080546001600160a01b0319166001600160a01b03928316178155602083015190911660805260c080830151151560a090815283015164ffffffffff16905260408201518051919290916001600160401b038111620006505760019283548481811c9116801562000645575b60208210146200063157601f8111620005e4575b50602090601f83116001146200057a5760e09392918691836200056e575b5050600019600383901b1c191690841b1783555b608081015182526060810151604051620002db60208281620002cd818301968781519384920162000775565b810103808452018262000735565b51905190602081106200055b575b5087520151151593610120948552610140938452805190835b82811062000499575050505060018060a01b036080511660018060a01b03835116906040519160208301848063095ea7b360e01b92838152846024880152600019604488015260448752620003578762000719565b86519082875af16200036862000808565b8162000458575b50806200044d575b1562000409575b6040516114eb9087898b846200092a85396080518481816104e2015281816108550152610f1c015260a05184818161087c0152610e42015260c05184818161022001528181610dbd015281816110750152611246015260e05184818161035401526106ba0152518361110b0152518281816108a40152610e0501525181818161012a0152610a0e0152f35b62000442946200043c926040519260208401526024830152604482015260448152620004358162000719565b826200083d565b6200083d565b38808080806200037e565b50823b151562000377565b80518015925082156200046f575b5050386200036f565b8192509060209181010312620004955760206200048d9101620007fa565b388062000466565b8580fd5b8151811015620005475760208160051b8301015160038054906801000000000000000082101562000533578682018082558210156200051f578752602080882083519201805493909101516cffffffffff000000000000000060409190911b166001600160401b039092166001600160681b031990931692909217179055830162000302565b634e487b7160e01b88526032600452602488fd5b634e487b7160e01b88526041600452602488fd5b634e487b7160e01b85526032600452602485fd5b6000199060200360031b1b1638620002e9565b0151905038806200028d565b92918491601f198216908388526020882091885b818110620005cb5750958360e09710620005b1575b505050811b018355620002a1565b015160001960f88460031b161c19169055388080620005a3565b828801518455889590930192602092830192016200058e565b84865260208620601f840160051c8101916020851062000626575b601f0160051c019085905b8281106200061a5750506200026f565b8781550185906200060a565b9091508190620005ff565b634e487b7160e01b86526022600452602486fd5b90607f16906200025b565b634e487b7160e01b84526041600452602484fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b6040868585010312620006e957604080519081016001600160401b03811182821017620006ee5760405286516001600160401b0381168103620006e95760209382859260409452620006d7838b01620007e7565b838201528152019601959150620001c4565b600080fd5b60246000634e487b7160e01b81526041600452fd5b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b038211176200070357604052565b601f909101601f19168101906001600160401b038211908210176200070357604052565b6001600160401b0381116200070357601f01601f191660200190565b60005b838110620007895750506000910152565b818101518382015260200162000778565b81601f82011215620006e9578051620007b38162000759565b92620007c3604051948562000735565b81845260208284010111620006e957620007e4916020808501910162000775565b90565b519064ffffffffff82168203620006e957565b51908115158203620006e957565b3d1562000838573d906200081c8262000759565b916200082c604051938462000735565b82523d6000602084013e565b606090565b6000806200086a9260018060a01b03169360208151910182865af16200086262000808565b9083620008c1565b80519081151591826200089a575b5050620008825750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620006e9576020620008b89101620007fa565b15388062000878565b90620008ea5750805115620008d857805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806200091f575b620008fd575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15620008f456fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde03146110f4575080631686c90914610e6757806316c3549d14610e2a5780631bfd681414610ded5780633f31ae3f146105065780634800d97f146104b557806349fc73dd1461037757806351e75e8b1461033c57806375829def1461025f57806390e64d1314610244578063bb4b573414610202578063bf4ed03f14610190578063ce5165071461014e578063da792468146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760206003193601126100f857602061018660043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f8576101a9611293565b6040516020918282018383528151809152836040840192019360005b8281106101d25784840385f35b8551805167ffffffffffffffff16855282015164ffffffffff1684830152948101946040909301926001016101c5565b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f857602061018661123e565b346100f85760206003193601126100f8576102786111a2565b60005473ffffffffffffffffffffffffffffffffffffffff808216923384036102ef577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c90600183169283156104ab575b602093848410811461047c5783865290811561043e57506001146103e3575b6103df846103d3818803826111fd565b6040519182918261113c565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b82841061042b57505050816103df936103d392820101936103c3565b805485850187015292850192810161040f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103d3816103df6103c3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f16916103a4565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f8576fffffffffffffffffffffffffffffffff60443516604435036100f85767ffffffffffffffff606435116100f8573660236064350112156100f857606435600401359067ffffffffffffffff82116100f85760248260051b60643501013681116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff841660408201526fffffffffffffffffffffffffffffffff604435166060820152606081526105eb816111e1565b5190206040516020810191825260208152610605816111c5565b5190209061061161123e565b610d8f5761063a60043560ff6001918060081c6000526002602052161b60406000205416151590565b610d5d576106478461127b565b9361065560405195866111fd565b8452606435602401602085015b828210610d4d575050506000905b83518210156106b657610683828561132e565b5190818110156106a357600052602052600160406000205b910190610670565b906000526020526001604060002061069b565b90507f000000000000000000000000000000000000000000000000000000000000000003610d23576106e6611293565b90600082516106f48161127b565b9361070260405195866111fd565b8185527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061072f8361127b565b0160005b818110610cfe5750506000905b828210610bd95750506fffffffffffffffffffffffffffffffff82166fffffffffffffffffffffffffffffffff604435168111610baa576fffffffffffffffffffffffffffffffff6044351611610b56575b505060043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff6000541691604051906107dc826111c5565b600082526000602083015260405193610100850185811067ffffffffffffffff821117610b2757604052845273ffffffffffffffffffffffffffffffffffffffff831660208501526fffffffffffffffffffffffffffffffff60443516604085015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608501527f0000000000000000000000000000000000000000000000000000000000000000151560808501527f0000000000000000000000000000000000000000000000000000000000000000151560a085015260c084015260e0830152604051917f897f362b0000000000000000000000000000000000000000000000000000000083526020600484015282610144810173ffffffffffffffffffffffffffffffffffffffff835116602483015273ffffffffffffffffffffffffffffffffffffffff60208401511660448301526fffffffffffffffffffffffffffffffff604084015116606483015273ffffffffffffffffffffffffffffffffffffffff60608401511660848301526080830151151560a483015260a0830151151560c483015260c08301519061012060e484015281518091526020610164840192019060005b818110610ae9575050508190602060e08195015173ffffffffffffffffffffffffffffffffffffffff81511661010485015201516101248301520381600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1918215610add57600092610aa8575b60208380847f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff81519360043585526fffffffffffffffffffffffffffffffff60443516888601521692a3604051908152f35b91506020823d602011610ad5575b81610ac3602093836111fd565b810103126100f8576020915191610a3f565b3d9150610ab6565b6040513d6000823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff1681860152889550604090940193909201916001016109b9565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6fffffffffffffffffffffffffffffffff610b947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8293018661132e565b5192604435031681835116011690528280610792565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b9092610c0e67ffffffffffffffff610bf1868561132e565b5151166fffffffffffffffffffffffffffffffff60443516611371565b6fffffffffffffffffffffffffffffffff8111610ccd576fffffffffffffffffffffffffffffffff8091169164ffffffffff6020610c4c888761132e565b5101511660405190610c5d826111c5565b8482526020820152610c6f878a61132e565b52610c7a868961132e565b5016016fffffffffffffffffffffffffffffffff8111610c9e579260010190610740565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b602490604051907f4916adce0000000000000000000000000000000000000000000000000000000082526004820152fd5b602090604051610d0d816111c5565b6000815260008382015282828a01015201610733565b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b8135815260209182019101610662565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857610e806111a2565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff80600054163381036110a55750610ec761123e565b15611047576040519060209160008083858401977fa9059cbb000000000000000000000000000000000000000000000000000000008952169687602485015286604485015260448452610f19846111e1565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d1561103b573d67ffffffffffffffff8111610b2757610fa29160405191610f92877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846111fd565b82523d60008784013e5b8361143e565b805184811515918261101a575b50509050610fe95750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488610faf565b610fa290606090610f9c565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f8576103df907f00000000000000000000000000000000000000000000000000000000000000006020820152602081526103d3816111c5565b60208082528251818301819052939260005b85811061118e575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b81810183015184820160400152820161114e565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff821117610b2757604052565b6080810190811067ffffffffffffffff821117610b2757604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610b2757604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081611273575090565b905042101590565b67ffffffffffffffff8111610b275760051b60200190565b600354906112a08261127b565b9160406112b060405194856111fd565b8184528360208091019160036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b906000935b8585106112f457505050505050565b60018481928451611304816111c5565b64ffffffffff875467ffffffffffffffff81168352871c16838201528152019301940193916112e5565b80518210156113425760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8382098382029182808310920391808303921461142d57670de0b6b3a764000090818310156113f657947faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac1066994950990828211900360ee1b910360121c170290565b60449086604051917f5173648d00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5050670de0b6b3a764000090049150565b9061147d575080511561145357805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b815115806114d5575b61148e575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b1561148656fea164736f6c6343000817000aa164736f6c6343000817000a"; + hex"6080806040523461001657614394908161001c8239f35b600080fdfe604060805260c06080515260043610156200001957600080fd5b6000803560e01c80631e32387614620004705763769bed20146200003c57600080fd5b346200046d5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126200046d5767ffffffffffffffff60043581811162000465576200009190369060040162000aa0565b9173ffffffffffffffffffffffffffffffffffffffff908160243516602435036200046d576080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126200046d576080515192620000f28462000972565b64ffffffffff60443581811681036200046957855260643590811681036200046557602060a05260a051850152845160a0518601511515608051870151916200015f6060890151926200018c60808b01516080515193849160a05160a05184015260805183019062000bf8565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101845283620009be565b60a08a015191620001d460c08c0151620001ce6080515182620001bd829451809260a05185019060a0510162000bd3565b81010380835260a0510182620009be565b62000c3d565b60e08c01511515916080515195620002058d60a0518901906020908164ffffffffff91828151168552015116910152565b608051875260608701978789108b8a1117620004385791620002fb9795939160a39795938a608051527fffffffffff000000000000000000000000000000000000000000000000000000608089019c8d977fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009889809660601b16905260f81b60948b015260d81b16609589015260601b16609a870152620002b28151809260ae89019060a0510162000bd3565b85019360ae85015260ce84015260f81b60ee83015260243560601b1660ef820152815190620002eb82610103830160a051860162000bd3565b01036083810184520182620009be565b519020608051519091611812808301918211838310176200040b5790829162000d618339608081526200036062000336608083018a62000cd1565b91876024351660a05182015260805101886020908164ffffffffff91828151168552015116910152565b039083f5908115620003fe5750620003de93827f2ba0fe49588281dbb122dd3b7f3e2b3396338f70dbe3c62bf3e3888b4ba7ffb893620003b493169586956080515194859460c0865260c086019062000cd1565b926024351660a0518501526080518401906020908164ffffffffff91828151168552015116910152565b608435608083015260a43560a08301520390a26080515190815260a05190f35b60805151903d90823e3d90fd5b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b60248c7f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b8280fd5b8380fd5b80fd5b50346200046d5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126200046d5767ffffffffffffffff6004358181116200046557620004c690369060040162000aa0565b9160249081359273ffffffffffffffffffffffffffffffffffffffff92838516928386036200046d57604435968388116200096e57366023890112156200096e57876004013594848611620009425760209860805151966200052e8b8260051b0189620009be565b808852858b89019160061b830101913683116200093e579398938601905b828210620008e8575050508296839284985b88518a1015620005d6578780620005768c8c62000b8f565b515116911601878111620005aa576001909464ffffffffff8d6200059b8d8d62000b8f565b5101511601990198936200055e565b86867f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b8794979196939a9298995016670de0b6b3a76400008103620008b7575086518a898982810151151593608051820151946060830151966080840151926080515194858881019589875260805182016200062f9162000bf8565b03947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0958681018852620006649088620009be565b60a08201519660c08301518a60805151828193519081848085019201916200068c9262000bd3565b810103808352016200069f9082620009be565b620006aa9062000c3d565b9260e001511515936080515197888c81019b8d8d526080518201620006cf9162000c7f565b039081018952620006e19089620009be565b608051519c8d9c8d9c8d019e8f997fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009a8b809660601b16905260f81b906034015260d81b7fffffffffff0000000000000000000000000000000000000000000000000000001660358d015260601b16603a8b0152519081604e8b01620007679262000bd3565b880195604e870152606e86015260f81b608e85015260601b16608f83015251918260a38301620007979262000bd3565b010360838101825260a301620007ae9082620009be565b519020916080515191611e159081840192848410908411176200088c57508291620008029162002573843960608152620007ec606082018a62000cd1565b908b8d8201528082039060805101528962000c7f565b039083f5908115620003fe5750916200086a7ffe44018cf74992b2720702385a1728bd329dd136e4f651203176c81c12710a8b94926200085694169687966080515195869560c0875260c087019062000cd1565b918a86015284820360805186015262000c7f565b906060830152606435608083015260843560a08301520390a260805151908152f35b857f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b905060805151907f1b05170a0000000000000000000000000000000000000000000000000000000082526004820152fd5b608099949951823603126200093e5760805151620009068162000972565b823589811681036200093a5781528c91906200092484840162000a13565b838201528152019060805101909893986200054c565b8780fd5b8580fd5b50507f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b5080fd5b6040810190811067ffffffffffffffff8211176200098f57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176200098f57604052565b3590811515820362000a0e57565b600080fd5b359064ffffffffff8216820362000a0e57565b81601f8201121562000a0e5780359067ffffffffffffffff82116200098f576040519262000a7d60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8601160185620009be565b8284526020838301011162000a0e57816000926020809301838601378301015290565b91909161010090818185031262000a0e5760405191820167ffffffffffffffff90838110828211176200098f576040528294823573ffffffffffffffffffffffffffffffffffffffff90818116810362000a0e57855262000b046020850162000a00565b602086015262000b176040850162000a13565b60408601526060840135908116810362000a0e576060850152608083013582811162000a0e578162000b4b91850162000a26565b608085015260a083013560a085015260c083013591821162000a0e578262000b7e60e0949262000b8a9486940162000a26565b60c08601520162000a00565b910152565b805182101562000ba45760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60005b83811062000be75750506000910152565b818101518382015260200162000bd6565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209362000c368151809281875287808801910162000bd3565b0116010190565b60208151910151906020811062000c52575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b90815180825260208080930193019160005b82811062000ca0575050505090565b8351805167ffffffffffffffff16865282015164ffffffffff16858301526040909401939281019260010162000c91565b9060e08062000d5562000d3861010073ffffffffffffffffffffffffffffffffffffffff80885116875260208801511515602088015264ffffffffff6040890151166040880152606088015116606087015260808701519080608088015286019062000bf8565b60a086015160a086015260c086015185820360c087015262000bf8565b93015115159101529056fe6101608060405234620005ba57620018128038038091620000218285620005db565b833981019080820360808112620005ba5781516001600160401b038111620005ba5782019061010082850312620005ba576040519361010085016001600160401b03811186821017620005a45760405282516001600160a01b0381168103620005ba5785526200009460208401620005ff565b9260208601938452620000aa604082016200060d565b604087019081526060820151909690926001600160a01b0384168403620005ba576060820193845260808301516001600160401b038111620005ba5781620000f491850162000661565b6080830190815260a08085015190840190815260c085015191949092906001600160401b038311620005ba576200013460e0926200014094830162000661565b60c086015201620005ff565b60e083019081526020880151959093906001600160a01b0387168703620005ba57604090603f190112620005ba5760408051989089016001600160401b0381118a821017620005a457620001ab91606091604052620001a2604082016200060d565b8b52016200060d565b946020890195865260c0840151516020811162000585575051600080546001600160a01b0319166001600160a01b0392831617815584519091166080529651151560a052975164ffffffffff90811660c052975180516001600160401b038111620005715760019182548381811c9116801562000566575b60208210146200055257601f811162000505575b50602090601f8311600114620004995760c095949392918a91836200048d575b5050600019600383901b1c191690821b1790555b5160e05201516040516200029f602082816200029181830196878151938492016200063c565b8101038084520182620005db565b51905190602081106200047a575b50610100525115159461012095865261014094838652511669ffffffffff0000000000600354925160281b169160018060501b031916171760035560018060a01b03608051166040519160208301848063095ea7b360e01b9283815260018060a01b03851660248801526000196044880152604487526200032e87620005bf565b86519082875af16200033f620006ae565b8162000439575b50806200042e575b15620003e2575b60405161104290878983620007d08439608051838181610470015281816107440152610c36015260a05183818161076b0152610b5c015260c05183818161015d01528181610aa801528181610d8f0152610f60015260e0518381816102e2015261066201526101005183610e250152518281816107930152610b1f0152518181816101ae01526109060152f35b62000423946200041d9260405192602084015260018060a01b031660248301526044820152604481526200041681620005bf565b82620006e3565b620006e3565b388080808062000355565b50823b15156200034e565b805180159250821562000450575b50503862000346565b8192509060209181010312620004765760206200046e9101620005ff565b388062000447565b8580fd5b6000199060200360031b1b1638620002ad565b01519050388062000257565b838a5260208a209190601f1984168b5b818110620004ee575091859492918360c0999897959310620004d4575b505050811b0190556200026b565b015160001960f88460031b161c19169055388080620004c6565b8284015185559386019360209384019301620004a9565b838a5260208a20601f840160051c8101916020851062000547575b601f0160051c019084905b8281106200053b57505062000237565b8b81550184906200052b565b909150819062000520565b634e487b7160e01b8a52602260045260248afd5b90607f169062000223565b634e487b7160e01b88526041600452602488fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b634e487b7160e01b600052604160045260246000fd5b600080fd5b608081019081106001600160401b03821117620005a457604052565b601f909101601f19168101906001600160401b03821190821017620005a457604052565b51908115158203620005ba57565b519064ffffffffff82168203620005ba57565b6001600160401b038111620005a457601f01601f191660200190565b60005b838110620006505750506000910152565b81810151838201526020016200063f565b81601f82011215620005ba5780516200067a8162000620565b926200068a6040519485620005db565b81845260208284010111620005ba57620006ab91602080850191016200063c565b90565b3d15620006de573d90620006c28262000620565b91620006d26040519384620005db565b82523d6000602084013e565b606090565b600080620007109260018060a01b03169360208151910182865af162000708620006ae565b908362000767565b805190811515918262000740575b5050620007285750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620005ba5760206200075e9101620005ff565b1538806200071e565b906200079057508051156200077e57805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580620007c5575b620007a3575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156200079a56fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde0314610e0e575080631686c90914610b8157806316c3549d14610b445780631bfd681414610b075780633bfe03a814610ad85780633f31ae3f146104945780634800d97f1461044357806349fc73dd1461030557806351e75e8b146102ca57806375829def146101ed57806390e64d13146101d25780639e93e57714610181578063bb4b57341461013f578063ce516507146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760206003193601126100f857602061013560043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f8576020610135610f58565b346100f85760206003193601126100f857610206610ebc565b60005473ffffffffffffffffffffffffffffffffffffffff8082169233840361027d577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c9060018316928315610439575b602093848410811461040a578386529081156103cc5750600114610371575b61036d8461036181880382610f17565b60405191829182610e56565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8284106103b9575050508161036d936103619282010193610351565b805485850187015292850192810161039d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103618161036d610351565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691610332565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f857604435906fffffffffffffffffffffffffffffffff821682036100f85767ffffffffffffffff606435116100f8573660236064350112156100f85767ffffffffffffffff60643560040135116100f8573660246064356004013560051b6064350101116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff831660408201526fffffffffffffffffffffffffffffffff841660608201526060815261057a81610efb565b519020604051602081019182526020815261059481610edf565b519020916105a0610f58565b610a7a576105c960043560ff6001918060081c6000526002602052161b60406000205416151590565b610a4857604051926105e760206064356004013560051b0185610f17565b60643560048101358552602401602085015b60246064356004013560051b60643501018210610a38575050906000915b845183101561065e5760208360051b860101519081811060001461064b57600052602052600160406000205b920191610617565b9060005260205260016040600020610643565b83907f000000000000000000000000000000000000000000000000000000000000000003610a0e5760043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff60005416906040516106cd81610edf565b600081526000602082015260405192610100840184811067ffffffffffffffff8211176109df57604052835273ffffffffffffffffffffffffffffffffffffffff821660208401526fffffffffffffffffffffffffffffffff8416604084015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608401527f0000000000000000000000000000000000000000000000000000000000000000151560808401527f0000000000000000000000000000000000000000000000000000000000000000151560a08401526040516107c581610edf565b64ffffffffff600354818116835260281c16602082015260c084015260e0830152602060e0604051937fab167ccc00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff815116600486015273ffffffffffffffffffffffffffffffffffffffff838201511660248601526fffffffffffffffffffffffffffffffff604082015116604486015273ffffffffffffffffffffffffffffffffffffffff606082015116606486015260808101511515608486015260a0810151151560a486015264ffffffffff8360c08301518281511660c489015201511660e4860152015173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015260208261014481600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af19182156109d35760009261099e575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d6020116109cb575b816109b960209383610f17565b810103126100f8579051906020610937565b3d91506109ac565b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b81358152602091820191016105f9565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f857604060035464ffffffffff825191818116835260281c166020820152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857610b9a610ebc565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff8060005416338103610dbf5750610be1610f58565b15610d61576040519060209160008083858401977fa9059cbb000000000000000000000000000000000000000000000000000000008952169687602485015286604485015260448452610c3384610efb565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610d55573d67ffffffffffffffff81116109df57610cbc9160405191610cac877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610f17565b82523d60008784013e5b83610f95565b8051848115159182610d34575b50509050610d035750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488610cc9565b610cbc90606090610cb6565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f85761036d907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261036181610edf565b60208082528251818301819052939260005b858110610ea8575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201610e68565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff8211176109df57604052565b6080810190811067ffffffffffffffff8211176109df57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176109df57604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081610f8d575090565b905042101590565b90610fd45750805115610faa57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b8151158061102c575b610fe5575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15610fdd56fea164736f6c6343000817000a6101608060405234620006e95762001e1590813803809262000022828462000735565b82396060818381010312620006e95780516001600160401b038111620006e9578101916101009283818385010312620006e957604051908482016001600160401b03811183821017620007035760405280516001600160a01b0381168103620006e9578252620000956020820162000759565b6020830152620000a86040820162000767565b604083015260608101516001600160a01b0381168103620006e957606083015260808101516001600160401b038111620006e957620000ed90848601908301620007bb565b608083015260a0818101519083015260c08101516001600160401b038111620006e95762000134916200012860e092868801908301620007bb565b60c08501520162000759565b60e08201526020830151916001600160a01b0383168303620006e9576040840151906001600160401b038211620006e957808501601f838701011215620006e95784820151906001600160401b0382116200070357604051956200019f60208460051b018862000735565b828752602087019382820160208560061b838501010111620006e95793602085830101945b60208560061b82850101018610620006835750505050505060c081015151602081116200066457506060810151600080546001600160a01b0319166001600160a01b03928316178155825190911660809081526020830151151560a052604083015164ffffffffff1660c0528201518051919290916001600160401b038111620006505760019283548481811c9116801562000645575b60208210146200063157601f8111620005e4575b50602090601f83116001146200057a5760e09392918691836200056e575b5050600019600383901b1c191690841b1783555b60a0810151825260c0810151604051620002db60208281620002cd818301968781519384920162000796565b810103808452018262000735565b51905190602081106200055b575b5087520151151593610120948552610140938452805190835b82811062000499575050505060018060a01b036080511660018060a01b03835116906040519160208301848063095ea7b360e01b92838152846024880152600019604488015260448752620003578762000719565b86519082875af16200036862000808565b8162000458575b50806200044d575b1562000409575b6040516114eb9087898b846200092a85396080518481816104e2015281816108550152610f1c015260a05184818161087c0152610e42015260c05184818161022001528181610dbd015281816110750152611246015260e05184818161035401526106ba0152518361110b0152518281816108a40152610e0501525181818161012a0152610a0e0152f35b62000442946200043c926040519260208401526024830152604482015260448152620004358162000719565b826200083d565b6200083d565b38808080806200037e565b50823b151562000377565b80518015925082156200046f575b5050386200036f565b8192509060209181010312620004955760206200048d910162000759565b388062000466565b8580fd5b8151811015620005475760208160051b8301015160038054906801000000000000000082101562000533578682018082558210156200051f578752602080882083519201805493909101516cffffffffff000000000000000060409190911b166001600160401b039092166001600160681b031990931692909217179055830162000302565b634e487b7160e01b88526032600452602488fd5b634e487b7160e01b88526041600452602488fd5b634e487b7160e01b85526032600452602485fd5b6000199060200360031b1b1638620002e9565b0151905038806200028d565b92918491601f198216908388526020882091885b818110620005cb5750958360e09710620005b1575b505050811b018355620002a1565b015160001960f88460031b161c19169055388080620005a3565b828801518455889590930192602092830192016200058e565b84865260208620601f840160051c8101916020851062000626575b601f0160051c019085905b8281106200061a5750506200026f565b8781550185906200060a565b9091508190620005ff565b634e487b7160e01b86526022600452602486fd5b90607f16906200025b565b634e487b7160e01b84526041600452602484fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b6040868585010312620006e957604080519081016001600160401b03811182821017620006ee5760405286516001600160401b0381168103620006e95760209382859260409452620006d7838b0162000767565b838201528152019601959150620001c4565b600080fd5b60246000634e487b7160e01b81526041600452fd5b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b038211176200070357604052565b601f909101601f19168101906001600160401b038211908210176200070357604052565b51908115158203620006e957565b519064ffffffffff82168203620006e957565b6001600160401b0381116200070357601f01601f191660200190565b60005b838110620007aa5750506000910152565b818101518382015260200162000799565b81601f82011215620006e9578051620007d4816200077a565b92620007e4604051948562000735565b81845260208284010111620006e95762000805916020808501910162000796565b90565b3d1562000838573d906200081c826200077a565b916200082c604051938462000735565b82523d6000602084013e565b606090565b6000806200086a9260018060a01b03169360208151910182865af16200086262000808565b9083620008c1565b80519081151591826200089a575b5050620008825750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620006e9576020620008b8910162000759565b15388062000878565b90620008ea5750805115620008d857805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806200091f575b620008fd575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15620008f456fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde03146110f4575080631686c90914610e6757806316c3549d14610e2a5780631bfd681414610ded5780633f31ae3f146105065780634800d97f146104b557806349fc73dd1461037757806351e75e8b1461033c57806375829def1461025f57806390e64d1314610244578063bb4b573414610202578063bf4ed03f14610190578063ce5165071461014e578063da792468146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760206003193601126100f857602061018660043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f8576101a9611293565b6040516020918282018383528151809152836040840192019360005b8281106101d25784840385f35b8551805167ffffffffffffffff16855282015164ffffffffff1684830152948101946040909301926001016101c5565b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f857602061018661123e565b346100f85760206003193601126100f8576102786111a2565b60005473ffffffffffffffffffffffffffffffffffffffff808216923384036102ef577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c90600183169283156104ab575b602093848410811461047c5783865290811561043e57506001146103e3575b6103df846103d3818803826111fd565b6040519182918261113c565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b82841061042b57505050816103df936103d392820101936103c3565b805485850187015292850192810161040f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103d3816103df6103c3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f16916103a4565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f8576fffffffffffffffffffffffffffffffff60443516604435036100f85767ffffffffffffffff606435116100f8573660236064350112156100f857606435600401359067ffffffffffffffff82116100f85760248260051b60643501013681116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff841660408201526fffffffffffffffffffffffffffffffff604435166060820152606081526105eb816111e1565b5190206040516020810191825260208152610605816111c5565b5190209061061161123e565b610d8f5761063a60043560ff6001918060081c6000526002602052161b60406000205416151590565b610d5d576106478461127b565b9361065560405195866111fd565b8452606435602401602085015b828210610d4d575050506000905b83518210156106b657610683828561132e565b5190818110156106a357600052602052600160406000205b910190610670565b906000526020526001604060002061069b565b90507f000000000000000000000000000000000000000000000000000000000000000003610d23576106e6611293565b90600082516106f48161127b565b9361070260405195866111fd565b8185527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061072f8361127b565b0160005b818110610cfe5750506000905b828210610bd95750506fffffffffffffffffffffffffffffffff82166fffffffffffffffffffffffffffffffff604435168111610baa576fffffffffffffffffffffffffffffffff6044351611610b56575b505060043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff6000541691604051906107dc826111c5565b600082526000602083015260405193610100850185811067ffffffffffffffff821117610b2757604052845273ffffffffffffffffffffffffffffffffffffffff831660208501526fffffffffffffffffffffffffffffffff60443516604085015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608501527f0000000000000000000000000000000000000000000000000000000000000000151560808501527f0000000000000000000000000000000000000000000000000000000000000000151560a085015260c084015260e0830152604051917f897f362b0000000000000000000000000000000000000000000000000000000083526020600484015282610144810173ffffffffffffffffffffffffffffffffffffffff835116602483015273ffffffffffffffffffffffffffffffffffffffff60208401511660448301526fffffffffffffffffffffffffffffffff604084015116606483015273ffffffffffffffffffffffffffffffffffffffff60608401511660848301526080830151151560a483015260a0830151151560c483015260c08301519061012060e484015281518091526020610164840192019060005b818110610ae9575050508190602060e08195015173ffffffffffffffffffffffffffffffffffffffff81511661010485015201516101248301520381600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1918215610add57600092610aa8575b60208380847f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff81519360043585526fffffffffffffffffffffffffffffffff60443516888601521692a3604051908152f35b91506020823d602011610ad5575b81610ac3602093836111fd565b810103126100f8576020915191610a3f565b3d9150610ab6565b6040513d6000823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff1681860152889550604090940193909201916001016109b9565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6fffffffffffffffffffffffffffffffff610b947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8293018661132e565b5192604435031681835116011690528280610792565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b9092610c0e67ffffffffffffffff610bf1868561132e565b5151166fffffffffffffffffffffffffffffffff60443516611371565b6fffffffffffffffffffffffffffffffff8111610ccd576fffffffffffffffffffffffffffffffff8091169164ffffffffff6020610c4c888761132e565b5101511660405190610c5d826111c5565b8482526020820152610c6f878a61132e565b52610c7a868961132e565b5016016fffffffffffffffffffffffffffffffff8111610c9e579260010190610740565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b602490604051907f4916adce0000000000000000000000000000000000000000000000000000000082526004820152fd5b602090604051610d0d816111c5565b6000815260008382015282828a01015201610733565b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b8135815260209182019101610662565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857610e806111a2565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff80600054163381036110a55750610ec761123e565b15611047576040519060209160008083858401977fa9059cbb000000000000000000000000000000000000000000000000000000008952169687602485015286604485015260448452610f19846111e1565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d1561103b573d67ffffffffffffffff8111610b2757610fa29160405191610f92877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846111fd565b82523d60008784013e5b8361143e565b805184811515918261101a575b50509050610fe95750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488610faf565b610fa290606090610f9c565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f8576103df907f00000000000000000000000000000000000000000000000000000000000000006020820152602081526103d3816111c5565b60208082528251818301819052939260005b85811061118e575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b81810183015184820160400152820161114e565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff821117610b2757604052565b6080810190811067ffffffffffffffff821117610b2757604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610b2757604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081611273575090565b905042101590565b67ffffffffffffffff8111610b275760051b60200190565b600354906112a08261127b565b9160406112b060405194856111fd565b8184528360208091019160036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b906000935b8585106112f457505050505050565b60018481928451611304816111c5565b64ffffffffff875467ffffffffffffffff81168352871c16838201528152019301940193916112e5565b80518210156113425760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8382098382029182808310920391808303921461142d57670de0b6b3a764000090818310156113f657947faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac1066994950990828211900360ee1b910360121c170290565b60449086604051917f5173648d00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5050670de0b6b3a764000090049150565b9061147d575080511561145357805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b815115806114d5575b61148e575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b1561148656fea164736f6c6343000817000aa164736f6c6343000817000a"; /*////////////////////////////////////////////////////////////////////////// DEPLOYERS diff --git a/src/SablierV2MerkleLockupFactory.sol b/src/SablierV2MerkleLockupFactory.sol index d0ef38ce..1625564a 100644 --- a/src/SablierV2MerkleLockupFactory.sol +++ b/src/SablierV2MerkleLockupFactory.sol @@ -35,13 +35,13 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { // Hash the parameters to generate a salt. bytes32 salt = keccak256( abi.encodePacked( - baseParams.initialAdmin, baseParams.asset, + baseParams.cancelable, + baseParams.expiration, + baseParams.initialAdmin, abi.encode(baseParams.ipfsCID), - bytes32(abi.encodePacked(baseParams.name)), baseParams.merkleRoot, - baseParams.expiration, - baseParams.cancelable, + bytes32(abi.encodePacked(baseParams.name)), baseParams.transferable, lockupLinear, abi.encode(streamDurations) @@ -86,13 +86,13 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { // Hash the parameters to generate a salt. bytes32 salt = keccak256( abi.encodePacked( - baseParams.initialAdmin, baseParams.asset, + baseParams.cancelable, + baseParams.expiration, + baseParams.initialAdmin, abi.encode(baseParams.ipfsCID), - bytes32(abi.encodePacked(baseParams.name)), baseParams.merkleRoot, - baseParams.expiration, - baseParams.cancelable, + bytes32(abi.encodePacked(baseParams.name)), baseParams.transferable, lockupTranched, abi.encode(tranchesWithPercentages) diff --git a/src/types/DataTypes.sol b/src/types/DataTypes.sol index 4acad74d..111353c9 100644 --- a/src/types/DataTypes.sol +++ b/src/types/DataTypes.sol @@ -90,22 +90,22 @@ library BatchLockup { library MerkleLockup { /// @notice Struct encapsulating the base constructor parameters of a MerkleLockup campaign. - /// @param initialAdmin The initial admin of the MerkleLockup campaign. /// @param asset The contract address of the ERC-20 asset to be distributed. + /// @param cancelable Indicates if the stream will be cancelable after claiming. + /// @param expiration The expiration of the campaign, as a Unix timestamp. + /// @param initialAdmin The initial admin of the MerkleLockup campaign. /// @param ipfsCID The content identifier for indexing the contract on IPFS. - /// @param name The name of the campaign. /// @param merkleRoot The Merkle root of the claim data. - /// @param expiration The expiration of the campaign, as a Unix timestamp. - /// @param cancelable Indicates if the stream will be cancelable after claiming. + /// @param name The name of the campaign. /// @param transferable Indicates if the stream will be transferable after claiming. struct ConstructorParams { - address initialAdmin; IERC20 asset; + bool cancelable; + uint40 expiration; + address initialAdmin; string ipfsCID; - string name; bytes32 merkleRoot; - uint40 expiration; - bool cancelable; + string name; bool transferable; } } @@ -114,7 +114,7 @@ library MerkleLT { /// @notice Struct encapsulating the unlock percentage and duration of a tranche. /// @dev Since users may have different amounts allocated, this struct makes it possible to calculate the amounts /// at claim time. An 18-decimal format is used to represent percentages: 100% = 1e18. For more information, see - /// the PRBMath documentation on {UD2x18}: https://github.com/PaulRBerg/prb-math + /// the PRBMath documentation on UD2x18: https://github.com/PaulRBerg/prb-math /// @param unlockPercentage The percentage designated to be unlocked in this tranche. /// @param duration The time difference in seconds between this tranche and the previous one. struct TrancheWithPercentage { diff --git a/test/Base.t.sol b/test/Base.t.sol index 06483407..5310e4ea 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -286,13 +286,13 @@ abstract contract Base_Test is { bytes32 salt = keccak256( abi.encodePacked( - admin, address(asset_), + defaults.CANCELABLE(), + expiration, + admin, abi.encode(defaults.IPFS_CID()), - defaults.NAME_BYTES32(), merkleRoot, - expiration, - defaults.CANCELABLE(), + defaults.NAME_BYTES32(), defaults.TRANSFERABLE(), lockupLinear, abi.encode(defaults.durations()) @@ -330,13 +330,13 @@ abstract contract Base_Test is { bytes32 salt = keccak256( abi.encodePacked( - admin, address(asset_), + defaults.CANCELABLE(), + expiration, + admin, abi.encode(defaults.IPFS_CID()), - defaults.NAME_BYTES32(), merkleRoot, - expiration, - defaults.CANCELABLE(), + defaults.NAME_BYTES32(), defaults.TRANSFERABLE(), lockupTranched, abi.encode(defaults.tranchesWithPercentages()) @@ -361,7 +361,7 @@ abstract contract Base_Test is returns (bytes memory) { bytes memory constructorArgs = - abi.encode(defaults.baseParams(admin, asset_, merkleRoot, expiration), lockupLinear, defaults.durations()); + abi.encode(defaults.baseParams(admin, asset_, expiration, merkleRoot), lockupLinear, defaults.durations()); if (!isTestOptimizedProfile()) { return bytes.concat(type(SablierV2MerkleLL).creationCode, constructorArgs); } else { @@ -381,7 +381,7 @@ abstract contract Base_Test is returns (bytes memory) { bytes memory constructorArgs = abi.encode( - defaults.baseParams(admin, asset_, merkleRoot, expiration), + defaults.baseParams(admin, asset_, expiration, merkleRoot), lockupTranched, defaults.tranchesWithPercentages() ); diff --git a/test/fork/merkle-lockup/MerkleLT.t.sol b/test/fork/merkle-lockup/MerkleLT.t.sol index ec8ec664..ad741646 100644 --- a/test/fork/merkle-lockup/MerkleLT.t.sol +++ b/test/fork/merkle-lockup/MerkleLT.t.sol @@ -35,11 +35,11 @@ abstract contract MerkleLT_Fork_Test is Fork_Test { } struct Vars { - uint256 actualStreamId; LockupTranched.StreamLT actualStream; + uint256 actualStreamId; LockupTranched.Tranche[] actualTranches; - uint128[] amounts; uint256 aggregateAmount; + uint128[] amounts; MerkleLockup.ConstructorParams baseParams; uint128 clawbackAmount; address expectedLT; diff --git a/test/integration/merkle-lockup/MerkleLockup.t.sol b/test/integration/merkle-lockup/MerkleLockup.t.sol index 8d6be352..ba55de28 100644 --- a/test/integration/merkle-lockup/MerkleLockup.t.sol +++ b/test/integration/merkle-lockup/MerkleLockup.t.sol @@ -62,7 +62,7 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { function createMerkleLL(address admin, uint40 expiration) internal returns (ISablierV2MerkleLL) { return merkleLockupFactory.createMerkleLL({ - baseParams: defaults.baseParams(admin, dai, defaults.MERKLE_ROOT(), expiration), + baseParams: defaults.baseParams(admin, dai, expiration, defaults.MERKLE_ROOT()), lockupLinear: lockupLinear, streamDurations: defaults.durations(), aggregateAmount: defaults.AGGREGATE_AMOUNT(), @@ -113,7 +113,7 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { function createMerkleLT(address admin, uint40 expiration) internal returns (ISablierV2MerkleLT) { return merkleLockupFactory.createMerkleLT({ - baseParams: defaults.baseParams(admin, dai, defaults.MERKLE_ROOT(), expiration), + baseParams: defaults.baseParams(admin, dai, expiration, defaults.MERKLE_ROOT()), lockupTranched: lockupTranched, tranchesWithPercentages: defaults.tranchesWithPercentages(), aggregateAmount: defaults.AGGREGATE_AMOUNT(), diff --git a/test/utils/BatchLockupBuilder.sol b/test/utils/BatchLockupBuilder.sol index c0a31621..f4efe199 100644 --- a/test/utils/BatchLockupBuilder.sol +++ b/test/utils/BatchLockupBuilder.sol @@ -21,7 +21,7 @@ library BatchLockupBuilder { } } - /// @notice Turns the `params` into an array of `BatchLockup.CreateWithDurationsLD` structs. + /// @notice Turns the `params` into an array of {BatchLockup.CreateWithDurationsLD} structs. function fillBatch( LockupDynamic.CreateWithDurations memory params, uint256 batchSize @@ -58,7 +58,7 @@ library BatchLockupBuilder { } } - /// @notice Turns the `params` into an array of `BatchLockup.CreateWithDurationsLL` structs. + /// @notice Turns the `params` into an array of {BatchLockup.CreateWithDurationsLL} structs. function fillBatch( LockupLinear.CreateWithDurations memory params, uint256 batchSize @@ -95,7 +95,7 @@ library BatchLockupBuilder { } } - /// @notice Turns the `params` into an array of `BatchLockup.CreateWithDurationsLT` structs. + /// @notice Turns the `params` into an array of {BatchLockup.CreateWithDurationsLT} structs. function fillBatch( LockupTranched.CreateWithDurations memory params, uint256 batchSize @@ -132,7 +132,7 @@ library BatchLockupBuilder { } } - /// @notice Turns the `params` into an array of `BatchLockup.CreateWithTimestampsLDs` structs. + /// @notice Turns the `params` into an array of {BatchLockup.CreateWithTimestampsLDs} structs. function fillBatch( LockupDynamic.CreateWithTimestamps memory params, uint256 batchSize @@ -170,7 +170,7 @@ library BatchLockupBuilder { } } - /// @notice Turns the `params` into an array of `BatchLockup.CreateWithTimestampsLL` structs. + /// @notice Turns the `params` into an array of {BatchLockup.CreateWithTimestampsLL} structs. function fillBatch( LockupLinear.CreateWithTimestamps memory params, uint256 batchSize @@ -207,7 +207,7 @@ library BatchLockupBuilder { } } - /// @notice Turns the `params` into an array of `BatchLockup.CreateWithTimestampsLT` structs. + /// @notice Turns the `params` into an array of {BatchLockup.CreateWithTimestampsLT} structs. function fillBatch( LockupTranched.CreateWithTimestamps memory params, uint256 batchSize diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index 2dbc9c70..c2b0afdd 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -119,27 +119,27 @@ contract Defaults is Merkle { } function baseParams() public view returns (MerkleLockup.ConstructorParams memory) { - return baseParams(users.admin, asset, MERKLE_ROOT, EXPIRATION); + return baseParams(users.admin, asset, EXPIRATION, MERKLE_ROOT); } function baseParams( address admin, IERC20 asset_, - bytes32 merkleRoot, - uint40 expiration + uint40 expiration, + bytes32 merkleRoot ) public pure returns (MerkleLockup.ConstructorParams memory) { return MerkleLockup.ConstructorParams({ - initialAdmin: admin, asset: asset_, + cancelable: CANCELABLE, + expiration: expiration, + initialAdmin: admin, ipfsCID: IPFS_CID, - name: NAME, merkleRoot: merkleRoot, - expiration: expiration, - cancelable: CANCELABLE, + name: NAME, transferable: TRANSFERABLE }); } @@ -212,7 +212,7 @@ contract Defaults is Merkle { }); } - /// @dev Returns a batch of `LockupDynamic.Segment` parameters. + /// @dev Returns a batch of {LockupDynamic.Segment} parameters. function segments() private view returns (LockupDynamic.Segment[] memory segments_) { segments_ = new LockupDynamic.Segment[](2); segments_[0] = LockupDynamic.Segment({ @@ -227,12 +227,12 @@ contract Defaults is Merkle { }); } - /// @dev Returns a batch of `LockupDynamic.SegmentWithDuration` parameters. + /// @dev Returns a batch of {LockupDynamic.SegmentWithDuration} parameters. function segmentsWithDurations() public pure returns (LockupDynamic.SegmentWithDuration[] memory) { return segmentsWithDurations({ amount0: 2500e18, amount1: 7500e18 }); } - /// @dev Returns a batch of `LockupDynamic.SegmentWithDuration` parameters. + /// @dev Returns a batch of {LockupDynamic.SegmentWithDuration} parameters. function segmentsWithDurations( uint128 amount0, uint128 amount1 @@ -356,12 +356,12 @@ contract Defaults is Merkle { } } - /// @dev Returns a batch of `LockupTranched.TrancheWithDuration` parameters. + /// @dev Returns a batch of {LockupTranched.TrancheWithDuration} parameters. function tranchesWithDurations() public pure returns (LockupTranched.TrancheWithDuration[] memory) { return tranchesWithDurations({ amount0: 2500e18, amount1: 7500e18 }); } - /// @dev Returns a batch of `LockupTranched.TrancheWithDuration` parameters. + /// @dev Returns a batch of {LockupTranched.TrancheWithDuration} parameters. function tranchesWithDurations( uint128 amount0, uint128 amount1 @@ -379,27 +379,27 @@ contract Defaults is Merkle { BATCH-LOCKUP //////////////////////////////////////////////////////////////////////////*/ - /// @dev Returns a default-size batch of `BatchLockup.CreateWithDurationsLD` parameters. + /// @dev Returns a default-size batch of {BatchLockup.CreateWithDurationsLD} parameters. function batchCreateWithDurationsLD() public view returns (BatchLockup.CreateWithDurationsLD[] memory batch) { batch = BatchLockupBuilder.fillBatch(createWithDurationsLD(), BATCH_SIZE); } - /// @dev Returns a default-size batch of `BatchLockup.CreateWithDurationsLL` parameters. + /// @dev Returns a default-size batch of {BatchLockup.CreateWithDurationsLL} parameters. function batchCreateWithDurationsLL() public view returns (BatchLockup.CreateWithDurationsLL[] memory batch) { batch = BatchLockupBuilder.fillBatch(createWithDurationsLL(), BATCH_SIZE); } - /// @dev Returns a default-size batch of `BatchLockup.CreateWithDurationsLT` parameters. + /// @dev Returns a default-size batch of {BatchLockup.CreateWithDurationsLT} parameters. function batchCreateWithDurationsLT() public view returns (BatchLockup.CreateWithDurationsLT[] memory batch) { batch = BatchLockupBuilder.fillBatch(createWithDurationsLT(), BATCH_SIZE); } - /// @dev Returns a default-size batch of `BatchLockup.CreateWithTimestampsLD` parameters. + /// @dev Returns a default-size batch of {BatchLockup.CreateWithTimestampsLD} parameters. function batchCreateWithTimestampsLD() public view returns (BatchLockup.CreateWithTimestampsLD[] memory batch) { batch = batchCreateWithTimestampsLD(BATCH_SIZE); } - /// @dev Returns a batch of `BatchLockup.CreateWithTimestampsLD` parameters. + /// @dev Returns a batch of {BatchLockup.CreateWithTimestampsLD} parameters. function batchCreateWithTimestampsLD(uint256 batchSize) public view @@ -408,12 +408,12 @@ contract Defaults is Merkle { batch = BatchLockupBuilder.fillBatch(createWithTimestampsLD(), batchSize); } - /// @dev Returns a default-size batch of `BatchLockup.CreateWithTimestampsLL` parameters. + /// @dev Returns a default-size batch of {BatchLockup.CreateWithTimestampsLL} parameters. function batchCreateWithTimestampsLL() public view returns (BatchLockup.CreateWithTimestampsLL[] memory batch) { batch = batchCreateWithTimestampsLL(BATCH_SIZE); } - /// @dev Returns a batch of `BatchLockup.CreateWithTimestampsLL` parameters. + /// @dev Returns a batch of {BatchLockup.CreateWithTimestampsLL} parameters. function batchCreateWithTimestampsLL(uint256 batchSize) public view @@ -422,12 +422,12 @@ contract Defaults is Merkle { batch = BatchLockupBuilder.fillBatch(createWithTimestampsLL(), batchSize); } - /// @dev Returns a default-size batch of `BatchLockup.CreateWithTimestampsLT` parameters. + /// @dev Returns a default-size batch of {BatchLockup.CreateWithTimestampsLT} parameters. function batchCreateWithTimestampsLT() public view returns (BatchLockup.CreateWithTimestampsLT[] memory batch) { batch = batchCreateWithTimestampsLT(BATCH_SIZE); } - /// @dev Returns a batch of `BatchLockup.CreateWithTimestampsLL` parameters. + /// @dev Returns a batch of {BatchLockup.CreateWithTimestampsLL} parameters. function batchCreateWithTimestampsLT(uint256 batchSize) public view From 90de8718194eaa1a4ca71ff78695d478bf58691f Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Fri, 12 Apr 2024 19:09:55 +0100 Subject: [PATCH 41/61] docs: update security assumptions --- SECURITY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/SECURITY.md b/SECURITY.md index 2745721e..dde5ba31 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -48,6 +48,7 @@ vulnerability, it must adhere to these assumptions as well: - [All assumptions](https://github.com/sablier-labs/v2-core/blob/main/SECURITY.md) in Sablier V2 Core apply to Sablier V2 Periphery as well. - In `SablierV2MerkleLT`, the tranche unlock percentages and the durations will be same for all airdrop claimers. +- In `SablierV2MerkleLockupFactory`, there should be no need to create two Merkle campaigns with identical parameters. ### Rewards From 502bacc61089dc4decf9c5e1a55b1cc92885c322 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Tue, 23 Apr 2024 11:30:50 +0100 Subject: [PATCH 42/61] build: replace ffi with stdJson for salt creation (#333) --- foundry.toml | 5 ++--- script/Base.s.sol | 17 ++++++----------- test/utils/BaseScript.t.sol | 2 +- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/foundry.toml b/foundry.toml index fa3849d6..81924c28 100644 --- a/foundry.toml +++ b/foundry.toml @@ -4,10 +4,10 @@ bytecode_hash = "none" evm_version = "paris" extra_output = ["storageLayout"] - ffi = true fs_permissions = [ - { access = "read", path = "out-optimized" }, { access = "read", path = "./out" }, + { access = "read", path = "./out-optimized" }, + { access = "read", path = "package.json"}, { access = "read-write", path = "./cache" }, ] gas_reports = [ @@ -41,7 +41,6 @@ # Test the optimized contracts without re-compiling them [profile.test-optimized] - ffi = true src = "test" [doc] diff --git a/script/Base.s.sol b/script/Base.s.sol index 786566d2..a8ef7a85 100644 --- a/script/Base.s.sol +++ b/script/Base.s.sol @@ -7,9 +7,11 @@ import { Sphinx } from "@sphinx-labs/contracts/SphinxPlugin.sol"; import { console2 } from "forge-std/src/console2.sol"; import { Script } from "forge-std/src/Script.sol"; +import { stdJson } from "forge-std/src/StdJson.sol"; contract BaseScript is Script, Sphinx { using Strings for uint256; + using stdJson for string; /// @dev Included to enable compilation of the script without a $MNEMONIC environment variable. string internal constant TEST_MNEMONIC = "test test test test test test test test test test test junk"; @@ -73,18 +75,11 @@ contract BaseScript is Script, Sphinx { /// /// Notes: /// - The salt format is "ChainID , Version ". - /// - The version is obtained from `package.json` using the `ffi` cheatcode: - /// https://book.getfoundry.sh/cheatcodes/ffi - /// - Requires the `jq` CLI installed: https://jqlang.github.io/jq/ - function constructCreate2Salt() public returns (bytes32) { + /// - The version is obtained from `package.json`. + function constructCreate2Salt() public view returns (bytes32) { string memory chainId = block.chainid.toString(); - string[] memory inputs = new string[](4); - inputs[0] = "jq"; - inputs[1] = "-r"; - inputs[2] = ".version"; - inputs[3] = "./package.json"; - bytes memory result = vm.ffi(inputs); - string memory version = string(result); + string memory json = vm.readFile("package.json"); + string memory version = json.readString(".version"); string memory create2Salt = string.concat("ChainID ", chainId, ", Version ", version); console2.log("The CREATE2 salt is \"%s\"", create2Salt); return bytes32(abi.encodePacked(create2Salt)); diff --git a/test/utils/BaseScript.t.sol b/test/utils/BaseScript.t.sol index f445f21d..334f7364 100644 --- a/test/utils/BaseScript.t.sol +++ b/test/utils/BaseScript.t.sol @@ -11,7 +11,7 @@ contract BaseScript_Test is StdAssertions { BaseScript internal baseScript = new BaseScript(); - function test_ConstructCreate2Salt() public { + function test_ConstructCreate2Salt() public view { string memory chainId = block.chainid.toString(); string memory version = "1.1.1"; string memory salt = string.concat("ChainID ", chainId, ", Version ", version); From 1bf91e6da480c1c9f03cb67d957b72db35b3108a Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Fri, 3 May 2024 12:36:40 +0100 Subject: [PATCH 43/61] refactor: use `CREATE` instead of `CREATE2` (#339) * refactor: use CREATE instead of CREATE2 * test: use vm.computeCreateAddress instead --------- Co-authored-by: andreivladbrg --- .gas-snapshot | 92 ++++++------ precompiles/Precompiles.sol | 2 +- src/SablierV2MerkleLockupFactory.sol | 40 +---- .../ISablierV2MerkleLockupFactory.sol | 2 +- test/Base.t.sol | 138 +----------------- test/fork/merkle-lockup/MerkleLL.t.sol | 2 +- test/fork/merkle-lockup/MerkleLT.t.sol | 2 +- .../merkle-lockup/MerkleLockup.t.sol | 38 +---- .../create-merkle-ll/createMerkleLL.t.sol | 33 +---- .../create-merkle-ll/createMerkleLL.tree | 7 +- .../create-merkle-lt/createMerkleLT.t.sol | 21 +-- .../create-merkle-lt/createMerkleLT.tree | 7 +- 12 files changed, 67 insertions(+), 317 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index ec228115..8f89bfa4 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,55 +1,53 @@ -BaseScript_Test:test_ConstructCreate2Salt() (gas: 18701) -Claim_Integration_Test:test_Claim() (gas: 296792) -Claim_Integration_Test:test_Claim() (gas: 359541) -Claim_Integration_Test:test_Claim_CalculatedAmountsSumNotEqualClaimAmount() (gas: 1906979) -Claim_Integration_Test:test_RevertGiven_AlreadyClaimed() (gas: 274624) -Claim_Integration_Test:test_RevertGiven_AlreadyClaimed() (gas: 329502) -Claim_Integration_Test:test_RevertGiven_CampaignExpired() (gas: 17786) -Claim_Integration_Test:test_RevertGiven_CampaignExpired() (gas: 17811) -Clawback_Integration_Test:testFuzz_Clawback(address) (runs: 38, μ: 295825, ~: 295825) -Clawback_Integration_Test:testFuzz_Clawback(address) (runs: 38, μ: 350750, ~: 350750) -Clawback_Integration_Test:test_Clawback() (gas: 278339) -Clawback_Integration_Test:test_Clawback() (gas: 333264) -Clawback_Integration_Test:test_RevertGiven_CampaignNotExpired() (gas: 19523) -Clawback_Integration_Test:test_RevertGiven_CampaignNotExpired() (gas: 19523) -Constructor_MerkleLL_Integration_Test:test_Constructor() (gas: 1150963) -Constructor_MerkleLT_Integration_Test:test_Constructor() (gas: 1495600) -CreateMerkleLL_Integration_Test:testFuzz_CreateMerkleLL(address,uint40) (runs: 38, μ: 1122843, ~: 1123367) -CreateMerkleLL_Integration_Test:test_RevertGiven_CreatedAlready() (gas: 8937393460516730662) -CreateMerkleLT_Integration_Test:testFuzz_CreateMerkleLT(address,uint40) (runs: 38, μ: 1431123, ~: 1431123) -CreateMerkleLT_Integration_Test:test_RevertGiven_CreatedAlready() (gas: 8937393460516730766) -CreateWithDurationsLD_Integration_Test:test_BatchCreateWithDurations() (gas: 2021956) -CreateWithDurationsLL_Integration_Test:test_BatchCreateWithDurations() (gas: 1498705) -CreateWithDurationsLT_Integration_Test:test_BatchCreateWithDurations() (gas: 2007334) -CreateWithTimestampsLD_Integration_Test:test_BatchCreateWithTimestamps() (gas: 2005042) -CreateWithTimestampsLL_Integration_Test:test_BatchCreateWithTimestamps() (gas: 1497928) -CreateWithTimestampsLT_Integration_Test:test_BatchCreateWithTimestamps() (gas: 1991536) -HasClaimed_Integration_Test:test_HasClaimed() (gas: 263197) -HasClaimed_Integration_Test:test_HasClaimed() (gas: 318100) +BaseScript_Test:test_ConstructCreate2Salt() (gas: 29789) +Claim_Integration_Test:test_Claim() (gas: 296791) +Claim_Integration_Test:test_Claim() (gas: 359475) +Claim_Integration_Test:test_Claim_CalculatedAmountsSumNotEqualClaimAmount() (gas: 1902438) +Claim_Integration_Test:test_RevertGiven_AlreadyClaimed() (gas: 274580) +Claim_Integration_Test:test_RevertGiven_AlreadyClaimed() (gas: 329458) +Claim_Integration_Test:test_RevertGiven_CampaignExpired() (gas: 17764) +Claim_Integration_Test:test_RevertGiven_CampaignExpired() (gas: 17789) +Clawback_Integration_Test:testFuzz_Clawback(address) (runs: 28, μ: 295781, ~: 295781) +Clawback_Integration_Test:testFuzz_Clawback(address) (runs: 28, μ: 350706, ~: 350706) +Clawback_Integration_Test:test_Clawback() (gas: 278295) +Clawback_Integration_Test:test_Clawback() (gas: 333220) +Clawback_Integration_Test:test_RevertGiven_CampaignNotExpired() (gas: 19501) +Clawback_Integration_Test:test_RevertGiven_CampaignNotExpired() (gas: 19501) +Constructor_MerkleLL_Integration_Test:test_Constructor() (gas: 1150927) +Constructor_MerkleLT_Integration_Test:test_Constructor() (gas: 1495564) +CreateMerkleLL_Integration_Test:testFuzz_CreateMerkleLL(address,uint40) (runs: 28, μ: 1074513, ~: 1074513) +CreateMerkleLT_Integration_Test:testFuzz_CreateMerkleLT(address,uint40) (runs: 28, μ: 1368933, ~: 1368933) +CreateWithDurationsLD_Integration_Test:test_BatchCreateWithDurations() (gas: 2021978) +CreateWithDurationsLL_Integration_Test:test_BatchCreateWithDurations() (gas: 1498683) +CreateWithDurationsLT_Integration_Test:test_BatchCreateWithDurations() (gas: 2007290) +CreateWithTimestampsLD_Integration_Test:test_BatchCreateWithTimestamps() (gas: 2005062) +CreateWithTimestampsLL_Integration_Test:test_BatchCreateWithTimestamps() (gas: 1498016) +CreateWithTimestampsLT_Integration_Test:test_BatchCreateWithTimestamps() (gas: 1991514) +HasClaimed_Integration_Test:test_HasClaimed() (gas: 263153) +HasClaimed_Integration_Test:test_HasClaimed() (gas: 318056) HasClaimed_Integration_Test:test_HasClaimed_IndexNotInTree() (gas: 11174) HasClaimed_Integration_Test:test_HasClaimed_IndexNotInTree() (gas: 11196) +HasClaimed_Integration_Test:test_HasClaimed_NotClaimed() (gas: 16387) HasClaimed_Integration_Test:test_HasClaimed_NotClaimed() (gas: 16409) -HasClaimed_Integration_Test:test_HasClaimed_NotClaimed() (gas: 16431) +HasExpired_Integration_Test:test_HasExpired_ExpirationEqualToBlockTimestamp() (gas: 14670) HasExpired_Integration_Test:test_HasExpired_ExpirationEqualToBlockTimestamp() (gas: 14692) -HasExpired_Integration_Test:test_HasExpired_ExpirationEqualToBlockTimestamp() (gas: 14714) +HasExpired_Integration_Test:test_HasExpired_ExpirationGreaterThanBlockTimestamp() (gas: 14766) HasExpired_Integration_Test:test_HasExpired_ExpirationGreaterThanBlockTimestamp() (gas: 14788) -HasExpired_Integration_Test:test_HasExpired_ExpirationGreaterThanBlockTimestamp() (gas: 14810) HasExpired_Integration_Test:test_HasExpired_ExpirationLessThanBlockTimestamp() (gas: 8902) HasExpired_Integration_Test:test_HasExpired_ExpirationLessThanBlockTimestamp() (gas: 8924) -HasExpired_Integration_Test:test_HasExpired_ExpirationZero() (gas: 1051899) -HasExpired_Integration_Test:test_HasExpired_ExpirationZero() (gas: 1343143) -MerkleBuilder_Test:testFuzz_ComputeLeaf(uint256,address,uint128) (runs: 38, μ: 4323, ~: 4323) -MerkleBuilder_Test:testFuzz_ComputeLeaves((uint256,address,uint128)[]) (runs: 38, μ: 366364, ~: 398189) +HasExpired_Integration_Test:test_HasExpired_ExpirationZero() (gas: 1053478) +HasExpired_Integration_Test:test_HasExpired_ExpirationZero() (gas: 1343750) +MerkleBuilder_Test:testFuzz_ComputeLeaf(uint256,address,uint128) (runs: 28, μ: 4323, ~: 4323) +MerkleBuilder_Test:testFuzz_ComputeLeaves((uint256,address,uint128)[]) (runs: 28, μ: 292561, ~: 288103) Precompiles_Test:test_DeployBatchLockup() (gas: 3323914) -Precompiles_Test:test_DeployMerkleLockupFactory() (gas: 6986949) -Precompiles_Test:test_DeployPeriphery() (gas: 10310682) -USDC_CreateWithTimestamps_LockupDynamic_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLD((uint128,address,address,uint128,uint40,(uint128,uint64,uint40)[])) (runs: 38, μ: 34210026, ~: 30903106) -USDC_CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLL((uint128,(uint40,uint40,uint40),address,address,uint128)) (runs: 38, μ: 1592674, ~: 1502438) -USDC_CreateWithTimestamps_LockupTranched_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLT((uint128,address,address,uint128,uint40,(uint128,uint40)[])) (runs: 36, μ: 24927469, ~: 23804750) -USDC_MerkleLL_Fork_Test:testForkFuzz_MerkleLL((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 38, μ: 5247542, ~: 5027798) -USDC_MerkleLT_Fork_Test:testForkFuzz_MerkleLT((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 38, μ: 5622048, ~: 5399736) -USDT_CreateWithTimestamps_LockupDynamic_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLD((uint128,address,address,uint128,uint40,(uint128,uint64,uint40)[])) (runs: 38, μ: 30978443, ~: 21899958) -USDT_CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLL((uint128,(uint40,uint40,uint40),address,address,uint128)) (runs: 38, μ: 1953484, ~: 2211406) -USDT_CreateWithTimestamps_LockupTranched_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLT((uint128,address,address,uint128,uint40,(uint128,uint40)[])) (runs: 38, μ: 27146173, ~: 17675525) -USDT_MerkleLL_Fork_Test:testForkFuzz_MerkleLL((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 38, μ: 5214786, ~: 4997326) -USDT_MerkleLT_Fork_Test:testForkFuzz_MerkleLT((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 38, μ: 5585576, ~: 5365241) \ No newline at end of file +Precompiles_Test:test_DeployMerkleLockupFactory() (gas: 6699573) +Precompiles_Test:test_DeployPeriphery() (gas: 10022773) +USDC_CreateWithTimestamps_LockupDynamic_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLD((uint128,address,address,uint128,uint40,(uint128,uint64,uint40)[])) (runs: 28, μ: 35637382, ~: 36910130) +USDC_CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLL((uint128,(uint40,uint40,uint40),address,address,uint128)) (runs: 28, μ: 1731234, ~: 2052336) +USDC_CreateWithTimestamps_LockupTranched_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLT((uint128,address,address,uint128,uint40,(uint128,uint40)[])) (runs: 28, μ: 32364663, ~: 14997747) +USDC_MerkleLL_Fork_Test:testForkFuzz_MerkleLL((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 28, μ: 6023356, ~: 6239037) +USDC_MerkleLT_Fork_Test:testForkFuzz_MerkleLT((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 28, μ: 6380188, ~: 6588339) +USDT_CreateWithTimestamps_LockupDynamic_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLD((uint128,address,address,uint128,uint40,(uint128,uint64,uint40)[])) (runs: 28, μ: 32017272, ~: 28939343) +USDT_CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLL((uint128,(uint40,uint40,uint40),address,address,uint128)) (runs: 28, μ: 1776276, ~: 1948060) +USDT_CreateWithTimestamps_LockupTranched_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLT((uint128,address,address,uint128,uint40,(uint128,uint40)[])) (runs: 28, μ: 32063121, ~: 16796582) +USDT_MerkleLL_Fork_Test:testForkFuzz_MerkleLL((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 28, μ: 5989767, ~: 6205276) +USDT_MerkleLT_Fork_Test:testForkFuzz_MerkleLT((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 28, μ: 6345991, ~: 6556943) \ No newline at end of file diff --git a/precompiles/Precompiles.sol b/precompiles/Precompiles.sol index 0d938c30..dcb526c5 100644 --- a/precompiles/Precompiles.sol +++ b/precompiles/Precompiles.sol @@ -19,7 +19,7 @@ contract Precompiles { bytes public constant BYTECODE_BATCH_LOCKUP = hex"6080806040523461001657611f10908161001c8239f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c90816337266dd31461125b5750806349a32c4014610eb7578063606ef87514610b6b5780639e743f29146107fc578063a514f83e146104295763f7ca34eb1461006157600080fd5b346103555761006f366115d6565b9283156103ff5760009060005b8581106103cb57506001600160a01b036100999116918483611b81565b6100a284611858565b9260005b8581106100bf57604051806100bb8782611664565b0390f35b6100ca818786611b1e565b6100d3906118a7565b906100df818887611b1e565b6020016100eb906118a7565b85886100f8848284611b1e565b60400161010490611725565b61010f858385611b1e565b60600161011b906118bb565b610126868486611b1e565b608001610132906118bb565b9061013e878587611b1e565b60a00161014a90611b5e565b9287610157818789611b1e565b60c0810161016491611a2a565b96610170929198611b1e565b60e00196604051996101818b6117aa565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c086015236906101e592611a7e565b60e0840152366101f4916119cc565b610100830152604051917f31df3d48000000000000000000000000000000000000000000000000000000008352600483016020905280516001600160a01b0316602484015260208101516001600160a01b0316604484015260408101516fffffffffffffffffffffffffffffffff16606484015260608101516001600160a01b031660848401526080810151151560a484015260a0810151151560c484015260c081015164ffffffffff1660e48401528260e082015161010482016101409052805180610164840152610184830191602001906000905b80821061036e5750505081906103006101006020950151610124840190602080916001600160a01b0381511684520151910152565b03816000885af180156103625760009061032a575b600192506103238288611a05565b52016100a6565b506020823d60201161035a575b81610344602093836117ff565b810103126103555760019151610315565b600080fd5b3d9150610337565b6040513d6000823e3d90fd5b919493509160206060826103bc600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b019501920186939492916102cb565b916001906fffffffffffffffffffffffffffffffff6103f660406103f0878b8a611b1e565b01611725565b1601920161007c565b60046040517ff8bf106c000000000000000000000000000000000000000000000000000000008152fd5b346103555760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610355576104606116a0565b6104686115c0565b9060443567ffffffffffffffff8082116103555736602383011215610355578160040135116103555736602461014083600401350283010111610355578060040135156103ff576000805b826004013582106107c7576104d39150846001600160a01b038516611b81565b6104e08160040135611858565b9160005b826004013581106104fd57604051806100bb8682611664565b61051761051282856004013560248701611b70565b6118a7565b9083610536602061053084846004013560248601611b70565b016118a7565b61054d60406103f085856004013560248701611b70565b9361056b606061056586866004013560248801611b70565b016118bb565b946080956fffffffffffffffffffffffffffffffff6105968861056589896004013560248b01611b70565b926001600160a01b036101006105c78a6105b88160048d013560248e01611b70565b9a602481600401359101611b70565b019681604051976105d789611742565b1687521660208601521660408401526001600160a01b038b166060840152151586830152151560a082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6084360301126103555761075c9261067f60e093604051610644816117c7565b61065060a08501611939565b815261066f8660c095610664878201611939565b602085015201611939565b60408201528385015236906119cc565b83830152604051957f53b157270000000000000000000000000000000000000000000000000000000087526001600160a01b0383511660048801526001600160a01b0360208401511660248801526fffffffffffffffffffffffffffffffff60408401511660448801526001600160a01b0360608401511660648801528201511515608487015260a0820151151560a4870152810151604064ffffffffff918281511660c48901528260208201511660e48901520151166101048601520151610124840190602080916001600160a01b0381511684520151910152565b6020826101648160006001600160a01b0388165af1801561036257600090610794575b6001925061078d8287611a05565b52016104e4565b506020823d6020116107bf575b816107ae602093836117ff565b81010312610355576001915161077f565b3d91506107a1565b6001906fffffffffffffffffffffffffffffffff6107f260406103f086886004013560248a01611b70565b16019101906104b3565b346103555761080a366115d6565b9283156103ff5760009060005b858110610b3d57506001600160a01b036108349116918483611b81565b61083d84611858565b9260005b85811061085657604051806100bb8782611664565b610861818786611b1e565b61086a906118a7565b90610876818887611b1e565b602001610882906118a7565b858861088f848284611b1e565b60400161089b90611725565b6108a6858385611b1e565b6060016108b2906118bb565b6108bd868486611b1e565b6080016108c9906118bb565b906108d5878587611b1e565b60a0016108e190611b5e565b92876108ee818789611b1e565b60c081016108fb916118c8565b96610907929198611b1e565b60e00196604051996109188b6117aa565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c0860152369061097c9261194b565b60e08401523661098b916119cc565b610100830152604051917f32fbe22b000000000000000000000000000000000000000000000000000000008352600483016020905280516001600160a01b0316602484015260208101516001600160a01b0316604484015260408101516fffffffffffffffffffffffffffffffff16606484015260608101516001600160a01b031660848401526080810151151560a484015260a0810151151560c484015260c081015164ffffffffff1660e48401528260e082015161010482016101409052805180610164840152610184830191602001906000905b808210610af4575050508190610a976101006020950151610124840190602080916001600160a01b0381511684520151910152565b03816000885af1801561036257600090610ac1575b60019250610aba8288611a05565b5201610841565b506020823d602011610aec575b81610adb602093836117ff565b810103126103555760019151610aac565b3d9150610ace565b91949350916020604082610b2e600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291610a62565b916001906fffffffffffffffffffffffffffffffff610b6260406103f0878b8a611b1e565b16019201610817565b3461035557610b79366115d6565b9283156103ff5760009060005b858110610e8957506001600160a01b03610ba39116918483611b81565b610bac84611858565b9260005b858110610bc557604051806100bb8782611664565b610bd08187866116b6565b610bd9906118a7565b90610be58188876116b6565b602001610bf1906118a7565b8782610bfe81838a6116b6565b604001610c0a90611725565b610c1582848b6116b6565b606001610c21906118bb565b610c2c83858c6116b6565b608001610c38906118bb565b91610c4484868d6116b6565b60a08101610c5191611a2a565b94610c5d91968d6116b6565b60c0019560405198610c6e8a611742565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a08601523690610cc692611a7e565b60c084015236610cd5916119cc565b60e0830152604051917f54c02292000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001906000905b808210610e2c575050508190610dcf60e06020950151610104840190602080916001600160a01b0381511684520151910152565b03816000885af1801561036257600090610df9575b60019250610df28288611a05565b5201610bb0565b506020823d602011610e24575b81610e13602093836117ff565b810103126103555760019151610de4565b3d9150610e06565b91949350916020606082610e7a600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b01950192018693949291610d9b565b916001906fffffffffffffffffffffffffffffffff610eae60406103f0878b8a6116b6565b16019201610b86565b346103555760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035557610eee6116a0565b610ef66115c0565b9060443567ffffffffffffffff8082116103555736602383011215610355578160040135116103555736602461012083600401350283010111610355578060040135156103ff576000805b8260040135821061122657610f619150846001600160a01b038516611b81565b610f6e8160040135611858565b9160005b82600401358110610f8b57604051806100bb8682611664565b610fa061051282856004013560248701611a19565b9083610fb9602061053084846004013560248601611a19565b610fd060406103f085856004013560248701611a19565b93610fe8606061056586866004013560248801611a19565b946080956fffffffffffffffffffffffffffffffff6110138861056589896004013560248b01611a19565b926001600160a01b0360e06110438a6110348160048d013560248e01611a19565b9a602481600401359101611a19565b0196816040519761105389611742565b1687521660208601521660408401526001600160a01b038b166060840152151586830152151560a082015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff608436030112610355576111bb926110eb60e0936040516110c08161178e565b6110cc60a08501611939565b81526110db60c0809501611939565b60208201528385015236906119cc565b83830152604051957fab167ccc0000000000000000000000000000000000000000000000000000000087526001600160a01b0383511660048801526001600160a01b0360208401511660248801526fffffffffffffffffffffffffffffffff60408401511660448801526001600160a01b0360608401511660648801528201511515608487015260a0820151151560a4870152810151602064ffffffffff918281511660c489015201511660e48601520151610104840190602080916001600160a01b0381511684520151910152565b6020826101448160006001600160a01b0388165af18015610362576000906111f3575b600192506111ec8287611a05565b5201610f72565b506020823d60201161121e575b8161120d602093836117ff565b8101031261035557600191516111de565b3d9150611200565b6001906fffffffffffffffffffffffffffffffff61125160406103f086886004013560248a01611a19565b1601910190610f41565b3461035557611269366115d6565b93849391929315611598575060009060005b85811061156a57506001600160a01b036112989116918483611b81565b6112a184611858565b9260005b8581106112ba57604051806100bb8782611664565b6112c58187866116b6565b6112ce906118a7565b906112da8188876116b6565b6020016112e6906118a7565b87826112f381838a6116b6565b6040016112ff90611725565b61130a82848b6116b6565b606001611316906118bb565b61132183858c6116b6565b60800161132d906118bb565b9161133984868d6116b6565b60a08101611346916118c8565b9461135291968d6116b6565b60c00195604051986113638a611742565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a086015236906113bb9261194b565b60c0840152366113ca916119cc565b60e0830152604051917f897f362b000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001906000905b8082106115215750505081906114c460e06020950151610104840190602080916001600160a01b0381511684520151910152565b03816000885af18015610362576000906114ee575b600192506114e78288611a05565b52016112a5565b506020823d602011611519575b81611508602093836117ff565b8101031261035557600191516114d9565b3d91506114fb565b9194935091602060408261155b600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291611490565b916001906fffffffffffffffffffffffffffffffff61158f60406103f0878b8a6116b6565b1601920161127b565b807ff8bf106c0000000000000000000000000000000000000000000000000000000060049252fd5b602435906001600160a01b038216820361035557565b9060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc830112610355576001600160a01b0391600435838116810361035557926024359081168103610355579160443567ffffffffffffffff9283821161035557806023830112156103555781600401359384116103555760248460051b83010111610355576024019190565b602090602060408183019282815285518094520193019160005b82811061168c575050505090565b83518552938101939281019260010161167e565b600435906001600160a01b038216820361035557565b91908110156116f65760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0181360301821215610355570190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b356fffffffffffffffffffffffffffffffff811681036103555790565b610100810190811067ffffffffffffffff82111761175f57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff82111761175f57604052565b610120810190811067ffffffffffffffff82111761175f57604052565b6060810190811067ffffffffffffffff82111761175f57604052565b6080810190811067ffffffffffffffff82111761175f57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761175f57604052565b67ffffffffffffffff811161175f5760051b60200190565b9061186282611840565b61186f60405191826117ff565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061189d8294611840565b0190602036910137565b356001600160a01b03811681036103555790565b3580151581036103555790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610355570180359067ffffffffffffffff821161035557602001918160061b3603831361035557565b35906fffffffffffffffffffffffffffffffff8216820361035557565b359064ffffffffff8216820361035557565b92919261195782611840565b60409261196760405192836117ff565b819581835260208093019160061b84019381851161035557915b84831061199057505050505050565b85838303126103555783869182516119a78161178e565b6119b08661191c565b81526119bd838701611939565b83820152815201920191611981565b9190826040910312610355576040516119e48161178e565b809280356001600160a01b0381168103610355578252602090810135910152565b80518210156116f65760209160051b010190565b91908110156116f657610120020190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610355570180359067ffffffffffffffff82116103555760200191606082023603831361035557565b929192611a8a82611840565b604094611a9a60405192836117ff565b8195848352602080930191606080960285019481861161035557925b858410611ac65750505050505050565b868483031261035557825190611adb826117c7565b611ae48561191c565b8252858501359067ffffffffffffffff8216820361035557828792838b950152611b0f868801611939565b86820152815201930192611ab6565b91908110156116f65760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee181360301821215610355570190565b3564ffffffffff811681036103555790565b91908110156116f657610140020190565b90604080516020907f23b872dd000000000000000000000000000000000000000000000000000000008282015233602482015260449030828201528660648201526064815260a081019080821067ffffffffffffffff83111761175f57611bea91855285611d86565b6001600160a01b0394858516958451917fdd62ed3e0000000000000000000000000000000000000000000000000000000083523060048401521690816024820152838184818a5afa908115611d6357908891600091611d32575b5010611c54575b50505050505050565b8351956000808589017f095ea7b3000000000000000000000000000000000000000000000000000000009a8b82528560248c0152868b0152858a52611c988a6117e3565b89519082855af190611ca8611e12565b82611cff575b5081611cf4575b50611c4b57611ce896611ce394519384015260248301526000818301528152611cdd816117e3565b82611d86565b611d86565b38808080808080611c4b565b90503b151538611cb5565b809192505190858215928315611d1a575b5050509038611cae565b611d2a9350820181019101611d6e565b388581611d10565b809250858092503d8311611d5c575b611d4b81836117ff565b810103126103555787905138611c44565b503d611d41565b85513d6000823e3d90fd5b90816020910312610355575180151581036103555790565b6000806001600160a01b03611db093169360208151910182865af1611da9611e12565b9083611e70565b8051908115159182611df7575b5050611dc65750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b611e0a9250602080918301019101611d6e565b153880611dbd565b3d15611e6b573d9067ffffffffffffffff821161175f5760405191611e5f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846117ff565b82523d6000602084013e565b606090565b90611eaf5750805115611e8557805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580611efa575b611ec0575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15611eb856fea164736f6c6343000817000a"; bytes public constant BYTECODE_MERKLE_LOCKUP_FACTORY = - hex"6080806040523461001657614394908161001c8239f35b600080fdfe604060805260c06080515260043610156200001957600080fd5b6000803560e01c80631e32387614620004705763769bed20146200003c57600080fd5b346200046d5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126200046d5767ffffffffffffffff60043581811162000465576200009190369060040162000aa0565b9173ffffffffffffffffffffffffffffffffffffffff908160243516602435036200046d576080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126200046d576080515192620000f28462000972565b64ffffffffff60443581811681036200046957855260643590811681036200046557602060a05260a051850152845160a0518601511515608051870151916200015f6060890151926200018c60808b01516080515193849160a05160a05184015260805183019062000bf8565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101845283620009be565b60a08a015191620001d460c08c0151620001ce6080515182620001bd829451809260a05185019060a0510162000bd3565b81010380835260a0510182620009be565b62000c3d565b60e08c01511515916080515195620002058d60a0518901906020908164ffffffffff91828151168552015116910152565b608051875260608701978789108b8a1117620004385791620002fb9795939160a39795938a608051527fffffffffff000000000000000000000000000000000000000000000000000000608089019c8d977fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009889809660601b16905260f81b60948b015260d81b16609589015260601b16609a870152620002b28151809260ae89019060a0510162000bd3565b85019360ae85015260ce84015260f81b60ee83015260243560601b1660ef820152815190620002eb82610103830160a051860162000bd3565b01036083810184520182620009be565b519020608051519091611812808301918211838310176200040b5790829162000d618339608081526200036062000336608083018a62000cd1565b91876024351660a05182015260805101886020908164ffffffffff91828151168552015116910152565b039083f5908115620003fe5750620003de93827f2ba0fe49588281dbb122dd3b7f3e2b3396338f70dbe3c62bf3e3888b4ba7ffb893620003b493169586956080515194859460c0865260c086019062000cd1565b926024351660a0518501526080518401906020908164ffffffffff91828151168552015116910152565b608435608083015260a43560a08301520390a26080515190815260a05190f35b60805151903d90823e3d90fd5b6024857f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b60248c7f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b8280fd5b8380fd5b80fd5b50346200046d5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126200046d5767ffffffffffffffff6004358181116200046557620004c690369060040162000aa0565b9160249081359273ffffffffffffffffffffffffffffffffffffffff92838516928386036200046d57604435968388116200096e57366023890112156200096e57876004013594848611620009425760209860805151966200052e8b8260051b0189620009be565b808852858b89019160061b830101913683116200093e579398938601905b828210620008e8575050508296839284985b88518a1015620005d6578780620005768c8c62000b8f565b515116911601878111620005aa576001909464ffffffffff8d6200059b8d8d62000b8f565b5101511601990198936200055e565b86867f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b8794979196939a9298995016670de0b6b3a76400008103620008b7575086518a898982810151151593608051820151946060830151966080840151926080515194858881019589875260805182016200062f9162000bf8565b03947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0958681018852620006649088620009be565b60a08201519660c08301518a60805151828193519081848085019201916200068c9262000bd3565b810103808352016200069f9082620009be565b620006aa9062000c3d565b9260e001511515936080515197888c81019b8d8d526080518201620006cf9162000c7f565b039081018952620006e19089620009be565b608051519c8d9c8d9c8d019e8f997fffffffffffffffffffffffffffffffffffffffff0000000000000000000000009a8b809660601b16905260f81b906034015260d81b7fffffffffff0000000000000000000000000000000000000000000000000000001660358d015260601b16603a8b0152519081604e8b01620007679262000bd3565b880195604e870152606e86015260f81b608e85015260601b16608f83015251918260a38301620007979262000bd3565b010360838101825260a301620007ae9082620009be565b519020916080515191611e159081840192848410908411176200088c57508291620008029162002573843960608152620007ec606082018a62000cd1565b908b8d8201528082039060805101528962000c7f565b039083f5908115620003fe5750916200086a7ffe44018cf74992b2720702385a1728bd329dd136e4f651203176c81c12710a8b94926200085694169687966080515195869560c0875260c087019062000cd1565b918a86015284820360805186015262000c7f565b906060830152606435608083015260843560a08301520390a260805151908152f35b857f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b905060805151907f1b05170a0000000000000000000000000000000000000000000000000000000082526004820152fd5b608099949951823603126200093e5760805151620009068162000972565b823589811681036200093a5781528c91906200092484840162000a13565b838201528152019060805101909893986200054c565b8780fd5b8580fd5b50507f4e487b710000000000000000000000000000000000000000000000000000000081526041600452fd5b5080fd5b6040810190811067ffffffffffffffff8211176200098f57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176200098f57604052565b3590811515820362000a0e57565b600080fd5b359064ffffffffff8216820362000a0e57565b81601f8201121562000a0e5780359067ffffffffffffffff82116200098f576040519262000a7d60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8601160185620009be565b8284526020838301011162000a0e57816000926020809301838601378301015290565b91909161010090818185031262000a0e5760405191820167ffffffffffffffff90838110828211176200098f576040528294823573ffffffffffffffffffffffffffffffffffffffff90818116810362000a0e57855262000b046020850162000a00565b602086015262000b176040850162000a13565b60408601526060840135908116810362000a0e576060850152608083013582811162000a0e578162000b4b91850162000a26565b608085015260a083013560a085015260c083013591821162000a0e578262000b7e60e0949262000b8a9486940162000a26565b60c08601520162000a00565b910152565b805182101562000ba45760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60005b83811062000be75750506000910152565b818101518382015260200162000bd6565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209362000c368151809281875287808801910162000bd3565b0116010190565b60208151910151906020811062000c52575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b90815180825260208080930193019160005b82811062000ca0575050505090565b8351805167ffffffffffffffff16865282015164ffffffffff16858301526040909401939281019260010162000c91565b9060e08062000d5562000d3861010073ffffffffffffffffffffffffffffffffffffffff80885116875260208801511515602088015264ffffffffff6040890151166040880152606088015116606087015260808701519080608088015286019062000bf8565b60a086015160a086015260c086015185820360c087015262000bf8565b93015115159101529056fe6101608060405234620005ba57620018128038038091620000218285620005db565b833981019080820360808112620005ba5781516001600160401b038111620005ba5782019061010082850312620005ba576040519361010085016001600160401b03811186821017620005a45760405282516001600160a01b0381168103620005ba5785526200009460208401620005ff565b9260208601938452620000aa604082016200060d565b604087019081526060820151909690926001600160a01b0384168403620005ba576060820193845260808301516001600160401b038111620005ba5781620000f491850162000661565b6080830190815260a08085015190840190815260c085015191949092906001600160401b038311620005ba576200013460e0926200014094830162000661565b60c086015201620005ff565b60e083019081526020880151959093906001600160a01b0387168703620005ba57604090603f190112620005ba5760408051989089016001600160401b0381118a821017620005a457620001ab91606091604052620001a2604082016200060d565b8b52016200060d565b946020890195865260c0840151516020811162000585575051600080546001600160a01b0319166001600160a01b0392831617815584519091166080529651151560a052975164ffffffffff90811660c052975180516001600160401b038111620005715760019182548381811c9116801562000566575b60208210146200055257601f811162000505575b50602090601f8311600114620004995760c095949392918a91836200048d575b5050600019600383901b1c191690821b1790555b5160e05201516040516200029f602082816200029181830196878151938492016200063c565b8101038084520182620005db565b51905190602081106200047a575b50610100525115159461012095865261014094838652511669ffffffffff0000000000600354925160281b169160018060501b031916171760035560018060a01b03608051166040519160208301848063095ea7b360e01b9283815260018060a01b03851660248801526000196044880152604487526200032e87620005bf565b86519082875af16200033f620006ae565b8162000439575b50806200042e575b15620003e2575b60405161104290878983620007d08439608051838181610470015281816107440152610c36015260a05183818161076b0152610b5c015260c05183818161015d01528181610aa801528181610d8f0152610f60015260e0518381816102e2015261066201526101005183610e250152518281816107930152610b1f0152518181816101ae01526109060152f35b62000423946200041d9260405192602084015260018060a01b031660248301526044820152604481526200041681620005bf565b82620006e3565b620006e3565b388080808062000355565b50823b15156200034e565b805180159250821562000450575b50503862000346565b8192509060209181010312620004765760206200046e9101620005ff565b388062000447565b8580fd5b6000199060200360031b1b1638620002ad565b01519050388062000257565b838a5260208a209190601f1984168b5b818110620004ee575091859492918360c0999897959310620004d4575b505050811b0190556200026b565b015160001960f88460031b161c19169055388080620004c6565b8284015185559386019360209384019301620004a9565b838a5260208a20601f840160051c8101916020851062000547575b601f0160051c019084905b8281106200053b57505062000237565b8b81550184906200052b565b909150819062000520565b634e487b7160e01b8a52602260045260248afd5b90607f169062000223565b634e487b7160e01b88526041600452602488fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b634e487b7160e01b600052604160045260246000fd5b600080fd5b608081019081106001600160401b03821117620005a457604052565b601f909101601f19168101906001600160401b03821190821017620005a457604052565b51908115158203620005ba57565b519064ffffffffff82168203620005ba57565b6001600160401b038111620005a457601f01601f191660200190565b60005b838110620006505750506000910152565b81810151838201526020016200063f565b81601f82011215620005ba5780516200067a8162000620565b926200068a6040519485620005db565b81845260208284010111620005ba57620006ab91602080850191016200063c565b90565b3d15620006de573d90620006c28262000620565b91620006d26040519384620005db565b82523d6000602084013e565b606090565b600080620007109260018060a01b03169360208151910182865af162000708620006ae565b908362000767565b805190811515918262000740575b5050620007285750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620005ba5760206200075e9101620005ff565b1538806200071e565b906200079057508051156200077e57805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580620007c5575b620007a3575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156200079a56fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde0314610e0e575080631686c90914610b8157806316c3549d14610b445780631bfd681414610b075780633bfe03a814610ad85780633f31ae3f146104945780634800d97f1461044357806349fc73dd1461030557806351e75e8b146102ca57806375829def146101ed57806390e64d13146101d25780639e93e57714610181578063bb4b57341461013f578063ce516507146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760206003193601126100f857602061013560043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f8576020610135610f58565b346100f85760206003193601126100f857610206610ebc565b60005473ffffffffffffffffffffffffffffffffffffffff8082169233840361027d577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c9060018316928315610439575b602093848410811461040a578386529081156103cc5750600114610371575b61036d8461036181880382610f17565b60405191829182610e56565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8284106103b9575050508161036d936103619282010193610351565b805485850187015292850192810161039d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103618161036d610351565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691610332565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f857604435906fffffffffffffffffffffffffffffffff821682036100f85767ffffffffffffffff606435116100f8573660236064350112156100f85767ffffffffffffffff60643560040135116100f8573660246064356004013560051b6064350101116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff831660408201526fffffffffffffffffffffffffffffffff841660608201526060815261057a81610efb565b519020604051602081019182526020815261059481610edf565b519020916105a0610f58565b610a7a576105c960043560ff6001918060081c6000526002602052161b60406000205416151590565b610a4857604051926105e760206064356004013560051b0185610f17565b60643560048101358552602401602085015b60246064356004013560051b60643501018210610a38575050906000915b845183101561065e5760208360051b860101519081811060001461064b57600052602052600160406000205b920191610617565b9060005260205260016040600020610643565b83907f000000000000000000000000000000000000000000000000000000000000000003610a0e5760043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff60005416906040516106cd81610edf565b600081526000602082015260405192610100840184811067ffffffffffffffff8211176109df57604052835273ffffffffffffffffffffffffffffffffffffffff821660208401526fffffffffffffffffffffffffffffffff8416604084015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608401527f0000000000000000000000000000000000000000000000000000000000000000151560808401527f0000000000000000000000000000000000000000000000000000000000000000151560a08401526040516107c581610edf565b64ffffffffff600354818116835260281c16602082015260c084015260e0830152602060e0604051937fab167ccc00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff815116600486015273ffffffffffffffffffffffffffffffffffffffff838201511660248601526fffffffffffffffffffffffffffffffff604082015116604486015273ffffffffffffffffffffffffffffffffffffffff606082015116606486015260808101511515608486015260a0810151151560a486015264ffffffffff8360c08301518281511660c489015201511660e4860152015173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015260208261014481600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af19182156109d35760009261099e575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d6020116109cb575b816109b960209383610f17565b810103126100f8579051906020610937565b3d91506109ac565b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b81358152602091820191016105f9565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f857604060035464ffffffffff825191818116835260281c166020820152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857610b9a610ebc565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff8060005416338103610dbf5750610be1610f58565b15610d61576040519060209160008083858401977fa9059cbb000000000000000000000000000000000000000000000000000000008952169687602485015286604485015260448452610c3384610efb565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610d55573d67ffffffffffffffff81116109df57610cbc9160405191610cac877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610f17565b82523d60008784013e5b83610f95565b8051848115159182610d34575b50509050610d035750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488610cc9565b610cbc90606090610cb6565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f85761036d907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261036181610edf565b60208082528251818301819052939260005b858110610ea8575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201610e68565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff8211176109df57604052565b6080810190811067ffffffffffffffff8211176109df57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176109df57604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081610f8d575090565b905042101590565b90610fd45750805115610faa57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b8151158061102c575b610fe5575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15610fdd56fea164736f6c6343000817000a6101608060405234620006e95762001e1590813803809262000022828462000735565b82396060818381010312620006e95780516001600160401b038111620006e9578101916101009283818385010312620006e957604051908482016001600160401b03811183821017620007035760405280516001600160a01b0381168103620006e9578252620000956020820162000759565b6020830152620000a86040820162000767565b604083015260608101516001600160a01b0381168103620006e957606083015260808101516001600160401b038111620006e957620000ed90848601908301620007bb565b608083015260a0818101519083015260c08101516001600160401b038111620006e95762000134916200012860e092868801908301620007bb565b60c08501520162000759565b60e08201526020830151916001600160a01b0383168303620006e9576040840151906001600160401b038211620006e957808501601f838701011215620006e95784820151906001600160401b0382116200070357604051956200019f60208460051b018862000735565b828752602087019382820160208560061b838501010111620006e95793602085830101945b60208560061b82850101018610620006835750505050505060c081015151602081116200066457506060810151600080546001600160a01b0319166001600160a01b03928316178155825190911660809081526020830151151560a052604083015164ffffffffff1660c0528201518051919290916001600160401b038111620006505760019283548481811c9116801562000645575b60208210146200063157601f8111620005e4575b50602090601f83116001146200057a5760e09392918691836200056e575b5050600019600383901b1c191690841b1783555b60a0810151825260c0810151604051620002db60208281620002cd818301968781519384920162000796565b810103808452018262000735565b51905190602081106200055b575b5087520151151593610120948552610140938452805190835b82811062000499575050505060018060a01b036080511660018060a01b03835116906040519160208301848063095ea7b360e01b92838152846024880152600019604488015260448752620003578762000719565b86519082875af16200036862000808565b8162000458575b50806200044d575b1562000409575b6040516114eb9087898b846200092a85396080518481816104e2015281816108550152610f1c015260a05184818161087c0152610e42015260c05184818161022001528181610dbd015281816110750152611246015260e05184818161035401526106ba0152518361110b0152518281816108a40152610e0501525181818161012a0152610a0e0152f35b62000442946200043c926040519260208401526024830152604482015260448152620004358162000719565b826200083d565b6200083d565b38808080806200037e565b50823b151562000377565b80518015925082156200046f575b5050386200036f565b8192509060209181010312620004955760206200048d910162000759565b388062000466565b8580fd5b8151811015620005475760208160051b8301015160038054906801000000000000000082101562000533578682018082558210156200051f578752602080882083519201805493909101516cffffffffff000000000000000060409190911b166001600160401b039092166001600160681b031990931692909217179055830162000302565b634e487b7160e01b88526032600452602488fd5b634e487b7160e01b88526041600452602488fd5b634e487b7160e01b85526032600452602485fd5b6000199060200360031b1b1638620002e9565b0151905038806200028d565b92918491601f198216908388526020882091885b818110620005cb5750958360e09710620005b1575b505050811b018355620002a1565b015160001960f88460031b161c19169055388080620005a3565b828801518455889590930192602092830192016200058e565b84865260208620601f840160051c8101916020851062000626575b601f0160051c019085905b8281106200061a5750506200026f565b8781550185906200060a565b9091508190620005ff565b634e487b7160e01b86526022600452602486fd5b90607f16906200025b565b634e487b7160e01b84526041600452602484fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b6040868585010312620006e957604080519081016001600160401b03811182821017620006ee5760405286516001600160401b0381168103620006e95760209382859260409452620006d7838b0162000767565b838201528152019601959150620001c4565b600080fd5b60246000634e487b7160e01b81526041600452fd5b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b038211176200070357604052565b601f909101601f19168101906001600160401b038211908210176200070357604052565b51908115158203620006e957565b519064ffffffffff82168203620006e957565b6001600160401b0381116200070357601f01601f191660200190565b60005b838110620007aa5750506000910152565b818101518382015260200162000799565b81601f82011215620006e9578051620007d4816200077a565b92620007e4604051948562000735565b81845260208284010111620006e95762000805916020808501910162000796565b90565b3d1562000838573d906200081c826200077a565b916200082c604051938462000735565b82523d6000602084013e565b606090565b6000806200086a9260018060a01b03169360208151910182865af16200086262000808565b9083620008c1565b80519081151591826200089a575b5050620008825750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620006e9576020620008b8910162000759565b15388062000878565b90620008ea5750805115620008d857805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806200091f575b620008fd575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15620008f456fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde03146110f4575080631686c90914610e6757806316c3549d14610e2a5780631bfd681414610ded5780633f31ae3f146105065780634800d97f146104b557806349fc73dd1461037757806351e75e8b1461033c57806375829def1461025f57806390e64d1314610244578063bb4b573414610202578063bf4ed03f14610190578063ce5165071461014e578063da792468146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760206003193601126100f857602061018660043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f8576101a9611293565b6040516020918282018383528151809152836040840192019360005b8281106101d25784840385f35b8551805167ffffffffffffffff16855282015164ffffffffff1684830152948101946040909301926001016101c5565b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f857602061018661123e565b346100f85760206003193601126100f8576102786111a2565b60005473ffffffffffffffffffffffffffffffffffffffff808216923384036102ef577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c90600183169283156104ab575b602093848410811461047c5783865290811561043e57506001146103e3575b6103df846103d3818803826111fd565b6040519182918261113c565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b82841061042b57505050816103df936103d392820101936103c3565b805485850187015292850192810161040f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103d3816103df6103c3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f16916103a4565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f8576fffffffffffffffffffffffffffffffff60443516604435036100f85767ffffffffffffffff606435116100f8573660236064350112156100f857606435600401359067ffffffffffffffff82116100f85760248260051b60643501013681116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff841660408201526fffffffffffffffffffffffffffffffff604435166060820152606081526105eb816111e1565b5190206040516020810191825260208152610605816111c5565b5190209061061161123e565b610d8f5761063a60043560ff6001918060081c6000526002602052161b60406000205416151590565b610d5d576106478461127b565b9361065560405195866111fd565b8452606435602401602085015b828210610d4d575050506000905b83518210156106b657610683828561132e565b5190818110156106a357600052602052600160406000205b910190610670565b906000526020526001604060002061069b565b90507f000000000000000000000000000000000000000000000000000000000000000003610d23576106e6611293565b90600082516106f48161127b565b9361070260405195866111fd565b8185527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061072f8361127b565b0160005b818110610cfe5750506000905b828210610bd95750506fffffffffffffffffffffffffffffffff82166fffffffffffffffffffffffffffffffff604435168111610baa576fffffffffffffffffffffffffffffffff6044351611610b56575b505060043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff6000541691604051906107dc826111c5565b600082526000602083015260405193610100850185811067ffffffffffffffff821117610b2757604052845273ffffffffffffffffffffffffffffffffffffffff831660208501526fffffffffffffffffffffffffffffffff60443516604085015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608501527f0000000000000000000000000000000000000000000000000000000000000000151560808501527f0000000000000000000000000000000000000000000000000000000000000000151560a085015260c084015260e0830152604051917f897f362b0000000000000000000000000000000000000000000000000000000083526020600484015282610144810173ffffffffffffffffffffffffffffffffffffffff835116602483015273ffffffffffffffffffffffffffffffffffffffff60208401511660448301526fffffffffffffffffffffffffffffffff604084015116606483015273ffffffffffffffffffffffffffffffffffffffff60608401511660848301526080830151151560a483015260a0830151151560c483015260c08301519061012060e484015281518091526020610164840192019060005b818110610ae9575050508190602060e08195015173ffffffffffffffffffffffffffffffffffffffff81511661010485015201516101248301520381600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1918215610add57600092610aa8575b60208380847f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff81519360043585526fffffffffffffffffffffffffffffffff60443516888601521692a3604051908152f35b91506020823d602011610ad5575b81610ac3602093836111fd565b810103126100f8576020915191610a3f565b3d9150610ab6565b6040513d6000823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff1681860152889550604090940193909201916001016109b9565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6fffffffffffffffffffffffffffffffff610b947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8293018661132e565b5192604435031681835116011690528280610792565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b9092610c0e67ffffffffffffffff610bf1868561132e565b5151166fffffffffffffffffffffffffffffffff60443516611371565b6fffffffffffffffffffffffffffffffff8111610ccd576fffffffffffffffffffffffffffffffff8091169164ffffffffff6020610c4c888761132e565b5101511660405190610c5d826111c5565b8482526020820152610c6f878a61132e565b52610c7a868961132e565b5016016fffffffffffffffffffffffffffffffff8111610c9e579260010190610740565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b602490604051907f4916adce0000000000000000000000000000000000000000000000000000000082526004820152fd5b602090604051610d0d816111c5565b6000815260008382015282828a01015201610733565b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b8135815260209182019101610662565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857610e806111a2565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff80600054163381036110a55750610ec761123e565b15611047576040519060209160008083858401977fa9059cbb000000000000000000000000000000000000000000000000000000008952169687602485015286604485015260448452610f19846111e1565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d1561103b573d67ffffffffffffffff8111610b2757610fa29160405191610f92877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846111fd565b82523d60008784013e5b8361143e565b805184811515918261101a575b50509050610fe95750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488610faf565b610fa290606090610f9c565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f8576103df907f00000000000000000000000000000000000000000000000000000000000000006020820152602081526103d3816111c5565b60208082528251818301819052939260005b85811061118e575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b81810183015184820160400152820161114e565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff821117610b2757604052565b6080810190811067ffffffffffffffff821117610b2757604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610b2757604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081611273575090565b905042101590565b67ffffffffffffffff8111610b275760051b60200190565b600354906112a08261127b565b9160406112b060405194856111fd565b8184528360208091019160036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b906000935b8585106112f457505050505050565b60018481928451611304816111c5565b64ffffffffff875467ffffffffffffffff81168352871c16838201528152019301940193916112e5565b80518210156113425760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8382098382029182808310920391808303921461142d57670de0b6b3a764000090818310156113f657947faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac1066994950990828211900360ee1b910360121c170290565b60449086604051917f5173648d00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5050670de0b6b3a764000090049150565b9061147d575080511561145357805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b815115806114d5575b61148e575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b1561148656fea164736f6c6343000817000aa164736f6c6343000817000a"; + hex"6080806040523461001657613f43908161001c8239f35b600080fdfe604060808152600490813610156200001657600080fd5b600091823560e01c80631e32387614620002425763769bed20146200003a57600080fd5b346200023e5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126200023e5767ffffffffffffffff9281358481116200023a576200008e90369084016200069b565b916024359173ffffffffffffffffffffffffffffffffffffffff918284168094036200023a57857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126200023a57620000e86200056a565b9664ffffffffff604435818116810362000236578952606435908116810362000232576020890152865191611812908184019284841090841117620002065750908291620009108339608081526200016b8862000149608084018a6200082e565b92886020820152018a6020908164ffffffffff91828151168552015116910152565b039082f0908115620001fb576020620001e287897f2ba0fe49588281dbb122dd3b7f3e2b3396338f70dbe3c62bf3e3888b4ba7ffb8620001c08a8a8a8a16968795875194859460c0865260c08601906200082e565b928a850152878401906020908164ffffffffff91828151168552015116910152565b608435608083015260a43560a08301520390a251908152f35b8551903d90823e3d90fd5b8460416024927f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8380fd5b8480fd5b5080fd5b8280fd5b508234620005675760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112620005675767ffffffffffffffff9082358281116200023a576200029890369085016200069b565b91602480359273ffffffffffffffffffffffffffffffffffffffff958685168095036200023a57604435978489116200023e573660238a0112156200023e5788820135958587116200053c57602099620002f78b8960051b01620005ba565b97868c8a838152019160061b8301019136831162000538578701905b828210620004f1575050509083978485995b89518b1015620003a25788806200033d8d8d6200078a565b51511691160188811162000377579392919060019064ffffffffff8e620003658e8e6200078a565b51015116019a01999091929362000325565b87876011887f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8c96888a8c9697988f948216670de0b6b3a76400008103620004c45750885192611e159182850193858510908511176200049a57505082916200040c9162002122843960608152620003f860608201896200082e565b908b8d8201528a81830391015288620008bd565b039082f09081156200048f575091620004707ffe44018cf74992b2720702385a1728bd329dd136e4f651203176c81c12710a8b94926200045e9416978896885195869560c0875260c08701906200082e565b918b86015284820389860152620008bd565b906060830152606435608083015260843560a08301520390a251908152f35b8651903d90823e3d90fd5b604186917f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b89517f1b05170a000000000000000000000000000000000000000000000000000000008152918201529050fd5b84823603126200053857620005056200056a565b82358a811681036200053457815285918e916200052485840162000612565b8382015281520191019062000313565b8880fd5b8680fd5b50506041907f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b80fd5b604051906040820182811067ffffffffffffffff8211176200058b57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f604051930116820182811067ffffffffffffffff8211176200058b57604052565b359081151582036200060d57565b600080fd5b359064ffffffffff821682036200060d57565b81601f820112156200060d5780359067ffffffffffffffff82116200058b576200067760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011601620005ba565b92828452602083830101116200060d57816000926020809301838601378301015290565b9190916101009081818503126200060d5760405191820167ffffffffffffffff90838110828211176200058b576040528294823573ffffffffffffffffffffffffffffffffffffffff9081811681036200060d578552620006ff60208501620005ff565b6020860152620007126040850162000612565b6040860152606084013590811681036200060d57606085015260808301358281116200060d57816200074691850162000625565b608085015260a083013560a085015260c08301359182116200060d57826200077960e09492620007859486940162000625565b60c086015201620005ff565b910152565b80518210156200079f5760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b919082519283825260005b848110620008195750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b602081830181015184830182015201620007d9565b9060e080620008b26200089561010073ffffffffffffffffffffffffffffffffffffffff80885116875260208801511515602088015264ffffffffff60408901511660408801526060880151166060870152608087015190806080880152860190620007ce565b60a086015160a086015260c086015185820360c0870152620007ce565b930151151591015290565b90815180825260208080930193019160005b828110620008de575050505090565b8351805167ffffffffffffffff16865282015164ffffffffff168583015260409094019392810192600101620008cf56fe6101608060405234620005ba57620018128038038091620000218285620005db565b833981019080820360808112620005ba5781516001600160401b038111620005ba5782019061010082850312620005ba576040519361010085016001600160401b03811186821017620005a45760405282516001600160a01b0381168103620005ba5785526200009460208401620005ff565b9260208601938452620000aa604082016200060d565b604087019081526060820151909690926001600160a01b0384168403620005ba576060820193845260808301516001600160401b038111620005ba5781620000f491850162000661565b6080830190815260a08085015190840190815260c085015191949092906001600160401b038311620005ba576200013460e0926200014094830162000661565b60c086015201620005ff565b60e083019081526020880151959093906001600160a01b0387168703620005ba57604090603f190112620005ba5760408051989089016001600160401b0381118a821017620005a457620001ab91606091604052620001a2604082016200060d565b8b52016200060d565b946020890195865260c0840151516020811162000585575051600080546001600160a01b0319166001600160a01b0392831617815584519091166080529651151560a052975164ffffffffff90811660c052975180516001600160401b038111620005715760019182548381811c9116801562000566575b60208210146200055257601f811162000505575b50602090601f8311600114620004995760c095949392918a91836200048d575b5050600019600383901b1c191690821b1790555b5160e05201516040516200029f602082816200029181830196878151938492016200063c565b8101038084520182620005db565b51905190602081106200047a575b50610100525115159461012095865261014094838652511669ffffffffff0000000000600354925160281b169160018060501b031916171760035560018060a01b03608051166040519160208301848063095ea7b360e01b9283815260018060a01b03851660248801526000196044880152604487526200032e87620005bf565b86519082875af16200033f620006ae565b8162000439575b50806200042e575b15620003e2575b60405161104290878983620007d08439608051838181610470015281816107440152610c36015260a05183818161076b0152610b5c015260c05183818161015d01528181610aa801528181610d8f0152610f60015260e0518381816102e2015261066201526101005183610e250152518281816107930152610b1f0152518181816101ae01526109060152f35b62000423946200041d9260405192602084015260018060a01b031660248301526044820152604481526200041681620005bf565b82620006e3565b620006e3565b388080808062000355565b50823b15156200034e565b805180159250821562000450575b50503862000346565b8192509060209181010312620004765760206200046e9101620005ff565b388062000447565b8580fd5b6000199060200360031b1b1638620002ad565b01519050388062000257565b838a5260208a209190601f1984168b5b818110620004ee575091859492918360c0999897959310620004d4575b505050811b0190556200026b565b015160001960f88460031b161c19169055388080620004c6565b8284015185559386019360209384019301620004a9565b838a5260208a20601f840160051c8101916020851062000547575b601f0160051c019084905b8281106200053b57505062000237565b8b81550184906200052b565b909150819062000520565b634e487b7160e01b8a52602260045260248afd5b90607f169062000223565b634e487b7160e01b88526041600452602488fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b634e487b7160e01b600052604160045260246000fd5b600080fd5b608081019081106001600160401b03821117620005a457604052565b601f909101601f19168101906001600160401b03821190821017620005a457604052565b51908115158203620005ba57565b519064ffffffffff82168203620005ba57565b6001600160401b038111620005a457601f01601f191660200190565b60005b838110620006505750506000910152565b81810151838201526020016200063f565b81601f82011215620005ba5780516200067a8162000620565b926200068a6040519485620005db565b81845260208284010111620005ba57620006ab91602080850191016200063c565b90565b3d15620006de573d90620006c28262000620565b91620006d26040519384620005db565b82523d6000602084013e565b606090565b600080620007109260018060a01b03169360208151910182865af162000708620006ae565b908362000767565b805190811515918262000740575b5050620007285750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620005ba5760206200075e9101620005ff565b1538806200071e565b906200079057508051156200077e57805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580620007c5575b620007a3575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156200079a56fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde0314610e0e575080631686c90914610b8157806316c3549d14610b445780631bfd681414610b075780633bfe03a814610ad85780633f31ae3f146104945780634800d97f1461044357806349fc73dd1461030557806351e75e8b146102ca57806375829def146101ed57806390e64d13146101d25780639e93e57714610181578063bb4b57341461013f578063ce516507146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760206003193601126100f857602061013560043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f8576020610135610f58565b346100f85760206003193601126100f857610206610ebc565b60005473ffffffffffffffffffffffffffffffffffffffff8082169233840361027d577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c9060018316928315610439575b602093848410811461040a578386529081156103cc5750600114610371575b61036d8461036181880382610f17565b60405191829182610e56565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8284106103b9575050508161036d936103619282010193610351565b805485850187015292850192810161039d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103618161036d610351565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691610332565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f857604435906fffffffffffffffffffffffffffffffff821682036100f85767ffffffffffffffff606435116100f8573660236064350112156100f85767ffffffffffffffff60643560040135116100f8573660246064356004013560051b6064350101116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff831660408201526fffffffffffffffffffffffffffffffff841660608201526060815261057a81610efb565b519020604051602081019182526020815261059481610edf565b519020916105a0610f58565b610a7a576105c960043560ff6001918060081c6000526002602052161b60406000205416151590565b610a4857604051926105e760206064356004013560051b0185610f17565b60643560048101358552602401602085015b60246064356004013560051b60643501018210610a38575050906000915b845183101561065e5760208360051b860101519081811060001461064b57600052602052600160406000205b920191610617565b9060005260205260016040600020610643565b83907f000000000000000000000000000000000000000000000000000000000000000003610a0e5760043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff60005416906040516106cd81610edf565b600081526000602082015260405192610100840184811067ffffffffffffffff8211176109df57604052835273ffffffffffffffffffffffffffffffffffffffff821660208401526fffffffffffffffffffffffffffffffff8416604084015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608401527f0000000000000000000000000000000000000000000000000000000000000000151560808401527f0000000000000000000000000000000000000000000000000000000000000000151560a08401526040516107c581610edf565b64ffffffffff600354818116835260281c16602082015260c084015260e0830152602060e0604051937fab167ccc00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff815116600486015273ffffffffffffffffffffffffffffffffffffffff838201511660248601526fffffffffffffffffffffffffffffffff604082015116604486015273ffffffffffffffffffffffffffffffffffffffff606082015116606486015260808101511515608486015260a0810151151560a486015264ffffffffff8360c08301518281511660c489015201511660e4860152015173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015260208261014481600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af19182156109d35760009261099e575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d6020116109cb575b816109b960209383610f17565b810103126100f8579051906020610937565b3d91506109ac565b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b81358152602091820191016105f9565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f857604060035464ffffffffff825191818116835260281c166020820152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857610b9a610ebc565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff8060005416338103610dbf5750610be1610f58565b15610d61576040519060209160008083858401977fa9059cbb000000000000000000000000000000000000000000000000000000008952169687602485015286604485015260448452610c3384610efb565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610d55573d67ffffffffffffffff81116109df57610cbc9160405191610cac877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610f17565b82523d60008784013e5b83610f95565b8051848115159182610d34575b50509050610d035750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488610cc9565b610cbc90606090610cb6565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f85761036d907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261036181610edf565b60208082528251818301819052939260005b858110610ea8575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201610e68565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff8211176109df57604052565b6080810190811067ffffffffffffffff8211176109df57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176109df57604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081610f8d575090565b905042101590565b90610fd45750805115610faa57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b8151158061102c575b610fe5575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15610fdd56fea164736f6c6343000817000a6101608060405234620006e95762001e1590813803809262000022828462000735565b82396060818381010312620006e95780516001600160401b038111620006e9578101916101009283818385010312620006e957604051908482016001600160401b03811183821017620007035760405280516001600160a01b0381168103620006e9578252620000956020820162000759565b6020830152620000a86040820162000767565b604083015260608101516001600160a01b0381168103620006e957606083015260808101516001600160401b038111620006e957620000ed90848601908301620007bb565b608083015260a0818101519083015260c08101516001600160401b038111620006e95762000134916200012860e092868801908301620007bb565b60c08501520162000759565b60e08201526020830151916001600160a01b0383168303620006e9576040840151906001600160401b038211620006e957808501601f838701011215620006e95784820151906001600160401b0382116200070357604051956200019f60208460051b018862000735565b828752602087019382820160208560061b838501010111620006e95793602085830101945b60208560061b82850101018610620006835750505050505060c081015151602081116200066457506060810151600080546001600160a01b0319166001600160a01b03928316178155825190911660809081526020830151151560a052604083015164ffffffffff1660c0528201518051919290916001600160401b038111620006505760019283548481811c9116801562000645575b60208210146200063157601f8111620005e4575b50602090601f83116001146200057a5760e09392918691836200056e575b5050600019600383901b1c191690841b1783555b60a0810151825260c0810151604051620002db60208281620002cd818301968781519384920162000796565b810103808452018262000735565b51905190602081106200055b575b5087520151151593610120948552610140938452805190835b82811062000499575050505060018060a01b036080511660018060a01b03835116906040519160208301848063095ea7b360e01b92838152846024880152600019604488015260448752620003578762000719565b86519082875af16200036862000808565b8162000458575b50806200044d575b1562000409575b6040516114eb9087898b846200092a85396080518481816104e2015281816108550152610f1c015260a05184818161087c0152610e42015260c05184818161022001528181610dbd015281816110750152611246015260e05184818161035401526106ba0152518361110b0152518281816108a40152610e0501525181818161012a0152610a0e0152f35b62000442946200043c926040519260208401526024830152604482015260448152620004358162000719565b826200083d565b6200083d565b38808080806200037e565b50823b151562000377565b80518015925082156200046f575b5050386200036f565b8192509060209181010312620004955760206200048d910162000759565b388062000466565b8580fd5b8151811015620005475760208160051b8301015160038054906801000000000000000082101562000533578682018082558210156200051f578752602080882083519201805493909101516cffffffffff000000000000000060409190911b166001600160401b039092166001600160681b031990931692909217179055830162000302565b634e487b7160e01b88526032600452602488fd5b634e487b7160e01b88526041600452602488fd5b634e487b7160e01b85526032600452602485fd5b6000199060200360031b1b1638620002e9565b0151905038806200028d565b92918491601f198216908388526020882091885b818110620005cb5750958360e09710620005b1575b505050811b018355620002a1565b015160001960f88460031b161c19169055388080620005a3565b828801518455889590930192602092830192016200058e565b84865260208620601f840160051c8101916020851062000626575b601f0160051c019085905b8281106200061a5750506200026f565b8781550185906200060a565b9091508190620005ff565b634e487b7160e01b86526022600452602486fd5b90607f16906200025b565b634e487b7160e01b84526041600452602484fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b6040868585010312620006e957604080519081016001600160401b03811182821017620006ee5760405286516001600160401b0381168103620006e95760209382859260409452620006d7838b0162000767565b838201528152019601959150620001c4565b600080fd5b60246000634e487b7160e01b81526041600452fd5b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b038211176200070357604052565b601f909101601f19168101906001600160401b038211908210176200070357604052565b51908115158203620006e957565b519064ffffffffff82168203620006e957565b6001600160401b0381116200070357601f01601f191660200190565b60005b838110620007aa5750506000910152565b818101518382015260200162000799565b81601f82011215620006e9578051620007d4816200077a565b92620007e4604051948562000735565b81845260208284010111620006e95762000805916020808501910162000796565b90565b3d1562000838573d906200081c826200077a565b916200082c604051938462000735565b82523d6000602084013e565b606090565b6000806200086a9260018060a01b03169360208151910182865af16200086262000808565b9083620008c1565b80519081151591826200089a575b5050620008825750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620006e9576020620008b8910162000759565b15388062000878565b90620008ea5750805115620008d857805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806200091f575b620008fd575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15620008f456fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde03146110f4575080631686c90914610e6757806316c3549d14610e2a5780631bfd681414610ded5780633f31ae3f146105065780634800d97f146104b557806349fc73dd1461037757806351e75e8b1461033c57806375829def1461025f57806390e64d1314610244578063bb4b573414610202578063bf4ed03f14610190578063ce5165071461014e578063da792468146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760206003193601126100f857602061018660043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f8576101a9611293565b6040516020918282018383528151809152836040840192019360005b8281106101d25784840385f35b8551805167ffffffffffffffff16855282015164ffffffffff1684830152948101946040909301926001016101c5565b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f857602061018661123e565b346100f85760206003193601126100f8576102786111a2565b60005473ffffffffffffffffffffffffffffffffffffffff808216923384036102ef577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c90600183169283156104ab575b602093848410811461047c5783865290811561043e57506001146103e3575b6103df846103d3818803826111fd565b6040519182918261113c565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b82841061042b57505050816103df936103d392820101936103c3565b805485850187015292850192810161040f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103d3816103df6103c3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f16916103a4565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f8576fffffffffffffffffffffffffffffffff60443516604435036100f85767ffffffffffffffff606435116100f8573660236064350112156100f857606435600401359067ffffffffffffffff82116100f85760248260051b60643501013681116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff841660408201526fffffffffffffffffffffffffffffffff604435166060820152606081526105eb816111e1565b5190206040516020810191825260208152610605816111c5565b5190209061061161123e565b610d8f5761063a60043560ff6001918060081c6000526002602052161b60406000205416151590565b610d5d576106478461127b565b9361065560405195866111fd565b8452606435602401602085015b828210610d4d575050506000905b83518210156106b657610683828561132e565b5190818110156106a357600052602052600160406000205b910190610670565b906000526020526001604060002061069b565b90507f000000000000000000000000000000000000000000000000000000000000000003610d23576106e6611293565b90600082516106f48161127b565b9361070260405195866111fd565b8185527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061072f8361127b565b0160005b818110610cfe5750506000905b828210610bd95750506fffffffffffffffffffffffffffffffff82166fffffffffffffffffffffffffffffffff604435168111610baa576fffffffffffffffffffffffffffffffff6044351611610b56575b505060043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff6000541691604051906107dc826111c5565b600082526000602083015260405193610100850185811067ffffffffffffffff821117610b2757604052845273ffffffffffffffffffffffffffffffffffffffff831660208501526fffffffffffffffffffffffffffffffff60443516604085015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608501527f0000000000000000000000000000000000000000000000000000000000000000151560808501527f0000000000000000000000000000000000000000000000000000000000000000151560a085015260c084015260e0830152604051917f897f362b0000000000000000000000000000000000000000000000000000000083526020600484015282610144810173ffffffffffffffffffffffffffffffffffffffff835116602483015273ffffffffffffffffffffffffffffffffffffffff60208401511660448301526fffffffffffffffffffffffffffffffff604084015116606483015273ffffffffffffffffffffffffffffffffffffffff60608401511660848301526080830151151560a483015260a0830151151560c483015260c08301519061012060e484015281518091526020610164840192019060005b818110610ae9575050508190602060e08195015173ffffffffffffffffffffffffffffffffffffffff81511661010485015201516101248301520381600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1918215610add57600092610aa8575b60208380847f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff81519360043585526fffffffffffffffffffffffffffffffff60443516888601521692a3604051908152f35b91506020823d602011610ad5575b81610ac3602093836111fd565b810103126100f8576020915191610a3f565b3d9150610ab6565b6040513d6000823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff1681860152889550604090940193909201916001016109b9565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6fffffffffffffffffffffffffffffffff610b947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8293018661132e565b5192604435031681835116011690528280610792565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b9092610c0e67ffffffffffffffff610bf1868561132e565b5151166fffffffffffffffffffffffffffffffff60443516611371565b6fffffffffffffffffffffffffffffffff8111610ccd576fffffffffffffffffffffffffffffffff8091169164ffffffffff6020610c4c888761132e565b5101511660405190610c5d826111c5565b8482526020820152610c6f878a61132e565b52610c7a868961132e565b5016016fffffffffffffffffffffffffffffffff8111610c9e579260010190610740565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b602490604051907f4916adce0000000000000000000000000000000000000000000000000000000082526004820152fd5b602090604051610d0d816111c5565b6000815260008382015282828a01015201610733565b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b8135815260209182019101610662565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857610e806111a2565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff80600054163381036110a55750610ec761123e565b15611047576040519060209160008083858401977fa9059cbb000000000000000000000000000000000000000000000000000000008952169687602485015286604485015260448452610f19846111e1565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d1561103b573d67ffffffffffffffff8111610b2757610fa29160405191610f92877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846111fd565b82523d60008784013e5b8361143e565b805184811515918261101a575b50509050610fe95750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488610faf565b610fa290606090610f9c565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f8576103df907f00000000000000000000000000000000000000000000000000000000000000006020820152602081526103d3816111c5565b60208082528251818301819052939260005b85811061118e575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b81810183015184820160400152820161114e565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff821117610b2757604052565b6080810190811067ffffffffffffffff821117610b2757604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610b2757604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081611273575090565b905042101590565b67ffffffffffffffff8111610b275760051b60200190565b600354906112a08261127b565b9160406112b060405194856111fd565b8184528360208091019160036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b906000935b8585106112f457505050505050565b60018481928451611304816111c5565b64ffffffffff875467ffffffffffffffff81168352871c16838201528152019301940193916112e5565b80518210156113425760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8382098382029182808310920391808303921461142d57670de0b6b3a764000090818310156113f657947faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac1066994950990828211900360ee1b910360121c170290565b60449086604051917f5173648d00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5050670de0b6b3a764000090049150565b9061147d575080511561145357805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b815115806114d5575b61148e575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b1561148656fea164736f6c6343000817000aa164736f6c6343000817000a"; /*////////////////////////////////////////////////////////////////////////// DEPLOYERS diff --git a/src/SablierV2MerkleLockupFactory.sol b/src/SablierV2MerkleLockupFactory.sol index 1625564a..9c0ef2a9 100644 --- a/src/SablierV2MerkleLockupFactory.sol +++ b/src/SablierV2MerkleLockupFactory.sol @@ -32,24 +32,8 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { external returns (ISablierV2MerkleLL merkleLL) { - // Hash the parameters to generate a salt. - bytes32 salt = keccak256( - abi.encodePacked( - baseParams.asset, - baseParams.cancelable, - baseParams.expiration, - baseParams.initialAdmin, - abi.encode(baseParams.ipfsCID), - baseParams.merkleRoot, - bytes32(abi.encodePacked(baseParams.name)), - baseParams.transferable, - lockupLinear, - abi.encode(streamDurations) - ) - ); - - // Deploy the MerkleLockup contract with CREATE2. - merkleLL = new SablierV2MerkleLL{ salt: salt }(baseParams, lockupLinear, streamDurations); + // Deploy the MerkleLockup contract with CREATE. + merkleLL = new SablierV2MerkleLL(baseParams, lockupLinear, streamDurations); // Log the creation of the MerkleLockup contract, including some metadata that is not stored on-chain. emit CreateMerkleLL(merkleLL, baseParams, lockupLinear, streamDurations, aggregateAmount, recipientCount); @@ -83,24 +67,8 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { revert Errors.SablierV2MerkleLockupFactory_TotalPercentageNotOneHundred(totalPercentage); } - // Hash the parameters to generate a salt. - bytes32 salt = keccak256( - abi.encodePacked( - baseParams.asset, - baseParams.cancelable, - baseParams.expiration, - baseParams.initialAdmin, - abi.encode(baseParams.ipfsCID), - baseParams.merkleRoot, - bytes32(abi.encodePacked(baseParams.name)), - baseParams.transferable, - lockupTranched, - abi.encode(tranchesWithPercentages) - ) - ); - - // Deploy the MerkleLockup contract with CREATE2. - merkleLT = new SablierV2MerkleLT{ salt: salt }(baseParams, lockupTranched, tranchesWithPercentages); + // Deploy the MerkleLockup contract with CREATE. + merkleLT = new SablierV2MerkleLT(baseParams, lockupTranched, tranchesWithPercentages); // Log the creation of the MerkleLockup contract, including some metadata that is not stored on-chain. emit CreateMerkleLT( diff --git a/src/interfaces/ISablierV2MerkleLockupFactory.sol b/src/interfaces/ISablierV2MerkleLockupFactory.sol index 29b84d2a..57a9d8bf 100644 --- a/src/interfaces/ISablierV2MerkleLockupFactory.sol +++ b/src/interfaces/ISablierV2MerkleLockupFactory.sol @@ -10,7 +10,7 @@ import { ISablierV2MerkleLT } from "./ISablierV2MerkleLT.sol"; import { MerkleLockup, MerkleLT } from "../types/DataTypes.sol"; /// @title ISablierV2MerkleLockupFactory -/// @notice Deploys MerkleLockup campaigns with CREATE2. +/// @notice Deploys MerkleLockup campaigns with CREATE. interface ISablierV2MerkleLockupFactory { /*////////////////////////////////////////////////////////////////////////// EVENTS diff --git a/test/Base.t.sol b/test/Base.t.sol index 5310e4ea..f7a106f2 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -18,9 +18,7 @@ import { ISablierV2MerkleLL } from "src/interfaces/ISablierV2MerkleLL.sol"; import { ISablierV2MerkleLockupFactory } from "src/interfaces/ISablierV2MerkleLockupFactory.sol"; import { ISablierV2MerkleLT } from "src/interfaces/ISablierV2MerkleLT.sol"; import { SablierV2BatchLockup } from "src/SablierV2BatchLockup.sol"; -import { SablierV2MerkleLL } from "src/SablierV2MerkleLL.sol"; import { SablierV2MerkleLockupFactory } from "src/SablierV2MerkleLockupFactory.sol"; -import { SablierV2MerkleLT } from "src/SablierV2MerkleLT.sol"; import { ERC20Mock } from "./mocks/erc20/ERC20Mock.sol"; import { Assertions } from "./utils/Assertions.sol"; @@ -57,6 +55,7 @@ abstract contract Base_Test is ISablierV2LockupLinear internal lockupLinear; ISablierV2LockupTranched internal lockupTranched; ISablierV2MerkleLockupFactory internal merkleLockupFactory; + uint256 internal merkleLockupFactoryNonce; ISablierV2MerkleLL internal merkleLL; ISablierV2MerkleLT internal merkleLT; @@ -257,139 +256,4 @@ abstract contract Base_Test is { vm.expectCall({ callee: asset_, count: count, data: abi.encodeCall(IERC20.transferFrom, (from, to, amount)) }); } - - /*////////////////////////////////////////////////////////////////////////// - MERKLE-LOCKUP - //////////////////////////////////////////////////////////////////////////*/ - - function computeMerkleLLAddress( - address admin, - bytes32 merkleRoot, - uint40 expiration - ) - internal - view - returns (address) - { - return computeMerkleLLAddress(admin, dai, merkleRoot, expiration); - } - - function computeMerkleLLAddress( - address admin, - IERC20 asset_, - bytes32 merkleRoot, - uint40 expiration - ) - internal - view - returns (address) - { - bytes32 salt = keccak256( - abi.encodePacked( - address(asset_), - defaults.CANCELABLE(), - expiration, - admin, - abi.encode(defaults.IPFS_CID()), - merkleRoot, - defaults.NAME_BYTES32(), - defaults.TRANSFERABLE(), - lockupLinear, - abi.encode(defaults.durations()) - ) - ); - bytes32 creationBytecodeHash = keccak256(getMerkleLLBytecode(admin, asset_, merkleRoot, expiration)); - return computeCreate2Address({ - salt: salt, - initcodeHash: creationBytecodeHash, - deployer: address(merkleLockupFactory) - }); - } - - function computeMerkleLTAddress( - address admin, - bytes32 merkleRoot, - uint40 expiration - ) - internal - view - returns (address) - { - return computeMerkleLTAddress(admin, dai, merkleRoot, expiration); - } - - function computeMerkleLTAddress( - address admin, - IERC20 asset_, - bytes32 merkleRoot, - uint40 expiration - ) - internal - view - returns (address) - { - bytes32 salt = keccak256( - abi.encodePacked( - address(asset_), - defaults.CANCELABLE(), - expiration, - admin, - abi.encode(defaults.IPFS_CID()), - merkleRoot, - defaults.NAME_BYTES32(), - defaults.TRANSFERABLE(), - lockupTranched, - abi.encode(defaults.tranchesWithPercentages()) - ) - ); - bytes32 creationBytecodeHash = keccak256(getMerkleLTBytecode(admin, asset_, merkleRoot, expiration)); - return computeCreate2Address({ - salt: salt, - initcodeHash: creationBytecodeHash, - deployer: address(merkleLockupFactory) - }); - } - - function getMerkleLLBytecode( - address admin, - IERC20 asset_, - bytes32 merkleRoot, - uint40 expiration - ) - internal - view - returns (bytes memory) - { - bytes memory constructorArgs = - abi.encode(defaults.baseParams(admin, asset_, expiration, merkleRoot), lockupLinear, defaults.durations()); - if (!isTestOptimizedProfile()) { - return bytes.concat(type(SablierV2MerkleLL).creationCode, constructorArgs); - } else { - return - bytes.concat(vm.getCode("out-optimized/SablierV2MerkleLL.sol/SablierV2MerkleLL.json"), constructorArgs); - } - } - - function getMerkleLTBytecode( - address admin, - IERC20 asset_, - bytes32 merkleRoot, - uint40 expiration - ) - internal - view - returns (bytes memory) - { - bytes memory constructorArgs = abi.encode( - defaults.baseParams(admin, asset_, expiration, merkleRoot), - lockupTranched, - defaults.tranchesWithPercentages() - ); - if (!isTestOptimizedProfile()) { - return bytes.concat(type(SablierV2MerkleLT).creationCode, constructorArgs); - } else { - return - bytes.concat(vm.getCode("out-optimized/SablierV2MerkleLT.sol/SablierV2MerkleLT.json"), constructorArgs); - } - } } diff --git a/test/fork/merkle-lockup/MerkleLL.t.sol b/test/fork/merkle-lockup/MerkleLL.t.sol index 1cad3e13..2f641c95 100644 --- a/test/fork/merkle-lockup/MerkleLL.t.sol +++ b/test/fork/merkle-lockup/MerkleLL.t.sol @@ -95,7 +95,7 @@ abstract contract MerkleLL_Fork_Test is Fork_Test { MerkleBuilder.sortLeaves(leaves); vars.merkleRoot = getRoot(leaves.toBytes32()); - vars.expectedLL = computeMerkleLLAddress(params.admin, FORK_ASSET, vars.merkleRoot, params.expiration); + vars.expectedLL = vm.computeCreateAddress(address(merkleLockupFactory), ++merkleLockupFactoryNonce); vars.baseParams = defaults.baseParams({ admin: params.admin, diff --git a/test/fork/merkle-lockup/MerkleLT.t.sol b/test/fork/merkle-lockup/MerkleLT.t.sol index ad741646..41fff26a 100644 --- a/test/fork/merkle-lockup/MerkleLT.t.sol +++ b/test/fork/merkle-lockup/MerkleLT.t.sol @@ -96,7 +96,7 @@ abstract contract MerkleLT_Fork_Test is Fork_Test { MerkleBuilder.sortLeaves(leaves); vars.merkleRoot = getRoot(leaves.toBytes32()); - vars.expectedLT = computeMerkleLTAddress(params.admin, FORK_ASSET, vars.merkleRoot, params.expiration); + vars.expectedLT = vm.computeCreateAddress(address(merkleLockupFactory), ++merkleLockupFactoryNonce); vars.baseParams = defaults.baseParams({ admin: params.admin, diff --git a/test/integration/merkle-lockup/MerkleLockup.t.sol b/test/integration/merkle-lockup/MerkleLockup.t.sol index ba55de28..a37e3831 100644 --- a/test/integration/merkle-lockup/MerkleLockup.t.sol +++ b/test/integration/merkle-lockup/MerkleLockup.t.sol @@ -32,22 +32,6 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { }); } - function computeMerkleLLAddress() internal view returns (address) { - return computeMerkleLLAddress(users.admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); - } - - function computeMerkleLLAddress(address admin) internal view returns (address) { - return computeMerkleLLAddress(admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); - } - - function computeMerkleLLAddress(address admin, uint40 expiration) internal view returns (address) { - return computeMerkleLLAddress(admin, defaults.MERKLE_ROOT(), expiration); - } - - function computeMerkleLLAddress(address admin, bytes32 merkleRoot) internal view returns (address) { - return computeMerkleLLAddress(admin, merkleRoot, defaults.EXPIRATION()); - } - function createMerkleLL() internal returns (ISablierV2MerkleLL) { return createMerkleLL(users.admin, defaults.EXPIRATION()); } @@ -61,6 +45,9 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { } function createMerkleLL(address admin, uint40 expiration) internal returns (ISablierV2MerkleLL) { + // Increment the CREATE nonce for factory contract. + ++merkleLockupFactoryNonce; + return merkleLockupFactory.createMerkleLL({ baseParams: defaults.baseParams(admin, dai, expiration, defaults.MERKLE_ROOT()), lockupLinear: lockupLinear, @@ -83,22 +70,6 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { }); } - function computeMerkleLTAddress() internal view returns (address) { - return computeMerkleLTAddress(users.admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); - } - - function computeMerkleLTAddress(address admin) internal view returns (address) { - return computeMerkleLTAddress(admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); - } - - function computeMerkleLTAddress(address admin, uint40 expiration) internal view returns (address) { - return computeMerkleLTAddress(admin, defaults.MERKLE_ROOT(), expiration); - } - - function computeMerkleLTAddress(address admin, bytes32 merkleRoot) internal view returns (address) { - return computeMerkleLTAddress(admin, merkleRoot, defaults.EXPIRATION()); - } - function createMerkleLT() internal returns (ISablierV2MerkleLT) { return createMerkleLT(users.admin, defaults.EXPIRATION()); } @@ -112,6 +83,9 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { } function createMerkleLT(address admin, uint40 expiration) internal returns (ISablierV2MerkleLT) { + // Increment the CREATE nonce for factory contract. + ++merkleLockupFactoryNonce; + return merkleLockupFactory.createMerkleLT({ baseParams: defaults.baseParams(admin, dai, expiration, defaults.MERKLE_ROOT()), lockupTranched: lockupTranched, diff --git a/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.t.sol b/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.t.sol index f4e3f268..cc9bbfe7 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.t.sol +++ b/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.t.sol @@ -41,38 +41,9 @@ contract CreateMerkleLL_Integration_Test is MerkleLockup_Integration_Test { _; } - /// @dev This test works because a default MerkleLockup contract is deployed in {Integration_Test.setUp} - function test_RevertGiven_CreatedAlready() external whenCampaignNameNotTooLong { - MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); - LockupLinear.Durations memory streamDurations = defaults.durations(); - uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); - uint256 recipientCount = defaults.RECIPIENT_COUNT(); - - // Expect a revert due to CREATE2. - vm.expectRevert(); - merkleLockupFactory.createMerkleLL({ - baseParams: baseParams, - lockupLinear: lockupLinear, - streamDurations: streamDurations, - aggregateAmount: aggregateAmount, - recipientCount: recipientCount - }); - } - - modifier givenNotCreatedAlready() { - _; - } - - function testFuzz_CreateMerkleLL( - address admin, - uint40 expiration - ) - external - whenCampaignNameNotTooLong - givenNotCreatedAlready - { + function testFuzz_CreateMerkleLL(address admin, uint40 expiration) external whenCampaignNameNotTooLong { vm.assume(admin != users.admin); - address expectedLL = computeMerkleLLAddress(admin, expiration); + address expectedLL = vm.computeCreateAddress(address(merkleLockupFactory), ++merkleLockupFactoryNonce); MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams({ admin: admin, diff --git a/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.tree b/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.tree index 81de5f39..3ad2c770 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.tree +++ b/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.tree @@ -2,8 +2,5 @@ createMerkleLL.t.sol ├── when the campaign name is too long │ └── it should revert └── when the campaign name is not too long - ├── given the campaign has been created already - │ └── it should revert - └── given the campaign has not been created already - ├── it should create the campaign - └── it should emit a {CreateMerkleLL} event + ├── it should create the campaign + └── it should emit a {CreateMerkleLL} event diff --git a/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.t.sol b/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.t.sol index 8cae789a..1be80142 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.t.sol +++ b/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.t.sol @@ -91,24 +91,6 @@ contract CreateMerkleLT_Integration_Test is MerkleLockup_Integration_Test { _; } - /// @dev This test works because a default MerkleLockup contract is deployed in {Integration_Test.setUp} - function test_RevertGiven_CreatedAlready() external whenTotalPercentageOneHundred whenCampaignNameNotTooLong { - MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); - MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); - uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); - uint256 recipientCount = defaults.RECIPIENT_COUNT(); - - // Expect a revert due to CREATE2. - vm.expectRevert(); - merkleLockupFactory.createMerkleLT( - baseParams, lockupTranched, tranchesWithPercentages, aggregateAmount, recipientCount - ); - } - - modifier givenNotCreatedAlready() { - _; - } - function testFuzz_CreateMerkleLT( address admin, uint40 expiration @@ -116,10 +98,9 @@ contract CreateMerkleLT_Integration_Test is MerkleLockup_Integration_Test { external whenTotalPercentageOneHundred whenCampaignNameNotTooLong - givenNotCreatedAlready { vm.assume(admin != users.admin); - address expectedLT = computeMerkleLTAddress(admin, expiration); + address expectedLT = vm.computeCreateAddress(address(merkleLockupFactory), ++merkleLockupFactoryNonce); MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams({ admin: admin, diff --git a/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.tree b/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.tree index 215638cb..a2c91fc9 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.tree +++ b/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.tree @@ -8,8 +8,5 @@ createMerkleLL.t.sol ├── when the campaign name is too long │ └── it should revert └── when the campaign name is not too long - ├── given the campaign has been already created - │ └── it should revert - └── given the campaign has not been already created - ├── it should create the campaign - └── it should emit a {CreateMerkleLT} event + ├── it should create the campaign + └── it should emit a {CreateMerkleLT} event From 9eff88c4549d4f7a20f0ad1122241be540d53720 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Fri, 3 May 2024 17:11:31 +0100 Subject: [PATCH 44/61] feat: grace period mechanism for clawback (#340) * feat: grace period mechanism for clawback * feat: add a helper function for grace period feat: add a getter function for first claim time test: remove unneeded functions from MerkleLockup_Integration_Test test: remove unneeded claimIndex from clawback tests test: add FIRST_CLAIM_TIME to Defaults * chore: add override specifier * refactor: update precompiles * test: add test for getFirstClaimTime --------- Co-authored-by: andreivladbrg --- precompiles/Precompiles.sol | 2 +- src/abstracts/SablierV2MerkleLockup.sol | 30 +++++++++++--- src/interfaces/ISablierV2MerkleLockup.sol | 7 +++- src/libraries/Errors.sol | 5 ++- .../merkle-lockup/ll/clawback/clawback.t.sol | 39 ++++++++++++++++--- .../merkle-lockup/ll/clawback/clawback.tree | 18 ++++++--- .../getFirstClaimTime.t.sol | 26 +++++++++++++ .../getFirstClaimTime.tree | 5 +++ .../merkle-lockup/lt/clawback/clawback.t.sol | 38 +++++++++++++++--- .../merkle-lockup/lt/clawback/clawback.tree | 18 ++++++--- .../getFirstClaimTime.t.sol | 26 +++++++++++++ .../getFirstClaimTime.tree | 5 +++ test/utils/Defaults.sol | 2 + 13 files changed, 190 insertions(+), 31 deletions(-) create mode 100644 test/integration/merkle-lockup/ll/get-first-claim-time/getFirstClaimTime.t.sol create mode 100644 test/integration/merkle-lockup/ll/get-first-claim-time/getFirstClaimTime.tree create mode 100644 test/integration/merkle-lockup/lt/get-first-claim-time/getFirstClaimTime.t.sol create mode 100644 test/integration/merkle-lockup/lt/get-first-claim-time/getFirstClaimTime.tree diff --git a/precompiles/Precompiles.sol b/precompiles/Precompiles.sol index dcb526c5..5f2f8ead 100644 --- a/precompiles/Precompiles.sol +++ b/precompiles/Precompiles.sol @@ -19,7 +19,7 @@ contract Precompiles { bytes public constant BYTECODE_BATCH_LOCKUP = hex"6080806040523461001657611f10908161001c8239f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c90816337266dd31461125b5750806349a32c4014610eb7578063606ef87514610b6b5780639e743f29146107fc578063a514f83e146104295763f7ca34eb1461006157600080fd5b346103555761006f366115d6565b9283156103ff5760009060005b8581106103cb57506001600160a01b036100999116918483611b81565b6100a284611858565b9260005b8581106100bf57604051806100bb8782611664565b0390f35b6100ca818786611b1e565b6100d3906118a7565b906100df818887611b1e565b6020016100eb906118a7565b85886100f8848284611b1e565b60400161010490611725565b61010f858385611b1e565b60600161011b906118bb565b610126868486611b1e565b608001610132906118bb565b9061013e878587611b1e565b60a00161014a90611b5e565b9287610157818789611b1e565b60c0810161016491611a2a565b96610170929198611b1e565b60e00196604051996101818b6117aa565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c086015236906101e592611a7e565b60e0840152366101f4916119cc565b610100830152604051917f31df3d48000000000000000000000000000000000000000000000000000000008352600483016020905280516001600160a01b0316602484015260208101516001600160a01b0316604484015260408101516fffffffffffffffffffffffffffffffff16606484015260608101516001600160a01b031660848401526080810151151560a484015260a0810151151560c484015260c081015164ffffffffff1660e48401528260e082015161010482016101409052805180610164840152610184830191602001906000905b80821061036e5750505081906103006101006020950151610124840190602080916001600160a01b0381511684520151910152565b03816000885af180156103625760009061032a575b600192506103238288611a05565b52016100a6565b506020823d60201161035a575b81610344602093836117ff565b810103126103555760019151610315565b600080fd5b3d9150610337565b6040513d6000823e3d90fd5b919493509160206060826103bc600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b019501920186939492916102cb565b916001906fffffffffffffffffffffffffffffffff6103f660406103f0878b8a611b1e565b01611725565b1601920161007c565b60046040517ff8bf106c000000000000000000000000000000000000000000000000000000008152fd5b346103555760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610355576104606116a0565b6104686115c0565b9060443567ffffffffffffffff8082116103555736602383011215610355578160040135116103555736602461014083600401350283010111610355578060040135156103ff576000805b826004013582106107c7576104d39150846001600160a01b038516611b81565b6104e08160040135611858565b9160005b826004013581106104fd57604051806100bb8682611664565b61051761051282856004013560248701611b70565b6118a7565b9083610536602061053084846004013560248601611b70565b016118a7565b61054d60406103f085856004013560248701611b70565b9361056b606061056586866004013560248801611b70565b016118bb565b946080956fffffffffffffffffffffffffffffffff6105968861056589896004013560248b01611b70565b926001600160a01b036101006105c78a6105b88160048d013560248e01611b70565b9a602481600401359101611b70565b019681604051976105d789611742565b1687521660208601521660408401526001600160a01b038b166060840152151586830152151560a082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6084360301126103555761075c9261067f60e093604051610644816117c7565b61065060a08501611939565b815261066f8660c095610664878201611939565b602085015201611939565b60408201528385015236906119cc565b83830152604051957f53b157270000000000000000000000000000000000000000000000000000000087526001600160a01b0383511660048801526001600160a01b0360208401511660248801526fffffffffffffffffffffffffffffffff60408401511660448801526001600160a01b0360608401511660648801528201511515608487015260a0820151151560a4870152810151604064ffffffffff918281511660c48901528260208201511660e48901520151166101048601520151610124840190602080916001600160a01b0381511684520151910152565b6020826101648160006001600160a01b0388165af1801561036257600090610794575b6001925061078d8287611a05565b52016104e4565b506020823d6020116107bf575b816107ae602093836117ff565b81010312610355576001915161077f565b3d91506107a1565b6001906fffffffffffffffffffffffffffffffff6107f260406103f086886004013560248a01611b70565b16019101906104b3565b346103555761080a366115d6565b9283156103ff5760009060005b858110610b3d57506001600160a01b036108349116918483611b81565b61083d84611858565b9260005b85811061085657604051806100bb8782611664565b610861818786611b1e565b61086a906118a7565b90610876818887611b1e565b602001610882906118a7565b858861088f848284611b1e565b60400161089b90611725565b6108a6858385611b1e565b6060016108b2906118bb565b6108bd868486611b1e565b6080016108c9906118bb565b906108d5878587611b1e565b60a0016108e190611b5e565b92876108ee818789611b1e565b60c081016108fb916118c8565b96610907929198611b1e565b60e00196604051996109188b6117aa565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c0860152369061097c9261194b565b60e08401523661098b916119cc565b610100830152604051917f32fbe22b000000000000000000000000000000000000000000000000000000008352600483016020905280516001600160a01b0316602484015260208101516001600160a01b0316604484015260408101516fffffffffffffffffffffffffffffffff16606484015260608101516001600160a01b031660848401526080810151151560a484015260a0810151151560c484015260c081015164ffffffffff1660e48401528260e082015161010482016101409052805180610164840152610184830191602001906000905b808210610af4575050508190610a976101006020950151610124840190602080916001600160a01b0381511684520151910152565b03816000885af1801561036257600090610ac1575b60019250610aba8288611a05565b5201610841565b506020823d602011610aec575b81610adb602093836117ff565b810103126103555760019151610aac565b3d9150610ace565b91949350916020604082610b2e600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291610a62565b916001906fffffffffffffffffffffffffffffffff610b6260406103f0878b8a611b1e565b16019201610817565b3461035557610b79366115d6565b9283156103ff5760009060005b858110610e8957506001600160a01b03610ba39116918483611b81565b610bac84611858565b9260005b858110610bc557604051806100bb8782611664565b610bd08187866116b6565b610bd9906118a7565b90610be58188876116b6565b602001610bf1906118a7565b8782610bfe81838a6116b6565b604001610c0a90611725565b610c1582848b6116b6565b606001610c21906118bb565b610c2c83858c6116b6565b608001610c38906118bb565b91610c4484868d6116b6565b60a08101610c5191611a2a565b94610c5d91968d6116b6565b60c0019560405198610c6e8a611742565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a08601523690610cc692611a7e565b60c084015236610cd5916119cc565b60e0830152604051917f54c02292000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001906000905b808210610e2c575050508190610dcf60e06020950151610104840190602080916001600160a01b0381511684520151910152565b03816000885af1801561036257600090610df9575b60019250610df28288611a05565b5201610bb0565b506020823d602011610e24575b81610e13602093836117ff565b810103126103555760019151610de4565b3d9150610e06565b91949350916020606082610e7a600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b01950192018693949291610d9b565b916001906fffffffffffffffffffffffffffffffff610eae60406103f0878b8a6116b6565b16019201610b86565b346103555760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035557610eee6116a0565b610ef66115c0565b9060443567ffffffffffffffff8082116103555736602383011215610355578160040135116103555736602461012083600401350283010111610355578060040135156103ff576000805b8260040135821061122657610f619150846001600160a01b038516611b81565b610f6e8160040135611858565b9160005b82600401358110610f8b57604051806100bb8682611664565b610fa061051282856004013560248701611a19565b9083610fb9602061053084846004013560248601611a19565b610fd060406103f085856004013560248701611a19565b93610fe8606061056586866004013560248801611a19565b946080956fffffffffffffffffffffffffffffffff6110138861056589896004013560248b01611a19565b926001600160a01b0360e06110438a6110348160048d013560248e01611a19565b9a602481600401359101611a19565b0196816040519761105389611742565b1687521660208601521660408401526001600160a01b038b166060840152151586830152151560a082015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff608436030112610355576111bb926110eb60e0936040516110c08161178e565b6110cc60a08501611939565b81526110db60c0809501611939565b60208201528385015236906119cc565b83830152604051957fab167ccc0000000000000000000000000000000000000000000000000000000087526001600160a01b0383511660048801526001600160a01b0360208401511660248801526fffffffffffffffffffffffffffffffff60408401511660448801526001600160a01b0360608401511660648801528201511515608487015260a0820151151560a4870152810151602064ffffffffff918281511660c489015201511660e48601520151610104840190602080916001600160a01b0381511684520151910152565b6020826101448160006001600160a01b0388165af18015610362576000906111f3575b600192506111ec8287611a05565b5201610f72565b506020823d60201161121e575b8161120d602093836117ff565b8101031261035557600191516111de565b3d9150611200565b6001906fffffffffffffffffffffffffffffffff61125160406103f086886004013560248a01611a19565b1601910190610f41565b3461035557611269366115d6565b93849391929315611598575060009060005b85811061156a57506001600160a01b036112989116918483611b81565b6112a184611858565b9260005b8581106112ba57604051806100bb8782611664565b6112c58187866116b6565b6112ce906118a7565b906112da8188876116b6565b6020016112e6906118a7565b87826112f381838a6116b6565b6040016112ff90611725565b61130a82848b6116b6565b606001611316906118bb565b61132183858c6116b6565b60800161132d906118bb565b9161133984868d6116b6565b60a08101611346916118c8565b9461135291968d6116b6565b60c00195604051986113638a611742565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a086015236906113bb9261194b565b60c0840152366113ca916119cc565b60e0830152604051917f897f362b000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001906000905b8082106115215750505081906114c460e06020950151610104840190602080916001600160a01b0381511684520151910152565b03816000885af18015610362576000906114ee575b600192506114e78288611a05565b52016112a5565b506020823d602011611519575b81611508602093836117ff565b8101031261035557600191516114d9565b3d91506114fb565b9194935091602060408261155b600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291611490565b916001906fffffffffffffffffffffffffffffffff61158f60406103f0878b8a6116b6565b1601920161127b565b807ff8bf106c0000000000000000000000000000000000000000000000000000000060049252fd5b602435906001600160a01b038216820361035557565b9060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc830112610355576001600160a01b0391600435838116810361035557926024359081168103610355579160443567ffffffffffffffff9283821161035557806023830112156103555781600401359384116103555760248460051b83010111610355576024019190565b602090602060408183019282815285518094520193019160005b82811061168c575050505090565b83518552938101939281019260010161167e565b600435906001600160a01b038216820361035557565b91908110156116f65760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0181360301821215610355570190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b356fffffffffffffffffffffffffffffffff811681036103555790565b610100810190811067ffffffffffffffff82111761175f57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff82111761175f57604052565b610120810190811067ffffffffffffffff82111761175f57604052565b6060810190811067ffffffffffffffff82111761175f57604052565b6080810190811067ffffffffffffffff82111761175f57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761175f57604052565b67ffffffffffffffff811161175f5760051b60200190565b9061186282611840565b61186f60405191826117ff565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061189d8294611840565b0190602036910137565b356001600160a01b03811681036103555790565b3580151581036103555790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610355570180359067ffffffffffffffff821161035557602001918160061b3603831361035557565b35906fffffffffffffffffffffffffffffffff8216820361035557565b359064ffffffffff8216820361035557565b92919261195782611840565b60409261196760405192836117ff565b819581835260208093019160061b84019381851161035557915b84831061199057505050505050565b85838303126103555783869182516119a78161178e565b6119b08661191c565b81526119bd838701611939565b83820152815201920191611981565b9190826040910312610355576040516119e48161178e565b809280356001600160a01b0381168103610355578252602090810135910152565b80518210156116f65760209160051b010190565b91908110156116f657610120020190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610355570180359067ffffffffffffffff82116103555760200191606082023603831361035557565b929192611a8a82611840565b604094611a9a60405192836117ff565b8195848352602080930191606080960285019481861161035557925b858410611ac65750505050505050565b868483031261035557825190611adb826117c7565b611ae48561191c565b8252858501359067ffffffffffffffff8216820361035557828792838b950152611b0f868801611939565b86820152815201930192611ab6565b91908110156116f65760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee181360301821215610355570190565b3564ffffffffff811681036103555790565b91908110156116f657610140020190565b90604080516020907f23b872dd000000000000000000000000000000000000000000000000000000008282015233602482015260449030828201528660648201526064815260a081019080821067ffffffffffffffff83111761175f57611bea91855285611d86565b6001600160a01b0394858516958451917fdd62ed3e0000000000000000000000000000000000000000000000000000000083523060048401521690816024820152838184818a5afa908115611d6357908891600091611d32575b5010611c54575b50505050505050565b8351956000808589017f095ea7b3000000000000000000000000000000000000000000000000000000009a8b82528560248c0152868b0152858a52611c988a6117e3565b89519082855af190611ca8611e12565b82611cff575b5081611cf4575b50611c4b57611ce896611ce394519384015260248301526000818301528152611cdd816117e3565b82611d86565b611d86565b38808080808080611c4b565b90503b151538611cb5565b809192505190858215928315611d1a575b5050509038611cae565b611d2a9350820181019101611d6e565b388581611d10565b809250858092503d8311611d5c575b611d4b81836117ff565b810103126103555787905138611c44565b503d611d41565b85513d6000823e3d90fd5b90816020910312610355575180151581036103555790565b6000806001600160a01b03611db093169360208151910182865af1611da9611e12565b9083611e70565b8051908115159182611df7575b5050611dc65750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b611e0a9250602080918301019101611d6e565b153880611dbd565b3d15611e6b573d9067ffffffffffffffff821161175f5760405191611e5f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846117ff565b82523d6000602084013e565b606090565b90611eaf5750805115611e8557805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580611efa575b611ec0575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15611eb856fea164736f6c6343000817000a"; bytes public constant BYTECODE_MERKLE_LOCKUP_FACTORY = - hex"6080806040523461001657613f43908161001c8239f35b600080fdfe604060808152600490813610156200001657600080fd5b600091823560e01c80631e32387614620002425763769bed20146200003a57600080fd5b346200023e5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126200023e5767ffffffffffffffff9281358481116200023a576200008e90369084016200069b565b916024359173ffffffffffffffffffffffffffffffffffffffff918284168094036200023a57857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126200023a57620000e86200056a565b9664ffffffffff604435818116810362000236578952606435908116810362000232576020890152865191611812908184019284841090841117620002065750908291620009108339608081526200016b8862000149608084018a6200082e565b92886020820152018a6020908164ffffffffff91828151168552015116910152565b039082f0908115620001fb576020620001e287897f2ba0fe49588281dbb122dd3b7f3e2b3396338f70dbe3c62bf3e3888b4ba7ffb8620001c08a8a8a8a16968795875194859460c0865260c08601906200082e565b928a850152878401906020908164ffffffffff91828151168552015116910152565b608435608083015260a43560a08301520390a251908152f35b8551903d90823e3d90fd5b8460416024927f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8380fd5b8480fd5b5080fd5b8280fd5b508234620005675760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112620005675767ffffffffffffffff9082358281116200023a576200029890369085016200069b565b91602480359273ffffffffffffffffffffffffffffffffffffffff958685168095036200023a57604435978489116200023e573660238a0112156200023e5788820135958587116200053c57602099620002f78b8960051b01620005ba565b97868c8a838152019160061b8301019136831162000538578701905b828210620004f1575050509083978485995b89518b1015620003a25788806200033d8d8d6200078a565b51511691160188811162000377579392919060019064ffffffffff8e620003658e8e6200078a565b51015116019a01999091929362000325565b87876011887f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8c96888a8c9697988f948216670de0b6b3a76400008103620004c45750885192611e159182850193858510908511176200049a57505082916200040c9162002122843960608152620003f860608201896200082e565b908b8d8201528a81830391015288620008bd565b039082f09081156200048f575091620004707ffe44018cf74992b2720702385a1728bd329dd136e4f651203176c81c12710a8b94926200045e9416978896885195869560c0875260c08701906200082e565b918b86015284820389860152620008bd565b906060830152606435608083015260843560a08301520390a251908152f35b8651903d90823e3d90fd5b604186917f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b89517f1b05170a000000000000000000000000000000000000000000000000000000008152918201529050fd5b84823603126200053857620005056200056a565b82358a811681036200053457815285918e916200052485840162000612565b8382015281520191019062000313565b8880fd5b8680fd5b50506041907f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b80fd5b604051906040820182811067ffffffffffffffff8211176200058b57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f604051930116820182811067ffffffffffffffff8211176200058b57604052565b359081151582036200060d57565b600080fd5b359064ffffffffff821682036200060d57565b81601f820112156200060d5780359067ffffffffffffffff82116200058b576200067760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011601620005ba565b92828452602083830101116200060d57816000926020809301838601378301015290565b9190916101009081818503126200060d5760405191820167ffffffffffffffff90838110828211176200058b576040528294823573ffffffffffffffffffffffffffffffffffffffff9081811681036200060d578552620006ff60208501620005ff565b6020860152620007126040850162000612565b6040860152606084013590811681036200060d57606085015260808301358281116200060d57816200074691850162000625565b608085015260a083013560a085015260c08301359182116200060d57826200077960e09492620007859486940162000625565b60c086015201620005ff565b910152565b80518210156200079f5760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b919082519283825260005b848110620008195750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b602081830181015184830182015201620007d9565b9060e080620008b26200089561010073ffffffffffffffffffffffffffffffffffffffff80885116875260208801511515602088015264ffffffffff60408901511660408801526060880151166060870152608087015190806080880152860190620007ce565b60a086015160a086015260c086015185820360c0870152620007ce565b930151151591015290565b90815180825260208080930193019160005b828110620008de575050505090565b8351805167ffffffffffffffff16865282015164ffffffffff168583015260409094019392810192600101620008cf56fe6101608060405234620005ba57620018128038038091620000218285620005db565b833981019080820360808112620005ba5781516001600160401b038111620005ba5782019061010082850312620005ba576040519361010085016001600160401b03811186821017620005a45760405282516001600160a01b0381168103620005ba5785526200009460208401620005ff565b9260208601938452620000aa604082016200060d565b604087019081526060820151909690926001600160a01b0384168403620005ba576060820193845260808301516001600160401b038111620005ba5781620000f491850162000661565b6080830190815260a08085015190840190815260c085015191949092906001600160401b038311620005ba576200013460e0926200014094830162000661565b60c086015201620005ff565b60e083019081526020880151959093906001600160a01b0387168703620005ba57604090603f190112620005ba5760408051989089016001600160401b0381118a821017620005a457620001ab91606091604052620001a2604082016200060d565b8b52016200060d565b946020890195865260c0840151516020811162000585575051600080546001600160a01b0319166001600160a01b0392831617815584519091166080529651151560a052975164ffffffffff90811660c052975180516001600160401b038111620005715760019182548381811c9116801562000566575b60208210146200055257601f811162000505575b50602090601f8311600114620004995760c095949392918a91836200048d575b5050600019600383901b1c191690821b1790555b5160e05201516040516200029f602082816200029181830196878151938492016200063c565b8101038084520182620005db565b51905190602081106200047a575b50610100525115159461012095865261014094838652511669ffffffffff0000000000600354925160281b169160018060501b031916171760035560018060a01b03608051166040519160208301848063095ea7b360e01b9283815260018060a01b03851660248801526000196044880152604487526200032e87620005bf565b86519082875af16200033f620006ae565b8162000439575b50806200042e575b15620003e2575b60405161104290878983620007d08439608051838181610470015281816107440152610c36015260a05183818161076b0152610b5c015260c05183818161015d01528181610aa801528181610d8f0152610f60015260e0518381816102e2015261066201526101005183610e250152518281816107930152610b1f0152518181816101ae01526109060152f35b62000423946200041d9260405192602084015260018060a01b031660248301526044820152604481526200041681620005bf565b82620006e3565b620006e3565b388080808062000355565b50823b15156200034e565b805180159250821562000450575b50503862000346565b8192509060209181010312620004765760206200046e9101620005ff565b388062000447565b8580fd5b6000199060200360031b1b1638620002ad565b01519050388062000257565b838a5260208a209190601f1984168b5b818110620004ee575091859492918360c0999897959310620004d4575b505050811b0190556200026b565b015160001960f88460031b161c19169055388080620004c6565b8284015185559386019360209384019301620004a9565b838a5260208a20601f840160051c8101916020851062000547575b601f0160051c019084905b8281106200053b57505062000237565b8b81550184906200052b565b909150819062000520565b634e487b7160e01b8a52602260045260248afd5b90607f169062000223565b634e487b7160e01b88526041600452602488fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b634e487b7160e01b600052604160045260246000fd5b600080fd5b608081019081106001600160401b03821117620005a457604052565b601f909101601f19168101906001600160401b03821190821017620005a457604052565b51908115158203620005ba57565b519064ffffffffff82168203620005ba57565b6001600160401b038111620005a457601f01601f191660200190565b60005b838110620006505750506000910152565b81810151838201526020016200063f565b81601f82011215620005ba5780516200067a8162000620565b926200068a6040519485620005db565b81845260208284010111620005ba57620006ab91602080850191016200063c565b90565b3d15620006de573d90620006c28262000620565b91620006d26040519384620005db565b82523d6000602084013e565b606090565b600080620007109260018060a01b03169360208151910182865af162000708620006ae565b908362000767565b805190811515918262000740575b5050620007285750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620005ba5760206200075e9101620005ff565b1538806200071e565b906200079057508051156200077e57805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580620007c5575b620007a3575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156200079a56fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde0314610e0e575080631686c90914610b8157806316c3549d14610b445780631bfd681414610b075780633bfe03a814610ad85780633f31ae3f146104945780634800d97f1461044357806349fc73dd1461030557806351e75e8b146102ca57806375829def146101ed57806390e64d13146101d25780639e93e57714610181578063bb4b57341461013f578063ce516507146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760206003193601126100f857602061013560043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f8576020610135610f58565b346100f85760206003193601126100f857610206610ebc565b60005473ffffffffffffffffffffffffffffffffffffffff8082169233840361027d577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c9060018316928315610439575b602093848410811461040a578386529081156103cc5750600114610371575b61036d8461036181880382610f17565b60405191829182610e56565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8284106103b9575050508161036d936103619282010193610351565b805485850187015292850192810161039d565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103618161036d610351565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691610332565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f857604435906fffffffffffffffffffffffffffffffff821682036100f85767ffffffffffffffff606435116100f8573660236064350112156100f85767ffffffffffffffff60643560040135116100f8573660246064356004013560051b6064350101116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff831660408201526fffffffffffffffffffffffffffffffff841660608201526060815261057a81610efb565b519020604051602081019182526020815261059481610edf565b519020916105a0610f58565b610a7a576105c960043560ff6001918060081c6000526002602052161b60406000205416151590565b610a4857604051926105e760206064356004013560051b0185610f17565b60643560048101358552602401602085015b60246064356004013560051b60643501018210610a38575050906000915b845183101561065e5760208360051b860101519081811060001461064b57600052602052600160406000205b920191610617565b9060005260205260016040600020610643565b83907f000000000000000000000000000000000000000000000000000000000000000003610a0e5760043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff60005416906040516106cd81610edf565b600081526000602082015260405192610100840184811067ffffffffffffffff8211176109df57604052835273ffffffffffffffffffffffffffffffffffffffff821660208401526fffffffffffffffffffffffffffffffff8416604084015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608401527f0000000000000000000000000000000000000000000000000000000000000000151560808401527f0000000000000000000000000000000000000000000000000000000000000000151560a08401526040516107c581610edf565b64ffffffffff600354818116835260281c16602082015260c084015260e0830152602060e0604051937fab167ccc00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff815116600486015273ffffffffffffffffffffffffffffffffffffffff838201511660248601526fffffffffffffffffffffffffffffffff604082015116604486015273ffffffffffffffffffffffffffffffffffffffff606082015116606486015260808101511515608486015260a0810151151560a486015264ffffffffff8360c08301518281511660c489015201511660e4860152015173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015260208261014481600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af19182156109d35760009261099e575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d6020116109cb575b816109b960209383610f17565b810103126100f8579051906020610937565b3d91506109ac565b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b81358152602091820191016105f9565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f857604060035464ffffffffff825191818116835260281c166020820152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857610b9a610ebc565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff8060005416338103610dbf5750610be1610f58565b15610d61576040519060209160008083858401977fa9059cbb000000000000000000000000000000000000000000000000000000008952169687602485015286604485015260448452610c3384610efb565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610d55573d67ffffffffffffffff81116109df57610cbc9160405191610cac877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610f17565b82523d60008784013e5b83610f95565b8051848115159182610d34575b50509050610d035750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488610cc9565b610cbc90606090610cb6565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f85761036d907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261036181610edf565b60208082528251818301819052939260005b858110610ea8575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201610e68565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff8211176109df57604052565b6080810190811067ffffffffffffffff8211176109df57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176109df57604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081610f8d575090565b905042101590565b90610fd45750805115610faa57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b8151158061102c575b610fe5575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15610fdd56fea164736f6c6343000817000a6101608060405234620006e95762001e1590813803809262000022828462000735565b82396060818381010312620006e95780516001600160401b038111620006e9578101916101009283818385010312620006e957604051908482016001600160401b03811183821017620007035760405280516001600160a01b0381168103620006e9578252620000956020820162000759565b6020830152620000a86040820162000767565b604083015260608101516001600160a01b0381168103620006e957606083015260808101516001600160401b038111620006e957620000ed90848601908301620007bb565b608083015260a0818101519083015260c08101516001600160401b038111620006e95762000134916200012860e092868801908301620007bb565b60c08501520162000759565b60e08201526020830151916001600160a01b0383168303620006e9576040840151906001600160401b038211620006e957808501601f838701011215620006e95784820151906001600160401b0382116200070357604051956200019f60208460051b018862000735565b828752602087019382820160208560061b838501010111620006e95793602085830101945b60208560061b82850101018610620006835750505050505060c081015151602081116200066457506060810151600080546001600160a01b0319166001600160a01b03928316178155825190911660809081526020830151151560a052604083015164ffffffffff1660c0528201518051919290916001600160401b038111620006505760019283548481811c9116801562000645575b60208210146200063157601f8111620005e4575b50602090601f83116001146200057a5760e09392918691836200056e575b5050600019600383901b1c191690841b1783555b60a0810151825260c0810151604051620002db60208281620002cd818301968781519384920162000796565b810103808452018262000735565b51905190602081106200055b575b5087520151151593610120948552610140938452805190835b82811062000499575050505060018060a01b036080511660018060a01b03835116906040519160208301848063095ea7b360e01b92838152846024880152600019604488015260448752620003578762000719565b86519082875af16200036862000808565b8162000458575b50806200044d575b1562000409575b6040516114eb9087898b846200092a85396080518481816104e2015281816108550152610f1c015260a05184818161087c0152610e42015260c05184818161022001528181610dbd015281816110750152611246015260e05184818161035401526106ba0152518361110b0152518281816108a40152610e0501525181818161012a0152610a0e0152f35b62000442946200043c926040519260208401526024830152604482015260448152620004358162000719565b826200083d565b6200083d565b38808080806200037e565b50823b151562000377565b80518015925082156200046f575b5050386200036f565b8192509060209181010312620004955760206200048d910162000759565b388062000466565b8580fd5b8151811015620005475760208160051b8301015160038054906801000000000000000082101562000533578682018082558210156200051f578752602080882083519201805493909101516cffffffffff000000000000000060409190911b166001600160401b039092166001600160681b031990931692909217179055830162000302565b634e487b7160e01b88526032600452602488fd5b634e487b7160e01b88526041600452602488fd5b634e487b7160e01b85526032600452602485fd5b6000199060200360031b1b1638620002e9565b0151905038806200028d565b92918491601f198216908388526020882091885b818110620005cb5750958360e09710620005b1575b505050811b018355620002a1565b015160001960f88460031b161c19169055388080620005a3565b828801518455889590930192602092830192016200058e565b84865260208620601f840160051c8101916020851062000626575b601f0160051c019085905b8281106200061a5750506200026f565b8781550185906200060a565b9091508190620005ff565b634e487b7160e01b86526022600452602486fd5b90607f16906200025b565b634e487b7160e01b84526041600452602484fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b6040868585010312620006e957604080519081016001600160401b03811182821017620006ee5760405286516001600160401b0381168103620006e95760209382859260409452620006d7838b0162000767565b838201528152019601959150620001c4565b600080fd5b60246000634e487b7160e01b81526041600452fd5b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b038211176200070357604052565b601f909101601f19168101906001600160401b038211908210176200070357604052565b51908115158203620006e957565b519064ffffffffff82168203620006e957565b6001600160401b0381116200070357601f01601f191660200190565b60005b838110620007aa5750506000910152565b818101518382015260200162000799565b81601f82011215620006e9578051620007d4816200077a565b92620007e4604051948562000735565b81845260208284010111620006e95762000805916020808501910162000796565b90565b3d1562000838573d906200081c826200077a565b916200082c604051938462000735565b82523d6000602084013e565b606090565b6000806200086a9260018060a01b03169360208151910182865af16200086262000808565b9083620008c1565b80519081151591826200089a575b5050620008825750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620006e9576020620008b8910162000759565b15388062000878565b90620008ea5750805115620008d857805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806200091f575b620008fd575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15620008f456fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde03146110f4575080631686c90914610e6757806316c3549d14610e2a5780631bfd681414610ded5780633f31ae3f146105065780634800d97f146104b557806349fc73dd1461037757806351e75e8b1461033c57806375829def1461025f57806390e64d1314610244578063bb4b573414610202578063bf4ed03f14610190578063ce5165071461014e578063da792468146100fd5763f851a440146100c457600080fd5b346100f85760006003193601126100f857602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760206003193601126100f857602061018660043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b346100f85760006003193601126100f8576101a9611293565b6040516020918282018383528151809152836040840192019360005b8281106101d25784840385f35b8551805167ffffffffffffffff16855282015164ffffffffff1684830152948101946040909301926001016101c5565b346100f85760006003193601126100f857602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760006003193601126100f857602061018661123e565b346100f85760206003193601126100f8576102786111a2565b60005473ffffffffffffffffffffffffffffffffffffffff808216923384036102ef577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100f85760006003193601126100f85760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100f85760006003193601126100f85760405160009060018054908160011c90600183169283156104ab575b602093848410811461047c5783865290811561043e57506001146103e3575b6103df846103d3818803826111fd565b6040519182918261113c565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b82841061042b57505050816103df936103d392820101936103c3565b805485850187015292850192810161040f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103d3816103df6103c3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f16916103a4565b346100f85760006003193601126100f857602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100f85760806003193601126100f85760243573ffffffffffffffffffffffffffffffffffffffff811681036100f8576fffffffffffffffffffffffffffffffff60443516604435036100f85767ffffffffffffffff606435116100f8573660236064350112156100f857606435600401359067ffffffffffffffff82116100f85760248260051b60643501013681116100f8576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff841660408201526fffffffffffffffffffffffffffffffff604435166060820152606081526105eb816111e1565b5190206040516020810191825260208152610605816111c5565b5190209061061161123e565b610d8f5761063a60043560ff6001918060081c6000526002602052161b60406000205416151590565b610d5d576106478461127b565b9361065560405195866111fd565b8452606435602401602085015b828210610d4d575050506000905b83518210156106b657610683828561132e565b5190818110156106a357600052602052600160406000205b910190610670565b906000526020526001604060002061069b565b90507f000000000000000000000000000000000000000000000000000000000000000003610d23576106e6611293565b90600082516106f48161127b565b9361070260405195866111fd565b8185527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061072f8361127b565b0160005b818110610cfe5750506000905b828210610bd95750506fffffffffffffffffffffffffffffffff82166fffffffffffffffffffffffffffffffff604435168111610baa576fffffffffffffffffffffffffffffffff6044351611610b56575b505060043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff6000541691604051906107dc826111c5565b600082526000602083015260405193610100850185811067ffffffffffffffff821117610b2757604052845273ffffffffffffffffffffffffffffffffffffffff831660208501526fffffffffffffffffffffffffffffffff60443516604085015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608501527f0000000000000000000000000000000000000000000000000000000000000000151560808501527f0000000000000000000000000000000000000000000000000000000000000000151560a085015260c084015260e0830152604051917f897f362b0000000000000000000000000000000000000000000000000000000083526020600484015282610144810173ffffffffffffffffffffffffffffffffffffffff835116602483015273ffffffffffffffffffffffffffffffffffffffff60208401511660448301526fffffffffffffffffffffffffffffffff604084015116606483015273ffffffffffffffffffffffffffffffffffffffff60608401511660848301526080830151151560a483015260a0830151151560c483015260c08301519061012060e484015281518091526020610164840192019060005b818110610ae9575050508190602060e08195015173ffffffffffffffffffffffffffffffffffffffff81511661010485015201516101248301520381600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1918215610add57600092610aa8575b60208380847f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff81519360043585526fffffffffffffffffffffffffffffffff60443516888601521692a3604051908152f35b91506020823d602011610ad5575b81610ac3602093836111fd565b810103126100f8576020915191610a3f565b3d9150610ab6565b6040513d6000823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff1681860152889550604090940193909201916001016109b9565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6fffffffffffffffffffffffffffffffff610b947fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8293018661132e565b5192604435031681835116011690528280610792565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b9092610c0e67ffffffffffffffff610bf1868561132e565b5151166fffffffffffffffffffffffffffffffff60443516611371565b6fffffffffffffffffffffffffffffffff8111610ccd576fffffffffffffffffffffffffffffffff8091169164ffffffffff6020610c4c888761132e565b5101511660405190610c5d826111c5565b8482526020820152610c6f878a61132e565b52610c7a868961132e565b5016016fffffffffffffffffffffffffffffffff8111610c9e579260010190610740565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b602490604051907f4916adce0000000000000000000000000000000000000000000000000000000082526004820152fd5b602090604051610d0d816111c5565b6000815260008382015282828a01015201610733565b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b8135815260209182019101610662565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b6040517f442b18410000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760006003193601126100f85760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100f85760406003193601126100f857610e806111a2565b6024356fffffffffffffffffffffffffffffffff81168091036100f85773ffffffffffffffffffffffffffffffffffffffff80600054163381036110a55750610ec761123e565b15611047576040519060209160008083858401977fa9059cbb000000000000000000000000000000000000000000000000000000008952169687602485015286604485015260448452610f19846111e1565b847f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d1561103b573d67ffffffffffffffff8111610b2757610fa29160405191610f92877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846111fd565b82523d60008784013e5b8361143e565b805184811515918261101a575b50509050610fe95750907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100f8578301518015908115036100f857808488610faf565b610fa290606090610f9c565b6040517fe13612970000000000000000000000000000000000000000000000000000000081524260048201527f000000000000000000000000000000000000000000000000000000000000000064ffffffffff166024820152604490fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100f85760006003193601126100f8576103df907f00000000000000000000000000000000000000000000000000000000000000006020820152602081526103d3816111c5565b60208082528251818301819052939260005b85811061118e575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b81810183015184820160400152820161114e565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100f857565b6040810190811067ffffffffffffffff821117610b2757604052565b6080810190811067ffffffffffffffff821117610b2757604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610b2757604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081611273575090565b905042101590565b67ffffffffffffffff8111610b275760051b60200190565b600354906112a08261127b565b9160406112b060405194856111fd565b8184528360208091019160036000527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b906000935b8585106112f457505050505050565b60018481928451611304816111c5565b64ffffffffff875467ffffffffffffffff81168352871c16838201528152019301940193916112e5565b80518210156113425760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8382098382029182808310920391808303921461142d57670de0b6b3a764000090818310156113f657947faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac1066994950990828211900360ee1b910360121c170290565b60449086604051917f5173648d00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5050670de0b6b3a764000090049150565b9061147d575080511561145357805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b815115806114d5575b61148e575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b1561148656fea164736f6c6343000817000aa164736f6c6343000817000a"; + hex"608080604052346100165761416c908161001c8239f35b600080fdfe604060808152600490813610156200001657600080fd5b600091823560e01c80631e32387614620002425763769bed20146200003a57600080fd5b346200023e5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126200023e5767ffffffffffffffff9281358481116200023a576200008e90369084016200069b565b916024359173ffffffffffffffffffffffffffffffffffffffff918284168094036200023a57857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126200023a57620000e86200056a565b9664ffffffffff60443581811681036200023657895260643590811681036200023257602089015286519161191e908184019284841090841117620002065750908291620009108339608081526200016b8862000149608084018a6200082e565b92886020820152018a6020908164ffffffffff91828151168552015116910152565b039082f0908115620001fb576020620001e287897f2ba0fe49588281dbb122dd3b7f3e2b3396338f70dbe3c62bf3e3888b4ba7ffb8620001c08a8a8a8a16968795875194859460c0865260c08601906200082e565b928a850152878401906020908164ffffffffff91828151168552015116910152565b608435608083015260a43560a08301520390a251908152f35b8551903d90823e3d90fd5b8460416024927f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8380fd5b8480fd5b5080fd5b8280fd5b508234620005675760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112620005675767ffffffffffffffff9082358281116200023a576200029890369085016200069b565b91602480359273ffffffffffffffffffffffffffffffffffffffff958685168095036200023a57604435978489116200023e573660238a0112156200023e5788820135958587116200053c57602099620002f78b8960051b01620005ba565b97868c8a838152019160061b8301019136831162000538578701905b828210620004f1575050509083978485995b89518b1015620003a25788806200033d8d8d6200078a565b51511691160188811162000377579392919060019064ffffffffff8e620003658e8e6200078a565b51015116019a01999091929362000325565b87876011887f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8c96888a8c9697988f948216670de0b6b3a76400008103620004c45750885192611f329182850193858510908511176200049a57505082916200040c916200222e843960608152620003f860608201896200082e565b908b8d8201528a81830391015288620008bd565b039082f09081156200048f575091620004707ffe44018cf74992b2720702385a1728bd329dd136e4f651203176c81c12710a8b94926200045e9416978896885195869560c0875260c08701906200082e565b918b86015284820389860152620008bd565b906060830152606435608083015260843560a08301520390a251908152f35b8651903d90823e3d90fd5b604186917f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b89517f1b05170a000000000000000000000000000000000000000000000000000000008152918201529050fd5b84823603126200053857620005056200056a565b82358a811681036200053457815285918e916200052485840162000612565b8382015281520191019062000313565b8880fd5b8680fd5b50506041907f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b80fd5b604051906040820182811067ffffffffffffffff8211176200058b57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f604051930116820182811067ffffffffffffffff8211176200058b57604052565b359081151582036200060d57565b600080fd5b359064ffffffffff821682036200060d57565b81601f820112156200060d5780359067ffffffffffffffff82116200058b576200067760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011601620005ba565b92828452602083830101116200060d57816000926020809301838601378301015290565b9190916101009081818503126200060d5760405191820167ffffffffffffffff90838110828211176200058b576040528294823573ffffffffffffffffffffffffffffffffffffffff9081811681036200060d578552620006ff60208501620005ff565b6020860152620007126040850162000612565b6040860152606084013590811681036200060d57606085015260808301358281116200060d57816200074691850162000625565b608085015260a083013560a085015260c08301359182116200060d57826200077960e09492620007859486940162000625565b60c086015201620005ff565b910152565b80518210156200079f5760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b919082519283825260005b848110620008195750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b602081830181015184830182015201620007d9565b9060e080620008b26200089561010073ffffffffffffffffffffffffffffffffffffffff80885116875260208801511515602088015264ffffffffff60408901511660408801526060880151166060870152608087015190806080880152860190620007ce565b60a086015160a086015260c086015185820360c0870152620007ce565b930151151591015290565b90815180825260208080930193019160005b828110620008de575050505090565b8351805167ffffffffffffffff16865282015164ffffffffff168583015260409094019392810192600101620008cf56fe6101608060405234620005ba576200191e8038038091620000218285620005db565b833981019080820360808112620005ba5781516001600160401b038111620005ba5782019061010082850312620005ba576040519361010085016001600160401b03811186821017620005a45760405282516001600160a01b0381168103620005ba5785526200009460208401620005ff565b9260208601938452620000aa604082016200060d565b604087019081526060820151909690926001600160a01b0384168403620005ba576060820193845260808301516001600160401b038111620005ba5781620000f491850162000661565b6080830190815260a08085015190840190815260c085015191949092906001600160401b038311620005ba576200013460e0926200014094830162000661565b60c086015201620005ff565b60e083019081526020880151959093906001600160a01b0387168703620005ba57604090603f190112620005ba5760408051989089016001600160401b0381118a821017620005a457620001ab91606091604052620001a2604082016200060d565b8b52016200060d565b946020890195865260c0840151516020811162000585575051600080546001600160a01b0319166001600160a01b0392831617815584519091166080529651151560a052975164ffffffffff90811660c052975180516001600160401b038111620005715760019182548381811c9116801562000566575b60208210146200055257601f811162000505575b50602090601f8311600114620004995760c095949392918a91836200048d575b5050600019600383901b1c191690821b1790555b5160e05201516040516200029f602082816200029181830196878151938492016200063c565b8101038084520182620005db565b51905190602081106200047a575b50610100525115159461012095865261014094838652511669ffffffffff0000000000600454925160281b169160018060501b031916171760045560018060a01b03608051166040519160208301848063095ea7b360e01b9283815260018060a01b03851660248801526000196044880152604487526200032e87620005bf565b86519082875af16200033f620006ae565b8162000439575b50806200042e575b15620003e2575b60405161114e90878983620007d084396080518381816104a0015281816107860152610cbf015260a0518381816107ad0152610bd1015260c05183818161016801528181610b2601528181610e4c015261106c015260e0518381816102ed015261069201526101005183610f310152518281816107d50152610b940152518181816101b901526109480152f35b62000423946200041d9260405192602084015260018060a01b031660248301526044820152604481526200041681620005bf565b82620006e3565b620006e3565b388080808062000355565b50823b15156200034e565b805180159250821562000450575b50503862000346565b8192509060209181010312620004765760206200046e9101620005ff565b388062000447565b8580fd5b6000199060200360031b1b1638620002ad565b01519050388062000257565b838a5260208a209190601f1984168b5b818110620004ee575091859492918360c0999897959310620004d4575b505050811b0190556200026b565b015160001960f88460031b161c19169055388080620004c6565b8284015185559386019360209384019301620004a9565b838a5260208a20601f840160051c8101916020851062000547575b601f0160051c019084905b8281106200053b57505062000237565b8b81550184906200052b565b909150819062000520565b634e487b7160e01b8a52602260045260248afd5b90607f169062000223565b634e487b7160e01b88526041600452602488fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b634e487b7160e01b600052604160045260246000fd5b600080fd5b608081019081106001600160401b03821117620005a457604052565b601f909101601f19168101906001600160401b03821190821017620005a457604052565b51908115158203620005ba57565b519064ffffffffff82168203620005ba57565b6001600160401b038111620005a457601f01601f191660200190565b60005b838110620006505750506000910152565b81810151838201526020016200063f565b81601f82011215620005ba5780516200067a8162000620565b926200068a6040519485620005db565b81845260208284010111620005ba57620006ab91602080850191016200063c565b90565b3d15620006de573d90620006c28262000620565b91620006d26040519384620005db565b82523d6000602084013e565b606090565b600080620007109260018060a01b03169360208151910182865af162000708620006ae565b908362000767565b805190811515918262000740575b5050620007285750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620005ba5760206200075e9101620005ff565b1538806200071e565b906200079057508051156200077e57805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580620007c5575b620007a3575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156200079a56fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde0314610f1a575080631686c90914610bf657806316c3549d14610bb95780631bfd681414610b7c5780633bfe03a814610b4d5780633f31ae3f146104c45780634800d97f1461047357806349fc73dd146103355780634e390d3e1461031057806351e75e8b146102d557806375829def146101f857806390e64d13146101dd5780639e93e5771461018c578063bb4b57341461014a578063ce516507146101085763f851a440146100cf57600080fd5b3461010357600060031936011261010357602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b3461010357602060031936011261010357602061014060043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b3461010357600060031936011261010357602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461010357600060031936011261010357602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610103576000600319360112610103576020610140611064565b3461010357602060031936011261010357610211610fc8565b60005473ffffffffffffffffffffffffffffffffffffffff80821692338403610288577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346101035760006003193601126101035760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461010357600060031936011261010357602064ffffffffff60035416604051908152f35b346101035760006003193601126101035760405160009060018054908160011c9060018316928315610469575b602093848410811461043a578386529081156103fc57506001146103a1575b61039d8461039181880382611023565b60405191829182610f62565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8284106103e9575050508161039d936103919282010193610381565b80548585018701529285019281016103cd565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103918161039d610381565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691610362565b3461010357600060031936011261010357602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101035760806003193601126101035760243573ffffffffffffffffffffffffffffffffffffffff8116810361010357604435906fffffffffffffffffffffffffffffffff821682036101035767ffffffffffffffff60643511610103573660236064350112156101035767ffffffffffffffff6064356004013511610103573660246064356004013560051b606435010111610103576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff831660408201526fffffffffffffffffffffffffffffffff84166060820152606081526105aa81611007565b51902060405160208101918252602081526105c481610feb565b519020916105d0611064565b610af0576105f960043560ff6001918060081c6000526002602052161b60406000205416151590565b610abe576040519261061760206064356004013560051b0185611023565b60643560048101358552602401602085015b60246064356004013560051b60643501018210610aae575050906000915b845183101561068e5760208360051b860101519081811060001461067b57600052602052600160406000205b920191610647565b9060005260205260016040600020610673565b83907f000000000000000000000000000000000000000000000000000000000000000003610a845760035464ffffffffff811615610a50575b5060043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff600054169060405161070f81610feb565b600081526000602082015260405192610100840184811067ffffffffffffffff821117610a2157604052835273ffffffffffffffffffffffffffffffffffffffff821660208401526fffffffffffffffffffffffffffffffff8416604084015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608401527f0000000000000000000000000000000000000000000000000000000000000000151560808401527f0000000000000000000000000000000000000000000000000000000000000000151560a084015260405161080781610feb565b64ffffffffff600454818116835260281c16602082015260c084015260e0830152602060e0604051937fab167ccc00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff815116600486015273ffffffffffffffffffffffffffffffffffffffff838201511660248601526fffffffffffffffffffffffffffffffff604082015116604486015273ffffffffffffffffffffffffffffffffffffffff606082015116606486015260808101511515608486015260a0810151151560a486015264ffffffffff8360c08301518281511660c489015201511660e4860152015173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015260208261014481600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1918215610a15576000926109e0575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d602011610a0d575b816109fb60209383611023565b81010312610103579051906020610979565b3d91506109ee565b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff1617600355826106c7565b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b8135815260209182019101610629565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b60446040517f442b184100000000000000000000000000000000000000000000000000000000815242600482015264ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166024820152fd5b3461010357600060031936011261010357604060045464ffffffffff825191818116835260281c166020820152f35b346101035760006003193601126101035760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346101035760006003193601126101035760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b3461010357604060031936011261010357610c0f610fc8565b60248035906fffffffffffffffffffffffffffffffff82168092036101035773ffffffffffffffffffffffffffffffffffffffff8060005416338103610ecb575064ffffffffff806003541680151580610e86575b80610e77575b610e185750506040519160209260008084868401987fa9059cbb000000000000000000000000000000000000000000000000000000008a521697888585015287604485015260448452610cbc84611007565b857f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610e0c573d9067ffffffffffffffff8211610dde5790610d479160405191610d37887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184611023565b82523d60008884013e5b836110a1565b8051858115159182610dbd575b50509050610d8f575050907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126101035784015180159081150361010357808589610d54565b837f4e487b710000000000000000000000000000000000000000000000000000000060005260416004526000fd5b610d4790606090610d41565b6064925083604051927f92b666970000000000000000000000000000000000000000000000000000000084524260048501527f000000000000000000000000000000000000000000000000000000000000000016908301526044820152fd5b50610e80611064565b15610c6a565b5062093a808101828111610e9d5782164211610c64565b847f4e487b710000000000000000000000000000000000000000000000000000000060005260116004526000fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346101035760006003193601126101035761039d907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261039181610feb565b60208082528251818301819052939260005b858110610fb4575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201610f74565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361010357565b6040810190811067ffffffffffffffff821117610a2157604052565b6080810190811067ffffffffffffffff821117610a2157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610a2157604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081611099575090565b905042101590565b906110e057508051156110b657805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580611138575b6110f1575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b156110e956fea164736f6c6343000817000a6101608060405234620006e75762001f3290813803809262000022828462000733565b82396060818381010312620006e75780516001600160401b038111620006e7578101916101009283818385010312620006e757604051908482016001600160401b03811183821017620007015760405280516001600160a01b0381168103620006e7578252620000956020820162000757565b6020830152620000a86040820162000765565b604083015260608101516001600160a01b0381168103620006e757606083015260808101516001600160401b038111620006e757620000ed90848601908301620007b9565b608083015260a0818101519083015260c08101516001600160401b038111620006e75762000134916200012860e092868801908301620007b9565b60c08501520162000757565b60e08201526020830151916001600160a01b0383168303620006e7576040840151906001600160401b038211620006e757808501601f838701011215620006e75784820151906001600160401b0382116200070157604051956200019f60208460051b018862000733565b828752602087019382820160208560061b838501010111620006e75793602085830101945b60208560061b82850101018610620006815750505050505060c081015151602081116200066257506060810151600080546001600160a01b0319166001600160a01b03928316178155825190911660809081526020830151151560a052604083015164ffffffffff1660c0528201518051919290916001600160401b0381116200064e5760019283548481811c9116801562000643575b60208210146200062f57601f8111620005e2575b50602090601f8311600114620005785760e09392918691836200056c575b5050600019600383901b1c191690841b1783555b60a0810151825260c0810151604051620002db60208281620002cd818301968781519384920162000794565b810103808452018262000733565b519051906020811062000559575b5087520151151593610120948552610140938452805190835b82811062000499575050505060018060a01b036080511660018060a01b03835116906040519160208301848063095ea7b360e01b92838152846024880152600019604488015260448752620003578762000717565b86519082875af16200036862000806565b8162000458575b50806200044d575b1562000409575b60405161160a9087898b84620009288539608051848181610512015281816108aa0152610fb8015260a0518481816108d10152610eca015260c05184818161022b01528181610e4e015281816111450152611365015260e05184818161035f01526106e30152518361122a0152518281816108f90152610e8d0152518181816101350152610a630152f35b62000442946200043c926040519260208401526024830152604482015260448152620004358162000717565b826200083b565b6200083b565b38808080806200037e565b50823b151562000377565b80518015925082156200046f575b5050386200036f565b8192509060209181010312620004955760206200048d910162000757565b388062000466565b8580fd5b8151811015620005455760208160051b8301015160048054906801000000000000000082101562000532578682018082558210156200051f578752602080882083519201805493909101516cffffffffff000000000000000060409190911b166001600160401b039092166001600160681b031990931692909217179055830162000302565b634e487b7160e01b885260329052602487fd5b634e487b7160e01b885260419052602487fd5b634e487b7160e01b85526032600452602485fd5b6000199060200360031b1b1638620002e9565b0151905038806200028d565b92918491601f198216908388526020882091885b818110620005c95750958360e09710620005af575b505050811b018355620002a1565b015160001960f88460031b161c19169055388080620005a1565b828801518455889590930192602092830192016200058c565b84865260208620601f840160051c8101916020851062000624575b601f0160051c019085905b828110620006185750506200026f565b87815501859062000608565b9091508190620005fd565b634e487b7160e01b86526022600452602486fd5b90607f16906200025b565b634e487b7160e01b84526041600452602484fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b6040868585010312620006e757604080519081016001600160401b03811182821017620006ec5760405286516001600160401b0381168103620006e75760209382859260409452620006d5838b0162000765565b838201528152019601959150620001c4565b600080fd5b60246000634e487b7160e01b81526041600452fd5b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b038211176200070157604052565b601f909101601f19168101906001600160401b038211908210176200070157604052565b51908115158203620006e757565b519064ffffffffff82168203620006e757565b6001600160401b0381116200070157601f01601f191660200190565b60005b838110620007a85750506000910152565b818101518382015260200162000797565b81601f82011215620006e7578051620007d28162000778565b92620007e2604051948562000733565b81845260208284010111620006e75762000803916020808501910162000794565b90565b3d1562000836573d906200081a8262000778565b916200082a604051938462000733565b82523d6000602084013e565b606090565b600080620008689260018060a01b03169360208151910182865af16200086062000806565b9083620008bf565b805190811515918262000898575b5050620008805750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620006e7576020620008b6910162000757565b15388062000876565b90620008e85750805115620008d657805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806200091d575b620008fb575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15620008f256fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde0314611213575080631686c90914610eef57806316c3549d14610eb25780631bfd681414610e755780633f31ae3f146105365780634800d97f146104e557806349fc73dd146103a75780634e390d3e1461038257806351e75e8b1461034757806375829def1461026a57806390e64d131461024f578063bb4b57341461020d578063bf4ed03f1461019b578063ce51650714610159578063da792468146101085763f851a440146100cf57600080fd5b3461010357600060031936011261010357602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b3461010357600060031936011261010357602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461010357602060031936011261010357602061019160043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b34610103576000600319360112610103576101b46113b2565b6040516020918282018383528151809152836040840192019360005b8281106101dd5784840385f35b8551805167ffffffffffffffff16855282015164ffffffffff1684830152948101946040909301926001016101d0565b3461010357600060031936011261010357602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461010357600060031936011261010357602061019161135d565b34610103576020600319360112610103576102836112c1565b60005473ffffffffffffffffffffffffffffffffffffffff808216923384036102fa577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346101035760006003193601126101035760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461010357600060031936011261010357602064ffffffffff60035416604051908152f35b346101035760006003193601126101035760405160009060018054908160011c90600183169283156104db575b60209384841081146104ac5783865290811561046e5750600114610413575b61040f846104038188038261131c565b6040519182918261125b565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b82841061045b575050508161040f9361040392820101936103f3565b805485850187015292850192810161043f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506104038161040f6103f3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f16916103d4565b3461010357600060031936011261010357602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101035760806003193601126101035760243573ffffffffffffffffffffffffffffffffffffffff81168103610103576fffffffffffffffffffffffffffffffff6044351660443503610103576064359067ffffffffffffffff821161010357366023830112156101035781600401359167ffffffffffffffff83116101035760248360051b820101368111610103576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff851660408201526fffffffffffffffffffffffffffffffff6044351660608201526060815261061781611300565b5190206040516020810191825260208152610631816112e4565b5190209161063d61135d565b610e185761066660043560ff6001918060081c6000526002602052161b60406000205416151590565b610de6576106738561139a565b94610681604051968761131c565b8552602401602085015b828210610dd6575050506000905b83518210156106df576106ac828561144d565b5190818110156106cc57600052602052600160406000205b910190610699565b90600052602052600160406000206106c4565b90507f000000000000000000000000000000000000000000000000000000000000000003610dac5760035464ffffffffff811615610d78575b506107216113b2565b906000825161072f8161139a565b9361073d604051958661131c565b8185527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061076a8361139a565b0160005b818110610d535750506000905b828210610c2e5750506fffffffffffffffffffffffffffffffff604435166fffffffffffffffffffffffffffffffff831611610bff576fffffffffffffffffffffffffffffffff604435166fffffffffffffffffffffffffffffffff831610610bab575b505060043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff600054169160405190610829826112e4565b6000825260006020830152604051938461010081011067ffffffffffffffff61010087011117610b7c576101008501604052845273ffffffffffffffffffffffffffffffffffffffff831660208501526fffffffffffffffffffffffffffffffff60443516604085015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608501527f0000000000000000000000000000000000000000000000000000000000000000151560808501527f0000000000000000000000000000000000000000000000000000000000000000151560a085015260c084015260e0830152604051917f897f362b0000000000000000000000000000000000000000000000000000000083526020600484015282610144810173ffffffffffffffffffffffffffffffffffffffff835116602483015273ffffffffffffffffffffffffffffffffffffffff60208401511660448301526fffffffffffffffffffffffffffffffff604084015116606483015273ffffffffffffffffffffffffffffffffffffffff60608401511660848301526080830151151560a483015260a0830151151560c483015260c08301519061012060e484015281518091526020610164840192019060005b818110610b3e575050508190602060e08195015173ffffffffffffffffffffffffffffffffffffffff81511661010485015201516101248301520381600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1918215610b3257600092610afd575b60208380847f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff81519360043585526fffffffffffffffffffffffffffffffff60443516888601521692a3604051908152f35b91506020823d602011610b2a575b81610b186020938361131c565b81010312610103576020915191610a94565b3d9150610b0b565b6040513d6000823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff168186015288955060409094019390920191600101610a0e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6fffffffffffffffffffffffffffffffff610be97fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8293018661144d565b51926044350316818351160116905282806107df565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b9092610c6367ffffffffffffffff610c46868561144d565b5151166fffffffffffffffffffffffffffffffff60443516611490565b6fffffffffffffffffffffffffffffffff8111610d22576fffffffffffffffffffffffffffffffff8091169164ffffffffff6020610ca1888761144d565b5101511660405190610cb2826112e4565b8482526020820152610cc4878a61144d565b52610ccf868961144d565b5016016fffffffffffffffffffffffffffffffff8111610cf357926001019061077b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b602490604051907f4916adce0000000000000000000000000000000000000000000000000000000082526004820152fd5b602090604051610d62816112e4565b6000815260008382015282828a0101520161076e565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161760035581610718565b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b813581526020918201910161068b565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b60446040517f442b184100000000000000000000000000000000000000000000000000000000815242600482015264ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166024820152fd5b346101035760006003193601126101035760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346101035760006003193601126101035760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b3461010357604060031936011261010357610f086112c1565b60248035906fffffffffffffffffffffffffffffffff82168092036101035773ffffffffffffffffffffffffffffffffffffffff80600054163381036111c4575064ffffffffff80600354168015158061117f575b80611170575b6111115750506040519160209260008084868401987fa9059cbb000000000000000000000000000000000000000000000000000000008a521697888585015287604485015260448452610fb584611300565b857f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15611105573d9067ffffffffffffffff82116110d757906110409160405191611030887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116018461131c565b82523d60008884013e5b8361155d565b80518581151591826110b6575b50509050611088575050907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b8380929350010312610103578401518015908115036101035780858961104d565b837f4e487b710000000000000000000000000000000000000000000000000000000060005260416004526000fd5b6110409060609061103a565b6064925083604051927f92b666970000000000000000000000000000000000000000000000000000000084524260048501527f000000000000000000000000000000000000000000000000000000000000000016908301526044820152fd5b5061117961135d565b15610f63565b5062093a8081018281116111965782164211610f5d565b847f4e487b710000000000000000000000000000000000000000000000000000000060005260116004526000fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346101035760006003193601126101035761040f907f0000000000000000000000000000000000000000000000000000000000000000602082015260208152610403816112e4565b60208082528251818301819052939260005b8581106112ad575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b81810183015184820160400152820161126d565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361010357565b6040810190811067ffffffffffffffff821117610b7c57604052565b6080810190811067ffffffffffffffff821117610b7c57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610b7c57604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081611392575090565b905042101590565b67ffffffffffffffff8111610b7c5760051b60200190565b600454906113bf8261139a565b9160406113cf604051948561131c565b8184528360208091019160046000527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b906000935b85851061141357505050505050565b60018481928451611423816112e4565b64ffffffffff875467ffffffffffffffff81168352871c1683820152815201930194019391611404565b80518210156114615760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8382098382029182808310920391808303921461154c57670de0b6b3a7640000908183101561151557947faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac1066994950990828211900360ee1b910360121c170290565b60449086604051917f5173648d00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5050670de0b6b3a764000090049150565b9061159c575080511561157257805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b815115806115f4575b6115ad575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b156115a556fea164736f6c6343000817000aa164736f6c6343000817000a"; /*////////////////////////////////////////////////////////////////////////// DEPLOYERS diff --git a/src/abstracts/SablierV2MerkleLockup.sol b/src/abstracts/SablierV2MerkleLockup.sol index cd269866..cd5c1783 100644 --- a/src/abstracts/SablierV2MerkleLockup.sol +++ b/src/abstracts/SablierV2MerkleLockup.sol @@ -48,6 +48,9 @@ abstract contract SablierV2MerkleLockup is /// @dev Packed booleans that record the history of claims. BitMaps.BitMap internal _claimedBitMap; + /// @dev The timestamp when the first claim is made. + uint40 internal _firstClaimTime; + /*////////////////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////////////////*/ @@ -76,6 +79,11 @@ abstract contract SablierV2MerkleLockup is USER-FACING CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ + /// @inheritdoc ISablierV2MerkleLockup + function getFirstClaimTime() external view override returns (uint40) { + return _firstClaimTime; + } + /// @inheritdoc ISablierV2MerkleLockup function hasClaimed(uint256 index) public view override returns (bool) { return _claimedBitMap.get(index); @@ -97,11 +105,12 @@ abstract contract SablierV2MerkleLockup is /// @inheritdoc ISablierV2MerkleLockup function clawback(address to, uint128 amount) external override onlyAdmin { - // Check: the campaign is not expired. - if (!hasExpired()) { - revert Errors.SablierV2MerkleLockup_CampaignNotExpired({ + // Check: current timestamp is over the grace period and the campaign has not expired. + if (_hasGracePeriodPassed() && !hasExpired()) { + revert Errors.SablierV2MerkleLockup_ClawbackNotAllowed({ blockTimestamp: block.timestamp, - expiration: EXPIRATION + expiration: EXPIRATION, + firstClaimTime: _firstClaimTime }); } @@ -117,7 +126,7 @@ abstract contract SablierV2MerkleLockup is //////////////////////////////////////////////////////////////////////////*/ /// @dev Validates the parameters of the `claim` function, which is implemented by child contracts. - function _checkClaim(uint256 index, bytes32 leaf, bytes32[] calldata merkleProof) internal view { + function _checkClaim(uint256 index, bytes32 leaf, bytes32[] calldata merkleProof) internal { // Check: the campaign has not expired. if (hasExpired()) { revert Errors.SablierV2MerkleLockup_CampaignExpired({ @@ -135,5 +144,16 @@ abstract contract SablierV2MerkleLockup is if (!MerkleProof.verify(merkleProof, MERKLE_ROOT, leaf)) { revert Errors.SablierV2MerkleLockup_InvalidProof(); } + + // Effect: set the `_firstClaimTime` if its zero. + if (_firstClaimTime == 0) { + _firstClaimTime = uint40(block.timestamp); + } + } + + /// @notice Returns a flag indicating whether the grace period has passed. + /// @dev The grace period is 7 days after the first claim. + function _hasGracePeriodPassed() internal view returns (bool) { + return _firstClaimTime > 0 && block.timestamp > _firstClaimTime + 7 days; } } diff --git a/src/interfaces/ISablierV2MerkleLockup.sol b/src/interfaces/ISablierV2MerkleLockup.sol index 009e1b3d..73576eb3 100644 --- a/src/interfaces/ISablierV2MerkleLockup.sol +++ b/src/interfaces/ISablierV2MerkleLockup.sol @@ -36,6 +36,9 @@ interface ISablierV2MerkleLockup is IAdminable { /// @dev This is an immutable state variable. function EXPIRATION() external returns (uint40); + /// @notice Returns the timestamp when the first claim is made. + function getFirstClaimTime() external view returns (uint40); + /// @notice Returns a flag indicating whether a claim has been made for a given index. /// @dev Uses a bitmap to save gas. /// @param index The index of the recipient to check. @@ -68,7 +71,9 @@ interface ISablierV2MerkleLockup is IAdminable { /// /// Requirements: /// - The caller must be the admin. - /// - The campaign must either be expired or not have an expiration. + /// - No claim must be made, OR + /// The current timestamp must not exceed 7 days after the first claim, OR + /// The campaign must be expired. /// /// @param to The address to receive the tokens. /// @param amount The amount of tokens to claw back. diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index a70d5285..e64958d8 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -20,8 +20,9 @@ library Errors { /// @notice Thrown when trying to create a campaign with a name that is too long. error SablierV2MerkleLockup_CampaignNameTooLong(uint256 nameLength, uint256 maxLength); - /// @notice Thrown when trying to clawback when the campaign has not expired. - error SablierV2MerkleLockup_CampaignNotExpired(uint256 blockTimestamp, uint40 expiration); + /// @notice Thrown when trying to clawback when the current timestamp is over the grace period and the campaign has + /// not expired. + error SablierV2MerkleLockup_ClawbackNotAllowed(uint256 blockTimestamp, uint40 expiration, uint40 firstClaimTime); /// @notice Thrown when trying to claim with an invalid Merkle proof. error SablierV2MerkleLockup_InvalidProof(); diff --git a/test/integration/merkle-lockup/ll/clawback/clawback.t.sol b/test/integration/merkle-lockup/ll/clawback/clawback.t.sol index c0079bd7..226b62a6 100644 --- a/test/integration/merkle-lockup/ll/clawback/clawback.t.sol +++ b/test/integration/merkle-lockup/ll/clawback/clawback.t.sol @@ -23,27 +23,54 @@ contract Clawback_Integration_Test is MerkleLockup_Integration_Test { _; } - function test_RevertGiven_CampaignNotExpired() external whenCallerAdmin { + function test_Clawback_BeforeFirstClaim() external whenCallerAdmin { + test_Clawback(users.admin); + } + + modifier afterFirstClaim() { + // Make the first claim to set `_firstClaimTime`. + claimLL(); + _; + } + + function test_Clawback_GracePeriod() external whenCallerAdmin afterFirstClaim { + vm.warp({ newTimestamp: block.timestamp + 6 days }); + test_Clawback(users.admin); + } + + modifier postGracePeriod() { + vm.warp({ newTimestamp: block.timestamp + 8 days }); + _; + } + + function test_RevertGiven_CampaignNotExpired() external whenCallerAdmin afterFirstClaim postGracePeriod { vm.expectRevert( abi.encodeWithSelector( - Errors.SablierV2MerkleLockup_CampaignNotExpired.selector, block.timestamp, defaults.EXPIRATION() + Errors.SablierV2MerkleLockup_ClawbackNotAllowed.selector, + block.timestamp, + defaults.EXPIRATION(), + defaults.FIRST_CLAIM_TIME() ) ); merkleLL.clawback({ to: users.admin, amount: 1 }); } modifier givenCampaignExpired() { - // Make a claim to have a different contract balance. - claimLL(); vm.warp({ newTimestamp: defaults.EXPIRATION() + 1 seconds }); _; } - function test_Clawback() external whenCallerAdmin givenCampaignExpired { + function test_Clawback() external whenCallerAdmin afterFirstClaim postGracePeriod givenCampaignExpired { test_Clawback(users.admin); } - function testFuzz_Clawback(address to) external whenCallerAdmin givenCampaignExpired { + function testFuzz_Clawback(address to) + external + whenCallerAdmin + afterFirstClaim + postGracePeriod + givenCampaignExpired + { vm.assume(to != address(0)); test_Clawback(to); } diff --git a/test/integration/merkle-lockup/ll/clawback/clawback.tree b/test/integration/merkle-lockup/ll/clawback/clawback.tree index feef74bc..b961e888 100644 --- a/test/integration/merkle-lockup/ll/clawback/clawback.tree +++ b/test/integration/merkle-lockup/ll/clawback/clawback.tree @@ -2,8 +2,16 @@ clawback.t.sol ├── when the caller is not the admin │ └── it should revert └── when the caller is the admin - ├── given the campaign has not expired - │ └── it should revert - └── given the campaign has expired - ├── it should perform the ERC-20 transfer - └── it should emit a {Clawback} event + ├── when the first claim has not been made + │ ├── it should perform the ERC-20 transfer + │ └── it should emit a {Clawback} event + └── when the first claim has been made + ├── given the current time is not more than 7 days after the first claim + │ ├── it should perform the ERC-20 transfer + │ └── it should emit a {Clawback} event + └── given the current time is more than 7 days after the first claim + ├── given the campaign has not expired + │ └── it should revert + └── given the campaign has expired + ├── it should perform the ERC-20 transfer + └── it should emit a {Clawback} event diff --git a/test/integration/merkle-lockup/ll/get-first-claim-time/getFirstClaimTime.t.sol b/test/integration/merkle-lockup/ll/get-first-claim-time/getFirstClaimTime.t.sol new file mode 100644 index 00000000..bd61c2e1 --- /dev/null +++ b/test/integration/merkle-lockup/ll/get-first-claim-time/getFirstClaimTime.t.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22 <0.9.0; + +import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; + +contract GetFirstClaimTime_Integration_Test is MerkleLockup_Integration_Test { + function setUp() public virtual override { + MerkleLockup_Integration_Test.setUp(); + } + + function test_GetFirstClaimTime_BeforeFirstClaim() external view { + uint256 firstClaimTime = merkleLL.getFirstClaimTime(); + assertEq(firstClaimTime, 0); + } + + modifier afterFirstClaim() { + // Make the first claim to set `_firstClaimTime`. + claimLL(); + _; + } + + function test_GetFirstClaimTime() external afterFirstClaim { + uint256 firstClaimTime = merkleLL.getFirstClaimTime(); + assertEq(firstClaimTime, block.timestamp); + } +} diff --git a/test/integration/merkle-lockup/ll/get-first-claim-time/getFirstClaimTime.tree b/test/integration/merkle-lockup/ll/get-first-claim-time/getFirstClaimTime.tree new file mode 100644 index 00000000..cab77842 --- /dev/null +++ b/test/integration/merkle-lockup/ll/get-first-claim-time/getFirstClaimTime.tree @@ -0,0 +1,5 @@ +getFirstClaimTime.t.sol +├── when the first claim has not been made +│ └── it should return 0 +└── when the first claim has been made + └── it should return the time of the first claim diff --git a/test/integration/merkle-lockup/lt/clawback/clawback.t.sol b/test/integration/merkle-lockup/lt/clawback/clawback.t.sol index 119e2836..41c8f0a5 100644 --- a/test/integration/merkle-lockup/lt/clawback/clawback.t.sol +++ b/test/integration/merkle-lockup/lt/clawback/clawback.t.sol @@ -23,27 +23,53 @@ contract Clawback_Integration_Test is MerkleLockup_Integration_Test { _; } - function test_RevertGiven_CampaignNotExpired() external whenCallerAdmin { + function test_Clawback_BeforeFirstClaim() external whenCallerAdmin { + test_Clawback(users.admin); + } + + modifier afterFirstClaim() { + claimLT(); + _; + } + + function test_Clawback_GracePeriod() external whenCallerAdmin afterFirstClaim { + vm.warp({ newTimestamp: block.timestamp + 6 days }); + test_Clawback(users.admin); + } + + modifier postGracePeriod() { + vm.warp({ newTimestamp: block.timestamp + 8 days }); + _; + } + + function test_RevertGiven_CampaignNotExpired() external whenCallerAdmin afterFirstClaim postGracePeriod { vm.expectRevert( abi.encodeWithSelector( - Errors.SablierV2MerkleLockup_CampaignNotExpired.selector, block.timestamp, defaults.EXPIRATION() + Errors.SablierV2MerkleLockup_ClawbackNotAllowed.selector, + block.timestamp, + defaults.EXPIRATION(), + defaults.FIRST_CLAIM_TIME() ) ); merkleLT.clawback({ to: users.admin, amount: 1 }); } modifier givenCampaignExpired() { - // Make a claim to have a different contract balance. - claimLT(); vm.warp({ newTimestamp: defaults.EXPIRATION() + 1 seconds }); _; } - function test_Clawback() external whenCallerAdmin givenCampaignExpired { + function test_Clawback() external whenCallerAdmin afterFirstClaim postGracePeriod givenCampaignExpired { test_Clawback(users.admin); } - function testFuzz_Clawback(address to) external whenCallerAdmin givenCampaignExpired { + function testFuzz_Clawback(address to) + external + whenCallerAdmin + afterFirstClaim + postGracePeriod + givenCampaignExpired + { vm.assume(to != address(0)); test_Clawback(to); } diff --git a/test/integration/merkle-lockup/lt/clawback/clawback.tree b/test/integration/merkle-lockup/lt/clawback/clawback.tree index feef74bc..b961e888 100644 --- a/test/integration/merkle-lockup/lt/clawback/clawback.tree +++ b/test/integration/merkle-lockup/lt/clawback/clawback.tree @@ -2,8 +2,16 @@ clawback.t.sol ├── when the caller is not the admin │ └── it should revert └── when the caller is the admin - ├── given the campaign has not expired - │ └── it should revert - └── given the campaign has expired - ├── it should perform the ERC-20 transfer - └── it should emit a {Clawback} event + ├── when the first claim has not been made + │ ├── it should perform the ERC-20 transfer + │ └── it should emit a {Clawback} event + └── when the first claim has been made + ├── given the current time is not more than 7 days after the first claim + │ ├── it should perform the ERC-20 transfer + │ └── it should emit a {Clawback} event + └── given the current time is more than 7 days after the first claim + ├── given the campaign has not expired + │ └── it should revert + └── given the campaign has expired + ├── it should perform the ERC-20 transfer + └── it should emit a {Clawback} event diff --git a/test/integration/merkle-lockup/lt/get-first-claim-time/getFirstClaimTime.t.sol b/test/integration/merkle-lockup/lt/get-first-claim-time/getFirstClaimTime.t.sol new file mode 100644 index 00000000..3e286f81 --- /dev/null +++ b/test/integration/merkle-lockup/lt/get-first-claim-time/getFirstClaimTime.t.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22 <0.9.0; + +import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; + +contract GetFirstClaimTime_Integration_Test is MerkleLockup_Integration_Test { + function setUp() public virtual override { + MerkleLockup_Integration_Test.setUp(); + } + + function test_GetFirstClaimTime_BeforeFirstClaim() external view { + uint256 firstClaimTime = merkleLT.getFirstClaimTime(); + assertEq(firstClaimTime, 0); + } + + modifier afterFirstClaim() { + // Make the first claim to set `_firstClaimTime`. + claimLT(); + _; + } + + function test_GetFirstClaimTime() external afterFirstClaim { + uint256 firstClaimTime = merkleLT.getFirstClaimTime(); + assertEq(firstClaimTime, block.timestamp); + } +} diff --git a/test/integration/merkle-lockup/lt/get-first-claim-time/getFirstClaimTime.tree b/test/integration/merkle-lockup/lt/get-first-claim-time/getFirstClaimTime.tree new file mode 100644 index 00000000..cab77842 --- /dev/null +++ b/test/integration/merkle-lockup/lt/get-first-claim-time/getFirstClaimTime.tree @@ -0,0 +1,5 @@ +getFirstClaimTime.t.sol +├── when the first claim has not been made +│ └── it should return 0 +└── when the first claim has been made + └── it should return the time of the first claim diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index c2b0afdd..12cbdf93 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -44,6 +44,7 @@ contract Defaults is Merkle { bool public constant CANCELABLE = false; uint128 public constant CLAIM_AMOUNT = 10_000e18; uint40 public immutable EXPIRATION; + uint40 public immutable FIRST_CLAIM_TIME; uint256 public constant INDEX1 = 1; uint256 public constant INDEX2 = 2; uint256 public constant INDEX3 = 3; @@ -76,6 +77,7 @@ contract Defaults is Merkle { CLIFF_TIME = START_TIME + CLIFF_DURATION; END_TIME = START_TIME + TOTAL_DURATION; EXPIRATION = uint40(block.timestamp) + 12 weeks; + FIRST_CLAIM_TIME = uint40(block.timestamp); // Initialize the Merkle tree. LEAVES[0] = MerkleBuilder.computeLeaf(INDEX1, users.recipient1, CLAIM_AMOUNT); From 64a849b22e925d8d909d49f4e1cbf93a73e0cd66 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Sat, 4 May 2024 02:49:45 +0100 Subject: [PATCH 45/61] refactor: `Range` to `Timestamp` (#335) * refactor: rename Range struct to Timestamp * refactor: rename timestamp to timestamps * refactor: rename Timestamp to Timestamps --- bun.lockb | Bin 307932 -> 307851 bytes package.json | 2 +- src/SablierV2BatchLockup.sol | 2 +- src/types/DataTypes.sol | 2 +- .../batch-lockup/createWithTimestampsLL.t.sol | 15 +++++++++------ test/utils/BatchLockupBuilder.sol | 2 +- test/utils/Defaults.sol | 6 +++--- 7 files changed, 16 insertions(+), 13 deletions(-) diff --git a/bun.lockb b/bun.lockb index fbf1247899869434e4fd68677dd0d1224df23551..eff7a86c3a6f82be8ddca6baa3e02297fb6dde09 100755 GIT binary patch delta 41838 zcmeFad3;S*7e9Q?y}8^IVoXRx5c8CfL~_GTa$^ob%>+T_giJD-C1y2q$`%AcjWrbw zRbx>@6*UH-s#@9_TU1T&cb&aY()Q8k_xrr>`{#2%UDjUTz4qQ~uf6tghUVP3TWQwa zO7lHy@9aIV!oaK>`*tbor!4TPv8>O9M|s~o=-2V;ld$y-9kx7pwPQi|)QF=7NwSxu zoQ#Ob#287EJ{R&2MvgBBFN^$4J4vbvJ`B7J_(G8%XX5#TzzKf>?f@QKikF|Py2~}Q zYm}Cx%0TVG9l>8Aw>)@yOj1@%nj{@DOOgV4bVOoeLVAX@16Ginm=Yb49xq92AeV#u z2q0P04JH#$i-O7corg^ICnY2&B%uN@4L=SXR%Pc{g+g7FqKfjsse+&J53+J*fIESY zgYDFfRLIWYaVZ&EMc<7M|n@|T6Y51hLD2EC#D-@(c79l)vHRp1q+C@E(S5@cb& zioAkxT85NUw-T>F$A`y^&Y~870#1fh04I+ia80%@iA!$8R-ehv55(B@fla4N3|~%ex3(TauWJX<)gsK{JJh=zLZlM z+zmV?EiEO@BW9FKHLm|0dX)beWaww-tV4p#846pqisB;@hod|9A(J8T5$PVWi4k#^ zAXB&gEo_32UmN-T&;a?>y9Vd=z{%f-z^VReq61M{JJ$bfXsqa2L~=%AS_?^v7^b;b z(j#$kElGmSIepZMb`7$#M87lAJ(5$RW2BW0ct1xc)0jxoB*zt|Ja6wM8deW|D#BWXc zaAu^Zq-CJGFY9r)f8oKsumdvUB&VMzH>(Rc4QF~vqBcS5HRH`^B&Cvd(iX_%J$?GQ zsGUp=S{60uTK^29YTmro;fWC=sr0b)l;qfi#2DT8Wz-#}s;zhUaILh6ksgsL(W5hy zQxj5SB&ijgPOdr(P9xb2oNNp8<#KvPL`G(MOnRuQl&aaO3wnYnlvB#W$N7~X*G@{o z2&3)B{@l^?z^OLG5;;Vg3Yl6N2Tnb!p^hk3HQTNg_tUeMyw;fXsEEXf=$NR)gv<2dt|0c z>w~z$a=}wQ5>uigGGf57US&wjZ9M;ytJ4P@Bm!$+= z2$`net6*L{GA$x12DYth%k_Q*r}9DVczqGcqmv>sqT+voy@cNoc5i^!$q!lWwR*C1 zhQ~xjMGS}2I&jCOfm4BK!DYdJLjlde6psw@No*?TD@BE|=~yf>(jqp6@Zr)!NB4{F z58Xf9FY3C=m9nRz3aa{OXJJAjkKM>*&tcS5-B8}gLU5W|r@$$4Vz7gxW+X(N?!sr% z9`MS*w}De1u>!?Y4Ut&MB9fyey}nemM+>U?1I^UzoOj)Lf#-s!Mh=gT#Z*X-kI*c5 z4MnOjqdVunK(7pB7Z^f3DcV=ldxm_HzZdmHru!nSBq=f?J;q~{BwYkv4*AP_a=sAU zUK%FlL?%WIkAbB}p+G$>0H?LDIV`2v*DYSqo6DEMsiAmq(wi#qOmHe6Ck7=Zc|>MH zT1=Wg`s#OzbEoG0cwv1>X#kme=LSv_YoKUU&yR>s@)(nlDg^+i%5|gas;%v-W}gk? zfvFDQrK3_(h9|^ep9fB+?G}6;I1Sp7ft)XcOkJ1j|BGu_Xckc zE(`f$xFlgM%vlYNAAO~{ng?xRGBsBy6LtRO#K?T5TfxCg%1fL@C8+CZY z(IQ_zpiDw1u&3teD?tGA8$uqI?vWmyo+!OVxtdb8loKAud(s1(dh#7O`SUX<)CbSO zsfK#?2Aslk5pZ|#BdCz@O2fEcvw>5C`yo@5>;k8Hl7LqNubs&Ae;>}ncL%t0HZq2z z7H9B00FK}a6kWh?kOKHgaB6r;GA}qCGBxC$!YhsiP98c2nLL!5%JDwnv@?|(!8cBQ zz6AlV3Vd`-dU7_(NTabCri0f&w37$)MYj?xF9W$0IN^W7P~x+oOosVqaKkWBQ%1sw zGmt4}8iAJwuLVvXZ~~`#vPbb)yADqI&$Hlv%7{xzjYj*@hAdve18^!BOGlW9sCc)L zynVM}`arCl@yv%RN{Qauvwa$MK%0iUzxl=N`NTPW22J3I9{#EPx8& z2f%5W-Ypc`f|G%RQ2{j&1w&|TJ3=NGHW$2x$hQNhGm0I0O!a&y4F46}8S)iyTDnev zlLr#A(>>B+;$lV>0H6X1=@DsZ5u>Gi$kc6+EL(S zNWdI!=_>VQxdydOb9r?yfl~h-iQM#g96vTsZByQ}+Ku^qTpuH1shbNHaDHWhImn-xKnR}J3>AV4m>;OGAgFwKAp?EVO`4Y%gl)NC$D`9nYyQMc^gFjKQ|J+Bl-rS zcW5E>$v{1S5@d4njy!6oH&#<5$h#S7F)<0Tqa|-BP*3ZEQ(QTNlK~0I>2wt$4PVLS z0&vn(R&me0f=v3GLR9;TH4@I`b2VLk1T8E09RNb#k2$Bc@Jihv3B zpKw#(0;kqeBQoMWa4q;$$oIjil^fvH1^xOmHIpCd%WUQNdE}Qt{>b=*DC{f}=F2F| zm~3eY6v({c+qhz&sAv#eO?ZMh6lBDwrDVp%|Lc8#f8HO^?+c`j(oSVk-ga*2LU1x9 zHX$xEEv8PoG#P;ApZJKh=$H&?j8H6|YidO0s`5Z}bmf|c&_Q_`YGrbVPa7ILhJ zqli%nDU_`JIUhPPq}e%#Frg^maUF$oa}3JUy=VTKM_(%T9`f?Tb7I2d zk`wER)Z#p^?<#N#lB3`lyv8}Zks$Z42B*-yrM+)e3hDEkwSWg zu93FuCEn*m;8ZGRRBBpGdb*Sqp^c?9@-oLWMyJMjB&16Xu5fuca9TOiV{lP}-w?G^ z)tZWj(5|bFs9H0-4VrL zE7|-rY$sdqL8jFyA}T5-6&HlviggCnIr7Lo2DNap`^uVeR@8#FRt7VL@)K?wh)j&>FbG>YaJ+gYuE9gH|AE0PM z6y>Tl)i7_Hp~#>v^$wDq)Ix8Y+(UKovB?|MFdv)p&?rd(P&TO%J{GyLS_o{moh0=I zW>oWASd{M}1wk^CB-c`NTiBFzlOzQLGicaO)y3DQyam=q!>FdtYOb%%G_8~*bx_0C z29!mj8C3$eIG|7yb-tHb_EN+AY;ug6>t|E)N=s5VUZt1C{0pS+kXU|#p}D4ORl_Vc zd9Ipku_>ofO)KPc6DsTP(_DNTh30Z!h*&U92{F zqZ($l8GbLTF0}?J%@LSgkS(kEUKV+}>Jnfx9CuI$1O&;>YHk4hU5`_>aNM8OzXzg!?&=~@T#Mh!Im{??{ z<{>2p5^p9Vz(NMwY>F3_R2nY$%E#XhLOXJysQ{_wYPh#md5RSE+ene2G)Gi-hh#@B zn{puaV-dcFs%rDL)z$gE6f=+z7!~G?Rq$2H z0-!-av`fxvM2I#mObOUVd24Hs@bx5~Ow!fD5S#KPFg{oa z6IoWnI@y%AZoD;Fjj&i8vLVI(vnk~+P#$vVtSP7lB(0khkRroS7K48h61fDSfI%|W zlq4%8do@4VUkwYjDXBon&81ax$}UKlK$wreB1Kk7VsQ!UVw2<4+%7g{do3PL$idjs zJ&8fkLx3PY2dQCQZSrb0x2w(VDspHq$%2DuIn46%^!N0Vr0;=OXZc|US6Un#%GZ{p z_S6u5HC1=NCTfLtiW=6LqerN@y={hL9_qE;L56o8YPCK=N`xo3533l4 zFb@*949ys>d#cy^1j)XtOJAEjK@IB*)3I37h-(wa@MkmiTHheW6IKLktzbgR$!b_X zo4i)d?PpUSAcv+Z;RZ%GALK2~bmM?gz*ZO0wz;<$;X1+2M z5*0_=h>iJ>#FEg~qMU%li_`jQP{Rh;lny?;ajbQI7Q+}H_1b_S!#*Fi+Q1;g3msYeLBP>bT=%e|uQY6>=6Essxuk0Eh0s24?$cqAa; z6?Z>wFfZ2w60D@j5^6C`CP|wNyOE-WNt;Bb$`(v84ex^#jRi(B+}{iV-ND>)M{oVn zA~nM+UIs}R8(=YAhtyon^RgPsv{aY22vRz>y|PRQfm}-RP(JC41r2@(w*oF8a0 z9e~tMb!%x=3;<~Z!Xh=<-wdHUSHgi}GbG+&T1|czl1ba(yb;qhH8CKVRD&T=Q+yj) z0*T6D3wrEthCo#!WKc;f;8YAV4dW3D3C>0_Iv#uqDM<75V@d(%o1&+U_fgYQVj)o( zZFH3NkVsRj-EkD}#fJOPoz%z=X+&=r4oSrEx)WZM6_t126)@jX(L@ zLBOi+XH_mCMZ?N>eU~=+4w&D=qJ%+$M<^OFDvKcT0H7{I5Cet*Zqb&T2;YqKH$$NK zhV@G{fvPC2t=)eji>ly0?1*IX;F%^u@>jzxR%I_zq{Zjl8ziaw+Ga0X)xzO6Wg^yB z!Z5`V1P3AU25Bp;q2?yqlpMs5r~`}T(PD|hu`^Bsl!wK#r$w17B*e9s#qe!M^;%Mp zY*Jm4Z8A2LWSf$P1%n*NyYLw#3Ve(PX4G@a(?-Ly6Zf39Z79)@xPcgTWwnsFkH3RN z_3?@13@_4HYdgHT8zhSE;uC-&r?Yx3HAuOD44M*JXv>~z*a(|4D3mMnXq^j*JdH)Q zt;KvF5-oJa6`8%d@EVE{d8C?~h7Ab#0BFM7$^Pc9e5PpYjA=HcR%%h2OZ%XQI4MC1Tjo49B-39R$V68lpDw)8*%DS@HdC?4&nl2 zj78~NEamsJ7`_Wrt7Qi%l@VKHpQPT7!!-jWS~mCskOzs{L{MXr7DA%2geE4b!$9sI z1b08I^M8{jL&7qMS}5=Q-=qqIxR-feM@TdRT*`q2Wwb!!ad@yKr58&rhT!>4v9tx! z$YQDP(7)#8K^jqdYYZ)D$01=j zauiK!7%fQ%1@wXP#zP`aND=;a5C;C`(#OlJG>PG5OKI9mAX%Y}E!>I|PK-KWT9D!% z%L9Rj#SlnwP|?;>({V_CYLTy1d5sh`%kg-q1gS;dR(YLTINfHrAE#EE5u~`q<0e0p zxZSHEk$(|=Q~b>d`ld^3?qEm=57CJNNPHk@;A;=#YZX6n4TD4>qD@r8>0xTMnL(xs z!?if=VWn&NnKpxeqFQZ^%`Q=r+Nyaot)@drb<$EwlAh{`RBH`WkqXsPH;Z!{BqN42 zEEg$W%MGO36K3#9QJ2mPQc_cN*XM8Ww}TLd+yHYbKki}(Fij6a`apG?9bku`=t`xP zOr&Uq$F(p{09znokD;#PJo-B%nv;k@tOE7ZByA=4FTxe5%9>sN0JGRH>qv^UqNybc}#guOoDXsW&8VT%5 zCH9%_0Kuup*J|<^hZ|m0*qo0PCUl9G@&*X?OS4sp!er}NEZu|DN|P{)OtmKn6HJ4U z3ek*NixdSOI)&BpA!+FPO2cfu1Mq?=7E%{AubtKODN^m#aH~~$ffSZo(RSAyU2;F@ zZ-zkK;buL8L}p`=A8JuTC+dyT2D1oKkd_yLgDM0*u23{JnS|b`;RCFu@kn*h_NgzC z!s!E(^%YVjwzr?G?+J8%9t){AuXLbA`3@3k@~{Y+!cVR+1~E1lQXAyi(Qd@;A@aol27`36c-8D$*1)T$rg=TOFi$&*G;7T)W_EB?}S_ z3D#50FTZ5(! zbI2vM%N2@@gFN});Sosil=DsjvU zAi+Sely|czmU(2RRPR;9JRq`l>T)4#B zVz{cR)i&eO{v%#(1vS5$#UTum1u8}~QV%Ba+rU`$Hrq_c7tvBwwApH^u~<)KA=O>O zjw3Z#OVwqFTP-!FICT!G-aK~+%~`4?3#l$z>IhO=u0xev?r=0u_uF=({a%Wkt6tk0 zWNN<@5uz3)S(O7wH76@Ir#*xetaU4Yrp4sB4B@NhjkYQgNCg1b4*yCXq@IxYh3g|o zEwwTf&kdIsI|k>mC`ddn;v;`^aTYcK(-la)>9}udl1E;3Yim_vkqSUn7y%6bJ|Uq| z1n3J$v|wucw&Jxy57&GwfDkAPp@pMBJ|v1y9?5qhQI8aD)vdRZj|r;WXi-K&qCA{J zamVq{N_D`lAaj*fe32>%@BTojLWDPt@yj9M0>s;DxUotdusewM%H1~QPQLC(+IK6j z<~5@ooRfPA3I0N0sgNkla4Lm|4nm^E2x0cAzj=-B=7`-ExwRTrK=-I}3v9}6boDd60;Rj8{~0|D=b#b0LWl?;FQ1lV+`*AAO+JNsD|yeDc?e(X|7#t zD)#I2IYP(RR*?L(y!>>FITcb6q++j_wgPFXx@B7pKdn=*eIBG#U9Vp&-Nqh2R1Mo_ zQ}Th)fNEEtN)aUTH{Yn-Hi-DtPAUT-Q716hanxTAiOOj!mFW&7Tqnj^6}OH0(v4Nm zJOUE+v)CwQHxS+-8ifats7{Om!l~INZXY5YJ3M0M_&5+0{eh>DJ$m5!Iw(zzP z8(5~oA(6%Wif$z&@&XpLG5%%<#S>L_Rtpc~p37G5LfoF}fdw5BU$Llzzm-T>4eM>w z!;IF(QIMz^KIuOf5=_`&QCX)Q>xuU+eI8Q{S&&3u;P_7<(GX$E-0(L;AnS3= zz;0T1H;)Z_O_~O&1y`I$cG)LJYg-Wn}Y%OUZRq6WT!)EVXY zC^{GD9(T|5H$$M-_&k^biQnBjB9wUd|Aa){#Jobh`t9LnXug*-)UdB@@-8(O|CUn= zzqXk>?d1)W%$kWnxZCp+Erxx2)um^Gl*a(cM(l8@7E|-j;X7@$&Oi#@ZDDoTic}|P zNciEB;y&L+UjU_^@&k>>K7NG3MCfEOWkSL>6=F5K*{3c&8)SI-1>I~n`5n+NsKG-1w7@iNHjE< z+TIrP=OV8p4oaQi?nQn#i@6mf(k{tQ1wxZfyPPU_5mF!|Bl=~b0BuZjGhIF2P)kzxP4MxdM!xtJEh+Upu1knAcdd+0tS2K9g&APKkIKk&F3%A znhA*%aa3OKU-oPMwsI-hQgWah1k?r<2W{pqgGBw|o85Uxe8SUW>3D{FlP@4WA^EFr z0|LxQ@OY!6%>hVM0bi=$KxzdE*5I~e+i$RB&~*zQhvnY184i4-Ub`7&dI8v~7TvU( zywB>XB%~~a;dxTwO`E9*vW;Y=`Z>N3;NBN@&{#G1JDc(cFzzxeiE^;&a@%Ged7iIN z#Uo=X0Mh&KH)_g!%P+mOMa(=95)EB(5hWi;H?23c+dqQD*Dko-;Bi46urElNctLNQ ze6$}DsbDCvguQ~q{fFYRk6L)gro>#-pAOKHw*Zn@e6hfufke&W)PoaIlS_Q?v?G<0 z4vE)HecYjj-L)x`F7r8n1sPY%pF$!HNVt%Cd|9pbBQB(_a1hfB?!s##KiW(?fO)Cm zU9HOZNCj&}=*qYD)#B=Y@i#;8MiwFg8_SfEtezIbuB+M|h^GK)boo_j(?X2Anztvw zjD)`iW7f$_RhRoV4C#1-s=mho<28lu)%fUFjxB;n+ zCSh<)&P8N>cq^;iUoHIArhG&)awT?lH`-3lxkl z5AnVMuuy1d3!CW&(on-u(t1np76roy)#VXx8v!F5ureTc?m{BRX|u)P_8mFdH1IoZ z#l#)|e57bZv}-%V1!O-CGPSvl@m2F4TTMB|sRKyiCi-J5o`>c>!7Zwtg-?2!cD8sC zVz)EnRlHrcLBd@$EJ?H~4Z=Tm;6sHR1x~AQw2 z34AIz{occ~wFag^ff|}F6#vQznkjJNq&Ev(0slzIiv(W`PQUkYD#wsd2IdOAT;o7q z;Zgw*r(_=fA;pyf|0^dvU*xY5`G4i5k-v%Z*?1THkEhh`7NPb&UIzJlME?Juyb9|5 z#)ub2X#_5y0UDv}q9Nkck0K!xC;1jQMZg^)-xc}9DR~e7P>1dd{xgC25vSw>A(!xM zE&n|kB{+@ba}=b8{{$yPUWT90l~>IJH_8 zdSp^9a4P36$`LoPK%-n$>r-8kT~F}(;P_8!jDM&a4-)=`lZDMhK5^3b2B+qH1pZ%m zW2n7fkt+5T75RZHEU>yhUIXh}Z!KtJD!0AUE zPLUi23V?V;Hoi3GHI+{hFmXy|;2*Lp3!L&tf>Zlrz^R@I;Pm?|CkIUfPUU8ZeBxAY z7C1rkg{*>8y&suyQA>NrG5}OCkF@?@aub_VOU$^nqW+J;sr?P2TH@61M(~>8Cj|ai zPG+1EIC1LlH{cYJ*9HDxxHiT9t0Eb20~Jw2H${d2H=O)*SLnZwQ@uZ$D)YJVR45WR zurv5;5bns9oj;feQ|2uj~|2uji7XI6jixv>drEOT$fmgc6%)1rU@uM!It?T>j?ALto zto5@B2aj&)V!n~r{-bWEuI#>F>uv6jjy?VMJ+<7Q_dIdR@zC+5qU+t9wRz6`f(MP{ zb5(<%_WW`0)||dIUx$3!qkZ>3`<)tlW7OssofsfHFpq(9sQfV-Jy7n)Mhyf| zM8JC1Xb^ygg8{gB5FP=bfNhKbupt6~LnMH`EG!a0 z|40D)3E0P!C;;Xt07+2*_OrbN>>4l|b+0M0Q0#>W8olAR>r z1OXng0FJTIu>eNJ0w^NjE7m9uK*KlybK(G;WQ7D=B_J>!z-jhjJb)ST03HxF9E<=cAtQI1aut+;5^G624KlB0B;Dmz&Z^F5HcLVmf-*{v6lq=K|pvSfGccc zB7hBv034D46tb`+0R58y>?hzlQl51L#m}x~6HC#`v;q4^JzhL0 ziH#mmtLn6xKfSP3K33YzyHdHu>Ue|GMvqsQ93Q^;Gx=3w1v&Wml4lv)+_!B~L+;ku zv3xk2lZpF2Z&0uN8|##XhC;H?(3UI!&)7=>{vaTHB!Cxe<46D-Mgnjc1>hwM8wH^M zC;8fSds$%gkjgRGh~G7(W(( zk)0&q1OXo70GQb5aR5e*15iXjY1U{wfQI7%%oz_rVTA--B_MDDfHLgE2>@nH0PuhS z2i7thfPXepdD#HUv-QbARv4a zfU0ccBmf&G0dSZMz=efP2GD;pfc*rxGGz(?^ArF{QvkTJy#(wbz-=mknk-=|fcU8Z z&Jf_vT&4kVo(5q2GyrwjNdis~;4vLQJvMqefKk%{6cNyXHJSmS;S2zCW&mi!3JJJM zK;VZ6nkICqt_RJ#Pi$q9K3LY>ny_TcM{~Q*dh_AE9^EY`cH6&xF+Ww7N8;)UtqlWI*q+vb>YH`L>sm0G&!NyGcISmsv~ zTNXTEa;@6IHpg$v+MmArJg}mZr+c54iROEO({o;Zu_d_Qjw+E4`xWdz zA?FxcPk5N(_RF~^3-+uoKWp&4fY1AQ^mG0uwL`NHpAP@^KR>|MHhjG*NH6K(Te|b%}Co9^2U%L67l?8cc z@S zCYfJ+-?VMnX%8;W9dq#Qp2#ZS*BDmdaYb%kH`wkokH+2OLa$9&uz&IBvEMw4IeTW~ zPUi&!lg2h0LDE*uUcsuHP`oAsd8S*kQLXifAm}JcbB{4@47f`%{}RJv0%TwVU!%a z>SOcWm*aw))j4(8&m*{F^kC;z-Uf5xZf-@Y=v zYIY=i;^>a7 z>TOVsMGw7m+7$l!q};tPty>%IUu9SNYPl|9ucSWj?gagQHmClh9t9rd3@J5SoN$v1 z_fvD|UKjo@L`FtJOxiaP$`|1OxXqUFPJsB#x$ab7bsMqsejT(t5CKWjI!x*Ggv@* zHZ8AegbL!LDF=U&9{;W&Kv}fgJ^TVTL9~MgJo&>)820hRPi3*3m$QO*ckOKW-s<|xC6Tse`Ad2RS6dj{QU z#E>Yk|=PG2KAfO9}ke3yf}ne<83<0;9YB;{>)v)JKmJR)grbRbX_p zca4Tf?5j5hhwQankhVhs|7p)F)(PxWQHXA&ZV=c`p;r;|MuF`XSS84t1Xdu*;pwxE~n)qd!(7 zeF381L7`U->HQiaNr!}9?z|3dQ=sDv4Xug#v2;jQ$)hMbI^1RChzrVA04eVE9kM+>{~&c3YHf3@l1u z-y3lGNm1DZ6f3|VMd7Bv5(IWnU>?A_10x6iEHF=`ABph%RWyQ$FFh9617K85bI@-B zdkT!&_3Dm)eiz_#Q5f$_NZ$(Vg}{7(^+KBB{ts}US*LCz$i>h3-w^6YXGA#H4DAgNH-Fgz0hj|jLzC_pfUn$ zi}WI@7{9UtYlk$R-eG9*a}Z#Aq^Um?cjW}u0cq+FVdVwZ5ox+TLm^f{U?E7~14bcM zQDB{r&Z8m3uadwzBfTAZ6k?T)XdfM**J`BQ0O{u_6uTgOSYS@Tr~~v2M}Lx5Rp{Y0 zAZZ2kC_J47_5srL$dAI)1sGLBQAC}i@T?*9dQktVcLdfHU{9p!i8RG*ErInyI$HF^ zU0}U|(Vs7;Uu}W)L3+5r>Ikeau+G3J-s=LRj`RbC0?Q__zEJFsbWc&ZfxyCm^%8{} zioyec^%huTp*Ij1y%$2iCITCT^d*5c6<9d1%ShAw@i5{YC2DUl=!#JE5{g5BT@{$O zz=i_5Ap*%qU=b|B4sjfbtk*P@`1y*gD5S@MT7moo7L9bg(6b0E23T8Q!QlP^i$(ek zh<+^v7Ke12hM-P?#Y0AzmD6CW027#Of=$B!(qANR3mz!4h9gZ6o#_`OutcQW3Ct$2 zBw%EFdr+{zl99d%J&M-W0!u-f9->mTwh>q=(wH9E^m?q+R)8arrV1$x+X;+1LoX{( zbhQ^)I@0w114U8?fn^{~p-Yj}QDB)!Q{)mBBCsr^>75ITu1@;g8VR94K#HW!LXj$? zH%sUjDzMQ=R}okjfl&j$AWhNL6`Zoig5H8?gn9@)>KGYBBh(WZ#mIP?Tb+eldke)0 z!01^&{rU)u^yp0w>Um#*Af<6YV1FZ*b0Br=V1kD1?0nG)?1I-650I8sk za2%}-V~`#TqBG@q(1d~n#n@Js|6n`pjZLym=*<-LA_VGmx}nnyy-eZ`qEk#=5WQ^T z2C4y~14~5^9a6#}4+hbpgAN(dAUapjryt@$be3SqGv7XP~B8VQ! z<$xxFXcLS9#e(8M2_V`8Y18Wh>I&)x>JI7wqW!E7h+cqe0eTHTz5%@j(Q+b#j37HZ zyw7Vwq7s!4jK(g26@Ad%AgETI%p7xmQYWS zBghFv%jci4;T7m-(62`PIZHbvVn7j~NYD`GQPG&*l~zVt3Ae%qdi!WR$PeZHK`lY8 zKvqx?r~#-Us1b;ss=I)yfvSUQfNHV_6^(V=+acK=)B)546awl9ng#ci0Zn3Fm5i>9 znxUl%AY55UP9VBS7y^m}eE_1fdoR#*HoB6rA8xdssbsWf%RpLz=+&>rAX-dm@pMLP zJAts_N!g$r&_vKw5S<4ff_?{W1<|?h3(x`33eYML9n(@ldVO^A>I9+_6P=Lg@Porn z70G}^8IS|0Jm?Sj@-oY-Z0wia9_48frv-mD(rZBHphEm6h&JdeptT^v-e;>`o=$z4 zD4UDwX&=_@qJ4D$s2_;7)4m|u8u9XM741#i?;zX;eGe)EU1uLU8k=Q@qP>ow@u-*D zCL$Y2rnZt%kKXoBq{BcKkQ?Y0s`+cRw5#d%bU_^O${sZ(vr2jPTOP+5#MU5wQRQMwAq0aOZP z1j!%@KngJWi>Aa$uMEf@q=2Y#Gk9r%>1iEz2VM)5T@(LQ2f2W#Atw-3@&FZ71lQeI z0qOFfav(B9@2Os~`YQ*a!WMdjqFqXqS988;i`S?o-0<1N!SfX=WTb&O5qhpecM z@s>L^XvDGXE%+PID-Z?OpWt-8YR8J|8f!X`_YF{^E0=mqsb^Fx(g;?8=mc^Eonl++ zq25neVLhX(!zakC50vN=Cf7IK!XxwB^`X#+IW#cV#6v}o2F89)w1O^y&SKC)&@hy{ zT9DfSLnl{YI~p1*%l7P8L*paO^|Zd$1g&F-8yQ_?4_4I3ShXIFfQqv9z~_VKpiT=J z45FAM_v3kFVB-#xXiq^K^?L_(^3vXhag{S!U zn_#?lFt4VtaSQ9z)L2LJ1>I}B0@9XbHomE`i3hFHbQ|#|s0c*&2Cjo>HK$vSwCd8m zQMx6$ja@=bEom{|1S0R!!v71hz6R-4QBm3ms2JUi+=%og@J-Ci!#Le=S7x7k80#yK zaqohD-?RH3#s-vT|HZsoV)~Z|kr%Sz#FjJnbfb$4c$d*0U z*Fd{u5xhBrEr5cY$2Nl*elE+7w=jD9>R-n5?|kZ^_p)lE3~~!kZ%;4icPxkVDS8ms zDyZAV^w%Mp@6Z^|Ud+Q6P3hmPo4D*d_O$WMS5UCe6)LKTiiTF2 zUT^EdF;k$27CgNXZEPD9{PeHwb!l3E=bHNN-a-LJ=-=f#RxS0)vmh55B_H0z<#Oz; zuhH4>dsL29D97i;&x5wf8%mdG{CnBo%d;+iMrT+3>wb>OWxaj!|A;|Zn8kaT?FY;C zuk%gruGAj2Vk8Y>3p8JBIhKwyehAHeOtzrEb(t$z^?x_%r%J55#po=@vIGl4`Kl9J zVKH_w+;L*Rg3B}6f^Ha^TUFTB5Ti3o^f$IZ)%pHLAJ-^n9%%8tL)JBG`d|?(@#Ur_ zu}A)BRR6@?%6d6Xn@o*~bd-YvTX?qg^p&1Fv$`#f-maxxwA;}+)|snsc)Pzmj4Hf6 z;Z1CWY(z_#@4{w*x$58gJ2;}j^5Zt6K4S-=;G%!T@5k5&yBU>44EG-XZ4y{lnFM&DK8j|6XD;w0x=xn&}%CcG^_MVuqXkzRlN7M}rE>qqg z6?wsASoELsih>xE2fIQMs`Ax89Qf-Wkwrr)n_HkLjSPw&s-Z2pIlq*x+$bnSfZL`T#^GYu`+vn^H(dT(~yiY^Xk z@^G-p%qhU=Q+;)9TneH~zm0vo?9S89&$TXjBiyA!wb|f##wvDRUYBdL2?0p8#3y6e zk^p0l;c;zNEfDr#Lfnyp)6($!ZMh|h6iDIvg=^CK@0asADeuv zQNWXuo-bl6p#YyZi-NOn)L{=};Ow{HL8#M>jR!0TFn1eRG}}Sa6t*n^Y&A;(=zF9t z|3F0H%gTd}_iwn~Afvw&?a~j(p>9QgwR~LOl16bCQAWM^V?h*c53R4oUg3 z`KOz14uw^|o?i5qqnooQ)L;NB6O6ueVYXmM16f$GaW@v#@~u%g6@{HpxapNc7p37I zV>G8Vr{SK;+O{@Mth^Em*hX`Fy4~B-zkbsogRHVEt&M%;4a}pBad-6{$ghO_#T9Z| z`-E?8j|!T5qJ1BHQjxuFV_fR{pVOl;jHjRYMq`dkpuNGb^k{pnEH)rXs>RN zFi0xRl2Ft&xd~3vsC#Iqs4}%b>~jr`(;Uzonhymzf$amUp59cuWuJ4-*Rglljo(W< z^6e2L{##S_xIH#F{kw37D_Nfn>9X*&<{b-lL;o1%ly|#VymEF*7H)yx`mxrlja6zO zRi+uQKi}Zjbpb%Gy}k@nEbF;`LHUTurD`bbB-XR6?^oR z(S?ocWUS_R7!!el|D{9i-A=))9vkG{E!d_`##VUy^KK`Mwf<4eqS?n6lqp-|D*-&(^@!5KUvuF&c;|;UF|}R zIn|$`e{`}jjB)sEX6*`@Toa>#?^?1|p+@iO^gqEnLSs--LgF+ZpWO!859W}@d3*LS z6g#8-#mx3!Mn#=z@LQTe##Gh}c4Y2d&?EipZ-H^=qQ^}iR@WeVW2K-Kp)pJ8g3!{x z*g4&_u;F#<_3P+~Xk@BCTiXQ(oq7K3Y8Q;>vzF{(8Wt!!X6p*aYA3iVETyZ_4RJB2 z1Tw)ue)w~;L(;~sQ=T}UX{8)XYX=>n#BAM+ZiZC>Y;ZT)a{^d%X4Fb*+pJG@{i~iM z))h%v4P4@3A;yHjDwu8mKwF+e!!gqxS#~&%W(!-h zF5QjJ)%CA<+UMQAHl_3(AJj+390XHg8e2^V7L!SR|z1nE4F>MTgYtOnv z18+ul?FavqXDee6r^|a7tGPyZ;L~y6w?9U1NZ&Obgex-SG5G8@p_*v+u53)|EiP3-ByZj#dJ zF6<8>H|@ja4t+}UtLq=svBgG(#qj?yPD< z4{i1L>S=~Q>&{m7*E>^%o$ZhLfTkV-)0;AI9VgZ!%xIxZvV*M-GggzeR(!<7ac1@d zjMYe}KL7!ub)gE)NItKe*=%I{z1I}Y6a4F^_yTCr9&6$2Ms+lgYdexKfTnawt;>6| z3Ij2usJ)%w+U$i-SF!~I5kG%t@$42rKmC)aPY(D0DlP3vUs`9i%}Ub0d6#)=&>wCa zMq!ty$oIvPx1cxk90VuDv$2CP1upky+e^gi|5@V%suIQu#p?gLFk|$!NyXP8XZCuK zvBmq>4jjtPgrm`M{_I{j+}eP-4o2Vj5@^CUjGM&W2P4Gv&$ybdy=tGd{mgc_kFF+R zhFAvq`d3O@0sguc@(LVHba>`2bJah>+J3wmwAL@-ph5Qa^wA0~VI#{s=bB>JI$B=+=dhssk2?fIod zA%zWrg74~Z?ICMU2Ud647ZFp3lql#Qp54^F>fBb9R+cHrk=YiiTmMpR`vb?;{d{ov z#S(=`b{h(C$17t0U(e)hemoqR%FafkGI500Bp>~NucQ17&sBZIiT)pXlUNVz1$3t& z5myF!t$Y~~6Y4#y$pjpvX)zNQ6ndTF3W7JQo0$@e9~c<@lgC$DHEufL>!2mtYM~jSe{Z*aTJM_c`dcR%iVwxy)5VSD zu=4RGE_>hQj&3u#R<~InFX3t4Im4TH=9y6Ju;M0!();Sx`}6mDbjMd>p~G-Qnu@Cy z+`Y)zeZ#nEU!95F(97nYesnrM+L`4H!*Njm81}nbIcMgMYJ5f0@bbiIRR3P^$!_M_ zhfY;+);y%Gr~8MoZ%|gA&bDla1H|QN9y?E0sXN$?RP^@baMpb|jm&UXVI%nO!`b{~ zlu=m46lm3E2a~}5UPjmXTZMW_mY9g8K&a?kRKPAG54SuXC8ADY)f9G{8v1)(|5w)h z_ag3WU~;kLx*djNDJ*ycy7%|8dWS@(-<#>GfBtw-_|l1;D)+qrKl}3aUli-Z7HmX| zsjO%lnCKVxoZkLF-1^@J=3iVOx>b(leexf@L+uks7S)G^7VmcEp|8qJ#55MpeOsY^bD*gXENCQu_(fkAi%LAvf`voT zRsUS^waJH_t9W%ecuHkD(oXMnjeuaRI4S^hMz7A$6@(Z^T+z_atz6S)WaZ@eQq zgzNtSBl!h}=-7i3RX+OwdVU3HsUqAn%A?6I;~H1Hr&Xlg_0_*;J$B6pog=n> zO_xD*ISlKrjAScDVQBR)V5bI;+k7b7T25$SWwc}G$%q>47cf`-i`tH@ecNOnIZGFr zR1=1vBda+YW2AqI+f*rI&#Gk|^ccYcs(OKEo|MRbZ_6$9sFf-QXPy!lWG?Y{+9afI{maMiz9{odmzEZreIz3Nc19&6FC|}XU%HXSiQvOo)LpOTuI=Z$b9`%pUuP(P_Z|0nR z!6FAe;`8bK75)SL*M+mW#8*tA?_`Gn7h`>BB7)){jwCmW#ZknLSYMpkph;XE%e7XE z=pV1l_3UImr?;26mNaVWXS5sAJ@J1Arren2O+{?!ix_Xq$qu2w>(iSPi%GG4x=|%g z)?!l&V%#~WPI*niX>m7u^f`hRSI&9~ZlLImES4(X7@rq^vEgsJdfR+&((YQme|w_& z$NN<>kzs%bJ5OD`HJ<%CwIq-!RQc4?*9_Kk+TT4Ztl$f`7&v{86U(_k|1z9v7yjYC)_K(O?sMoS6}_Z;?kr88%}gcI}SZ@Df|*O&~4bYeb#QuJv|^w zi%#4!!8&|(hEBKq*J%>O=WG9?QlcG$J2s9xQQG`&xW> z*NraGOJz1F_)UXCH7F!?^D8?k9e-(|*oajkXVD7K#J#DfZu*FaaRx6hmNn1#k^QH+{494kmpRVI@#}Oh3z?5N#{;>= z;I7U~d1#Fu`g!M&plIA!8jRxnki-F|35T&ZX^o8dp zM4k&OUsdZOF7a^sz@?uPa{ltfi|!R8*f+2%XgSXL$=@krp*hwJ_n|dn?R`PG58@>{r}ce3tEm-5a z{+hhF^vfIC%N*K*bta!hGp@OW;nwrvwTqYN%$emZ!O$OW!9H7pRc9BwzXWZDGPhi` zxrjNFR)Bywdmlwznk{U=w?? z6xBwtGRxrDz|GvTc2~{(e6<+p5`r3UwHb zuhr$j==bbY!rf!owmkHHGCP@viWY9??KYXWOxjX=(U<%oBreIMJm$3mFBQ=L6hZEO zlPGUbt=*0u=ZcE3JLRl^=|4c@AMZKQ)fjBaWM@`jJ^383UXg-Z?b~$@DYf^a3h`j^ z2O~4Aq$^JTat7-(N6Z>5g_GYEg9g$FT6^3l+ZeHQ>!_l+Drriif zh2qBeFxG1o%4*N{wL@Db%U@-jXuy-Ay7_2`KPhU$Cg&U1xIW&)M<@KswUp)UWd0lr zrv>p8*~j&zP;k9Grg^{7Et=Z#?Y;%Q34*7^F0QeNF&f`_xgF!5x^BWYtM{xn`*QWa z2cbW`%vN!Y_F}~l+$nqC@b;<-Qaj&GB(;IC&|% zy+*tD-E1vfl#`HfIQTKvs? zk^u~_jya(J{rBvQA%?Hfm8Bx9Vy3_Ms*U(Vy=*sjjiMs85!R-~4 z#b`>5r+y~;8!$`+!&r}=h>t>icMLJOk!uljSU@58G7N%`rBA65kKXxh+B#q-njmXR z14H176*<_q+xcW5{wjJ=xO}#kORRsh!+;tfA$nn;DFr_(EYK}{) z60kA#?1!t3ToetS{0Ah_M6VsdTva4d1uvbcm^~bZTAHh&g28;;5y0_Qie|{9iixU& zs4y}ggdB=-r}=9Xe_6$W=a+juU>l|rScGM^QwMK9PPa;MurSw!j-ay!A$!yNhcK48 zT{MfDDWcP)sna!62pH0Rv({8srK&i&Ce?hbVWzz|%D*n7iVTmY{BTrW9d(qqMq6BS zJ9Bh+m>O{BAz(W4zmFBE#Dd`e@j*>p%{b;jIImI@vgCKla9~0u!RaG>2a(DV_{LI9 zIY``ZWR@d$sU#LK4SyiTXZu@UNS>Ep`^`Qjum<=9YQK`ABngFe+bZEsyt0T~Dn-euwUHlM`U~boOjvV{UAwRf z1vuf=MtZgqSwHXPr>kzF`bv}woN3}?Rg~98cusXNp$9uiAn;#pA{!Gjxj}HSO=z~4 zzNPPlIV=|d@XcgGR+tI99uA3XOk#wS2|s_a0jGaxn}6aCpSy}I1tf23p)26TUrE`? ztm0Q8C+V77X@jin8La#d=oSo{ghKndUy3-l1JBCcd&@We>F&)k2wx=? zcBkkn?14Xk;X#c0cw6_)k}f+|AcvieO(By$t-|I!*iJ4-QNCf-!4aMe+t<&ITly<2 zVnO1}rs_z0k79gxI(8JhyMn+(f|_*<4>M`uF)_oaM|}xP_uBg{VnaP^jVKCo*|32f zI!3l=B^ke*qVd(3o|0SHQ(84@_VtuqjX*oIle@+KqR-L>ylfy&F*~+{kyqG8AKzeB z*p)+<&=u;H)xeu^QEhu1#@FtsmB&nZt@X_s7@YTE*UU3&;47oH@~4qV?e9XcA-{|7 zHpvlFOAg;ymj=~XQi+_RlKg8$e`5m}*e>EfFh9{UY}vBOV6PqI!I(1#)3j#HbWH(}XW~(w_w90-AOb)M65vusqOw%}Ff4 zGAd?`bE)wpgr<{S166samg zM;cK^t*GJ12iEXI2}z_#(a=N~N6B2v`$Y0SNleQpo1vu@r8eWuGpqko<&s;A_?C9w z^A8vkMFlOG$*`C@+QpwCR84sfu4%}e_~H{!Vr|rXWAwXPA(t+-LSywxP>u3x3|4L- zAECu9{xXV-9e+NjKR645Svm^?PN5Po2tL%?1|?N=O`()@U3uWZ5KQHnqe0)x zoxSGT`C~ErKV=@PA~kMQ)(&0#D6#_rIYpB?5UkBwoc=jJX;-s6*`QAJwVrVbrx|=u zIou(JQ_v}KhVGmPEj=YVZ*ubFUa`ycXsJV1sfU+JudOmojp?{`(GN5WK~c{w>|9kJ zoE@sR2U!Q_=NE_jM5yhhm(uM*!wju)pvQwh#qC3fjdjcNlR?{prmPbMEoHu)>p`=( z^^30ta!VFZcRR5i4_GSR@%`oc^+`M13PH2TKRpJloik}pr|3K( z4>5_IyL$T?<5ROHH4C&B&6})V7^5Vk`4=i}%Fa4%oD7=9=j}aJpE&K=YCj#c9q9gI zE8CT+-~BlYG{al2zueZmw8uKV1Lt)n_Wg~q&+MF1ufp(DC-SYJ2UH zQdi6NZ*L8#?N_hFZ>xGb@v*YM@PKW(S1-)3o|pJRid6v*5e1WXS!|gWp8h)f!9G4e zm&wna7N-bytKW`KN?x9{IzAx<|4rtd5}&Y|>bk^-$NC4(NPOdsC3NSs=qlgo5{oPZ zf4T6y7-la7%6&hG`*gYhA3BpLxyVQQ#VASV>n968iE%b^j!yc2NfSH( delta 42088 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 z zozI?ot5Jv?SLF1QHkAA8uQ}u1=HTwrYm{{jOXg)A%FM4-^6L?}jMmMZ%6!(simzFF zFxPI-BbS|h)bHc}tC4Guin7|`etaK_bx|T3)4?3`n%x@^12f}{7_)b_}E{m3V)3`r@nE7bI_ zzMTH9+|Ii#J~h<-5q3q)df9XMck{&wMfc_408{(Us*zufaWlIXy%VC}bGId#v<0xv zA*>Lap?%WajsM8{{@h)L&>C7=0O89Zf?4NmrpC~-dI?gns_-iG^+wAhK6$tNyZ(+- zpS9qX7?ViVX=;bS&=LA*5nFIu2G94&FfBg&54$R;rab5Oh+9^ zLBZ95T!$aFD1!svbF~#Lc(nNp$K2IXU7+QA7+S`(hpE90g`$BO7vp?%4GtW{&d1-g zIaLK!#azK|xl~OC?tR3!O8KWaR1>t84PG*fxTW;$Cm@luc=Hov-hchr9W0AeOUd?^ zV=oM?ADB#w%Xx#=fodxDVp_uYoZGtl-8^Y$n^^EK9rTf`(C(*lTx&kxBJ@%>hIWCNvsmAg`$PkZ!DNK zvE_Qi!aHaI&dtsU2n=;V2I9Un2X2f3s&z1fx6&#RPFu8!*R18Zn>~q`~6n=uwWo9(iTb+q7cO7AQ$792A_6kfoWbgCNaU8CJK_!yu* zP%II=rUs_^<0&kOnnsq}=p#IHT=YbV=TNhN`;?Mw6z|ZttPP^n%U`zM#$Yj zrGVvVO(Pa!3q98W2Bl{1ZTf&w71Rwr>TN*T%1FeAg|1iBH=+_-R#R3$1sk=CF3GQT=I!&Atise7W(yg7Sx#|H7}#Z;p;M*)CIn!ZhoKn( zSQI5T!x@zgqisMK%+|iOIJ7E0dajO1MFBRRDz@@%;n%5?2{z#UI@AJ#4?C(y%^BuW zN-GR8dj3}1qqv9zA%52iUym_jlPI2mk`Yy|3*~5+5}1)#@3&bVGnoq7FyjpBYJ)N| zf%f1Y8zwLlqC`C)yPo1>-40=iIlerk$&8Q^ucj6j>n^%w=QOaX5Rner&qM6HCT4$D(&X+mi+v}@PK_v zd&%XD2gBxTTR#NNAb{jYcWE})Cq12Ch6n5tqMW)TEv5SQ)1Wn=KmM%u{h}BDTz3gH z^DCV{eAAGbxVP;lXm(XZ+oD(9mtEfQGfi*jr|GpgJ=j+NZ^R`1$#DM zQ~9mlf98?C9kbBRHcj{SMyoyN;pT0$9^JBbMaYhC{)Gy!($9T27l3Bd-Q87r(D%J< zK4qZU+kB(Y!+X8`+AocuSpeL#tgZRwuJIWfU;<3+TbpD4vUkRjG#zL*y@B`I18(k` z+!vs<*Cv!qAA9DzTV|#G>J{sb%23-Ar3>(Y>3#J=boEl(_T*)ty+9wCw$DBLPvIHs zI^;E*Unb7!;Ae2FOtwJtY>U~Vj}0`KY;k%+yv3l84YC@|=0Jnq5*!<3jJMf>&FR+I zWc=2ef3h_;{rC2v)K31PaSFG%y_0Wo=YquL=lS_Q_&oR{|Dl`ol^lH+uM^ka;1>&= zMJ(y#S9+1#Wqukh%N2AKm@Ww7L^t7*4|#vf>!@?B;4V&>Bpjb0{ybAKO{DLR2|97H zQCK*g9L4ze#PdSP{hVIBZ595*aVCng3y;!ao8Te(#0z->XA(=+39(~6g9BboeB-sW zWdBraS~_(f5q!mv6hWh<`XWe4%N2S(CNhi%TyZIOFeUg?m{piTSEfSN!M#F<;1+BY Zlin3FInF5d91uD=3XVhH3wc7h=Kteuix2<+ diff --git a/package.json b/package.json index ac9ed32a..bdd86bae 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "dependencies": { "@openzeppelin/contracts": "5.0.2", "@prb/math": "github:PaulRBerg/prb-math#16419e5686504e15f973ebe73c3a75bd618d6ca4", - "@sablier/v2-core": "github:sablier-labs/v2-core#6f0879891b046fc14d941b2a58804193b25dff38" + "@sablier/v2-core": "github:sablier-labs/v2-core#staging" }, "devDependencies": { "@sphinx-labs/plugins": "^0.31.9", diff --git a/src/SablierV2BatchLockup.sol b/src/SablierV2BatchLockup.sol index 5c3d958c..70f6bc13 100644 --- a/src/SablierV2BatchLockup.sol +++ b/src/SablierV2BatchLockup.sol @@ -211,7 +211,7 @@ contract SablierV2BatchLockup is ISablierV2BatchLockup { asset: asset, cancelable: batch[i].cancelable, transferable: batch[i].transferable, - range: batch[i].range, + timestamps: batch[i].timestamps, broker: batch[i].broker }) ); diff --git a/src/types/DataTypes.sol b/src/types/DataTypes.sol index 111353c9..11b9eeb0 100644 --- a/src/types/DataTypes.sol +++ b/src/types/DataTypes.sol @@ -70,7 +70,7 @@ library BatchLockup { uint128 totalAmount; bool cancelable; bool transferable; - LockupLinear.Range range; + LockupLinear.Timestamps timestamps; Broker broker; } diff --git a/test/fork/batch-lockup/createWithTimestampsLL.t.sol b/test/fork/batch-lockup/createWithTimestampsLL.t.sol index 9b68add4..31896b2f 100644 --- a/test/fork/batch-lockup/createWithTimestampsLL.t.sol +++ b/test/fork/batch-lockup/createWithTimestampsLL.t.sol @@ -20,7 +20,7 @@ abstract contract CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test is For struct CreateWithTimestampsParams { uint128 batchSize; - LockupLinear.Range range; + LockupLinear.Timestamps timestamps; address sender; address recipient; uint128 perStreamAmount; @@ -29,10 +29,13 @@ abstract contract CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test is For function testForkFuzz_CreateWithTimestampsLL(CreateWithTimestampsParams memory params) external { params.batchSize = boundUint128(params.batchSize, 1, 20); params.perStreamAmount = boundUint128(params.perStreamAmount, 1, MAX_UINT128 / params.batchSize); - params.range.start = boundUint40(params.range.start, getBlockTimestamp(), getBlockTimestamp() + 24 hours); - params.range.cliff = - boundUint40(params.range.cliff, params.range.start + 1 seconds, params.range.start + 52 weeks); - params.range.end = boundUint40(params.range.end, params.range.cliff + 1 seconds, MAX_UNIX_TIMESTAMP); + params.timestamps.start = + boundUint40(params.timestamps.start, getBlockTimestamp(), getBlockTimestamp() + 24 hours); + params.timestamps.cliff = boundUint40( + params.timestamps.cliff, params.timestamps.start + 1 seconds, params.timestamps.start + 52 weeks + ); + params.timestamps.end = + boundUint40(params.timestamps.end, params.timestamps.cliff + 1 seconds, MAX_UNIX_TIMESTAMP); checkUsers(params.sender, params.recipient); @@ -49,7 +52,7 @@ abstract contract CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test is For asset: FORK_ASSET, cancelable: true, transferable: true, - range: params.range, + timestamps: params.timestamps, broker: defaults.broker() }); BatchLockup.CreateWithTimestampsLL[] memory batchParams = diff --git a/test/utils/BatchLockupBuilder.sol b/test/utils/BatchLockupBuilder.sol index f4efe199..3cf567fa 100644 --- a/test/utils/BatchLockupBuilder.sol +++ b/test/utils/BatchLockupBuilder.sol @@ -186,7 +186,7 @@ library BatchLockupBuilder { totalAmount: params.totalAmount, cancelable: params.cancelable, transferable: params.transferable, - range: params.range, + timestamps: params.timestamps, broker: params.broker }); batch = fillBatch(batchSingle, batchSize); diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index 12cbdf93..6ace3290 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -283,7 +283,7 @@ contract Defaults is Merkle { asset: asset_, cancelable: true, transferable: true, - range: linearRange(), + timestamps: linearTimestamps(), broker: broker() }); } @@ -292,8 +292,8 @@ contract Defaults is Merkle { return LockupLinear.Durations({ cliff: CLIFF_DURATION, total: TOTAL_DURATION }); } - function linearRange() private view returns (LockupLinear.Range memory) { - return LockupLinear.Range({ start: START_TIME, cliff: CLIFF_TIME, end: END_TIME }); + function linearTimestamps() private view returns (LockupLinear.Timestamps memory) { + return LockupLinear.Timestamps({ start: START_TIME, cliff: CLIFF_TIME, end: END_TIME }); } /*////////////////////////////////////////////////////////////////////////// From 37f524b397abc541f3fb36d6763274e77bf38990 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Tue, 7 May 2024 20:48:01 +0100 Subject: [PATCH 46/61] docs: move _checkClaim to INTERNAL NON-CONSTANT FUNCTIONS --- src/abstracts/SablierV2MerkleLockup.sol | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/abstracts/SablierV2MerkleLockup.sol b/src/abstracts/SablierV2MerkleLockup.sol index cd5c1783..b342c56f 100644 --- a/src/abstracts/SablierV2MerkleLockup.sol +++ b/src/abstracts/SablierV2MerkleLockup.sol @@ -125,6 +125,16 @@ abstract contract SablierV2MerkleLockup is INTERNAL CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ + /// @notice Returns a flag indicating whether the grace period has passed. + /// @dev The grace period is 7 days after the first claim. + function _hasGracePeriodPassed() internal view returns (bool) { + return _firstClaimTime > 0 && block.timestamp > _firstClaimTime + 7 days; + } + + /*////////////////////////////////////////////////////////////////////////// + INTERNAL NON-CONSTANT FUNCTIONS + //////////////////////////////////////////////////////////////////////////*/ + /// @dev Validates the parameters of the `claim` function, which is implemented by child contracts. function _checkClaim(uint256 index, bytes32 leaf, bytes32[] calldata merkleProof) internal { // Check: the campaign has not expired. @@ -150,10 +160,4 @@ abstract contract SablierV2MerkleLockup is _firstClaimTime = uint40(block.timestamp); } } - - /// @notice Returns a flag indicating whether the grace period has passed. - /// @dev The grace period is 7 days after the first claim. - function _hasGracePeriodPassed() internal view returns (bool) { - return _firstClaimTime > 0 && block.timestamp > _firstClaimTime + 7 days; - } } From bcb143e998847826a09580e7968019691818932a Mon Sep 17 00:00:00 2001 From: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com> Date: Wed, 15 May 2024 11:09:13 +0300 Subject: [PATCH 47/61] refactor: remove sphinx (#346) --- .env.example | 3 -- bun.lockb | Bin 307851 -> 43610 bytes foundry.toml | 3 -- package.json | 1 - remappings.txt | 1 - script/Base.s.sol | 25 +----------- script/CreateMerkleLL.s.sol | 25 +----------- script/CreateMerkleLT.s.sol | 25 +----------- script/DeployBatchLockup.t.sol | 11 +---- script/DeployDeterministicBatchLockup.s.sol | 11 +---- script/DeployDeterministicPeriphery.s.sol | 19 +-------- script/DeployMerkleLockupFactory.s.sol | 11 +---- script/DeployPeriphery.s.sol | 19 +-------- script/DeployProtocol.s.sol | 43 +------------------- 14 files changed, 9 insertions(+), 188 deletions(-) diff --git a/.env.example b/.env.example index 5a628011..d0f0d17e 100644 --- a/.env.example +++ b/.env.example @@ -3,6 +3,3 @@ export EOA="YOUR_EOA_ADDRESS" export FOUNDRY_PROFILE="lite" export MNEMONIC="YOUR_MNEMONIC" export RPC_URL_MAINNET="YOUR_RPC_URL_MAINNET" -export SPHINX_API_KEY="YOUR_API_KEY" -export SPHINX_ORG_ID="YOUR_ORG_ID" -export SPHINX_PROJECT_NAME="YOUR_PROJECT_NAME" diff --git a/bun.lockb b/bun.lockb index eff7a86c3a6f82be8ddca6baa3e02297fb6dde09..8f50fbca239012938be2f11efd814b219ea59967 100755 GIT binary patch delta 6871 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�tl9A_bCP5^8C(ZDuQFnTjuAenxWy9v_ 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 diff --git a/foundry.toml b/foundry.toml index 81924c28..8b024f9a 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,12 +3,9 @@ block_timestamp = 1_714_518_000 # May 1, 2024 at 00:00 GMT bytecode_hash = "none" evm_version = "paris" - extra_output = ["storageLayout"] fs_permissions = [ - { access = "read", path = "./out" }, { access = "read", path = "./out-optimized" }, { access = "read", path = "package.json"}, - { access = "read-write", path = "./cache" }, ] gas_reports = [ "SablierV2BatchLockup", diff --git a/package.json b/package.json index bdd86bae..1ab85d93 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,6 @@ "@sablier/v2-core": "github:sablier-labs/v2-core#staging" }, "devDependencies": { - "@sphinx-labs/plugins": "^0.31.9", "forge-std": "github:foundry-rs/forge-std#v1.8.1", "prettier": "^2.8.8", "solady": "0.0.129", diff --git a/remappings.txt b/remappings.txt index 8f335792..7089bf38 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,6 +1,5 @@ @openzeppelin/contracts/=node_modules/@openzeppelin/contracts/ @prb/math/=node_modules/@prb/math/ @sablier/v2-core/=node_modules/@sablier/v2-core/ -@sphinx-labs/contracts/=node_modules/@sphinx-labs/contracts/contracts/foundry forge-std/=node_modules/forge-std/ solady/=node_modules/solady/ diff --git a/script/Base.s.sol b/script/Base.s.sol index a8ef7a85..ef9c4cea 100644 --- a/script/Base.s.sol +++ b/script/Base.s.sol @@ -3,22 +3,18 @@ pragma solidity >=0.8.22 <0.9.0; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; -import { Sphinx } from "@sphinx-labs/contracts/SphinxPlugin.sol"; import { console2 } from "forge-std/src/console2.sol"; import { Script } from "forge-std/src/Script.sol"; import { stdJson } from "forge-std/src/StdJson.sol"; -contract BaseScript is Script, Sphinx { +contract BaseScript is Script { using Strings for uint256; using stdJson for string; /// @dev Included to enable compilation of the script without a $MNEMONIC environment variable. string internal constant TEST_MNEMONIC = "test test test test test test test test test test test junk"; - /// @dev The project name for the Sphinx plugin. - string internal constant TEST_SPHINX_PROJECT_NAME = "test-test"; - /// @dev Needed for the deterministic deployments. bytes32 internal constant ZERO_SALT = bytes32(0); @@ -28,15 +24,11 @@ contract BaseScript is Script, Sphinx { /// @dev Used to derive the broadcaster's address if $EOA is not defined. string internal mnemonic; - /// @dev The project name for the Sphinx plugin. - string internal sphinxProjectName; - /// @dev Initializes the transaction broadcaster like this: /// /// - If $EOA is defined, use it. /// - Otherwise, derive the broadcaster address from $MNEMONIC. /// - If $MNEMONIC is not defined, default to a test mnemonic. - /// - If $SPHINX_PROJECT_NAME is not defined, default to a test project name. /// /// The use case for $EOA is to specify the broadcaster key and its address via the command line. constructor() { @@ -47,7 +39,6 @@ contract BaseScript is Script, Sphinx { mnemonic = vm.envOr({ name: "MNEMONIC", defaultValue: TEST_MNEMONIC }); (broadcaster,) = deriveRememberKey({ mnemonic: mnemonic, index: 0 }); } - sphinxProjectName = vm.envOr({ name: "SPHINX_PROJECT_NAME", defaultValue: TEST_SPHINX_PROJECT_NAME }); } modifier broadcast() { @@ -56,20 +47,6 @@ contract BaseScript is Script, Sphinx { vm.stopBroadcast(); } - /// @dev Configures the Sphinx plugin to manage the deployment of the contracts. - /// Refer to https://github.com/sphinx-labs/sphinx/tree/main/docs. - /// - /// CLI example: - /// bun sphinx propose script/DeploybatchLockup.s.sol --networks testnets --sig "runSphinx()" - function configureSphinx() public override { - sphinxConfig.mainnets = ["arbitrum", "avalanche", "base", "bnb", "gnosis", "ethereum", "optimism", "polygon"]; - sphinxConfig.orgId = vm.envOr({ name: "SPHINX_ORG_ID", defaultValue: TEST_MNEMONIC }); - sphinxConfig.owners = [broadcaster]; - sphinxConfig.projectName = sphinxProjectName; - sphinxConfig.testnets = ["sepolia"]; - sphinxConfig.threshold = 1; - } - /// @dev The presence of the salt instructs Forge to deploy contracts via this deterministic CREATE2 factory: /// https://github.com/Arachnid/deterministic-deployment-proxy /// diff --git a/script/CreateMerkleLL.s.sol b/script/CreateMerkleLL.s.sol index 733b297f..daf788f7 100644 --- a/script/CreateMerkleLL.s.sol +++ b/script/CreateMerkleLL.s.sol @@ -20,7 +20,7 @@ contract CreateMerkleLL is BaseScript { } /// @dev Deploy via Forge. - function runBroadcast( + function run( ISablierV2MerkleLockupFactory merkleLockupFactory, Params calldata params ) @@ -28,29 +28,6 @@ contract CreateMerkleLL is BaseScript { virtual broadcast returns (ISablierV2MerkleLL merkleLL) - { - merkleLL = _run(merkleLockupFactory, params); - } - - /// @dev Deploy via Sphinx. - function runSphinx( - ISablierV2MerkleLockupFactory merkleLockupFactory, - Params calldata params - ) - public - virtual - sphinx - returns (ISablierV2MerkleLL merkleLL) - { - merkleLL = _run(merkleLockupFactory, params); - } - - function _run( - ISablierV2MerkleLockupFactory merkleLockupFactory, - Params calldata params - ) - internal - returns (ISablierV2MerkleLL merkleLL) { merkleLL = merkleLockupFactory.createMerkleLL( params.baseParams, diff --git a/script/CreateMerkleLT.s.sol b/script/CreateMerkleLT.s.sol index dc739621..c32c3eea 100644 --- a/script/CreateMerkleLT.s.sol +++ b/script/CreateMerkleLT.s.sol @@ -19,7 +19,7 @@ contract CreateMerkleLT is BaseScript { } /// @dev Deploy via Forge. - function runBroadcast( + function run( ISablierV2MerkleLockupFactory merkleLockupFactory, Params calldata params ) @@ -27,29 +27,6 @@ contract CreateMerkleLT is BaseScript { virtual broadcast returns (ISablierV2MerkleLT merkleLT) - { - merkleLT = _run(merkleLockupFactory, params); - } - - /// @dev Deploy via Sphinx. - function runSphinx( - ISablierV2MerkleLockupFactory merkleLockupFactory, - Params calldata params - ) - public - virtual - sphinx - returns (ISablierV2MerkleLT merkleLT) - { - merkleLT = _run(merkleLockupFactory, params); - } - - function _run( - ISablierV2MerkleLockupFactory merkleLockupFactory, - Params calldata params - ) - internal - returns (ISablierV2MerkleLT merkleLT) { merkleLT = merkleLockupFactory.createMerkleLT( params.baseParams, diff --git a/script/DeployBatchLockup.t.sol b/script/DeployBatchLockup.t.sol index 2047bbc2..03bfc7d1 100644 --- a/script/DeployBatchLockup.t.sol +++ b/script/DeployBatchLockup.t.sol @@ -7,16 +7,7 @@ import { SablierV2BatchLockup } from "../src/SablierV2BatchLockup.sol"; contract DeployBatchLockup is BaseScript { /// @dev Deploy via Forge. - function runBroadcast() public virtual broadcast returns (SablierV2BatchLockup batchLockup) { - batchLockup = _run(); - } - - /// @dev Deploy via Sphinx. - function runSphinx() public virtual sphinx returns (SablierV2BatchLockup batchLockup) { - batchLockup = _run(); - } - - function _run() internal returns (SablierV2BatchLockup batchLockup) { + function run() public virtual broadcast returns (SablierV2BatchLockup batchLockup) { batchLockup = new SablierV2BatchLockup(); } } diff --git a/script/DeployDeterministicBatchLockup.s.sol b/script/DeployDeterministicBatchLockup.s.sol index 0b4fd633..e214fc07 100644 --- a/script/DeployDeterministicBatchLockup.s.sol +++ b/script/DeployDeterministicBatchLockup.s.sol @@ -9,16 +9,7 @@ import { SablierV2BatchLockup } from "../src/SablierV2BatchLockup.sol"; /// @dev Reverts if the contract has already been deployed. contract DeployDeterministicBatchLockup is BaseScript { /// @dev Deploy via Forge. - function runBroadcast() public virtual broadcast returns (SablierV2BatchLockup batchLockup) { - batchLockup = _run(); - } - - /// @dev Deploy via Sphinx. - function runSphinx() public virtual sphinx returns (SablierV2BatchLockup batchLockup) { - batchLockup = _run(); - } - - function _run() internal returns (SablierV2BatchLockup batchLockup) { + function run() public virtual broadcast returns (SablierV2BatchLockup batchLockup) { bytes32 salt = constructCreate2Salt(); batchLockup = new SablierV2BatchLockup{ salt: salt }(); } diff --git a/script/DeployDeterministicPeriphery.s.sol b/script/DeployDeterministicPeriphery.s.sol index 452509d7..e4bc8c63 100644 --- a/script/DeployDeterministicPeriphery.s.sol +++ b/script/DeployDeterministicPeriphery.s.sol @@ -14,28 +14,11 @@ import { SablierV2MerkleLockupFactory } from "../src/SablierV2MerkleLockupFactor /// @dev Reverts if any contract has already been deployed. contract DeployDeterministicPeriphery is BaseScript { /// @dev Deploy via Forge. - function runBroadcast() + function run() public virtual broadcast returns (SablierV2BatchLockup batchLockup, SablierV2MerkleLockupFactory merkleLockupFactory) - { - (batchLockup, merkleLockupFactory) = _run(); - } - - /// @dev Deploy via Sphinx. - function runSphinx() - public - virtual - sphinx - returns (SablierV2BatchLockup batchLockup, SablierV2MerkleLockupFactory merkleLockupFactory) - { - (batchLockup, merkleLockupFactory) = _run(); - } - - function _run() - internal - returns (SablierV2BatchLockup batchLockup, SablierV2MerkleLockupFactory merkleLockupFactory) { bytes32 salt = constructCreate2Salt(); batchLockup = new SablierV2BatchLockup{ salt: salt }(); diff --git a/script/DeployMerkleLockupFactory.s.sol b/script/DeployMerkleLockupFactory.s.sol index 3879f2e8..4b1c5de9 100644 --- a/script/DeployMerkleLockupFactory.s.sol +++ b/script/DeployMerkleLockupFactory.s.sol @@ -7,16 +7,7 @@ import { SablierV2MerkleLockupFactory } from "../src/SablierV2MerkleLockupFactor contract DeployMerkleLockupFactory is BaseScript { /// @dev Deploy via Forge. - function runBroadcast() public virtual broadcast returns (SablierV2MerkleLockupFactory merkleLockupFactory) { - merkleLockupFactory = _run(); - } - - /// @dev Deploy via Sphinx. - function runSphinx() public virtual sphinx returns (SablierV2MerkleLockupFactory merkleLockupFactory) { - merkleLockupFactory = _run(); - } - - function _run() internal returns (SablierV2MerkleLockupFactory merkleLockupFactory) { + function run() public virtual broadcast returns (SablierV2MerkleLockupFactory merkleLockupFactory) { merkleLockupFactory = new SablierV2MerkleLockupFactory(); } } diff --git a/script/DeployPeriphery.s.sol b/script/DeployPeriphery.s.sol index 151fd81f..a51f4b9c 100644 --- a/script/DeployPeriphery.s.sol +++ b/script/DeployPeriphery.s.sol @@ -12,28 +12,11 @@ import { SablierV2BatchLockup } from "../src/SablierV2BatchLockup.sol"; /// 2. {SablierV2MerkleLockupFactory} contract DeployPeriphery is BaseScript { /// @dev Deploy via Forge. - function runBroadcast() + function run() public virtual broadcast returns (SablierV2BatchLockup batchLockup, SablierV2MerkleLockupFactory merkleLockupFactory) - { - (batchLockup, merkleLockupFactory) = _run(); - } - - /// @dev Deploy via Sphinx. - function runSphinx() - public - virtual - sphinx - returns (SablierV2BatchLockup batchLockup, SablierV2MerkleLockupFactory merkleLockupFactory) - { - (batchLockup, merkleLockupFactory) = _run(); - } - - function _run() - internal - returns (SablierV2BatchLockup batchLockup, SablierV2MerkleLockupFactory merkleLockupFactory) { batchLockup = new SablierV2BatchLockup(); merkleLockupFactory = new SablierV2MerkleLockupFactory(); diff --git a/script/DeployProtocol.s.sol b/script/DeployProtocol.s.sol index c27c7f6a..1976faeb 100644 --- a/script/DeployProtocol.s.sol +++ b/script/DeployProtocol.s.sol @@ -13,7 +13,7 @@ import { SablierV2BatchLockup } from "../src/SablierV2BatchLockup.sol"; /// @notice Deploys the Sablier V2 Protocol. contract DeployProtocol is BaseScript { /// @dev Deploy via Forge. - function runBroadcast( + function run( address initialAdmin, uint256 maxSegmentCount, uint256 maxTrancheCount @@ -29,47 +29,6 @@ contract DeployProtocol is BaseScript { SablierV2BatchLockup batchLockup, SablierV2MerkleLockupFactory merkleLockupFactory ) - { - (lockupDynamic, lockupLinear, lockupTranched, nftDescriptor, batchLockup, merkleLockupFactory) = - _run(initialAdmin, maxSegmentCount, maxTrancheCount); - } - - /// @dev Deploy via Sphinx. - function runSphinx( - address initialAdmin, - uint256 maxSegmentCount, - uint256 maxTrancheCount - ) - public - virtual - sphinx - returns ( - SablierV2LockupDynamic lockupDynamic, - SablierV2LockupLinear lockupLinear, - SablierV2LockupTranched lockupTranched, - SablierV2NFTDescriptor nftDescriptor, - SablierV2BatchLockup batchLockup, - SablierV2MerkleLockupFactory merkleLockupFactory - ) - { - (lockupDynamic, lockupLinear, lockupTranched, nftDescriptor, batchLockup, merkleLockupFactory) = - _run(initialAdmin, maxSegmentCount, maxTrancheCount); - } - - function _run( - address initialAdmin, - uint256 maxSegmentCount, - uint256 maxTrancheCount - ) - internal - returns ( - SablierV2LockupDynamic lockupDynamic, - SablierV2LockupLinear lockupLinear, - SablierV2LockupTranched lockupTranched, - SablierV2NFTDescriptor nftDescriptor, - SablierV2BatchLockup batchLockup, - SablierV2MerkleLockupFactory merkleLockupFactory - ) { // Deploy V2 Core. nftDescriptor = new SablierV2NFTDescriptor(); From 8016b5bd1b6ac5934df1470efaca6e9d608b7aec Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Thu, 16 May 2024 11:03:10 +0100 Subject: [PATCH 48/61] build: update evm version to Shanghai --- bun.lockb | Bin 43610 -> 44194 bytes foundry.toml | 2 +- precompiles/Precompiles.sol | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bun.lockb b/bun.lockb index 8f50fbca239012938be2f11efd814b219ea59967..bad2ad29af3fec13f59d9c322ee29d2c4b40ab9f 100755 GIT binary patch delta 1060 zcmY+@PiT^H9LMqJ(QWBdnM6rYIXA*dIn!0PY&q8-w`x7Ef4aeVs6*CZxI^<%H<0My z*bZX+z@tQkco?qnfTs|G;i14og{%mxi$|f0m%w_yA0yTS&x`Nx{d=C<@Au`C=EY}C zM%3?L=kUayg?crI<<0nOt=_2?BgZ}2Qu?|!v_|*J*sq_N5B8zVEVtcv=so)(`Cb}z zN10JK!S}Km-2f}-4k&MIue9vBt*cwB{=TMc+BSA9*^GTw&vP8lwfD7o_Du@#JTnZ2 zRKu`57~1o1pX0jOTl=KC8FA1j6=*(5vka@y>ZFo{HRy0rNx?dF4^c_O2K1hyl7&s^ zKTRbETTnbhB@f#$=%!MHJ!ff_U>vEVGTM)sH9*W zx-U>k!v^$vsbpaj`bVkcU<-;qDtXw3!7(aD*mIF)36`MY5|uD4L(^p{5ms0cv4TFMf%@Qm@gGeO| z%g_{{5`h(Ho}wbdDzx6Dl7uzrxJ4xe>(G6hN*Xqx_s)MuHA+Wy=h>AhFi~1ojCa54 z*!NvFb>X5{5v7j@SwM2qVmE(J?%X|FjGb5YjwKXN^c~L&tUER!OeH!s zzRWz)eq}m#L}U7`K6KMG>RVXu?60q~WWuTx5~aN?z9b5*)lP4%vPUbvt9 zeG*0n2fQw%<`Uc(92gvkgCS}RBZ&^giOq}re&^r~pZh=G=f6I0zj?lY@$9J6pY~k* z^f#$fp40jFoBLjC@qp>IejiGlwDxBw)5bZ|E5dW@hbN0}mwnWBPyXVVwk$r+t-DFX;I%L%xT)D zfjCN*c4+W2N{)7EXc5JrJsP=!Ql@<>T}7$T0gYcnsnH>oucOrIh$=Tw8gxum8Kp^W zsx6_ks4syzP1`hZ6D3PKG2^&yoeRfSS!U79=}<1Qo1U(N)hkm jV7)7SIN~>}PB*Kf{W{_Q`tSI0Z+AQFbFQrL_SVc_d6ftV diff --git a/foundry.toml b/foundry.toml index 8b024f9a..4c769e00 100644 --- a/foundry.toml +++ b/foundry.toml @@ -2,7 +2,7 @@ auto_detect_solc = false block_timestamp = 1_714_518_000 # May 1, 2024 at 00:00 GMT bytecode_hash = "none" - evm_version = "paris" + evm_version = "shanghai" fs_permissions = [ { access = "read", path = "./out-optimized" }, { access = "read", path = "package.json"}, diff --git a/precompiles/Precompiles.sol b/precompiles/Precompiles.sol index 5f2f8ead..23a20fe0 100644 --- a/precompiles/Precompiles.sol +++ b/precompiles/Precompiles.sol @@ -17,9 +17,9 @@ contract Precompiles { //////////////////////////////////////////////////////////////////////////*/ bytes public constant BYTECODE_BATCH_LOCKUP = - hex"6080806040523461001657611f10908161001c8239f35b600080fdfe608080604052600436101561001357600080fd5b60003560e01c90816337266dd31461125b5750806349a32c4014610eb7578063606ef87514610b6b5780639e743f29146107fc578063a514f83e146104295763f7ca34eb1461006157600080fd5b346103555761006f366115d6565b9283156103ff5760009060005b8581106103cb57506001600160a01b036100999116918483611b81565b6100a284611858565b9260005b8581106100bf57604051806100bb8782611664565b0390f35b6100ca818786611b1e565b6100d3906118a7565b906100df818887611b1e565b6020016100eb906118a7565b85886100f8848284611b1e565b60400161010490611725565b61010f858385611b1e565b60600161011b906118bb565b610126868486611b1e565b608001610132906118bb565b9061013e878587611b1e565b60a00161014a90611b5e565b9287610157818789611b1e565b60c0810161016491611a2a565b96610170929198611b1e565b60e00196604051996101818b6117aa565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c086015236906101e592611a7e565b60e0840152366101f4916119cc565b610100830152604051917f31df3d48000000000000000000000000000000000000000000000000000000008352600483016020905280516001600160a01b0316602484015260208101516001600160a01b0316604484015260408101516fffffffffffffffffffffffffffffffff16606484015260608101516001600160a01b031660848401526080810151151560a484015260a0810151151560c484015260c081015164ffffffffff1660e48401528260e082015161010482016101409052805180610164840152610184830191602001906000905b80821061036e5750505081906103006101006020950151610124840190602080916001600160a01b0381511684520151910152565b03816000885af180156103625760009061032a575b600192506103238288611a05565b52016100a6565b506020823d60201161035a575b81610344602093836117ff565b810103126103555760019151610315565b600080fd5b3d9150610337565b6040513d6000823e3d90fd5b919493509160206060826103bc600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b019501920186939492916102cb565b916001906fffffffffffffffffffffffffffffffff6103f660406103f0878b8a611b1e565b01611725565b1601920161007c565b60046040517ff8bf106c000000000000000000000000000000000000000000000000000000008152fd5b346103555760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610355576104606116a0565b6104686115c0565b9060443567ffffffffffffffff8082116103555736602383011215610355578160040135116103555736602461014083600401350283010111610355578060040135156103ff576000805b826004013582106107c7576104d39150846001600160a01b038516611b81565b6104e08160040135611858565b9160005b826004013581106104fd57604051806100bb8682611664565b61051761051282856004013560248701611b70565b6118a7565b9083610536602061053084846004013560248601611b70565b016118a7565b61054d60406103f085856004013560248701611b70565b9361056b606061056586866004013560248801611b70565b016118bb565b946080956fffffffffffffffffffffffffffffffff6105968861056589896004013560248b01611b70565b926001600160a01b036101006105c78a6105b88160048d013560248e01611b70565b9a602481600401359101611b70565b019681604051976105d789611742565b1687521660208601521660408401526001600160a01b038b166060840152151586830152151560a082015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6084360301126103555761075c9261067f60e093604051610644816117c7565b61065060a08501611939565b815261066f8660c095610664878201611939565b602085015201611939565b60408201528385015236906119cc565b83830152604051957f53b157270000000000000000000000000000000000000000000000000000000087526001600160a01b0383511660048801526001600160a01b0360208401511660248801526fffffffffffffffffffffffffffffffff60408401511660448801526001600160a01b0360608401511660648801528201511515608487015260a0820151151560a4870152810151604064ffffffffff918281511660c48901528260208201511660e48901520151166101048601520151610124840190602080916001600160a01b0381511684520151910152565b6020826101648160006001600160a01b0388165af1801561036257600090610794575b6001925061078d8287611a05565b52016104e4565b506020823d6020116107bf575b816107ae602093836117ff565b81010312610355576001915161077f565b3d91506107a1565b6001906fffffffffffffffffffffffffffffffff6107f260406103f086886004013560248a01611b70565b16019101906104b3565b346103555761080a366115d6565b9283156103ff5760009060005b858110610b3d57506001600160a01b036108349116918483611b81565b61083d84611858565b9260005b85811061085657604051806100bb8782611664565b610861818786611b1e565b61086a906118a7565b90610876818887611b1e565b602001610882906118a7565b858861088f848284611b1e565b60400161089b90611725565b6108a6858385611b1e565b6060016108b2906118bb565b6108bd868486611b1e565b6080016108c9906118bb565b906108d5878587611b1e565b60a0016108e190611b5e565b92876108ee818789611b1e565b60c081016108fb916118c8565b96610907929198611b1e565b60e00196604051996109188b6117aa565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c0860152369061097c9261194b565b60e08401523661098b916119cc565b610100830152604051917f32fbe22b000000000000000000000000000000000000000000000000000000008352600483016020905280516001600160a01b0316602484015260208101516001600160a01b0316604484015260408101516fffffffffffffffffffffffffffffffff16606484015260608101516001600160a01b031660848401526080810151151560a484015260a0810151151560c484015260c081015164ffffffffff1660e48401528260e082015161010482016101409052805180610164840152610184830191602001906000905b808210610af4575050508190610a976101006020950151610124840190602080916001600160a01b0381511684520151910152565b03816000885af1801561036257600090610ac1575b60019250610aba8288611a05565b5201610841565b506020823d602011610aec575b81610adb602093836117ff565b810103126103555760019151610aac565b3d9150610ace565b91949350916020604082610b2e600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291610a62565b916001906fffffffffffffffffffffffffffffffff610b6260406103f0878b8a611b1e565b16019201610817565b3461035557610b79366115d6565b9283156103ff5760009060005b858110610e8957506001600160a01b03610ba39116918483611b81565b610bac84611858565b9260005b858110610bc557604051806100bb8782611664565b610bd08187866116b6565b610bd9906118a7565b90610be58188876116b6565b602001610bf1906118a7565b8782610bfe81838a6116b6565b604001610c0a90611725565b610c1582848b6116b6565b606001610c21906118bb565b610c2c83858c6116b6565b608001610c38906118bb565b91610c4484868d6116b6565b60a08101610c5191611a2a565b94610c5d91968d6116b6565b60c0019560405198610c6e8a611742565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a08601523690610cc692611a7e565b60c084015236610cd5916119cc565b60e0830152604051917f54c02292000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001906000905b808210610e2c575050508190610dcf60e06020950151610104840190602080916001600160a01b0381511684520151910152565b03816000885af1801561036257600090610df9575b60019250610df28288611a05565b5201610bb0565b506020823d602011610e24575b81610e13602093836117ff565b810103126103555760019151610de4565b3d9150610e06565b91949350916020606082610e7a600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b01950192018693949291610d9b565b916001906fffffffffffffffffffffffffffffffff610eae60406103f0878b8a6116b6565b16019201610b86565b346103555760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261035557610eee6116a0565b610ef66115c0565b9060443567ffffffffffffffff8082116103555736602383011215610355578160040135116103555736602461012083600401350283010111610355578060040135156103ff576000805b8260040135821061122657610f619150846001600160a01b038516611b81565b610f6e8160040135611858565b9160005b82600401358110610f8b57604051806100bb8682611664565b610fa061051282856004013560248701611a19565b9083610fb9602061053084846004013560248601611a19565b610fd060406103f085856004013560248701611a19565b93610fe8606061056586866004013560248801611a19565b946080956fffffffffffffffffffffffffffffffff6110138861056589896004013560248b01611a19565b926001600160a01b0360e06110438a6110348160048d013560248e01611a19565b9a602481600401359101611a19565b0196816040519761105389611742565b1687521660208601521660408401526001600160a01b038b166060840152151586830152151560a082015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff608436030112610355576111bb926110eb60e0936040516110c08161178e565b6110cc60a08501611939565b81526110db60c0809501611939565b60208201528385015236906119cc565b83830152604051957fab167ccc0000000000000000000000000000000000000000000000000000000087526001600160a01b0383511660048801526001600160a01b0360208401511660248801526fffffffffffffffffffffffffffffffff60408401511660448801526001600160a01b0360608401511660648801528201511515608487015260a0820151151560a4870152810151602064ffffffffff918281511660c489015201511660e48601520151610104840190602080916001600160a01b0381511684520151910152565b6020826101448160006001600160a01b0388165af18015610362576000906111f3575b600192506111ec8287611a05565b5201610f72565b506020823d60201161121e575b8161120d602093836117ff565b8101031261035557600191516111de565b3d9150611200565b6001906fffffffffffffffffffffffffffffffff61125160406103f086886004013560248a01611a19565b1601910190610f41565b3461035557611269366115d6565b93849391929315611598575060009060005b85811061156a57506001600160a01b036112989116918483611b81565b6112a184611858565b9260005b8581106112ba57604051806100bb8782611664565b6112c58187866116b6565b6112ce906118a7565b906112da8188876116b6565b6020016112e6906118a7565b87826112f381838a6116b6565b6040016112ff90611725565b61130a82848b6116b6565b606001611316906118bb565b61132183858c6116b6565b60800161132d906118bb565b9161133984868d6116b6565b60a08101611346916118c8565b9461135291968d6116b6565b60c00195604051986113638a611742565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a086015236906113bb9261194b565b60c0840152366113ca916119cc565b60e0830152604051917f897f362b000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001906000905b8082106115215750505081906114c460e06020950151610104840190602080916001600160a01b0381511684520151910152565b03816000885af18015610362576000906114ee575b600192506114e78288611a05565b52016112a5565b506020823d602011611519575b81611508602093836117ff565b8101031261035557600191516114d9565b3d91506114fb565b9194935091602060408261155b600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291611490565b916001906fffffffffffffffffffffffffffffffff61158f60406103f0878b8a6116b6565b1601920161127b565b807ff8bf106c0000000000000000000000000000000000000000000000000000000060049252fd5b602435906001600160a01b038216820361035557565b9060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc830112610355576001600160a01b0391600435838116810361035557926024359081168103610355579160443567ffffffffffffffff9283821161035557806023830112156103555781600401359384116103555760248460051b83010111610355576024019190565b602090602060408183019282815285518094520193019160005b82811061168c575050505090565b83518552938101939281019260010161167e565b600435906001600160a01b038216820361035557565b91908110156116f65760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0181360301821215610355570190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b356fffffffffffffffffffffffffffffffff811681036103555790565b610100810190811067ffffffffffffffff82111761175f57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040810190811067ffffffffffffffff82111761175f57604052565b610120810190811067ffffffffffffffff82111761175f57604052565b6060810190811067ffffffffffffffff82111761175f57604052565b6080810190811067ffffffffffffffff82111761175f57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761175f57604052565b67ffffffffffffffff811161175f5760051b60200190565b9061186282611840565b61186f60405191826117ff565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061189d8294611840565b0190602036910137565b356001600160a01b03811681036103555790565b3580151581036103555790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610355570180359067ffffffffffffffff821161035557602001918160061b3603831361035557565b35906fffffffffffffffffffffffffffffffff8216820361035557565b359064ffffffffff8216820361035557565b92919261195782611840565b60409261196760405192836117ff565b819581835260208093019160061b84019381851161035557915b84831061199057505050505050565b85838303126103555783869182516119a78161178e565b6119b08661191c565b81526119bd838701611939565b83820152815201920191611981565b9190826040910312610355576040516119e48161178e565b809280356001600160a01b0381168103610355578252602090810135910152565b80518210156116f65760209160051b010190565b91908110156116f657610120020190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610355570180359067ffffffffffffffff82116103555760200191606082023603831361035557565b929192611a8a82611840565b604094611a9a60405192836117ff565b8195848352602080930191606080960285019481861161035557925b858410611ac65750505050505050565b868483031261035557825190611adb826117c7565b611ae48561191c565b8252858501359067ffffffffffffffff8216820361035557828792838b950152611b0f868801611939565b86820152815201930192611ab6565b91908110156116f65760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee181360301821215610355570190565b3564ffffffffff811681036103555790565b91908110156116f657610140020190565b90604080516020907f23b872dd000000000000000000000000000000000000000000000000000000008282015233602482015260449030828201528660648201526064815260a081019080821067ffffffffffffffff83111761175f57611bea91855285611d86565b6001600160a01b0394858516958451917fdd62ed3e0000000000000000000000000000000000000000000000000000000083523060048401521690816024820152838184818a5afa908115611d6357908891600091611d32575b5010611c54575b50505050505050565b8351956000808589017f095ea7b3000000000000000000000000000000000000000000000000000000009a8b82528560248c0152868b0152858a52611c988a6117e3565b89519082855af190611ca8611e12565b82611cff575b5081611cf4575b50611c4b57611ce896611ce394519384015260248301526000818301528152611cdd816117e3565b82611d86565b611d86565b38808080808080611c4b565b90503b151538611cb5565b809192505190858215928315611d1a575b5050509038611cae565b611d2a9350820181019101611d6e565b388581611d10565b809250858092503d8311611d5c575b611d4b81836117ff565b810103126103555787905138611c44565b503d611d41565b85513d6000823e3d90fd5b90816020910312610355575180151581036103555790565b6000806001600160a01b03611db093169360208151910182865af1611da9611e12565b9083611e70565b8051908115159182611df7575b5050611dc65750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b611e0a9250602080918301019101611d6e565b153880611dbd565b3d15611e6b573d9067ffffffffffffffff821161175f5760405191611e5f60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846117ff565b82523d6000602084013e565b606090565b90611eaf5750805115611e8557805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580611efa575b611ec0575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15611eb856fea164736f6c6343000817000a"; + hex"6080806040523461001657611e91908161001b8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c90816337266dd3146111ed5750806349a32c4014610e9a578063606ef87514610b545780639e743f29146107e9578063a514f83e146104205763f7ca34eb1461005e575f80fd5b3461034e5761006c36611562565b9283156103f6575f905f5b8581106103c257506001600160a01b036100949116918483611b08565b61009d846117df565b925f5b8581106100b957604051806100b587826115f0565b0390f35b6100c4818786611aa5565b6100cd9061182e565b906100d9818887611aa5565b6020016100e59061182e565b85886100f2848284611aa5565b6040016100fe906116ae565b610109858385611aa5565b60600161011590611842565b610120868486611aa5565b60800161012c90611842565b90610138878587611aa5565b60a00161014490611ae5565b9287610151818789611aa5565b60c0810161015e916119b1565b9661016a929198611aa5565b60e001966040519961017b8b611731565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c086015236906101df92611a05565b60e0840152366101ee91611953565b610100830152604051917f31df3d48000000000000000000000000000000000000000000000000000000008352600483016020905282610164810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c083015164ffffffffff1660e483015260e083015190610104830161014090528151809152610184830191602001905f905b8082106103655750505081906102fb6101006020950151610124840190602080916001600160a01b0381511684520151910152565b03815f885af1801561035a575f90610323575b6001925061031c828861198c565b52016100a0565b506020823d602011610352575b8161033d60209383611786565b8101031261034e576001915161030e565b5f80fd5b3d9150610330565b6040513d5f823e3d90fd5b919493509160206060826103b3600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b019501920186939492916102c6565b916001906fffffffffffffffffffffffffffffffff6103ed60406103e7878b8a611aa5565b016116ae565b16019201610077565b60046040517ff8bf106c000000000000000000000000000000000000000000000000000000008152fd5b3461034e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261034e5761045761162b565b61045f61154c565b604480359267ffffffffffffffff80851161034e573660238601121561034e5784600401351161034e576024913660246101408760040135028701011161034e578460040135156103f6575f915f5b866004013581106107b457506001600160a01b036104cf9116928584611b08565b6104dc85600401356117df565b935f5b866004013581106104f857604051806100b588826115f0565b61051261050d82896004013560248b01611af7565b61182e565b90610530602061052a838b6004013560248d01611af7565b0161182e565b8861054860406103e785846004013560248601611af7565b610565606061055f86856004013560248701611af7565b01611842565b6fffffffffffffffffffffffffffffffff61058d608061055f88876004013560248901611af7565b926001600160a01b036101006105be896105af818a6004013560248c01611af7565b98602481600401359101611af7565b0196816040519a6105ce8c6116cb565b168a521660208901521660408701526001600160a01b038716606087015215156080860152151560a085015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60823603011261034e57610677610754926040519261063a8461174e565b61064660a082016118c0565b845261066660e060c09261065b8482016118c0565b6020880152016118c0565b604085015286019283523690611953565b9060e0850191825260a0604051957f53b157270000000000000000000000000000000000000000000000000000000087526001600160a01b0381511660048801526001600160a01b036020820151168b8801526fffffffffffffffffffffffffffffffff604082015116898801526001600160a01b0360608201511660648801526080810151151560848801520151151560a486015251604064ffffffffff918281511660c48801528260208201511660e488015201511661010485015251610124840190602080916001600160a01b0381511684520151910152565b602082610164815f895af1801561035a575f90610781575b6001925061077a828961198c565b52016104df565b506020823d6020116107ac575b8161079b60209383611786565b8101031261034e576001915161076c565b3d915061078e565b926001906fffffffffffffffffffffffffffffffff6107e060406103e78860048d013560248e01611af7565b160193016104ae565b3461034e576107f736611562565b9283156103f6575f905f5b858110610b2657506001600160a01b0361081f9116918483611b08565b610828846117df565b925f5b85811061084057604051806100b587826115f0565b61084b818786611aa5565b6108549061182e565b90610860818887611aa5565b60200161086c9061182e565b8588610879848284611aa5565b604001610885906116ae565b610890858385611aa5565b60600161089c90611842565b6108a7868486611aa5565b6080016108b390611842565b906108bf878587611aa5565b60a0016108cb90611ae5565b92876108d8818789611aa5565b60c081016108e59161184f565b966108f1929198611aa5565b60e00196604051996109028b611731565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c08601523690610966926118d2565b60e08401523661097591611953565b610100830152604051917f32fbe22b000000000000000000000000000000000000000000000000000000008352600483016020905282610164810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c083015164ffffffffff1660e483015260e083015190610104830161014090528151809152610184830191602001905f905b808210610add575050508190610a826101006020950151610124840190602080916001600160a01b0381511684520151910152565b03815f885af1801561035a575f90610aaa575b60019250610aa3828861198c565b520161082b565b506020823d602011610ad5575b81610ac460209383611786565b8101031261034e5760019151610a95565b3d9150610ab7565b91949350916020604082610b17600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291610a4d565b916001906fffffffffffffffffffffffffffffffff610b4b60406103e7878b8a611aa5565b16019201610802565b3461034e57610b6236611562565b9283156103f6575f905f5b858110610e6c57506001600160a01b03610b8a9116918483611b08565b610b93846117df565b925f5b858110610bab57604051806100b587826115f0565b610bb6818786611641565b610bbf9061182e565b90610bcb818887611641565b602001610bd79061182e565b8782610be481838a611641565b604001610bf0906116ae565b610bfb82848b611641565b606001610c0790611842565b610c1283858c611641565b608001610c1e90611842565b91610c2a84868d611641565b60a08101610c37916119b1565b94610c4391968d611641565b60c0019560405198610c548a6116cb565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a08601523690610cac92611a05565b60c084015236610cbb91611953565b60e0830152604051917f54c02292000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001905f905b808210610e0f575050508190610db460e06020950151610104840190602080916001600160a01b0381511684520151910152565b03815f885af1801561035a575f90610ddc575b60019250610dd5828861198c565b5201610b96565b506020823d602011610e07575b81610df660209383611786565b8101031261034e5760019151610dc7565b3d9150610de9565b91949350916020606082610e5d600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b01950192018693949291610d80565b916001906fffffffffffffffffffffffffffffffff610e9160406103e7878b8a611641565b16019201610b6d565b3461034e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261034e57610ed161162b565b610ed961154c565b604480359267ffffffffffffffff9384811161034e573660238201121561034e57806004013594851161034e57602481019060243691610120880201011161034e5784156103f6575f915f5b8681106111bf57506001600160a01b03610f429116928584611b08565b610f4b856117df565b935f5b868110610f6357604051806100b588826115f0565b610f7161050d8289866119a0565b90610f82602061052a838b886119a0565b8489610f9460406103e78684866119a0565b6fffffffffffffffffffffffffffffffff610fb5606061055f8886886119a0565b916001600160a01b0360e0610fe689610fd4608061055f838b8d6119a0565b97610fe082828c6119a0565b996119a0565b0196816040519a610ff68c6116cb565b168a521660208901521660408701526001600160a01b038716606087015215156080860152151560a085015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60823603011261034e5761108e61115f926040519261106284611715565b61106e60a082016118c0565b845261107d60c08092016118c0565b602085015286019283523690611953565b9060e0850191825260a0604051957fab167ccc0000000000000000000000000000000000000000000000000000000087526001600160a01b0381511660048801526001600160a01b0360208201511660248801526fffffffffffffffffffffffffffffffff6040820151168b8801526001600160a01b0360608201511660648801526080810151151560848801520151151560a486015251602064ffffffffff918281511660c488015201511660e485015251610104840190602080916001600160a01b0381511684520151910152565b602082610144815f895af1801561035a575f9061118c575b60019250611185828961198c565b5201610f4e565b506020823d6020116111b7575b816111a660209383611786565b8101031261034e5760019151611177565b3d9150611199565b926001906fffffffffffffffffffffffffffffffff6111e460406103e7888c896119a0565b16019301610f25565b3461034e576111fb36611562565b9384939192931561152457505f905f5b8581106114f657506001600160a01b036112289116918483611b08565b611231846117df565b925f5b85811061124957604051806100b587826115f0565b611254818786611641565b61125d9061182e565b90611269818887611641565b6020016112759061182e565b878261128281838a611641565b60400161128e906116ae565b61129982848b611641565b6060016112a590611842565b6112b083858c611641565b6080016112bc90611842565b916112c884868d611641565b60a081016112d59161184f565b946112e191968d611641565b60c00195604051986112f28a6116cb565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a0860152369061134a926118d2565b60c08401523661135991611953565b60e0830152604051917f897f362b000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001905f905b8082106114ad57505050819061145260e06020950151610104840190602080916001600160a01b0381511684520151910152565b03815f885af1801561035a575f9061147a575b60019250611473828861198c565b5201611234565b506020823d6020116114a5575b8161149460209383611786565b8101031261034e5760019151611465565b3d9150611487565b919493509160206040826114e7600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b0195019201869394929161141e565b916001906fffffffffffffffffffffffffffffffff61151b60406103e7878b8a611641565b1601920161120b565b807ff8bf106c0000000000000000000000000000000000000000000000000000000060049252fd5b602435906001600160a01b038216820361034e57565b9060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261034e576001600160a01b0391600435838116810361034e5792602435908116810361034e579160443567ffffffffffffffff9283821161034e578060238301121561034e57816004013593841161034e5760248460051b8301011161034e576024019190565b60209060206040818301928281528551809452019301915f5b828110611617575050505090565b835185529381019392810192600101611609565b600435906001600160a01b038216820361034e57565b91908110156116815760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018136030182121561034e570190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b356fffffffffffffffffffffffffffffffff8116810361034e5790565b610100810190811067ffffffffffffffff8211176116e857604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff8211176116e857604052565b610120810190811067ffffffffffffffff8211176116e857604052565b6060810190811067ffffffffffffffff8211176116e857604052565b6080810190811067ffffffffffffffff8211176116e857604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176116e857604052565b67ffffffffffffffff81116116e85760051b60200190565b906117e9826117c7565b6117f66040519182611786565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061182482946117c7565b0190602036910137565b356001600160a01b038116810361034e5790565b35801515810361034e5790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561034e570180359067ffffffffffffffff821161034e57602001918160061b3603831361034e57565b35906fffffffffffffffffffffffffffffffff8216820361034e57565b359064ffffffffff8216820361034e57565b9291926118de826117c7565b6040926118ee6040519283611786565b819581835260208093019160061b84019381851161034e57915b84831061191757505050505050565b858383031261034e57838691825161192e81611715565b611937866118a3565b81526119448387016118c0565b83820152815201920191611908565b919082604091031261034e5760405161196b81611715565b809280356001600160a01b038116810361034e578252602090810135910152565b80518210156116815760209160051b010190565b919081101561168157610120020190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561034e570180359067ffffffffffffffff821161034e5760200191606082023603831361034e57565b929192611a11826117c7565b604094611a216040519283611786565b8195848352602080930191606080960285019481861161034e57925b858410611a4d5750505050505050565b868483031261034e57825190611a628261174e565b611a6b856118a3565b8252858501359067ffffffffffffffff8216820361034e57828792838b950152611a968688016118c0565b86820152815201930192611a3d565b91908110156116815760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18136030182121561034e570190565b3564ffffffffff8116810361034e5790565b919081101561168157610140020190565b90604080516020907f23b872dd000000000000000000000000000000000000000000000000000000008282015233602482015260449030828201528660648201526064815260a081019080821067ffffffffffffffff8311176116e857611b7191855285611d09565b6001600160a01b0394858516958451917fdd62ed3e0000000000000000000000000000000000000000000000000000000083523060048401521690816024820152838184818a5afa908115611ce7579088915f91611cb6575b5010611bda575b50505050505050565b8351955f808589017f095ea7b3000000000000000000000000000000000000000000000000000000009a8b82528560248c0152868b0152858a52611c1d8a61176a565b89519082855af190611c2d611d94565b82611c83575b5081611c78575b50611bd157611c6c96611c6794519384015260248301525f818301528152611c618161176a565b82611d09565b611d09565b5f808080808080611bd1565b90503b15155f611c3a565b809192505190858215928315611c9e575b505050905f611c33565b611cae9350820181019101611cf1565b5f8581611c94565b809250858092503d8311611ce0575b611ccf8183611786565b8101031261034e578790515f611bca565b503d611cc5565b85513d5f823e3d90fd5b9081602091031261034e5751801515810361034e5790565b5f806001600160a01b03611d3293169360208151910182865af1611d2b611d94565b9083611df1565b8051908115159182611d79575b5050611d485750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b611d8c9250602080918301019101611cf1565b155f80611d3f565b3d15611dec573d9067ffffffffffffffff82116116e85760405191611de160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184611786565b82523d5f602084013e565b606090565b90611e305750805115611e0657805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580611e7b575b611e41575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15611e3956fea164736f6c6343000817000a"; bytes public constant BYTECODE_MERKLE_LOCKUP_FACTORY = - hex"608080604052346100165761416c908161001c8239f35b600080fdfe604060808152600490813610156200001657600080fd5b600091823560e01c80631e32387614620002425763769bed20146200003a57600080fd5b346200023e5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126200023e5767ffffffffffffffff9281358481116200023a576200008e90369084016200069b565b916024359173ffffffffffffffffffffffffffffffffffffffff918284168094036200023a57857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126200023a57620000e86200056a565b9664ffffffffff60443581811681036200023657895260643590811681036200023257602089015286519161191e908184019284841090841117620002065750908291620009108339608081526200016b8862000149608084018a6200082e565b92886020820152018a6020908164ffffffffff91828151168552015116910152565b039082f0908115620001fb576020620001e287897f2ba0fe49588281dbb122dd3b7f3e2b3396338f70dbe3c62bf3e3888b4ba7ffb8620001c08a8a8a8a16968795875194859460c0865260c08601906200082e565b928a850152878401906020908164ffffffffff91828151168552015116910152565b608435608083015260a43560a08301520390a251908152f35b8551903d90823e3d90fd5b8460416024927f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8380fd5b8480fd5b5080fd5b8280fd5b508234620005675760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112620005675767ffffffffffffffff9082358281116200023a576200029890369085016200069b565b91602480359273ffffffffffffffffffffffffffffffffffffffff958685168095036200023a57604435978489116200023e573660238a0112156200023e5788820135958587116200053c57602099620002f78b8960051b01620005ba565b97868c8a838152019160061b8301019136831162000538578701905b828210620004f1575050509083978485995b89518b1015620003a25788806200033d8d8d6200078a565b51511691160188811162000377579392919060019064ffffffffff8e620003658e8e6200078a565b51015116019a01999091929362000325565b87876011887f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b8c96888a8c9697988f948216670de0b6b3a76400008103620004c45750885192611f329182850193858510908511176200049a57505082916200040c916200222e843960608152620003f860608201896200082e565b908b8d8201528a81830391015288620008bd565b039082f09081156200048f575091620004707ffe44018cf74992b2720702385a1728bd329dd136e4f651203176c81c12710a8b94926200045e9416978896885195869560c0875260c08701906200082e565b918b86015284820389860152620008bd565b906060830152606435608083015260843560a08301520390a251908152f35b8651903d90823e3d90fd5b604186917f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b89517f1b05170a000000000000000000000000000000000000000000000000000000008152918201529050fd5b84823603126200053857620005056200056a565b82358a811681036200053457815285918e916200052485840162000612565b8382015281520191019062000313565b8880fd5b8680fd5b50506041907f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b80fd5b604051906040820182811067ffffffffffffffff8211176200058b57604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f604051930116820182811067ffffffffffffffff8211176200058b57604052565b359081151582036200060d57565b600080fd5b359064ffffffffff821682036200060d57565b81601f820112156200060d5780359067ffffffffffffffff82116200058b576200067760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f85011601620005ba565b92828452602083830101116200060d57816000926020809301838601378301015290565b9190916101009081818503126200060d5760405191820167ffffffffffffffff90838110828211176200058b576040528294823573ffffffffffffffffffffffffffffffffffffffff9081811681036200060d578552620006ff60208501620005ff565b6020860152620007126040850162000612565b6040860152606084013590811681036200060d57606085015260808301358281116200060d57816200074691850162000625565b608085015260a083013560a085015260c08301359182116200060d57826200077960e09492620007859486940162000625565b60c086015201620005ff565b910152565b80518210156200079f5760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b919082519283825260005b848110620008195750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006020809697860101520116010190565b602081830181015184830182015201620007d9565b9060e080620008b26200089561010073ffffffffffffffffffffffffffffffffffffffff80885116875260208801511515602088015264ffffffffff60408901511660408801526060880151166060870152608087015190806080880152860190620007ce565b60a086015160a086015260c086015185820360c0870152620007ce565b930151151591015290565b90815180825260208080930193019160005b828110620008de575050505090565b8351805167ffffffffffffffff16865282015164ffffffffff168583015260409094019392810192600101620008cf56fe6101608060405234620005ba576200191e8038038091620000218285620005db565b833981019080820360808112620005ba5781516001600160401b038111620005ba5782019061010082850312620005ba576040519361010085016001600160401b03811186821017620005a45760405282516001600160a01b0381168103620005ba5785526200009460208401620005ff565b9260208601938452620000aa604082016200060d565b604087019081526060820151909690926001600160a01b0384168403620005ba576060820193845260808301516001600160401b038111620005ba5781620000f491850162000661565b6080830190815260a08085015190840190815260c085015191949092906001600160401b038311620005ba576200013460e0926200014094830162000661565b60c086015201620005ff565b60e083019081526020880151959093906001600160a01b0387168703620005ba57604090603f190112620005ba5760408051989089016001600160401b0381118a821017620005a457620001ab91606091604052620001a2604082016200060d565b8b52016200060d565b946020890195865260c0840151516020811162000585575051600080546001600160a01b0319166001600160a01b0392831617815584519091166080529651151560a052975164ffffffffff90811660c052975180516001600160401b038111620005715760019182548381811c9116801562000566575b60208210146200055257601f811162000505575b50602090601f8311600114620004995760c095949392918a91836200048d575b5050600019600383901b1c191690821b1790555b5160e05201516040516200029f602082816200029181830196878151938492016200063c565b8101038084520182620005db565b51905190602081106200047a575b50610100525115159461012095865261014094838652511669ffffffffff0000000000600454925160281b169160018060501b031916171760045560018060a01b03608051166040519160208301848063095ea7b360e01b9283815260018060a01b03851660248801526000196044880152604487526200032e87620005bf565b86519082875af16200033f620006ae565b8162000439575b50806200042e575b15620003e2575b60405161114e90878983620007d084396080518381816104a0015281816107860152610cbf015260a0518381816107ad0152610bd1015260c05183818161016801528181610b2601528181610e4c015261106c015260e0518381816102ed015261069201526101005183610f310152518281816107d50152610b940152518181816101b901526109480152f35b62000423946200041d9260405192602084015260018060a01b031660248301526044820152604481526200041681620005bf565b82620006e3565b620006e3565b388080808062000355565b50823b15156200034e565b805180159250821562000450575b50503862000346565b8192509060209181010312620004765760206200046e9101620005ff565b388062000447565b8580fd5b6000199060200360031b1b1638620002ad565b01519050388062000257565b838a5260208a209190601f1984168b5b818110620004ee575091859492918360c0999897959310620004d4575b505050811b0190556200026b565b015160001960f88460031b161c19169055388080620004c6565b8284015185559386019360209384019301620004a9565b838a5260208a20601f840160051c8101916020851062000547575b601f0160051c019084905b8281106200053b57505062000237565b8b81550184906200052b565b909150819062000520565b634e487b7160e01b8a52602260045260248afd5b90607f169062000223565b634e487b7160e01b88526041600452602488fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b634e487b7160e01b600052604160045260246000fd5b600080fd5b608081019081106001600160401b03821117620005a457604052565b601f909101601f19168101906001600160401b03821190821017620005a457604052565b51908115158203620005ba57565b519064ffffffffff82168203620005ba57565b6001600160401b038111620005a457601f01601f191660200190565b60005b838110620006505750506000910152565b81810151838201526020016200063f565b81601f82011215620005ba5780516200067a8162000620565b926200068a6040519485620005db565b81845260208284010111620005ba57620006ab91602080850191016200063c565b90565b3d15620006de573d90620006c28262000620565b91620006d26040519384620005db565b82523d6000602084013e565b606090565b600080620007109260018060a01b03169360208151910182865af162000708620006ae565b908362000767565b805190811515918262000740575b5050620007285750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620005ba5760206200075e9101620005ff565b1538806200071e565b906200079057508051156200077e57805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580620007c5575b620007a3575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156200079a56fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde0314610f1a575080631686c90914610bf657806316c3549d14610bb95780631bfd681414610b7c5780633bfe03a814610b4d5780633f31ae3f146104c45780634800d97f1461047357806349fc73dd146103355780634e390d3e1461031057806351e75e8b146102d557806375829def146101f857806390e64d13146101dd5780639e93e5771461018c578063bb4b57341461014a578063ce516507146101085763f851a440146100cf57600080fd5b3461010357600060031936011261010357602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b3461010357602060031936011261010357602061014060043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b3461010357600060031936011261010357602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461010357600060031936011261010357602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610103576000600319360112610103576020610140611064565b3461010357602060031936011261010357610211610fc8565b60005473ffffffffffffffffffffffffffffffffffffffff80821692338403610288577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346101035760006003193601126101035760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461010357600060031936011261010357602064ffffffffff60035416604051908152f35b346101035760006003193601126101035760405160009060018054908160011c9060018316928315610469575b602093848410811461043a578386529081156103fc57506001146103a1575b61039d8461039181880382611023565b60405191829182610f62565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8284106103e9575050508161039d936103919282010193610381565b80548585018701529285019281016103cd565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103918161039d610381565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691610362565b3461010357600060031936011261010357602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101035760806003193601126101035760243573ffffffffffffffffffffffffffffffffffffffff8116810361010357604435906fffffffffffffffffffffffffffffffff821682036101035767ffffffffffffffff60643511610103573660236064350112156101035767ffffffffffffffff6064356004013511610103573660246064356004013560051b606435010111610103576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff831660408201526fffffffffffffffffffffffffffffffff84166060820152606081526105aa81611007565b51902060405160208101918252602081526105c481610feb565b519020916105d0611064565b610af0576105f960043560ff6001918060081c6000526002602052161b60406000205416151590565b610abe576040519261061760206064356004013560051b0185611023565b60643560048101358552602401602085015b60246064356004013560051b60643501018210610aae575050906000915b845183101561068e5760208360051b860101519081811060001461067b57600052602052600160406000205b920191610647565b9060005260205260016040600020610673565b83907f000000000000000000000000000000000000000000000000000000000000000003610a845760035464ffffffffff811615610a50575b5060043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff600054169060405161070f81610feb565b600081526000602082015260405192610100840184811067ffffffffffffffff821117610a2157604052835273ffffffffffffffffffffffffffffffffffffffff821660208401526fffffffffffffffffffffffffffffffff8416604084015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608401527f0000000000000000000000000000000000000000000000000000000000000000151560808401527f0000000000000000000000000000000000000000000000000000000000000000151560a084015260405161080781610feb565b64ffffffffff600454818116835260281c16602082015260c084015260e0830152602060e0604051937fab167ccc00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff815116600486015273ffffffffffffffffffffffffffffffffffffffff838201511660248601526fffffffffffffffffffffffffffffffff604082015116604486015273ffffffffffffffffffffffffffffffffffffffff606082015116606486015260808101511515608486015260a0810151151560a486015264ffffffffff8360c08301518281511660c489015201511660e4860152015173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015260208261014481600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1918215610a15576000926109e0575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d602011610a0d575b816109fb60209383611023565b81010312610103579051906020610979565b3d91506109ee565b6040513d6000823e3d90fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff1617600355826106c7565b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b8135815260209182019101610629565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b60446040517f442b184100000000000000000000000000000000000000000000000000000000815242600482015264ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166024820152fd5b3461010357600060031936011261010357604060045464ffffffffff825191818116835260281c166020820152f35b346101035760006003193601126101035760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346101035760006003193601126101035760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b3461010357604060031936011261010357610c0f610fc8565b60248035906fffffffffffffffffffffffffffffffff82168092036101035773ffffffffffffffffffffffffffffffffffffffff8060005416338103610ecb575064ffffffffff806003541680151580610e86575b80610e77575b610e185750506040519160209260008084868401987fa9059cbb000000000000000000000000000000000000000000000000000000008a521697888585015287604485015260448452610cbc84611007565b857f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610e0c573d9067ffffffffffffffff8211610dde5790610d479160405191610d37887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184611023565b82523d60008884013e5b836110a1565b8051858115159182610dbd575b50509050610d8f575050907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126101035784015180159081150361010357808589610d54565b837f4e487b710000000000000000000000000000000000000000000000000000000060005260416004526000fd5b610d4790606090610d41565b6064925083604051927f92b666970000000000000000000000000000000000000000000000000000000084524260048501527f000000000000000000000000000000000000000000000000000000000000000016908301526044820152fd5b50610e80611064565b15610c6a565b5062093a808101828111610e9d5782164211610c64565b847f4e487b710000000000000000000000000000000000000000000000000000000060005260116004526000fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346101035760006003193601126101035761039d907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261039181610feb565b60208082528251818301819052939260005b858110610fb4575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b818101830151848201604001528201610f74565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361010357565b6040810190811067ffffffffffffffff821117610a2157604052565b6080810190811067ffffffffffffffff821117610a2157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610a2157604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081611099575090565b905042101590565b906110e057508051156110b657805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580611138575b6110f1575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b156110e956fea164736f6c6343000817000a6101608060405234620006e75762001f3290813803809262000022828462000733565b82396060818381010312620006e75780516001600160401b038111620006e7578101916101009283818385010312620006e757604051908482016001600160401b03811183821017620007015760405280516001600160a01b0381168103620006e7578252620000956020820162000757565b6020830152620000a86040820162000765565b604083015260608101516001600160a01b0381168103620006e757606083015260808101516001600160401b038111620006e757620000ed90848601908301620007b9565b608083015260a0818101519083015260c08101516001600160401b038111620006e75762000134916200012860e092868801908301620007b9565b60c08501520162000757565b60e08201526020830151916001600160a01b0383168303620006e7576040840151906001600160401b038211620006e757808501601f838701011215620006e75784820151906001600160401b0382116200070157604051956200019f60208460051b018862000733565b828752602087019382820160208560061b838501010111620006e75793602085830101945b60208560061b82850101018610620006815750505050505060c081015151602081116200066257506060810151600080546001600160a01b0319166001600160a01b03928316178155825190911660809081526020830151151560a052604083015164ffffffffff1660c0528201518051919290916001600160401b0381116200064e5760019283548481811c9116801562000643575b60208210146200062f57601f8111620005e2575b50602090601f8311600114620005785760e09392918691836200056c575b5050600019600383901b1c191690841b1783555b60a0810151825260c0810151604051620002db60208281620002cd818301968781519384920162000794565b810103808452018262000733565b519051906020811062000559575b5087520151151593610120948552610140938452805190835b82811062000499575050505060018060a01b036080511660018060a01b03835116906040519160208301848063095ea7b360e01b92838152846024880152600019604488015260448752620003578762000717565b86519082875af16200036862000806565b8162000458575b50806200044d575b1562000409575b60405161160a9087898b84620009288539608051848181610512015281816108aa0152610fb8015260a0518481816108d10152610eca015260c05184818161022b01528181610e4e015281816111450152611365015260e05184818161035f01526106e30152518361122a0152518281816108f90152610e8d0152518181816101350152610a630152f35b62000442946200043c926040519260208401526024830152604482015260448152620004358162000717565b826200083b565b6200083b565b38808080806200037e565b50823b151562000377565b80518015925082156200046f575b5050386200036f565b8192509060209181010312620004955760206200048d910162000757565b388062000466565b8580fd5b8151811015620005455760208160051b8301015160048054906801000000000000000082101562000532578682018082558210156200051f578752602080882083519201805493909101516cffffffffff000000000000000060409190911b166001600160401b039092166001600160681b031990931692909217179055830162000302565b634e487b7160e01b885260329052602487fd5b634e487b7160e01b885260419052602487fd5b634e487b7160e01b85526032600452602485fd5b6000199060200360031b1b1638620002e9565b0151905038806200028d565b92918491601f198216908388526020882091885b818110620005c95750958360e09710620005af575b505050811b018355620002a1565b015160001960f88460031b161c19169055388080620005a1565b828801518455889590930192602092830192016200058c565b84865260208620601f840160051c8101916020851062000624575b601f0160051c019085905b828110620006185750506200026f565b87815501859062000608565b9091508190620005fd565b634e487b7160e01b86526022600452602486fd5b90607f16906200025b565b634e487b7160e01b84526041600452602484fd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b6040868585010312620006e757604080519081016001600160401b03811182821017620006ec5760405286516001600160401b0381168103620006e75760209382859260409452620006d5838b0162000765565b838201528152019601959150620001c4565b600080fd5b60246000634e487b7160e01b81526041600452fd5b634e487b7160e01b600052604160045260246000fd5b608081019081106001600160401b038211176200070157604052565b601f909101601f19168101906001600160401b038211908210176200070157604052565b51908115158203620006e757565b519064ffffffffff82168203620006e757565b6001600160401b0381116200070157601f01601f191660200190565b60005b838110620007a85750506000910152565b818101518382015260200162000797565b81601f82011215620006e7578051620007d28162000778565b92620007e2604051948562000733565b81845260208284010111620006e75762000803916020808501910162000794565b90565b3d1562000836573d906200081a8262000778565b916200082a604051938462000733565b82523d6000602084013e565b606090565b600080620008689260018060a01b03169360208151910182865af16200086062000806565b9083620008bf565b805190811515918262000898575b5050620008805750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620006e7576020620008b6910162000757565b15388062000876565b90620008e85750805115620008d657805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806200091d575b620008fb575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15620008f256fe608080604052600436101561001357600080fd5b60003560e01c90816306fdde0314611213575080631686c90914610eef57806316c3549d14610eb25780631bfd681414610e755780633f31ae3f146105365780634800d97f146104e557806349fc73dd146103a75780634e390d3e1461038257806351e75e8b1461034757806375829def1461026a57806390e64d131461024f578063bb4b57341461020d578063bf4ed03f1461019b578063ce51650714610159578063da792468146101085763f851a440146100cf57600080fd5b3461010357600060031936011261010357602073ffffffffffffffffffffffffffffffffffffffff60005416604051908152f35b600080fd5b3461010357600060031936011261010357602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461010357602060031936011261010357602061019160043560ff6001918060081c6000526002602052161b60406000205416151590565b6040519015158152f35b34610103576000600319360112610103576101b46113b2565b6040516020918282018383528151809152836040840192019360005b8281106101dd5784840385f35b8551805167ffffffffffffffff16855282015164ffffffffff1684830152948101946040909301926001016101d0565b3461010357600060031936011261010357602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461010357600060031936011261010357602061019161135d565b34610103576020600319360112610103576102836112c1565b60005473ffffffffffffffffffffffffffffffffffffffff808216923384036102fa577fffffffffffffffffffffffff00000000000000000000000000000000000000009350169182911617600055337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf80600080a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346101035760006003193601126101035760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461010357600060031936011261010357602064ffffffffff60035416604051908152f35b346101035760006003193601126101035760405160009060018054908160011c90600183169283156104db575b60209384841081146104ac5783865290811561046e5750600114610413575b61040f846104038188038261131c565b6040519182918261125b565b0390f35b600160009081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b82841061045b575050508161040f9361040392820101936103f3565b805485850187015292850192810161043f565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506104038161040f6103f3565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f16916103d4565b3461010357600060031936011261010357602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346101035760806003193601126101035760243573ffffffffffffffffffffffffffffffffffffffff81168103610103576fffffffffffffffffffffffffffffffff6044351660443503610103576064359067ffffffffffffffff821161010357366023830112156101035781600401359167ffffffffffffffff83116101035760248360051b820101368111610103576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff851660408201526fffffffffffffffffffffffffffffffff6044351660608201526060815261061781611300565b5190206040516020810191825260208152610631816112e4565b5190209161063d61135d565b610e185761066660043560ff6001918060081c6000526002602052161b60406000205416151590565b610de6576106738561139a565b94610681604051968761131c565b8552602401602085015b828210610dd6575050506000905b83518210156106df576106ac828561144d565b5190818110156106cc57600052602052600160406000205b910190610699565b90600052602052600160406000206106c4565b90507f000000000000000000000000000000000000000000000000000000000000000003610dac5760035464ffffffffff811615610d78575b506107216113b2565b906000825161072f8161139a565b9361073d604051958661131c565b8185527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061076a8361139a565b0160005b818110610d535750506000905b828210610c2e5750506fffffffffffffffffffffffffffffffff604435166fffffffffffffffffffffffffffffffff831611610bff576fffffffffffffffffffffffffffffffff604435166fffffffffffffffffffffffffffffffff831610610bab575b505060043560081c60005260026020526040600020600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff600054169160405190610829826112e4565b6000825260006020830152604051938461010081011067ffffffffffffffff61010087011117610b7c576101008501604052845273ffffffffffffffffffffffffffffffffffffffff831660208501526fffffffffffffffffffffffffffffffff60443516604085015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608501527f0000000000000000000000000000000000000000000000000000000000000000151560808501527f0000000000000000000000000000000000000000000000000000000000000000151560a085015260c084015260e0830152604051917f897f362b0000000000000000000000000000000000000000000000000000000083526020600484015282610144810173ffffffffffffffffffffffffffffffffffffffff835116602483015273ffffffffffffffffffffffffffffffffffffffff60208401511660448301526fffffffffffffffffffffffffffffffff604084015116606483015273ffffffffffffffffffffffffffffffffffffffff60608401511660848301526080830151151560a483015260a0830151151560c483015260c08301519061012060e484015281518091526020610164840192019060005b818110610b3e575050508190602060e08195015173ffffffffffffffffffffffffffffffffffffffff81511661010485015201516101248301520381600073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1918215610b3257600092610afd575b60208380847f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff81519360043585526fffffffffffffffffffffffffffffffff60443516888601521692a3604051908152f35b91506020823d602011610b2a575b81610b186020938361131c565b81010312610103576020915191610a94565b3d9150610b0b565b6040513d6000823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff168186015288955060409094019390920191600101610a0e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6fffffffffffffffffffffffffffffffff610be97fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8293018661144d565b51926044350316818351160116905282806107df565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b9092610c6367ffffffffffffffff610c46868561144d565b5151166fffffffffffffffffffffffffffffffff60443516611490565b6fffffffffffffffffffffffffffffffff8111610d22576fffffffffffffffffffffffffffffffff8091169164ffffffffff6020610ca1888761144d565b5101511660405190610cb2826112e4565b8482526020820152610cc4878a61144d565b52610ccf868961144d565b5016016fffffffffffffffffffffffffffffffff8111610cf357926001019061077b565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b602490604051907f4916adce0000000000000000000000000000000000000000000000000000000082526004820152fd5b602090604051610d62816112e4565b6000815260008382015282828a0101520161076e565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161760035581610718565b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b813581526020918201910161068b565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b60446040517f442b184100000000000000000000000000000000000000000000000000000000815242600482015264ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166024820152fd5b346101035760006003193601126101035760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346101035760006003193601126101035760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b3461010357604060031936011261010357610f086112c1565b60248035906fffffffffffffffffffffffffffffffff82168092036101035773ffffffffffffffffffffffffffffffffffffffff80600054163381036111c4575064ffffffffff80600354168015158061117f575b80611170575b6111115750506040519160209260008084868401987fa9059cbb000000000000000000000000000000000000000000000000000000008a521697888585015287604485015260448452610fb584611300565b857f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15611105573d9067ffffffffffffffff82116110d757906110409160405191611030887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116018461131c565b82523d60008884013e5b8361155d565b80518581151591826110b6575b50509050611088575050907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f916000541692604051908152a3005b604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b8380929350010312610103578401518015908115036101035780858961104d565b837f4e487b710000000000000000000000000000000000000000000000000000000060005260416004526000fd5b6110409060609061103a565b6064925083604051927f92b666970000000000000000000000000000000000000000000000000000000084524260048501527f000000000000000000000000000000000000000000000000000000000000000016908301526044820152fd5b5061117961135d565b15610f63565b5062093a8081018281116111965782164211610f5d565b847f4e487b710000000000000000000000000000000000000000000000000000000060005260116004526000fd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346101035760006003193601126101035761040f907f0000000000000000000000000000000000000000000000000000000000000000602082015260208152610403816112e4565b60208082528251818301819052939260005b8581106112ad575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8460006040809697860101520116010190565b81810183015184820160400152820161126d565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361010357565b6040810190811067ffffffffffffffff821117610b7c57604052565b6080810190811067ffffffffffffffff821117610b7c57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610b7c57604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081611392575090565b905042101590565b67ffffffffffffffff8111610b7c5760051b60200190565b600454906113bf8261139a565b9160406113cf604051948561131c565b8184528360208091019160046000527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b906000935b85851061141357505050505050565b60018481928451611423816112e4565b64ffffffffff875467ffffffffffffffff81168352871c1683820152815201930194019391611404565b80518210156114615760209160051b010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8382098382029182808310920391808303921461154c57670de0b6b3a7640000908183101561151557947faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac1066994950990828211900360ee1b910360121c170290565b60449086604051917f5173648d00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5050670de0b6b3a764000090049150565b9061159c575080511561157257805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b815115806115f4575b6115ad575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b156115a556fea164736f6c6343000817000aa164736f6c6343000817000a"; + hex"608080604052346100165761400c908161001b8239f35b5f80fdfe60406080815260048036101562000014575f80fd5b5f3560e01c80631e323876146200022e5763769bed201462000034575f80fd5b346200022a5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126200022a5767ffffffffffffffff9080358281116200022a576200008890369083016200066a565b906024359073ffffffffffffffffffffffffffffffffffffffff908183168093036200022a57857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126200022a57620000e262000541565b9464ffffffffff60443581811681036200022a57875260643590811681036200022a57602087015286519161188c908184019284841090841117620001fe5750908291620008da8339608081526200016588620001436080840189620007f9565b9287602082015201886020908164ffffffffff91828151168552015116910152565b03905ff08015620001f45760209593620001db95937f2ba0fe49588281dbb122dd3b7f3e2b3396338f70dbe3c62bf3e3888b4ba7ffb893620001b99316968795875194859460c0865260c0860190620007f9565b928a850152878401906020908164ffffffffff91828151168552015116910152565b608435608083015260a43560a08301520390a251908152f35b85513d5f823e3d90fd5b6041907f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b5f80fd5b50346200022a5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126200022a5767ffffffffffffffff81358181116200022a576200028290369084016200066a565b9060249081359173ffffffffffffffffffffffffffffffffffffffff948584168094036200022a57604435968388116200022a57366023890112156200022a5787820135948486116200051657602098620002e28a8860051b016200058f565b96858b89838152019160061b830101913683116200022a579398938601905b828210620004cb575050505f965f925f985b88518a10156200038a5787806200032b8c8c62000759565b5151169116018781116200035f576001909464ffffffffff8d620003508d8d62000759565b51015116019901989362000313565b866011877f4e487b71000000000000000000000000000000000000000000000000000000005f52525ffd5b908b948b888a89818e9716670de0b6b3a764000081036200049e5750875192611e9a918285019385851090851117620004745750508291620003f39162002166843960608152620003df6060820188620007f9565b908a8c820152898183039101528762000888565b03905ff08015620001f45791620004557ffe44018cf74992b2720702385a1728bd329dd136e4f651203176c81c12710a8b9492620004439416978896885195869560c0875260c0870190620007f9565b918b8601528482038986015262000888565b906060830152606435608083015260843560a08301520390a251908152f35b6041907f4e487b71000000000000000000000000000000000000000000000000000000005f52525ffd5b88517f1b05170a000000000000000000000000000000000000000000000000000000008152918201529050fd5b89829a959a3603126200022a57620004e262000541565b9082359089821682036200022a57828e9288945262000503838601620005e2565b8382015281520191019098939862000301565b836041847f4e487b71000000000000000000000000000000000000000000000000000000005f52525ffd5b604051906040820182811067ffffffffffffffff8211176200056257604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f604051930116820182811067ffffffffffffffff8211176200056257604052565b359081151582036200022a57565b359064ffffffffff821682036200022a57565b81601f820112156200022a5780359067ffffffffffffffff821162000562576200064760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f850116016200058f565b92828452602083830101116200022a57815f926020809301838601378301015290565b9190916101009081818503126200022a5760405191820167ffffffffffffffff908381108282111762000562576040528294823573ffffffffffffffffffffffffffffffffffffffff9081811681036200022a578552620006ce60208501620005d4565b6020860152620006e160408501620005e2565b6040860152606084013590811681036200022a57606085015260808301358281116200022a578162000715918501620005f5565b608085015260a083013560a085015260c08301359182116200022a57826200074860e094926200075494869401620005f5565b60c086015201620005d4565b910152565b80518210156200076e5760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b91908251928382525f5b848110620007e45750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6020809697860101520116010190565b602081830181015184830182015201620007a5565b9060e0806200087d6200086061010073ffffffffffffffffffffffffffffffffffffffff80885116875260208801511515602088015264ffffffffff604089015116604088015260608801511660608701526080870151908060808801528601906200079b565b60a086015160a086015260c086015185820360c08701526200079b565b930151151591015290565b9081518082526020808093019301915f5b828110620008a8575050505090565b8351805167ffffffffffffffff16865282015164ffffffffff1685830152604090940193928101926001016200089956fe61016080604052346200046c576200188c8038038091620000218285620005b5565b8339810190808203608081126200046c5781516001600160401b0381116200046c578201610100818503126200046c5760405161010081016001600160401b03811182821017620005665760405281516001600160a01b03811681036200046c5781526200009260208301620005d9565b9060208101918252620000a860408401620005e7565b604082019081526060840151909690936001600160a01b03851685036200046c576060830194855260808101516001600160401b0381116200046c5782620000f291830162000639565b906080840191825260a08101519260a0850193845260c08201519160018060401b0383116200046c576200012f60e0926200013b94830162000639565b60c087015201620005d9565b60e084019081526020880151969094906001600160a01b03881688036200046c57604090603f1901126200046c5760408051989089016001600160401b0381118a8210176200056657620001a6916060916040526200019d60408201620005e7565b8b5201620005e7565b956020890196875260c085015151602081116200057a5750515f80546001600160a01b0319166001600160a01b0392831617905584511660805251151560a052965164ffffffffff90811660c052965180516001600160401b038111620005665760019182548381811c911680156200055b575b60208210146200054757601f8111620004fa575b50602090601f83116001146200048e5760c095949392915f918362000482575b50505f19600383901b1c191690821b1790555b5160e0520151604051620002956020828162000287818301968781519384920162000616565b8101038084520182620005b5565b519051906020811062000470575b50610100525115159361012094855261014093838552511669ffffffffff0000000000600454925160281b169160018060501b031916171760045560018060a01b03608051166040519060208201925f8063095ea7b360e01b9586815260018060a01b03841660248701528119604487015260448652620003248662000599565b85519082865af16200033562000686565b816200042f575b508062000424575b15620003d8575b6040516110e690868883620007a6843960805183818161048a015281816107310152610c60015260a0518381816107580152610b74015260c05183818161015f01528181610acc01528181610de90152611004015260e0518381816102de015261064a01526101005183610ecb0152518281816107800152610b380152518181816101af01526108f20152f35b6200041a93620004149160405191602083015260018060a01b031660248201525f6044820152604481526200040d8162000599565b82620006ba565b620006ba565b5f8080806200034b565b50813b151562000344565b805180159250821562000446575b50505f6200033c565b81925090602091810103126200046c576020620004649101620005d9565b5f806200043d565b5f80fd5b5f199060200360031b1b165f620002a3565b015190505f806200024e565b90601f19831691845f5260205f20925f5b818110620004e3575091859492918360c0999897959310620004ca575b505050811b01905562000261565b01515f1960f88460031b161c191690555f8080620004bc565b82840151855593860193602093840193016200049f565b835f5260205f20601f840160051c810191602085106200053c575b601f0160051c019084905b828110620005305750506200022e565b5f815501849062000520565b909150819062000515565b634e487b7160e01b5f52602260045260245ffd5b90607f16906200021a565b634e487b7160e01b5f52604160045260245ffd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b608081019081106001600160401b038211176200056657604052565b601f909101601f19168101906001600160401b038211908210176200056657604052565b519081151582036200046c57565b519064ffffffffff821682036200046c57565b6001600160401b0381116200056657601f01601f191660200190565b5f5b838110620006285750505f910152565b818101518382015260200162000618565b81601f820112156200046c5780516200065281620005fa565b92620006626040519485620005b5565b818452602082840101116200046c5762000683916020808501910162000616565b90565b3d15620006b5573d906200069a82620005fa565b91620006aa6040519384620005b5565b82523d5f602084013e565b606090565b5f80620006e69260018060a01b03169360208151910182865af1620006de62000686565b90836200073d565b805190811515918262000716575b5050620006fe5750565b60249060405190635274afe760e01b82526004820152fd5b81925090602091810103126200046c576020620007349101620005d9565b155f80620006f4565b906200076657508051156200075457805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806200079b575b62000779575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156200077056fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde0314610eb5575080631686c90914610b9957806316c3549d14610b5d5780631bfd681414610b215780633bfe03a814610af35780633f31ae3f146104ae5780634800d97f1461045e57806349fc73dd146103255780634e390d3e1461030157806351e75e8b146102c757806375829def146101ed57806390e64d13146101d35780639e93e57714610183578063bb4b573414610142578063ce516507146101025763f851a440146100cc575f80fd5b346100fe575f6003193601126100fe57602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b5f80fd5b346100fe5760206003193601126100fe57602061013860043560ff6001918060081c5f526002602052161b60405f205416151590565b6040519015158152f35b346100fe575f6003193601126100fe57602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe576020610138610ffc565b346100fe5760206003193601126100fe57610206610f60565b5f5473ffffffffffffffffffffffffffffffffffffffff8082169233840361027a577fffffffffffffffffffffffff000000000000000000000000000000000000000093501691829116175f55337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf805f80a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100fe575f6003193601126100fe5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100fe575f6003193601126100fe57602064ffffffffff60035416604051908152f35b346100fe575f6003193601126100fe576040515f9060018054908160011c9060018316928315610454575b6020938484108114610427578386529081156103e9575060011461038f575b61038b8461037f81880382610fbb565b60405191829182610efc565b0390f35b60015f9081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8284106103d6575050508161038b9361037f928201019361036f565b80548585018701529285019281016103ba565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b820101915061037f8161038b61036f565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610350565b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe5760806003193601126100fe5760243573ffffffffffffffffffffffffffffffffffffffff811681036100fe57604435906fffffffffffffffffffffffffffffffff821682036100fe576064359167ffffffffffffffff908184116100fe57366023850112156100fe578360040135908282116100fe576005918060051b95602487820101903682116100fe576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff891660408201526fffffffffffffffffffffffffffffffff861660608201526060815261058d81610f9f565b51902060405160208101918252602081526105a781610f83565b519020926105b3610ffc565b610a96576105da60043560ff6001918060081c5f526002602052161b60405f205416151590565b610a64576105ee60206040519a018a610fbb565b8852602401602088015b828210610a5457505050925f935b86518510156106465784841b8701602001519081811015610635575f52602052600160405f205b940193610606565b905f52602052600160405f2061062d565b85907f000000000000000000000000000000000000000000000000000000000000000003610a2a5760035464ffffffffff8116156109f6575b5060043560081c5f52600260205260405f20600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff5f5416604051906106c482610f83565b5f8083526020830152604051936101008501908111858210176109c957604052835273ffffffffffffffffffffffffffffffffffffffff821660208401526fffffffffffffffffffffffffffffffff8416604084015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608401527f0000000000000000000000000000000000000000000000000000000000000000151560808401527f0000000000000000000000000000000000000000000000000000000000000000151560a08401526040516107b281610f83565b64ffffffffff600454818116835260281c16602082015260c084015260e0830152602060e0604051937fab167ccc00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff815116600486015273ffffffffffffffffffffffffffffffffffffffff838201511660248601526fffffffffffffffffffffffffffffffff604082015116604486015273ffffffffffffffffffffffffffffffffffffffff606082015116606486015260808101511515608486015260a0810151151560a486015264ffffffffff8360c08301518281511660c489015201511660e4860152015173ffffffffffffffffffffffffffffffffffffffff8151166101048501520151610124830152602082610144815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af19182156109be575f92610989575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d6020116109b6575b816109a460209383610fbb565b810103126100fe579051906020610922565b3d9150610997565b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff16176003558361067f565b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b81358152602091820191016105f8565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b60446040517f442b184100000000000000000000000000000000000000000000000000000000815242600482015264ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166024820152fd5b346100fe575f6003193601126100fe57604060045464ffffffffff825191818116835260281c166020820152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe5760406003193601126100fe57610bb2610f60565b60248035906fffffffffffffffffffffffffffffffff82168092036100fe5773ffffffffffffffffffffffffffffffffffffffff805f5416338103610e66575064ffffffffff806003541680151580610e23575b80610e14575b610db5575050604051916020925f8084868401987fa9059cbb000000000000000000000000000000000000000000000000000000008a521697888585015287604485015260448452610c5d84610f9f565b857f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610da9573d9067ffffffffffffffff8211610d7d5790610ce79160405191610cd8887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610fbb565b82523d5f8884013e5b83611039565b8051858115159182610d5c575b50509050610d2e575050907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f915f541692604051908152a3005b604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100fe578401518015908115036100fe57808589610cf4565b837f4e487b71000000000000000000000000000000000000000000000000000000005f5260416004525ffd5b610ce790606090610ce1565b6064925083604051927f92b666970000000000000000000000000000000000000000000000000000000084524260048501527f000000000000000000000000000000000000000000000000000000000000000016908301526044820152fd5b50610e1d610ffc565b15610c0c565b5062093a808101828111610e3a5782164211610c06565b847f4e487b71000000000000000000000000000000000000000000000000000000005f5260116004525ffd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100fe575f6003193601126100fe5761038b907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261037f81610f83565b6020808252825181830181905293925f5b858110610f4c575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b818101830151848201604001528201610f0d565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100fe57565b6040810190811067ffffffffffffffff8211176109c957604052565b6080810190811067ffffffffffffffff8211176109c957604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176109c957604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081611031575090565b905042101590565b90611078575080511561104e57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b815115806110d0575b611089575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b1561108156fea164736f6c6343000817000a6101608060405234620004925762001e9a908138038092620000228284620006f6565b82396060818381010312620004925780516001600160401b038111620004925781019161010092838183850103126200049257604051908482016001600160401b03811183821017620006415760405280516001600160a01b03811681036200049257825262000095602082016200071a565b6020830152620000a86040820162000728565b604083015260608101516001600160a01b03811681036200049257606083015260808101516001600160401b0381116200049257620000ed908486019083016200077a565b608083015260a0818101519083015260c08101516001600160401b038111620004925762000134916200012860e0928688019083016200077a565b60c0850152016200071a565b60e08201526020830151916001600160a01b038316830362000492576040840151906001600160401b0382116200049257808501601f838701011215620004925784820151906001600160401b0382116200064157604051956200019f60208460051b0188620006f6565b828752602087019382820160208560061b838501010111620004925793602085830101945b60208560061b82850101018610620006745750505050505060c0810151516020811162000655575060608101515f80546001600160a01b0319166001600160a01b0392831617905581511660809081526020820151151560a052604082015164ffffffffff1660c05281015180519091906001600160401b038111620006415760019283548481811c9116801562000636575b60208210146200062257601f8111620005d5575b50602090601f83116001146200056c5760e09392915f918362000560575b50505f19600383901b1c191690841b1783555b60a0810151825260c0810151604051620002d660208281620002c8818301968781519384920162000757565b8101038084520182620006f6565b51905190602081106200054e575b50865201511515926101209384526101409283528051905f5b82811062000496575050505060018060a01b036080511660018060a01b03825116906040519060208201925f8063095ea7b360e01b9586815283602487015281196044870152604486526200035286620006da565b85519082865af162000363620007c7565b8162000455575b50806200044a575b1562000406575b50505050604051916115b39384620008e785396080518481816104fa015281816108700152610f6e015260a0518481816108970152610e82015260c05184818161021f01528181610e08015281816110f70152611312015260e05184818161034e01526106cb015251836111d90152518281816108bf0152610e4601525181818161012e0152610a270152f35b62000440936200043a9160405191602083015260248201525f6044820152604481526200043381620006da565b82620007fb565b620007fb565b5f80808062000379565b50813b151562000372565b80518015925082156200046c575b50505f6200036a565b8192509060209181010312620004925760206200048a91016200071a565b5f8062000463565b5f80fd5b81518110156200053a5760208160051b830101516004805490680100000000000000008210156200052757868201808255821015620005145790869392915f5260205f20019060018060401b038151166cffffffffff00000000000000006020845493015160401b169160018060681b0319161717905501620002fd565b603290634e487b7160e01b5f525260245ffd5b604190634e487b7160e01b5f525260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f199060200360031b1b165f620002e4565b015190505f8062000289565b92918491601f19821690835f5260205f20915f5b818110620005bc5750958360e09710620005a3575b505050811b0183556200029c565b01515f1960f88460031b161c191690555f808062000595565b8288015184558895909301926020928301920162000580565b845f5260205f20601f840160051c8101916020851062000617575b601f0160051c019085905b8281106200060b5750506200026b565b5f8155018590620005fb565b9091508190620005f0565b634e487b7160e01b5f52602260045260245ffd5b90607f169062000257565b634e487b7160e01b5f52604160045260245ffd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b60408685850103126200049257604080519081016001600160401b03811182821017620006415760405286516001600160401b0381168103620004925760209382859260409452620006c8838b0162000728565b838201528152019601959150620001c4565b608081019081106001600160401b038211176200064157604052565b601f909101601f19168101906001600160401b038211908210176200064157604052565b519081151582036200049257565b519064ffffffffff821682036200049257565b6001600160401b0381116200064157601f01601f191660200190565b5f5b838110620007695750505f910152565b818101518382015260200162000759565b81601f820112156200049257805162000793816200073b565b92620007a36040519485620006f6565b818452602082840101116200049257620007c4916020808501910162000757565b90565b3d15620007f6573d90620007db826200073b565b91620007eb6040519384620006f6565b82523d5f602084013e565b606090565b5f80620008279260018060a01b03169360208151910182865af16200081f620007c7565b90836200087e565b805190811515918262000857575b50506200083f5750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620004925760206200087591016200071a565b155f8062000835565b90620008a757508051156200089557805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580620008dc575b620008ba575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15620008b156fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde03146111c3575080631686c90914610ea757806316c3549d14610e6b5780631bfd681414610e2f5780633f31ae3f1461051e5780634800d97f146104ce57806349fc73dd146103955780634e390d3e1461037157806351e75e8b1461033757806375829def1461025d57806390e64d1314610243578063bb4b573414610202578063bf4ed03f14610192578063ce51650714610152578063da792468146101025763f851a440146100cc575f80fd5b346100fe575f6003193601126100fe57602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b5f80fd5b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe5760206003193601126100fe57602061018860043560ff6001918060081c5f526002602052161b60405f205416151590565b6040519015158152f35b346100fe575f6003193601126100fe576101aa61135f565b604051602091828201838352815180915283604084019201935f5b8281106101d25784840385f35b8551805167ffffffffffffffff16855282015164ffffffffff1684830152948101946040909301926001016101c5565b346100fe575f6003193601126100fe57602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe57602061018861130a565b346100fe5760206003193601126100fe5761027661126e565b5f5473ffffffffffffffffffffffffffffffffffffffff808216923384036102ea577fffffffffffffffffffffffff000000000000000000000000000000000000000093501691829116175f55337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf805f80a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100fe575f6003193601126100fe5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100fe575f6003193601126100fe57602064ffffffffff60035416604051908152f35b346100fe575f6003193601126100fe576040515f9060018054908160011c90600183169283156104c4575b60209384841081146104975783865290811561045957506001146103ff575b6103fb846103ef818803826112c9565b6040519182918261120a565b0390f35b60015f9081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b82841061044657505050816103fb936103ef92820101936103df565b805485850187015292850192810161042a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103ef816103fb6103df565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f16916103c0565b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe5760806003193601126100fe5760243573ffffffffffffffffffffffffffffffffffffffff811681036100fe576fffffffffffffffffffffffffffffffff60443516604435036100fe5767ffffffffffffffff606435116100fe573660236064350112156100fe57606435600401359067ffffffffffffffff82116100fe5760248260051b60643501013681116100fe576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff841660408201526fffffffffffffffffffffffffffffffff60443516606082015260608152610603816112ad565b519020604051602081019182526020815261061d81611291565b5190209061062961130a565b610dd25761065060043560ff6001918060081c5f526002602052161b60405f205416151590565b610da05761065d84611347565b9361066b60405195866112c9565b8452606435602401602085015b828210610d90575050505f905b83518210156106c75761069882856113f8565b5190818110156106b6575f52602052600160405f205b910190610685565b905f52602052600160405f206106ae565b90507f000000000000000000000000000000000000000000000000000000000000000003610d665760035464ffffffffff811615610d32575b5061070961135f565b905f825161071681611347565b9361072460405195866112c9565b8185527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061075183611347565b015f5b818110610d0f5750505f905b828210610bec5750506fffffffffffffffffffffffffffffffff82166fffffffffffffffffffffffffffffffff604435168111610bbf576fffffffffffffffffffffffffffffffff6044351611610b6b575b505060043560081c5f52600260205260405f20600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff5f541691604051906107f982611291565b5f82525f602083015260405193610100850185811067ffffffffffffffff821117610b3e57604052845273ffffffffffffffffffffffffffffffffffffffff831660208501526fffffffffffffffffffffffffffffffff60443516604085015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608501527f0000000000000000000000000000000000000000000000000000000000000000151560808501527f0000000000000000000000000000000000000000000000000000000000000000151560a085015260c084015260e0830152604051917f897f362b0000000000000000000000000000000000000000000000000000000083526020600484015282610144810173ffffffffffffffffffffffffffffffffffffffff835116602483015273ffffffffffffffffffffffffffffffffffffffff60208401511660448301526fffffffffffffffffffffffffffffffff604084015116606483015273ffffffffffffffffffffffffffffffffffffffff60608401511660848301526080830151151560a483015260a0830151151560c483015260c08301519061012060e48401528151809152602061016484019201905f5b818110610b00575050508190602060e08195015173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015203815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1918215610af5575f92610ac0575b60208380847f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff81519360043585526fffffffffffffffffffffffffffffffff60443516888601521692a3604051908152f35b91506020823d602011610aed575b81610adb602093836112c9565b810103126100fe576020915191610a57565b3d9150610ace565b6040513d5f823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff1681860152889550604090940193909201916001016109d3565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6fffffffffffffffffffffffffffffffff610ba97fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff829301866113f8565b51926044350316818351160116905282806107b2565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b9092610c2167ffffffffffffffff610c0486856113f8565b5151166fffffffffffffffffffffffffffffffff60443516611439565b6fffffffffffffffffffffffffffffffff8111610cde576fffffffffffffffffffffffffffffffff8091169164ffffffffff6020610c5f88876113f8565b5101511660405190610c7082611291565b8482526020820152610c82878a6113f8565b52610c8d86896113f8565b5016016fffffffffffffffffffffffffffffffff8111610cb1579260010190610760565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b602490604051907f4916adce0000000000000000000000000000000000000000000000000000000082526004820152fd5b602090604051610d1e81611291565b5f81525f8382015282828a01015201610754565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161760035581610700565b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b8135815260209182019101610678565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b60446040517f442b184100000000000000000000000000000000000000000000000000000000815242600482015264ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166024820152fd5b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe5760406003193601126100fe57610ec061126e565b60248035906fffffffffffffffffffffffffffffffff82168092036100fe5773ffffffffffffffffffffffffffffffffffffffff805f5416338103611174575064ffffffffff806003541680151580611131575b80611122575b6110c3575050604051916020925f8084868401987fa9059cbb000000000000000000000000000000000000000000000000000000008a521697888585015287604485015260448452610f6b846112ad565b857f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d156110b7573d9067ffffffffffffffff821161108b5790610ff59160405191610fe6887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846112c9565b82523d5f8884013e5b83611506565b805185811515918261106a575b5050905061103c575050907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f915f541692604051908152a3005b604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100fe578401518015908115036100fe57808589611002565b837f4e487b71000000000000000000000000000000000000000000000000000000005f5260416004525ffd5b610ff590606090610fef565b6064925083604051927f92b666970000000000000000000000000000000000000000000000000000000084524260048501527f000000000000000000000000000000000000000000000000000000000000000016908301526044820152fd5b5061112b61130a565b15610f1a565b5062093a8081018281116111485782164211610f14565b847f4e487b71000000000000000000000000000000000000000000000000000000005f5260116004525ffd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100fe575f6003193601126100fe576103fb907f00000000000000000000000000000000000000000000000000000000000000006020820152602081526103ef81611291565b6020808252825181830181905293925f5b85811061125a575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b81810183015184820160400152820161121b565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100fe57565b6040810190811067ffffffffffffffff821117610b3e57604052565b6080810190811067ffffffffffffffff821117610b3e57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610b3e57604052565b64ffffffffff7f000000000000000000000000000000000000000000000000000000000000000016801515908161133f575090565b905042101590565b67ffffffffffffffff8111610b3e5760051b60200190565b6004549061136c82611347565b91604061137c60405194856112c9565b8184528360208091019160045f527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b905f935b8585106113be57505050505050565b600184819284516113ce81611291565b64ffffffffff875467ffffffffffffffff81168352871c16838201528152019301940193916113af565b805182101561140c5760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff838209838202918280831092039180830392146114f557670de0b6b3a764000090818310156114be57947faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac1066994950990828211900360ee1b910360121c170290565b60449086604051917f5173648d00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5050670de0b6b3a764000090049150565b90611545575080511561151b57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b8151158061159d575b611556575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b1561154e56fea164736f6c6343000817000aa164736f6c6343000817000a"; /*////////////////////////////////////////////////////////////////////////// DEPLOYERS From 9234368e53d26c6a02deeb438be4f13b623b8289 Mon Sep 17 00:00:00 2001 From: andreivladbrg Date: Thu, 16 May 2024 14:40:01 +0300 Subject: [PATCH 49/61] build: update bun.lockb file --- bun.lockb | Bin 44194 -> 43610 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bun.lockb b/bun.lockb index bad2ad29af3fec13f59d9c322ee29d2c4b40ab9f..7942f4481a178c9e5a4d41a05e489b5e03464eb8 100755 GIT binary patch delta 966 zcmYk*PiT^H9LI4VEw^MDJLIJ@*O)PA&(zwJ{c~2!S}R@U9)mzkhKvqI;G;7^YJt)M zS^I%U4k~{l53;{j(3XudkQ(%?yZ@yVC8ocf-@d!@!HpbsawGY3QJSDDiS}!=Si# z$Oh#{pZ9>WG18kBMr8xDjm-`87CD`|N9B5dHYf(z*4P}UV|G5&Gs}nmU)YHS$2cwI z#%ZAi1qaOq*oGb_l_KmwpNmQfcA@_$l`>SJc#KK~8YgHD!zwfjRAR6OEytkZ)Qz^m@^qrwnf?eqMP$@$dij!0-(CDQ(46D$5 zmP!oPpyeEuIIKhKc`8ZRfc7aWS=fa91u8k%f`X4q0k)y%B9$WSK;I=QCD?`jX)0x? zLh&+{3N+5p9EMeBzCtAiYtV9)N*vaq)lVe}8_*u0l7&sk&r-?378I^gDZn=LT&Gfm z9q7A3r3AasAEZ)-YB0oBZe^4Q;+KB*O>(jCl9Sz!tSae=1dJMv+4bMlbqI4IM0?cc0=!=JkPw^ZIbg^ mm-DRp*lYUu@A!6ob=hXrt`ytyAlEk^op8-ZU5fk3ouR*N@*KGU delta 1105 zcmY+@T}V@59LMoBkcrLdo$Fmi&h3A>3szcTT<5e9S|Lt>JA$x6UmN> z`Xm7j`)KyVBsAHmgkTCEUOeF+U&{{_&4AanYgh~Wvpvy&N0egVs4i2+g-%hDBuw9{FlOMWi z3i+KK&hO8SvQW?@%?4woY;Z_WXGilrJIbz)H0&{s<(KNecV1oRbGnRnQ)Y9_EX1>^YrKaa{wN3hd`$okd^p8Tq From 58a14466dcb98f6a274b44c29161737c4a8f9f0f Mon Sep 17 00:00:00 2001 From: Paul Razvan Berg Date: Sun, 2 Jun 2024 10:20:25 +0100 Subject: [PATCH 50/61] build: update bun lockfile --- bun.lockb | Bin 43610 -> 44222 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bun.lockb b/bun.lockb index 7942f4481a178c9e5a4d41a05e489b5e03464eb8..74692f5b8d3d5aa8b5962e7b90477d08325a9a96 100755 GIT binary patch delta 1102 zcmY+@T}V@59LMo&=e#<`m?Rnk!*UPu1Vpzngn_YM@@Mbr`>imC;umk7A|M@Q z0ZY(xj!F`iq4zwM6s$m@gUTwb!XX!xb*Sm2S%ev=cT@4hEHqxA5`;Nu>Y^gSJhWb< z5`zWk?4}ZjMd-RjB>_v&MntFQ`(`l+l# z%^jLWn1OnMiXUd7aezt?=AdbiiUjk}dY4KJ7NGMUl{hRy*L^ApSc0Ah{~gs39o7A3 zSE9fKF)AA#e$%oa2W-lsjb2$0KOJKOVk<2U@{gq2EwqK%nYkVonA_G3hij=hB6o$} z^SqALhg)=mky@RPvaZl6c{qGRW&Eu?bkkJIn~v7@*Opi;VwKa8%waaaAm}PfwO(0b zPZxVnTb64}>)+e2>khd+mNv`m)WYPn->yt!fvG3KX}?b1S=?=EoE;kvOij)>0%PN| X4yoNSF+DTs=yX4JPjs-YH!uDG=V3h$ delta 925 zcmYk*J!}$T9LI4E6x)kp9Ar|Ynqo}s6{?=~?TT;b`?V?slO}{XkWNgz6iJ9WK)-f{8E zy-l2O&tyMr?0T%leWu6yeIR<=+MS(D>t{@l2+pk@oGeN%`>-ud{^Ce{S$vsWm0TjE zBqo-X#QzKXxe%VmLS-Hc1FD8G*XWRH5tKR|QGEfWLC4fMhti}rjh{zpQSSxJN!q49 z6(vnO)PE5rL%TGvh?1v08oGp1rhTehMyb#NRj;7b=#XkxQR;L=^=l{%I;Mt((xf(x zFQK%kH;Oq)+tha*B~3fje*+~$yEJeUB~N=abPJ_S`&7A&QlSH?>L@ijq}m;nIvr8{ zE=q%rsc{dbNo^XxkJ6&v2bhzzO??kh(zHYUk5DqSO9KW Date: Sun, 2 Jun 2024 14:27:10 +0100 Subject: [PATCH 51/61] docs: update security assumptions (#349) * docs: update security assumptions * build: update bun --------- Co-authored-by: smol-ninja --- SECURITY.md | 4 ++-- bun.lockb | Bin 44222 -> 42913 bytes 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SECURITY.md b/SECURITY.md index dde5ba31..f751d39a 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -31,8 +31,8 @@ The Program does NOT cover the following: Vulnerabilities contingent upon the occurrence of any of the following also are outside the scope of this Program: -- Front-end bugs -- DDOS attacks +- Front-end bugs (clickjacking etc.) +- DDoS attacks - Spamming - Phishing - Social engineering attacks diff --git a/bun.lockb b/bun.lockb index 74692f5b8d3d5aa8b5962e7b90477d08325a9a96..fad47eb58fbeb278602ece9c7f36d90026cd8d97 100755 GIT binary patch delta 5721 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 delta 6246 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 y`<2-ER&5CAaM| Date: Fri, 14 Jun 2024 20:57:47 +0100 Subject: [PATCH 52/61] build: bump deps (#351) * build: bump solc * build: bump deps * chore: update bun lockfile * build: bump core * chore: update precompiles --------- Co-authored-by: andreivladbrg --- .github/workflows/multibuild.yml | 2 +- bun.lockb | Bin 42913 -> 42555 bytes foundry.toml | 2 +- package.json | 10 +++++----- precompiles/Precompiles.sol | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/multibuild.yml b/.github/workflows/multibuild.yml index e94a4c33..7bee0f5c 100644 --- a/.github/workflows/multibuild.yml +++ b/.github/workflows/multibuild.yml @@ -22,5 +22,5 @@ jobs: uses: "PaulRBerg/foundry-multibuild@v1" with: min: "0.8.22" - max: "0.8.23" + max: "0.8.26" skip-test: "true" diff --git a/bun.lockb b/bun.lockb index fad47eb58fbeb278602ece9c7f36d90026cd8d97..7ca32db28539985a3b3e17ad2c81737a98b09e38 100755 GIT binary patch delta 6310 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 Kx(vshU;P_ELfc9J delta 6467 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 diff --git a/foundry.toml b/foundry.toml index 4c769e00..dc2ea62f 100644 --- a/foundry.toml +++ b/foundry.toml @@ -18,7 +18,7 @@ out = "out" script = "script" sender = "0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38" - solc = "0.8.23" + solc = "0.8.26" src = "src" test = "test" diff --git a/package.json b/package.json index 1ab85d93..2c5d6595 100644 --- a/package.json +++ b/package.json @@ -11,14 +11,14 @@ }, "dependencies": { "@openzeppelin/contracts": "5.0.2", - "@prb/math": "github:PaulRBerg/prb-math#16419e5686504e15f973ebe73c3a75bd618d6ca4", + "@prb/math": "4.0.3", "@sablier/v2-core": "github:sablier-labs/v2-core#staging" }, "devDependencies": { - "forge-std": "github:foundry-rs/forge-std#v1.8.1", - "prettier": "^2.8.8", - "solady": "0.0.129", - "solhint": "^4.0.0" + "forge-std": "github:foundry-rs/forge-std#v1.8.2", + "prettier": "^3.3.2", + "solady": "0.0.208", + "solhint": "^5.0.1" }, "files": [ "artifacts", diff --git a/precompiles/Precompiles.sol b/precompiles/Precompiles.sol index 23a20fe0..025a9abc 100644 --- a/precompiles/Precompiles.sol +++ b/precompiles/Precompiles.sol @@ -17,9 +17,9 @@ contract Precompiles { //////////////////////////////////////////////////////////////////////////*/ bytes public constant BYTECODE_BATCH_LOCKUP = - hex"6080806040523461001657611e91908161001b8239f35b5f80fdfe6080806040526004361015610012575f80fd5b5f3560e01c90816337266dd3146111ed5750806349a32c4014610e9a578063606ef87514610b545780639e743f29146107e9578063a514f83e146104205763f7ca34eb1461005e575f80fd5b3461034e5761006c36611562565b9283156103f6575f905f5b8581106103c257506001600160a01b036100949116918483611b08565b61009d846117df565b925f5b8581106100b957604051806100b587826115f0565b0390f35b6100c4818786611aa5565b6100cd9061182e565b906100d9818887611aa5565b6020016100e59061182e565b85886100f2848284611aa5565b6040016100fe906116ae565b610109858385611aa5565b60600161011590611842565b610120868486611aa5565b60800161012c90611842565b90610138878587611aa5565b60a00161014490611ae5565b9287610151818789611aa5565b60c0810161015e916119b1565b9661016a929198611aa5565b60e001966040519961017b8b611731565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c086015236906101df92611a05565b60e0840152366101ee91611953565b610100830152604051917f31df3d48000000000000000000000000000000000000000000000000000000008352600483016020905282610164810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c083015164ffffffffff1660e483015260e083015190610104830161014090528151809152610184830191602001905f905b8082106103655750505081906102fb6101006020950151610124840190602080916001600160a01b0381511684520151910152565b03815f885af1801561035a575f90610323575b6001925061031c828861198c565b52016100a0565b506020823d602011610352575b8161033d60209383611786565b8101031261034e576001915161030e565b5f80fd5b3d9150610330565b6040513d5f823e3d90fd5b919493509160206060826103b3600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b019501920186939492916102c6565b916001906fffffffffffffffffffffffffffffffff6103ed60406103e7878b8a611aa5565b016116ae565b16019201610077565b60046040517ff8bf106c000000000000000000000000000000000000000000000000000000008152fd5b3461034e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261034e5761045761162b565b61045f61154c565b604480359267ffffffffffffffff80851161034e573660238601121561034e5784600401351161034e576024913660246101408760040135028701011161034e578460040135156103f6575f915f5b866004013581106107b457506001600160a01b036104cf9116928584611b08565b6104dc85600401356117df565b935f5b866004013581106104f857604051806100b588826115f0565b61051261050d82896004013560248b01611af7565b61182e565b90610530602061052a838b6004013560248d01611af7565b0161182e565b8861054860406103e785846004013560248601611af7565b610565606061055f86856004013560248701611af7565b01611842565b6fffffffffffffffffffffffffffffffff61058d608061055f88876004013560248901611af7565b926001600160a01b036101006105be896105af818a6004013560248c01611af7565b98602481600401359101611af7565b0196816040519a6105ce8c6116cb565b168a521660208901521660408701526001600160a01b038716606087015215156080860152151560a085015260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60823603011261034e57610677610754926040519261063a8461174e565b61064660a082016118c0565b845261066660e060c09261065b8482016118c0565b6020880152016118c0565b604085015286019283523690611953565b9060e0850191825260a0604051957f53b157270000000000000000000000000000000000000000000000000000000087526001600160a01b0381511660048801526001600160a01b036020820151168b8801526fffffffffffffffffffffffffffffffff604082015116898801526001600160a01b0360608201511660648801526080810151151560848801520151151560a486015251604064ffffffffff918281511660c48801528260208201511660e488015201511661010485015251610124840190602080916001600160a01b0381511684520151910152565b602082610164815f895af1801561035a575f90610781575b6001925061077a828961198c565b52016104df565b506020823d6020116107ac575b8161079b60209383611786565b8101031261034e576001915161076c565b3d915061078e565b926001906fffffffffffffffffffffffffffffffff6107e060406103e78860048d013560248e01611af7565b160193016104ae565b3461034e576107f736611562565b9283156103f6575f905f5b858110610b2657506001600160a01b0361081f9116918483611b08565b610828846117df565b925f5b85811061084057604051806100b587826115f0565b61084b818786611aa5565b6108549061182e565b90610860818887611aa5565b60200161086c9061182e565b8588610879848284611aa5565b604001610885906116ae565b610890858385611aa5565b60600161089c90611842565b6108a7868486611aa5565b6080016108b390611842565b906108bf878587611aa5565b60a0016108cb90611ae5565b92876108d8818789611aa5565b60c081016108e59161184f565b966108f1929198611aa5565b60e00196604051996109028b611731565b6001600160a01b03168a526001600160a01b031660208a01526fffffffffffffffffffffffffffffffff1660408901526001600160a01b038916606089015215156080880152151560a087015264ffffffffff1660c08601523690610966926118d2565b60e08401523661097591611953565b610100830152604051917f32fbe22b000000000000000000000000000000000000000000000000000000008352600483016020905282610164810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c083015164ffffffffff1660e483015260e083015190610104830161014090528151809152610184830191602001905f905b808210610add575050508190610a826101006020950151610124840190602080916001600160a01b0381511684520151910152565b03815f885af1801561035a575f90610aaa575b60019250610aa3828861198c565b520161082b565b506020823d602011610ad5575b81610ac460209383611786565b8101031261034e5760019151610a95565b3d9150610ab7565b91949350916020604082610b17600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291610a4d565b916001906fffffffffffffffffffffffffffffffff610b4b60406103e7878b8a611aa5565b16019201610802565b3461034e57610b6236611562565b9283156103f6575f905f5b858110610e6c57506001600160a01b03610b8a9116918483611b08565b610b93846117df565b925f5b858110610bab57604051806100b587826115f0565b610bb6818786611641565b610bbf9061182e565b90610bcb818887611641565b602001610bd79061182e565b8782610be481838a611641565b604001610bf0906116ae565b610bfb82848b611641565b606001610c0790611842565b610c1283858c611641565b608001610c1e90611842565b91610c2a84868d611641565b60a08101610c37916119b1565b94610c4391968d611641565b60c0019560405198610c548a6116cb565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a08601523690610cac92611a05565b60c084015236610cbb91611953565b60e0830152604051917f54c02292000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001905f905b808210610e0f575050508190610db460e06020950151610104840190602080916001600160a01b0381511684520151910152565b03815f885af1801561035a575f90610ddc575b60019250610dd5828861198c565b5201610b96565b506020823d602011610e07575b81610df660209383611786565b8101031261034e5760019151610dc7565b3d9150610de9565b91949350916020606082610e5d600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b01950192018693949291610d80565b916001906fffffffffffffffffffffffffffffffff610e9160406103e7878b8a611641565b16019201610b6d565b3461034e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261034e57610ed161162b565b610ed961154c565b604480359267ffffffffffffffff9384811161034e573660238201121561034e57806004013594851161034e57602481019060243691610120880201011161034e5784156103f6575f915f5b8681106111bf57506001600160a01b03610f429116928584611b08565b610f4b856117df565b935f5b868110610f6357604051806100b588826115f0565b610f7161050d8289866119a0565b90610f82602061052a838b886119a0565b8489610f9460406103e78684866119a0565b6fffffffffffffffffffffffffffffffff610fb5606061055f8886886119a0565b916001600160a01b0360e0610fe689610fd4608061055f838b8d6119a0565b97610fe082828c6119a0565b996119a0565b0196816040519a610ff68c6116cb565b168a521660208901521660408701526001600160a01b038716606087015215156080860152151560a085015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60823603011261034e5761108e61115f926040519261106284611715565b61106e60a082016118c0565b845261107d60c08092016118c0565b602085015286019283523690611953565b9060e0850191825260a0604051957fab167ccc0000000000000000000000000000000000000000000000000000000087526001600160a01b0381511660048801526001600160a01b0360208201511660248801526fffffffffffffffffffffffffffffffff6040820151168b8801526001600160a01b0360608201511660648801526080810151151560848801520151151560a486015251602064ffffffffff918281511660c488015201511660e485015251610104840190602080916001600160a01b0381511684520151910152565b602082610144815f895af1801561035a575f9061118c575b60019250611185828961198c565b5201610f4e565b506020823d6020116111b7575b816111a660209383611786565b8101031261034e5760019151611177565b3d9150611199565b926001906fffffffffffffffffffffffffffffffff6111e460406103e7888c896119a0565b16019301610f25565b3461034e576111fb36611562565b9384939192931561152457505f905f5b8581106114f657506001600160a01b036112289116918483611b08565b611231846117df565b925f5b85811061124957604051806100b587826115f0565b611254818786611641565b61125d9061182e565b90611269818887611641565b6020016112759061182e565b878261128281838a611641565b60400161128e906116ae565b61129982848b611641565b6060016112a590611842565b6112b083858c611641565b6080016112bc90611842565b916112c884868d611641565b60a081016112d59161184f565b946112e191968d611641565b60c00195604051986112f28a6116cb565b6001600160a01b031689526001600160a01b031660208901526fffffffffffffffffffffffffffffffff1660408801526001600160a01b038816606088015215156080870152151560a0860152369061134a926118d2565b60c08401523661135991611953565b60e0830152604051917f897f362b000000000000000000000000000000000000000000000000000000008352600483016020905282610144810182516001600160a01b0316602483015260208301516001600160a01b0316604483015260408301516fffffffffffffffffffffffffffffffff16606483015260608301516001600160a01b031660848301526080830151151560a483015260a0830151151560c483015260c08301519060e4830161012090528151809152610164830191602001905f905b8082106114ad57505050819061145260e06020950151610104840190602080916001600160a01b0381511684520151910152565b03815f885af1801561035a575f9061147a575b60019250611473828861198c565b5201611234565b506020823d6020116114a5575b8161149460209383611786565b8101031261034e5760019151611465565b3d9150611487565b919493509160206040826114e7600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b0195019201869394929161141e565b916001906fffffffffffffffffffffffffffffffff61151b60406103e7878b8a611641565b1601920161120b565b807ff8bf106c0000000000000000000000000000000000000000000000000000000060049252fd5b602435906001600160a01b038216820361034e57565b9060607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc83011261034e576001600160a01b0391600435838116810361034e5792602435908116810361034e579160443567ffffffffffffffff9283821161034e578060238301121561034e57816004013593841161034e5760248460051b8301011161034e576024019190565b60209060206040818301928281528551809452019301915f5b828110611617575050505090565b835185529381019392810192600101611609565b600435906001600160a01b038216820361034e57565b91908110156116815760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018136030182121561034e570190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b356fffffffffffffffffffffffffffffffff8116810361034e5790565b610100810190811067ffffffffffffffff8211176116e857604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff8211176116e857604052565b610120810190811067ffffffffffffffff8211176116e857604052565b6060810190811067ffffffffffffffff8211176116e857604052565b6080810190811067ffffffffffffffff8211176116e857604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176116e857604052565b67ffffffffffffffff81116116e85760051b60200190565b906117e9826117c7565b6117f66040519182611786565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061182482946117c7565b0190602036910137565b356001600160a01b038116810361034e5790565b35801515810361034e5790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561034e570180359067ffffffffffffffff821161034e57602001918160061b3603831361034e57565b35906fffffffffffffffffffffffffffffffff8216820361034e57565b359064ffffffffff8216820361034e57565b9291926118de826117c7565b6040926118ee6040519283611786565b819581835260208093019160061b84019381851161034e57915b84831061191757505050505050565b858383031261034e57838691825161192e81611715565b611937866118a3565b81526119448387016118c0565b83820152815201920191611908565b919082604091031261034e5760405161196b81611715565b809280356001600160a01b038116810361034e578252602090810135910152565b80518210156116815760209160051b010190565b919081101561168157610120020190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561034e570180359067ffffffffffffffff821161034e5760200191606082023603831361034e57565b929192611a11826117c7565b604094611a216040519283611786565b8195848352602080930191606080960285019481861161034e57925b858410611a4d5750505050505050565b868483031261034e57825190611a628261174e565b611a6b856118a3565b8252858501359067ffffffffffffffff8216820361034e57828792838b950152611a968688016118c0565b86820152815201930192611a3d565b91908110156116815760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18136030182121561034e570190565b3564ffffffffff8116810361034e5790565b919081101561168157610140020190565b90604080516020907f23b872dd000000000000000000000000000000000000000000000000000000008282015233602482015260449030828201528660648201526064815260a081019080821067ffffffffffffffff8311176116e857611b7191855285611d09565b6001600160a01b0394858516958451917fdd62ed3e0000000000000000000000000000000000000000000000000000000083523060048401521690816024820152838184818a5afa908115611ce7579088915f91611cb6575b5010611bda575b50505050505050565b8351955f808589017f095ea7b3000000000000000000000000000000000000000000000000000000009a8b82528560248c0152868b0152858a52611c1d8a61176a565b89519082855af190611c2d611d94565b82611c83575b5081611c78575b50611bd157611c6c96611c6794519384015260248301525f818301528152611c618161176a565b82611d09565b611d09565b5f808080808080611bd1565b90503b15155f611c3a565b809192505190858215928315611c9e575b505050905f611c33565b611cae9350820181019101611cf1565b5f8581611c94565b809250858092503d8311611ce0575b611ccf8183611786565b8101031261034e578790515f611bca565b503d611cc5565b85513d5f823e3d90fd5b9081602091031261034e5751801515810361034e5790565b5f806001600160a01b03611d3293169360208151910182865af1611d2b611d94565b9083611df1565b8051908115159182611d79575b5050611d485750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b611d8c9250602080918301019101611cf1565b155f80611d3f565b3d15611dec573d9067ffffffffffffffff82116116e85760405191611de160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184611786565b82523d5f602084013e565b606090565b90611e305750805115611e0657805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b81511580611e7b575b611e41575090565b6024906001600160a01b03604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15611e3956fea164736f6c6343000817000a"; + hex"60808060405234601557611e0a908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806337266dd3146111a357806349a32c4014610e3e578063606ef87514610b025780639e743f29146107a6578063a514f83e1461040c5763f7ca34eb1461005b575f80fd5b3461033c5761006936611512565b91909282156103e4575f905f5b8481106103b057506001600160a01b036100939116918383611a7a565b61009c83611758565b926001600160a01b035f9316925b8181106100c357604051806100bf8782611587565b0390f35b6100ce818388611a17565b6100d7906117a7565b90826100e482828a611a17565b6020016100f0906117a7565b6100fb83838b611a17565b60400161010790611643565b9389610114858583611a17565b606001610120906117bb565b61012b868684611a17565b608001610137906117bb565b610142878785611a17565b60a00161014e90611a57565b918761015b818987611a17565b60c0810161016891611926565b98610174929196611a17565b60e0019360405195610185876116c6565b6001600160a01b0316865260208601966001600160a01b0316875260408601996fffffffffffffffffffffffffffffffff168a5260608601978d895260808701921515835260a08701931515845260c087019464ffffffffff16855236906101ec9261197a565b9360e08601948552366101fe916118c8565b966101008601978852604051998a977f31df3d480000000000000000000000000000000000000000000000000000000089526004890160209052610164890197516001600160a01b031660248a0152516001600160a01b03166044890152516fffffffffffffffffffffffffffffffff166064880152516001600160a01b0316608487015251151560a486015251151560c48501525164ffffffffff1660e48401525190610104830161014090528151809152610184830191602001905f905b808210610353575050925180516001600160a01b03166101248401526020908101516101448401529250819003815f885af18015610348575f90610312575b6001925061030b8288611901565b52016100aa565b506020823d8211610340575b8161032b602093836116ff565b8101031261033c57600191516102fd565b5f80fd5b3d915061031e565b6040513d5f823e3d90fd5b919493509160206060826103a1600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b019501920186939492916102be565b916001906fffffffffffffffffffffffffffffffff6103db60406103d5878a8c611a17565b01611643565b16019201610076565b7ff8bf106c000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461033c5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033c576104436115c0565b61044b6114cb565b906044359167ffffffffffffffff831161033c573660238401121561033c5782600401359267ffffffffffffffff841161033c57602481019060243691610140870201011161033c5783156103e45790915f9190825b85811061077457506001600160a01b036104be9116928484611a7a565b6104c784611758565b926001600160a01b03165f5b8581106104e857604051806100bf8782611587565b6104fb6104f6828886611a69565b6117a7565b90610512602061050c838a88611a69565b016117a7565b878561052460406103d5868585611a69565b61053a6060610534878686611a69565b016117bb565b906101006105648761055d8188610557608061053484848d611a69565b98611a69565b968c611a69565b01906001600160a01b036040519861057b8a611660565b1688526001600160a01b0360208901961686526fffffffffffffffffffffffffffffffff6040890191168152606088019189835260808901931515845260a08901941515855260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60873603011261033c57604051956105fa876116e3565b61060660a08201611839565b875261061460c08201611839565b602088015260e00161062590611839565b604087015260c089019586523661063b916118c8565b9560e08901968752604051987f53b15727000000000000000000000000000000000000000000000000000000008a52516001600160a01b031660048a0152516001600160a01b03166024890152516fffffffffffffffffffffffffffffffff166044880152516001600160a01b03166064870152511515608486015251151560a485015251805164ffffffffff1660c4850152602081015164ffffffffff1660e48501526040015164ffffffffff1661010484015251610124830161071291602080916001600160a01b0381511684520151910152565b8180865a925f61016492602095f18015610348575f90610742575b6001925061073b8288611901565b52016104d3565b506020823d821161076c575b8161075b602093836116ff565b8101031261033c576001915161072d565b3d915061074e565b93926001906fffffffffffffffffffffffffffffffff61079a60406103d5898b89611a69565b160194019392936104a1565b3461033c576107b436611512565b91909282156103e4575f905f5b848110610ad457506001600160a01b036107de9116918383611a7a565b6107e783611758565b926001600160a01b035f9316925b81811061080a57604051806100bf8782611587565b610815818388611a17565b61081e906117a7565b908261082b82828a611a17565b602001610837906117a7565b61084283838b611a17565b60400161084e90611643565b938961085b858583611a17565b606001610867906117bb565b610872868684611a17565b60800161087e906117bb565b610889878785611a17565b60a00161089590611a57565b91876108a2818987611a17565b60c081016108af916117c8565b986108bb929196611a17565b60e00193604051956108cc876116c6565b6001600160a01b0316865260208601966001600160a01b0316875260408601996fffffffffffffffffffffffffffffffff168a5260608601978d895260808701921515835260a08701931515845260c087019464ffffffffff16855236906109339261184b565b9360e0860194855236610945916118c8565b966101008601978852604051998a977f32fbe22b0000000000000000000000000000000000000000000000000000000089526004890160209052610164890197516001600160a01b031660248a0152516001600160a01b03166044890152516fffffffffffffffffffffffffffffffff166064880152516001600160a01b0316608487015251151560a486015251151560c48501525164ffffffffff1660e48401525190610104830161014090528151809152610184830191602001905f905b808210610a8b575050925180516001600160a01b03166101248401526020908101516101448401529250819003815f885af18015610348575f90610a59575b60019250610a528288611901565b52016107f5565b506020823d8211610a83575b81610a72602093836116ff565b8101031261033c5760019151610a44565b3d9150610a65565b91949350916020604082610ac5600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291610a05565b916001906fffffffffffffffffffffffffffffffff610af960406103d5878a8c611a17565b160192016107c1565b3461033c57610b1036611512565b91909282156103e4575f905f5b848110610e1057506001600160a01b03610b3a9116918383611a7a565b610b4383611758565b926001600160a01b035f9316925b818110610b6657604051806100bf8782611587565b610b718183886115d6565b610b7a906117a7565b9082610b8782828a6115d6565b602001610b93906117a7565b610b9e83838b6115d6565b604001610baa90611643565b9389610bb78585836115d6565b606001610bc3906117bb565b610bce8686846115d6565b608001610bda906117bb565b9086610be78188866115d6565b60a08101610bf491611926565b97610c009291956115d6565b60c0019260405194610c1186611660565b6001600160a01b0316855260208501956001600160a01b0316865260408501986fffffffffffffffffffffffffffffffff16895260608501968c885260808601921515835260a0860193151584523690610c6a9261197a565b9260c0850193845236610c7c916118c8565b9560e085019687526040519889967f54c022920000000000000000000000000000000000000000000000000000000088526004880160209052610144880196516001600160a01b03166024890152516001600160a01b03166044880152516fffffffffffffffffffffffffffffffff166064870152516001600160a01b0316608486015251151560a485015251151560c4840152519060e4830161012090528151809152610164830191602001905f905b808210610db3575050925180516001600160a01b03166101048401526020908101516101248401529250819003815f885af18015610348575f90610d81575b60019250610d7a8288611901565b5201610b51565b506020823d8211610dab575b81610d9a602093836116ff565b8101031261033c5760019151610d6c565b3d9150610d8d565b91949350916020606082610e01600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b01950192018693949291610d2d565b916001906fffffffffffffffffffffffffffffffff610e3560406103d5878a8c6115d6565b16019201610b1d565b3461033c5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033c57610e756115c0565b610e7d6114cb565b906044359167ffffffffffffffff831161033c573660238401121561033c5782600401359267ffffffffffffffff841161033c57602481019060243691610120870201011161033c5783156103e45790915f9190825b85811061117157506001600160a01b03610ef09116928484611a7a565b610ef984611758565b926001600160a01b03165f5b858110610f1a57604051806100bf8782611587565b610f286104f6828886611915565b90610f39602061050c838a88611915565b8785610f4b60406103d5868585611915565b610f5b6060610534878686611915565b9060e0610f8487610f7d8188610f77608061053484848d611915565b98611915565b968c611915565b01906001600160a01b0360405198610f9b8a611660565b1688526001600160a01b0360208901961686526fffffffffffffffffffffffffffffffff6040890191168152606088019189835260808901931515845260a08901941515855260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60873603011261033c576040519561101a876116aa565b61102660a08201611839565b875260c00161103490611839565b602087015260c089019586523661104a916118c8565b9560e08901968752604051987fab167ccc000000000000000000000000000000000000000000000000000000008a52516001600160a01b031660048a0152516001600160a01b03166024890152516fffffffffffffffffffffffffffffffff166044880152516001600160a01b03166064870152511515608486015251151560a485015251805164ffffffffff1660c48501526020015164ffffffffff1660e484015251610104830161110f91602080916001600160a01b0381511684520151910152565b8180865a925f61014492602095f18015610348575f9061113f575b600192506111388288611901565b5201610f05565b506020823d8211611169575b81611158602093836116ff565b8101031261033c576001915161112a565b3d915061114b565b93926001906fffffffffffffffffffffffffffffffff61119760406103d5898b89611915565b16019401939293610ed3565b3461033c576111b136611512565b91909282156103e4575f905f5b84811061149d57506001600160a01b036111db9116918383611a7a565b6111e483611758565b926001600160a01b035f9316925b81811061120757604051806100bf8782611587565b6112128183886115d6565b61121b906117a7565b908261122882828a6115d6565b602001611234906117a7565b61123f83838b6115d6565b60400161124b90611643565b93896112588585836115d6565b606001611264906117bb565b61126f8686846115d6565b60800161127b906117bb565b90866112888188866115d6565b60a08101611295916117c8565b976112a19291956115d6565b60c00192604051946112b286611660565b6001600160a01b0316855260208501956001600160a01b0316865260408501986fffffffffffffffffffffffffffffffff16895260608501968c885260808601921515835260a086019315158452369061130b9261184b565b9260c085019384523661131d916118c8565b9560e085019687526040519889967f897f362b0000000000000000000000000000000000000000000000000000000088526004880160209052610144880196516001600160a01b03166024890152516001600160a01b03166044880152516fffffffffffffffffffffffffffffffff166064870152516001600160a01b0316608486015251151560a485015251151560c4840152519060e4830161012090528151809152610164830191602001905f905b808210611454575050925180516001600160a01b03166101048401526020908101516101248401529250819003815f885af18015610348575f90611422575b6001925061141b8288611901565b52016111f2565b506020823d821161144c575b8161143b602093836116ff565b8101031261033c576001915161140d565b3d915061142e565b9194935091602060408261148e600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b019501920186939492916113ce565b916001906fffffffffffffffffffffffffffffffff6114c260406103d5878a8c6115d6565b160192016111be565b602435906001600160a01b038216820361033c57565b9181601f8401121561033c5782359167ffffffffffffffff831161033c576020808501948460051b01011161033c57565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82011261033c576004356001600160a01b038116810361033c57916024356001600160a01b038116810361033c57916044359067ffffffffffffffff821161033c57611583916004016114e1565b9091565b60206040818301928281528451809452019201905f5b8181106115aa5750505090565b825184526020938401939092019160010161159d565b600435906001600160a01b038216820361033c57565b91908110156116165760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018136030182121561033c570190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b356fffffffffffffffffffffffffffffffff8116810361033c5790565b610100810190811067ffffffffffffffff82111761167d57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff82111761167d57604052565b610120810190811067ffffffffffffffff82111761167d57604052565b6060810190811067ffffffffffffffff82111761167d57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761167d57604052565b67ffffffffffffffff811161167d5760051b60200190565b9061176282611740565b61176f60405191826116ff565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061179d8294611740565b0190602036910137565b356001600160a01b038116810361033c5790565b35801515810361033c5790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561033c570180359067ffffffffffffffff821161033c57602001918160061b3603831361033c57565b35906fffffffffffffffffffffffffffffffff8216820361033c57565b359064ffffffffff8216820361033c57565b92919261185782611740565b9361186560405195866116ff565b602085848152019260061b82019181831161033c57925b8284106118895750505050565b60408483031261033c57602060409182516118a3816116aa565b6118ac8761181c565b81526118b9838801611839565b8382015281520193019261187c565b919082604091031261033c576040516118e0816116aa565b809280356001600160a01b038116810361033c578252602090810135910152565b80518210156116165760209160051b010190565b919081101561161657610120020190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561033c570180359067ffffffffffffffff821161033c5760200191606082023603831361033c57565b92919261198682611740565b9361199460405195866116ff565b606060208685815201930282019181831161033c57925b8284106119b85750505050565b60608483031261033c57604051906119cf826116e3565b6119d88561181c565b825260208501359067ffffffffffffffff8216820361033c5782602092836060950152611a0760408801611839565b60408201528152019301926119ab565b91908110156116165760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18136030182121561033c570190565b3564ffffffffff8116810361033c5790565b919081101561161657610140020190565b9190611acf6040517f23b872dd00000000000000000000000000000000000000000000000000000000602082015233602482015230604482015283606482015260648152611ac96084826116ff565b82611c8f565b6001600160a01b0381166001600160a01b03604051947fdd62ed3e0000000000000000000000000000000000000000000000000000000086523060048701521693846024820152602081604481855afa80156103485784915f91611c42575b5010611b3b575b50505050565b5f806040519460208601907f095ea7b3000000000000000000000000000000000000000000000000000000008252876024880152604487015260448652611b836064876116ff565b85519082855af190611b93611d14565b82611c10575b5081611c05575b5015611bad575b80611b35565b611bf8611bfd93604051907f095ea7b300000000000000000000000000000000000000000000000000000000602083015260248201525f604482015260448152611ac96064826116ff565b611c8f565b5f8080611ba7565b90503b15155f611ba0565b80519192508115918215611c28575b5050905f611b99565b611c3b9250602080918301019101611c77565b5f80611c1f565b9150506020813d602011611c6f575b81611c5e602093836116ff565b8101031261033c578390515f611b2e565b3d9150611c51565b9081602091031261033c5751801515810361033c5790565b5f806001600160a01b03611cb893169360208151910182865af1611cb1611d14565b9083611d71565b8051908115159182611cf9575b5050611cce5750565b7f5274afe7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b611d0c9250602080918301019101611c77565b155f80611cc5565b3d15611d6c573d9067ffffffffffffffff821161167d5760405191611d6160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846116ff565b82523d5f602084013e565b606090565b90611dae5750805115611d8657805190602001fd5b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b81511580611df4575b611dbf575090565b6001600160a01b03907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b15611db756fea164736f6c634300081a000a"; bytes public constant BYTECODE_MERKLE_LOCKUP_FACTORY = - hex"608080604052346100165761400c908161001b8239f35b5f80fdfe60406080815260048036101562000014575f80fd5b5f3560e01c80631e323876146200022e5763769bed201462000034575f80fd5b346200022a5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126200022a5767ffffffffffffffff9080358281116200022a576200008890369083016200066a565b906024359073ffffffffffffffffffffffffffffffffffffffff908183168093036200022a57857fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126200022a57620000e262000541565b9464ffffffffff60443581811681036200022a57875260643590811681036200022a57602087015286519161188c908184019284841090841117620001fe5750908291620008da8339608081526200016588620001436080840189620007f9565b9287602082015201886020908164ffffffffff91828151168552015116910152565b03905ff08015620001f45760209593620001db95937f2ba0fe49588281dbb122dd3b7f3e2b3396338f70dbe3c62bf3e3888b4ba7ffb893620001b99316968795875194859460c0865260c0860190620007f9565b928a850152878401906020908164ffffffffff91828151168552015116910152565b608435608083015260a43560a08301520390a251908152f35b85513d5f823e3d90fd5b6041907f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b5f80fd5b50346200022a5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126200022a5767ffffffffffffffff81358181116200022a576200028290369084016200066a565b9060249081359173ffffffffffffffffffffffffffffffffffffffff948584168094036200022a57604435968388116200022a57366023890112156200022a5787820135948486116200051657602098620002e28a8860051b016200058f565b96858b89838152019160061b830101913683116200022a579398938601905b828210620004cb575050505f965f925f985b88518a10156200038a5787806200032b8c8c62000759565b5151169116018781116200035f576001909464ffffffffff8d620003508d8d62000759565b51015116019901989362000313565b866011877f4e487b71000000000000000000000000000000000000000000000000000000005f52525ffd5b908b948b888a89818e9716670de0b6b3a764000081036200049e5750875192611e9a918285019385851090851117620004745750508291620003f39162002166843960608152620003df6060820188620007f9565b908a8c820152898183039101528762000888565b03905ff08015620001f45791620004557ffe44018cf74992b2720702385a1728bd329dd136e4f651203176c81c12710a8b9492620004439416978896885195869560c0875260c0870190620007f9565b918b8601528482038986015262000888565b906060830152606435608083015260843560a08301520390a251908152f35b6041907f4e487b71000000000000000000000000000000000000000000000000000000005f52525ffd5b88517f1b05170a000000000000000000000000000000000000000000000000000000008152918201529050fd5b89829a959a3603126200022a57620004e262000541565b9082359089821682036200022a57828e9288945262000503838601620005e2565b8382015281520191019098939862000301565b836041847f4e487b71000000000000000000000000000000000000000000000000000000005f52525ffd5b604051906040820182811067ffffffffffffffff8211176200056257604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f604051930116820182811067ffffffffffffffff8211176200056257604052565b359081151582036200022a57565b359064ffffffffff821682036200022a57565b81601f820112156200022a5780359067ffffffffffffffff821162000562576200064760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f850116016200058f565b92828452602083830101116200022a57815f926020809301838601378301015290565b9190916101009081818503126200022a5760405191820167ffffffffffffffff908381108282111762000562576040528294823573ffffffffffffffffffffffffffffffffffffffff9081811681036200022a578552620006ce60208501620005d4565b6020860152620006e160408501620005e2565b6040860152606084013590811681036200022a57606085015260808301358281116200022a578162000715918501620005f5565b608085015260a083013560a085015260c08301359182116200022a57826200074860e094926200075494869401620005f5565b60c086015201620005d4565b910152565b80518210156200076e5760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b91908251928382525f5b848110620007e45750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6020809697860101520116010190565b602081830181015184830182015201620007a5565b9060e0806200087d6200086061010073ffffffffffffffffffffffffffffffffffffffff80885116875260208801511515602088015264ffffffffff604089015116604088015260608801511660608701526080870151908060808801528601906200079b565b60a086015160a086015260c086015185820360c08701526200079b565b930151151591015290565b9081518082526020808093019301915f5b828110620008a8575050505090565b8351805167ffffffffffffffff16865282015164ffffffffff1685830152604090940193928101926001016200089956fe61016080604052346200046c576200188c8038038091620000218285620005b5565b8339810190808203608081126200046c5781516001600160401b0381116200046c578201610100818503126200046c5760405161010081016001600160401b03811182821017620005665760405281516001600160a01b03811681036200046c5781526200009260208301620005d9565b9060208101918252620000a860408401620005e7565b604082019081526060840151909690936001600160a01b03851685036200046c576060830194855260808101516001600160401b0381116200046c5782620000f291830162000639565b906080840191825260a08101519260a0850193845260c08201519160018060401b0383116200046c576200012f60e0926200013b94830162000639565b60c087015201620005d9565b60e084019081526020880151969094906001600160a01b03881688036200046c57604090603f1901126200046c5760408051989089016001600160401b0381118a8210176200056657620001a6916060916040526200019d60408201620005e7565b8b5201620005e7565b956020890196875260c085015151602081116200057a5750515f80546001600160a01b0319166001600160a01b0392831617905584511660805251151560a052965164ffffffffff90811660c052965180516001600160401b038111620005665760019182548381811c911680156200055b575b60208210146200054757601f8111620004fa575b50602090601f83116001146200048e5760c095949392915f918362000482575b50505f19600383901b1c191690821b1790555b5160e0520151604051620002956020828162000287818301968781519384920162000616565b8101038084520182620005b5565b519051906020811062000470575b50610100525115159361012094855261014093838552511669ffffffffff0000000000600454925160281b169160018060501b031916171760045560018060a01b03608051166040519060208201925f8063095ea7b360e01b9586815260018060a01b03841660248701528119604487015260448652620003248662000599565b85519082865af16200033562000686565b816200042f575b508062000424575b15620003d8575b6040516110e690868883620007a6843960805183818161048a015281816107310152610c60015260a0518381816107580152610b74015260c05183818161015f01528181610acc01528181610de90152611004015260e0518381816102de015261064a01526101005183610ecb0152518281816107800152610b380152518181816101af01526108f20152f35b6200041a93620004149160405191602083015260018060a01b031660248201525f6044820152604481526200040d8162000599565b82620006ba565b620006ba565b5f8080806200034b565b50813b151562000344565b805180159250821562000446575b50505f6200033c565b81925090602091810103126200046c576020620004649101620005d9565b5f806200043d565b5f80fd5b5f199060200360031b1b165f620002a3565b015190505f806200024e565b90601f19831691845f5260205f20925f5b818110620004e3575091859492918360c0999897959310620004ca575b505050811b01905562000261565b01515f1960f88460031b161c191690555f8080620004bc565b82840151855593860193602093840193016200049f565b835f5260205f20601f840160051c810191602085106200053c575b601f0160051c019084905b828110620005305750506200022e565b5f815501849062000520565b909150819062000515565b634e487b7160e01b5f52602260045260245ffd5b90607f16906200021a565b634e487b7160e01b5f52604160045260245ffd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b608081019081106001600160401b038211176200056657604052565b601f909101601f19168101906001600160401b038211908210176200056657604052565b519081151582036200046c57565b519064ffffffffff821682036200046c57565b6001600160401b0381116200056657601f01601f191660200190565b5f5b838110620006285750505f910152565b818101518382015260200162000618565b81601f820112156200046c5780516200065281620005fa565b92620006626040519485620005b5565b818452602082840101116200046c5762000683916020808501910162000616565b90565b3d15620006b5573d906200069a82620005fa565b91620006aa6040519384620005b5565b82523d5f602084013e565b606090565b5f80620006e69260018060a01b03169360208151910182865af1620006de62000686565b90836200073d565b805190811515918262000716575b5050620006fe5750565b60249060405190635274afe760e01b82526004820152fd5b81925090602091810103126200046c576020620007349101620005d9565b155f80620006f4565b906200076657508051156200075457805190602001fd5b604051630a12f52160e11b8152600490fd5b815115806200079b575b62000779575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b156200077056fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde0314610eb5575080631686c90914610b9957806316c3549d14610b5d5780631bfd681414610b215780633bfe03a814610af35780633f31ae3f146104ae5780634800d97f1461045e57806349fc73dd146103255780634e390d3e1461030157806351e75e8b146102c757806375829def146101ed57806390e64d13146101d35780639e93e57714610183578063bb4b573414610142578063ce516507146101025763f851a440146100cc575f80fd5b346100fe575f6003193601126100fe57602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b5f80fd5b346100fe5760206003193601126100fe57602061013860043560ff6001918060081c5f526002602052161b60405f205416151590565b6040519015158152f35b346100fe575f6003193601126100fe57602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe576020610138610ffc565b346100fe5760206003193601126100fe57610206610f60565b5f5473ffffffffffffffffffffffffffffffffffffffff8082169233840361027a577fffffffffffffffffffffffff000000000000000000000000000000000000000093501691829116175f55337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf805f80a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100fe575f6003193601126100fe5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100fe575f6003193601126100fe57602064ffffffffff60035416604051908152f35b346100fe575f6003193601126100fe576040515f9060018054908160011c9060018316928315610454575b6020938484108114610427578386529081156103e9575060011461038f575b61038b8461037f81880382610fbb565b60405191829182610efc565b0390f35b60015f9081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b8284106103d6575050508161038b9361037f928201019361036f565b80548585018701529285019281016103ba565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b820101915061037f8161038b61036f565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610350565b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe5760806003193601126100fe5760243573ffffffffffffffffffffffffffffffffffffffff811681036100fe57604435906fffffffffffffffffffffffffffffffff821682036100fe576064359167ffffffffffffffff908184116100fe57366023850112156100fe578360040135908282116100fe576005918060051b95602487820101903682116100fe576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff891660408201526fffffffffffffffffffffffffffffffff861660608201526060815261058d81610f9f565b51902060405160208101918252602081526105a781610f83565b519020926105b3610ffc565b610a96576105da60043560ff6001918060081c5f526002602052161b60405f205416151590565b610a64576105ee60206040519a018a610fbb565b8852602401602088015b828210610a5457505050925f935b86518510156106465784841b8701602001519081811015610635575f52602052600160405f205b940193610606565b905f52602052600160405f2061062d565b85907f000000000000000000000000000000000000000000000000000000000000000003610a2a5760035464ffffffffff8116156109f6575b5060043560081c5f52600260205260405f20600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff5f5416604051906106c482610f83565b5f8083526020830152604051936101008501908111858210176109c957604052835273ffffffffffffffffffffffffffffffffffffffff821660208401526fffffffffffffffffffffffffffffffff8416604084015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608401527f0000000000000000000000000000000000000000000000000000000000000000151560808401527f0000000000000000000000000000000000000000000000000000000000000000151560a08401526040516107b281610f83565b64ffffffffff600454818116835260281c16602082015260c084015260e0830152602060e0604051937fab167ccc00000000000000000000000000000000000000000000000000000000855273ffffffffffffffffffffffffffffffffffffffff815116600486015273ffffffffffffffffffffffffffffffffffffffff838201511660248601526fffffffffffffffffffffffffffffffff604082015116604486015273ffffffffffffffffffffffffffffffffffffffff606082015116606486015260808101511515608486015260a0810151151560a486015264ffffffffff8360c08301518281511660c489015201511660e4860152015173ffffffffffffffffffffffffffffffffffffffff8151166101048501520151610124830152602082610144815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af19182156109be575f92610989575b506020927f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff85946fffffffffffffffffffffffffffffffff835195600435875216888601521692a3604051908152f35b91506020823d6020116109b6575b816109a460209383610fbb565b810103126100fe579051906020610922565b3d9150610997565b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff16176003558361067f565b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b81358152602091820191016105f8565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b60446040517f442b184100000000000000000000000000000000000000000000000000000000815242600482015264ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166024820152fd5b346100fe575f6003193601126100fe57604060045464ffffffffff825191818116835260281c166020820152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe5760406003193601126100fe57610bb2610f60565b60248035906fffffffffffffffffffffffffffffffff82168092036100fe5773ffffffffffffffffffffffffffffffffffffffff805f5416338103610e66575064ffffffffff806003541680151580610e23575b80610e14575b610db5575050604051916020925f8084868401987fa9059cbb000000000000000000000000000000000000000000000000000000008a521697888585015287604485015260448452610c5d84610f9f565b857f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610da9573d9067ffffffffffffffff8211610d7d5790610ce79160405191610cd8887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610fbb565b82523d5f8884013e5b83611039565b8051858115159182610d5c575b50509050610d2e575050907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f915f541692604051908152a3005b604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100fe578401518015908115036100fe57808589610cf4565b837f4e487b71000000000000000000000000000000000000000000000000000000005f5260416004525ffd5b610ce790606090610ce1565b6064925083604051927f92b666970000000000000000000000000000000000000000000000000000000084524260048501527f000000000000000000000000000000000000000000000000000000000000000016908301526044820152fd5b50610e1d610ffc565b15610c0c565b5062093a808101828111610e3a5782164211610c06565b847f4e487b71000000000000000000000000000000000000000000000000000000005f5260116004525ffd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100fe575f6003193601126100fe5761038b907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261037f81610f83565b6020808252825181830181905293925f5b858110610f4c575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b818101830151848201604001528201610f0d565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100fe57565b6040810190811067ffffffffffffffff8211176109c957604052565b6080810190811067ffffffffffffffff8211176109c957604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176109c957604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081611031575090565b905042101590565b90611078575080511561104e57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b815115806110d0575b611089575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b1561108156fea164736f6c6343000817000a6101608060405234620004925762001e9a908138038092620000228284620006f6565b82396060818381010312620004925780516001600160401b038111620004925781019161010092838183850103126200049257604051908482016001600160401b03811183821017620006415760405280516001600160a01b03811681036200049257825262000095602082016200071a565b6020830152620000a86040820162000728565b604083015260608101516001600160a01b03811681036200049257606083015260808101516001600160401b0381116200049257620000ed908486019083016200077a565b608083015260a0818101519083015260c08101516001600160401b038111620004925762000134916200012860e0928688019083016200077a565b60c0850152016200071a565b60e08201526020830151916001600160a01b038316830362000492576040840151906001600160401b0382116200049257808501601f838701011215620004925784820151906001600160401b0382116200064157604051956200019f60208460051b0188620006f6565b828752602087019382820160208560061b838501010111620004925793602085830101945b60208560061b82850101018610620006745750505050505060c0810151516020811162000655575060608101515f80546001600160a01b0319166001600160a01b0392831617905581511660809081526020820151151560a052604082015164ffffffffff1660c05281015180519091906001600160401b038111620006415760019283548481811c9116801562000636575b60208210146200062257601f8111620005d5575b50602090601f83116001146200056c5760e09392915f918362000560575b50505f19600383901b1c191690841b1783555b60a0810151825260c0810151604051620002d660208281620002c8818301968781519384920162000757565b8101038084520182620006f6565b51905190602081106200054e575b50865201511515926101209384526101409283528051905f5b82811062000496575050505060018060a01b036080511660018060a01b03825116906040519060208201925f8063095ea7b360e01b9586815283602487015281196044870152604486526200035286620006da565b85519082865af162000363620007c7565b8162000455575b50806200044a575b1562000406575b50505050604051916115b39384620008e785396080518481816104fa015281816108700152610f6e015260a0518481816108970152610e82015260c05184818161021f01528181610e08015281816110f70152611312015260e05184818161034e01526106cb015251836111d90152518281816108bf0152610e4601525181818161012e0152610a270152f35b62000440936200043a9160405191602083015260248201525f6044820152604481526200043381620006da565b82620007fb565b620007fb565b5f80808062000379565b50813b151562000372565b80518015925082156200046c575b50505f6200036a565b8192509060209181010312620004925760206200048a91016200071a565b5f8062000463565b5f80fd5b81518110156200053a5760208160051b830101516004805490680100000000000000008210156200052757868201808255821015620005145790869392915f5260205f20019060018060401b038151166cffffffffff00000000000000006020845493015160401b169160018060681b0319161717905501620002fd565b603290634e487b7160e01b5f525260245ffd5b604190634e487b7160e01b5f525260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f199060200360031b1b165f620002e4565b015190505f8062000289565b92918491601f19821690835f5260205f20915f5b818110620005bc5750958360e09710620005a3575b505050811b0183556200029c565b01515f1960f88460031b161c191690555f808062000595565b8288015184558895909301926020928301920162000580565b845f5260205f20601f840160051c8101916020851062000617575b601f0160051c019085905b8281106200060b5750506200026b565b5f8155018590620005fb565b9091508190620005f0565b634e487b7160e01b5f52602260045260245ffd5b90607f169062000257565b634e487b7160e01b5f52604160045260245ffd5b6044906040519063a52d539b60e01b8252600482015260206024820152fd5b60408685850103126200049257604080519081016001600160401b03811182821017620006415760405286516001600160401b0381168103620004925760209382859260409452620006c8838b0162000728565b838201528152019601959150620001c4565b608081019081106001600160401b038211176200064157604052565b601f909101601f19168101906001600160401b038211908210176200064157604052565b519081151582036200049257565b519064ffffffffff821682036200049257565b6001600160401b0381116200064157601f01601f191660200190565b5f5b838110620007695750505f910152565b818101518382015260200162000759565b81601f820112156200049257805162000793816200073b565b92620007a36040519485620006f6565b818452602082840101116200049257620007c4916020808501910162000757565b90565b3d15620007f6573d90620007db826200073b565b91620007eb6040519384620006f6565b82523d5f602084013e565b606090565b5f80620008279260018060a01b03169360208151910182865af16200081f620007c7565b90836200087e565b805190811515918262000857575b50506200083f5750565b60249060405190635274afe760e01b82526004820152fd5b8192509060209181010312620004925760206200087591016200071a565b155f8062000835565b90620008a757508051156200089557805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580620008dc575b620008ba575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b15620008b156fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde03146111c3575080631686c90914610ea757806316c3549d14610e6b5780631bfd681414610e2f5780633f31ae3f1461051e5780634800d97f146104ce57806349fc73dd146103955780634e390d3e1461037157806351e75e8b1461033757806375829def1461025d57806390e64d1314610243578063bb4b573414610202578063bf4ed03f14610192578063ce51650714610152578063da792468146101025763f851a440146100cc575f80fd5b346100fe575f6003193601126100fe57602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b5f80fd5b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe5760206003193601126100fe57602061018860043560ff6001918060081c5f526002602052161b60405f205416151590565b6040519015158152f35b346100fe575f6003193601126100fe576101aa61135f565b604051602091828201838352815180915283604084019201935f5b8281106101d25784840385f35b8551805167ffffffffffffffff16855282015164ffffffffff1684830152948101946040909301926001016101c5565b346100fe575f6003193601126100fe57602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe57602061018861130a565b346100fe5760206003193601126100fe5761027661126e565b5f5473ffffffffffffffffffffffffffffffffffffffff808216923384036102ea577fffffffffffffffffffffffff000000000000000000000000000000000000000093501691829116175f55337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf805f80a3005b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85166004820152336024820152604490fd5b346100fe575f6003193601126100fe5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100fe575f6003193601126100fe57602064ffffffffff60035416604051908152f35b346100fe575f6003193601126100fe576040515f9060018054908160011c90600183169283156104c4575b60209384841081146104975783865290811561045957506001146103ff575b6103fb846103ef818803826112c9565b6040519182918261120a565b0390f35b60015f9081529294507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b82841061044657505050816103fb936103ef92820101936103df565b805485850187015292850192810161042a565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016858501525050151560051b82010191506103ef816103fb6103df565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f16916103c0565b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe5760806003193601126100fe5760243573ffffffffffffffffffffffffffffffffffffffff811681036100fe576fffffffffffffffffffffffffffffffff60443516604435036100fe5767ffffffffffffffff606435116100fe573660236064350112156100fe57606435600401359067ffffffffffffffff82116100fe5760248260051b60643501013681116100fe576040516020810190600435825273ffffffffffffffffffffffffffffffffffffffff841660408201526fffffffffffffffffffffffffffffffff60443516606082015260608152610603816112ad565b519020604051602081019182526020815261061d81611291565b5190209061062961130a565b610dd25761065060043560ff6001918060081c5f526002602052161b60405f205416151590565b610da05761065d84611347565b9361066b60405195866112c9565b8452606435602401602085015b828210610d90575050505f905b83518210156106c75761069882856113f8565b5190818110156106b6575f52602052600160405f205b910190610685565b905f52602052600160405f206106ae565b90507f000000000000000000000000000000000000000000000000000000000000000003610d665760035464ffffffffff811615610d32575b5061070961135f565b905f825161071681611347565b9361072460405195866112c9565b8185527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061075183611347565b015f5b818110610d0f5750505f905b828210610bec5750506fffffffffffffffffffffffffffffffff82166fffffffffffffffffffffffffffffffff604435168111610bbf576fffffffffffffffffffffffffffffffff6044351611610b6b575b505060043560081c5f52600260205260405f20600160ff600435161b815417905573ffffffffffffffffffffffffffffffffffffffff5f541691604051906107f982611291565b5f82525f602083015260405193610100850185811067ffffffffffffffff821117610b3e57604052845273ffffffffffffffffffffffffffffffffffffffff831660208501526fffffffffffffffffffffffffffffffff60443516604085015273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660608501527f0000000000000000000000000000000000000000000000000000000000000000151560808501527f0000000000000000000000000000000000000000000000000000000000000000151560a085015260c084015260e0830152604051917f897f362b0000000000000000000000000000000000000000000000000000000083526020600484015282610144810173ffffffffffffffffffffffffffffffffffffffff835116602483015273ffffffffffffffffffffffffffffffffffffffff60208401511660448301526fffffffffffffffffffffffffffffffff604084015116606483015273ffffffffffffffffffffffffffffffffffffffff60608401511660848301526080830151151560a483015260a0830151151560c483015260c08301519061012060e48401528151809152602061016484019201905f5b818110610b00575050508190602060e08195015173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015203815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1918215610af5575f92610ac0575b60208380847f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d604073ffffffffffffffffffffffffffffffffffffffff81519360043585526fffffffffffffffffffffffffffffffff60443516888601521692a3604051908152f35b91506020823d602011610aed575b81610adb602093836112c9565b810103126100fe576020915191610a57565b3d9150610ace565b6040513d5f823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff1681860152889550604090940193909201916001016109d3565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6fffffffffffffffffffffffffffffffff610ba97fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff829301866113f8565b51926044350316818351160116905282806107b2565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b9092610c2167ffffffffffffffff610c0486856113f8565b5151166fffffffffffffffffffffffffffffffff60443516611439565b6fffffffffffffffffffffffffffffffff8111610cde576fffffffffffffffffffffffffffffffff8091169164ffffffffff6020610c5f88876113f8565b5101511660405190610c7082611291565b8482526020820152610c82878a6113f8565b52610c8d86896113f8565b5016016fffffffffffffffffffffffffffffffff8111610cb1579260010190610760565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b602490604051907f4916adce0000000000000000000000000000000000000000000000000000000082526004820152fd5b602090604051610d1e81611291565b5f81525f8382015282828a01015201610754565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161760035581610700565b60046040517f0fa7d73c000000000000000000000000000000000000000000000000000000008152fd5b8135815260209182019101610678565b60246040517f712b37a30000000000000000000000000000000000000000000000000000000081526004356004820152fd5b60446040517f442b184100000000000000000000000000000000000000000000000000000000815242600482015264ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166024820152fd5b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe5760406003193601126100fe57610ec061126e565b60248035906fffffffffffffffffffffffffffffffff82168092036100fe5773ffffffffffffffffffffffffffffffffffffffff805f5416338103611174575064ffffffffff806003541680151580611131575b80611122575b6110c3575050604051916020925f8084868401987fa9059cbb000000000000000000000000000000000000000000000000000000008a521697888585015287604485015260448452610f6b846112ad565b857f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d156110b7573d9067ffffffffffffffff821161108b5790610ff59160405191610fe6887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846112c9565b82523d5f8884013e5b83611506565b805185811515918261106a575b5050905061103c575050907f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f915f541692604051908152a3005b604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b83809293500103126100fe578401518015908115036100fe57808589611002565b837f4e487b71000000000000000000000000000000000000000000000000000000005f5260416004525ffd5b610ff590606090610fef565b6064925083604051927f92b666970000000000000000000000000000000000000000000000000000000084524260048501527f000000000000000000000000000000000000000000000000000000000000000016908301526044820152fd5b5061112b61130a565b15610f1a565b5062093a8081018281116111485782164211610f14565b847f4e487b71000000000000000000000000000000000000000000000000000000005f5260116004525ffd5b6040517fc6cce6a400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff919091166004820152336024820152604490fd5b346100fe575f6003193601126100fe576103fb907f00000000000000000000000000000000000000000000000000000000000000006020820152602081526103ef81611291565b6020808252825181830181905293925f5b85811061125a575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b81810183015184820160400152820161121b565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100fe57565b6040810190811067ffffffffffffffff821117610b3e57604052565b6080810190811067ffffffffffffffff821117610b3e57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610b3e57604052565b64ffffffffff7f000000000000000000000000000000000000000000000000000000000000000016801515908161133f575090565b905042101590565b67ffffffffffffffff8111610b3e5760051b60200190565b6004549061136c82611347565b91604061137c60405194856112c9565b8184528360208091019160045f527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b905f935b8585106113be57505050505050565b600184819284516113ce81611291565b64ffffffffff875467ffffffffffffffff81168352871c16838201528152019301940193916113af565b805182101561140c5760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff838209838202918280831092039180830392146114f557670de0b6b3a764000090818310156114be57947faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac1066994950990828211900360ee1b910360121c170290565b60449086604051917f5173648d00000000000000000000000000000000000000000000000000000000835260048301526024820152fd5b5050670de0b6b3a764000090049150565b90611545575080511561151b57805190602001fd5b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b8151158061159d575b611556575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b1561154e56fea164736f6c6343000817000aa164736f6c6343000817000a"; + hex"60808060405234601557613c28908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c80631e323876146102325763769bed201461002f575f80fd5b3461022e5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022e5760043567ffffffffffffffff811161022e5761007e9036906004016105fe565b6024359073ffffffffffffffffffffffffffffffffffffffff821680920361022e5760407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc36011261022e576100d261050b565b60443564ffffffffff8116810361022e57815260643564ffffffffff8116810361022e57602082015260405161174880820182811067ffffffffffffffff82111761020157829161088e8339608081526101556040610134608084018861079d565b92886020820152018564ffffffffff60208092828151168552015116910152565b03905ff09283156101f6576101b96101db937f2ba0fe49588281dbb122dd3b7f3e2b3396338f70dbe3c62bf3e3888b4ba7ffb89273ffffffffffffffffffffffffffffffffffffffff6020971695869560405194859460c0865260c086019061079d565b9289850152604084019064ffffffffff60208092828151168552015116910152565b608435608083015260a43560a08301520390a2604051908152f35b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f80fd5b3461022e5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022e5760043567ffffffffffffffff811161022e576102819036906004016105fe565b60243573ffffffffffffffffffffffffffffffffffffffff811680910361022e576044359167ffffffffffffffff831161022e573660238401121561022e57826004013567ffffffffffffffff8111610201576102e360208260051b0161052b565b936024602086848152019260061b8201019036821161022e57602401915b8183106104c2575050505f905f905f925b85518410156103955767ffffffffffffffff8061032f86896106ff565b51511691160167ffffffffffffffff8111610368576001909264ffffffffff602061035a878a6106ff565b510151160193019291610312565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b67ffffffffffffffff9195935016670de0b6b3a764000081036104975750604051611c468082019082821067ffffffffffffffff83111761020157829161040191611fd68439606081526103ec606082018961079d565b9087602082015260408183039101528561083d565b03905ff080156101f6576020947ffe44018cf74992b2720702385a1728bd329dd136e4f651203176c81c12710a8b9261047673ffffffffffffffffffffffffffffffffffffffff610464941696879660405195869560c0875260c087019061079d565b918a860152848203604086015261083d565b906060830152606435608083015260843560a08301520390a2604051908152f35b7f1b05170a000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b60408336031261022e576104d461050b565b9083359067ffffffffffffffff8216820361022e5782602092604094526104fc83870161057c565b83820152815201920191610301565b604051906040820182811067ffffffffffffffff82111761020157604052565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f604051930116820182811067ffffffffffffffff82111761020157604052565b3590811515820361022e57565b359064ffffffffff8216820361022e57565b81601f8201121561022e5780359067ffffffffffffffff8211610201576105dc60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501160161052b565b928284526020838301011161022e57815f926020809301838601378301015290565b9190916101008184031261022e5760405190610100820182811067ffffffffffffffff821117610201576040528193813573ffffffffffffffffffffffffffffffffffffffff8116810361022e57835261065a6020830161056f565b602084015261066b6040830161057c565b6040840152606082013573ffffffffffffffffffffffffffffffffffffffff8116810361022e576060840152608082013567ffffffffffffffff811161022e57816106b791840161058e565b608084015260a082013560a084015260c08201359067ffffffffffffffff821161022e57826106ef60e094926106fa9486940161058e565b60c08601520161056f565b910152565b80518210156107135760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b91908251928382525f5b8481106107885750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6020809697860101520116010190565b8060208092840101518282860101520161074a565b9073ffffffffffffffffffffffffffffffffffffffff825116815260208201511515602082015264ffffffffff604083015116604082015273ffffffffffffffffffffffffffffffffffffffff606083015116606082015260e08061083261081660808601516101006080870152610100860190610740565b60a086015160a086015260c086015185820360c0870152610740565b930151151591015290565b90602080835192838152019201905f5b81811061085a5750505090565b8251805167ffffffffffffffff16855260209081015164ffffffffff16818601526040909401939092019160010161084d56fe610160806040523461042857611748803803809161001d8285610560565b833981019080820390608082126104285780516001600160401b03811161042857810190610100828503126104285760405161010081016001600160401b038111828210176105355760405282516001600160a01b038116810361042857815261008960208401610583565b6020820190815261009c60408501610590565b60408301908152606085015194906001600160a01b0386168603610428576060840195865260808201516001600160401b03811161042857886100e09184016105de565b6080850190815260a08381015190860190815260c084015190999192916001600160401b0382116104285761011c60e09161012a9387016105de565b9460c0880195865201610583565b9360e0860194855260208701519560018060a01b038716998a880361042857604090603f1901126104285760408051989089016001600160401b0381118a8210176105355761018d9160609160405261018560408201610590565b8b5201610590565b9860208901998a52855151602081116105495750515f80546001600160a01b0319166001600160a01b0392831617905590511660805251151560a0525164ffffffffff1660c0525180519097906001600160401b03811161053557600154600181811c9116801561052b575b602082101461051757601f81116104b4575b506020601f821160011461044857819064ffffffffff98999a5f9261043d575b50508160011b915f199060031b1c1916176001555b5160e052516040516102736020828161026281830196878151938492016105bd565b81010301601f198101835282610560565b519051906020811061042c575b50610100525115156101205261014052511669ffffffffff0000000000600454925160281b169160018060501b031916171760045560018060a01b0360805116604051905f806020840163095ea7b360e01b815285602486015281196044860152604485526102f0606486610560565b84519082855af16102ff610623565b816103f1575b50806103e7575b156103a2575b60405161102490816107248239608051818181610481015281816106d60152610c20015260a0518181816107140152610b11015260c05181818161015f01528181610a6801528181610d8a0152610f49015260e0518181816102d3015261062201526101005181610e2801526101205181818161073e0152610ad50152610140518181816101af01526108870152f35b6103da6103df936040519063095ea7b360e01b602083015260248201525f6044820152604481526103d4606482610560565b82610652565b610652565b5f8080610312565b50803b151561030c565b8051801592508215610406575b50505f610305565b81925090602091810103126104285760206104219101610583565b5f806103fe565b5f80fd5b5f199060200360031b1b165f610280565b015190505f8061022b565b601f1982169960015f52815f209a5f5b81811061049c57509164ffffffffff999a9b91846001959410610484575b505050811b01600155610240565b01515f1960f88460031b161c191690555f8080610476565b838301518d556001909c019b60209384019301610458565b60015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6601f830160051c8101916020841061050d575b601f0160051c01905b818110610502575061020b565b5f81556001016104f5565b90915081906104ec565b634e487b7160e01b5f52602260045260245ffd5b90607f16906101f9565b634e487b7160e01b5f52604160045260245ffd5b63a52d539b60e01b5f52600452602060245260445ffd5b601f909101601f19168101906001600160401b0382119082101761053557604052565b5190811515820361042857565b519064ffffffffff8216820361042857565b6001600160401b03811161053557601f01601f191660200190565b5f5b8381106105ce5750505f910152565b81810151838201526020016105bf565b81601f820112156104285780516105f4816105a2565b926106026040519485610560565b818452602082840101116104285761062091602080850191016105bd565b90565b3d1561064d573d90610634826105a2565b916106426040519384610560565b82523d5f602084013e565b606090565b5f8061067a9260018060a01b03169360208151910182865af1610673610623565b90836106c5565b80519081151591826106a2575b50506106905750565b635274afe760e01b5f5260045260245ffd5b81925090602091810103126104285760206106bd9101610583565b155f80610687565b906106e957508051156106da57805190602001fd5b630a12f52160e11b5f5260045ffd5b8151158061071a575b6106fa575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156106f256fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde0314610e12575080631686c90914610b3657806316c3549d14610afa5780631bfd681414610abe5780633bfe03a814610a905780633f31ae3f146104a55780634800d97f1461045557806349fc73dd1461031a5780634e390d3e146102f657806351e75e8b146102bc57806375829def146101ed57806390e64d13146101d35780639e93e57714610183578063bb4b573414610142578063ce516507146101025763f851a440146100cc575f80fd5b346100fe575f6003193601126100fe57602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b5f80fd5b346100fe5760206003193601126100fe57602061013860043560ff6001918060081c5f526002602052161b60405f205416151590565b6040519015158152f35b346100fe575f6003193601126100fe57602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe576020610138610f41565b346100fe5760206003193601126100fe57610206610ec1565b5f5473ffffffffffffffffffffffffffffffffffffffff811633810361028d575073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffff0000000000000000000000000000000000000000921691829116175f55337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf805f80a3005b7fc6cce6a4000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b346100fe575f6003193601126100fe5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100fe575f6003193601126100fe57602064ffffffffff60035416604051908152f35b346100fe575f6003193601126100fe576040515f6001548060011c9060018116801561044b575b60208310811461041e578285529081156103dc575060011461037e575b61037a8361036e81850382610f00565b60405191829182610e5b565b0390f35b91905060015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6915f905b8082106103c25750909150810160200161036e61035e565b9192600181602092548385880101520191019092916103aa565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b8401909101915061036e905061035e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610341565b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe5760806003193601126100fe5760043560243573ffffffffffffffffffffffffffffffffffffffff81168091036100fe57604435916fffffffffffffffffffffffffffffffff83168093036100fe576064359067ffffffffffffffff82116100fe57366023830112156100fe57816004013567ffffffffffffffff81116100fe578060051b92602484820101903682116100fe57604051602081019085825287604082015288606082015260608152610563608082610f00565b519020604051602081019182526020815261057f604082610f00565b5190209261058b610f41565b610a39576105b08560ff6001918060081c5f526002602052161b60405f205416151590565b610a0d576105c46020604051970187610f00565b8552602401602085015b8282106109fd57505050935f945b835186101561061e5760208660051b85010151908181105f1461060d575f52602052600160405f205b9501946105dc565b905f52602052600160405f20610605565b84907f0000000000000000000000000000000000000000000000000000000000000000036109d55760035464ffffffffff8116156109a1575b508260081c5f52600260205260405f20600160ff85161b815417905573ffffffffffffffffffffffffffffffffffffffff5f54169160405161069881610ee4565b5f81525f602082015260405193610100850185811067ffffffffffffffff8211176109745760405284526020840183815260408501838152606086017f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16815260808701907f00000000000000000000000000000000000000000000000000000000000000001515825260a08801927f0000000000000000000000000000000000000000000000000000000000000000151584526040519461076e86610ee4565b60045464ffffffffff8116875260281c64ffffffffff16602087015260c08a0195865260e08a01968752604051997fab167ccc000000000000000000000000000000000000000000000000000000008b525173ffffffffffffffffffffffffffffffffffffffff1660048b01525173ffffffffffffffffffffffffffffffffffffffff1660248a0152516fffffffffffffffffffffffffffffffff1660448901525173ffffffffffffffffffffffffffffffffffffffff166064880152511515608487015251151560a486015251805164ffffffffff1660c48601526020015164ffffffffff1660e485015251805173ffffffffffffffffffffffffffffffffffffffff166101048501526020015161012484015282807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165a925f61014492602095f1928315610969575f93610911575b50907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d60406020958594825191825287820152a3604051908152f35b939250906020843d602011610961575b8161092e60209383610f00565b810103126100fe5792519192907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d6108d5565b3d9150610921565b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161760035583610657565b7f0fa7d73c000000000000000000000000000000000000000000000000000000005f5260045ffd5b81358152602091820191016105ce565b847f712b37a3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f442b1841000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445ffd5b346100fe575f6003193601126100fe57604060045464ffffffffff825191818116835260281c166020820152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe5760406003193601126100fe57610b4f610ec1565b6024356fffffffffffffffffffffffffffffffff81168091036100fe5773ffffffffffffffffffffffffffffffffffffffff5f541633810361028d575064ffffffffff6003541680151580610dc4575b80610db5575b610d5b57506040515f8073ffffffffffffffffffffffffffffffffffffffff60208401957fa9059cbb000000000000000000000000000000000000000000000000000000008752169485602485015284604485015260448452610c09606485610f00565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610d4f573d67ffffffffffffffff811161097457610ca79160405191610c9760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610f00565b82523d5f602084013e5b83610f7e565b8051908115159182610d2b575b5050610d0057507f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f602073ffffffffffffffffffffffffffffffffffffffff5f541692604051908152a3005b7f5274afe7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b81925090602091810103126100fe57602001518015908115036100fe578480610cb4565b610ca790606090610ca1565b7f92b66697000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445260645ffd5b50610dbe610f41565b15610ba5565b5062093a80810164ffffffffff8111610de55764ffffffffff164211610b9f565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b346100fe575f6003193601126100fe5761037a907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261036e604082610f00565b919091602081528251928360208301525f5b848110610eab5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b8060208092840101516040828601015201610e6d565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100fe57565b6040810190811067ffffffffffffffff82111761097457604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761097457604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081610f76575090565b905042101590565b90610fbb5750805115610f9357805190602001fd5b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b8151158061100e575b610fcc575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b15610fc456fea164736f6c634300081a000a610160806040523461042f57611c46803803809161001d8285610656565b833981019060608183031261042f5780516001600160401b03811161042f5781016101008184031261042f576040519061010082016001600160401b038111838210176104c15760405280516001600160a01b038116810361042f57825261008760208201610679565b916020810192835261009b60408301610686565b60408201908152606083015193906001600160a01b038516850361042f576060830194855260808401516001600160401b03811161042f57876100df9186016106d4565b916080840192835260a08501519360a0810194855260c086015160018060401b03811161042f5760e06101178b610125938a016106d4565b9760c0840198895201610679565b60e082019081526020890151989097906001600160a01b038a168a0361042f576040810151906001600160401b03821161042f57018a601f8201121561042f578051906001600160401b0382116104c15760209b8c6040519d8e61018e828760051b0182610656565b858152019360061b8301019181831161042f57602001925b8284106105fa5750505050865151602081116105e35750515f80546001600160a01b0319166001600160a01b0392831617905590511660805251151560a0525164ffffffffff1660c052518051906001600160401b0382116104c15760015490600182811c921680156105d9575b60208310146105c55781601f849311610557575b50602090601f83116001146104f1575f926104e6575b50508160011b915f199060031b1c1916176001555b5160e052516040516102866020828161027581830196878151938492016106b3565b81010301601f198101835282610656565b51905190602081106104d5575b506101005251151561012052610140528051905f5b8281106104335760018060a01b036080511660018060a01b03610140511690604051905f806020840163095ea7b360e01b815285602486015281196044860152604485526102f7606486610656565b84519082855af1610306610719565b816103f8575b50806103ee575b156103a9575b60405161142c908161081a82396080518181816104fa015281816107ff0152610e9b015260a0518181816108290152610d8c015260c05181818161022801528181610d11015281816110050152611197015260e05181818161034c01526106a2015261010051816110760152610120518181816108640152610d5001526101405181818161012e01526109640152f35b6103e16103e6936040519063095ea7b360e01b602083015260248201525f6044820152604481526103db606482610656565b82610748565b610748565b808080610319565b50803b1515610313565b805180159250821561040d575b50508461030c565b819250906020918101031261042f5760206104289101610679565b8480610405565b5f80fd5b81518110156104ad5760208160051b830101519060045491680100000000000000008310156104c15760018301806004558310156104ad5760019260045f5260205f200190838060401b038151166cffffffffff00000000000000006020845493015160401b1691858060681b03191617179055016102a8565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b5f199060200360031b1b165f610293565b015190505f8061023e565b60015f9081528281209350601f198516905b81811061053f5750908460019594939210610527575b505050811b01600155610253565b01515f1960f88460031b161c191690555f8080610519565b92936020600181928786015181550195019301610503565b60015f529091507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6601f840160051c810191602085106105bb575b90601f859493920160051c01905b8181106105ad5750610228565b5f81558493506001016105a0565b9091508190610592565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610214565b63a52d539b60e01b5f52600452602060245260445ffd5b60408483031261042f5760408051919082016001600160401b038111838210176104c1576040528451906001600160401b038216820361042f578260209260409452610647838801610686565b838201528152019301926101a6565b601f909101601f19168101906001600160401b038211908210176104c157604052565b5190811515820361042f57565b519064ffffffffff8216820361042f57565b6001600160401b0381116104c157601f01601f191660200190565b5f5b8381106106c45750505f910152565b81810151838201526020016106b5565b81601f8201121561042f5780516106ea81610698565b926106f86040519485610656565b8184526020828401011161042f5761071691602080850191016106b3565b90565b3d15610743573d9061072a82610698565b916107386040519384610656565b82523d5f602084013e565b606090565b5f806107709260018060a01b03169360208151910182865af1610769610719565b90836107bb565b8051908115159182610798575b50506107865750565b635274afe760e01b5f5260045260245ffd5b819250906020918101031261042f5760206107b39101610679565b155f8061077d565b906107df57508051156107d057805190602001fd5b630a12f52160e11b5f5260045ffd5b81511580610810575b6107f0575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156107e856fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde0314611060575080631686c90914610db157806316c3549d14610d755780631bfd681414610d395780633f31ae3f1461051e5780634800d97f146104ce57806349fc73dd146103935780634e390d3e1461036f57806351e75e8b1461033557806375829def1461026657806390e64d131461024c578063bb4b57341461020b578063bf4ed03f14610192578063ce51650714610152578063da792468146101025763f851a440146100cc575f80fd5b346100fe575f6003193601126100fe57602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b5f80fd5b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe5760206003193601126100fe57602061018860043560ff6001918060081c5f526002602052161b60405f205416151590565b6040519015158152f35b346100fe575f6003193601126100fe576101aa6111e4565b6040518091602082016020835281518091526020604084019201905f5b8181106101d5575050500390f35b8251805167ffffffffffffffff16855260209081015164ffffffffff1681860152869550604090940193909201916001016101c7565b346100fe575f6003193601126100fe57602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe57602061018861118f565b346100fe5760206003193601126100fe5761027f61110f565b5f5473ffffffffffffffffffffffffffffffffffffffff8116338103610306575073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffff0000000000000000000000000000000000000000921691829116175f55337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf805f80a3005b7fc6cce6a4000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b346100fe575f6003193601126100fe5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100fe575f6003193601126100fe57602064ffffffffff60035416604051908152f35b346100fe575f6003193601126100fe576040515f6001548060011c906001811680156104c4575b6020831081146104975782855290811561045557506001146103f7575b6103f3836103e78185038261114e565b604051918291826110a9565b0390f35b91905060015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6915f905b80821061043b575090915081016020016103e76103d7565b919260018160209254838588010152019101909291610423565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b840190910191506103e790506103d7565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f16916103ba565b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe5760806003193601126100fe576004356024359073ffffffffffffffffffffffffffffffffffffffff82168092036100fe57604435916fffffffffffffffffffffffffffffffff8316918284036100fe576064359367ffffffffffffffff85116100fe57366023860112156100fe5784600401359467ffffffffffffffff86116100fe5760248660051b8201013681116100fe576040516020810190858252866040820152876060820152606081526105dc60808261114e565b51902060405160208101918252602081526105f860408261114e565b5190209161060461118f565b610ce2576106298560ff6001918060081c5f526002602052161b60405f205416151590565b610cb657610636886111cc565b97610644604051998a61114e565b8852602401602088015b828210610ca657505050925f935b865185101561069e5761066f8588611278565b51908181101561068d575f52602052600160405f205b94019361065c565b905f52602052600160405f20610685565b85907f000000000000000000000000000000000000000000000000000000000000000003610c7e5760035464ffffffffff811615610c4a575b506106e06111e4565b925f84516106ed816111cc565b956106fb604051978861114e565b8187527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610728836111cc565b015f5b818110610c275750505f905b828210610b1e5750506fffffffffffffffffffffffffffffffff8216848111610af1578411610a9e575b5050508360081c5f52600260205260405f20600160ff86161b815417905573ffffffffffffffffffffffffffffffffffffffff5f5416926040516107a481611132565b5f81525f60208201526040519161010083019583871067ffffffffffffffff881117610a715773ffffffffffffffffffffffffffffffffffffffff9660409492939452815260208101918583526040820185815260608301887f00000000000000000000000000000000000000000000000000000000000000001681528860808501917f0000000000000000000000000000000000000000000000000000000000000000151583526fffffffffffffffffffffffffffffffff60a08701947f00000000000000000000000000000000000000000000000000000000000000001515865260c0880196875260e08801998a526040519c8d997f897f362b000000000000000000000000000000000000000000000000000000008b52602060048c0152816101448c019a511660248c0152511660448a0152511660648801525116608486015251151560a485015251151560c4840152519061012060e48401528151809152602061016484019201905f5b818110610a33575050508190602080945173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015203815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1928315610a28575f936109d0575b50907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d60406020958594825191825287820152a3604051908152f35b939250906020843d602011610a20575b816109ed6020938361114e565b810103126100fe5792519192907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d610994565b3d91506109e0565b6040513d5f823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff168186015289955060409094019390920191600101610913565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6fffffffffffffffffffffffffffffffff91610add7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84930188611278565b519303168183511601169052848080610761565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b9092610b3f67ffffffffffffffff610b368685611278565b515116876112b9565b6fffffffffffffffffffffffffffffffff8111610bfc576fffffffffffffffffffffffffffffffff8091169164ffffffffff6020610b7d8887611278565b5101511660405190610b8e82611132565b8482526020820152610ba0878c611278565b52610bab868b611278565b5016016fffffffffffffffffffffffffffffffff8111610bcf579260010190610737565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f4916adce000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b602090604051610c3681611132565b5f81525f8382015282828c0101520161072b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff1617600355846106d7565b7f0fa7d73c000000000000000000000000000000000000000000000000000000005f5260045ffd5b813581526020918201910161064e565b847f712b37a3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f442b1841000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445ffd5b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe5760406003193601126100fe57610dca61110f565b6024356fffffffffffffffffffffffffffffffff81168091036100fe5773ffffffffffffffffffffffffffffffffffffffff5f5416338103610306575064ffffffffff600354168015158061103f575b80611030575b610fd657506040515f8073ffffffffffffffffffffffffffffffffffffffff60208401957fa9059cbb000000000000000000000000000000000000000000000000000000008752169485602485015284604485015260448452610e8460648561114e565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610fca573d67ffffffffffffffff8111610a7157610f229160405191610f1260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116018461114e565b82523d5f602084013e5b83611386565b8051908115159182610fa6575b5050610f7b57507f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f602073ffffffffffffffffffffffffffffffffffffffff5f541692604051908152a3005b7f5274afe7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b81925090602091810103126100fe57602001518015908115036100fe578480610f2f565b610f2290606090610f1c565b7f92b66697000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445260645ffd5b5061103961118f565b15610e20565b5062093a80810164ffffffffff8111610bcf5764ffffffffff164211610e1a565b346100fe575f6003193601126100fe576103f3907f00000000000000000000000000000000000000000000000000000000000000006020820152602081526103e760408261114e565b919091602081528251928360208301525f5b8481106110f95750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b80602080928401015160408286010152016110bb565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100fe57565b6040810190811067ffffffffffffffff821117610a7157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610a7157604052565b64ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001680151590816111c4575090565b905042101590565b67ffffffffffffffff8111610a715760051b60200190565b600454906111f1826111cc565b916111ff604051938461114e565b80835260045f9081527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b602085015b83831061123b5750505050565b60016020819260405161124d81611132565b64ffffffffff865467ffffffffffffffff8116835260401c168382015281520192019201919061122e565b805182101561128c5760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8382098382029182808310920391808303921461137557670de0b6b3a7640000821015611345577faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106699394670de0b6b3a7640000910990828211900360ee1b910360121c170290565b84907f5173648d000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b5050670de0b6b3a764000090049150565b906113c3575080511561139b57805190602001fd5b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b81511580611416575b6113d4575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b156113cc56fea164736f6c634300081a000aa164736f6c634300081a000a"; /*////////////////////////////////////////////////////////////////////////// DEPLOYERS From 9ea29b29f70d5fdb5edcc79c9c2230f5bd25a350 Mon Sep 17 00:00:00 2001 From: Paul Razvan Berg Date: Mon, 17 Jun 2024 16:07:32 +0300 Subject: [PATCH 53/61] build: update bun lockfile --- bun.lockb | Bin 42555 -> 42927 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bun.lockb b/bun.lockb index 7ca32db28539985a3b3e17ad2c81737a98b09e38..b97f471a2c8a443b47a04d5be43b7783288c36c1 100755 GIT binary patch delta 983 zcmYk*PiT^H9LI4V-E?lvI+R^ZbBH@kO_x2nYNq|Mf9t6XG!TSY3c;}XxIOM95o(tZ z;|H>n?BqIG@qn-bi5+A?7z<%dJw)4epYpSrWtb5@ng#DX!4Y_q-5 zu*%{Tr;*Rt*koj@OHYn*Dix<19Usw~S;K1_#|BGL#gK!n!66i!RO)a9BQ7cps69t> z95$iBO(g|e(CDF(hHYqGrXs-(w4bMvhh6CMQYpb6#k;1CKv zDs?!5krgTpsJ%pU95$ihGL;l;LE{xFY1oG5RVotfK)atx9(JMYDwPuKK|VmG4ExX@ zq*8?g7`jHK28U3%PNfboZGN;(5n0t=f6vuyzFU!1{0Lx^K^x1K7Y%_jqL4GEF z<@DR*w3CzhEnaQ>t1OL@)XMWNH#sXZNwUj((yMv4nGt1NDMKGw6*nYt783!^-r(0O<^%ihN&Utq*sTAG!__1 z&6^M=#F*k>f|@sBF(D30G->F@$jC^X7%$%Q(4jYc-rxJpo68OFeLc4SIIHa6lsq}ZlaWE zpGH-b3LQ}G7D|npG&YA)r_OoI3EH6E1(YOhQr~Tq6m3!e9TbDMY49#emUd`3f>NMe zs@_8>(jJX0qLgT#M(?9k=zwYuP-@hqv4<#i>U@MbK^xTj7$r%Y)E7lb(H8YTK{05X z2A5E>v_r#BQ3|w6)n_P0+M|)@C?(pb(HAHcI-uGzN{yN{rlHiSa|LsPHmG+MB}tpq z_Yx&VThzZ6KYdZt)AHeEsIH;?4}yrXkgB)$XYp!+jtp-t7b()<%Bb*+1*q%$=OeSuYzGrF_U! UK1rVr#Zf*W|K-OnS)Ww?4>6YgivR!s From 46ca3bc1253057d9fefa0646e5d48d01d0319375 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Tue, 18 Jun 2024 21:09:20 +0100 Subject: [PATCH 54/61] feat: use CREATE2 to deploy merkle lockup campaigns (#355) * feat: use create2 to deploy merkle lockup campaigns * test: computeMerkleLL and computeMerkleLT * build: update bun lockfile * chore: update precompiles * refactor: remove compute functions * refactor: calculate "totalPercentage" in MerkleLT refactor: remove total percentage from salt * feat: include msg.sender in create2 salt * chore: update precompiles * refactor: add tranchesWithPercentages to create2 salt * chore: update bun lockfile * build: update precompiles * docs: use contract state --------- Co-authored-by: Paul Razvan Berg --- bun.lockb | Bin 42927 -> 42555 bytes precompiles/Precompiles.sol | 2 +- src/SablierV2MerkleLT.sol | 20 ++- src/SablierV2MerkleLockupFactory.sol | 50 +++++-- src/interfaces/ISablierV2MerkleLT.sol | 4 + .../ISablierV2MerkleLockupFactory.sol | 5 +- src/libraries/Errors.sol | 6 +- test/Base.t.sol | 140 +++++++++++++++++- test/fork/merkle-lockup/MerkleLL.t.sol | 2 +- test/fork/merkle-lockup/MerkleLT.t.sol | 2 +- .../merkle-lockup/MerkleLockup.t.sol | 38 ++++- .../create-merkle-ll/createMerkleLL.t.sol | 36 ++++- .../create-merkle-ll/createMerkleLL.tree | 7 +- .../create-merkle-lt/createMerkleLT.t.sol | 61 ++------ .../create-merkle-lt/createMerkleLT.tree | 15 +- .../merkle-lockup/lt/claim/claim.t.sol | 75 +++++++++- .../merkle-lockup/lt/claim/claim.tree | 57 +++---- .../lt/constructor/constructor.t.sol | 6 + test/utils/Defaults.sol | 3 +- 19 files changed, 405 insertions(+), 124 deletions(-) diff --git a/bun.lockb b/bun.lockb index b97f471a2c8a443b47a04d5be43b7783288c36c1..42d19a44cf6d59680d13267c2c2a7359e2063446 100755 GIT binary patch delta 2829 zcmc&$X;hO}8ved)57aEGO-RQd{XgHylQ+-%-sQP( z?sxBd_zTgQ%cAw_)SR*1gJ+~_&b%B}+3Z?WzppjGI`;8rJ0<;0hUR-0SADd9H|Kq9 z8_e;dw5Fp=s`-{}3XU7VajxgEp|J13_J?%`a~wV{CwLfaO`lDK4FcD}viOM*j>FHN z+t*r`E@G4PVz7Sj??OBRHVBOZVLQO1VW)$mNwUl6`{7c<;^TT6?wjBttN=a`&LCIjU_q*Yt&s`+)6DmEXJ@V>++X}Huk(+b!? zY8U5-BBJTE_;pW|WDai#Q9e*>I?Z{R;$J&9)LFJDn%;eF(4Do<&b-_oRy5+nsyX3r zB^}!6Y}*#0um!)h`nNexf0%KY-y87xpvFHx(JM#ORcXrLltG)iu{ar;lQWw$5MVAPsV(>O$9VGZud>t4o0UL#w z+&lD|zLk!wZQ!szrsy^w}KmdOq9 z3bcXcgGp&+s8)0!oSgmjf*3oHN#bc`f35Hun6C*Nb9rA(%({D?MTScG0Aytt%Su=~ zwgS@|fNc;3{4eqtk@>$A5vC3DiR8U&+yurZ^R7|20+tOXqGBX0F`Dw@^um1Xr#v{l z3ixewDy~R)2%j$tP%O%bq5OEgurTK5krj{{pC|D&4t4OwJd{lY*z>Aos$U0K+zhbi zmn=``QX}bds{B>(BIdkZ$?5lhepF;^cGj$Xy zXsh1DYv_B#^7INCUu5F7)Kz4rdtg0aBk5LwnL3LURADgjI@)0{Q;9)AA7j2k`Vyl& z1Je|k_##?~mV1j8)UGof8}&eDkp4d4dXiDnU4K4N`L$-&EJ;W9qpq9fU!K)YNSK;^ z+BiNnJ?p0*YJv<4dV_W14E@+$WBPv=cJ}U$uK#TBJ-}@ul`*O%EZw;0Q1GdBS(ayG zE0%t{hBJ<-n|*wD&&ErugO~2Ebm{`0yqV&Pd9os<<*61#w7%b9PzQ@rKY#N8IJHaie+n=?_gS46c8k zdh?R_h0;0bY{|`@dv{isbq7ml{VOfH-SXMcSiAcg{nZ#nV~i$#I(=_Mib|2bQWO6M zb(JDTCZrE+CK*jg5!gYKiJwhBf$c3*(9AN^vGG@>^hbFTed0Z;B%WncF39PUJOvJc zS&w^0$$)-*V-wShSII_MHUl|q<*AK654Ql8y#zR*Ia8mHo$Smf1Dfc8txT}_A{h?K zZBF#evzPHn%;f;40n-8Yc1;2%15<#hz3}RC6Ho&g02_NafU|{T{cwPAxqt>3 z0kAPxe=Wc?>FL}evx=>Qod~}I*m@Xe%4Y#Hfj58|z-(X+6)#Q?bbD`KT##+FYH_j{ ze$PjXPsujA21a{tQk+}32%28*kp)!#yEy`&B4P2Iy?Aeu^kd1h@I!m3E$>~Vd zqU4Py-M`xVif_&7COXSx(r$=Py7e-I>%fS~&R}{WUBd9QQPwj-J3nvfeeWQ~0yA-|TIT9rzF@#4Sd5!b4-N2s;*c58Hu zzf^g5$FYi@DE(t3qeD(l!9CJn}^>rk*6t}3#hzv2d(dlrK(N$Y1F19&%v(WNaM9?N0qa& zrm~^3p{~JZH_Wxnwbl)Acz^-yM)LIwXY2waAxQCE=ryl9Py&;~~ VZ!hqSbPp7HKHfQ2LOZ5({ukN-3IG5A delta 2982 zcmd5;YgAKL7QXjF5&{IGMF|f<7(fsbNq8jk2+5Qx5=6mbEY#WrB0&Rr+z=2SAcA5o zE^%AMiU>2q$f!k~>#DTsOl6ijj^m>)we?X)mz^19jaD3OJKE9NCnWtbYfXR8UHP)l zx6eN3?6c2nANwho zmvVn>mp+=A<~ZDFrknSQC@PSmS~^h%q1@%+EhzEVGJ;A{{|QPVN*5~qQJPRrKv|0N zHI$L!@%CDe{sD3<{-|GWTHzvtAHYTdsK1K!ArzI+@+lS~P$5TaEVe|VoP;tNdnG)O z=%Jn2uUHv2KJ;>wq$WOm+@(sh!Ll5?9zss0U^cMQr4~c=hvBfyCyM8T>R4#^Nn?h> z;ZvW5yi~Lmb0XhLdQsYUqi-Li2L-3XZC^Z7hvgft-M!3Tb+@D_C4u{8LX0A$E>Mxy z|GlZ?UP*JlaQJ!R*zIrF(8ovLowy->XlqlO$iDgY@O$rnf9!|-6Y|o3RoxpRas+Ky z|1limB_^($FN|&MZP>qd*PR_trr&-M9QII_(XCgd`gA(GEQTG`S?b~|><4|{z?XcD-{0mp`OGcbc@!jq4!_I|+81M6E~jvPf-Ams5cCU@ zxxTVF9un zWPCX6rxdm!D@4Y?Sb$P^0a>xT&hD=iPRE$%Bl7|IL?u%vhSAs5!f#Reb5#1do2zhf ziHR>@17_6(m_A7@Jc>%SyB!(f8M66q6C9m+VZ6JBTfl$p(S7!C%bKAxDQBk zw{t=>GKITqd7zRxj$0*4%_AF8MPf*+;6YTLumkzH>lirZbObDmRtx8&HAthmurrWtuPT!%c~6`2TV!H@T0m>ED!5qAULHLbs4g`r(18xi(?+|I+`*3-2Y%gu<_C_ z1`0#CDT+mY(4M2CrSN%<9wgZ!DAVfb8KBkZ;g84$G&))iW5^nFMDS3pqtoDQwjL65 zMG&8@qcgyc>`P=)>_rJlxq9eOi=Z=CN6&=o$W(bE$i;Tquu-jt`^etN)6u!mm8XYp zjR@+qbaWn^%+f=FRs_%TbX=eIAzxF~UC#DbP58>uR@)N!V2|BCY#$BSeuH_l`}|*5 zPF7wrt_n&1*U7WB<|7*p3YLDodDg+B<(FL-3v8j4v#$QG$_D2PB=Er;-CVtA?TPTc zzVT63uYj4m_E7lQgl`$krrBqTt{oo92v~Keyy)d;IqRNoJ{?p3m!9MMXRpsFi$1pZ z?=jTuB{IDU%2Zply!vS*ut`w`;&|Uyx+=#91K1g^(hMQ{({GBN`AZ z5RHf?#3G`^ud#v4bWW!iYp{&B36+b;M5H6q5O@tyGZ3Ud86w3~mZMBXWFYWzbK7O3 zoQY5&ln7#rml~A^C+hW5vN4F??;^;Yh}VQKM!bzEM^qr*K`a5SD|zxO{Ctp+3u%x{ z0Kdj|aJdq=KG!f`F2<|P`%W?RI~g*C3|;cR(cTx$U6U$9hw)&o4x_aYuqKQyg}gOk z5#B_>`(&c6LME5tq+M_l>%EDF_sR9t%TuXNIDvib0_*Cq2ydF=eX!9gO_oZ0aa%=t z(-ZGQ?e<7cJQ@jt1I-d9QVg5dNFu!1hWBkoyL9Zw>kV^N47b)Oh_$#`((FxQ9{ExS z&OFi&p|xs!)c@^|H^He*iZbroW}L^+#8I-A-i*fk{$p<#O;TlWRND*=*2Dxqy1H=W z_GwQIce6Q|0pHdTpG;~c%xet`bLTR+B;LdT4V5xQx-1jIS|cL73DNz*_kyooLK|HmF0x))q+v9P`E zGGCsdkf)`=$xRX5#&#`_PUp_Ocby-vVQcED946N4FxRs)YFG{1RBLclHW`-BFgu+# zsk5fmgr~bJW=@r<(pqanQ&x(9HftSgT55IFN~;V`gVa=4X{|Cjs!dKQ>vWh5wWP5^ z+TbwQY$k`r+F){28d#GQ6G;|^x+3A!rch|u)&{BF@zAp^0+#If916GR0NydcgFg5M zI=C0x=FzZzdq0@B?uPgs(Og;gZ+KCeGG}#@#%M5_EHlhC)n*IAX{)KUR$47q2XysB zLcpFJQ1!<0+@@gejTJ0orB3T|6Kv_3!p+_@nSp`cS#&0hZI0#^_s{Z;WDQ13jmaUk Z7>umcrI1!y9VV$sWt^Fsj)$VO`#<;OK9v9f diff --git a/precompiles/Precompiles.sol b/precompiles/Precompiles.sol index 025a9abc..abd20a78 100644 --- a/precompiles/Precompiles.sol +++ b/precompiles/Precompiles.sol @@ -19,7 +19,7 @@ contract Precompiles { bytes public constant BYTECODE_BATCH_LOCKUP = hex"60808060405234601557611e0a908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806337266dd3146111a357806349a32c4014610e3e578063606ef87514610b025780639e743f29146107a6578063a514f83e1461040c5763f7ca34eb1461005b575f80fd5b3461033c5761006936611512565b91909282156103e4575f905f5b8481106103b057506001600160a01b036100939116918383611a7a565b61009c83611758565b926001600160a01b035f9316925b8181106100c357604051806100bf8782611587565b0390f35b6100ce818388611a17565b6100d7906117a7565b90826100e482828a611a17565b6020016100f0906117a7565b6100fb83838b611a17565b60400161010790611643565b9389610114858583611a17565b606001610120906117bb565b61012b868684611a17565b608001610137906117bb565b610142878785611a17565b60a00161014e90611a57565b918761015b818987611a17565b60c0810161016891611926565b98610174929196611a17565b60e0019360405195610185876116c6565b6001600160a01b0316865260208601966001600160a01b0316875260408601996fffffffffffffffffffffffffffffffff168a5260608601978d895260808701921515835260a08701931515845260c087019464ffffffffff16855236906101ec9261197a565b9360e08601948552366101fe916118c8565b966101008601978852604051998a977f31df3d480000000000000000000000000000000000000000000000000000000089526004890160209052610164890197516001600160a01b031660248a0152516001600160a01b03166044890152516fffffffffffffffffffffffffffffffff166064880152516001600160a01b0316608487015251151560a486015251151560c48501525164ffffffffff1660e48401525190610104830161014090528151809152610184830191602001905f905b808210610353575050925180516001600160a01b03166101248401526020908101516101448401529250819003815f885af18015610348575f90610312575b6001925061030b8288611901565b52016100aa565b506020823d8211610340575b8161032b602093836116ff565b8101031261033c57600191516102fd565b5f80fd5b3d915061031e565b6040513d5f823e3d90fd5b919493509160206060826103a1600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b019501920186939492916102be565b916001906fffffffffffffffffffffffffffffffff6103db60406103d5878a8c611a17565b01611643565b16019201610076565b7ff8bf106c000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461033c5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033c576104436115c0565b61044b6114cb565b906044359167ffffffffffffffff831161033c573660238401121561033c5782600401359267ffffffffffffffff841161033c57602481019060243691610140870201011161033c5783156103e45790915f9190825b85811061077457506001600160a01b036104be9116928484611a7a565b6104c784611758565b926001600160a01b03165f5b8581106104e857604051806100bf8782611587565b6104fb6104f6828886611a69565b6117a7565b90610512602061050c838a88611a69565b016117a7565b878561052460406103d5868585611a69565b61053a6060610534878686611a69565b016117bb565b906101006105648761055d8188610557608061053484848d611a69565b98611a69565b968c611a69565b01906001600160a01b036040519861057b8a611660565b1688526001600160a01b0360208901961686526fffffffffffffffffffffffffffffffff6040890191168152606088019189835260808901931515845260a08901941515855260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60873603011261033c57604051956105fa876116e3565b61060660a08201611839565b875261061460c08201611839565b602088015260e00161062590611839565b604087015260c089019586523661063b916118c8565b9560e08901968752604051987f53b15727000000000000000000000000000000000000000000000000000000008a52516001600160a01b031660048a0152516001600160a01b03166024890152516fffffffffffffffffffffffffffffffff166044880152516001600160a01b03166064870152511515608486015251151560a485015251805164ffffffffff1660c4850152602081015164ffffffffff1660e48501526040015164ffffffffff1661010484015251610124830161071291602080916001600160a01b0381511684520151910152565b8180865a925f61016492602095f18015610348575f90610742575b6001925061073b8288611901565b52016104d3565b506020823d821161076c575b8161075b602093836116ff565b8101031261033c576001915161072d565b3d915061074e565b93926001906fffffffffffffffffffffffffffffffff61079a60406103d5898b89611a69565b160194019392936104a1565b3461033c576107b436611512565b91909282156103e4575f905f5b848110610ad457506001600160a01b036107de9116918383611a7a565b6107e783611758565b926001600160a01b035f9316925b81811061080a57604051806100bf8782611587565b610815818388611a17565b61081e906117a7565b908261082b82828a611a17565b602001610837906117a7565b61084283838b611a17565b60400161084e90611643565b938961085b858583611a17565b606001610867906117bb565b610872868684611a17565b60800161087e906117bb565b610889878785611a17565b60a00161089590611a57565b91876108a2818987611a17565b60c081016108af916117c8565b986108bb929196611a17565b60e00193604051956108cc876116c6565b6001600160a01b0316865260208601966001600160a01b0316875260408601996fffffffffffffffffffffffffffffffff168a5260608601978d895260808701921515835260a08701931515845260c087019464ffffffffff16855236906109339261184b565b9360e0860194855236610945916118c8565b966101008601978852604051998a977f32fbe22b0000000000000000000000000000000000000000000000000000000089526004890160209052610164890197516001600160a01b031660248a0152516001600160a01b03166044890152516fffffffffffffffffffffffffffffffff166064880152516001600160a01b0316608487015251151560a486015251151560c48501525164ffffffffff1660e48401525190610104830161014090528151809152610184830191602001905f905b808210610a8b575050925180516001600160a01b03166101248401526020908101516101448401529250819003815f885af18015610348575f90610a59575b60019250610a528288611901565b52016107f5565b506020823d8211610a83575b81610a72602093836116ff565b8101031261033c5760019151610a44565b3d9150610a65565b91949350916020604082610ac5600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291610a05565b916001906fffffffffffffffffffffffffffffffff610af960406103d5878a8c611a17565b160192016107c1565b3461033c57610b1036611512565b91909282156103e4575f905f5b848110610e1057506001600160a01b03610b3a9116918383611a7a565b610b4383611758565b926001600160a01b035f9316925b818110610b6657604051806100bf8782611587565b610b718183886115d6565b610b7a906117a7565b9082610b8782828a6115d6565b602001610b93906117a7565b610b9e83838b6115d6565b604001610baa90611643565b9389610bb78585836115d6565b606001610bc3906117bb565b610bce8686846115d6565b608001610bda906117bb565b9086610be78188866115d6565b60a08101610bf491611926565b97610c009291956115d6565b60c0019260405194610c1186611660565b6001600160a01b0316855260208501956001600160a01b0316865260408501986fffffffffffffffffffffffffffffffff16895260608501968c885260808601921515835260a0860193151584523690610c6a9261197a565b9260c0850193845236610c7c916118c8565b9560e085019687526040519889967f54c022920000000000000000000000000000000000000000000000000000000088526004880160209052610144880196516001600160a01b03166024890152516001600160a01b03166044880152516fffffffffffffffffffffffffffffffff166064870152516001600160a01b0316608486015251151560a485015251151560c4840152519060e4830161012090528151809152610164830191602001905f905b808210610db3575050925180516001600160a01b03166101048401526020908101516101248401529250819003815f885af18015610348575f90610d81575b60019250610d7a8288611901565b5201610b51565b506020823d8211610dab575b81610d9a602093836116ff565b8101031261033c5760019151610d6c565b3d9150610d8d565b91949350916020606082610e01600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b01950192018693949291610d2d565b916001906fffffffffffffffffffffffffffffffff610e3560406103d5878a8c6115d6565b16019201610b1d565b3461033c5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033c57610e756115c0565b610e7d6114cb565b906044359167ffffffffffffffff831161033c573660238401121561033c5782600401359267ffffffffffffffff841161033c57602481019060243691610120870201011161033c5783156103e45790915f9190825b85811061117157506001600160a01b03610ef09116928484611a7a565b610ef984611758565b926001600160a01b03165f5b858110610f1a57604051806100bf8782611587565b610f286104f6828886611915565b90610f39602061050c838a88611915565b8785610f4b60406103d5868585611915565b610f5b6060610534878686611915565b9060e0610f8487610f7d8188610f77608061053484848d611915565b98611915565b968c611915565b01906001600160a01b0360405198610f9b8a611660565b1688526001600160a01b0360208901961686526fffffffffffffffffffffffffffffffff6040890191168152606088019189835260808901931515845260a08901941515855260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60873603011261033c576040519561101a876116aa565b61102660a08201611839565b875260c00161103490611839565b602087015260c089019586523661104a916118c8565b9560e08901968752604051987fab167ccc000000000000000000000000000000000000000000000000000000008a52516001600160a01b031660048a0152516001600160a01b03166024890152516fffffffffffffffffffffffffffffffff166044880152516001600160a01b03166064870152511515608486015251151560a485015251805164ffffffffff1660c48501526020015164ffffffffff1660e484015251610104830161110f91602080916001600160a01b0381511684520151910152565b8180865a925f61014492602095f18015610348575f9061113f575b600192506111388288611901565b5201610f05565b506020823d8211611169575b81611158602093836116ff565b8101031261033c576001915161112a565b3d915061114b565b93926001906fffffffffffffffffffffffffffffffff61119760406103d5898b89611915565b16019401939293610ed3565b3461033c576111b136611512565b91909282156103e4575f905f5b84811061149d57506001600160a01b036111db9116918383611a7a565b6111e483611758565b926001600160a01b035f9316925b81811061120757604051806100bf8782611587565b6112128183886115d6565b61121b906117a7565b908261122882828a6115d6565b602001611234906117a7565b61123f83838b6115d6565b60400161124b90611643565b93896112588585836115d6565b606001611264906117bb565b61126f8686846115d6565b60800161127b906117bb565b90866112888188866115d6565b60a08101611295916117c8565b976112a19291956115d6565b60c00192604051946112b286611660565b6001600160a01b0316855260208501956001600160a01b0316865260408501986fffffffffffffffffffffffffffffffff16895260608501968c885260808601921515835260a086019315158452369061130b9261184b565b9260c085019384523661131d916118c8565b9560e085019687526040519889967f897f362b0000000000000000000000000000000000000000000000000000000088526004880160209052610144880196516001600160a01b03166024890152516001600160a01b03166044880152516fffffffffffffffffffffffffffffffff166064870152516001600160a01b0316608486015251151560a485015251151560c4840152519060e4830161012090528151809152610164830191602001905f905b808210611454575050925180516001600160a01b03166101048401526020908101516101248401529250819003815f885af18015610348575f90611422575b6001925061141b8288611901565b52016111f2565b506020823d821161144c575b8161143b602093836116ff565b8101031261033c576001915161140d565b3d915061142e565b9194935091602060408261148e600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b019501920186939492916113ce565b916001906fffffffffffffffffffffffffffffffff6114c260406103d5878a8c6115d6565b160192016111be565b602435906001600160a01b038216820361033c57565b9181601f8401121561033c5782359167ffffffffffffffff831161033c576020808501948460051b01011161033c57565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82011261033c576004356001600160a01b038116810361033c57916024356001600160a01b038116810361033c57916044359067ffffffffffffffff821161033c57611583916004016114e1565b9091565b60206040818301928281528451809452019201905f5b8181106115aa5750505090565b825184526020938401939092019160010161159d565b600435906001600160a01b038216820361033c57565b91908110156116165760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018136030182121561033c570190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b356fffffffffffffffffffffffffffffffff8116810361033c5790565b610100810190811067ffffffffffffffff82111761167d57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff82111761167d57604052565b610120810190811067ffffffffffffffff82111761167d57604052565b6060810190811067ffffffffffffffff82111761167d57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761167d57604052565b67ffffffffffffffff811161167d5760051b60200190565b9061176282611740565b61176f60405191826116ff565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061179d8294611740565b0190602036910137565b356001600160a01b038116810361033c5790565b35801515810361033c5790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561033c570180359067ffffffffffffffff821161033c57602001918160061b3603831361033c57565b35906fffffffffffffffffffffffffffffffff8216820361033c57565b359064ffffffffff8216820361033c57565b92919261185782611740565b9361186560405195866116ff565b602085848152019260061b82019181831161033c57925b8284106118895750505050565b60408483031261033c57602060409182516118a3816116aa565b6118ac8761181c565b81526118b9838801611839565b8382015281520193019261187c565b919082604091031261033c576040516118e0816116aa565b809280356001600160a01b038116810361033c578252602090810135910152565b80518210156116165760209160051b010190565b919081101561161657610120020190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561033c570180359067ffffffffffffffff821161033c5760200191606082023603831361033c57565b92919261198682611740565b9361199460405195866116ff565b606060208685815201930282019181831161033c57925b8284106119b85750505050565b60608483031261033c57604051906119cf826116e3565b6119d88561181c565b825260208501359067ffffffffffffffff8216820361033c5782602092836060950152611a0760408801611839565b60408201528152019301926119ab565b91908110156116165760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18136030182121561033c570190565b3564ffffffffff8116810361033c5790565b919081101561161657610140020190565b9190611acf6040517f23b872dd00000000000000000000000000000000000000000000000000000000602082015233602482015230604482015283606482015260648152611ac96084826116ff565b82611c8f565b6001600160a01b0381166001600160a01b03604051947fdd62ed3e0000000000000000000000000000000000000000000000000000000086523060048701521693846024820152602081604481855afa80156103485784915f91611c42575b5010611b3b575b50505050565b5f806040519460208601907f095ea7b3000000000000000000000000000000000000000000000000000000008252876024880152604487015260448652611b836064876116ff565b85519082855af190611b93611d14565b82611c10575b5081611c05575b5015611bad575b80611b35565b611bf8611bfd93604051907f095ea7b300000000000000000000000000000000000000000000000000000000602083015260248201525f604482015260448152611ac96064826116ff565b611c8f565b5f8080611ba7565b90503b15155f611ba0565b80519192508115918215611c28575b5050905f611b99565b611c3b9250602080918301019101611c77565b5f80611c1f565b9150506020813d602011611c6f575b81611c5e602093836116ff565b8101031261033c578390515f611b2e565b3d9150611c51565b9081602091031261033c5751801515810361033c5790565b5f806001600160a01b03611cb893169360208151910182865af1611cb1611d14565b9083611d71565b8051908115159182611cf9575b5050611cce5750565b7f5274afe7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b611d0c9250602080918301019101611c77565b155f80611cc5565b3d15611d6c573d9067ffffffffffffffff821161167d5760405191611d6160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846116ff565b82523d5f602084013e565b606090565b90611dae5750805115611d8657805190602001fd5b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b81511580611df4575b611dbf575090565b6001600160a01b03907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b15611db756fea164736f6c634300081a000a"; bytes public constant BYTECODE_MERKLE_LOCKUP_FACTORY = - hex"60808060405234601557613c28908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c80631e323876146102325763769bed201461002f575f80fd5b3461022e5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022e5760043567ffffffffffffffff811161022e5761007e9036906004016105fe565b6024359073ffffffffffffffffffffffffffffffffffffffff821680920361022e5760407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc36011261022e576100d261050b565b60443564ffffffffff8116810361022e57815260643564ffffffffff8116810361022e57602082015260405161174880820182811067ffffffffffffffff82111761020157829161088e8339608081526101556040610134608084018861079d565b92886020820152018564ffffffffff60208092828151168552015116910152565b03905ff09283156101f6576101b96101db937f2ba0fe49588281dbb122dd3b7f3e2b3396338f70dbe3c62bf3e3888b4ba7ffb89273ffffffffffffffffffffffffffffffffffffffff6020971695869560405194859460c0865260c086019061079d565b9289850152604084019064ffffffffff60208092828151168552015116910152565b608435608083015260a43560a08301520390a2604051908152f35b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f80fd5b3461022e5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261022e5760043567ffffffffffffffff811161022e576102819036906004016105fe565b60243573ffffffffffffffffffffffffffffffffffffffff811680910361022e576044359167ffffffffffffffff831161022e573660238401121561022e57826004013567ffffffffffffffff8111610201576102e360208260051b0161052b565b936024602086848152019260061b8201019036821161022e57602401915b8183106104c2575050505f905f905f925b85518410156103955767ffffffffffffffff8061032f86896106ff565b51511691160167ffffffffffffffff8111610368576001909264ffffffffff602061035a878a6106ff565b510151160193019291610312565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b67ffffffffffffffff9195935016670de0b6b3a764000081036104975750604051611c468082019082821067ffffffffffffffff83111761020157829161040191611fd68439606081526103ec606082018961079d565b9087602082015260408183039101528561083d565b03905ff080156101f6576020947ffe44018cf74992b2720702385a1728bd329dd136e4f651203176c81c12710a8b9261047673ffffffffffffffffffffffffffffffffffffffff610464941696879660405195869560c0875260c087019061079d565b918a860152848203604086015261083d565b906060830152606435608083015260843560a08301520390a2604051908152f35b7f1b05170a000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b60408336031261022e576104d461050b565b9083359067ffffffffffffffff8216820361022e5782602092604094526104fc83870161057c565b83820152815201920191610301565b604051906040820182811067ffffffffffffffff82111761020157604052565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f604051930116820182811067ffffffffffffffff82111761020157604052565b3590811515820361022e57565b359064ffffffffff8216820361022e57565b81601f8201121561022e5780359067ffffffffffffffff8211610201576105dc60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501160161052b565b928284526020838301011161022e57815f926020809301838601378301015290565b9190916101008184031261022e5760405190610100820182811067ffffffffffffffff821117610201576040528193813573ffffffffffffffffffffffffffffffffffffffff8116810361022e57835261065a6020830161056f565b602084015261066b6040830161057c565b6040840152606082013573ffffffffffffffffffffffffffffffffffffffff8116810361022e576060840152608082013567ffffffffffffffff811161022e57816106b791840161058e565b608084015260a082013560a084015260c08201359067ffffffffffffffff821161022e57826106ef60e094926106fa9486940161058e565b60c08601520161056f565b910152565b80518210156107135760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b91908251928382525f5b8481106107885750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6020809697860101520116010190565b8060208092840101518282860101520161074a565b9073ffffffffffffffffffffffffffffffffffffffff825116815260208201511515602082015264ffffffffff604083015116604082015273ffffffffffffffffffffffffffffffffffffffff606083015116606082015260e08061083261081660808601516101006080870152610100860190610740565b60a086015160a086015260c086015185820360c0870152610740565b930151151591015290565b90602080835192838152019201905f5b81811061085a5750505090565b8251805167ffffffffffffffff16855260209081015164ffffffffff16818601526040909401939092019160010161084d56fe610160806040523461042857611748803803809161001d8285610560565b833981019080820390608082126104285780516001600160401b03811161042857810190610100828503126104285760405161010081016001600160401b038111828210176105355760405282516001600160a01b038116810361042857815261008960208401610583565b6020820190815261009c60408501610590565b60408301908152606085015194906001600160a01b0386168603610428576060840195865260808201516001600160401b03811161042857886100e09184016105de565b6080850190815260a08381015190860190815260c084015190999192916001600160401b0382116104285761011c60e09161012a9387016105de565b9460c0880195865201610583565b9360e0860194855260208701519560018060a01b038716998a880361042857604090603f1901126104285760408051989089016001600160401b0381118a8210176105355761018d9160609160405261018560408201610590565b8b5201610590565b9860208901998a52855151602081116105495750515f80546001600160a01b0319166001600160a01b0392831617905590511660805251151560a0525164ffffffffff1660c0525180519097906001600160401b03811161053557600154600181811c9116801561052b575b602082101461051757601f81116104b4575b506020601f821160011461044857819064ffffffffff98999a5f9261043d575b50508160011b915f199060031b1c1916176001555b5160e052516040516102736020828161026281830196878151938492016105bd565b81010301601f198101835282610560565b519051906020811061042c575b50610100525115156101205261014052511669ffffffffff0000000000600454925160281b169160018060501b031916171760045560018060a01b0360805116604051905f806020840163095ea7b360e01b815285602486015281196044860152604485526102f0606486610560565b84519082855af16102ff610623565b816103f1575b50806103e7575b156103a2575b60405161102490816107248239608051818181610481015281816106d60152610c20015260a0518181816107140152610b11015260c05181818161015f01528181610a6801528181610d8a0152610f49015260e0518181816102d3015261062201526101005181610e2801526101205181818161073e0152610ad50152610140518181816101af01526108870152f35b6103da6103df936040519063095ea7b360e01b602083015260248201525f6044820152604481526103d4606482610560565b82610652565b610652565b5f8080610312565b50803b151561030c565b8051801592508215610406575b50505f610305565b81925090602091810103126104285760206104219101610583565b5f806103fe565b5f80fd5b5f199060200360031b1b165f610280565b015190505f8061022b565b601f1982169960015f52815f209a5f5b81811061049c57509164ffffffffff999a9b91846001959410610484575b505050811b01600155610240565b01515f1960f88460031b161c191690555f8080610476565b838301518d556001909c019b60209384019301610458565b60015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6601f830160051c8101916020841061050d575b601f0160051c01905b818110610502575061020b565b5f81556001016104f5565b90915081906104ec565b634e487b7160e01b5f52602260045260245ffd5b90607f16906101f9565b634e487b7160e01b5f52604160045260245ffd5b63a52d539b60e01b5f52600452602060245260445ffd5b601f909101601f19168101906001600160401b0382119082101761053557604052565b5190811515820361042857565b519064ffffffffff8216820361042857565b6001600160401b03811161053557601f01601f191660200190565b5f5b8381106105ce5750505f910152565b81810151838201526020016105bf565b81601f820112156104285780516105f4816105a2565b926106026040519485610560565b818452602082840101116104285761062091602080850191016105bd565b90565b3d1561064d573d90610634826105a2565b916106426040519384610560565b82523d5f602084013e565b606090565b5f8061067a9260018060a01b03169360208151910182865af1610673610623565b90836106c5565b80519081151591826106a2575b50506106905750565b635274afe760e01b5f5260045260245ffd5b81925090602091810103126104285760206106bd9101610583565b155f80610687565b906106e957508051156106da57805190602001fd5b630a12f52160e11b5f5260045ffd5b8151158061071a575b6106fa575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156106f256fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde0314610e12575080631686c90914610b3657806316c3549d14610afa5780631bfd681414610abe5780633bfe03a814610a905780633f31ae3f146104a55780634800d97f1461045557806349fc73dd1461031a5780634e390d3e146102f657806351e75e8b146102bc57806375829def146101ed57806390e64d13146101d35780639e93e57714610183578063bb4b573414610142578063ce516507146101025763f851a440146100cc575f80fd5b346100fe575f6003193601126100fe57602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b5f80fd5b346100fe5760206003193601126100fe57602061013860043560ff6001918060081c5f526002602052161b60405f205416151590565b6040519015158152f35b346100fe575f6003193601126100fe57602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe576020610138610f41565b346100fe5760206003193601126100fe57610206610ec1565b5f5473ffffffffffffffffffffffffffffffffffffffff811633810361028d575073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffff0000000000000000000000000000000000000000921691829116175f55337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf805f80a3005b7fc6cce6a4000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b346100fe575f6003193601126100fe5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100fe575f6003193601126100fe57602064ffffffffff60035416604051908152f35b346100fe575f6003193601126100fe576040515f6001548060011c9060018116801561044b575b60208310811461041e578285529081156103dc575060011461037e575b61037a8361036e81850382610f00565b60405191829182610e5b565b0390f35b91905060015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6915f905b8082106103c25750909150810160200161036e61035e565b9192600181602092548385880101520191019092916103aa565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b8401909101915061036e905061035e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610341565b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe5760806003193601126100fe5760043560243573ffffffffffffffffffffffffffffffffffffffff81168091036100fe57604435916fffffffffffffffffffffffffffffffff83168093036100fe576064359067ffffffffffffffff82116100fe57366023830112156100fe57816004013567ffffffffffffffff81116100fe578060051b92602484820101903682116100fe57604051602081019085825287604082015288606082015260608152610563608082610f00565b519020604051602081019182526020815261057f604082610f00565b5190209261058b610f41565b610a39576105b08560ff6001918060081c5f526002602052161b60405f205416151590565b610a0d576105c46020604051970187610f00565b8552602401602085015b8282106109fd57505050935f945b835186101561061e5760208660051b85010151908181105f1461060d575f52602052600160405f205b9501946105dc565b905f52602052600160405f20610605565b84907f0000000000000000000000000000000000000000000000000000000000000000036109d55760035464ffffffffff8116156109a1575b508260081c5f52600260205260405f20600160ff85161b815417905573ffffffffffffffffffffffffffffffffffffffff5f54169160405161069881610ee4565b5f81525f602082015260405193610100850185811067ffffffffffffffff8211176109745760405284526020840183815260408501838152606086017f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16815260808701907f00000000000000000000000000000000000000000000000000000000000000001515825260a08801927f0000000000000000000000000000000000000000000000000000000000000000151584526040519461076e86610ee4565b60045464ffffffffff8116875260281c64ffffffffff16602087015260c08a0195865260e08a01968752604051997fab167ccc000000000000000000000000000000000000000000000000000000008b525173ffffffffffffffffffffffffffffffffffffffff1660048b01525173ffffffffffffffffffffffffffffffffffffffff1660248a0152516fffffffffffffffffffffffffffffffff1660448901525173ffffffffffffffffffffffffffffffffffffffff166064880152511515608487015251151560a486015251805164ffffffffff1660c48601526020015164ffffffffff1660e485015251805173ffffffffffffffffffffffffffffffffffffffff166101048501526020015161012484015282807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165a925f61014492602095f1928315610969575f93610911575b50907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d60406020958594825191825287820152a3604051908152f35b939250906020843d602011610961575b8161092e60209383610f00565b810103126100fe5792519192907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d6108d5565b3d9150610921565b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161760035583610657565b7f0fa7d73c000000000000000000000000000000000000000000000000000000005f5260045ffd5b81358152602091820191016105ce565b847f712b37a3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f442b1841000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445ffd5b346100fe575f6003193601126100fe57604060045464ffffffffff825191818116835260281c166020820152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe5760406003193601126100fe57610b4f610ec1565b6024356fffffffffffffffffffffffffffffffff81168091036100fe5773ffffffffffffffffffffffffffffffffffffffff5f541633810361028d575064ffffffffff6003541680151580610dc4575b80610db5575b610d5b57506040515f8073ffffffffffffffffffffffffffffffffffffffff60208401957fa9059cbb000000000000000000000000000000000000000000000000000000008752169485602485015284604485015260448452610c09606485610f00565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610d4f573d67ffffffffffffffff811161097457610ca79160405191610c9760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610f00565b82523d5f602084013e5b83610f7e565b8051908115159182610d2b575b5050610d0057507f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f602073ffffffffffffffffffffffffffffffffffffffff5f541692604051908152a3005b7f5274afe7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b81925090602091810103126100fe57602001518015908115036100fe578480610cb4565b610ca790606090610ca1565b7f92b66697000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445260645ffd5b50610dbe610f41565b15610ba5565b5062093a80810164ffffffffff8111610de55764ffffffffff164211610b9f565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b346100fe575f6003193601126100fe5761037a907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261036e604082610f00565b919091602081528251928360208301525f5b848110610eab5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b8060208092840101516040828601015201610e6d565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100fe57565b6040810190811067ffffffffffffffff82111761097457604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761097457604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081610f76575090565b905042101590565b90610fbb5750805115610f9357805190602001fd5b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b8151158061100e575b610fcc575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b15610fc456fea164736f6c634300081a000a610160806040523461042f57611c46803803809161001d8285610656565b833981019060608183031261042f5780516001600160401b03811161042f5781016101008184031261042f576040519061010082016001600160401b038111838210176104c15760405280516001600160a01b038116810361042f57825261008760208201610679565b916020810192835261009b60408301610686565b60408201908152606083015193906001600160a01b038516850361042f576060830194855260808401516001600160401b03811161042f57876100df9186016106d4565b916080840192835260a08501519360a0810194855260c086015160018060401b03811161042f5760e06101178b610125938a016106d4565b9760c0840198895201610679565b60e082019081526020890151989097906001600160a01b038a168a0361042f576040810151906001600160401b03821161042f57018a601f8201121561042f578051906001600160401b0382116104c15760209b8c6040519d8e61018e828760051b0182610656565b858152019360061b8301019181831161042f57602001925b8284106105fa5750505050865151602081116105e35750515f80546001600160a01b0319166001600160a01b0392831617905590511660805251151560a0525164ffffffffff1660c052518051906001600160401b0382116104c15760015490600182811c921680156105d9575b60208310146105c55781601f849311610557575b50602090601f83116001146104f1575f926104e6575b50508160011b915f199060031b1c1916176001555b5160e052516040516102866020828161027581830196878151938492016106b3565b81010301601f198101835282610656565b51905190602081106104d5575b506101005251151561012052610140528051905f5b8281106104335760018060a01b036080511660018060a01b03610140511690604051905f806020840163095ea7b360e01b815285602486015281196044860152604485526102f7606486610656565b84519082855af1610306610719565b816103f8575b50806103ee575b156103a9575b60405161142c908161081a82396080518181816104fa015281816107ff0152610e9b015260a0518181816108290152610d8c015260c05181818161022801528181610d11015281816110050152611197015260e05181818161034c01526106a2015261010051816110760152610120518181816108640152610d5001526101405181818161012e01526109640152f35b6103e16103e6936040519063095ea7b360e01b602083015260248201525f6044820152604481526103db606482610656565b82610748565b610748565b808080610319565b50803b1515610313565b805180159250821561040d575b50508461030c565b819250906020918101031261042f5760206104289101610679565b8480610405565b5f80fd5b81518110156104ad5760208160051b830101519060045491680100000000000000008310156104c15760018301806004558310156104ad5760019260045f5260205f200190838060401b038151166cffffffffff00000000000000006020845493015160401b1691858060681b03191617179055016102a8565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b5f199060200360031b1b165f610293565b015190505f8061023e565b60015f9081528281209350601f198516905b81811061053f5750908460019594939210610527575b505050811b01600155610253565b01515f1960f88460031b161c191690555f8080610519565b92936020600181928786015181550195019301610503565b60015f529091507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6601f840160051c810191602085106105bb575b90601f859493920160051c01905b8181106105ad5750610228565b5f81558493506001016105a0565b9091508190610592565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610214565b63a52d539b60e01b5f52600452602060245260445ffd5b60408483031261042f5760408051919082016001600160401b038111838210176104c1576040528451906001600160401b038216820361042f578260209260409452610647838801610686565b838201528152019301926101a6565b601f909101601f19168101906001600160401b038211908210176104c157604052565b5190811515820361042f57565b519064ffffffffff8216820361042f57565b6001600160401b0381116104c157601f01601f191660200190565b5f5b8381106106c45750505f910152565b81810151838201526020016106b5565b81601f8201121561042f5780516106ea81610698565b926106f86040519485610656565b8184526020828401011161042f5761071691602080850191016106b3565b90565b3d15610743573d9061072a82610698565b916107386040519384610656565b82523d5f602084013e565b606090565b5f806107709260018060a01b03169360208151910182865af1610769610719565b90836107bb565b8051908115159182610798575b50506107865750565b635274afe760e01b5f5260045260245ffd5b819250906020918101031261042f5760206107b39101610679565b155f8061077d565b906107df57508051156107d057805190602001fd5b630a12f52160e11b5f5260045ffd5b81511580610810575b6107f0575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156107e856fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde0314611060575080631686c90914610db157806316c3549d14610d755780631bfd681414610d395780633f31ae3f1461051e5780634800d97f146104ce57806349fc73dd146103935780634e390d3e1461036f57806351e75e8b1461033557806375829def1461026657806390e64d131461024c578063bb4b57341461020b578063bf4ed03f14610192578063ce51650714610152578063da792468146101025763f851a440146100cc575f80fd5b346100fe575f6003193601126100fe57602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b5f80fd5b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe5760206003193601126100fe57602061018860043560ff6001918060081c5f526002602052161b60405f205416151590565b6040519015158152f35b346100fe575f6003193601126100fe576101aa6111e4565b6040518091602082016020835281518091526020604084019201905f5b8181106101d5575050500390f35b8251805167ffffffffffffffff16855260209081015164ffffffffff1681860152869550604090940193909201916001016101c7565b346100fe575f6003193601126100fe57602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe57602061018861118f565b346100fe5760206003193601126100fe5761027f61110f565b5f5473ffffffffffffffffffffffffffffffffffffffff8116338103610306575073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffff0000000000000000000000000000000000000000921691829116175f55337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf805f80a3005b7fc6cce6a4000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b346100fe575f6003193601126100fe5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100fe575f6003193601126100fe57602064ffffffffff60035416604051908152f35b346100fe575f6003193601126100fe576040515f6001548060011c906001811680156104c4575b6020831081146104975782855290811561045557506001146103f7575b6103f3836103e78185038261114e565b604051918291826110a9565b0390f35b91905060015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6915f905b80821061043b575090915081016020016103e76103d7565b919260018160209254838588010152019101909291610423565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b840190910191506103e790506103d7565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f16916103ba565b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe5760806003193601126100fe576004356024359073ffffffffffffffffffffffffffffffffffffffff82168092036100fe57604435916fffffffffffffffffffffffffffffffff8316918284036100fe576064359367ffffffffffffffff85116100fe57366023860112156100fe5784600401359467ffffffffffffffff86116100fe5760248660051b8201013681116100fe576040516020810190858252866040820152876060820152606081526105dc60808261114e565b51902060405160208101918252602081526105f860408261114e565b5190209161060461118f565b610ce2576106298560ff6001918060081c5f526002602052161b60405f205416151590565b610cb657610636886111cc565b97610644604051998a61114e565b8852602401602088015b828210610ca657505050925f935b865185101561069e5761066f8588611278565b51908181101561068d575f52602052600160405f205b94019361065c565b905f52602052600160405f20610685565b85907f000000000000000000000000000000000000000000000000000000000000000003610c7e5760035464ffffffffff811615610c4a575b506106e06111e4565b925f84516106ed816111cc565b956106fb604051978861114e565b8187527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610728836111cc565b015f5b818110610c275750505f905b828210610b1e5750506fffffffffffffffffffffffffffffffff8216848111610af1578411610a9e575b5050508360081c5f52600260205260405f20600160ff86161b815417905573ffffffffffffffffffffffffffffffffffffffff5f5416926040516107a481611132565b5f81525f60208201526040519161010083019583871067ffffffffffffffff881117610a715773ffffffffffffffffffffffffffffffffffffffff9660409492939452815260208101918583526040820185815260608301887f00000000000000000000000000000000000000000000000000000000000000001681528860808501917f0000000000000000000000000000000000000000000000000000000000000000151583526fffffffffffffffffffffffffffffffff60a08701947f00000000000000000000000000000000000000000000000000000000000000001515865260c0880196875260e08801998a526040519c8d997f897f362b000000000000000000000000000000000000000000000000000000008b52602060048c0152816101448c019a511660248c0152511660448a0152511660648801525116608486015251151560a485015251151560c4840152519061012060e48401528151809152602061016484019201905f5b818110610a33575050508190602080945173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015203815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1928315610a28575f936109d0575b50907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d60406020958594825191825287820152a3604051908152f35b939250906020843d602011610a20575b816109ed6020938361114e565b810103126100fe5792519192907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d610994565b3d91506109e0565b6040513d5f823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff168186015289955060409094019390920191600101610913565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6fffffffffffffffffffffffffffffffff91610add7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff84930188611278565b519303168183511601169052848080610761565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b9092610b3f67ffffffffffffffff610b368685611278565b515116876112b9565b6fffffffffffffffffffffffffffffffff8111610bfc576fffffffffffffffffffffffffffffffff8091169164ffffffffff6020610b7d8887611278565b5101511660405190610b8e82611132565b8482526020820152610ba0878c611278565b52610bab868b611278565b5016016fffffffffffffffffffffffffffffffff8111610bcf579260010190610737565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f4916adce000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b602090604051610c3681611132565b5f81525f8382015282828c0101520161072b565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff1617600355846106d7565b7f0fa7d73c000000000000000000000000000000000000000000000000000000005f5260045ffd5b813581526020918201910161064e565b847f712b37a3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f442b1841000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445ffd5b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe5760406003193601126100fe57610dca61110f565b6024356fffffffffffffffffffffffffffffffff81168091036100fe5773ffffffffffffffffffffffffffffffffffffffff5f5416338103610306575064ffffffffff600354168015158061103f575b80611030575b610fd657506040515f8073ffffffffffffffffffffffffffffffffffffffff60208401957fa9059cbb000000000000000000000000000000000000000000000000000000008752169485602485015284604485015260448452610e8460648561114e565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610fca573d67ffffffffffffffff8111610a7157610f229160405191610f1260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116018461114e565b82523d5f602084013e5b83611386565b8051908115159182610fa6575b5050610f7b57507f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f602073ffffffffffffffffffffffffffffffffffffffff5f541692604051908152a3005b7f5274afe7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b81925090602091810103126100fe57602001518015908115036100fe578480610f2f565b610f2290606090610f1c565b7f92b66697000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445260645ffd5b5061103961118f565b15610e20565b5062093a80810164ffffffffff8111610bcf5764ffffffffff164211610e1a565b346100fe575f6003193601126100fe576103f3907f00000000000000000000000000000000000000000000000000000000000000006020820152602081526103e760408261114e565b919091602081528251928360208301525f5b8481106110f95750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b80602080928401015160408286010152016110bb565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100fe57565b6040810190811067ffffffffffffffff821117610a7157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610a7157604052565b64ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001680151590816111c4575090565b905042101590565b67ffffffffffffffff8111610a715760051b60200190565b600454906111f1826111cc565b916111ff604051938461114e565b80835260045f9081527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b602085015b83831061123b5750505050565b60016020819260405161124d81611132565b64ffffffffff865467ffffffffffffffff8116835260401c168382015281520192019201919061122e565b805182101561128c5760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8382098382029182808310920391808303921461137557670de0b6b3a7640000821015611345577faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106699394670de0b6b3a7640000910990828211900360ee1b910360121c170290565b84907f5173648d000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b5050670de0b6b3a764000090049150565b906113c3575080511561139b57805190602001fd5b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b81511580611416575b6113d4575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b156113cc56fea164736f6c634300081a000aa164736f6c634300081a000a"; + hex"6080806040523460155761408a908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c80631e323876146104135763769bed201461002f575f80fd5b3461040f5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261040f5760043567ffffffffffffffff811161040f5761007e9036906004016108e5565b6024359073ffffffffffffffffffffffffffffffffffffffff82169182810361040f5760407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc36011261040f57604051906100d882610831565b60443564ffffffffff8116810361040f57825260643564ffffffffff8116810361040f57602083015282519060208401511515836040860151926060870151608088015191604051806020810194602086526040820161013791610a07565b03601f1981018252610149908261084d565b60a08a01519360c08b015160405181819251908160208401916020019161016f926109e6565b810103808252610182906020018261084d565b61018b90610a2c565b9060e08c0151151592604051956020870198896101ba9164ffffffffff60208092828151168552015116910152565b604087526101c960608861084d565b6040519a8b9a60208c019d8e3360601b905260601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660348d015260f81b60488c015260d81b7fffffffffff0000000000000000000000000000000000000000000000000000001660498b015260601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016604e8a015251908160628a01610271926109e6565b8701946062860152608285015260f81b60a284015260601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660a383015251918260b783016102c0926109e6565b0160620103605501601f19810182526102d9908261084d565b51902060405161174880820182811067ffffffffffffffff8211176103e2578291610b5e83396080815261033660406103156080840189610abd565b92896020820152018664ffffffffff60208092828151168552015116910152565b03905ff59283156103d75761039a6103bc937f2ba0fe49588281dbb122dd3b7f3e2b3396338f70dbe3c62bf3e3888b4ba7ffb89273ffffffffffffffffffffffffffffffffffffffff6020971695869560405194859460c0865260c0860190610abd565b9289850152604084019064ffffffffff60208092828151168552015116910152565b608435608083015260a43560a08301520390a2604051908152f35b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f80fd5b3461040f5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261040f5760043567ffffffffffffffff811161040f576104629036906004016108e5565b6024359073ffffffffffffffffffffffffffffffffffffffff82169081830361040f576044359067ffffffffffffffff821161040f573660238301121561040f57816004013567ffffffffffffffff81116103e257604051926104cb60208360051b018561084d565b8184526024602085019260061b8201019036821161040f57949594602401915b8183106107de575050505f935f938351955b868610156105255760019064ffffffffff6020808960051b89010151015116019501946104fd565b84918451906020860151151584604088015192606089015160808a015191604051806020810194602086526040820161055d91610a07565b03601f198101825261056f908261084d565b8b60a08101519460c0820151604051818192519081602084019160200191610596926109e6565b8101038082526105a9906020018261084d565b6105b290610a2c565b9160e001511515926040519586602081019960208b52604082016105d591610a6d565b03601f19810188526105e7908861084d565b6040519a8b9a60208c019d8e3360601b905260601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660348d015260f81b60488c015260d81b7fffffffffff0000000000000000000000000000000000000000000000000000001660498b015260601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016604e8a015251908160628a0161068f926109e6565b8701946062860152608285015260f81b60a284015260601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660a383015251918260b783016106de926109e6565b0160620103605501601f19810182526106f7908261084d565b519020604051611dd88082019082821067ffffffffffffffff8311176103e2578291610748916122a6843960608152610733606082018a610abd565b90886020820152604081830391015286610a6d565b03905ff580156103d7576020947ffe44018cf74992b2720702385a1728bd329dd136e4f651203176c81c12710a8b926107bd73ffffffffffffffffffffffffffffffffffffffff6107ab941696879660405195869560c0875260c0870190610abd565b918a8601528482036040860152610a6d565b906060830152606435608083015260843560a08301520390a2604051908152f35b60408397969736031261040f57604051906107f882610831565b83359067ffffffffffffffff8216820361040f57826020926040945261081f83870161087d565b838201528152019201919594956104eb565b6040810190811067ffffffffffffffff8211176103e257604052565b90601f601f19910116810190811067ffffffffffffffff8211176103e257604052565b3590811515820361040f57565b359064ffffffffff8216820361040f57565b81601f8201121561040f5780359067ffffffffffffffff82116103e257604051926108c46020601f19601f860116018561084d565b8284526020838301011161040f57815f926020809301838601378301015290565b9190916101008184031261040f5760405190610100820182811067ffffffffffffffff8211176103e2576040528193813573ffffffffffffffffffffffffffffffffffffffff8116810361040f57835261094160208301610870565b60208401526109526040830161087d565b6040840152606082013573ffffffffffffffffffffffffffffffffffffffff8116810361040f576060840152608082013567ffffffffffffffff811161040f578161099e91840161088f565b608084015260a082013560a084015260c08201359067ffffffffffffffff821161040f57826109d660e094926109e19486940161088f565b60c086015201610870565b910152565b5f5b8381106109f75750505f910152565b81810151838201526020016109e8565b90601f19601f602093610a25815180928187528780880191016109e6565b0116010190565b602081519101519060208110610a40575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b90602080835192838152019201905f5b818110610a8a5750505090565b8251805167ffffffffffffffff16855260209081015164ffffffffff168186015260409094019390920191600101610a7d565b9073ffffffffffffffffffffffffffffffffffffffff825116815260208201511515602082015264ffffffffff604083015116604082015273ffffffffffffffffffffffffffffffffffffffff606083015116606082015260e080610b52610b3660808601516101006080870152610100860190610a07565b60a086015160a086015260c086015185820360c0870152610a07565b93015115159101529056fe610160806040523461042857611748803803809161001d8285610560565b833981019080820390608082126104285780516001600160401b03811161042857810190610100828503126104285760405161010081016001600160401b038111828210176105355760405282516001600160a01b038116810361042857815261008960208401610583565b6020820190815261009c60408501610590565b60408301908152606085015194906001600160a01b0386168603610428576060840195865260808201516001600160401b03811161042857886100e09184016105de565b6080850190815260a08381015190860190815260c084015190999192916001600160401b0382116104285761011c60e09161012a9387016105de565b9460c0880195865201610583565b9360e0860194855260208701519560018060a01b038716998a880361042857604090603f1901126104285760408051989089016001600160401b0381118a8210176105355761018d9160609160405261018560408201610590565b8b5201610590565b9860208901998a52855151602081116105495750515f80546001600160a01b0319166001600160a01b0392831617905590511660805251151560a0525164ffffffffff1660c0525180519097906001600160401b03811161053557600154600181811c9116801561052b575b602082101461051757601f81116104b4575b506020601f821160011461044857819064ffffffffff98999a5f9261043d575b50508160011b915f199060031b1c1916176001555b5160e052516040516102736020828161026281830196878151938492016105bd565b81010301601f198101835282610560565b519051906020811061042c575b50610100525115156101205261014052511669ffffffffff0000000000600454925160281b169160018060501b031916171760045560018060a01b0360805116604051905f806020840163095ea7b360e01b815285602486015281196044860152604485526102f0606486610560565b84519082855af16102ff610623565b816103f1575b50806103e7575b156103a2575b60405161102490816107248239608051818181610481015281816106d60152610c20015260a0518181816107140152610b11015260c05181818161015f01528181610a6801528181610d8a0152610f49015260e0518181816102d3015261062201526101005181610e2801526101205181818161073e0152610ad50152610140518181816101af01526108870152f35b6103da6103df936040519063095ea7b360e01b602083015260248201525f6044820152604481526103d4606482610560565b82610652565b610652565b5f8080610312565b50803b151561030c565b8051801592508215610406575b50505f610305565b81925090602091810103126104285760206104219101610583565b5f806103fe565b5f80fd5b5f199060200360031b1b165f610280565b015190505f8061022b565b601f1982169960015f52815f209a5f5b81811061049c57509164ffffffffff999a9b91846001959410610484575b505050811b01600155610240565b01515f1960f88460031b161c191690555f8080610476565b838301518d556001909c019b60209384019301610458565b60015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6601f830160051c8101916020841061050d575b601f0160051c01905b818110610502575061020b565b5f81556001016104f5565b90915081906104ec565b634e487b7160e01b5f52602260045260245ffd5b90607f16906101f9565b634e487b7160e01b5f52604160045260245ffd5b63a52d539b60e01b5f52600452602060245260445ffd5b601f909101601f19168101906001600160401b0382119082101761053557604052565b5190811515820361042857565b519064ffffffffff8216820361042857565b6001600160401b03811161053557601f01601f191660200190565b5f5b8381106105ce5750505f910152565b81810151838201526020016105bf565b81601f820112156104285780516105f4816105a2565b926106026040519485610560565b818452602082840101116104285761062091602080850191016105bd565b90565b3d1561064d573d90610634826105a2565b916106426040519384610560565b82523d5f602084013e565b606090565b5f8061067a9260018060a01b03169360208151910182865af1610673610623565b90836106c5565b80519081151591826106a2575b50506106905750565b635274afe760e01b5f5260045260245ffd5b81925090602091810103126104285760206106bd9101610583565b155f80610687565b906106e957508051156106da57805190602001fd5b630a12f52160e11b5f5260045ffd5b8151158061071a575b6106fa575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156106f256fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde0314610e12575080631686c90914610b3657806316c3549d14610afa5780631bfd681414610abe5780633bfe03a814610a905780633f31ae3f146104a55780634800d97f1461045557806349fc73dd1461031a5780634e390d3e146102f657806351e75e8b146102bc57806375829def146101ed57806390e64d13146101d35780639e93e57714610183578063bb4b573414610142578063ce516507146101025763f851a440146100cc575f80fd5b346100fe575f6003193601126100fe57602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b5f80fd5b346100fe5760206003193601126100fe57602061013860043560ff6001918060081c5f526002602052161b60405f205416151590565b6040519015158152f35b346100fe575f6003193601126100fe57602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe576020610138610f41565b346100fe5760206003193601126100fe57610206610ec1565b5f5473ffffffffffffffffffffffffffffffffffffffff811633810361028d575073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffff0000000000000000000000000000000000000000921691829116175f55337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf805f80a3005b7fc6cce6a4000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b346100fe575f6003193601126100fe5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100fe575f6003193601126100fe57602064ffffffffff60035416604051908152f35b346100fe575f6003193601126100fe576040515f6001548060011c9060018116801561044b575b60208310811461041e578285529081156103dc575060011461037e575b61037a8361036e81850382610f00565b60405191829182610e5b565b0390f35b91905060015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6915f905b8082106103c25750909150810160200161036e61035e565b9192600181602092548385880101520191019092916103aa565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b8401909101915061036e905061035e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610341565b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe5760806003193601126100fe5760043560243573ffffffffffffffffffffffffffffffffffffffff81168091036100fe57604435916fffffffffffffffffffffffffffffffff83168093036100fe576064359067ffffffffffffffff82116100fe57366023830112156100fe57816004013567ffffffffffffffff81116100fe578060051b92602484820101903682116100fe57604051602081019085825287604082015288606082015260608152610563608082610f00565b519020604051602081019182526020815261057f604082610f00565b5190209261058b610f41565b610a39576105b08560ff6001918060081c5f526002602052161b60405f205416151590565b610a0d576105c46020604051970187610f00565b8552602401602085015b8282106109fd57505050935f945b835186101561061e5760208660051b85010151908181105f1461060d575f52602052600160405f205b9501946105dc565b905f52602052600160405f20610605565b84907f0000000000000000000000000000000000000000000000000000000000000000036109d55760035464ffffffffff8116156109a1575b508260081c5f52600260205260405f20600160ff85161b815417905573ffffffffffffffffffffffffffffffffffffffff5f54169160405161069881610ee4565b5f81525f602082015260405193610100850185811067ffffffffffffffff8211176109745760405284526020840183815260408501838152606086017f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16815260808701907f00000000000000000000000000000000000000000000000000000000000000001515825260a08801927f0000000000000000000000000000000000000000000000000000000000000000151584526040519461076e86610ee4565b60045464ffffffffff8116875260281c64ffffffffff16602087015260c08a0195865260e08a01968752604051997fab167ccc000000000000000000000000000000000000000000000000000000008b525173ffffffffffffffffffffffffffffffffffffffff1660048b01525173ffffffffffffffffffffffffffffffffffffffff1660248a0152516fffffffffffffffffffffffffffffffff1660448901525173ffffffffffffffffffffffffffffffffffffffff166064880152511515608487015251151560a486015251805164ffffffffff1660c48601526020015164ffffffffff1660e485015251805173ffffffffffffffffffffffffffffffffffffffff166101048501526020015161012484015282807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165a925f61014492602095f1928315610969575f93610911575b50907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d60406020958594825191825287820152a3604051908152f35b939250906020843d602011610961575b8161092e60209383610f00565b810103126100fe5792519192907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d6108d5565b3d9150610921565b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161760035583610657565b7f0fa7d73c000000000000000000000000000000000000000000000000000000005f5260045ffd5b81358152602091820191016105ce565b847f712b37a3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f442b1841000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445ffd5b346100fe575f6003193601126100fe57604060045464ffffffffff825191818116835260281c166020820152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe5760406003193601126100fe57610b4f610ec1565b6024356fffffffffffffffffffffffffffffffff81168091036100fe5773ffffffffffffffffffffffffffffffffffffffff5f541633810361028d575064ffffffffff6003541680151580610dc4575b80610db5575b610d5b57506040515f8073ffffffffffffffffffffffffffffffffffffffff60208401957fa9059cbb000000000000000000000000000000000000000000000000000000008752169485602485015284604485015260448452610c09606485610f00565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610d4f573d67ffffffffffffffff811161097457610ca79160405191610c9760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610f00565b82523d5f602084013e5b83610f7e565b8051908115159182610d2b575b5050610d0057507f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f602073ffffffffffffffffffffffffffffffffffffffff5f541692604051908152a3005b7f5274afe7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b81925090602091810103126100fe57602001518015908115036100fe578480610cb4565b610ca790606090610ca1565b7f92b66697000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445260645ffd5b50610dbe610f41565b15610ba5565b5062093a80810164ffffffffff8111610de55764ffffffffff164211610b9f565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b346100fe575f6003193601126100fe5761037a907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261036e604082610f00565b919091602081528251928360208301525f5b848110610eab5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b8060208092840101516040828601015201610e6d565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100fe57565b6040810190811067ffffffffffffffff82111761097457604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761097457604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081610f76575090565b905042101590565b90610fbb5750805115610f9357805190602001fd5b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b8151158061100e575b610fcc575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b15610fc456fea164736f6c634300081a000a610180806040523461051d57611dd8803803809161001d82856106a2565b833981019060608183031261051d5780516001600160401b03811161051d5781016101008184031261051d576040519061010082016001600160401b038111838210176103605760405280516001600160a01b038116810361051d578252610087602082016106c5565b916020810192835261009b604083016106d2565b60408201908152606083015193906001600160a01b038516850361051d576060830194855260808401516001600160401b03811161051d57876100df918601610720565b916080840192835260a08501519360a0810194855260c086015160018060401b03811161051d5760e06101178b610125938a01610720565b9760c08401988952016106c5565b60e082019081526020890151989097906001600160a01b038a168a0361051d576040810151906001600160401b03821161051d57018a601f8201121561051d578051906001600160401b0382116103605760209b8c6040519d8e61018e828760051b01826106a2565b858152019360061b8301019181831161051d57602001925b82841061064657505050508651516020811161062f5750515f80546001600160a01b0319166001600160a01b0392831617905590511660805251151560a0525164ffffffffff1660c052518051906001600160401b0382116103605760015490600182811c92168015610625575b60208310146106115781601f8493116105a3575b50602090601f831160011461053d575f92610532575b50508160011b915f199060031b1c1916176001555b5160e052516040516102866020828161027581830196878151938492016106ff565b81010301601f1981018352826106a2565b5190519060208110610521575b506101005251151561012052610140525f90815b8151831015610388576001600160401b036102c28484610765565b5151166001600160401b039182160190811161037457916102e38183610765565b5190600454916801000000000000000083101561036057600183018060045583101561034c5760019260045f5260205f200190838060401b038151166cffffffffff00000000000000006020845493015160401b1691858060681b0319161717905501916102a7565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b6101605260018060a01b036080511660018060a01b03610140511690604051905f806020840163095ea7b360e01b815285602486015281196044860152604485526103d46064866106a2565b84519082855af16103e3610779565b816104e6575b50806104dc575b15610497575b60405161155e908161087a82396080518181816105d10152818161095d0152611061015260a0518181816109870152610f52015260c0518181816102bb01528181610eac015281816111cb015261135d015260e05181818161042301526107b40152610100518161123c0152610120518181816109c20152610f160152610140518181816101390152610ac20152610160518181816102ff01526106980152f35b6104cf6104d4936040519063095ea7b360e01b602083015260248201525f6044820152604481526104c96064826106a2565b826107a8565b6107a8565b8080806103f6565b50803b15156103f0565b80518015925082156104fb575b5050846103e9565b819250906020918101031261051d57602061051691016106c5565b84806104f3565b5f80fd5b5f199060200360031b1b165f610293565b015190505f8061023e565b60015f9081528281209350601f198516905b81811061058b5750908460019594939210610573575b505050811b01600155610253565b01515f1960f88460031b161c191690555f8080610565565b9293602060018192878601518155019501930161054f565b60015f529091507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6601f840160051c81019160208510610607575b90601f859493920160051c01905b8181106105f95750610228565b5f81558493506001016105ec565b90915081906105de565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610214565b63a52d539b60e01b5f52600452602060245260445ffd5b60408483031261051d5760408051919082016001600160401b03811183821017610360576040528451906001600160401b038216820361051d5782602092604094526106938388016106d2565b838201528152019301926101a6565b601f909101601f19168101906001600160401b0382119082101761036057604052565b5190811515820361051d57565b519064ffffffffff8216820361051d57565b6001600160401b03811161036057601f01601f191660200190565b5f5b8381106107105750505f910152565b8181015183820152602001610701565b81601f8201121561051d578051610736816106e4565b9261074460405194856106a2565b8184526020828401011161051d5761076291602080850191016106ff565b90565b805182101561034c5760209160051b010190565b3d156107a3573d9061078a826106e4565b9161079860405193846106a2565b82523d5f602084013e565b606090565b5f806107d09260018060a01b03169360208151910182865af16107c9610779565b908361081b565b80519081151591826107f8575b50506107e65750565b635274afe760e01b5f5260045260245ffd5b819250906020918101031261051d57602061081391016106c5565b155f806107dd565b9061083f575080511561083057805190602001fd5b630a12f52160e11b5f5260045ffd5b81511580610870575b610850575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b1561084856fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde0314611226575080631686c90914610f7757806316c3549d14610f3b5780631bfd681414610eff5780633f31ae3f146105f55780634800d97f146105a557806349fc73dd1461046a5780634e390d3e1461044657806351e75e8b1461040c57806375829def1461033d57806390e64d1314610323578063936c63d9146102df578063bb4b57341461029e578063bf4ed03f1461019d578063ce5165071461015d578063da7924681461010d5763f851a440146100d7575f80fd5b34610109575f60031936011261010957602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b5f80fd5b34610109575f60031936011261010957602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461010957602060031936011261010957602061019360043560ff6001918060081c5f526002602052161b60405f205416151590565b6040519015158152f35b34610109575f600319360112610109576004546101b981611392565b906101c76040519283611314565b80825260045f9081526020830191907f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b835b838310610261578486604051918291602083019060208452518091526040830191905f5b81811061022b575050500390f35b8251805167ffffffffffffffff16855260209081015164ffffffffff16818601528695506040909401939092019160010161021d565b600160208192604051610273816112f8565b64ffffffffff865467ffffffffffffffff8116835260401c16838201528152019201920191906101f9565b34610109575f60031936011261010957602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109575f60031936011261010957602060405167ffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109575f600319360112610109576020610193611355565b34610109576020600319360112610109576103566112d5565b5f5473ffffffffffffffffffffffffffffffffffffffff81163381036103dd575073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffff0000000000000000000000000000000000000000921691829116175f55337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf805f80a3005b7fc6cce6a4000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b34610109575f6003193601126101095760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b34610109575f60031936011261010957602064ffffffffff60035416604051908152f35b34610109575f600319360112610109576040515f6001548060011c9060018116801561059b575b60208310811461056e5782855290811561052c57506001146104ce575b6104ca836104be81850382611314565b6040519182918261126f565b0390f35b91905060015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6915f905b808210610512575090915081016020016104be6104ae565b9192600181602092548385880101520191019092916104fa565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b840190910191506104be90506104ae565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610491565b34610109575f60031936011261010957602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109576080600319360112610109576004356024359073ffffffffffffffffffffffffffffffffffffffff821680920361010957604435916fffffffffffffffffffffffffffffffff831691828403610109576064359367ffffffffffffffff851161010957366023860112156101095784600401359467ffffffffffffffff86116101095760248660051b8201013681116101095767ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016670de0b6b3a76400008103610ed457506040516020810190858252866040820152876060820152606081526106ee608082611314565b519020604051602081019182526020815261070a604082611314565b51902091610716611355565b610e7d5761073b8560ff6001918060081c5f526002602052161b60405f205416151590565b610e515761074888611392565b97610756604051998a611314565b8852602401602088015b828210610e4157505050925f935b86518510156107b05761078185886113aa565b51908181101561079f575f52602052600160405f205b94019361076e565b905f52602052600160405f20610797565b85907f000000000000000000000000000000000000000000000000000000000000000003610e195760035464ffffffffff811615610de5575b50600454926107f784611392565b936108056040519586611314565b80855260045f9081527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b602087015b838310610da857505050505f845161084b81611392565b956108596040519788611314565b8187527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061088683611392565b015f5b818110610d855750505f905b828210610c7c5750506fffffffffffffffffffffffffffffffff8216848111610c4f578411610bfc575b5050508360081c5f52600260205260405f20600160ff86161b815417905573ffffffffffffffffffffffffffffffffffffffff5f541692604051610902816112f8565b5f81525f60208201526040519161010083019583871067ffffffffffffffff881117610bcf5773ffffffffffffffffffffffffffffffffffffffff9660409492939452815260208101918583526040820185815260608301887f00000000000000000000000000000000000000000000000000000000000000001681528860808501917f0000000000000000000000000000000000000000000000000000000000000000151583526fffffffffffffffffffffffffffffffff60a08701947f00000000000000000000000000000000000000000000000000000000000000001515865260c0880196875260e08801998a526040519c8d997f897f362b000000000000000000000000000000000000000000000000000000008b52602060048c0152816101448c019a511660248c0152511660448a0152511660648801525116608486015251151560a485015251151560c4840152519061012060e48401528151809152602061016484019201905f5b818110610b91575050508190602080945173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015203815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1928315610b86575f93610b2e575b50907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d60406020958594825191825287820152a3604051908152f35b939250906020843d602011610b7e575b81610b4b60209383611314565b810103126101095792519192907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d610af2565b3d9150610b3e565b6040513d5f823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff168186015289955060409094019390920191600101610a71565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6fffffffffffffffffffffffffffffffff91610c3b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff849301886113aa565b5193031681835116011690528480806108bf565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b9092610c9d67ffffffffffffffff610c9486856113aa565b515116876113eb565b6fffffffffffffffffffffffffffffffff8111610d5a576fffffffffffffffffffffffffffffffff8091169164ffffffffff6020610cdb88876113aa565b5101511660405190610cec826112f8565b8482526020820152610cfe878c6113aa565b52610d09868b6113aa565b5016016fffffffffffffffffffffffffffffffff8111610d2d579260010190610895565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f4916adce000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b602090604051610d94816112f8565b5f81525f8382015282828c01015201610889565b600160208192604051610dba816112f8565b64ffffffffff865467ffffffffffffffff8116835260401c1683820152815201920192019190610834565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff1617600355846107e9565b7f0fa7d73c000000000000000000000000000000000000000000000000000000005f5260045ffd5b8135815260209182019101610760565b847f712b37a3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f442b1841000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445ffd5b7f4557880f000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b34610109575f6003193601126101095760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b34610109575f6003193601126101095760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b3461010957604060031936011261010957610f906112d5565b6024356fffffffffffffffffffffffffffffffff81168091036101095773ffffffffffffffffffffffffffffffffffffffff5f54163381036103dd575064ffffffffff6003541680151580611205575b806111f6575b61119c57506040515f8073ffffffffffffffffffffffffffffffffffffffff60208401957fa9059cbb00000000000000000000000000000000000000000000000000000000875216948560248501528460448501526044845261104a606485611314565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15611190573d67ffffffffffffffff8111610bcf576110e891604051916110d860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184611314565b82523d5f602084013e5b836114b8565b805190811515918261116c575b505061114157507f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f602073ffffffffffffffffffffffffffffffffffffffff5f541692604051908152a3005b7f5274afe7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b819250906020918101031261010957602001518015908115036101095784806110f5565b6110e8906060906110e2565b7f92b66697000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445260645ffd5b506111ff611355565b15610fe6565b5062093a80810164ffffffffff8111610d2d5764ffffffffff164211610fe0565b34610109575f600319360112610109576104ca907f00000000000000000000000000000000000000000000000000000000000000006020820152602081526104be604082611314565b919091602081528251928360208301525f5b8481106112bf5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b8060208092840101516040828601015201611281565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361010957565b6040810190811067ffffffffffffffff821117610bcf57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610bcf57604052565b64ffffffffff7f000000000000000000000000000000000000000000000000000000000000000016801515908161138a575090565b905042101590565b67ffffffffffffffff8111610bcf5760051b60200190565b80518210156113be5760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff838209838202918280831092039180830392146114a757670de0b6b3a7640000821015611477577faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106699394670de0b6b3a7640000910990828211900360ee1b910360121c170290565b84907f5173648d000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b5050670de0b6b3a764000090049150565b906114f557508051156114cd57805190602001fd5b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b81511580611548575b611506575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b156114fe56fea164736f6c634300081a000aa164736f6c634300081a000a"; /*////////////////////////////////////////////////////////////////////////// DEPLOYERS diff --git a/src/SablierV2MerkleLT.sol b/src/SablierV2MerkleLT.sol index 7d091f52..e647b6aa 100644 --- a/src/SablierV2MerkleLT.sol +++ b/src/SablierV2MerkleLT.sol @@ -4,12 +4,14 @@ pragma solidity >=0.8.22; import { BitMaps } from "@openzeppelin/contracts/utils/structs/BitMaps.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { uUNIT } from "@prb/math/src/UD2x18.sol"; import { UD60x18, ud60x18, ZERO } from "@prb/math/src/UD60x18.sol"; import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; import { Broker, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; import { SablierV2MerkleLockup } from "./abstracts/SablierV2MerkleLockup.sol"; import { ISablierV2MerkleLT } from "./interfaces/ISablierV2MerkleLT.sol"; +import { Errors } from "./libraries/Errors.sol"; import { MerkleLockup, MerkleLT } from "./types/DataTypes.sol"; /// @title SablierV2MerkleLT @@ -28,6 +30,9 @@ contract SablierV2MerkleLT is /// @inheritdoc ISablierV2MerkleLT ISablierV2LockupTranched public immutable override LOCKUP_TRANCHED; + /// @inheritdoc ISablierV2MerkleLT + uint64 public immutable override TOTAL_PERCENTAGE; + /// @dev The tranches with their respective unlock percentages and durations. MerkleLT.TrancheWithPercentage[] internal _tranchesWithPercentages; @@ -46,12 +51,14 @@ contract SablierV2MerkleLT is { LOCKUP_TRANCHED = lockupTranched; - // Since Solidity lacks a syntax for copying arrays of structs directly from memory to storage, a manual - // approach is necessary. See https://github.com/ethereum/solidity/issues/12783. - uint256 count = tranchesWithPercentages.length; - for (uint256 i = 0; i < count; ++i) { + // Calculate the total percentage of the tranches and save them in the contract state. + uint64 totalPercentage; + for (uint256 i = 0; i < tranchesWithPercentages.length; ++i) { + uint64 percentage = tranchesWithPercentages[i].unlockPercentage.unwrap(); + totalPercentage += percentage; _tranchesWithPercentages.push(tranchesWithPercentages[i]); } + TOTAL_PERCENTAGE = totalPercentage; // Max approve the Sablier contract to spend funds from the MerkleLockup contract. ASSET.forceApprove(address(LOCKUP_TRANCHED), type(uint256).max); @@ -81,6 +88,11 @@ contract SablierV2MerkleLT is override returns (uint256 streamId) { + // Check: the sum of percentages equals 100%. + if (TOTAL_PERCENTAGE != uUNIT) { + revert Errors.SablierV2MerkleLT_TotalPercentageNotOneHundred(TOTAL_PERCENTAGE); + } + // Generate the Merkle tree leaf by hashing the corresponding parameters. Hashing twice prevents second // preimage attacks. bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(index, recipient, amount)))); diff --git a/src/SablierV2MerkleLockupFactory.sol b/src/SablierV2MerkleLockupFactory.sol index 9c0ef2a9..1cd6c153 100644 --- a/src/SablierV2MerkleLockupFactory.sol +++ b/src/SablierV2MerkleLockupFactory.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; -import { uUNIT } from "@prb/math/src/UD2x18.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; @@ -9,7 +8,6 @@ import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; import { ISablierV2MerkleLL } from "./interfaces/ISablierV2MerkleLL.sol"; import { ISablierV2MerkleLockupFactory } from "./interfaces/ISablierV2MerkleLockupFactory.sol"; import { ISablierV2MerkleLT } from "./interfaces/ISablierV2MerkleLT.sol"; -import { Errors } from "./libraries/Errors.sol"; import { SablierV2MerkleLL } from "./SablierV2MerkleLL.sol"; import { SablierV2MerkleLT } from "./SablierV2MerkleLT.sol"; import { MerkleLockup, MerkleLT } from "./types/DataTypes.sol"; @@ -32,8 +30,25 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { external returns (ISablierV2MerkleLL merkleLL) { - // Deploy the MerkleLockup contract with CREATE. - merkleLL = new SablierV2MerkleLL(baseParams, lockupLinear, streamDurations); + // Hash the parameters to generate a salt. + bytes32 salt = keccak256( + abi.encodePacked( + msg.sender, + baseParams.asset, + baseParams.cancelable, + baseParams.expiration, + baseParams.initialAdmin, + abi.encode(baseParams.ipfsCID), + baseParams.merkleRoot, + bytes32(abi.encodePacked(baseParams.name)), + baseParams.transferable, + lockupLinear, + abi.encode(streamDurations) + ) + ); + + // Deploy the MerkleLockup contract with CREATE2. + merkleLL = new SablierV2MerkleLL{ salt: salt }(baseParams, lockupLinear, streamDurations); // Log the creation of the MerkleLockup contract, including some metadata that is not stored on-chain. emit CreateMerkleLL(merkleLL, baseParams, lockupLinear, streamDurations, aggregateAmount, recipientCount); @@ -51,24 +66,33 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { returns (ISablierV2MerkleLT merkleLT) { // Calculate the sum of percentages and durations across all tranches. - uint64 totalPercentage; uint256 totalDuration; for (uint256 i = 0; i < tranchesWithPercentages.length; ++i) { - uint64 percentage = tranchesWithPercentages[i].unlockPercentage.unwrap(); - totalPercentage = totalPercentage + percentage; unchecked { // Safe to use `unchecked` because its only used in the event. totalDuration += tranchesWithPercentages[i].duration; } } - // Check: the sum of percentages equals 100%. - if (totalPercentage != uUNIT) { - revert Errors.SablierV2MerkleLockupFactory_TotalPercentageNotOneHundred(totalPercentage); - } + // Hash the parameters to generate a salt. + bytes32 salt = keccak256( + abi.encodePacked( + msg.sender, + baseParams.asset, + baseParams.cancelable, + baseParams.expiration, + baseParams.initialAdmin, + abi.encode(baseParams.ipfsCID), + baseParams.merkleRoot, + bytes32(abi.encodePacked(baseParams.name)), + baseParams.transferable, + lockupTranched, + abi.encode(tranchesWithPercentages) + ) + ); - // Deploy the MerkleLockup contract with CREATE. - merkleLT = new SablierV2MerkleLT(baseParams, lockupTranched, tranchesWithPercentages); + // Deploy the MerkleLockup contract with CREATE2. + merkleLT = new SablierV2MerkleLT{ salt: salt }(baseParams, lockupTranched, tranchesWithPercentages); // Log the creation of the MerkleLockup contract, including some metadata that is not stored on-chain. emit CreateMerkleLT( diff --git a/src/interfaces/ISablierV2MerkleLT.sol b/src/interfaces/ISablierV2MerkleLT.sol index 6304ef52..d64a082f 100644 --- a/src/interfaces/ISablierV2MerkleLT.sol +++ b/src/interfaces/ISablierV2MerkleLT.sol @@ -19,6 +19,9 @@ interface ISablierV2MerkleLT is ISablierV2MerkleLockup { /// @notice The address of the {SablierV2LockupTranched} contract. function LOCKUP_TRANCHED() external view returns (ISablierV2LockupTranched); + /// @notice The total percentage of the tranches. + function TOTAL_PERCENTAGE() external view returns (uint64); + /*////////////////////////////////////////////////////////////////////////// NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ @@ -32,6 +35,7 @@ interface ISablierV2MerkleLT is ISablierV2MerkleLockup { /// - The campaign must not have expired. /// - The stream must not have been claimed already. /// - The Merkle proof must be valid. + /// - TOTAL_PERCENTAGE must be equal to 100%. /// /// @param index The index of the recipient in the Merkle tree. /// @param recipient The address of the stream holder. diff --git a/src/interfaces/ISablierV2MerkleLockupFactory.sol b/src/interfaces/ISablierV2MerkleLockupFactory.sol index 57a9d8bf..beaf81e7 100644 --- a/src/interfaces/ISablierV2MerkleLockupFactory.sol +++ b/src/interfaces/ISablierV2MerkleLockupFactory.sol @@ -10,7 +10,7 @@ import { ISablierV2MerkleLT } from "./ISablierV2MerkleLT.sol"; import { MerkleLockup, MerkleLT } from "../types/DataTypes.sol"; /// @title ISablierV2MerkleLockupFactory -/// @notice Deploys MerkleLockup campaigns with CREATE. +/// @notice Deploys MerkleLockup campaigns with CREATE2. interface ISablierV2MerkleLockupFactory { /*////////////////////////////////////////////////////////////////////////// EVENTS @@ -63,9 +63,6 @@ interface ISablierV2MerkleLockupFactory { /// @notice Creates a new MerkleLockup campaign with a LockupTranched distribution. /// @dev Emits a {CreateMerkleLT} event. /// - /// Requirements: - /// - The sum of the tranches' unlock percentages must equal 100% = 1e18. - /// /// @param baseParams Struct encapsulating the {SablierV2MerkleLockup} parameters, which are documented in /// {DataTypes}. /// @param lockupTranched The address of the {SablierV2LockupTranched} contract. diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index e64958d8..ecd1b2e1 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -31,9 +31,9 @@ library Errors { error SablierV2MerkleLockup_StreamClaimed(uint256 index); /*////////////////////////////////////////////////////////////////////////// - SABLIER-V2-MERKLE-LOCKUP-FACTORY + SABLIER-V2-MERKLE-LT //////////////////////////////////////////////////////////////////////////*/ - /// @notice Thrown when the sum of the tranches' unlock percentages does not equal 100%. - error SablierV2MerkleLockupFactory_TotalPercentageNotOneHundred(uint64 totalPercentage); + /// @notice Thrown when trying to claim from an LT campaign with tranches' unlock percentages not adding up to 100%. + error SablierV2MerkleLT_TotalPercentageNotOneHundred(uint64 totalPercentage); } diff --git a/test/Base.t.sol b/test/Base.t.sol index f7a106f2..41d556a8 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -18,7 +18,9 @@ import { ISablierV2MerkleLL } from "src/interfaces/ISablierV2MerkleLL.sol"; import { ISablierV2MerkleLockupFactory } from "src/interfaces/ISablierV2MerkleLockupFactory.sol"; import { ISablierV2MerkleLT } from "src/interfaces/ISablierV2MerkleLT.sol"; import { SablierV2BatchLockup } from "src/SablierV2BatchLockup.sol"; +import { SablierV2MerkleLL } from "src/SablierV2MerkleLL.sol"; import { SablierV2MerkleLockupFactory } from "src/SablierV2MerkleLockupFactory.sol"; +import { SablierV2MerkleLT } from "src/SablierV2MerkleLT.sol"; import { ERC20Mock } from "./mocks/erc20/ERC20Mock.sol"; import { Assertions } from "./utils/Assertions.sol"; @@ -55,7 +57,6 @@ abstract contract Base_Test is ISablierV2LockupLinear internal lockupLinear; ISablierV2LockupTranched internal lockupTranched; ISablierV2MerkleLockupFactory internal merkleLockupFactory; - uint256 internal merkleLockupFactoryNonce; ISablierV2MerkleLL internal merkleLL; ISablierV2MerkleLT internal merkleLT; @@ -256,4 +257,141 @@ abstract contract Base_Test is { vm.expectCall({ callee: asset_, count: count, data: abi.encodeCall(IERC20.transferFrom, (from, to, amount)) }); } + + /*////////////////////////////////////////////////////////////////////////// + MERKLE-LOCKUP + //////////////////////////////////////////////////////////////////////////*/ + + function computeMerkleLLAddress( + address admin, + bytes32 merkleRoot, + uint40 expiration + ) + internal + view + returns (address) + { + return computeMerkleLLAddress(admin, dai, merkleRoot, expiration); + } + + function computeMerkleLLAddress( + address admin, + IERC20 asset_, + bytes32 merkleRoot, + uint40 expiration + ) + internal + view + returns (address) + { + bytes32 salt = keccak256( + abi.encodePacked( + users.alice, + address(asset_), + defaults.CANCELABLE(), + expiration, + admin, + abi.encode(defaults.IPFS_CID()), + merkleRoot, + defaults.NAME_BYTES32(), + defaults.TRANSFERABLE(), + lockupLinear, + abi.encode(defaults.durations()) + ) + ); + bytes32 creationBytecodeHash = keccak256(getMerkleLLBytecode(admin, asset_, merkleRoot, expiration)); + return vm.computeCreate2Address({ + salt: salt, + initCodeHash: creationBytecodeHash, + deployer: address(merkleLockupFactory) + }); + } + + function computeMerkleLTAddress( + address admin, + bytes32 merkleRoot, + uint40 expiration + ) + internal + view + returns (address) + { + return computeMerkleLTAddress(admin, dai, merkleRoot, expiration); + } + + function computeMerkleLTAddress( + address admin, + IERC20 asset_, + bytes32 merkleRoot, + uint40 expiration + ) + internal + view + returns (address) + { + bytes32 salt = keccak256( + abi.encodePacked( + users.alice, + address(asset_), + defaults.CANCELABLE(), + expiration, + admin, + abi.encode(defaults.IPFS_CID()), + merkleRoot, + defaults.NAME_BYTES32(), + defaults.TRANSFERABLE(), + lockupTranched, + abi.encode(defaults.tranchesWithPercentages()) + ) + ); + bytes32 creationBytecodeHash = keccak256(getMerkleLTBytecode(admin, asset_, merkleRoot, expiration)); + return vm.computeCreate2Address({ + salt: salt, + initCodeHash: creationBytecodeHash, + deployer: address(merkleLockupFactory) + }); + } + + function getMerkleLLBytecode( + address admin, + IERC20 asset_, + bytes32 merkleRoot, + uint40 expiration + ) + internal + view + returns (bytes memory) + { + bytes memory constructorArgs = + abi.encode(defaults.baseParams(admin, asset_, expiration, merkleRoot), lockupLinear, defaults.durations()); + if (!isTestOptimizedProfile()) { + return bytes.concat(type(SablierV2MerkleLL).creationCode, constructorArgs); + } else { + return + bytes.concat(vm.getCode("out-optimized/SablierV2MerkleLL.sol/SablierV2MerkleLL.json"), constructorArgs); + } + } + + function getMerkleLTBytecode( + address admin, + IERC20 asset_, + bytes32 merkleRoot, + uint40 expiration + ) + internal + view + returns (bytes memory) + { + bytes memory constructorArgs = abi.encode( + defaults.baseParams(admin, asset_, expiration, merkleRoot), + lockupTranched, + defaults.tranchesWithPercentages() + ); + if (!isTestOptimizedProfile()) { + return bytes.concat(type(SablierV2MerkleLT).creationCode, constructorArgs); + } else { + return + bytes.concat(vm.getCode("out-optimized/SablierV2MerkleLT.sol/SablierV2MerkleLT.json"), constructorArgs); + } + } } diff --git a/test/fork/merkle-lockup/MerkleLL.t.sol b/test/fork/merkle-lockup/MerkleLL.t.sol index 2f641c95..1cad3e13 100644 --- a/test/fork/merkle-lockup/MerkleLL.t.sol +++ b/test/fork/merkle-lockup/MerkleLL.t.sol @@ -95,7 +95,7 @@ abstract contract MerkleLL_Fork_Test is Fork_Test { MerkleBuilder.sortLeaves(leaves); vars.merkleRoot = getRoot(leaves.toBytes32()); - vars.expectedLL = vm.computeCreateAddress(address(merkleLockupFactory), ++merkleLockupFactoryNonce); + vars.expectedLL = computeMerkleLLAddress(params.admin, FORK_ASSET, vars.merkleRoot, params.expiration); vars.baseParams = defaults.baseParams({ admin: params.admin, diff --git a/test/fork/merkle-lockup/MerkleLT.t.sol b/test/fork/merkle-lockup/MerkleLT.t.sol index 41fff26a..ad741646 100644 --- a/test/fork/merkle-lockup/MerkleLT.t.sol +++ b/test/fork/merkle-lockup/MerkleLT.t.sol @@ -96,7 +96,7 @@ abstract contract MerkleLT_Fork_Test is Fork_Test { MerkleBuilder.sortLeaves(leaves); vars.merkleRoot = getRoot(leaves.toBytes32()); - vars.expectedLT = vm.computeCreateAddress(address(merkleLockupFactory), ++merkleLockupFactoryNonce); + vars.expectedLT = computeMerkleLTAddress(params.admin, FORK_ASSET, vars.merkleRoot, params.expiration); vars.baseParams = defaults.baseParams({ admin: params.admin, diff --git a/test/integration/merkle-lockup/MerkleLockup.t.sol b/test/integration/merkle-lockup/MerkleLockup.t.sol index a37e3831..ba55de28 100644 --- a/test/integration/merkle-lockup/MerkleLockup.t.sol +++ b/test/integration/merkle-lockup/MerkleLockup.t.sol @@ -32,6 +32,22 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { }); } + function computeMerkleLLAddress() internal view returns (address) { + return computeMerkleLLAddress(users.admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); + } + + function computeMerkleLLAddress(address admin) internal view returns (address) { + return computeMerkleLLAddress(admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); + } + + function computeMerkleLLAddress(address admin, uint40 expiration) internal view returns (address) { + return computeMerkleLLAddress(admin, defaults.MERKLE_ROOT(), expiration); + } + + function computeMerkleLLAddress(address admin, bytes32 merkleRoot) internal view returns (address) { + return computeMerkleLLAddress(admin, merkleRoot, defaults.EXPIRATION()); + } + function createMerkleLL() internal returns (ISablierV2MerkleLL) { return createMerkleLL(users.admin, defaults.EXPIRATION()); } @@ -45,9 +61,6 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { } function createMerkleLL(address admin, uint40 expiration) internal returns (ISablierV2MerkleLL) { - // Increment the CREATE nonce for factory contract. - ++merkleLockupFactoryNonce; - return merkleLockupFactory.createMerkleLL({ baseParams: defaults.baseParams(admin, dai, expiration, defaults.MERKLE_ROOT()), lockupLinear: lockupLinear, @@ -70,6 +83,22 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { }); } + function computeMerkleLTAddress() internal view returns (address) { + return computeMerkleLTAddress(users.admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); + } + + function computeMerkleLTAddress(address admin) internal view returns (address) { + return computeMerkleLTAddress(admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); + } + + function computeMerkleLTAddress(address admin, uint40 expiration) internal view returns (address) { + return computeMerkleLTAddress(admin, defaults.MERKLE_ROOT(), expiration); + } + + function computeMerkleLTAddress(address admin, bytes32 merkleRoot) internal view returns (address) { + return computeMerkleLTAddress(admin, merkleRoot, defaults.EXPIRATION()); + } + function createMerkleLT() internal returns (ISablierV2MerkleLT) { return createMerkleLT(users.admin, defaults.EXPIRATION()); } @@ -83,9 +112,6 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { } function createMerkleLT(address admin, uint40 expiration) internal returns (ISablierV2MerkleLT) { - // Increment the CREATE nonce for factory contract. - ++merkleLockupFactoryNonce; - return merkleLockupFactory.createMerkleLT({ baseParams: defaults.baseParams(admin, dai, expiration, defaults.MERKLE_ROOT()), lockupTranched: lockupTranched, diff --git a/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.t.sol b/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.t.sol index cc9bbfe7..a5088c74 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.t.sol +++ b/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.t.sol @@ -12,6 +12,9 @@ import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; contract CreateMerkleLL_Integration_Test is MerkleLockup_Integration_Test { function setUp() public override { MerkleLockup_Integration_Test.setUp(); + + // Make alice the caller of createMerkleLT. + resetPrank(users.alice); } function test_RevertWhen_CampaignNameTooLong() external { @@ -41,9 +44,38 @@ contract CreateMerkleLL_Integration_Test is MerkleLockup_Integration_Test { _; } - function testFuzz_CreateMerkleLL(address admin, uint40 expiration) external whenCampaignNameNotTooLong { + /// @dev This test works because a default MerkleLockup contract is deployed in {Integration_Test.setUp} + function test_RevertGiven_CreatedAlready() external whenCampaignNameNotTooLong { + MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); + LockupLinear.Durations memory streamDurations = defaults.durations(); + uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); + uint256 recipientCount = defaults.RECIPIENT_COUNT(); + + // Expect a revert due to CREATE2. + vm.expectRevert(); + merkleLockupFactory.createMerkleLL({ + baseParams: baseParams, + lockupLinear: lockupLinear, + streamDurations: streamDurations, + aggregateAmount: aggregateAmount, + recipientCount: recipientCount + }); + } + + modifier givenNotCreatedAlready() { + _; + } + + function testFuzz_CreateMerkleLL( + address admin, + uint40 expiration + ) + external + whenCampaignNameNotTooLong + givenNotCreatedAlready + { vm.assume(admin != users.admin); - address expectedLL = vm.computeCreateAddress(address(merkleLockupFactory), ++merkleLockupFactoryNonce); + address expectedLL = computeMerkleLLAddress(admin, expiration); MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams({ admin: admin, diff --git a/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.tree b/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.tree index 3ad2c770..81de5f39 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.tree +++ b/test/integration/merkle-lockup/factory/create-merkle-ll/createMerkleLL.tree @@ -2,5 +2,8 @@ createMerkleLL.t.sol ├── when the campaign name is too long │ └── it should revert └── when the campaign name is not too long - ├── it should create the campaign - └── it should emit a {CreateMerkleLL} event + ├── given the campaign has been created already + │ └── it should revert + └── given the campaign has not been created already + ├── it should create the campaign + └── it should emit a {CreateMerkleLL} event diff --git a/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.t.sol b/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.t.sol index 1be80142..e7225abd 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.t.sol +++ b/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.t.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.22 <0.9.0; -import { ud2x18 } from "@prb/math/src/UD2x18.sol"; - import { Errors } from "src/libraries/Errors.sol"; import { ISablierV2MerkleLT } from "src/interfaces/ISablierV2MerkleLT.sol"; import { MerkleLockup, MerkleLT } from "src/types/DataTypes.sol"; @@ -12,50 +10,22 @@ import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; contract CreateMerkleLT_Integration_Test is MerkleLockup_Integration_Test { function setUp() public override { MerkleLockup_Integration_Test.setUp(); - } - modifier whenTotalPercentageNotOneHundred() { - _; + // Make alice the caller of createMerkleLT. + resetPrank(users.alice); } - function test_RevertWhen_TotalPercentageLessThanOneHundred() external whenTotalPercentageNotOneHundred { + function test_RevertWhen_CampaignNameTooLong() external { MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); - uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); - uint256 recipientCount = defaults.RECIPIENT_COUNT(); - MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); - tranchesWithPercentages[0].unlockPercentage = ud2x18(0.05e18); - tranchesWithPercentages[1].unlockPercentage = ud2x18(0.2e18); - - uint64 totalPercentage = - tranchesWithPercentages[0].unlockPercentage.unwrap() + tranchesWithPercentages[1].unlockPercentage.unwrap(); - - vm.expectRevert( - abi.encodeWithSelector( - Errors.SablierV2MerkleLockupFactory_TotalPercentageNotOneHundred.selector, totalPercentage - ) - ); - - merkleLockupFactory.createMerkleLT( - baseParams, lockupTranched, tranchesWithPercentages, aggregateAmount, recipientCount - ); - } - - function test_RevertWhen_TotalPercentageGreaterThanOneHundred() external whenTotalPercentageNotOneHundred { - MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); uint256 recipientCount = defaults.RECIPIENT_COUNT(); - MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); - tranchesWithPercentages[0].unlockPercentage = ud2x18(0.75e18); - tranchesWithPercentages[1].unlockPercentage = ud2x18(0.8e18); - - uint64 totalPercentage = - tranchesWithPercentages[0].unlockPercentage.unwrap() + tranchesWithPercentages[1].unlockPercentage.unwrap(); + baseParams.name = "this string is longer than 32 characters"; vm.expectRevert( abi.encodeWithSelector( - Errors.SablierV2MerkleLockupFactory_TotalPercentageNotOneHundred.selector, totalPercentage + Errors.SablierV2MerkleLockup_CampaignNameTooLong.selector, bytes(baseParams.name).length, 32 ) ); @@ -64,30 +34,25 @@ contract CreateMerkleLT_Integration_Test is MerkleLockup_Integration_Test { ); } - modifier whenTotalPercentageOneHundred() { + modifier whenCampaignNameNotTooLong() { _; } - function test_RevertWhen_CampaignNameTooLong() external whenTotalPercentageOneHundred { + /// @dev This test works because a default MerkleLockup contract is deployed in {Integration_Test.setUp} + function test_RevertGiven_CreatedAlready() external whenCampaignNameNotTooLong { MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); uint256 recipientCount = defaults.RECIPIENT_COUNT(); - baseParams.name = "this string is longer than 32 characters"; - - vm.expectRevert( - abi.encodeWithSelector( - Errors.SablierV2MerkleLockup_CampaignNameTooLong.selector, bytes(baseParams.name).length, 32 - ) - ); - + // Expect a revert due to CREATE2. + vm.expectRevert(); merkleLockupFactory.createMerkleLT( baseParams, lockupTranched, tranchesWithPercentages, aggregateAmount, recipientCount ); } - modifier whenCampaignNameNotTooLong() { + modifier givenNotCreatedAlready() { _; } @@ -96,11 +61,11 @@ contract CreateMerkleLT_Integration_Test is MerkleLockup_Integration_Test { uint40 expiration ) external - whenTotalPercentageOneHundred whenCampaignNameNotTooLong + givenNotCreatedAlready { vm.assume(admin != users.admin); - address expectedLT = vm.computeCreateAddress(address(merkleLockupFactory), ++merkleLockupFactoryNonce); + address expectedLT = computeMerkleLTAddress(admin, expiration); MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams({ admin: admin, diff --git a/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.tree b/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.tree index a2c91fc9..eda67426 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.tree +++ b/test/integration/merkle-lockup/factory/create-merkle-lt/createMerkleLT.tree @@ -1,12 +1,9 @@ -createMerkleLL.t.sol -├── when the total percentage does not equal 100% -│ ├── when the total percentage is less than 100% -│ │ └── it should revert -│ └── when the total percentage is greater than 100% -│ └── it should revert -└── when the total percentage equals 100% - ├── when the campaign name is too long +createMerkleLT.t.sol +├── when the campaign name is too long +│ └── it should revert +└── when the campaign name is not too long + ├── given the campaign has been created already │ └── it should revert - └── when the campaign name is not too long + └── given the campaign has not been created already ├── it should create the campaign └── it should emit a {CreateMerkleLT} event diff --git a/test/integration/merkle-lockup/lt/claim/claim.t.sol b/test/integration/merkle-lockup/lt/claim/claim.t.sol index 243fc995..2052bcea 100644 --- a/test/integration/merkle-lockup/lt/claim/claim.t.sol +++ b/test/integration/merkle-lockup/lt/claim/claim.t.sol @@ -2,11 +2,12 @@ pragma solidity >=0.8.22 <0.9.0; import { Arrays } from "@openzeppelin/contracts/utils/Arrays.sol"; +import { ud2x18 } from "@prb/math/src/UD2x18.sol"; import { Lockup, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; import { ISablierV2MerkleLT } from "src/interfaces/ISablierV2MerkleLT.sol"; import { Errors } from "src/libraries/Errors.sol"; -import { MerkleLockup } from "src/types/DataTypes.sol"; +import { MerkleLockup, MerkleLT } from "src/types/DataTypes.sol"; import { MerkleBuilder } from "../../../../utils/MerkleBuilder.sol"; import { Merkle } from "../../../../utils/Murky.sol"; @@ -20,7 +21,69 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { MerkleLockup_Integration_Test.setUp(); } - function test_RevertGiven_CampaignExpired() external { + modifier whenTotalPercentageNotOneHundred() { + _; + } + + function test_RevertWhen_TotalPercentageLessThanOneHundred() external whenTotalPercentageNotOneHundred { + // Create a MerkleLT campaign with a total percentage less than 100. + MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); + uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); + uint256 recipientCount = defaults.RECIPIENT_COUNT(); + + MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); + tranchesWithPercentages[0].unlockPercentage = ud2x18(0.05e18); + tranchesWithPercentages[1].unlockPercentage = ud2x18(0.2e18); + + uint64 totalPercentage = + tranchesWithPercentages[0].unlockPercentage.unwrap() + tranchesWithPercentages[1].unlockPercentage.unwrap(); + + merkleLT = merkleLockupFactory.createMerkleLT( + baseParams, lockupTranched, tranchesWithPercentages, aggregateAmount, recipientCount + ); + + // Claim an airstream. + bytes32[] memory merkleProof = defaults.index1Proof(); + + vm.expectRevert( + abi.encodeWithSelector(Errors.SablierV2MerkleLT_TotalPercentageNotOneHundred.selector, totalPercentage) + ); + + merkleLT.claim({ index: 1, recipient: users.recipient1, amount: 1, merkleProof: merkleProof }); + } + + function test_RevertWhen_TotalPercentageGreaterThanOneHundred() external whenTotalPercentageNotOneHundred { + // Create a MerkleLT campaign with a total percentage less than 100. + MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); + uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); + uint256 recipientCount = defaults.RECIPIENT_COUNT(); + + MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); + tranchesWithPercentages[0].unlockPercentage = ud2x18(0.75e18); + tranchesWithPercentages[1].unlockPercentage = ud2x18(0.8e18); + + uint64 totalPercentage = + tranchesWithPercentages[0].unlockPercentage.unwrap() + tranchesWithPercentages[1].unlockPercentage.unwrap(); + + merkleLT = merkleLockupFactory.createMerkleLT( + baseParams, lockupTranched, tranchesWithPercentages, aggregateAmount, recipientCount + ); + + // Claim an airstream. + bytes32[] memory merkleProof = defaults.index1Proof(); + + vm.expectRevert( + abi.encodeWithSelector(Errors.SablierV2MerkleLT_TotalPercentageNotOneHundred.selector, totalPercentage) + ); + + merkleLT.claim({ index: 1, recipient: users.recipient1, amount: 1, merkleProof: merkleProof }); + } + + modifier whenTotalPercentageOneHundred() { + _; + } + + function test_RevertGiven_CampaignExpired() external whenTotalPercentageOneHundred { uint40 expiration = defaults.EXPIRATION(); uint256 warpTime = expiration + 1 seconds; bytes32[] memory merkleProof; @@ -35,7 +98,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { _; } - function test_RevertGiven_AlreadyClaimed() external givenCampaignNotExpired { + function test_RevertGiven_AlreadyClaimed() external whenTotalPercentageOneHundred givenCampaignNotExpired { claimLT(); uint256 index1 = defaults.INDEX1(); uint128 amount = defaults.CLAIM_AMOUNT(); @@ -54,6 +117,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { function test_RevertWhen_InvalidIndex() external + whenTotalPercentageOneHundred givenCampaignNotExpired givenNotClaimed givenNotIncludedInMerkleTree @@ -67,6 +131,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { function test_RevertWhen_InvalidRecipient() external + whenTotalPercentageOneHundred givenCampaignNotExpired givenNotClaimed givenNotIncludedInMerkleTree @@ -81,6 +146,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { function test_RevertWhen_InvalidAmount() external + whenTotalPercentageOneHundred givenCampaignNotExpired givenNotClaimed givenNotIncludedInMerkleTree @@ -94,6 +160,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { function test_RevertWhen_InvalidMerkleProof() external + whenTotalPercentageOneHundred givenCampaignNotExpired givenNotClaimed givenNotIncludedInMerkleTree @@ -114,6 +181,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { function test_Claim_CalculatedAmountsSumNotEqualClaimAmount() external + whenTotalPercentageOneHundred givenCampaignNotExpired givenNotClaimed givenIncludedInMerkleTree @@ -179,6 +247,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { function test_Claim() external + whenTotalPercentageOneHundred givenCampaignNotExpired givenNotClaimed givenIncludedInMerkleTree diff --git a/test/integration/merkle-lockup/lt/claim/claim.tree b/test/integration/merkle-lockup/lt/claim/claim.tree index 390d2248..ab986c29 100644 --- a/test/integration/merkle-lockup/lt/claim/claim.tree +++ b/test/integration/merkle-lockup/lt/claim/claim.tree @@ -1,26 +1,33 @@ claim.t.sol -├── given the campaign has expired -│ └── it should revert -└── given the campaign has not expired - ├── given the recipient has claimed - │ └── it should revert - └── given the recipient has not claimed - ├── given the claim is not included in the Merkle tree - │ ├── when the index is not valid - │ │ └── it should revert - │ ├── when the recipient address is not valid - │ │ └── it should revert - │ ├── when the amount is not valid - │ │ └── it should revert - │ └── when the Merkle proof is not valid - │ └── it should revert - └── given the claim is included in the Merkle tree - ├── when the sum of the calculated amounts does not equal the claim amount - │ ├── it should adjust the last tranche amount - │ ├── it should mark the index as claimed - │ ├── it should create a stream - │ └── it should emit a {Claim} event - └── when the sum of the calculated amounts equals the claim amount - ├── it should mark the index as claimed - ├── it should create a stream - └── it should emit a {Claim} event +. +├── when the total percentage does not equal 100% +│ ├── when the total percentage is less than 100% +│ │ └── it should revert +│ └── when the total percentage is greater than 100% +│ └── it should revert +└── when the total percentage equals 100% + ├── given the campaign has expired + │ └── it should revert + └── given the campaign has not expired + ├── given the recipient has claimed + │ └── it should revert + └── given the recipient has not claimed + ├── given the claim is not included in the Merkle tree + │ ├── when the index is not valid + │ │ └── it should revert + │ ├── when the recipient address is not valid + │ │ └── it should revert + │ ├── when the amount is not valid + │ │ └── it should revert + │ └── when the Merkle proof is not valid + │ └── it should revert + └── given the claim is included in the Merkle tree + ├── when the sum of the calculated amounts does not equal the claim amount + │ ├── it should adjust the last tranche amount + │ ├── it should mark the index as claimed + │ ├── it should create a stream + │ └── it should emit a {Claim} event + └── when the sum of the calculated amounts equals the claim amount + ├── it should mark the index as claimed + ├── it should create a stream + └── it should emit a {Claim} event diff --git a/test/integration/merkle-lockup/lt/constructor/constructor.t.sol b/test/integration/merkle-lockup/lt/constructor/constructor.t.sol index 660ba300..2b98685f 100644 --- a/test/integration/merkle-lockup/lt/constructor/constructor.t.sol +++ b/test/integration/merkle-lockup/lt/constructor/constructor.t.sol @@ -18,6 +18,7 @@ contract Constructor_MerkleLT_Integration_Test is MerkleLockup_Integration_Test uint40 actualExpiration; address actualLockupTranched; bytes32 actualMerkleRoot; + uint64 actualTotalPercentage; MerkleLT.TrancheWithPercentage[] actualTranchesWithPercentages; bool actualTransferable; address expectedAdmin; @@ -29,6 +30,7 @@ contract Constructor_MerkleLT_Integration_Test is MerkleLockup_Integration_Test address expectedLockupTranched; bytes32 expectedMerkleRoot; bytes32 expectedName; + uint64 expectedTotalPercentage; MerkleLT.TrancheWithPercentage[] expectedTranchesWithPercentages; bool expectedTransferable; } @@ -75,6 +77,10 @@ contract Constructor_MerkleLT_Integration_Test is MerkleLockup_Integration_Test vars.expectedMerkleRoot = defaults.MERKLE_ROOT(); assertEq(vars.actualMerkleRoot, vars.expectedMerkleRoot, "merkleRoot"); + vars.actualTotalPercentage = constructedLT.TOTAL_PERCENTAGE(); + vars.expectedTotalPercentage = defaults.TOTAL_PERCENTAGE(); + assertEq(vars.actualTotalPercentage, vars.expectedTotalPercentage, "totalPercentage"); + vars.actualTranchesWithPercentages = constructedLT.getTranchesWithPercentages(); vars.expectedTranchesWithPercentages = defaults.tranchesWithPercentages(); assertEq(vars.actualTranchesWithPercentages, vars.expectedTranchesWithPercentages, "tranchesWithPercentages"); diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index 6ace3290..178cadf7 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -3,7 +3,7 @@ pragma solidity >=0.8.22 <0.9.0; import { Arrays } from "@openzeppelin/contracts/utils/Arrays.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { ud2x18 } from "@prb/math/src/UD2x18.sol"; +import { ud2x18, uUNIT } from "@prb/math/src/UD2x18.sol"; import { UD60x18, ud } from "@prb/math/src/UD60x18.sol"; import { Broker, LockupDynamic, LockupLinear, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; @@ -55,6 +55,7 @@ contract Defaults is Merkle { bytes32 public immutable MERKLE_ROOT; string public constant NAME = "Airdrop Campaign"; bytes32 public constant NAME_BYTES32 = bytes32(abi.encodePacked("Airdrop Campaign")); + uint64 public constant TOTAL_PERCENTAGE = uUNIT; bool public constant TRANSFERABLE = false; /*////////////////////////////////////////////////////////////////////////// From 78835c53d7e2434c50cc2ccaad2299c0d258efc4 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Fri, 21 Jun 2024 09:45:37 +0100 Subject: [PATCH 55/61] feat: view function `isPercentagesSum100` (#357) * feat: isValidMerkleLT * chore: update precompile * chore: update bun lockfile * docs: improve natspec * refactor: isPercentagesSum100 * docs: polish NatSpec test: polish tests --------- Co-authored-by: andreivladbrg Co-authored-by: Paul Razvan Berg --- bun.lockb | Bin 42555 -> 42555 bytes precompiles/Precompiles.sol | 2 +- src/SablierV2MerkleLockupFactory.sol | 19 ++++++ .../ISablierV2MerkleLockupFactory.sol | 13 ++++ .../isPercentagesSum100.t.sol | 60 ++++++++++++++++++ .../isPercentagesSum100.tree | 11 ++++ 6 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 test/integration/merkle-lockup/factory/is-percentages-sum-100/isPercentagesSum100.t.sol create mode 100644 test/integration/merkle-lockup/factory/is-percentages-sum-100/isPercentagesSum100.tree diff --git a/bun.lockb b/bun.lockb index 42d19a44cf6d59680d13267c2c2a7359e2063446..e505bbc2779419e59df455d3fff6431a3150ffc4 100755 GIT binary patch delta 583 zcmdmehH3X1rU`lq78dV|L^9v!UuzM+Rcklha_62Mv({?|&X`!@EO@eGh4DteC?(x)_M8r43=AeP z6-*^!lV3^LZGIt@!fgq1M*;RV|#E0_~z&KO`oX zx_VEJaOR!7*V&uVeKMy@_+$!JDJzn zd-DO;7G1`K$*#r5jES2oi~X4ty?`$B1Y%&AFt`G-I}m#Su@4Y?gYf2;rC&KGyERBn z{!wqjICrvcgCyg!$@l9e88s(Y1KD+xr#ARbPH2$Y{HURoh26}+AjvQ(W%9vp>CK(3 zYlKB|GxIWY6HAga(u(zpQWJ9u5=$~B3--uNmYQw5`NYgX#>rD=noLfaX|Oqb_7g4u Dzf7f;v92%mODiIL(|< zk;rvq&dqb`HD$jiJFsbRus)w^mN#P^&*loY5~0a!q-;1J0yUWcH8D;u89gU+x`a>O;lejL&&7+;d-6^oFT<60GOx4u<^!%Rx{QgFU5kww zlQvft`!gwe16}0>#O^@s3dF!bVekNAPZ)pm%hIo$Vz7{h1uqk0oS~k<=0.8.22; +import { uUNIT } from "@prb/math/src/UD2x18.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; @@ -15,6 +16,24 @@ import { MerkleLockup, MerkleLT } from "./types/DataTypes.sol"; /// @title SablierV2MerkleLockupFactory /// @notice See the documentation in {ISablierV2MerkleLockupFactory}. contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { + /*////////////////////////////////////////////////////////////////////////// + USER-FACING CONSTANT FUNCTIONS + //////////////////////////////////////////////////////////////////////////*/ + + /// @inheritdoc ISablierV2MerkleLockupFactory + function isPercentagesSum100(MerkleLT.TrancheWithPercentage[] calldata tranches) + external + pure + override + returns (bool result) + { + uint64 totalPercentage; + for (uint256 i = 0; i < tranches.length; ++i) { + totalPercentage += tranches[i].unlockPercentage.unwrap(); + } + return totalPercentage == uUNIT; + } + /*////////////////////////////////////////////////////////////////////////// USER-FACING NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ diff --git a/src/interfaces/ISablierV2MerkleLockupFactory.sol b/src/interfaces/ISablierV2MerkleLockupFactory.sol index beaf81e7..8574492f 100644 --- a/src/interfaces/ISablierV2MerkleLockupFactory.sol +++ b/src/interfaces/ISablierV2MerkleLockupFactory.sol @@ -37,6 +37,19 @@ interface ISablierV2MerkleLockupFactory { uint256 recipientCount ); + /*////////////////////////////////////////////////////////////////////////// + CONSTANT FUNCTIONS + //////////////////////////////////////////////////////////////////////////*/ + + /// @notice Verifies if the sum of percentages in `tranches` equals 100% , i.e. 1e18. + /// @dev Reverts if the sum of percentages overflows. + /// @param tranches The tranches with their respective unlock percentages. + /// @return result True if the sum of percentages equals 100%, otherwise false. + function isPercentagesSum100(MerkleLT.TrancheWithPercentage[] calldata tranches) + external + pure + returns (bool result); + /*////////////////////////////////////////////////////////////////////////// NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ diff --git a/test/integration/merkle-lockup/factory/is-percentages-sum-100/isPercentagesSum100.t.sol b/test/integration/merkle-lockup/factory/is-percentages-sum-100/isPercentagesSum100.t.sol new file mode 100644 index 00000000..24f94da1 --- /dev/null +++ b/test/integration/merkle-lockup/factory/is-percentages-sum-100/isPercentagesSum100.t.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22 <0.9.0; + +import { MAX_UD2x18, ud2x18 } from "@prb/math/src/UD2x18.sol"; + +import { MerkleLT } from "src/types/DataTypes.sol"; + +import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol"; + +contract IsPercentagesSum100_Integration_Test is MerkleLockup_Integration_Test { + function test_RevertWhen_SumOverflow() public { + MerkleLT.TrancheWithPercentage[] memory tranches = defaults.tranchesWithPercentages(); + tranches[0].unlockPercentage = MAX_UD2x18; + + vm.expectRevert(); + merkleLockupFactory.isPercentagesSum100(tranches); + } + + modifier whenSumDoesNotOverflow() { + _; + } + + modifier whenTotalPercentageNotOneHundred() { + _; + } + + function test_TotalPercentageLessThanOneHundred() + external + view + whenSumDoesNotOverflow + whenTotalPercentageNotOneHundred + { + MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); + tranchesWithPercentages[0].unlockPercentage = ud2x18(0.05e18); + tranchesWithPercentages[1].unlockPercentage = ud2x18(0.2e18); + + assertFalse(merkleLockupFactory.isPercentagesSum100(tranchesWithPercentages), "isPercentagesSum100"); + } + + function test_TotalPercentageGreaterThanOneHundred() + external + view + whenSumDoesNotOverflow + whenTotalPercentageNotOneHundred + { + MerkleLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); + tranchesWithPercentages[0].unlockPercentage = ud2x18(0.5e18); + tranchesWithPercentages[1].unlockPercentage = ud2x18(0.6e18); + + assertFalse(merkleLockupFactory.isPercentagesSum100(tranchesWithPercentages), "isPercentagesSum100"); + } + + modifier whenTotalPercentageOneHundred() { + _; + } + + function test_IsPercentagesSum100() external view whenSumDoesNotOverflow whenTotalPercentageOneHundred { + assertTrue(merkleLockupFactory.isPercentagesSum100(defaults.tranchesWithPercentages()), "isPercentagesSum100"); + } +} diff --git a/test/integration/merkle-lockup/factory/is-percentages-sum-100/isPercentagesSum100.tree b/test/integration/merkle-lockup/factory/is-percentages-sum-100/isPercentagesSum100.tree new file mode 100644 index 00000000..d500edc0 --- /dev/null +++ b/test/integration/merkle-lockup/factory/is-percentages-sum-100/isPercentagesSum100.tree @@ -0,0 +1,11 @@ +isPercentagesSum100.t.sol +├── when the sum of the percentages overflows +│ └── it should revert +└── when the sum of the percentages does not overflow + ├── when the sum of the percentages does not equal 100% + │ ├── when the sum of the percentages is less than 100% + │ │ └── it should return false + │ └── when the sum of the percentages is greater than 100% + │ └── it should return false + └── when the sum of the percentages equals 100% + └── it should return true From 9d7a15b0128d549cbac7e33ab2593cfdbf229fc7 Mon Sep 17 00:00:00 2001 From: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:05:43 +0300 Subject: [PATCH 56/61] perf: declare a count for tranches length (#358) * feat: isValidMerkleLT * chore: update precompile * perf: declare a count for tranches length * chore: update precompiles --------- Co-authored-by: smol-ninja --- precompiles/Precompiles.sol | 2 +- src/SablierV2MerkleLT.sol | 4 +++- src/SablierV2MerkleLockupFactory.sol | 15 ++++++++++----- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/precompiles/Precompiles.sol b/precompiles/Precompiles.sol index 6e5c6946..738c5213 100644 --- a/precompiles/Precompiles.sol +++ b/precompiles/Precompiles.sol @@ -19,7 +19,7 @@ contract Precompiles { bytes public constant BYTECODE_BATCH_LOCKUP = hex"60808060405234601557611e0a908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c806337266dd3146111a357806349a32c4014610e3e578063606ef87514610b025780639e743f29146107a6578063a514f83e1461040c5763f7ca34eb1461005b575f80fd5b3461033c5761006936611512565b91909282156103e4575f905f5b8481106103b057506001600160a01b036100939116918383611a7a565b61009c83611758565b926001600160a01b035f9316925b8181106100c357604051806100bf8782611587565b0390f35b6100ce818388611a17565b6100d7906117a7565b90826100e482828a611a17565b6020016100f0906117a7565b6100fb83838b611a17565b60400161010790611643565b9389610114858583611a17565b606001610120906117bb565b61012b868684611a17565b608001610137906117bb565b610142878785611a17565b60a00161014e90611a57565b918761015b818987611a17565b60c0810161016891611926565b98610174929196611a17565b60e0019360405195610185876116c6565b6001600160a01b0316865260208601966001600160a01b0316875260408601996fffffffffffffffffffffffffffffffff168a5260608601978d895260808701921515835260a08701931515845260c087019464ffffffffff16855236906101ec9261197a565b9360e08601948552366101fe916118c8565b966101008601978852604051998a977f31df3d480000000000000000000000000000000000000000000000000000000089526004890160209052610164890197516001600160a01b031660248a0152516001600160a01b03166044890152516fffffffffffffffffffffffffffffffff166064880152516001600160a01b0316608487015251151560a486015251151560c48501525164ffffffffff1660e48401525190610104830161014090528151809152610184830191602001905f905b808210610353575050925180516001600160a01b03166101248401526020908101516101448401529250819003815f885af18015610348575f90610312575b6001925061030b8288611901565b52016100aa565b506020823d8211610340575b8161032b602093836116ff565b8101031261033c57600191516102fd565b5f80fd5b3d915061031e565b6040513d5f823e3d90fd5b919493509160206060826103a1600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b019501920186939492916102be565b916001906fffffffffffffffffffffffffffffffff6103db60406103d5878a8c611a17565b01611643565b16019201610076565b7ff8bf106c000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461033c5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033c576104436115c0565b61044b6114cb565b906044359167ffffffffffffffff831161033c573660238401121561033c5782600401359267ffffffffffffffff841161033c57602481019060243691610140870201011161033c5783156103e45790915f9190825b85811061077457506001600160a01b036104be9116928484611a7a565b6104c784611758565b926001600160a01b03165f5b8581106104e857604051806100bf8782611587565b6104fb6104f6828886611a69565b6117a7565b90610512602061050c838a88611a69565b016117a7565b878561052460406103d5868585611a69565b61053a6060610534878686611a69565b016117bb565b906101006105648761055d8188610557608061053484848d611a69565b98611a69565b968c611a69565b01906001600160a01b036040519861057b8a611660565b1688526001600160a01b0360208901961686526fffffffffffffffffffffffffffffffff6040890191168152606088019189835260808901931515845260a08901941515855260607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60873603011261033c57604051956105fa876116e3565b61060660a08201611839565b875261061460c08201611839565b602088015260e00161062590611839565b604087015260c089019586523661063b916118c8565b9560e08901968752604051987f53b15727000000000000000000000000000000000000000000000000000000008a52516001600160a01b031660048a0152516001600160a01b03166024890152516fffffffffffffffffffffffffffffffff166044880152516001600160a01b03166064870152511515608486015251151560a485015251805164ffffffffff1660c4850152602081015164ffffffffff1660e48501526040015164ffffffffff1661010484015251610124830161071291602080916001600160a01b0381511684520151910152565b8180865a925f61016492602095f18015610348575f90610742575b6001925061073b8288611901565b52016104d3565b506020823d821161076c575b8161075b602093836116ff565b8101031261033c576001915161072d565b3d915061074e565b93926001906fffffffffffffffffffffffffffffffff61079a60406103d5898b89611a69565b160194019392936104a1565b3461033c576107b436611512565b91909282156103e4575f905f5b848110610ad457506001600160a01b036107de9116918383611a7a565b6107e783611758565b926001600160a01b035f9316925b81811061080a57604051806100bf8782611587565b610815818388611a17565b61081e906117a7565b908261082b82828a611a17565b602001610837906117a7565b61084283838b611a17565b60400161084e90611643565b938961085b858583611a17565b606001610867906117bb565b610872868684611a17565b60800161087e906117bb565b610889878785611a17565b60a00161089590611a57565b91876108a2818987611a17565b60c081016108af916117c8565b986108bb929196611a17565b60e00193604051956108cc876116c6565b6001600160a01b0316865260208601966001600160a01b0316875260408601996fffffffffffffffffffffffffffffffff168a5260608601978d895260808701921515835260a08701931515845260c087019464ffffffffff16855236906109339261184b565b9360e0860194855236610945916118c8565b966101008601978852604051998a977f32fbe22b0000000000000000000000000000000000000000000000000000000089526004890160209052610164890197516001600160a01b031660248a0152516001600160a01b03166044890152516fffffffffffffffffffffffffffffffff166064880152516001600160a01b0316608487015251151560a486015251151560c48501525164ffffffffff1660e48401525190610104830161014090528151809152610184830191602001905f905b808210610a8b575050925180516001600160a01b03166101248401526020908101516101448401529250819003815f885af18015610348575f90610a59575b60019250610a528288611901565b52016107f5565b506020823d8211610a83575b81610a72602093836116ff565b8101031261033c5760019151610a44565b3d9150610a65565b91949350916020604082610ac5600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b01950192018693949291610a05565b916001906fffffffffffffffffffffffffffffffff610af960406103d5878a8c611a17565b160192016107c1565b3461033c57610b1036611512565b91909282156103e4575f905f5b848110610e1057506001600160a01b03610b3a9116918383611a7a565b610b4383611758565b926001600160a01b035f9316925b818110610b6657604051806100bf8782611587565b610b718183886115d6565b610b7a906117a7565b9082610b8782828a6115d6565b602001610b93906117a7565b610b9e83838b6115d6565b604001610baa90611643565b9389610bb78585836115d6565b606001610bc3906117bb565b610bce8686846115d6565b608001610bda906117bb565b9086610be78188866115d6565b60a08101610bf491611926565b97610c009291956115d6565b60c0019260405194610c1186611660565b6001600160a01b0316855260208501956001600160a01b0316865260408501986fffffffffffffffffffffffffffffffff16895260608501968c885260808601921515835260a0860193151584523690610c6a9261197a565b9260c0850193845236610c7c916118c8565b9560e085019687526040519889967f54c022920000000000000000000000000000000000000000000000000000000088526004880160209052610144880196516001600160a01b03166024890152516001600160a01b03166044880152516fffffffffffffffffffffffffffffffff166064870152516001600160a01b0316608486015251151560a485015251151560c4840152519060e4830161012090528151809152610164830191602001905f905b808210610db3575050925180516001600160a01b03166101048401526020908101516101248401529250819003815f885af18015610348575f90610d81575b60019250610d7a8288611901565b5201610b51565b506020823d8211610dab575b81610d9a602093836116ff565b8101031261033c5760019151610d6c565b3d9150610d8d565b91949350916020606082610e01600194895164ffffffffff604080926fffffffffffffffffffffffffffffffff815116855267ffffffffffffffff6020820151166020860152015116910152565b01950192018693949291610d2d565b916001906fffffffffffffffffffffffffffffffff610e3560406103d5878a8c6115d6565b16019201610b1d565b3461033c5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261033c57610e756115c0565b610e7d6114cb565b906044359167ffffffffffffffff831161033c573660238401121561033c5782600401359267ffffffffffffffff841161033c57602481019060243691610120870201011161033c5783156103e45790915f9190825b85811061117157506001600160a01b03610ef09116928484611a7a565b610ef984611758565b926001600160a01b03165f5b858110610f1a57604051806100bf8782611587565b610f286104f6828886611915565b90610f39602061050c838a88611915565b8785610f4b60406103d5868585611915565b610f5b6060610534878686611915565b9060e0610f8487610f7d8188610f77608061053484848d611915565b98611915565b968c611915565b01906001600160a01b0360405198610f9b8a611660565b1688526001600160a01b0360208901961686526fffffffffffffffffffffffffffffffff6040890191168152606088019189835260808901931515845260a08901941515855260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60873603011261033c576040519561101a876116aa565b61102660a08201611839565b875260c00161103490611839565b602087015260c089019586523661104a916118c8565b9560e08901968752604051987fab167ccc000000000000000000000000000000000000000000000000000000008a52516001600160a01b031660048a0152516001600160a01b03166024890152516fffffffffffffffffffffffffffffffff166044880152516001600160a01b03166064870152511515608486015251151560a485015251805164ffffffffff1660c48501526020015164ffffffffff1660e484015251610104830161110f91602080916001600160a01b0381511684520151910152565b8180865a925f61014492602095f18015610348575f9061113f575b600192506111388288611901565b5201610f05565b506020823d8211611169575b81611158602093836116ff565b8101031261033c576001915161112a565b3d915061114b565b93926001906fffffffffffffffffffffffffffffffff61119760406103d5898b89611915565b16019401939293610ed3565b3461033c576111b136611512565b91909282156103e4575f905f5b84811061149d57506001600160a01b036111db9116918383611a7a565b6111e483611758565b926001600160a01b035f9316925b81811061120757604051806100bf8782611587565b6112128183886115d6565b61121b906117a7565b908261122882828a6115d6565b602001611234906117a7565b61123f83838b6115d6565b60400161124b90611643565b93896112588585836115d6565b606001611264906117bb565b61126f8686846115d6565b60800161127b906117bb565b90866112888188866115d6565b60a08101611295916117c8565b976112a19291956115d6565b60c00192604051946112b286611660565b6001600160a01b0316855260208501956001600160a01b0316865260408501986fffffffffffffffffffffffffffffffff16895260608501968c885260808601921515835260a086019315158452369061130b9261184b565b9260c085019384523661131d916118c8565b9560e085019687526040519889967f897f362b0000000000000000000000000000000000000000000000000000000088526004880160209052610144880196516001600160a01b03166024890152516001600160a01b03166044880152516fffffffffffffffffffffffffffffffff166064870152516001600160a01b0316608486015251151560a485015251151560c4840152519060e4830161012090528151809152610164830191602001905f905b808210611454575050925180516001600160a01b03166101048401526020908101516101248401529250819003815f885af18015610348575f90611422575b6001925061141b8288611901565b52016111f2565b506020823d821161144c575b8161143b602093836116ff565b8101031261033c576001915161140d565b3d915061142e565b9194935091602060408261148e600194895164ffffffffff602080926fffffffffffffffffffffffffffffffff8151168552015116910152565b019501920186939492916113ce565b916001906fffffffffffffffffffffffffffffffff6114c260406103d5878a8c6115d6565b160192016111be565b602435906001600160a01b038216820361033c57565b9181601f8401121561033c5782359167ffffffffffffffff831161033c576020808501948460051b01011161033c57565b60607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc82011261033c576004356001600160a01b038116810361033c57916024356001600160a01b038116810361033c57916044359067ffffffffffffffff821161033c57611583916004016114e1565b9091565b60206040818301928281528451809452019201905f5b8181106115aa5750505090565b825184526020938401939092019160010161159d565b600435906001600160a01b038216820361033c57565b91908110156116165760051b810135907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff018136030182121561033c570190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b356fffffffffffffffffffffffffffffffff8116810361033c5790565b610100810190811067ffffffffffffffff82111761167d57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6040810190811067ffffffffffffffff82111761167d57604052565b610120810190811067ffffffffffffffff82111761167d57604052565b6060810190811067ffffffffffffffff82111761167d57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761167d57604052565b67ffffffffffffffff811161167d5760051b60200190565b9061176282611740565b61176f60405191826116ff565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061179d8294611740565b0190602036910137565b356001600160a01b038116810361033c5790565b35801515810361033c5790565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561033c570180359067ffffffffffffffff821161033c57602001918160061b3603831361033c57565b35906fffffffffffffffffffffffffffffffff8216820361033c57565b359064ffffffffff8216820361033c57565b92919261185782611740565b9361186560405195866116ff565b602085848152019260061b82019181831161033c57925b8284106118895750505050565b60408483031261033c57602060409182516118a3816116aa565b6118ac8761181c565b81526118b9838801611839565b8382015281520193019261187c565b919082604091031261033c576040516118e0816116aa565b809280356001600160a01b038116810361033c578252602090810135910152565b80518210156116165760209160051b010190565b919081101561161657610120020190565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18136030182121561033c570180359067ffffffffffffffff821161033c5760200191606082023603831361033c57565b92919261198682611740565b9361199460405195866116ff565b606060208685815201930282019181831161033c57925b8284106119b85750505050565b60608483031261033c57604051906119cf826116e3565b6119d88561181c565b825260208501359067ffffffffffffffff8216820361033c5782602092836060950152611a0760408801611839565b60408201528152019301926119ab565b91908110156116165760051b810135907ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee18136030182121561033c570190565b3564ffffffffff8116810361033c5790565b919081101561161657610140020190565b9190611acf6040517f23b872dd00000000000000000000000000000000000000000000000000000000602082015233602482015230604482015283606482015260648152611ac96084826116ff565b82611c8f565b6001600160a01b0381166001600160a01b03604051947fdd62ed3e0000000000000000000000000000000000000000000000000000000086523060048701521693846024820152602081604481855afa80156103485784915f91611c42575b5010611b3b575b50505050565b5f806040519460208601907f095ea7b3000000000000000000000000000000000000000000000000000000008252876024880152604487015260448652611b836064876116ff565b85519082855af190611b93611d14565b82611c10575b5081611c05575b5015611bad575b80611b35565b611bf8611bfd93604051907f095ea7b300000000000000000000000000000000000000000000000000000000602083015260248201525f604482015260448152611ac96064826116ff565b611c8f565b5f8080611ba7565b90503b15155f611ba0565b80519192508115918215611c28575b5050905f611b99565b611c3b9250602080918301019101611c77565b5f80611c1f565b9150506020813d602011611c6f575b81611c5e602093836116ff565b8101031261033c578390515f611b2e565b3d9150611c51565b9081602091031261033c5751801515810361033c5790565b5f806001600160a01b03611cb893169360208151910182865af1611cb1611d14565b9083611d71565b8051908115159182611cf9575b5050611cce5750565b7f5274afe7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b611d0c9250602080918301019101611c77565b155f80611cc5565b3d15611d6c573d9067ffffffffffffffff821161167d5760405191611d6160207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846116ff565b82523d5f602084013e565b606090565b90611dae5750805115611d8657805190602001fd5b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b81511580611df4575b611dbf575090565b6001600160a01b03907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b15611db756fea164736f6c634300081a000a"; bytes public constant BYTECODE_MERKLE_LOCKUP_FACTORY = - hex"6080806040523460155761419f908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c80631e323876146105285780634d7c0f111461041e5763769bed201461003a575f80fd5b3461041a5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261041a5760043567ffffffffffffffff811161041a576100899036906004016109fa565b6024359073ffffffffffffffffffffffffffffffffffffffff82169182810361041a5760407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc36011261041a57604051906100e382610946565b60443564ffffffffff8116810361041a57825260643564ffffffffff8116810361041a57602083015282519060208401511515836040860151926060870151608088015191604051806020810194602086526040820161014291610b1c565b03601f19810182526101549082610962565b60a08a01519360c08b015160405181819251908160208401916020019161017a92610afb565b81010380825261018d9060200182610962565b61019690610b41565b9060e08c0151151592604051956020870198896101c59164ffffffffff60208092828151168552015116910152565b604087526101d4606088610962565b6040519a8b9a60208c019d8e3360601b905260601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660348d015260f81b60488c015260d81b7fffffffffff0000000000000000000000000000000000000000000000000000001660498b015260601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016604e8a015251908160628a0161027c92610afb565b8701946062860152608285015260f81b60a284015260601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660a383015251918260b783016102cb92610afb565b0160620103605501601f19810182526102e49082610962565b51902060405161174880820182811067ffffffffffffffff8211176103ed578291610c7383396080815261034160406103206080840189610bd2565b92896020820152018664ffffffffff60208092828151168552015116910152565b03905ff59283156103e2576103a56103c7937f2ba0fe49588281dbb122dd3b7f3e2b3396338f70dbe3c62bf3e3888b4ba7ffb89273ffffffffffffffffffffffffffffffffffffffff6020971695869560405194859460c0865260c0860190610bd2565b9289850152604084019064ffffffffff60208092828151168552015116910152565b608435608083015260a43560a08301520390a2604051908152f35b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f80fd5b3461041a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261041a5760043567ffffffffffffffff811161041a573660238201121561041a5780600401359067ffffffffffffffff821161041a573660248360061b8301011161041a575f90815b838310156105095760248360061b830101359067ffffffffffffffff821680920361041a5767ffffffffffffffff160167ffffffffffffffff81116104dc57600190920191610492565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b602090670de0b6b3a764000067ffffffffffffffff6040519216148152f35b3461041a5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261041a5760043567ffffffffffffffff811161041a576105779036906004016109fa565b6024359073ffffffffffffffffffffffffffffffffffffffff82169081830361041a576044359067ffffffffffffffff821161041a573660238301121561041a57816004013567ffffffffffffffff81116103ed57604051926105e060208360051b0185610962565b8184526024602085019260061b8201019036821161041a57949594602401915b8183106108f3575050505f935f938351955b8686101561063a5760019064ffffffffff6020808960051b8901015101511601950194610612565b84918451906020860151151584604088015192606089015160808a015191604051806020810194602086526040820161067291610b1c565b03601f19810182526106849082610962565b8b60a08101519460c08201516040518181925190816020840191602001916106ab92610afb565b8101038082526106be9060200182610962565b6106c790610b41565b9160e001511515926040519586602081019960208b52604082016106ea91610b82565b03601f19810188526106fc9088610962565b6040519a8b9a60208c019d8e3360601b905260601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660348d015260f81b60488c015260d81b7fffffffffff0000000000000000000000000000000000000000000000000000001660498b015260601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016604e8a015251908160628a016107a492610afb565b8701946062860152608285015260f81b60a284015260601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660a383015251918260b783016107f392610afb565b0160620103605501601f198101825261080c9082610962565b519020604051611dd88082019082821067ffffffffffffffff8311176103ed57829161085d916123bb843960608152610848606082018a610bd2565b90886020820152604081830391015286610b82565b03905ff580156103e2576020947ffe44018cf74992b2720702385a1728bd329dd136e4f651203176c81c12710a8b926108d273ffffffffffffffffffffffffffffffffffffffff6108c0941696879660405195869560c0875260c0870190610bd2565b918a8601528482036040860152610b82565b906060830152606435608083015260843560a08301520390a2604051908152f35b60408397969736031261041a576040519061090d82610946565b83359067ffffffffffffffff8216820361041a578260209260409452610934838701610992565b83820152815201920191959495610600565b6040810190811067ffffffffffffffff8211176103ed57604052565b90601f601f19910116810190811067ffffffffffffffff8211176103ed57604052565b3590811515820361041a57565b359064ffffffffff8216820361041a57565b81601f8201121561041a5780359067ffffffffffffffff82116103ed57604051926109d96020601f19601f8601160185610962565b8284526020838301011161041a57815f926020809301838601378301015290565b9190916101008184031261041a5760405190610100820182811067ffffffffffffffff8211176103ed576040528193813573ffffffffffffffffffffffffffffffffffffffff8116810361041a578352610a5660208301610985565b6020840152610a6760408301610992565b6040840152606082013573ffffffffffffffffffffffffffffffffffffffff8116810361041a576060840152608082013567ffffffffffffffff811161041a5781610ab39184016109a4565b608084015260a082013560a084015260c08201359067ffffffffffffffff821161041a5782610aeb60e09492610af6948694016109a4565b60c086015201610985565b910152565b5f5b838110610b0c5750505f910152565b8181015183820152602001610afd565b90601f19601f602093610b3a81518092818752878088019101610afb565b0116010190565b602081519101519060208110610b55575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b90602080835192838152019201905f5b818110610b9f5750505090565b8251805167ffffffffffffffff16855260209081015164ffffffffff168186015260409094019390920191600101610b92565b9073ffffffffffffffffffffffffffffffffffffffff825116815260208201511515602082015264ffffffffff604083015116604082015273ffffffffffffffffffffffffffffffffffffffff606083015116606082015260e080610c67610c4b60808601516101006080870152610100860190610b1c565b60a086015160a086015260c086015185820360c0870152610b1c565b93015115159101529056fe610160806040523461042857611748803803809161001d8285610560565b833981019080820390608082126104285780516001600160401b03811161042857810190610100828503126104285760405161010081016001600160401b038111828210176105355760405282516001600160a01b038116810361042857815261008960208401610583565b6020820190815261009c60408501610590565b60408301908152606085015194906001600160a01b0386168603610428576060840195865260808201516001600160401b03811161042857886100e09184016105de565b6080850190815260a08381015190860190815260c084015190999192916001600160401b0382116104285761011c60e09161012a9387016105de565b9460c0880195865201610583565b9360e0860194855260208701519560018060a01b038716998a880361042857604090603f1901126104285760408051989089016001600160401b0381118a8210176105355761018d9160609160405261018560408201610590565b8b5201610590565b9860208901998a52855151602081116105495750515f80546001600160a01b0319166001600160a01b0392831617905590511660805251151560a0525164ffffffffff1660c0525180519097906001600160401b03811161053557600154600181811c9116801561052b575b602082101461051757601f81116104b4575b506020601f821160011461044857819064ffffffffff98999a5f9261043d575b50508160011b915f199060031b1c1916176001555b5160e052516040516102736020828161026281830196878151938492016105bd565b81010301601f198101835282610560565b519051906020811061042c575b50610100525115156101205261014052511669ffffffffff0000000000600454925160281b169160018060501b031916171760045560018060a01b0360805116604051905f806020840163095ea7b360e01b815285602486015281196044860152604485526102f0606486610560565b84519082855af16102ff610623565b816103f1575b50806103e7575b156103a2575b60405161102490816107248239608051818181610481015281816106d60152610c20015260a0518181816107140152610b11015260c05181818161015f01528181610a6801528181610d8a0152610f49015260e0518181816102d3015261062201526101005181610e2801526101205181818161073e0152610ad50152610140518181816101af01526108870152f35b6103da6103df936040519063095ea7b360e01b602083015260248201525f6044820152604481526103d4606482610560565b82610652565b610652565b5f8080610312565b50803b151561030c565b8051801592508215610406575b50505f610305565b81925090602091810103126104285760206104219101610583565b5f806103fe565b5f80fd5b5f199060200360031b1b165f610280565b015190505f8061022b565b601f1982169960015f52815f209a5f5b81811061049c57509164ffffffffff999a9b91846001959410610484575b505050811b01600155610240565b01515f1960f88460031b161c191690555f8080610476565b838301518d556001909c019b60209384019301610458565b60015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6601f830160051c8101916020841061050d575b601f0160051c01905b818110610502575061020b565b5f81556001016104f5565b90915081906104ec565b634e487b7160e01b5f52602260045260245ffd5b90607f16906101f9565b634e487b7160e01b5f52604160045260245ffd5b63a52d539b60e01b5f52600452602060245260445ffd5b601f909101601f19168101906001600160401b0382119082101761053557604052565b5190811515820361042857565b519064ffffffffff8216820361042857565b6001600160401b03811161053557601f01601f191660200190565b5f5b8381106105ce5750505f910152565b81810151838201526020016105bf565b81601f820112156104285780516105f4816105a2565b926106026040519485610560565b818452602082840101116104285761062091602080850191016105bd565b90565b3d1561064d573d90610634826105a2565b916106426040519384610560565b82523d5f602084013e565b606090565b5f8061067a9260018060a01b03169360208151910182865af1610673610623565b90836106c5565b80519081151591826106a2575b50506106905750565b635274afe760e01b5f5260045260245ffd5b81925090602091810103126104285760206106bd9101610583565b155f80610687565b906106e957508051156106da57805190602001fd5b630a12f52160e11b5f5260045ffd5b8151158061071a575b6106fa575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156106f256fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde0314610e12575080631686c90914610b3657806316c3549d14610afa5780631bfd681414610abe5780633bfe03a814610a905780633f31ae3f146104a55780634800d97f1461045557806349fc73dd1461031a5780634e390d3e146102f657806351e75e8b146102bc57806375829def146101ed57806390e64d13146101d35780639e93e57714610183578063bb4b573414610142578063ce516507146101025763f851a440146100cc575f80fd5b346100fe575f6003193601126100fe57602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b5f80fd5b346100fe5760206003193601126100fe57602061013860043560ff6001918060081c5f526002602052161b60405f205416151590565b6040519015158152f35b346100fe575f6003193601126100fe57602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe576020610138610f41565b346100fe5760206003193601126100fe57610206610ec1565b5f5473ffffffffffffffffffffffffffffffffffffffff811633810361028d575073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffff0000000000000000000000000000000000000000921691829116175f55337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf805f80a3005b7fc6cce6a4000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b346100fe575f6003193601126100fe5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100fe575f6003193601126100fe57602064ffffffffff60035416604051908152f35b346100fe575f6003193601126100fe576040515f6001548060011c9060018116801561044b575b60208310811461041e578285529081156103dc575060011461037e575b61037a8361036e81850382610f00565b60405191829182610e5b565b0390f35b91905060015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6915f905b8082106103c25750909150810160200161036e61035e565b9192600181602092548385880101520191019092916103aa565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b8401909101915061036e905061035e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610341565b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe5760806003193601126100fe5760043560243573ffffffffffffffffffffffffffffffffffffffff81168091036100fe57604435916fffffffffffffffffffffffffffffffff83168093036100fe576064359067ffffffffffffffff82116100fe57366023830112156100fe57816004013567ffffffffffffffff81116100fe578060051b92602484820101903682116100fe57604051602081019085825287604082015288606082015260608152610563608082610f00565b519020604051602081019182526020815261057f604082610f00565b5190209261058b610f41565b610a39576105b08560ff6001918060081c5f526002602052161b60405f205416151590565b610a0d576105c46020604051970187610f00565b8552602401602085015b8282106109fd57505050935f945b835186101561061e5760208660051b85010151908181105f1461060d575f52602052600160405f205b9501946105dc565b905f52602052600160405f20610605565b84907f0000000000000000000000000000000000000000000000000000000000000000036109d55760035464ffffffffff8116156109a1575b508260081c5f52600260205260405f20600160ff85161b815417905573ffffffffffffffffffffffffffffffffffffffff5f54169160405161069881610ee4565b5f81525f602082015260405193610100850185811067ffffffffffffffff8211176109745760405284526020840183815260408501838152606086017f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16815260808701907f00000000000000000000000000000000000000000000000000000000000000001515825260a08801927f0000000000000000000000000000000000000000000000000000000000000000151584526040519461076e86610ee4565b60045464ffffffffff8116875260281c64ffffffffff16602087015260c08a0195865260e08a01968752604051997fab167ccc000000000000000000000000000000000000000000000000000000008b525173ffffffffffffffffffffffffffffffffffffffff1660048b01525173ffffffffffffffffffffffffffffffffffffffff1660248a0152516fffffffffffffffffffffffffffffffff1660448901525173ffffffffffffffffffffffffffffffffffffffff166064880152511515608487015251151560a486015251805164ffffffffff1660c48601526020015164ffffffffff1660e485015251805173ffffffffffffffffffffffffffffffffffffffff166101048501526020015161012484015282807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165a925f61014492602095f1928315610969575f93610911575b50907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d60406020958594825191825287820152a3604051908152f35b939250906020843d602011610961575b8161092e60209383610f00565b810103126100fe5792519192907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d6108d5565b3d9150610921565b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161760035583610657565b7f0fa7d73c000000000000000000000000000000000000000000000000000000005f5260045ffd5b81358152602091820191016105ce565b847f712b37a3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f442b1841000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445ffd5b346100fe575f6003193601126100fe57604060045464ffffffffff825191818116835260281c166020820152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe5760406003193601126100fe57610b4f610ec1565b6024356fffffffffffffffffffffffffffffffff81168091036100fe5773ffffffffffffffffffffffffffffffffffffffff5f541633810361028d575064ffffffffff6003541680151580610dc4575b80610db5575b610d5b57506040515f8073ffffffffffffffffffffffffffffffffffffffff60208401957fa9059cbb000000000000000000000000000000000000000000000000000000008752169485602485015284604485015260448452610c09606485610f00565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610d4f573d67ffffffffffffffff811161097457610ca79160405191610c9760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610f00565b82523d5f602084013e5b83610f7e565b8051908115159182610d2b575b5050610d0057507f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f602073ffffffffffffffffffffffffffffffffffffffff5f541692604051908152a3005b7f5274afe7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b81925090602091810103126100fe57602001518015908115036100fe578480610cb4565b610ca790606090610ca1565b7f92b66697000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445260645ffd5b50610dbe610f41565b15610ba5565b5062093a80810164ffffffffff8111610de55764ffffffffff164211610b9f565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b346100fe575f6003193601126100fe5761037a907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261036e604082610f00565b919091602081528251928360208301525f5b848110610eab5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b8060208092840101516040828601015201610e6d565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100fe57565b6040810190811067ffffffffffffffff82111761097457604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761097457604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081610f76575090565b905042101590565b90610fbb5750805115610f9357805190602001fd5b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b8151158061100e575b610fcc575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b15610fc456fea164736f6c634300081a000a610180806040523461051d57611dd8803803809161001d82856106a2565b833981019060608183031261051d5780516001600160401b03811161051d5781016101008184031261051d576040519061010082016001600160401b038111838210176103605760405280516001600160a01b038116810361051d578252610087602082016106c5565b916020810192835261009b604083016106d2565b60408201908152606083015193906001600160a01b038516850361051d576060830194855260808401516001600160401b03811161051d57876100df918601610720565b916080840192835260a08501519360a0810194855260c086015160018060401b03811161051d5760e06101178b610125938a01610720565b9760c08401988952016106c5565b60e082019081526020890151989097906001600160a01b038a168a0361051d576040810151906001600160401b03821161051d57018a601f8201121561051d578051906001600160401b0382116103605760209b8c6040519d8e61018e828760051b01826106a2565b858152019360061b8301019181831161051d57602001925b82841061064657505050508651516020811161062f5750515f80546001600160a01b0319166001600160a01b0392831617905590511660805251151560a0525164ffffffffff1660c052518051906001600160401b0382116103605760015490600182811c92168015610625575b60208310146106115781601f8493116105a3575b50602090601f831160011461053d575f92610532575b50508160011b915f199060031b1c1916176001555b5160e052516040516102866020828161027581830196878151938492016106ff565b81010301601f1981018352826106a2565b5190519060208110610521575b506101005251151561012052610140525f90815b8151831015610388576001600160401b036102c28484610765565b5151166001600160401b039182160190811161037457916102e38183610765565b5190600454916801000000000000000083101561036057600183018060045583101561034c5760019260045f5260205f200190838060401b038151166cffffffffff00000000000000006020845493015160401b1691858060681b0319161717905501916102a7565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b6101605260018060a01b036080511660018060a01b03610140511690604051905f806020840163095ea7b360e01b815285602486015281196044860152604485526103d46064866106a2565b84519082855af16103e3610779565b816104e6575b50806104dc575b15610497575b60405161155e908161087a82396080518181816105d10152818161095d0152611061015260a0518181816109870152610f52015260c0518181816102bb01528181610eac015281816111cb015261135d015260e05181818161042301526107b40152610100518161123c0152610120518181816109c20152610f160152610140518181816101390152610ac20152610160518181816102ff01526106980152f35b6104cf6104d4936040519063095ea7b360e01b602083015260248201525f6044820152604481526104c96064826106a2565b826107a8565b6107a8565b8080806103f6565b50803b15156103f0565b80518015925082156104fb575b5050846103e9565b819250906020918101031261051d57602061051691016106c5565b84806104f3565b5f80fd5b5f199060200360031b1b165f610293565b015190505f8061023e565b60015f9081528281209350601f198516905b81811061058b5750908460019594939210610573575b505050811b01600155610253565b01515f1960f88460031b161c191690555f8080610565565b9293602060018192878601518155019501930161054f565b60015f529091507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6601f840160051c81019160208510610607575b90601f859493920160051c01905b8181106105f95750610228565b5f81558493506001016105ec565b90915081906105de565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610214565b63a52d539b60e01b5f52600452602060245260445ffd5b60408483031261051d5760408051919082016001600160401b03811183821017610360576040528451906001600160401b038216820361051d5782602092604094526106938388016106d2565b838201528152019301926101a6565b601f909101601f19168101906001600160401b0382119082101761036057604052565b5190811515820361051d57565b519064ffffffffff8216820361051d57565b6001600160401b03811161036057601f01601f191660200190565b5f5b8381106107105750505f910152565b8181015183820152602001610701565b81601f8201121561051d578051610736816106e4565b9261074460405194856106a2565b8184526020828401011161051d5761076291602080850191016106ff565b90565b805182101561034c5760209160051b010190565b3d156107a3573d9061078a826106e4565b9161079860405193846106a2565b82523d5f602084013e565b606090565b5f806107d09260018060a01b03169360208151910182865af16107c9610779565b908361081b565b80519081151591826107f8575b50506107e65750565b635274afe760e01b5f5260045260245ffd5b819250906020918101031261051d57602061081391016106c5565b155f806107dd565b9061083f575080511561083057805190602001fd5b630a12f52160e11b5f5260045ffd5b81511580610870575b610850575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b1561084856fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde0314611226575080631686c90914610f7757806316c3549d14610f3b5780631bfd681414610eff5780633f31ae3f146105f55780634800d97f146105a557806349fc73dd1461046a5780634e390d3e1461044657806351e75e8b1461040c57806375829def1461033d57806390e64d1314610323578063936c63d9146102df578063bb4b57341461029e578063bf4ed03f1461019d578063ce5165071461015d578063da7924681461010d5763f851a440146100d7575f80fd5b34610109575f60031936011261010957602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b5f80fd5b34610109575f60031936011261010957602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461010957602060031936011261010957602061019360043560ff6001918060081c5f526002602052161b60405f205416151590565b6040519015158152f35b34610109575f600319360112610109576004546101b981611392565b906101c76040519283611314565b80825260045f9081526020830191907f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b835b838310610261578486604051918291602083019060208452518091526040830191905f5b81811061022b575050500390f35b8251805167ffffffffffffffff16855260209081015164ffffffffff16818601528695506040909401939092019160010161021d565b600160208192604051610273816112f8565b64ffffffffff865467ffffffffffffffff8116835260401c16838201528152019201920191906101f9565b34610109575f60031936011261010957602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109575f60031936011261010957602060405167ffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109575f600319360112610109576020610193611355565b34610109576020600319360112610109576103566112d5565b5f5473ffffffffffffffffffffffffffffffffffffffff81163381036103dd575073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffff0000000000000000000000000000000000000000921691829116175f55337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf805f80a3005b7fc6cce6a4000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b34610109575f6003193601126101095760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b34610109575f60031936011261010957602064ffffffffff60035416604051908152f35b34610109575f600319360112610109576040515f6001548060011c9060018116801561059b575b60208310811461056e5782855290811561052c57506001146104ce575b6104ca836104be81850382611314565b6040519182918261126f565b0390f35b91905060015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6915f905b808210610512575090915081016020016104be6104ae565b9192600181602092548385880101520191019092916104fa565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b840190910191506104be90506104ae565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610491565b34610109575f60031936011261010957602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109576080600319360112610109576004356024359073ffffffffffffffffffffffffffffffffffffffff821680920361010957604435916fffffffffffffffffffffffffffffffff831691828403610109576064359367ffffffffffffffff851161010957366023860112156101095784600401359467ffffffffffffffff86116101095760248660051b8201013681116101095767ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016670de0b6b3a76400008103610ed457506040516020810190858252866040820152876060820152606081526106ee608082611314565b519020604051602081019182526020815261070a604082611314565b51902091610716611355565b610e7d5761073b8560ff6001918060081c5f526002602052161b60405f205416151590565b610e515761074888611392565b97610756604051998a611314565b8852602401602088015b828210610e4157505050925f935b86518510156107b05761078185886113aa565b51908181101561079f575f52602052600160405f205b94019361076e565b905f52602052600160405f20610797565b85907f000000000000000000000000000000000000000000000000000000000000000003610e195760035464ffffffffff811615610de5575b50600454926107f784611392565b936108056040519586611314565b80855260045f9081527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b602087015b838310610da857505050505f845161084b81611392565b956108596040519788611314565b8187527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061088683611392565b015f5b818110610d855750505f905b828210610c7c5750506fffffffffffffffffffffffffffffffff8216848111610c4f578411610bfc575b5050508360081c5f52600260205260405f20600160ff86161b815417905573ffffffffffffffffffffffffffffffffffffffff5f541692604051610902816112f8565b5f81525f60208201526040519161010083019583871067ffffffffffffffff881117610bcf5773ffffffffffffffffffffffffffffffffffffffff9660409492939452815260208101918583526040820185815260608301887f00000000000000000000000000000000000000000000000000000000000000001681528860808501917f0000000000000000000000000000000000000000000000000000000000000000151583526fffffffffffffffffffffffffffffffff60a08701947f00000000000000000000000000000000000000000000000000000000000000001515865260c0880196875260e08801998a526040519c8d997f897f362b000000000000000000000000000000000000000000000000000000008b52602060048c0152816101448c019a511660248c0152511660448a0152511660648801525116608486015251151560a485015251151560c4840152519061012060e48401528151809152602061016484019201905f5b818110610b91575050508190602080945173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015203815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1928315610b86575f93610b2e575b50907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d60406020958594825191825287820152a3604051908152f35b939250906020843d602011610b7e575b81610b4b60209383611314565b810103126101095792519192907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d610af2565b3d9150610b3e565b6040513d5f823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff168186015289955060409094019390920191600101610a71565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6fffffffffffffffffffffffffffffffff91610c3b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff849301886113aa565b5193031681835116011690528480806108bf565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b9092610c9d67ffffffffffffffff610c9486856113aa565b515116876113eb565b6fffffffffffffffffffffffffffffffff8111610d5a576fffffffffffffffffffffffffffffffff8091169164ffffffffff6020610cdb88876113aa565b5101511660405190610cec826112f8565b8482526020820152610cfe878c6113aa565b52610d09868b6113aa565b5016016fffffffffffffffffffffffffffffffff8111610d2d579260010190610895565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f4916adce000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b602090604051610d94816112f8565b5f81525f8382015282828c01015201610889565b600160208192604051610dba816112f8565b64ffffffffff865467ffffffffffffffff8116835260401c1683820152815201920192019190610834565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff1617600355846107e9565b7f0fa7d73c000000000000000000000000000000000000000000000000000000005f5260045ffd5b8135815260209182019101610760565b847f712b37a3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f442b1841000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445ffd5b7f4557880f000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b34610109575f6003193601126101095760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b34610109575f6003193601126101095760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b3461010957604060031936011261010957610f906112d5565b6024356fffffffffffffffffffffffffffffffff81168091036101095773ffffffffffffffffffffffffffffffffffffffff5f54163381036103dd575064ffffffffff6003541680151580611205575b806111f6575b61119c57506040515f8073ffffffffffffffffffffffffffffffffffffffff60208401957fa9059cbb00000000000000000000000000000000000000000000000000000000875216948560248501528460448501526044845261104a606485611314565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15611190573d67ffffffffffffffff8111610bcf576110e891604051916110d860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184611314565b82523d5f602084013e5b836114b8565b805190811515918261116c575b505061114157507f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f602073ffffffffffffffffffffffffffffffffffffffff5f541692604051908152a3005b7f5274afe7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b819250906020918101031261010957602001518015908115036101095784806110f5565b6110e8906060906110e2565b7f92b66697000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445260645ffd5b506111ff611355565b15610fe6565b5062093a80810164ffffffffff8111610d2d5764ffffffffff164211610fe0565b34610109575f600319360112610109576104ca907f00000000000000000000000000000000000000000000000000000000000000006020820152602081526104be604082611314565b919091602081528251928360208301525f5b8481106112bf5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b8060208092840101516040828601015201611281565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361010957565b6040810190811067ffffffffffffffff821117610bcf57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610bcf57604052565b64ffffffffff7f000000000000000000000000000000000000000000000000000000000000000016801515908161138a575090565b905042101590565b67ffffffffffffffff8111610bcf5760051b60200190565b80518210156113be5760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff838209838202918280831092039180830392146114a757670de0b6b3a7640000821015611477577faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106699394670de0b6b3a7640000910990828211900360ee1b910360121c170290565b84907f5173648d000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b5050670de0b6b3a764000090049150565b906114f557508051156114cd57805190602001fd5b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b81511580611548575b611506575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b156114fe56fea164736f6c634300081a000aa164736f6c634300081a000a"; + hex"608080604052346015576141d4908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c80631e323876146105285780634d7c0f111461041e5763769bed201461003a575f80fd5b3461041a5760c07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261041a5760043567ffffffffffffffff811161041a57610089903690600401610a26565b6024359073ffffffffffffffffffffffffffffffffffffffff82169182810361041a5760407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc36011261041a57604051906100e382610972565b60443564ffffffffff8116810361041a57825260643564ffffffffff8116810361041a57602083015282519060208401511515836040860151926060870151608088015191604051806020810194602086526040820161014291610b48565b03601f1981018252610154908261098e565b60a08a01519360c08b015160405181819251908160208401916020019161017a92610b27565b81010380825261018d906020018261098e565b61019690610b6d565b9060e08c0151151592604051956020870198896101c59164ffffffffff60208092828151168552015116910152565b604087526101d460608861098e565b6040519a8b9a60208c019d8e3360601b905260601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660348d015260f81b60488c015260d81b7fffffffffff0000000000000000000000000000000000000000000000000000001660498b015260601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016604e8a015251908160628a0161027c92610b27565b8701946062860152608285015260f81b60a284015260601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660a383015251918260b783016102cb92610b27565b0160620103605501601f19810182526102e4908261098e565b51902060405161174880820182811067ffffffffffffffff8211176103ed578291610c9f83396080815261034160406103206080840189610bfe565b92896020820152018664ffffffffff60208092828151168552015116910152565b03905ff59283156103e2576103a56103c7937f2ba0fe49588281dbb122dd3b7f3e2b3396338f70dbe3c62bf3e3888b4ba7ffb89273ffffffffffffffffffffffffffffffffffffffff6020971695869560405194859460c0865260c0860190610bfe565b9289850152604084019064ffffffffff60208092828151168552015116910152565b608435608083015260a43560a08301520390a2604051908152f35b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f80fd5b3461041a5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261041a5760043567ffffffffffffffff811161041a573660238201121561041a5780600401359067ffffffffffffffff821161041a573660248360061b8301011161041a575f90815b838310156105095760248360061b830101359067ffffffffffffffff821680920361041a5767ffffffffffffffff160167ffffffffffffffff81116104dc57600190920191610492565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b602090670de0b6b3a764000067ffffffffffffffff6040519216148152f35b3461041a5760a07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261041a5760043567ffffffffffffffff811161041a57610577903690600401610a26565b60243573ffffffffffffffffffffffffffffffffffffffff81169081810361041a576044359067ffffffffffffffff821161041a573660238301121561041a57816004013567ffffffffffffffff81116103ed57604051926105df60208360051b018561098e565b8184526024602085019260061b8201019036821161041a57602401915b818310610925575050505f9082515f905b8082106108cd5750508451906020860151151584604088015192606089015160808a015191604051806020810194602086526040820161064c91610b48565b03601f198101825261065e908261098e565b8b60a08101519460c082015160405181819251908160208401916020019161068592610b27565b810103808252610698906020018261098e565b6106a190610b6d565b9160e001511515926040519586602081019960208b52604082016106c491610bae565b03601f19810188526106d6908861098e565b6040519a8b9a60208c019d8e3360601b905260601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660348d015260f81b60488c015260d81b7fffffffffff0000000000000000000000000000000000000000000000000000001660498b015260601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016604e8a015251908160628a0161077e92610b27565b8701946062860152608285015260f81b60a284015260601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001660a383015251918260b783016107cd92610b27565b0160620103605501601f19810182526107e6908261098e565b519020604051611de18082019082821067ffffffffffffffff8311176103ed578291610837916123e7843960608152610822606082018a610bfe565b90886020820152604081830391015286610bae565b03905ff580156103e2576020947ffe44018cf74992b2720702385a1728bd329dd136e4f651203176c81c12710a8b926108ac73ffffffffffffffffffffffffffffffffffffffff61089a941696879660405195869560c0875260c0870190610bfe565b918a8601528482036040860152610bae565b906060830152606435608083015260843560a08301520390a2604051908152f35b909284518410156108f85760019064ffffffffff6020808760051b890101510151160193019061060d565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b60408336031261041a576040519061093c82610972565b83359067ffffffffffffffff8216820361041a5782602092604094526109638387016109be565b838201528152019201916105fc565b6040810190811067ffffffffffffffff8211176103ed57604052565b90601f601f19910116810190811067ffffffffffffffff8211176103ed57604052565b3590811515820361041a57565b359064ffffffffff8216820361041a57565b81601f8201121561041a5780359067ffffffffffffffff82116103ed5760405192610a056020601f19601f860116018561098e565b8284526020838301011161041a57815f926020809301838601378301015290565b9190916101008184031261041a5760405190610100820182811067ffffffffffffffff8211176103ed576040528193813573ffffffffffffffffffffffffffffffffffffffff8116810361041a578352610a82602083016109b1565b6020840152610a93604083016109be565b6040840152606082013573ffffffffffffffffffffffffffffffffffffffff8116810361041a576060840152608082013567ffffffffffffffff811161041a5781610adf9184016109d0565b608084015260a082013560a084015260c08201359067ffffffffffffffff821161041a5782610b1760e09492610b22948694016109d0565b60c0860152016109b1565b910152565b5f5b838110610b385750505f910152565b8181015183820152602001610b29565b90601f19601f602093610b6681518092818752878088019101610b27565b0116010190565b602081519101519060208110610b81575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060200360031b1b1690565b90602080835192838152019201905f5b818110610bcb5750505090565b8251805167ffffffffffffffff16855260209081015164ffffffffff168186015260409094019390920191600101610bbe565b9073ffffffffffffffffffffffffffffffffffffffff825116815260208201511515602082015264ffffffffff604083015116604082015273ffffffffffffffffffffffffffffffffffffffff606083015116606082015260e080610c93610c7760808601516101006080870152610100860190610b48565b60a086015160a086015260c086015185820360c0870152610b48565b93015115159101529056fe610160806040523461042857611748803803809161001d8285610560565b833981019080820390608082126104285780516001600160401b03811161042857810190610100828503126104285760405161010081016001600160401b038111828210176105355760405282516001600160a01b038116810361042857815261008960208401610583565b6020820190815261009c60408501610590565b60408301908152606085015194906001600160a01b0386168603610428576060840195865260808201516001600160401b03811161042857886100e09184016105de565b6080850190815260a08381015190860190815260c084015190999192916001600160401b0382116104285761011c60e09161012a9387016105de565b9460c0880195865201610583565b9360e0860194855260208701519560018060a01b038716998a880361042857604090603f1901126104285760408051989089016001600160401b0381118a8210176105355761018d9160609160405261018560408201610590565b8b5201610590565b9860208901998a52855151602081116105495750515f80546001600160a01b0319166001600160a01b0392831617905590511660805251151560a0525164ffffffffff1660c0525180519097906001600160401b03811161053557600154600181811c9116801561052b575b602082101461051757601f81116104b4575b506020601f821160011461044857819064ffffffffff98999a5f9261043d575b50508160011b915f199060031b1c1916176001555b5160e052516040516102736020828161026281830196878151938492016105bd565b81010301601f198101835282610560565b519051906020811061042c575b50610100525115156101205261014052511669ffffffffff0000000000600454925160281b169160018060501b031916171760045560018060a01b0360805116604051905f806020840163095ea7b360e01b815285602486015281196044860152604485526102f0606486610560565b84519082855af16102ff610623565b816103f1575b50806103e7575b156103a2575b60405161102490816107248239608051818181610481015281816106d60152610c20015260a0518181816107140152610b11015260c05181818161015f01528181610a6801528181610d8a0152610f49015260e0518181816102d3015261062201526101005181610e2801526101205181818161073e0152610ad50152610140518181816101af01526108870152f35b6103da6103df936040519063095ea7b360e01b602083015260248201525f6044820152604481526103d4606482610560565b82610652565b610652565b5f8080610312565b50803b151561030c565b8051801592508215610406575b50505f610305565b81925090602091810103126104285760206104219101610583565b5f806103fe565b5f80fd5b5f199060200360031b1b165f610280565b015190505f8061022b565b601f1982169960015f52815f209a5f5b81811061049c57509164ffffffffff999a9b91846001959410610484575b505050811b01600155610240565b01515f1960f88460031b161c191690555f8080610476565b838301518d556001909c019b60209384019301610458565b60015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6601f830160051c8101916020841061050d575b601f0160051c01905b818110610502575061020b565b5f81556001016104f5565b90915081906104ec565b634e487b7160e01b5f52602260045260245ffd5b90607f16906101f9565b634e487b7160e01b5f52604160045260245ffd5b63a52d539b60e01b5f52600452602060245260445ffd5b601f909101601f19168101906001600160401b0382119082101761053557604052565b5190811515820361042857565b519064ffffffffff8216820361042857565b6001600160401b03811161053557601f01601f191660200190565b5f5b8381106105ce5750505f910152565b81810151838201526020016105bf565b81601f820112156104285780516105f4816105a2565b926106026040519485610560565b818452602082840101116104285761062091602080850191016105bd565b90565b3d1561064d573d90610634826105a2565b916106426040519384610560565b82523d5f602084013e565b606090565b5f8061067a9260018060a01b03169360208151910182865af1610673610623565b90836106c5565b80519081151591826106a2575b50506106905750565b635274afe760e01b5f5260045260245ffd5b81925090602091810103126104285760206106bd9101610583565b155f80610687565b906106e957508051156106da57805190602001fd5b630a12f52160e11b5f5260045ffd5b8151158061071a575b6106fa575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b156106f256fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde0314610e12575080631686c90914610b3657806316c3549d14610afa5780631bfd681414610abe5780633bfe03a814610a905780633f31ae3f146104a55780634800d97f1461045557806349fc73dd1461031a5780634e390d3e146102f657806351e75e8b146102bc57806375829def146101ed57806390e64d13146101d35780639e93e57714610183578063bb4b573414610142578063ce516507146101025763f851a440146100cc575f80fd5b346100fe575f6003193601126100fe57602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b5f80fd5b346100fe5760206003193601126100fe57602061013860043560ff6001918060081c5f526002602052161b60405f205416151590565b6040519015158152f35b346100fe575f6003193601126100fe57602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe575f6003193601126100fe576020610138610f41565b346100fe5760206003193601126100fe57610206610ec1565b5f5473ffffffffffffffffffffffffffffffffffffffff811633810361028d575073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffff0000000000000000000000000000000000000000921691829116175f55337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf805f80a3005b7fc6cce6a4000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b346100fe575f6003193601126100fe5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b346100fe575f6003193601126100fe57602064ffffffffff60035416604051908152f35b346100fe575f6003193601126100fe576040515f6001548060011c9060018116801561044b575b60208310811461041e578285529081156103dc575060011461037e575b61037a8361036e81850382610f00565b60405191829182610e5b565b0390f35b91905060015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6915f905b8082106103c25750909150810160200161036e61035e565b9192600181602092548385880101520191019092916103aa565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b8401909101915061036e905061035e565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610341565b346100fe575f6003193601126100fe57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346100fe5760806003193601126100fe5760043560243573ffffffffffffffffffffffffffffffffffffffff81168091036100fe57604435916fffffffffffffffffffffffffffffffff83168093036100fe576064359067ffffffffffffffff82116100fe57366023830112156100fe57816004013567ffffffffffffffff81116100fe578060051b92602484820101903682116100fe57604051602081019085825287604082015288606082015260608152610563608082610f00565b519020604051602081019182526020815261057f604082610f00565b5190209261058b610f41565b610a39576105b08560ff6001918060081c5f526002602052161b60405f205416151590565b610a0d576105c46020604051970187610f00565b8552602401602085015b8282106109fd57505050935f945b835186101561061e5760208660051b85010151908181105f1461060d575f52602052600160405f205b9501946105dc565b905f52602052600160405f20610605565b84907f0000000000000000000000000000000000000000000000000000000000000000036109d55760035464ffffffffff8116156109a1575b508260081c5f52600260205260405f20600160ff85161b815417905573ffffffffffffffffffffffffffffffffffffffff5f54169160405161069881610ee4565b5f81525f602082015260405193610100850185811067ffffffffffffffff8211176109745760405284526020840183815260408501838152606086017f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16815260808701907f00000000000000000000000000000000000000000000000000000000000000001515825260a08801927f0000000000000000000000000000000000000000000000000000000000000000151584526040519461076e86610ee4565b60045464ffffffffff8116875260281c64ffffffffff16602087015260c08a0195865260e08a01968752604051997fab167ccc000000000000000000000000000000000000000000000000000000008b525173ffffffffffffffffffffffffffffffffffffffff1660048b01525173ffffffffffffffffffffffffffffffffffffffff1660248a0152516fffffffffffffffffffffffffffffffff1660448901525173ffffffffffffffffffffffffffffffffffffffff166064880152511515608487015251151560a486015251805164ffffffffff1660c48601526020015164ffffffffff1660e485015251805173ffffffffffffffffffffffffffffffffffffffff166101048501526020015161012484015282807f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff165a925f61014492602095f1928315610969575f93610911575b50907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d60406020958594825191825287820152a3604051908152f35b939250906020843d602011610961575b8161092e60209383610f00565b810103126100fe5792519192907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d6108d5565b3d9150610921565b6040513d5f823e3d90fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff161760035583610657565b7f0fa7d73c000000000000000000000000000000000000000000000000000000005f5260045ffd5b81358152602091820191016105ce565b847f712b37a3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f442b1841000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445ffd5b346100fe575f6003193601126100fe57604060045464ffffffffff825191818116835260281c166020820152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe575f6003193601126100fe5760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b346100fe5760406003193601126100fe57610b4f610ec1565b6024356fffffffffffffffffffffffffffffffff81168091036100fe5773ffffffffffffffffffffffffffffffffffffffff5f541633810361028d575064ffffffffff6003541680151580610dc4575b80610db5575b610d5b57506040515f8073ffffffffffffffffffffffffffffffffffffffff60208401957fa9059cbb000000000000000000000000000000000000000000000000000000008752169485602485015284604485015260448452610c09606485610f00565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15610d4f573d67ffffffffffffffff811161097457610ca79160405191610c9760207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610f00565b82523d5f602084013e5b83610f7e565b8051908115159182610d2b575b5050610d0057507f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f602073ffffffffffffffffffffffffffffffffffffffff5f541692604051908152a3005b7f5274afe7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b81925090602091810103126100fe57602001518015908115036100fe578480610cb4565b610ca790606090610ca1565b7f92b66697000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445260645ffd5b50610dbe610f41565b15610ba5565b5062093a80810164ffffffffff8111610de55764ffffffffff164211610b9f565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b346100fe575f6003193601126100fe5761037a907f000000000000000000000000000000000000000000000000000000000000000060208201526020815261036e604082610f00565b919091602081528251928360208301525f5b848110610eab5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b8060208092840101516040828601015201610e6d565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036100fe57565b6040810190811067ffffffffffffffff82111761097457604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761097457604052565b64ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168015159081610f76575090565b905042101590565b90610fbb5750805115610f9357805190602001fd5b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b8151158061100e575b610fcc575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b15610fc456fea164736f6c634300081a000a610180806040523461044857611de1803803809161001d82856106ab565b83398101906060818303126104485780516001600160401b03811161044857810161010081840312610448576040519061010082016001600160401b038111838210176105025760405280516001600160a01b0381168103610448578252610087602082016106ce565b916020810192835261009b604083016106db565b60408201908152606083015193906001600160a01b0385168503610448576060830194855260808401516001600160401b03811161044857876100df918601610729565b916080840192835260a08501519360a0810194855260c086015160018060401b0381116104485760e06101178b610125938a01610729565b9760c08401988952016106ce565b60e082019081526020890151989097906001600160a01b038a168a03610448576040810151906001600160401b03821161044857018a601f82011215610448578051906001600160401b0382116105025760209b8c6040519d8e61018e828760051b01826106ab565b858152019360061b8301019181831161044857602001925b82841061064f5750505050865151602081116106385750515f80546001600160a01b0319166001600160a01b0392831617905590511660805251151560a0525164ffffffffff1660c052518051906001600160401b0382116105025760015490600182811c9216801561062e575b602083101461061a5781601f8493116105ac575b50602090601f8311600114610546575f9261053b575b50508160011b915f199060031b1c1916176001555b5160e05251604051610286602082816102758183019687815193849201610708565b81010301601f1981018352826106ab565b519051906020811061052a575b506101005251151561012052610140528051905f915f915b81831061044c57836101605260018060a01b036080511660018060a01b03610140511690604051905f806020840163095ea7b360e01b815285602486015281196044860152604485526102ff6064866106ab565b84519082855af161030e610782565b81610411575b5080610407575b156103c2575b60405161155e908161088382396080518181816105d10152818161095d0152611061015260a0518181816109870152610f52015260c0518181816102bb01528181610eac015281816111cb015261135d015260e05181818161042301526107b40152610100518161123c0152610120518181816109c20152610f160152610140518181816101390152610ac20152610160518181816102ff01526106980152f35b6103fa6103ff936040519063095ea7b360e01b602083015260248201525f6044820152604481526103f46064826106ab565b826107b1565b6107b1565b808080610321565b50803b151561031b565b8051801592508215610426575b505084610314565b819250906020918101031261044857602061044191016106ce565b848061041e565b5f80fd5b91929091906001600160401b03610463858461076e565b5151166001600160401b03918216019081116105165792610484818361076e565b519060045491680100000000000000008310156105025760018301806004558310156104ee5760019260045f5260205f200190838060401b038151166cffffffffff00000000000000006020845493015160401b1691858060681b031916171790550191906102ab565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b634e487b7160e01b5f52601160045260245ffd5b5f199060200360031b1b165f610293565b015190505f8061023e565b60015f9081528281209350601f198516905b818110610594575090846001959493921061057c575b505050811b01600155610253565b01515f1960f88460031b161c191690555f808061056e565b92936020600181928786015181550195019301610558565b60015f529091507fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6601f840160051c81019160208510610610575b90601f859493920160051c01905b8181106106025750610228565b5f81558493506001016105f5565b90915081906105e7565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610214565b63a52d539b60e01b5f52600452602060245260445ffd5b6040848303126104485760408051919082016001600160401b03811183821017610502576040528451906001600160401b038216820361044857826020926040945261069c8388016106db565b838201528152019301926101a6565b601f909101601f19168101906001600160401b0382119082101761050257604052565b5190811515820361044857565b519064ffffffffff8216820361044857565b6001600160401b03811161050257601f01601f191660200190565b5f5b8381106107195750505f910152565b818101518382015260200161070a565b81601f8201121561044857805161073f816106ed565b9261074d60405194856106ab565b818452602082840101116104485761076b9160208085019101610708565b90565b80518210156104ee5760209160051b010190565b3d156107ac573d90610793826106ed565b916107a160405193846106ab565b82523d5f602084013e565b606090565b5f806107d99260018060a01b03169360208151910182865af16107d2610782565b9083610824565b8051908115159182610801575b50506107ef5750565b635274afe760e01b5f5260045260245ffd5b819250906020918101031261044857602061081c91016106ce565b155f806107e6565b90610848575080511561083957805190602001fd5b630a12f52160e11b5f5260045ffd5b81511580610879575b610859575090565b639996b31560e01b5f9081526001600160a01b0391909116600452602490fd5b50803b1561085156fe6080806040526004361015610012575f80fd5b5f3560e01c90816306fdde0314611226575080631686c90914610f7757806316c3549d14610f3b5780631bfd681414610eff5780633f31ae3f146105f55780634800d97f146105a557806349fc73dd1461046a5780634e390d3e1461044657806351e75e8b1461040c57806375829def1461033d57806390e64d1314610323578063936c63d9146102df578063bb4b57341461029e578063bf4ed03f1461019d578063ce5165071461015d578063da7924681461010d5763f851a440146100d7575f80fd5b34610109575f60031936011261010957602073ffffffffffffffffffffffffffffffffffffffff5f5416604051908152f35b5f80fd5b34610109575f60031936011261010957602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461010957602060031936011261010957602061019360043560ff6001918060081c5f526002602052161b60405f205416151590565b6040519015158152f35b34610109575f600319360112610109576004546101b981611392565b906101c76040519283611314565b80825260045f9081526020830191907f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b835b838310610261578486604051918291602083019060208452518091526040830191905f5b81811061022b575050500390f35b8251805167ffffffffffffffff16855260209081015164ffffffffff16818601528695506040909401939092019160010161021d565b600160208192604051610273816112f8565b64ffffffffff865467ffffffffffffffff8116835260401c16838201528152019201920191906101f9565b34610109575f60031936011261010957602060405164ffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109575f60031936011261010957602060405167ffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109575f600319360112610109576020610193611355565b34610109576020600319360112610109576103566112d5565b5f5473ffffffffffffffffffffffffffffffffffffffff81163381036103dd575073ffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffff0000000000000000000000000000000000000000921691829116175f55337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf805f80a3005b7fc6cce6a4000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b34610109575f6003193601126101095760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b34610109575f60031936011261010957602064ffffffffff60035416604051908152f35b34610109575f600319360112610109576040515f6001548060011c9060018116801561059b575b60208310811461056e5782855290811561052c57506001146104ce575b6104ca836104be81850382611314565b6040519182918261126f565b0390f35b91905060015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6915f905b808210610512575090915081016020016104be6104ae565b9192600181602092548385880101520191019092916104fa565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b840190910191506104be90506104ae565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f1691610491565b34610109575f60031936011261010957602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b34610109576080600319360112610109576004356024359073ffffffffffffffffffffffffffffffffffffffff821680920361010957604435916fffffffffffffffffffffffffffffffff831691828403610109576064359367ffffffffffffffff851161010957366023860112156101095784600401359467ffffffffffffffff86116101095760248660051b8201013681116101095767ffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016670de0b6b3a76400008103610ed457506040516020810190858252866040820152876060820152606081526106ee608082611314565b519020604051602081019182526020815261070a604082611314565b51902091610716611355565b610e7d5761073b8560ff6001918060081c5f526002602052161b60405f205416151590565b610e515761074888611392565b97610756604051998a611314565b8852602401602088015b828210610e4157505050925f935b86518510156107b05761078185886113aa565b51908181101561079f575f52602052600160405f205b94019361076e565b905f52602052600160405f20610797565b85907f000000000000000000000000000000000000000000000000000000000000000003610e195760035464ffffffffff811615610de5575b50600454926107f784611392565b936108056040519586611314565b80855260045f9081527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b602087015b838310610da857505050505f845161084b81611392565b956108596040519788611314565b8187527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061088683611392565b015f5b818110610d855750505f905b828210610c7c5750506fffffffffffffffffffffffffffffffff8216848111610c4f578411610bfc575b5050508360081c5f52600260205260405f20600160ff86161b815417905573ffffffffffffffffffffffffffffffffffffffff5f541692604051610902816112f8565b5f81525f60208201526040519161010083019583871067ffffffffffffffff881117610bcf5773ffffffffffffffffffffffffffffffffffffffff9660409492939452815260208101918583526040820185815260608301887f00000000000000000000000000000000000000000000000000000000000000001681528860808501917f0000000000000000000000000000000000000000000000000000000000000000151583526fffffffffffffffffffffffffffffffff60a08701947f00000000000000000000000000000000000000000000000000000000000000001515865260c0880196875260e08801998a526040519c8d997f897f362b000000000000000000000000000000000000000000000000000000008b52602060048c0152816101448c019a511660248c0152511660448a0152511660648801525116608486015251151560a485015251151560c4840152519061012060e48401528151809152602061016484019201905f5b818110610b91575050508190602080945173ffffffffffffffffffffffffffffffffffffffff815116610104850152015161012483015203815f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af1928315610b86575f93610b2e575b50907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d60406020958594825191825287820152a3604051908152f35b939250906020843d602011610b7e575b81610b4b60209383611314565b810103126101095792519192907f28b58397e03322f670d6b223cc863f8c148e368b8b615412e6798a641a22842d610af2565b3d9150610b3e565b6040513d5f823e3d90fd5b825180516fffffffffffffffffffffffffffffffff16855260209081015164ffffffffff168186015289955060409094019390920191600101610a71565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6fffffffffffffffffffffffffffffffff91610c3b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff849301886113aa565b5193031681835116011690528480806108bf565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b9092610c9d67ffffffffffffffff610c9486856113aa565b515116876113eb565b6fffffffffffffffffffffffffffffffff8111610d5a576fffffffffffffffffffffffffffffffff8091169164ffffffffff6020610cdb88876113aa565b5101511660405190610cec826112f8565b8482526020820152610cfe878c6113aa565b52610d09868b6113aa565b5016016fffffffffffffffffffffffffffffffff8111610d2d579260010190610895565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b7f4916adce000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b602090604051610d94816112f8565b5f81525f8382015282828c01015201610889565b600160208192604051610dba816112f8565b64ffffffffff865467ffffffffffffffff8116835260401c1683820152815201920192019190610834565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000164264ffffffffff1617600355846107e9565b7f0fa7d73c000000000000000000000000000000000000000000000000000000005f5260045ffd5b8135815260209182019101610760565b847f712b37a3000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f442b1841000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445ffd5b7f4557880f000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b34610109575f6003193601126101095760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b34610109575f6003193601126101095760206040517f000000000000000000000000000000000000000000000000000000000000000015158152f35b3461010957604060031936011261010957610f906112d5565b6024356fffffffffffffffffffffffffffffffff81168091036101095773ffffffffffffffffffffffffffffffffffffffff5f54163381036103dd575064ffffffffff6003541680151580611205575b806111f6575b61119c57506040515f8073ffffffffffffffffffffffffffffffffffffffff60208401957fa9059cbb00000000000000000000000000000000000000000000000000000000875216948560248501528460448501526044845261104a606485611314565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001693519082855af13d15611190573d67ffffffffffffffff8111610bcf576110e891604051916110d860207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184611314565b82523d5f602084013e5b836114b8565b805190811515918261116c575b505061114157507f2e9d425ba8b27655048400b366d7b6a1f7180ebdb088e06bb7389704860ffe1f602073ffffffffffffffffffffffffffffffffffffffff5f541692604051908152a3005b7f5274afe7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b819250906020918101031261010957602001518015908115036101095784806110f5565b6110e8906060906110e2565b7f92b66697000000000000000000000000000000000000000000000000000000005f524260045264ffffffffff7f00000000000000000000000000000000000000000000000000000000000000001660245260445260645ffd5b506111ff611355565b15610fe6565b5062093a80810164ffffffffff8111610d2d5764ffffffffff164211610fe0565b34610109575f600319360112610109576104ca907f00000000000000000000000000000000000000000000000000000000000000006020820152602081526104be604082611314565b919091602081528251928360208301525f5b8481106112bf5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6040809697860101520116010190565b8060208092840101516040828601015201611281565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361010957565b6040810190811067ffffffffffffffff821117610bcf57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610bcf57604052565b64ffffffffff7f000000000000000000000000000000000000000000000000000000000000000016801515908161138a575090565b905042101590565b67ffffffffffffffff8111610bcf5760051b60200190565b80518210156113be5760209160051b010190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff838209838202918280831092039180830392146114a757670de0b6b3a7640000821015611477577faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106699394670de0b6b3a7640000910990828211900360ee1b910360121c170290565b84907f5173648d000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b5050670de0b6b3a764000090049150565b906114f557508051156114cd57805190602001fd5b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b81511580611548575b611506575090565b73ffffffffffffffffffffffffffffffffffffffff907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b156114fe56fea164736f6c634300081a000aa164736f6c634300081a000a"; /*////////////////////////////////////////////////////////////////////////// DEPLOYERS diff --git a/src/SablierV2MerkleLT.sol b/src/SablierV2MerkleLT.sol index e647b6aa..286614e4 100644 --- a/src/SablierV2MerkleLT.sol +++ b/src/SablierV2MerkleLT.sol @@ -51,9 +51,11 @@ contract SablierV2MerkleLT is { LOCKUP_TRANCHED = lockupTranched; + uint256 count = tranchesWithPercentages.length; + // Calculate the total percentage of the tranches and save them in the contract state. uint64 totalPercentage; - for (uint256 i = 0; i < tranchesWithPercentages.length; ++i) { + for (uint256 i = 0; i < count; ++i) { uint64 percentage = tranchesWithPercentages[i].unlockPercentage.unwrap(); totalPercentage += percentage; _tranchesWithPercentages.push(tranchesWithPercentages[i]); diff --git a/src/SablierV2MerkleLockupFactory.sol b/src/SablierV2MerkleLockupFactory.sol index 81a54cd0..35c5f12a 100644 --- a/src/SablierV2MerkleLockupFactory.sol +++ b/src/SablierV2MerkleLockupFactory.sol @@ -84,12 +84,17 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { external returns (ISablierV2MerkleLT merkleLT) { - // Calculate the sum of percentages and durations across all tranches. uint256 totalDuration; - for (uint256 i = 0; i < tranchesWithPercentages.length; ++i) { - unchecked { - // Safe to use `unchecked` because its only used in the event. - totalDuration += tranchesWithPercentages[i].duration; + + // Need a separate scope to prevent the stack too deep error. + { + // Calculate the sum of percentages and durations across all tranches. + uint256 count = tranchesWithPercentages.length; + for (uint256 i = 0; i < count; ++i) { + unchecked { + // Safe to use `unchecked` because its only used in the event. + totalDuration += tranchesWithPercentages[i].duration; + } } } From f9defaeb185360d09abba3f7e2f748d993063296 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Mon, 24 Jun 2024 22:46:42 +0100 Subject: [PATCH 57/61] chore: update bun lockfile --- bun.lockb | Bin 42555 -> 42555 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bun.lockb b/bun.lockb index e505bbc2779419e59df455d3fff6431a3150ffc4..c142804be73aaab79265dc53d9774d1b4130ecc6 100755 GIT binary patch delta 62 zcmV-E0Kxye%mTa20+22sTRCz6%Zs5_N6CI-f7hz-Ymgtm+Zn|`C>$BNTCycSu}&F< U2Vr7nH!(FdlfjD{vx$YWAeWgNqyPW_ delta 62 zcmV-E0Kxye%mTa20+22sI5_We6lw2o)`S<@eLj~txxBcVuPafQk#s{5$%v>ku}&F< U2R1M;VliT5lfjD{vx$YWAcE){q5uE@ From 37164aea066ad052974593ca63dcd35c27bf3f15 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Sat, 29 Jun 2024 11:38:30 +0100 Subject: [PATCH 58/61] add gas benchmarks for `BatchLockup` (#359) * docs: add gas benchmarks for Batch lockup * doc(benchmark): use various batch size * test: rename variables test: misc enhancements * test(benchmark): use n/a in the benchmark table --------- Co-authored-by: Paul Razvan Berg --- .gas-snapshot | 53 ----- benchmark/BatchLockup.Gas.t.sol | 272 ++++++++++++++++++++++ benchmark/Benchmark.t.sol | 55 +++++ benchmark/results/SablierV2BatchLockup.md | 34 +++ bun.lockb | Bin 42555 -> 42555 bytes foundry.toml | 6 + package.json | 4 +- test/utils/Defaults.sol | 66 ++++-- 8 files changed, 417 insertions(+), 73 deletions(-) delete mode 100644 .gas-snapshot create mode 100644 benchmark/BatchLockup.Gas.t.sol create mode 100644 benchmark/Benchmark.t.sol create mode 100644 benchmark/results/SablierV2BatchLockup.md diff --git a/.gas-snapshot b/.gas-snapshot deleted file mode 100644 index 8f89bfa4..00000000 --- a/.gas-snapshot +++ /dev/null @@ -1,53 +0,0 @@ -BaseScript_Test:test_ConstructCreate2Salt() (gas: 29789) -Claim_Integration_Test:test_Claim() (gas: 296791) -Claim_Integration_Test:test_Claim() (gas: 359475) -Claim_Integration_Test:test_Claim_CalculatedAmountsSumNotEqualClaimAmount() (gas: 1902438) -Claim_Integration_Test:test_RevertGiven_AlreadyClaimed() (gas: 274580) -Claim_Integration_Test:test_RevertGiven_AlreadyClaimed() (gas: 329458) -Claim_Integration_Test:test_RevertGiven_CampaignExpired() (gas: 17764) -Claim_Integration_Test:test_RevertGiven_CampaignExpired() (gas: 17789) -Clawback_Integration_Test:testFuzz_Clawback(address) (runs: 28, μ: 295781, ~: 295781) -Clawback_Integration_Test:testFuzz_Clawback(address) (runs: 28, μ: 350706, ~: 350706) -Clawback_Integration_Test:test_Clawback() (gas: 278295) -Clawback_Integration_Test:test_Clawback() (gas: 333220) -Clawback_Integration_Test:test_RevertGiven_CampaignNotExpired() (gas: 19501) -Clawback_Integration_Test:test_RevertGiven_CampaignNotExpired() (gas: 19501) -Constructor_MerkleLL_Integration_Test:test_Constructor() (gas: 1150927) -Constructor_MerkleLT_Integration_Test:test_Constructor() (gas: 1495564) -CreateMerkleLL_Integration_Test:testFuzz_CreateMerkleLL(address,uint40) (runs: 28, μ: 1074513, ~: 1074513) -CreateMerkleLT_Integration_Test:testFuzz_CreateMerkleLT(address,uint40) (runs: 28, μ: 1368933, ~: 1368933) -CreateWithDurationsLD_Integration_Test:test_BatchCreateWithDurations() (gas: 2021978) -CreateWithDurationsLL_Integration_Test:test_BatchCreateWithDurations() (gas: 1498683) -CreateWithDurationsLT_Integration_Test:test_BatchCreateWithDurations() (gas: 2007290) -CreateWithTimestampsLD_Integration_Test:test_BatchCreateWithTimestamps() (gas: 2005062) -CreateWithTimestampsLL_Integration_Test:test_BatchCreateWithTimestamps() (gas: 1498016) -CreateWithTimestampsLT_Integration_Test:test_BatchCreateWithTimestamps() (gas: 1991514) -HasClaimed_Integration_Test:test_HasClaimed() (gas: 263153) -HasClaimed_Integration_Test:test_HasClaimed() (gas: 318056) -HasClaimed_Integration_Test:test_HasClaimed_IndexNotInTree() (gas: 11174) -HasClaimed_Integration_Test:test_HasClaimed_IndexNotInTree() (gas: 11196) -HasClaimed_Integration_Test:test_HasClaimed_NotClaimed() (gas: 16387) -HasClaimed_Integration_Test:test_HasClaimed_NotClaimed() (gas: 16409) -HasExpired_Integration_Test:test_HasExpired_ExpirationEqualToBlockTimestamp() (gas: 14670) -HasExpired_Integration_Test:test_HasExpired_ExpirationEqualToBlockTimestamp() (gas: 14692) -HasExpired_Integration_Test:test_HasExpired_ExpirationGreaterThanBlockTimestamp() (gas: 14766) -HasExpired_Integration_Test:test_HasExpired_ExpirationGreaterThanBlockTimestamp() (gas: 14788) -HasExpired_Integration_Test:test_HasExpired_ExpirationLessThanBlockTimestamp() (gas: 8902) -HasExpired_Integration_Test:test_HasExpired_ExpirationLessThanBlockTimestamp() (gas: 8924) -HasExpired_Integration_Test:test_HasExpired_ExpirationZero() (gas: 1053478) -HasExpired_Integration_Test:test_HasExpired_ExpirationZero() (gas: 1343750) -MerkleBuilder_Test:testFuzz_ComputeLeaf(uint256,address,uint128) (runs: 28, μ: 4323, ~: 4323) -MerkleBuilder_Test:testFuzz_ComputeLeaves((uint256,address,uint128)[]) (runs: 28, μ: 292561, ~: 288103) -Precompiles_Test:test_DeployBatchLockup() (gas: 3323914) -Precompiles_Test:test_DeployMerkleLockupFactory() (gas: 6699573) -Precompiles_Test:test_DeployPeriphery() (gas: 10022773) -USDC_CreateWithTimestamps_LockupDynamic_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLD((uint128,address,address,uint128,uint40,(uint128,uint64,uint40)[])) (runs: 28, μ: 35637382, ~: 36910130) -USDC_CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLL((uint128,(uint40,uint40,uint40),address,address,uint128)) (runs: 28, μ: 1731234, ~: 2052336) -USDC_CreateWithTimestamps_LockupTranched_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLT((uint128,address,address,uint128,uint40,(uint128,uint40)[])) (runs: 28, μ: 32364663, ~: 14997747) -USDC_MerkleLL_Fork_Test:testForkFuzz_MerkleLL((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 28, μ: 6023356, ~: 6239037) -USDC_MerkleLT_Fork_Test:testForkFuzz_MerkleLT((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 28, μ: 6380188, ~: 6588339) -USDT_CreateWithTimestamps_LockupDynamic_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLD((uint128,address,address,uint128,uint40,(uint128,uint64,uint40)[])) (runs: 28, μ: 32017272, ~: 28939343) -USDT_CreateWithTimestamps_LockupLinear_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLL((uint128,(uint40,uint40,uint40),address,address,uint128)) (runs: 28, μ: 1776276, ~: 1948060) -USDT_CreateWithTimestamps_LockupTranched_BatchLockup_Fork_Test:testForkFuzz_CreateWithTimestampsLT((uint128,address,address,uint128,uint40,(uint128,uint40)[])) (runs: 28, μ: 32063121, ~: 16796582) -USDT_MerkleLL_Fork_Test:testForkFuzz_MerkleLL((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 28, μ: 5989767, ~: 6205276) -USDT_MerkleLT_Fork_Test:testForkFuzz_MerkleLT((address,uint40,(uint256,uint256,uint128)[],uint256)) (runs: 28, μ: 6345991, ~: 6556943) \ No newline at end of file diff --git a/benchmark/BatchLockup.Gas.t.sol b/benchmark/BatchLockup.Gas.t.sol new file mode 100644 index 00000000..6ac28654 --- /dev/null +++ b/benchmark/BatchLockup.Gas.t.sol @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22; + +import { ud2x18 } from "@prb/math/src/UD2x18.sol"; +import { LockupDynamic, LockupLinear, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; + +import { BatchLockup } from "../src/types/DataTypes.sol"; +import { BatchLockupBuilder } from "../test/utils/BatchLockupBuilder.sol"; +import { Benchmark_Test } from "./Benchmark.t.sol"; + +/// @notice Tests used to benchmark {BatchLockup}. +/// @dev This contract creates a Markdown file with the gas usage of each function. +contract BatchLockup_Gas_Test is Benchmark_Test { + /*////////////////////////////////////////////////////////////////////////// + STATE VARIABLES + //////////////////////////////////////////////////////////////////////////*/ + + uint128 internal constant AMOUNT_PER_ITEM = 10e18; + uint8[5] internal batches = [5, 10, 20, 30, 50]; + uint8[5] internal counts = [24, 24, 24, 24, 12]; + + /*////////////////////////////////////////////////////////////////////////// + TEST FUNCTION + //////////////////////////////////////////////////////////////////////////*/ + + function testGas_Implementations() external { + // Set the file path. + benchmarkResultsFile = string.concat(benchmarkResults, "SablierV2BatchLockup.md"); + + // Create the file if it doesn't exist, otherwise overwrite it. + vm.writeFile({ + path: benchmarkResultsFile, + data: string.concat( + "# Benchmarks for BatchLockup\n\n", + "| Function | Lockup Type | Segments/Tranches | Batch Size | Gas Usage |\n", + "| --- | --- | --- | --- | --- |\n" + ) + }); + + for (uint256 i; i < batches.length; ++i) { + // Benchmark the batch create functions for Lockup Linear. + gasCreateWithDurationsLL(batches[i]); + gasCreateWithTimestampsLL(batches[i]); + + // Benchmark the batch create functions for Lockup Dynamic. + gasCreateWithDurationsLD({ batchSize: batches[i], segmentsCount: counts[i] }); + gasCreateWithTimestampsLD({ batchSize: batches[i], segmentsCount: counts[i] }); + + // Benchmark the batch create functions for Lockup Tranched. + gasCreateWithDurationsLT({ batchSize: batches[i], tranchesCount: counts[i] }); + gasCreateWithTimestampsLT({ batchSize: batches[i], tranchesCount: counts[i] }); + } + } + + /*////////////////////////////////////////////////////////////////////////// + GAS BENCHMARKS FOR BATCH FUNCTIONS + //////////////////////////////////////////////////////////////////////////*/ + + function gasCreateWithDurationsLD(uint256 batchSize, uint256 segmentsCount) internal { + BatchLockup.CreateWithDurationsLD[] memory params = BatchLockupBuilder.fillBatch({ + params: defaults.createWithDurationsLD({ + asset_: dai, + totalAmount_: uint128(AMOUNT_PER_ITEM * segmentsCount), + segments_: _generateSegmentsWithDuration(segmentsCount) + }), + batchSize: batchSize + }); + + uint256 initialGas = gasleft(); + batchLockup.createWithDurationsLD(lockupDynamic, dai, params); + string memory gasUsed = vm.toString(initialGas - gasleft()); + + contentToAppend = string.concat( + "| `createWithDurationsLD` | Lockup Dynamic |", + vm.toString(segmentsCount), + " |", + vm.toString(batchSize), + " | ", + gasUsed, + " |" + ); + + // Append the content to the file. + appendToFile(benchmarkResultsFile, contentToAppend); + } + + function gasCreateWithTimestampsLD(uint256 batchSize, uint256 segmentsCount) internal { + BatchLockup.CreateWithTimestampsLD[] memory params = BatchLockupBuilder.fillBatch({ + params: defaults.createWithTimestampsLD({ + asset_: dai, + totalAmount_: uint128(AMOUNT_PER_ITEM * segmentsCount), + segments_: _generateSegments(segmentsCount) + }), + batchSize: batchSize + }); + + uint256 initialGas = gasleft(); + batchLockup.createWithTimestampsLD(lockupDynamic, dai, params); + string memory gasUsed = vm.toString(initialGas - gasleft()); + + contentToAppend = string.concat( + "| `createWithTimestampsLD` | Lockup Dynamic |", + vm.toString(segmentsCount), + " |", + vm.toString(batchSize), + " | ", + gasUsed, + " |" + ); + + // Append the data to the file + appendToFile(benchmarkResultsFile, contentToAppend); + } + + function gasCreateWithDurationsLL(uint256 batchSize) internal { + BatchLockup.CreateWithDurationsLL[] memory params = + BatchLockupBuilder.fillBatch({ params: defaults.createWithDurationsLL(dai), batchSize: batchSize }); + + uint256 initialGas = gasleft(); + batchLockup.createWithDurationsLL(lockupLinear, dai, params); + string memory gasUsed = vm.toString(initialGas - gasleft()); + + contentToAppend = string.concat( + "| `createWithDurationsLL` | Lockup Linear | N/A |", vm.toString(batchSize), " | ", gasUsed, " |" + ); + + // Append the content to the file. + appendToFile(benchmarkResultsFile, contentToAppend); + } + + function gasCreateWithTimestampsLL(uint256 batchSize) internal { + BatchLockup.CreateWithTimestampsLL[] memory params = + BatchLockupBuilder.fillBatch({ params: defaults.createWithTimestampsLL(dai), batchSize: batchSize }); + + uint256 initialGas = gasleft(); + batchLockup.createWithTimestampsLL(lockupLinear, dai, params); + string memory gasUsed = vm.toString(initialGas - gasleft()); + + contentToAppend = string.concat( + "| `createWithTimestampsLL` | Lockup Linear | N/A |", vm.toString(batchSize), " | ", gasUsed, " |" + ); + + // Append the data to the file + appendToFile(benchmarkResultsFile, contentToAppend); + } + + function gasCreateWithDurationsLT(uint256 batchSize, uint256 tranchesCount) internal { + BatchLockup.CreateWithDurationsLT[] memory params = BatchLockupBuilder.fillBatch({ + params: defaults.createWithDurationsLT({ + asset_: dai, + totalAmount_: uint128(AMOUNT_PER_ITEM * tranchesCount), + tranches_: _generateTranchesWithDuration(tranchesCount) + }), + batchSize: batchSize + }); + + uint256 initialGas = gasleft(); + batchLockup.createWithDurationsLT(lockupTranched, dai, params); + string memory gasUsed = vm.toString(initialGas - gasleft()); + + contentToAppend = string.concat( + "| `createWithDurationsLT` | Lockup Tranched |", + vm.toString(tranchesCount), + " |", + vm.toString(batchSize), + " | ", + gasUsed, + " |" + ); + + // Append the content to the file. + appendToFile(benchmarkResultsFile, contentToAppend); + } + + function gasCreateWithTimestampsLT(uint256 batchSize, uint256 tranchesCount) internal { + BatchLockup.CreateWithTimestampsLT[] memory params = BatchLockupBuilder.fillBatch({ + params: defaults.createWithTimestampsLT({ + asset_: dai, + totalAmount_: uint128(AMOUNT_PER_ITEM * tranchesCount), + tranches_: _generateTranches(tranchesCount) + }), + batchSize: batchSize + }); + + uint256 initialGas = gasleft(); + batchLockup.createWithTimestampsLT(lockupTranched, dai, params); + string memory gasUsed = vm.toString(initialGas - gasleft()); + + contentToAppend = string.concat( + "| `createWithTimestampsLT` | Lockup Tranched |", + vm.toString(tranchesCount), + " |", + vm.toString(batchSize), + " | ", + gasUsed, + " |" + ); + + // Append the data to the file + appendToFile(benchmarkResultsFile, contentToAppend); + } + + /*////////////////////////////////////////////////////////////////////////// + HELPERS + //////////////////////////////////////////////////////////////////////////*/ + + function _generateSegments(uint256 segmentsCount) private view returns (LockupDynamic.Segment[] memory) { + LockupDynamic.Segment[] memory segments = new LockupDynamic.Segment[](segmentsCount); + + // Populate segments. + for (uint256 i = 0; i < segmentsCount; ++i) { + segments[i] = LockupDynamic.Segment({ + amount: AMOUNT_PER_ITEM, + exponent: ud2x18(0.5e18), + timestamp: getBlockTimestamp() + uint40(defaults.CLIFF_DURATION() * (1 + i)) + }); + } + + return segments; + } + + function _generateSegmentsWithDuration(uint256 segmentsCount) + private + view + returns (LockupDynamic.SegmentWithDuration[] memory) + { + LockupDynamic.SegmentWithDuration[] memory segments = new LockupDynamic.SegmentWithDuration[](segmentsCount); + + // Populate segments. + for (uint256 i; i < segmentsCount; ++i) { + segments[i] = LockupDynamic.SegmentWithDuration({ + amount: AMOUNT_PER_ITEM, + exponent: ud2x18(0.5e18), + duration: defaults.CLIFF_DURATION() + }); + } + + return segments; + } + + function _generateTranches(uint256 tranchesCount) private view returns (LockupTranched.Tranche[] memory) { + LockupTranched.Tranche[] memory tranches = new LockupTranched.Tranche[](tranchesCount); + + // Populate tranches. + for (uint256 i = 0; i < tranchesCount; ++i) { + tranches[i] = ( + LockupTranched.Tranche({ + amount: AMOUNT_PER_ITEM, + timestamp: getBlockTimestamp() + uint40(defaults.CLIFF_DURATION() * (1 + i)) + }) + ); + } + + return tranches; + } + + function _generateTranchesWithDuration(uint256 tranchesCount) + private + view + returns (LockupTranched.TrancheWithDuration[] memory) + { + LockupTranched.TrancheWithDuration[] memory tranches = new LockupTranched.TrancheWithDuration[](tranchesCount); + + // Populate tranches. + for (uint256 i; i < tranchesCount; ++i) { + tranches[i] = + LockupTranched.TrancheWithDuration({ amount: AMOUNT_PER_ITEM, duration: defaults.CLIFF_DURATION() }); + } + + return tranches; + } +} diff --git a/benchmark/Benchmark.t.sol b/benchmark/Benchmark.t.sol new file mode 100644 index 00000000..b03d8e50 --- /dev/null +++ b/benchmark/Benchmark.t.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.22; + +import { Integration_Test } from "../test/integration/Integration.t.sol"; + +/// @notice Benchmark contract with common logic needed by all tests. +abstract contract Benchmark_Test is Integration_Test { + /*////////////////////////////////////////////////////////////////////////// + STATE VARIABLES + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev The directory where the benchmark files are stored. + string internal benchmarkResults = "benchmark/results/"; + + /// @dev The path to the file where the benchmark results are stored. + string internal benchmarkResultsFile; + + /// @dev A variable used to store the content to append to the results file. + string internal contentToAppend; + + /*////////////////////////////////////////////////////////////////////////// + SET-UP FUNCTION + //////////////////////////////////////////////////////////////////////////*/ + + function setUp() public override { + super.setUp(); + + deal({ token: address(dai), to: users.alice, give: type(uint256).max }); + resetPrank({ msgSender: users.alice }); + + // Create the first streams in each Lockup contract to initialize all the variables. + _createFewStreams(); + } + + /*////////////////////////////////////////////////////////////////////////// + HELPERS + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev Append a line to the file at given path. + function appendToFile(string memory path, string memory line) internal { + vm.writeLine({ path: path, data: line }); + } + + /// @dev Internal function to creates a few streams in each Lockup contract. + function _createFewStreams() private { + approveContract({ asset_: dai, from: users.alice, spender: address(lockupDynamic) }); + approveContract({ asset_: dai, from: users.alice, spender: address(lockupLinear) }); + approveContract({ asset_: dai, from: users.alice, spender: address(lockupTranched) }); + for (uint128 i = 0; i < 100; ++i) { + lockupDynamic.createWithTimestamps(defaults.createWithTimestampsLD()); + lockupLinear.createWithTimestamps(defaults.createWithTimestampsLL()); + lockupTranched.createWithTimestamps(defaults.createWithTimestampsLT()); + } + } +} diff --git a/benchmark/results/SablierV2BatchLockup.md b/benchmark/results/SablierV2BatchLockup.md new file mode 100644 index 00000000..799b0eec --- /dev/null +++ b/benchmark/results/SablierV2BatchLockup.md @@ -0,0 +1,34 @@ +# Benchmarks for BatchLockup + +| Function | Lockup Type | Segments/Tranches | Batch Size | Gas Usage | +| ------------------------ | --------------- | ----------------- | ---------- | --------- | +| `createWithDurationsLL` | Lockup Linear | N/A | 5 | 771013 | +| `createWithTimestampsLL` | Lockup Linear | N/A | 5 | 732772 | +| `createWithDurationsLD` | Lockup Dynamic | 24 | 5 | 3951599 | +| `createWithTimestampsLD` | Lockup Dynamic | 24 | 5 | 3815274 | +| `createWithDurationsLT` | Lockup Tranched | 24 | 5 | 3862651 | +| `createWithTimestampsLT` | Lockup Tranched | 24 | 5 | 3744523 | +| `createWithDurationsLL` | Lockup Linear | N/A | 10 | 1417180 | +| `createWithTimestampsLL` | Lockup Linear | N/A | 10 | 1414247 | +| `createWithDurationsLD` | Lockup Dynamic | 24 | 10 | 7819165 | +| `createWithTimestampsLD` | Lockup Dynamic | 24 | 10 | 7585616 | +| `createWithDurationsLT` | Lockup Tranched | 24 | 10 | 7632114 | +| `createWithTimestampsLT` | Lockup Tranched | 24 | 10 | 7444115 | +| `createWithDurationsLL` | Lockup Linear | N/A | 20 | 2783510 | +| `createWithTimestampsLL` | Lockup Linear | N/A | 20 | 2779081 | +| `createWithDurationsLD` | Lockup Dynamic | 24 | 20 | 15617207 | +| `createWithTimestampsLD` | Lockup Dynamic | 24 | 20 | 15131248 | +| `createWithDurationsLT` | Lockup Tranched | 24 | 20 | 15211892 | +| `createWithTimestampsLT` | Lockup Tranched | 24 | 20 | 14846363 | +| `createWithDurationsLL` | Lockup Linear | N/A | 30 | 4143337 | +| `createWithTimestampsLL` | Lockup Linear | N/A | 30 | 4148585 | +| `createWithDurationsLD` | Lockup Dynamic | 24 | 30 | 23460912 | +| `createWithTimestampsLD` | Lockup Dynamic | 24 | 30 | 22697560 | +| `createWithDurationsLT` | Lockup Tranched | 24 | 30 | 22794686 | +| `createWithTimestampsLT` | Lockup Tranched | 24 | 30 | 22267335 | +| `createWithDurationsLL` | Lockup Linear | N/A | 50 | 6871104 | +| `createWithTimestampsLL` | Lockup Linear | N/A | 50 | 6893797 | +| `createWithDurationsLD` | Lockup Dynamic | 12 | 50 | 22990726 | +| `createWithTimestampsLD` | Lockup Dynamic | 12 | 50 | 22355943 | +| `createWithDurationsLT` | Lockup Tranched | 12 | 50 | 22413554 | +| `createWithTimestampsLT` | Lockup Tranched | 12 | 50 | 22006169 | diff --git a/bun.lockb b/bun.lockb index c142804be73aaab79265dc53d9774d1b4130ecc6..e0520367a58eecf09046257131a3266a1ecda263 100755 GIT binary patch delta 62 zcmV-E0Kxye%mTa20+22sFOMpB=l9q+gM+6|oN@l&-&4wmkCoP!(CiqII~;m@u}&F< U2Qy`5HZx>mlfjD{vx$YWAmdXWMF0Q* delta 62 zcmV-E0Kxye%mTa20+22sTRCz6%Zs5_N6CI-f7hz-Ymgtm+Zn|`C>$BNTCycSu}&F< U2Vr7nH!(FdlfjD{vx$YWAeWgNqyPW_ diff --git a/foundry.toml b/foundry.toml index dc2ea62f..ec45a0de 100644 --- a/foundry.toml +++ b/foundry.toml @@ -6,6 +6,8 @@ fs_permissions = [ { access = "read", path = "./out-optimized" }, { access = "read", path = "package.json"}, + { access = "read-write", path = "./benchmark/results"}, + { access = "read-write", path = "./cache" } ] gas_reports = [ "SablierV2BatchLockup", @@ -26,6 +28,10 @@ max_test_rejects = 1_000_000 # Number of times `vm.assume` can fail runs = 20 +# Run only the code inside benchmark directory +[profile.benchmark] + test = "benchmark" + # Speed up compilation and tests during development [profile.lite] optimizer = false diff --git a/package.json b/package.json index 2c5d6595..669baccd 100644 --- a/package.json +++ b/package.json @@ -58,12 +58,10 @@ }, "repository": "github:sablier-labs/v2-periphery", "scripts": { + "benchmark": "bun run build:optimized && FOUNDRY_PROFILE=benchmark forge test --mt testGas && bun run prettier:write", "build": "forge build", "build:optimized": "FOUNDRY_PROFILE=optimized forge build", "clean": "rm -rf artifacts broadcast cache docs out-optimized out", - "gas:report": "forge test --gas-report --no-match-test \"test(Fuzz)?_RevertWhen_\\w{1,}?\"", - "gas:snapshot": "forge snapshot --no-match-test \"test(Fuzz)?_RevertWhen_\\w{1,}?\"", - "gas:snapshot:optimized": "bun run build:optimized && FOUNDRY_PROFILE=test-optimized forge snapshot --no-match-test \"test(Fork)?(Fuzz)?_RevertWhen_\\w{1,}?\"", "lint": "bun run lint:sol && bun run prettier:check", "lint:sol": "forge fmt --check && bun solhint \"{precompiles,script,src,test}/**/*.sol\"", "prepack": "bun install && bash ./shell/prepare-artifacts.sh", diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index 178cadf7..93c664de 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -181,42 +181,58 @@ contract Defaults is Merkle { //////////////////////////////////////////////////////////////////////////*/ function createWithDurationsLD() public view returns (LockupDynamic.CreateWithDurations memory) { - return createWithDurationsLD(asset); + return createWithDurationsLD(asset, PER_STREAM_AMOUNT, segmentsWithDurations()); } - function createWithDurationsLD(IERC20 asset_) public view returns (LockupDynamic.CreateWithDurations memory) { + function createWithDurationsLD( + IERC20 asset_, + uint128 totalAmount_, + LockupDynamic.SegmentWithDuration[] memory segments_ + ) + public + view + returns (LockupDynamic.CreateWithDurations memory) + { return LockupDynamic.CreateWithDurations({ sender: users.alice, recipient: users.recipient0, - totalAmount: PER_STREAM_AMOUNT, + totalAmount: totalAmount_, asset: asset_, cancelable: true, transferable: true, - segments: segmentsWithDurations(), + segments: segments_, broker: broker() }); } function createWithTimestampsLD() public view returns (LockupDynamic.CreateWithTimestamps memory) { - return createWithTimestampsLD(asset); + return createWithTimestampsLD(asset, PER_STREAM_AMOUNT, segments()); } - function createWithTimestampsLD(IERC20 asset_) public view returns (LockupDynamic.CreateWithTimestamps memory) { + function createWithTimestampsLD( + IERC20 asset_, + uint128 totalAmount_, + LockupDynamic.Segment[] memory segments_ + ) + public + view + returns (LockupDynamic.CreateWithTimestamps memory) + { return LockupDynamic.CreateWithTimestamps({ sender: users.alice, recipient: users.recipient0, - totalAmount: PER_STREAM_AMOUNT, + totalAmount: totalAmount_, asset: asset_, cancelable: true, transferable: true, startTime: START_TIME, - segments: segments(), + segments: segments_, broker: broker() }); } /// @dev Returns a batch of {LockupDynamic.Segment} parameters. - function segments() private view returns (LockupDynamic.Segment[] memory segments_) { + function segments() public view returns (LockupDynamic.Segment[] memory segments_) { segments_ = new LockupDynamic.Segment[](2); segments_[0] = LockupDynamic.Segment({ amount: 2500e18, @@ -302,36 +318,52 @@ contract Defaults is Merkle { //////////////////////////////////////////////////////////////////////////*/ function createWithDurationsLT() public view returns (LockupTranched.CreateWithDurations memory) { - return createWithDurationsLT(asset); + return createWithDurationsLT(asset, PER_STREAM_AMOUNT, tranchesWithDurations()); } - function createWithDurationsLT(IERC20 asset_) public view returns (LockupTranched.CreateWithDurations memory) { + function createWithDurationsLT( + IERC20 asset_, + uint128 totalAmount_, + LockupTranched.TrancheWithDuration[] memory tranches_ + ) + public + view + returns (LockupTranched.CreateWithDurations memory) + { return LockupTranched.CreateWithDurations({ sender: users.alice, recipient: users.recipient0, - totalAmount: PER_STREAM_AMOUNT, + totalAmount: totalAmount_, asset: asset_, cancelable: true, transferable: true, - tranches: tranchesWithDurations(), + tranches: tranches_, broker: broker() }); } function createWithTimestampsLT() public view returns (LockupTranched.CreateWithTimestamps memory) { - return createWithTimestampsLT(asset); + return createWithTimestampsLT(asset, PER_STREAM_AMOUNT, tranches()); } - function createWithTimestampsLT(IERC20 asset_) public view returns (LockupTranched.CreateWithTimestamps memory) { + function createWithTimestampsLT( + IERC20 asset_, + uint128 totalAmount_, + LockupTranched.Tranche[] memory tranches_ + ) + public + view + returns (LockupTranched.CreateWithTimestamps memory) + { return LockupTranched.CreateWithTimestamps({ sender: users.alice, recipient: users.recipient0, - totalAmount: PER_STREAM_AMOUNT, + totalAmount: totalAmount_, asset: asset_, cancelable: true, transferable: true, startTime: START_TIME, - tranches: tranches(), + tranches: tranches_, broker: broker() }); } From c10978dd4cdb54301b9c2d63c7e0af41da9110f3 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Sat, 29 Jun 2024 23:02:36 +0100 Subject: [PATCH 59/61] chore: explicitely set gas_limit in foundry config --- foundry.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/foundry.toml b/foundry.toml index ec45a0de..bb3e8ab8 100644 --- a/foundry.toml +++ b/foundry.toml @@ -9,6 +9,7 @@ { access = "read-write", path = "./benchmark/results"}, { access = "read-write", path = "./cache" } ] + gas_limit = 9223372036854775807 gas_reports = [ "SablierV2BatchLockup", "SablierV2MerkleLL", From 98ee3b68a8232ea9b882fbdf4451880ceb9ad9fd Mon Sep 17 00:00:00 2001 From: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com> Date: Thu, 4 Jul 2024 16:57:48 +0300 Subject: [PATCH 60/61] build: bump core (#363) --- bun.lockb | Bin 42555 -> 42484 bytes package.json | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bun.lockb b/bun.lockb index e0520367a58eecf09046257131a3266a1ecda263..52f91a0dba18a5ba975a09892ffc26f7dc5c416c 100755 GIT binary patch delta 6450 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 G8u1T`V9B8X diff --git a/package.json b/package.json index 669baccd..719910c7 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "dependencies": { "@openzeppelin/contracts": "5.0.2", "@prb/math": "4.0.3", - "@sablier/v2-core": "github:sablier-labs/v2-core#staging" + "@sablier/v2-core": "1.2.0" }, "devDependencies": { "forge-std": "github:foundry-rs/forge-std#v1.8.2", @@ -51,7 +51,7 @@ "web3" ], "peerDependencies": { - "@sablier/v2-core": "github:sablier-labs/v2-core" + "@sablier/v2-core": "1.2.0" }, "publishConfig": { "access": "public" From 2c8643a0967b1252e4dee666fe5dd6a7568551cd Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Thu, 4 Jul 2024 15:16:04 +0100 Subject: [PATCH 61/61] docs: roll 1.2.0 (#329) * docs: roll 1.2.0 * build: npm ignore file in test utils * docs(refactor): use bump * docs(refactor): use bump in OpenZeppelin * docs: add merklelockup and batchlockup changelog for lockup tranched * docs: update changelog * andrei suggestion * docs(changelog): add solc update * docs(changelog): add core bump pr * docs: update date --------- Co-authored-by: andreivladbrg --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ package.json | 2 +- test/utils/.npmignore | 1 + test/utils/BaseScript.t.sol | 2 +- test/utils/Precompiles.t.sol | 6 +++--- 5 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 test/utils/.npmignore diff --git a/CHANGELOG.md b/CHANGELOG.md index fa7d7922..ff8c097e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Common Changelog](https://common-changelog.org). +[1.2.0]: https://github.com/sablier-labs/v2-periphery/compare/v1.1.1...v1.2.0 [1.1.1]: https://github.com/sablier-labs/v2-periphery/compare/v1.1.0...v1.1.1 [1.1.0]: https://github.com/sablier-labs/v2-periphery/compare/v1.0.3...v1.1.0 [1.0.3]: https://github.com/sablier-labs/v2-periphery/compare/v1.0.2...v1.0.3 @@ -11,6 +12,31 @@ The format is based on [Common Changelog](https://common-changelog.org). [1.0.1]: https://github.com/sablier-labs/v2-periphery/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/sablier-labs/v2-periphery/releases/tag/v1.0.0 +## [1.2.0] - 2024-07-04 + +### Changed + +- Bump dependencies ([#283](https://github.com/sablier-labs/v2-periphery/pull/283), + [#351](https://github.com/sablier-labs/v2-periphery/pull/351), + [#363](https://github.com/sablier-labs/v2-periphery/pull/363)) +- Rename `Batch` to `BatchLockup` ([#322](https://github.com/sablier-labs/v2-periphery/pull/322)) +- Rename `MerkleStreamer` to `MerkleLockup` ([#268](https://github.com/sablier-labs/v2-periphery/pull/268)) +- Refactor `Range` to `Timestamps` ([#335](https://github.com/sablier-labs/v2-periphery/pull/335)) +- Switch to Bun ([#249](https://github.com/sablier-labs/v2-periphery/pull/249)) +- Use Solidity v0.8.26 ([#351](https://github.com/sablier-labs/v2-periphery/pull/351)) + +### Added + +- And `BatchLockup` support for `LockupTranched` ([#300](https://github.com/sablier-labs/v2-periphery/pull/300)) +- Add grace period mechanism for `clawback` function ([#340](https://github.com/sablier-labs/v2-periphery/pull/340)) +- Add `MerkleLockup` support for `LockupTranched` ([#297](https://github.com/sablier-labs/v2-periphery/pull/297), + [#357](https://github.com/sablier-labs/v2-periphery/pull/357)) +- Add `precompiles` in the NPM release ([#302](https://github.com/sablier-labs/v2-periphery/pull/302)) + +### Removed + +- **Breaking**: Remove protocol fee check in `MerkleLL` ([#257](https://github.com/sablier-labs/v2-periphery/pull/257)) + ## [1.1.1] - 2023-12-20 ### Changed diff --git a/package.json b/package.json index 719910c7..88cca733 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@sablier/v2-periphery", "description": "Peripheral smart contracts for interacting with Sablier V2", - "version": "1.1.1", + "version": "1.2.0", "author": { "name": "Sablier Labs Ltd", "url": "https://sablier.com" diff --git a/test/utils/.npmignore b/test/utils/.npmignore new file mode 100644 index 00000000..c607d373 --- /dev/null +++ b/test/utils/.npmignore @@ -0,0 +1 @@ +*.t.sol diff --git a/test/utils/BaseScript.t.sol b/test/utils/BaseScript.t.sol index 334f7364..6fa78951 100644 --- a/test/utils/BaseScript.t.sol +++ b/test/utils/BaseScript.t.sol @@ -13,7 +13,7 @@ contract BaseScript_Test is StdAssertions { function test_ConstructCreate2Salt() public view { string memory chainId = block.chainid.toString(); - string memory version = "1.1.1"; + string memory version = "1.2.0"; string memory salt = string.concat("ChainID ", chainId, ", Version ", version); bytes32 actualSalt = baseScript.constructCreate2Salt(); diff --git a/test/utils/Precompiles.t.sol b/test/utils/Precompiles.t.sol index 88155421..1fec3c09 100644 --- a/test/utils/Precompiles.t.sol +++ b/test/utils/Precompiles.t.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.22 <0.9.0; -import { Precompiles } from "../../precompiles/Precompiles.sol"; -import { ISablierV2BatchLockup } from "../../src/interfaces/ISablierV2BatchLockup.sol"; -import { ISablierV2MerkleLockupFactory } from "../../src/interfaces/ISablierV2MerkleLockupFactory.sol"; +import { Precompiles } from "precompiles/Precompiles.sol"; +import { ISablierV2BatchLockup } from "src/interfaces/ISablierV2BatchLockup.sol"; +import { ISablierV2MerkleLockupFactory } from "src/interfaces/ISablierV2MerkleLockupFactory.sol"; import { Base_Test } from "../Base.t.sol";

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஻M|QS?$K3d*2Vl~``45koMSo- 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 TIU9+;oU 0) { vars.clawbackAmount = uint128(ASSET.balanceOf(address(vars.merkleLockupLL))); - vm.warp({ timestamp: uint256(params.expiration) + 1 seconds }); + vm.warp({ newTimestamp: uint256(params.expiration) + 1 seconds }); - changePrank({ msgSender: params.admin }); + resetPrank({ msgSender: params.admin }); expectCallToTransfer({ asset_: address(ASSET), to: params.admin, amount: vars.clawbackAmount }); vm.expectEmit({ emitter: address(vars.merkleLockupLL) }); emit Clawback({ to: params.admin, admin: params.admin, amount: vars.clawbackAmount }); diff --git a/test/fork/merkle-lockup/MerkleLockupLT.t.sol b/test/fork/merkle-lockup/MerkleLockupLT.t.sol index 095e5285..e1fe6eb2 100644 --- a/test/fork/merkle-lockup/MerkleLockupLT.t.sol +++ b/test/fork/merkle-lockup/MerkleLockupLT.t.sol @@ -182,9 +182,9 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { if (params.expiration > 0) { vars.clawbackAmount = uint128(ASSET.balanceOf(address(vars.merkleLockupLT))); - vm.warp({ timestamp: uint256(params.expiration) + 1 seconds }); + vm.warp({ newTimestamp: uint256(params.expiration) + 1 seconds }); - changePrank({ msgSender: params.admin }); + resetPrank({ msgSender: params.admin }); expectCallToTransfer({ asset_: address(ASSET), to: params.admin, amount: vars.clawbackAmount }); vm.expectEmit({ emitter: address(vars.merkleLockupLT) }); emit Clawback({ to: params.admin, admin: params.admin, amount: vars.clawbackAmount }); diff --git a/test/integration/merkle-lockup/MerkleLockup.t.sol b/test/integration/merkle-lockup/MerkleLockup.t.sol index 7d5ce8c1..19d2d0ef 100644 --- a/test/integration/merkle-lockup/MerkleLockup.t.sol +++ b/test/integration/merkle-lockup/MerkleLockup.t.sol @@ -32,19 +32,19 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { }); } - function computeMerkleLockupLLAddress() internal returns (address) { + function computeMerkleLockupLLAddress() internal view returns (address) { return computeMerkleLockupLLAddress(users.admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); } - function computeMerkleLockupLLAddress(address admin) internal returns (address) { + function computeMerkleLockupLLAddress(address admin) internal view returns (address) { return computeMerkleLockupLLAddress(admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); } - function computeMerkleLockupLLAddress(address admin, uint40 expiration) internal returns (address) { + function computeMerkleLockupLLAddress(address admin, uint40 expiration) internal view returns (address) { return computeMerkleLockupLLAddress(admin, defaults.MERKLE_ROOT(), expiration); } - function computeMerkleLockupLLAddress(address admin, bytes32 merkleRoot) internal returns (address) { + function computeMerkleLockupLLAddress(address admin, bytes32 merkleRoot) internal view returns (address) { return computeMerkleLockupLLAddress(admin, merkleRoot, defaults.EXPIRATION()); } @@ -83,19 +83,19 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { }); } - function computeMerkleLockupLTAddress() internal returns (address) { + function computeMerkleLockupLTAddress() internal view returns (address) { return computeMerkleLockupLTAddress(users.admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); } - function computeMerkleLockupLTAddress(address admin) internal returns (address) { + function computeMerkleLockupLTAddress(address admin) internal view returns (address) { return computeMerkleLockupLTAddress(admin, defaults.MERKLE_ROOT(), defaults.EXPIRATION()); } - function computeMerkleLockupLTAddress(address admin, uint40 expiration) internal returns (address) { + function computeMerkleLockupLTAddress(address admin, uint40 expiration) internal view returns (address) { return computeMerkleLockupLTAddress(admin, defaults.MERKLE_ROOT(), expiration); } - function computeMerkleLockupLTAddress(address admin, bytes32 merkleRoot) internal returns (address) { + function computeMerkleLockupLTAddress(address admin, bytes32 merkleRoot) internal view returns (address) { return computeMerkleLockupLTAddress(admin, merkleRoot, defaults.EXPIRATION()); } diff --git a/test/integration/merkle-lockup/ll/claim/claim.t.sol b/test/integration/merkle-lockup/ll/claim/claim.t.sol index 3551ef0c..1a77f244 100644 --- a/test/integration/merkle-lockup/ll/claim/claim.t.sol +++ b/test/integration/merkle-lockup/ll/claim/claim.t.sol @@ -16,7 +16,7 @@ contract Claim_Integration_Test is MerkleLockup_Integration_Test { uint40 expiration = defaults.EXPIRATION(); uint256 warpTime = expiration + 1 seconds; bytes32[] memory merkleProof; - vm.warp({ timestamp: warpTime }); + vm.warp({ newTimestamp: warpTime }); vm.expectRevert( abi.encodeWithSelector(Errors.SablierV2MerkleLockup_CampaignExpired.selector, warpTime, expiration) ); diff --git a/test/integration/merkle-lockup/ll/clawback/clawback.t.sol b/test/integration/merkle-lockup/ll/clawback/clawback.t.sol index e5fa4cb6..21110c7e 100644 --- a/test/integration/merkle-lockup/ll/clawback/clawback.t.sol +++ b/test/integration/merkle-lockup/ll/clawback/clawback.t.sol @@ -13,13 +13,13 @@ contract Clawback_Integration_Test is MerkleLockup_Integration_Test { } function test_RevertWhen_CallerNotAdmin() external { - changePrank({ msgSender: users.eve }); + resetPrank({ msgSender: users.eve }); vm.expectRevert(abi.encodeWithSelector(V2CoreErrors.CallerNotAdmin.selector, users.admin, users.eve)); merkleLockupLL.clawback({ to: users.eve, amount: 1 }); } modifier whenCallerAdmin() { - changePrank({ msgSender: users.admin }); + resetPrank({ msgSender: users.admin }); _; } @@ -35,7 +35,7 @@ contract Clawback_Integration_Test is MerkleLockup_Integration_Test { modifier givenCampaignExpired() { // Make a claim to have a different contract balance. claimLL(); - vm.warp({ timestamp: defaults.EXPIRATION() + 1 seconds }); + vm.warp({ newTimestamp: defaults.EXPIRATION() + 1 seconds }); _; } diff --git a/test/integration/merkle-lockup/ll/has-expired/hasExpired.t.sol b/test/integration/merkle-lockup/ll/has-expired/hasExpired.t.sol index c185e38f..5480efcc 100644 --- a/test/integration/merkle-lockup/ll/has-expired/hasExpired.t.sol +++ b/test/integration/merkle-lockup/ll/has-expired/hasExpired.t.sol @@ -19,17 +19,17 @@ contract HasExpired_Integration_Test is MerkleLockup_Integration_Test { _; } - function test_HasExpired_ExpirationLessThanCurrentTime() external whenExpirationNotZero { + function test_HasExpired_ExpirationLessThanCurrentTime() external view whenExpirationNotZero { assertFalse(merkleLockupLL.hasExpired(), "campaign expired"); } function test_HasExpired_ExpirationEqualToCurrentTime() external whenExpirationNotZero { - vm.warp({ timestamp: defaults.EXPIRATION() }); + vm.warp({ newTimestamp: defaults.EXPIRATION() }); assertTrue(merkleLockupLL.hasExpired(), "campaign not expired"); } function test_HasExpired_ExpirationGreaterThanCurrentTime() external whenExpirationNotZero { - vm.warp({ timestamp: defaults.EXPIRATION() + 1 seconds }); + vm.warp({ newTimestamp: defaults.EXPIRATION() + 1 seconds }); assertTrue(merkleLockupLL.hasExpired(), "campaign not expired"); } } diff --git a/test/integration/merkle-lockup/lt/claim/claim.t.sol b/test/integration/merkle-lockup/lt/claim/claim.t.sol index 41d6c613..6e50263c 100644 --- a/test/integration/merkle-lockup/lt/claim/claim.t.sol +++ b/test/integration/merkle-lockup/lt/claim/claim.t.sol @@ -24,7 +24,7 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { uint40 expiration = defaults.EXPIRATION(); uint256 warpTime = expiration + 1 seconds; bytes32[] memory merkleProof; - vm.warp({ timestamp: warpTime }); + vm.warp({ newTimestamp: warpTime }); vm.expectRevert( abi.encodeWithSelector(Errors.SablierV2MerkleLockup_CampaignExpired.selector, warpTime, expiration) ); diff --git a/test/integration/merkle-lockup/lt/clawback/clawback.t.sol b/test/integration/merkle-lockup/lt/clawback/clawback.t.sol index c8247109..e0b8cb0a 100644 --- a/test/integration/merkle-lockup/lt/clawback/clawback.t.sol +++ b/test/integration/merkle-lockup/lt/clawback/clawback.t.sol @@ -13,13 +13,13 @@ contract Clawback_Integration_Test is MerkleLockup_Integration_Test { } function test_RevertWhen_CallerNotAdmin() external { - changePrank({ msgSender: users.eve }); + resetPrank({ msgSender: users.eve }); vm.expectRevert(abi.encodeWithSelector(V2CoreErrors.CallerNotAdmin.selector, users.admin, users.eve)); merkleLockupLT.clawback({ to: users.eve, amount: 1 }); } modifier whenCallerAdmin() { - changePrank({ msgSender: users.admin }); + resetPrank({ msgSender: users.admin }); _; } @@ -35,7 +35,7 @@ contract Clawback_Integration_Test is MerkleLockup_Integration_Test { modifier givenCampaignExpired() { // Make a claim to have a different contract balance. claimLT(); - vm.warp({ timestamp: defaults.EXPIRATION() + 1 seconds }); + vm.warp({ newTimestamp: defaults.EXPIRATION() + 1 seconds }); _; } diff --git a/test/integration/merkle-lockup/lt/has-expired/hasExpired.t.sol b/test/integration/merkle-lockup/lt/has-expired/hasExpired.t.sol index a1b4998c..eb5d747d 100644 --- a/test/integration/merkle-lockup/lt/has-expired/hasExpired.t.sol +++ b/test/integration/merkle-lockup/lt/has-expired/hasExpired.t.sol @@ -19,17 +19,17 @@ contract HasExpired_Integration_Test is MerkleLockup_Integration_Test { _; } - function test_HasExpired_ExpirationLessThanCurrentTime() external whenExpirationNotZero { + function test_HasExpired_ExpirationLessThanCurrentTime() external view whenExpirationNotZero { assertFalse(merkleLockupLT.hasExpired(), "campaign expired"); } function test_HasExpired_ExpirationEqualToCurrentTime() external whenExpirationNotZero { - vm.warp({ timestamp: defaults.EXPIRATION() }); + vm.warp({ newTimestamp: defaults.EXPIRATION() }); assertTrue(merkleLockupLT.hasExpired(), "campaign not expired"); } function test_HasExpired_ExpirationGreaterThanCurrentTime() external whenExpirationNotZero { - vm.warp({ timestamp: defaults.EXPIRATION() + 1 seconds }); + vm.warp({ newTimestamp: defaults.EXPIRATION() + 1 seconds }); assertTrue(merkleLockupLT.hasExpired(), "campaign not expired"); } } diff --git a/test/utils/Assertions.sol b/test/utils/Assertions.sol index 066650b2..c78b0410 100644 --- a/test/utils/Assertions.sol +++ b/test/utils/Assertions.sol @@ -2,12 +2,11 @@ pragma solidity >=0.8.22; import { PRBMathAssertions } from "@prb/math/test/utils/Assertions.sol"; -import { PRBTest } from "@prb/test/src/PRBTest.sol"; import { MerkleLockupLT } from "src/types/DataTypes.sol"; -abstract contract Assertions is PRBTest, PRBMathAssertions { - event LogNamedArray(string key, MerkleLockupLT.TrancheWithPercentage[] tranchesWithPercentages); +abstract contract Assertions is PRBMathAssertions { + event log_named_array(string key, MerkleLockupLT.TrancheWithPercentage[] tranchesWithPercentages); /// @dev Compares two {MerkleLockupLT.TrancheWithPercentage[]} arrays. function assertEq( @@ -17,9 +16,9 @@ abstract contract Assertions is PRBTest, PRBMathAssertions { internal { if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { - emit Log("Error: a == b not satisfied [MerkleLockupLT.TrancheWithPercentage[]]"); - emit LogNamedArray(" Left", a); - emit LogNamedArray(" Right", b); + emit log("Error: a == b not satisfied [MerkleLockupLT.TrancheWithPercentage[]]"); + emit log_named_array(" Left", a); + emit log_named_array(" Right", b); fail(); } } diff --git a/test/utils/BaseScript.t.sol b/test/utils/BaseScript.t.sol index 0f033749..f445f21d 100644 --- a/test/utils/BaseScript.t.sol +++ b/test/utils/BaseScript.t.sol @@ -2,11 +2,11 @@ pragma solidity >=0.8.22 <0.9.0; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; -import { PRBTest } from "@prb/test/src/PRBTest.sol"; +import { StdAssertions } from "forge-std/src/StdAssertions.sol"; import { BaseScript } from "script/Base.s.sol"; -contract BaseScript_Test is PRBTest { +contract BaseScript_Test is StdAssertions { using Strings for uint256; BaseScript internal baseScript = new BaseScript(); diff --git a/test/utils/MerkleBuilder.t.sol b/test/utils/MerkleBuilder.t.sol index 9a92b4ee..ff670085 100644 --- a/test/utils/MerkleBuilder.t.sol +++ b/test/utils/MerkleBuilder.t.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; -import { PRBTest } from "@prb/test/src/PRBTest.sol"; +import { StdAssertions } from "forge-std/src/StdAssertions.sol"; import { StdUtils } from "forge-std/src/StdUtils.sol"; import { MerkleBuilder } from "./MerkleBuilder.sol"; -contract MerkleBuilder_Test is PRBTest, StdUtils { - function testFuzz_ComputeLeaf(uint256 index, address recipient, uint128 amount) external { +contract MerkleBuilder_Test is StdAssertions, StdUtils { + function testFuzz_ComputeLeaf(uint256 index, address recipient, uint128 amount) external pure { uint256 actualLeaf = MerkleBuilder.computeLeaf(index, recipient, amount); uint256 expectedLeaf = uint256(keccak256(bytes.concat(keccak256(abi.encode(index, recipient, amount))))); assertEq(actualLeaf, expectedLeaf, "computeLeaf"); @@ -20,7 +20,7 @@ contract MerkleBuilder_Test is PRBTest, StdUtils { uint128 amounts; } - function testFuzz_ComputeLeaves(LeavesParams[] memory params) external { + function testFuzz_ComputeLeaves(LeavesParams[] memory params) external pure { uint256 count = params.length; uint256[] memory indexes = new uint256[](count); diff --git a/test/utils/Precompiles.t.sol b/test/utils/Precompiles.t.sol index 54f57356..c29a8f6a 100644 --- a/test/utils/Precompiles.t.sol +++ b/test/utils/Precompiles.t.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.22 <0.9.0; -import { LibString } from "solady/src/utils/LibString.sol"; - import { Precompiles } from "../../precompiles/Precompiles.sol"; import { ISablierV2Batch } from "../../src/interfaces/ISablierV2Batch.sol"; import { ISablierV2MerkleLockupFactory } from "../../src/interfaces/ISablierV2MerkleLockupFactory.sol"; @@ -10,9 +8,6 @@ import { ISablierV2MerkleLockupFactory } from "../../src/interfaces/ISablierV2Me import { Base_Test } from "../Base.t.sol"; contract Precompiles_Test is Base_Test { - using LibString for address; - using LibString for string; - Precompiles internal precompiles = new Precompiles(); modifier onlyTestOptimizedProfile() { From 8683889a3db3fb2b607861ba0f20b53809710e1f Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Sun, 31 Mar 2024 10:12:55 +0100 Subject: [PATCH 32/61] feat: include stream duration in CreateMerkleLockupLT event (#309) * feat: include stream duration in CreateMerkleLockupLT event feat: stop indexing ConstructorParams in CreateMerkleLockup events * chore: update precompiles * chore: capitalize comment --- bun.lockb | Bin 308798 -> 308798 bytes precompiles/Precompiles.sol | 2 +- src/SablierV2MerkleLockupFactory.sol | 18 ++++++++++++++---- .../ISablierV2MerkleLockupFactory.sol | 5 +++-- test/fork/merkle-lockup/MerkleLockupLT.t.sol | 1 + .../createMerkleLockupLT.t.sol | 1 + test/utils/Events.sol | 5 +++-- 7 files changed, 23 insertions(+), 9 deletions(-) diff --git a/bun.lockb b/bun.lockb index 0576556979089cf339ff7187da2a5bb911bee43b..090f592c68554684e80e1d64f16cb08fb4d4df6d 100755 GIT binary patch delta 627 zcmV-(0*w8>>=M505|Az+XCQ8vj2ub$ZsA&5-HnKx$vn)!zb2Q_>cun>;(Ca~u}-=& zlf*J8ldv%evp6#l8 z)r;RIwSz}re@Ajqy;*B)ty4`HA&bX)VVEJ+fnyZxOgA#CKxm6%mN9E{W7po0Maj3V zO9AKvKn=uW3@f86`2v&o6nn+<@sz2X$&%h&`W=M505|Az+8QjV(UWaSS(kig!yK|h~QU=4p=Dv-5-eY%+DP_rYu}-=& zlSoo9vp6#l8v!C#2!1o9Dhk zBXgty6#w@{tblk6TesCv0Z0)*xLubf&%4DU?5<)jpMK*13EcNt@a=fcKpz})EO}^1 z9iE75?tiNiCQsXSqzQrJid1^|M(7fD>iv`h&w|o!w_RcZaBe^{6y)H6hodlDItFTn z22G#V5?D<(`FHHe8N)K?7u9F!`aLxQx1XYUF{3`^t>*06St`U3U)aI+l^@yI08~M@ z4bcH;D?rY+q0Fj1_^>tA0!x4AaeA4QtBIyGHG;~K294b0XVGD`n|cv$s-a*i20CBw zuSYyKl+CtVH|iRE!VI&Y=wXMo`~kPM`~u}c0XCPLJ_8v6H@CGu0~1pLHkaSG0~@#G zkpl%o0Wr7g)B_nG0Wg>0o&*%P81w^W2$%7l1R=Mc`~yf00W-Jf2?Wbw0XMg^YXoN@ L0XDZAjRYep Date: Mon, 8 Apr 2024 15:54:20 +0300 Subject: [PATCH 33/61] build: update bun.lockb --- bun.lockb | Bin 308798 -> 308798 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/bun.lockb b/bun.lockb index 090f592c68554684e80e1d64f16cb08fb4d4df6d..6df02ee08442dba9ed39615a824204540f0427ce 100755 GIT binary patch delta 68 zcmV-K0K5Ob>=M505|Az+e{hUHNL`fXcid`%2~4}vGn9!1e~7_d;pEV&rRT47flh@^ a0fkNiwN3+z(g!y-WimNtWw+qc1772NS|CsW delta 68 zcmV-K0K5Ob>=M505|Az+XCQ8vj2ub$ZsA&5-HnKx$vn)!zb2Q_>cun>;(Ca~flh@^ a0fkNiwN3+z(g$TQI51{1Ft^~+1772X=pZWq From cb2ed635769dbe49d57fe8e297d8b77ac86450c5 Mon Sep 17 00:00:00 2001 From: Paul Razvan Berg Date: Tue, 9 Apr 2024 16:11:39 +0100 Subject: [PATCH 34/61] ci: pass CODECOV_TOKEN --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb451da2..e8e74c3b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,6 +48,8 @@ jobs: coverage: needs: ["lint", "build"] + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} uses: "sablier-labs/reusable-workflows/.github/workflows/forge-coverage.yml@main" with: match-path: "test/integration/**/*.sol" From bac4db54e3d8434be583219dd9371f75467721f2 Mon Sep 17 00:00:00 2001 From: Paul Razvan Berg Date: Thu, 11 Apr 2024 13:50:21 +0100 Subject: [PATCH 35/61] ci: remove deployment workflow --- .github/workflows/create-merkle-lockup-ll.yml | 42 ------------------- 1 file changed, 42 deletions(-) delete mode 100644 .github/workflows/create-merkle-lockup-ll.yml diff --git a/.github/workflows/create-merkle-lockup-ll.yml b/.github/workflows/create-merkle-lockup-ll.yml deleted file mode 100644 index 500bbdb7..00000000 --- a/.github/workflows/create-merkle-lockup-ll.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: "Create Merkle Lockup LockupLinear" - -env: - API_KEY_INFURA: ${{ secrets.API_KEY_INFURA }} - FOUNDRY_PROFILE: "optimized" - MNEMONIC: ${{ secrets.EVM_MNEMONIC }} - RPC_URL_MAINNET: ${{ secrets.RPC_URL_MAINNET }} - -on: - workflow_dispatch: - inputs: - params: - description: "Parameters needed for the script, as comma-separated tupples." - required: true - chain: - default: "sepolia" - description: "Chain name as defined in the Foundry config." - required: false - -jobs: - create-merkle-lockup-ll: - runs-on: "ubuntu-latest" - steps: - - name: "Check out the repo" - uses: "actions/checkout@v4" - - - name: "Install Foundry" - uses: "foundry-rs/foundry-toolchain@v1" - - - name: "Create a Merkle Lockup contract that uses LL" - run: >- - forge script script/CreateMerkleLockupLL.s.sol - --broadcast - --rpc-url "${{ inputs.chain }}" - --sig "run(address,(address,address,address,bytes32,uint40,(uint40,uint40),bool,bool,string,uint256,uint256))" - -vvvv - "${{ inputs.params }}" - - - name: "Add workflow summary" - run: | - echo "## Result" >> $GITHUB_STEP_SUMMARY - echo "✅ Done" >> $GITHUB_STEP_SUMMARY From 606b31464682139553c4d3def24d4e351521cfa0 Mon Sep 17 00:00:00 2001 From: Paul Razvan Berg Date: Thu, 11 Apr 2024 17:53:39 +0300 Subject: [PATCH 36/61] Feedback from PRB on V2.2 (#324) * refactor: alphabetical ordering build: bump OpenZeppelin to v5.0.2 chore: improve wording in comments chore: misc improvements and renames docs: capitalize ID docs: improve wording in NatSpec * test: restructure create function tests * refactor: refactor "currentTime" to "blockTimestamp" chore: alphabetical ordering chore: misc improvements test: add "assertEq" for "TrancheWithPercentage[]" test: bound "params.expiration" test: improve BTs for "hasExpired" test: refactor "ASSET" to "FORK_ASSET" * docs: improve wording * refactor: singular "recipientCount" docs: improve writing in NatSpec comments * refactor: use UNIT * refactor: differentiate max segment and tranche count * build: bump PRBMath * perf: use uint64 for calculations closes #317 * build: bump sphinx * perf: improve MerkleLT docs: improve writing in NatSpec and comments perf: use "uint128" instead of "UD60x18" in "_calculateTranches" refactor: assertion in "_calculateTranches" refactor: rename custom error * refactor: rename custom error chore: exclude "node_modules" in VSCode search test: improve structures in tests test: improve wording in BTs test: use "boundUint128" * refactor: update precompiles * build: bump v2-core * build: use full commit hashes * doc: update comments * refactor: use uint64 instead of UD2x18 --------- Co-authored-by: smol-ninja --- .vscode/settings.json | 3 + bun.lockb | Bin 308798 -> 308178 bytes foundry.toml | 9 +- package.json | 14 +-- precompiles/Precompiles.sol | 2 +- script/Base.s.sol | 13 +-- script/CreateMerkleLockupLL.s.sol | 8 +- script/CreateMerkleLockupLT.s.sol | 8 +- script/DeployBatch.t.sol | 4 +- script/DeployDeterministicBatch.s.sol | 4 +- script/DeployDeterministicPeriphery.s.sol | 4 +- script/DeployMerkleLockupFactory.s.sol | 4 +- script/DeployPeriphery.s.sol | 4 +- script/DeployProtocol.s.sol | 22 +++-- src/SablierV2Batch.sol | 74 +++++++------- src/SablierV2MerkleLockupFactory.sol | 30 +++--- src/SablierV2MerkleLockupLL.sol | 8 +- src/SablierV2MerkleLockupLT.sol | 67 +++++++------ src/abstracts/SablierV2MerkleLockup.sol | 19 ++-- src/interfaces/ISablierV2Batch.sol | 20 ++-- src/interfaces/ISablierV2MerkleLockup.sol | 20 ++-- .../ISablierV2MerkleLockupFactory.sol | 32 +++---- src/interfaces/ISablierV2MerkleLockupLL.sol | 9 +- src/interfaces/ISablierV2MerkleLockupLT.sol | 9 +- src/libraries/Errors.sol | 6 +- src/types/DataTypes.sol | 22 ++--- test/Base.t.sol | 13 +-- test/fork/Fork.t.sol | 24 ++--- test/fork/batch/createWithTimestampsLD.t.sol | 18 ++-- test/fork/batch/createWithTimestampsLL.t.sol | 21 ++-- test/fork/batch/createWithTimestampsLT.t.sol | 18 ++-- test/fork/merkle-lockup/MerkleLockupLL.t.sol | 55 ++++++----- test/fork/merkle-lockup/MerkleLockupLT.t.sol | 51 +++++----- test/integration/Integration.t.sol | 4 +- .../createWithDurationsLD.t.sol} | 4 +- .../createWithDurationsLD.tree} | 2 +- .../createWithDurationsLL.t.sol} | 4 +- .../createWithDurationsLL.tree} | 2 +- .../createWithDurationsLT.t.sol} | 4 +- .../createWithDurationsLT.tree} | 2 +- .../createWithTimestampsLD.t.sol} | 4 +- .../createWithTimestampsLD.tree} | 2 +- .../createWithTimestamps.t.sol | 4 +- .../createWithTimestamps.tree | 2 +- .../createWithTimestampsLT.t.sol} | 4 +- .../createWithTimestampsLT.tree} | 2 +- .../merkle-lockup/MerkleLockup.t.sol | 8 +- .../createMerkleLockupLL.t.sol | 24 ++--- .../createMerkleLockupLL.tree | 12 +-- .../createMerkleLockupLT.t.sol | 90 ++++++++---------- .../createMerkleLockupLT.tree | 24 ++--- .../merkle-lockup/ll/claim/claim.t.sol | 6 +- .../merkle-lockup/ll/claim/claim.tree | 2 +- .../ll/constructor/constructor.t.sol | 50 +++++----- .../ll/has-expired/hasExpired.t.sol | 8 +- .../ll/has-expired/hasExpired.tree | 6 +- .../merkle-lockup/lt/claim/claim.t.sol | 62 ++++++------ .../merkle-lockup/lt/claim/claim.tree | 8 +- .../lt/constructor/constructor.t.sol | 54 +++++------ .../lt/has-expired/hasExpired.t.sol | 8 +- .../lt/has-expired/hasExpired.tree | 10 +- test/utils/Assertions.sol | 17 +++- test/utils/Defaults.sol | 8 +- test/utils/Events.sol | 4 +- 64 files changed, 544 insertions(+), 512 deletions(-) rename test/integration/batch/{lockup-dynamic/create-with-durations/createWithDurations.t.sol => create-with-durations-ld/createWithDurationsLD.t.sol} (92%) rename test/integration/batch/{lockup-linear/create-with-durations/createWithDurations.tree => create-with-durations-ld/createWithDurationsLD.tree} (89%) rename test/integration/batch/{lockup-linear/create-with-durations/createWithDurations.t.sol => create-with-durations-ll/createWithDurationsLL.t.sol} (92%) rename test/integration/batch/{lockup-dynamic/create-with-durations/createWithDurations.tree => create-with-durations-ll/createWithDurationsLL.tree} (89%) rename test/integration/batch/{lockup-tranched/create-with-durations/createWithDurations.t.sol => create-with-durations-lt/createWithDurationsLT.t.sol} (92%) rename test/integration/batch/{lockup-tranched/create-with-durations/createWithDurations.tree => create-with-durations-lt/createWithDurationsLT.tree} (89%) rename test/integration/batch/{lockup-dynamic/create-with-timestamps/createWithTimestamps.t.sol => create-with-timestamps-ld/createWithTimestampsLD.t.sol} (92%) rename test/integration/batch/{lockup-linear/create-with-timestamps/createWithTimestamps.tree => create-with-timestamps-ld/createWithTimestampsLD.tree} (88%) rename test/integration/batch/{lockup-linear/create-with-timestamps => create-with-timestamps-ll}/createWithTimestamps.t.sol (92%) rename test/integration/batch/{lockup-tranched/create-with-timestamps => create-with-timestamps-ll}/createWithTimestamps.tree (88%) rename test/integration/batch/{lockup-tranched/create-with-timestamps/createWithTimestamps.t.sol => create-with-timestamps-lt/createWithTimestampsLT.t.sol} (92%) rename test/integration/batch/{lockup-dynamic/create-with-timestamps/createWithTimestamps.tree => create-with-timestamps-lt/createWithTimestampsLT.tree} (88%) diff --git a/.vscode/settings.json b/.vscode/settings.json index fa100fc5..3844d3ee 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,5 +9,8 @@ ".gas-snapshot": "julia" }, "editor.formatOnSave": true, + "search.exclude": { + "**/node_modules": true + }, "solidity.formatter": "forge" } diff --git a/bun.lockb b/bun.lockb index 6df02ee08442dba9ed39615a824204540f0427ce..9f255c844b10cbc14e26acbeb62df45fbaf67e8d 100755 GIT binary patch delta 58378 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 VQBhE~d;, Version ". /// - The version is obtained from `package.json` using the `ffi` cheatcode: /// https://book.getfoundry.sh/cheatcodes/ffi - /// - Requires `jq` CLI tool installed: https://jqlang.github.io/jq/ + /// - Requires the `jq` CLI installed: https://jqlang.github.io/jq/ function constructCreate2Salt() public returns (bytes32) { string memory chainId = block.chainid.toString(); string[] memory inputs = new string[](4); diff --git a/script/CreateMerkleLockupLL.s.sol b/script/CreateMerkleLockupLL.s.sol index 2a8a4bf8..c41f2bde 100644 --- a/script/CreateMerkleLockupLL.s.sol +++ b/script/CreateMerkleLockupLL.s.sol @@ -16,10 +16,10 @@ contract CreateMerkleLockupLL is BaseScript { ISablierV2LockupLinear lockupLinear; LockupLinear.Durations streamDurations; uint256 campaignTotalAmount; - uint256 recipientsCount; + uint256 recipientCount; } - /// @dev Deploy using Forge CLI. + /// @dev Deploy via Forge. function runBroadcast( ISablierV2MerkleLockupFactory merkleLockupFactory, Params calldata params @@ -32,7 +32,7 @@ contract CreateMerkleLockupLL is BaseScript { merkleLockupLL = _run(merkleLockupFactory, params); } - /// @dev Deploy using Sphinx CLI. + /// @dev Deploy via Sphinx. function runSphinx( ISablierV2MerkleLockupFactory merkleLockupFactory, Params calldata params @@ -57,7 +57,7 @@ contract CreateMerkleLockupLL is BaseScript { params.lockupLinear, params.streamDurations, params.campaignTotalAmount, - params.recipientsCount + params.recipientCount ); } } diff --git a/script/CreateMerkleLockupLT.s.sol b/script/CreateMerkleLockupLT.s.sol index 4e028621..b1a82e75 100644 --- a/script/CreateMerkleLockupLT.s.sol +++ b/script/CreateMerkleLockupLT.s.sol @@ -15,10 +15,10 @@ contract CreateMerkleLockupLT is BaseScript { ISablierV2LockupTranched lockupTranched; MerkleLockupLT.TrancheWithPercentage[] tranchesWithPercentages; uint256 campaignTotalAmount; - uint256 recipientsCount; + uint256 recipientCount; } - /// @dev Deploy using Forge CLI. + /// @dev Deploy via Forge. function runBroadcast( ISablierV2MerkleLockupFactory merkleLockupFactory, Params calldata params @@ -31,7 +31,7 @@ contract CreateMerkleLockupLT is BaseScript { merkleLockupLT = _run(merkleLockupFactory, params); } - /// @dev Deploy using Sphinx CLI. + /// @dev Deploy via Sphinx. function runSphinx( ISablierV2MerkleLockupFactory merkleLockupFactory, Params calldata params @@ -56,7 +56,7 @@ contract CreateMerkleLockupLT is BaseScript { params.lockupTranched, params.tranchesWithPercentages, params.campaignTotalAmount, - params.recipientsCount + params.recipientCount ); } } diff --git a/script/DeployBatch.t.sol b/script/DeployBatch.t.sol index 0a32ebc5..80e6e56d 100644 --- a/script/DeployBatch.t.sol +++ b/script/DeployBatch.t.sol @@ -6,12 +6,12 @@ import { BaseScript } from "./Base.s.sol"; import { SablierV2Batch } from "../src/SablierV2Batch.sol"; contract DeployBatch is BaseScript { - /// @dev Deploy using Forge CLI. + /// @dev Deploy via Forge. function runBroadcast() public virtual broadcast returns (SablierV2Batch batch) { batch = _run(); } - /// @dev Deploy using Sphinx CLI. + /// @dev Deploy via Sphinx. function runSphinx() public virtual sphinx returns (SablierV2Batch batch) { batch = _run(); } diff --git a/script/DeployDeterministicBatch.s.sol b/script/DeployDeterministicBatch.s.sol index 632ca4de..d1728f53 100644 --- a/script/DeployDeterministicBatch.s.sol +++ b/script/DeployDeterministicBatch.s.sol @@ -8,12 +8,12 @@ import { SablierV2Batch } from "../src/SablierV2Batch.sol"; /// @notice Deploys {SablierV2Batch} at a deterministic address across chains. /// @dev Reverts if the contract has already been deployed. contract DeployDeterministicBatch is BaseScript { - /// @dev Deploy using Forge CLI. + /// @dev Deploy via Forge. function runBroadcast() public virtual broadcast returns (SablierV2Batch batch) { batch = _run(); } - /// @dev Deploy using Sphinx CLI. + /// @dev Deploy via Sphinx. function runSphinx() public virtual sphinx returns (SablierV2Batch batch) { batch = _run(); } diff --git a/script/DeployDeterministicPeriphery.s.sol b/script/DeployDeterministicPeriphery.s.sol index 70b17bb1..77968c78 100644 --- a/script/DeployDeterministicPeriphery.s.sol +++ b/script/DeployDeterministicPeriphery.s.sol @@ -13,7 +13,7 @@ import { SablierV2MerkleLockupFactory } from "../src/SablierV2MerkleLockupFactor /// /// @dev Reverts if any contract has already been deployed. contract DeployDeterministicPeriphery is BaseScript { - /// @dev Deploy using Forge CLI. + /// @dev Deploy via Forge. function runBroadcast() public virtual @@ -23,7 +23,7 @@ contract DeployDeterministicPeriphery is BaseScript { (batch, merkleLockupFactory) = _run(); } - /// @dev Deploy using Sphinx CLI. + /// @dev Deploy via Sphinx. function runSphinx() public virtual diff --git a/script/DeployMerkleLockupFactory.s.sol b/script/DeployMerkleLockupFactory.s.sol index d0a90429..3879f2e8 100644 --- a/script/DeployMerkleLockupFactory.s.sol +++ b/script/DeployMerkleLockupFactory.s.sol @@ -6,12 +6,12 @@ import { BaseScript } from "./Base.s.sol"; import { SablierV2MerkleLockupFactory } from "../src/SablierV2MerkleLockupFactory.sol"; contract DeployMerkleLockupFactory is BaseScript { - /// @dev Deploy using Forge CLI. + /// @dev Deploy via Forge. function runBroadcast() public virtual broadcast returns (SablierV2MerkleLockupFactory merkleLockupFactory) { merkleLockupFactory = _run(); } - /// @dev Deploy using Sphinx CLI. + /// @dev Deploy via Sphinx. function runSphinx() public virtual sphinx returns (SablierV2MerkleLockupFactory merkleLockupFactory) { merkleLockupFactory = _run(); } diff --git a/script/DeployPeriphery.s.sol b/script/DeployPeriphery.s.sol index a8576c1d..e8f0fee3 100644 --- a/script/DeployPeriphery.s.sol +++ b/script/DeployPeriphery.s.sol @@ -11,7 +11,7 @@ import { SablierV2Batch } from "../src/SablierV2Batch.sol"; /// 1. {SablierV2Batch} /// 2. {SablierV2MerkleLockupFactory} contract DeployPeriphery is BaseScript { - /// @dev Deploy using Forge CLI. + /// @dev Deploy via Forge. function runBroadcast() public virtual @@ -21,7 +21,7 @@ contract DeployPeriphery is BaseScript { (batch, merkleLockupFactory) = _run(); } - /// @dev Deploy using Sphinx CLI. + /// @dev Deploy via Sphinx. function runSphinx() public virtual diff --git a/script/DeployProtocol.s.sol b/script/DeployProtocol.s.sol index 27df1d40..3750c4be 100644 --- a/script/DeployProtocol.s.sol +++ b/script/DeployProtocol.s.sol @@ -12,10 +12,11 @@ import { SablierV2Batch } from "../src/SablierV2Batch.sol"; /// @notice Deploys the Sablier V2 Protocol. contract DeployProtocol is BaseScript { - /// @dev Deploy using Forge CLI. + /// @dev Deploy via Forge. function runBroadcast( address initialAdmin, - uint256 maxCount + uint256 maxSegmentCount, + uint256 maxTrancheCount ) public virtual @@ -30,13 +31,14 @@ contract DeployProtocol is BaseScript { ) { (lockupDynamic, lockupLinear, lockupTranched, nftDescriptor, batch, merkleLockupFactory) = - _run(initialAdmin, maxCount); + _run(initialAdmin, maxSegmentCount, maxTrancheCount); } - /// @dev Deploy using Sphinx CLI. + /// @dev Deploy via Sphinx. function runSphinx( address initialAdmin, - uint256 maxCount + uint256 maxSegmentCount, + uint256 maxTrancheCount ) public virtual @@ -51,12 +53,13 @@ contract DeployProtocol is BaseScript { ) { (lockupDynamic, lockupLinear, lockupTranched, nftDescriptor, batch, merkleLockupFactory) = - _run(initialAdmin, maxCount); + _run(initialAdmin, maxSegmentCount, maxTrancheCount); } function _run( address initialAdmin, - uint256 maxCount + uint256 maxSegmentCount, + uint256 maxTrancheCount ) internal returns ( @@ -70,10 +73,11 @@ contract DeployProtocol is BaseScript { { // Deploy V2 Core. nftDescriptor = new SablierV2NFTDescriptor(); - lockupDynamic = new SablierV2LockupDynamic(initialAdmin, nftDescriptor, maxCount); + lockupDynamic = new SablierV2LockupDynamic(initialAdmin, nftDescriptor, maxSegmentCount); lockupLinear = new SablierV2LockupLinear(initialAdmin, nftDescriptor); - lockupTranched = new SablierV2LockupTranched(initialAdmin, nftDescriptor, maxCount); + lockupTranched = new SablierV2LockupTranched(initialAdmin, nftDescriptor, maxTrancheCount); + // Deploy V2 Periphery. batch = new SablierV2Batch(); merkleLockupFactory = new SablierV2MerkleLockupFactory(); } diff --git a/src/SablierV2Batch.sol b/src/SablierV2Batch.sol index 0b15fb74..926ff490 100644 --- a/src/SablierV2Batch.sol +++ b/src/SablierV2Batch.sol @@ -18,14 +18,14 @@ contract SablierV2Batch is ISablierV2Batch { using SafeERC20 for IERC20; /*////////////////////////////////////////////////////////////////////////// - SABLIER-V2-LOCKUP-LINEAR + SABLIER-V2-LOCKUP-DYNAMIC //////////////////////////////////////////////////////////////////////////*/ /// @inheritdoc ISablierV2Batch - function createWithDurationsLL( - ISablierV2LockupLinear lockupLinear, + function createWithDurationsLD( + ISablierV2LockupDynamic lockupDynamic, IERC20 asset, - Batch.CreateWithDurationsLL[] calldata batch + Batch.CreateWithDurationsLD[] calldata batch ) external override @@ -47,22 +47,22 @@ contract SablierV2Batch is ISablierV2Batch { } } - // Transfers the assets to the batch and approves the Sablier contract to spend them. - _handleTransfer(address(lockupLinear), asset, transferAmount); + // Perform the ERC-20 transfer and approve {SablierV2LockupDynamic} to spend the amount of assets. + _handleTransfer(address(lockupDynamic), asset, transferAmount); // Create a stream for each element in the parameter array. streamIds = new uint256[](batchSize); for (i = 0; i < batchSize; ++i) { // Create the stream. - streamIds[i] = lockupLinear.createWithDurations( - LockupLinear.CreateWithDurations({ + streamIds[i] = lockupDynamic.createWithDurations( + LockupDynamic.CreateWithDurations({ sender: batch[i].sender, recipient: batch[i].recipient, totalAmount: batch[i].totalAmount, asset: asset, cancelable: batch[i].cancelable, transferable: batch[i].transferable, - durations: batch[i].durations, + segments: batch[i].segments, broker: batch[i].broker }) ); @@ -70,16 +70,16 @@ contract SablierV2Batch is ISablierV2Batch { } /// @inheritdoc ISablierV2Batch - function createWithTimestampsLL( - ISablierV2LockupLinear lockupLinear, + function createWithTimestampsLD( + ISablierV2LockupDynamic lockupDynamic, IERC20 asset, - Batch.CreateWithTimestampsLL[] calldata batch + Batch.CreateWithTimestampsLD[] calldata batch ) external override returns (uint256[] memory streamIds) { - // Check that the batch is not empty. + // Check that the batch size is not zero. uint256 batchSize = batch.length; if (batchSize == 0) { revert Errors.SablierV2Batch_BatchSizeZero(); @@ -95,22 +95,23 @@ contract SablierV2Batch is ISablierV2Batch { } } - // Transfers the assets to the batch and approve the Sablier contract to spend them. - _handleTransfer(address(lockupLinear), asset, transferAmount); + // Perform the ERC-20 transfer and approve {SablierV2LockupDynamic} to spend the amount of assets. + _handleTransfer(address(lockupDynamic), asset, transferAmount); // Create a stream for each element in the parameter array. streamIds = new uint256[](batchSize); for (i = 0; i < batchSize; ++i) { // Create the stream. - streamIds[i] = lockupLinear.createWithTimestamps( - LockupLinear.CreateWithTimestamps({ + streamIds[i] = lockupDynamic.createWithTimestamps( + LockupDynamic.CreateWithTimestamps({ sender: batch[i].sender, recipient: batch[i].recipient, totalAmount: batch[i].totalAmount, asset: asset, cancelable: batch[i].cancelable, transferable: batch[i].transferable, - range: batch[i].range, + startTime: batch[i].startTime, + segments: batch[i].segments, broker: batch[i].broker }) ); @@ -118,14 +119,14 @@ contract SablierV2Batch is ISablierV2Batch { } /*////////////////////////////////////////////////////////////////////////// - SABLIER-V2-LOCKUP-DYNAMIC + SABLIER-V2-LOCKUP-LINEAR //////////////////////////////////////////////////////////////////////////*/ /// @inheritdoc ISablierV2Batch - function createWithDurationsLD( - ISablierV2LockupDynamic lockupDynamic, + function createWithDurationsLL( + ISablierV2LockupLinear lockupLinear, IERC20 asset, - Batch.CreateWithDurationsLD[] calldata batch + Batch.CreateWithDurationsLL[] calldata batch ) external override @@ -147,22 +148,22 @@ contract SablierV2Batch is ISablierV2Batch { } } - // Perform the ERC-20 transfer and approve {SablierV2LockupDynamic} to spend the amount of assets. - _handleTransfer(address(lockupDynamic), asset, transferAmount); + // Perform the ERC-20 transfer and approve {SablierV2LockupLinear} to spend the amount of assets. + _handleTransfer(address(lockupLinear), asset, transferAmount); // Create a stream for each element in the parameter array. streamIds = new uint256[](batchSize); for (i = 0; i < batchSize; ++i) { // Create the stream. - streamIds[i] = lockupDynamic.createWithDurations( - LockupDynamic.CreateWithDurations({ + streamIds[i] = lockupLinear.createWithDurations( + LockupLinear.CreateWithDurations({ sender: batch[i].sender, recipient: batch[i].recipient, totalAmount: batch[i].totalAmount, asset: asset, cancelable: batch[i].cancelable, transferable: batch[i].transferable, - segments: batch[i].segments, + durations: batch[i].durations, broker: batch[i].broker }) ); @@ -170,16 +171,16 @@ contract SablierV2Batch is ISablierV2Batch { } /// @inheritdoc ISablierV2Batch - function createWithTimestampsLD( - ISablierV2LockupDynamic lockupDynamic, + function createWithTimestampsLL( + ISablierV2LockupLinear lockupLinear, IERC20 asset, - Batch.CreateWithTimestampsLD[] calldata batch + Batch.CreateWithTimestampsLL[] calldata batch ) external override returns (uint256[] memory streamIds) { - // Check that the batch size is not zero. + // Check that the batch is not empty. uint256 batchSize = batch.length; if (batchSize == 0) { revert Errors.SablierV2Batch_BatchSizeZero(); @@ -195,23 +196,22 @@ contract SablierV2Batch is ISablierV2Batch { } } - // Perform the ERC-20 transfer and approve {SablierV2LockupDynamic} to spend the amount of assets. - _handleTransfer(address(lockupDynamic), asset, transferAmount); + // Perform the ERC-20 transfer and approve {SablierV2LockupLinear} to spend the amount of assets. + _handleTransfer(address(lockupLinear), asset, transferAmount); // Create a stream for each element in the parameter array. streamIds = new uint256[](batchSize); for (i = 0; i < batchSize; ++i) { // Create the stream. - streamIds[i] = lockupDynamic.createWithTimestamps( - LockupDynamic.CreateWithTimestamps({ + streamIds[i] = lockupLinear.createWithTimestamps( + LockupLinear.CreateWithTimestamps({ sender: batch[i].sender, recipient: batch[i].recipient, totalAmount: batch[i].totalAmount, asset: asset, cancelable: batch[i].cancelable, transferable: batch[i].transferable, - startTime: batch[i].startTime, - segments: batch[i].segments, + range: batch[i].range, broker: batch[i].broker }) ); diff --git a/src/SablierV2MerkleLockupFactory.sol b/src/SablierV2MerkleLockupFactory.sol index d5028612..43110f0a 100644 --- a/src/SablierV2MerkleLockupFactory.sol +++ b/src/SablierV2MerkleLockupFactory.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-3.0-or-later pragma solidity >=0.8.22; -import { UD60x18, ud } from "@prb/math/src/UD60x18.sol"; +import { uUNIT } from "@prb/math/src/UD2x18.sol"; import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablierV2LockupLinear.sol"; import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; import { LockupLinear } from "@sablier/v2-core/src/types/DataTypes.sol"; @@ -27,7 +27,7 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { ISablierV2LockupLinear lockupLinear, LockupLinear.Durations memory streamDurations, uint256 aggregateAmount, - uint256 recipientsCount + uint256 recipientCount ) external returns (ISablierV2MerkleLockupLL merkleLockupLL) @@ -48,12 +48,12 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { ) ); - // Deploy the Merkle Lockup contract with CREATE2. + // Deploy the MerkleLockup contract with CREATE2. merkleLockupLL = new SablierV2MerkleLockupLL{ salt: salt }(baseParams, lockupLinear, streamDurations); - // Log the creation of the Merkle Lockup, including some metadata that is not stored on-chain. + // Log the creation of the MerkleLockup contract, including some metadata that is not stored on-chain. emit CreateMerkleLockupLL( - merkleLockupLL, baseParams, lockupLinear, streamDurations, aggregateAmount, recipientsCount + merkleLockupLL, baseParams, lockupLinear, streamDurations, aggregateAmount, recipientCount ); } @@ -63,26 +63,26 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { ISablierV2LockupTranched lockupTranched, MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages, uint256 aggregateAmount, - uint256 recipientsCount + uint256 recipientCount ) external returns (ISablierV2MerkleLockupLT merkleLockupLT) { // Calculate the sum of percentages and durations across all tranches. - UD60x18 totalPercentage; + uint64 totalPercentage; uint256 totalDuration; for (uint256 i = 0; i < tranchesWithPercentages.length; ++i) { - UD60x18 percentage = (tranchesWithPercentages[i].unlockPercentage).intoUD60x18(); - totalPercentage = totalPercentage.add(percentage); + uint64 percentage = tranchesWithPercentages[i].unlockPercentage.unwrap(); + totalPercentage = totalPercentage + percentage; unchecked { // Safe to use `unchecked` because its only used in the event. totalDuration += tranchesWithPercentages[i].duration; } } - // Checks: the sum of percentages equals 100%. - if (!totalPercentage.eq(ud(1e18))) { - revert Errors.SablierV2MerkleLockupFactory_TotalPercentageNotEqualOneHundred(totalPercentage.intoUint256()); + // Check: the sum of percentages equals 100%. + if (totalPercentage != uUNIT) { + revert Errors.SablierV2MerkleLockupFactory_TotalPercentageNotOneHundred(totalPercentage); } // Hash the parameters to generate a salt. @@ -101,10 +101,10 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { ) ); - // Deploy the Merkle Lockup contract with CREATE2. + // Deploy the MerkleLockup contract with CREATE2. merkleLockupLT = new SablierV2MerkleLockupLT{ salt: salt }(baseParams, lockupTranched, tranchesWithPercentages); - // Log the creation of the Merkle Lockup, including some metadata that is not stored on-chain. + // Log the creation of the MerkleLockup contract, including some metadata that is not stored on-chain. emit CreateMerkleLockupLT( merkleLockupLT, baseParams, @@ -112,7 +112,7 @@ contract SablierV2MerkleLockupFactory is ISablierV2MerkleLockupFactory { tranchesWithPercentages, totalDuration, aggregateAmount, - recipientsCount + recipientCount ); } } diff --git a/src/SablierV2MerkleLockupLL.sol b/src/SablierV2MerkleLockupLL.sol index 6214875e..05921dd0 100644 --- a/src/SablierV2MerkleLockupLL.sol +++ b/src/SablierV2MerkleLockupLL.sol @@ -47,7 +47,7 @@ contract SablierV2MerkleLockupLL is LOCKUP_LINEAR = lockupLinear; streamDurations = streamDurations_; - // Max approve the Sablier contract to spend funds from the Merkle Lockup contract. + // Max approve the Sablier contract to spend funds from the MerkleLockup contract. ASSET.forceApprove(address(LOCKUP_LINEAR), type(uint256).max); } @@ -70,13 +70,13 @@ contract SablierV2MerkleLockupLL is // preimage attacks. bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(index, recipient, amount)))); - // Checks: validate the function. + // Check: validate the function. _checkClaim(index, leaf, merkleProof); - // Effects: mark the index as claimed. + // Effect: mark the index as claimed. _claimedBitMap.set(index); - // Interactions: create the stream via {SablierV2LockupLinear}. + // Interaction: create the stream via {SablierV2LockupLinear}. streamId = LOCKUP_LINEAR.createWithDurations( LockupLinear.CreateWithDurations({ sender: admin, diff --git a/src/SablierV2MerkleLockupLT.sol b/src/SablierV2MerkleLockupLT.sol index ffd243e9..08af9250 100644 --- a/src/SablierV2MerkleLockupLT.sol +++ b/src/SablierV2MerkleLockupLT.sol @@ -4,7 +4,7 @@ pragma solidity >=0.8.22; import { BitMaps } from "@openzeppelin/contracts/utils/structs/BitMaps.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import { ud, UD60x18 } from "@prb/math/src/UD60x18.sol"; +import { UD60x18, ud60x18, ZERO } from "@prb/math/src/UD60x18.sol"; import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISablierV2LockupTranched.sol"; import { Broker, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; @@ -46,14 +46,14 @@ contract SablierV2MerkleLockupLT is { LOCKUP_TRANCHED = lockupTranched; - // Since Solidity lacks a syntax for copying arrays directly from memory to storage, - // a manual approach is necessary. See https://github.com/ethereum/solidity/issues/12783. + // Since Solidity lacks a syntax for copying arrays of structs directly from memory to storage, a manual + // approach is necessary. See https://github.com/ethereum/solidity/issues/12783. uint256 count = tranchesWithPercentages.length; for (uint256 i = 0; i < count; ++i) { _tranchesWithPercentages.push(tranchesWithPercentages[i]); } - // Max approve the Sablier contract to spend funds from the Merkle Lockup contract. + // Max approve the Sablier contract to spend funds from the MerkleLockup contract. ASSET.forceApprove(address(LOCKUP_TRANCHED), type(uint256).max); } @@ -90,16 +90,16 @@ contract SablierV2MerkleLockupLT is // preimage attacks. bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(index, recipient, amount)))); - // Checks: validate the function. + // Check: validate the function. _checkClaim(index, leaf, merkleProof); - // Calculate the tranches based on the `amount`. + // Calculate the tranches based on the unlock percentages. LockupTranched.TrancheWithDuration[] memory tranches = _calculateTranches(amount); - // Effects: mark the index as claimed. + // Effect: mark the index as claimed. _claimedBitMap.set(index); - // Interactions: create the stream via {SablierV2LockupTranched}. + // Interaction: create the stream via {SablierV2LockupTranched}. streamId = LOCKUP_TRANCHED.createWithDurations( LockupTranched.CreateWithDurations({ sender: admin, @@ -109,7 +109,7 @@ contract SablierV2MerkleLockupLT is cancelable: CANCELABLE, transferable: TRANSFERABLE, tranches: tranches, - broker: Broker({ account: address(0), fee: ud(0) }) + broker: Broker({ account: address(0), fee: ZERO }) }) ); @@ -117,44 +117,53 @@ contract SablierV2MerkleLockupLT is emit Claim(index, recipient, amount, streamId); } - /// @dev Calculates the stream tranches based on Merkle tree amount and unlock percentage for each tranche. - function _calculateTranches(uint128 amount) + /*////////////////////////////////////////////////////////////////////////// + INTERNAL CONSTANT FUNCTIONS + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev Calculates the tranches based on the claim amount and the unlock percentages for each tranche. + function _calculateTranches(uint128 claimAmount) internal view returns (LockupTranched.TrancheWithDuration[] memory tranches) { - // Load the tranches in memory to save gas. + // Load the tranches in memory (to save gas). MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages = _tranchesWithPercentages; - // Declare the variables need for calculation. - UD60x18 trancheAmountsSum; + // Declare the variables needed for calculation. + uint128 calculatedAmountsSum; + UD60x18 claimAmountUD = ud60x18(claimAmount); uint256 trancheCount = tranchesWithPercentages.length; tranches = new LockupTranched.TrancheWithDuration[](trancheCount); - UD60x18 udAmount = ud(amount); - - // Iterate over each tranche to calculate its amount based on its percentage. + // Iterate over each tranche to calculate its unlock amount. for (uint256 i = 0; i < trancheCount; ++i) { - // Convert the tranche's percentage to `UD60x18` for calculation. + // Convert the tranche's percentage from the `UD2x18` to the `UD60x18` type. UD60x18 percentage = (tranchesWithPercentages[i].unlockPercentage).intoUD60x18(); - // Calculate the tranche's amount by applying its percentage to the `amount`. - UD60x18 trancheAmount = udAmount.mul(percentage); + // Calculate the tranche's amount by multiplying the claim amount by the unlock percentage. + uint128 calculatedAmount = claimAmountUD.mul(percentage).intoUint128(); - // Sum all tranche amounts. - trancheAmountsSum = trancheAmountsSum.add(trancheAmount); - - // Assign calculated amount and duration to the tranche. + // Create the tranche with duration. tranches[i] = LockupTranched.TrancheWithDuration({ - amount: trancheAmount.intoUint128(), + amount: calculatedAmount, duration: tranchesWithPercentages[i].duration }); + + // Add the calculated tranche amount. + calculatedAmountsSum += calculatedAmount; } - // Adjust the last tranche amount to prevent claim failure due to rounding differences during calculations. We - // need to ensure the core protocol invariant: the sum of all tranches' amounts equals the deposit amount. - if (!udAmount.eq(trancheAmountsSum)) { - tranches[trancheCount - 1].amount += udAmount.intoUint128() - trancheAmountsSum.intoUint128(); + // It should never be the case that the sum of the calculated amounts is greater than the claim amount because + // PRBMath always rounds down. + assert(calculatedAmountsSum <= claimAmount); + + // Since there can be rounding errors, the last tranche amount needs to be adjusted to ensure the sum of all + // tranche amounts equals the claim amount. + if (calculatedAmountsSum < claimAmount) { + unchecked { + tranches[trancheCount - 1].amount += claimAmount - calculatedAmountsSum; + } } } } diff --git a/src/abstracts/SablierV2MerkleLockup.sol b/src/abstracts/SablierV2MerkleLockup.sol index 7f0e342c..cd269866 100644 --- a/src/abstracts/SablierV2MerkleLockup.sol +++ b/src/abstracts/SablierV2MerkleLockup.sol @@ -54,7 +54,7 @@ abstract contract SablierV2MerkleLockup is /// @dev Constructs the contract by initializing the immutable state variables. constructor(MerkleLockup.ConstructorParams memory params) { - // Checks: the campaign name is not greater than 32 bytes + // Check: the campaign name is not greater than 32 bytes if (bytes(params.name).length > 32) { revert Errors.SablierV2MerkleLockup_CampaignNameTooLong({ nameLength: bytes(params.name).length, @@ -97,15 +97,15 @@ abstract contract SablierV2MerkleLockup is /// @inheritdoc ISablierV2MerkleLockup function clawback(address to, uint128 amount) external override onlyAdmin { - // Checks: the campaign is not expired. + // Check: the campaign is not expired. if (!hasExpired()) { revert Errors.SablierV2MerkleLockup_CampaignNotExpired({ - currentTime: block.timestamp, + blockTimestamp: block.timestamp, expiration: EXPIRATION }); } - // Effects: transfer the tokens to the provided address. + // Effect: transfer the tokens to the provided address. ASSET.safeTransfer(to, amount); // Log the clawback. @@ -118,17 +118,20 @@ abstract contract SablierV2MerkleLockup is /// @dev Validates the parameters of the `claim` function, which is implemented by child contracts. function _checkClaim(uint256 index, bytes32 leaf, bytes32[] calldata merkleProof) internal view { - // Checks: the campaign has not expired. + // Check: the campaign has not expired. if (hasExpired()) { - revert Errors.SablierV2MerkleLockup_CampaignExpired({ currentTime: block.timestamp, expiration: EXPIRATION }); + revert Errors.SablierV2MerkleLockup_CampaignExpired({ + blockTimestamp: block.timestamp, + expiration: EXPIRATION + }); } - // Checks: the index has not been claimed. + // Check: the index has not been claimed. if (_claimedBitMap.get(index)) { revert Errors.SablierV2MerkleLockup_StreamClaimed(index); } - // Checks: the input claim is included in the Merkle tree. + // Check: the input claim is included in the Merkle tree. if (!MerkleProof.verify(merkleProof, MERKLE_ROOT, leaf)) { revert Errors.SablierV2MerkleLockup_InvalidProof(); } diff --git a/src/interfaces/ISablierV2Batch.sol b/src/interfaces/ISablierV2Batch.sol index 32ec6e95..55d35863 100644 --- a/src/interfaces/ISablierV2Batch.sol +++ b/src/interfaces/ISablierV2Batch.sol @@ -15,14 +15,14 @@ interface ISablierV2Batch { SABLIER-V2-LOCKUP-LINEAR //////////////////////////////////////////////////////////////////////////*/ - /// @notice Creates a batch of Lockup Linear streams using `createWithDurations`. + /// @notice Creates a batch of LockupLinear streams using `createWithDurations`. /// /// @dev Requirements: /// - There must be at least one element in `batch`. /// - All requirements from {ISablierV2LockupLinear.createWithDurations} must be met for each stream. /// /// @param lockupLinear The address of the {SablierV2LockupLinear} contract. - /// @param asset The contract address of the ERC-20 asset used for streaming. + /// @param asset The contract address of the ERC-20 asset to be distributed. /// @param batch An array of structs, each encapsulating a subset of the parameters of /// {SablierV2LockupLinear.createWithDurations}. /// @return streamIds The ids of the newly created streams. @@ -34,14 +34,14 @@ interface ISablierV2Batch { external returns (uint256[] memory streamIds); - /// @notice Creates a batch of Lockup Linear streams using `createWithTimestamps`. + /// @notice Creates a batch of LockupLinear streams using `createWithTimestamps`. /// /// @dev Requirements: /// - There must be at least one element in `batch`. /// - All requirements from {ISablierV2LockupLinear.createWithTimestamps} must be met for each stream. /// /// @param lockupLinear The address of the {SablierV2LockupLinear} contract. - /// @param asset The contract address of the ERC-20 asset used for streaming. + /// @param asset The contract address of the ERC-20 asset to be distributed. /// @param batch An array of structs, each encapsulating a subset of the parameters of /// {SablierV2LockupLinear.createWithTimestamps}. /// @return streamIds The ids of the newly created streams. @@ -64,7 +64,7 @@ interface ISablierV2Batch { /// - All requirements from {ISablierV2LockupDynamic.createWithDurations} must be met for each stream. /// /// @param lockupDynamic The address of the {SablierV2LockupDynamic} contract. - /// @param asset The contract address of the ERC-20 asset used for streaming. + /// @param asset The contract address of the ERC-20 asset to be distributed. /// @param batch An array of structs, each encapsulating a subset of the parameters of /// {SablierV2LockupDynamic.createWithDurations}. /// @return streamIds The ids of the newly created streams. @@ -83,7 +83,7 @@ interface ISablierV2Batch { /// - All requirements from {ISablierV2LockupDynamic.createWithTimestamps} must be met for each stream. /// /// @param lockupDynamic The address of the {SablierV2LockupDynamic} contract. - /// @param asset The contract address of the ERC-20 asset used for streaming. + /// @param asset The contract address of the ERC-20 asset to be distributed. /// @param batch An array of structs, each encapsulating a subset of the parameters of /// {SablierV2LockupDynamic.createWithTimestamps}. /// @return streamIds The ids of the newly created streams. @@ -99,14 +99,14 @@ interface ISablierV2Batch { SABLIER-V2-LOCKUP-TRANCHED //////////////////////////////////////////////////////////////////////////*/ - /// @notice Creates a batch of Lockup Tranched streams using `createWithDurations`. + /// @notice Creates a batch of LockupTranched streams using `createWithDurations`. /// /// @dev Requirements: /// - There must be at least one element in `batch`. /// - All requirements from {ISablierV2LockupTranched.createWithDurations} must be met for each stream. /// /// @param lockupTranched The address of the {SablierV2LockupTranched} contract. - /// @param asset The contract address of the ERC-20 asset used for streaming. + /// @param asset The contract address of the ERC-20 asset to be distributed. /// @param batch An array of structs, each encapsulating a subset of the parameters of /// {SablierV2LockupTranched.createWithDurations}. /// @return streamIds The ids of the newly created streams. @@ -118,14 +118,14 @@ interface ISablierV2Batch { external returns (uint256[] memory streamIds); - /// @notice Creates a batch of Lockup Tranched streams using `createWithTimestamps`. + /// @notice Creates a batch of LockupTranched streams using `createWithTimestamps`. /// /// @dev Requirements: /// - There must be at least one element in `batch`. /// - All requirements from {ISablierV2LockupTranched.createWithTimestamps} must be met for each stream. /// /// @param lockupTranched The address of the {SablierV2LockupTranched} contract. - /// @param asset The contract address of the ERC-20 asset used for streaming. + /// @param asset The contract address of the ERC-20 asset to be distributed. /// @param batch An array of structs, each encapsulating a subset of the parameters of /// {SablierV2LockupTranched.createWithTimestamps}. /// @return streamIds The ids of the newly created streams. diff --git a/src/interfaces/ISablierV2MerkleLockup.sol b/src/interfaces/ISablierV2MerkleLockup.sol index 92688563..009e1b3d 100644 --- a/src/interfaces/ISablierV2MerkleLockup.sol +++ b/src/interfaces/ISablierV2MerkleLockup.sol @@ -5,11 +5,10 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IAdminable } from "@sablier/v2-core/src/interfaces/IAdminable.sol"; /// @title ISablierV2MerkleLockup -/// @notice A contract that lets user claim Sablier streams using Merkle proofs. An interesting use case for -/// MerkleStream is airstreams, which is a portmanteau of "airdrop" and "stream". This is an airdrop model where the -/// tokens are distributed over time, as opposed to all at once. -/// @dev This is the base interface for MerkleLockup contracts. See the Sablier docs for more guidance on how -/// streaming works: https://docs.sablier.com/. +/// @notice A contract that lets user claim Sablier streams using Merkle proofs. A popular use case for MerkleLockup +/// is airstreams: a portmanteau of "airdrop" and "stream". This is an airdrop model where the tokens are distributed +/// over time, as opposed to all at once. +/// @dev This is the base interface for MerkleLockup. See the Sablier docs for more guidance: https://docs.sablier.com interface ISablierV2MerkleLockup is IAdminable { /*////////////////////////////////////////////////////////////////////////// EVENTS @@ -25,7 +24,7 @@ interface ISablierV2MerkleLockup is IAdminable { CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @notice The streamed ERC-20 asset. + /// @notice The ERC-20 asset to distribute. /// @dev This is an immutable state variable. function ASSET() external returns (IERC20); @@ -33,8 +32,7 @@ interface ISablierV2MerkleLockup is IAdminable { /// @dev This is an immutable state variable. function CANCELABLE() external returns (bool); - /// @notice The cut-off point for the Merkle Lockup contract, as a Unix timestamp. A value of zero means there - /// is no expiration. + /// @notice The cut-off point for the campaign, as a Unix timestamp. A value of zero means there is no expiration. /// @dev This is an immutable state variable. function EXPIRATION() external returns (uint40); @@ -46,10 +44,10 @@ interface ISablierV2MerkleLockup is IAdminable { /// @notice Returns a flag indicating whether the campaign has expired. function hasExpired() external view returns (bool); - /// @notice The content identifier for indexing the contract on IPFS. + /// @notice The content identifier for indexing the campaign on IPFS. function ipfsCID() external view returns (string memory); - /// @notice The root of the Merkle tree used to validate the claims. + /// @notice The root of the Merkle tree used to validate the proofs of inclusion. /// @dev This is an immutable state variable. function MERKLE_ROOT() external returns (bytes32); @@ -64,7 +62,7 @@ interface ISablierV2MerkleLockup is IAdminable { NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Claws back the unclaimed tokens from the Merkle Lockup. + /// @notice Claws back the unclaimed tokens from the campaign. /// /// @dev Emits a {Clawback} event. /// diff --git a/src/interfaces/ISablierV2MerkleLockupFactory.sol b/src/interfaces/ISablierV2MerkleLockupFactory.sol index faa27155..6f09f0bc 100644 --- a/src/interfaces/ISablierV2MerkleLockupFactory.sol +++ b/src/interfaces/ISablierV2MerkleLockupFactory.sol @@ -10,23 +10,23 @@ import { ISablierV2MerkleLockupLT } from "./ISablierV2MerkleLockupLT.sol"; import { MerkleLockup, MerkleLockupLT } from "../types/DataTypes.sol"; /// @title ISablierV2MerkleLockupFactory -/// @notice Deploys new Lockup Linear Merkle lockups via CREATE2. +/// @notice Deploys MerkleLockup campaigns with CREATE2. interface ISablierV2MerkleLockupFactory { /*////////////////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Emitted when a Sablier V2 Lockup Linear Merkle Lockup is created. + /// @notice Emitted when a {SablierV2MerkleLockupLL} campaign is created. event CreateMerkleLockupLL( ISablierV2MerkleLockupLL indexed merkleLockupLL, MerkleLockup.ConstructorParams baseParams, ISablierV2LockupLinear lockupLinear, LockupLinear.Durations streamDurations, uint256 aggregateAmount, - uint256 recipientsCount + uint256 recipientCount ); - /// @notice Emitted when a Sablier V2 Lockup Tranched Merkle Lockup is created. + /// @notice Emitted when a {SablierV2MerkleLockupLT} campaign is created. event CreateMerkleLockupLT( ISablierV2MerkleLockupLT indexed merkleLockupLT, MerkleLockup.ConstructorParams baseParams, @@ -34,33 +34,33 @@ interface ISablierV2MerkleLockupFactory { MerkleLockupLT.TrancheWithPercentage[] tranchesWithPercentages, uint256 totalDuration, uint256 aggregateAmount, - uint256 recipientsCount + uint256 recipientCount ); /*////////////////////////////////////////////////////////////////////////// NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Creates a new Merkle Lockup that uses Lockup Linear. + /// @notice Creates a new MerkleLockup campaign with a LockupLinear distribution. /// @dev Emits a {CreateMerkleLockupLL} event. /// @param baseParams Struct encapsulating the {SablierV2MerkleLockup} parameters, which are documented in /// {DataTypes}. /// @param lockupLinear The address of the {SablierV2LockupLinear} contract. - /// @param streamDurations The durations for each stream due to the recipient. - /// @param aggregateAmount Total amount of ERC-20 assets to be streamed to all recipients. - /// @param recipientsCount Total number of recipients eligible to claim. - /// @return merkleLockupLL The address of the newly created Merkle Lockup contract. + /// @param streamDurations The durations for each stream. + /// @param aggregateAmount The total amount of ERC-20 assets to be distributed to all recipients. + /// @param recipientCount The total number of recipients who are eligible to claim. + /// @return merkleLockupLL The address of the newly created MerkleLockup contract. function createMerkleLockupLL( MerkleLockup.ConstructorParams memory baseParams, ISablierV2LockupLinear lockupLinear, LockupLinear.Durations memory streamDurations, uint256 aggregateAmount, - uint256 recipientsCount + uint256 recipientCount ) external returns (ISablierV2MerkleLockupLL merkleLockupLL); - /// @notice Creates a new Merkle Lockup that uses Lockup Tranched. + /// @notice Creates a new MerkleLockup campaign with a LockupTranched distribution. /// @dev Emits a {CreateMerkleLockupLT} event. /// /// Requirements: @@ -70,15 +70,15 @@ interface ISablierV2MerkleLockupFactory { /// {DataTypes}. /// @param lockupTranched The address of the {SablierV2LockupTranched} contract. /// @param tranchesWithPercentages The tranches with their respective unlock percentages. - /// @param aggregateAmount Total amount of ERC-20 assets to be streamed to all recipients. - /// @param recipientsCount Total number of recipients eligible to claim. - /// @return merkleLockupLT The address of the newly created Merkle Lockup contract. + /// @param aggregateAmount The total amount of ERC-20 assets to be distributed to all recipients. + /// @param recipientCount The total number of recipients who are eligible to claim. + /// @return merkleLockupLT The address of the newly created MerkleLockup contract. function createMerkleLockupLT( MerkleLockup.ConstructorParams memory baseParams, ISablierV2LockupTranched lockupTranched, MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages, uint256 aggregateAmount, - uint256 recipientsCount + uint256 recipientCount ) external returns (ISablierV2MerkleLockupLT merkleLockupLT); diff --git a/src/interfaces/ISablierV2MerkleLockupLL.sol b/src/interfaces/ISablierV2MerkleLockupLL.sol index f901ec5d..e98573c4 100644 --- a/src/interfaces/ISablierV2MerkleLockupLL.sol +++ b/src/interfaces/ISablierV2MerkleLockupLL.sol @@ -6,7 +6,7 @@ import { ISablierV2LockupLinear } from "@sablier/v2-core/src/interfaces/ISablier import { ISablierV2MerkleLockup } from "./ISablierV2MerkleLockup.sol"; /// @title ISablierV2MerkleLockupLL -/// @notice Merkle Lockup that creates Lockup Linear streams. +/// @notice MerkleLockup campaign that creates LockupLinear streams. interface ISablierV2MerkleLockupLL is ISablierV2MerkleLockup { /*////////////////////////////////////////////////////////////////////////// CONSTANT FUNCTIONS @@ -22,7 +22,8 @@ interface ISablierV2MerkleLockupLL is ISablierV2MerkleLockup { NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Makes the claim by creating a Lockup Linear stream to the recipient. + /// @notice Makes the claim by creating a LockupLinear stream to the recipient. A stream NFT is minted to the + /// recipient. /// /// @dev Emits a {Claim} event. /// @@ -33,8 +34,8 @@ interface ISablierV2MerkleLockupLL is ISablierV2MerkleLockup { /// /// @param index The index of the recipient in the Merkle tree. /// @param recipient The address of the stream holder. - /// @param amount The amount of tokens to be streamed. - /// @param merkleProof The Merkle proof of inclusion in the stream. + /// @param amount The amount of ERC-20 assets to be distributed via the claimed stream. + /// @param merkleProof The proof of inclusion in the Merkle tree. /// @return streamId The id of the newly created stream. function claim( uint256 index, diff --git a/src/interfaces/ISablierV2MerkleLockupLT.sol b/src/interfaces/ISablierV2MerkleLockupLT.sol index e34d53fe..de723cf7 100644 --- a/src/interfaces/ISablierV2MerkleLockupLT.sol +++ b/src/interfaces/ISablierV2MerkleLockupLT.sol @@ -7,7 +7,7 @@ import { ISablierV2MerkleLockup } from "./ISablierV2MerkleLockup.sol"; import { MerkleLockupLT } from "./../types/DataTypes.sol"; /// @title ISablierV2MerkleLockupLT -/// @notice Merkle Lockup that creates Lockup Tranched streams. +/// @notice MerkleLockup campaign that creates LockupTranched streams. interface ISablierV2MerkleLockupLT is ISablierV2MerkleLockup { /*////////////////////////////////////////////////////////////////////////// CONSTANT FUNCTIONS @@ -23,7 +23,8 @@ interface ISablierV2MerkleLockupLT is ISablierV2MerkleLockup { NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Makes the claim by creating a Lockup Tranched stream to the recipient. + /// @notice Makes the claim by creating a LockupTranched stream to the recipient. A stream NFT is minted to the + /// recipient. /// /// @dev Emits a {Claim} event. /// @@ -34,8 +35,8 @@ interface ISablierV2MerkleLockupLT is ISablierV2MerkleLockup { /// /// @param index The index of the recipient in the Merkle tree. /// @param recipient The address of the stream holder. - /// @param amount The amount of tokens to be streamed. - /// @param merkleProof The Merkle proof of inclusion in the stream. + /// @param amount The amount of ERC-20 assets to be distributed via the claimed stream. + /// @param merkleProof The proof of inclusion in the Merkle tree. /// @return streamId The id of the newly created stream. function claim( uint256 index, diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index bf8bec32..dd14b0b0 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -15,13 +15,13 @@ library Errors { //////////////////////////////////////////////////////////////////////////*/ /// @notice Thrown when trying to claim after the campaign has expired. - error SablierV2MerkleLockup_CampaignExpired(uint256 currentTime, uint40 expiration); + error SablierV2MerkleLockup_CampaignExpired(uint256 blockTimestamp, uint40 expiration); /// @notice Thrown when trying to create a campaign with a name that is too long. error SablierV2MerkleLockup_CampaignNameTooLong(uint256 nameLength, uint256 maxLength); /// @notice Thrown when trying to clawback when the campaign has not expired. - error SablierV2MerkleLockup_CampaignNotExpired(uint256 currentTime, uint40 expiration); + error SablierV2MerkleLockup_CampaignNotExpired(uint256 blockTimestamp, uint40 expiration); /// @notice Thrown when trying to claim with an invalid Merkle proof. error SablierV2MerkleLockup_InvalidProof(); @@ -34,5 +34,5 @@ library Errors { //////////////////////////////////////////////////////////////////////////*/ /// @notice Thrown when the sum of the tranches' unlock percentages does not equal 100%. - error SablierV2MerkleLockupFactory_TotalPercentageNotEqualOneHundred(uint256 totalPercentage); + error SablierV2MerkleLockupFactory_TotalPercentageNotOneHundred(uint64 totalPercentage); } diff --git a/src/types/DataTypes.sol b/src/types/DataTypes.sol index 9b84ad03..dd9bcbfb 100644 --- a/src/types/DataTypes.sol +++ b/src/types/DataTypes.sol @@ -89,15 +89,15 @@ library Batch { } library MerkleLockup { - /// @notice Struct encapsulating the base constructor parameter of a {SablierV2MerkleLockup} contract. - /// @param initialAdmin The initial admin of the Merkle Lockup contract. - /// @param asset The address of the streamed ERC-20 asset. + /// @notice Struct encapsulating the base constructor parameters of a MerkleLockup campaign. + /// @param initialAdmin The initial admin of the MerkleLockup campaign. + /// @param asset The contract address of the ERC-20 asset to be distributed. /// @param ipfsCID The content identifier for indexing the contract on IPFS. /// @param name The name of the campaign. /// @param merkleRoot The Merkle root of the claim data. - /// @param expiration The expiration of the streaming campaign, as a Unix timestamp. - /// @param cancelable Indicates if each stream will be cancelable. - /// @param transferable Indicates if each stream NFT will be transferable. + /// @param expiration The expiration of the campaign, as a Unix timestamp. + /// @param cancelable Indicates if the stream will be cancelable after claiming. + /// @param transferable Indicates if the stream will be transferable after claiming. struct ConstructorParams { address initialAdmin; IERC20 asset; @@ -111,11 +111,11 @@ library MerkleLockup { } library MerkleLockupLT { - /// @notice Struct encapsulating the amount percentage and the tranche duration of the stream. - /// @dev Each recipient may have a different amount allocated, this struct stores the percentage of the - /// amount designated for each duration unlock. We use a 18 decimals format to represent percentages: - /// 100% = 1e18. - /// @param unlockPercentage The percentage of the amount designated to be unlocked in this tranche. + /// @notice Struct encapsulating the unlock percentage and duration of a tranche. + /// @dev Since users may have different amounts allocated, this struct makes it possible to calculate the amounts + /// at claim time. An 18-decimal format is used to represent percentages: 100% = 1e18. For more information, see + /// the PRBMath documentation on {UD2x18}: https://github.com/PaulRBerg/prb-math + /// @param unlockPercentage The percentage designated to be unlocked in this tranche. /// @param duration The time difference in seconds between this tranche and the previous one. struct TrancheWithPercentage { // slot 0 diff --git a/test/Base.t.sol b/test/Base.t.sol index 638f425e..290c95ba 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -10,7 +10,7 @@ import { ISablierV2LockupTranched } from "@sablier/v2-core/src/interfaces/ISabli import { LockupDynamic, LockupLinear, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol"; import { Assertions as V2CoreAssertions } from "@sablier/v2-core/test/utils/Assertions.sol"; -import { Constants as V2Constants } from "@sablier/v2-core/test/utils/Constants.sol"; +import { Constants as V2CoreConstants } from "@sablier/v2-core/test/utils/Constants.sol"; import { Utils as V2CoreUtils } from "@sablier/v2-core/test/utils/Utils.sol"; import { ISablierV2Batch } from "src/interfaces/ISablierV2Batch.sol"; @@ -36,7 +36,7 @@ abstract contract Base_Test is DeployOptimized, Events, Merkle, - V2Constants, + V2CoreConstants, V2CoreAssertions, V2CoreUtils { @@ -50,8 +50,8 @@ abstract contract Base_Test is TEST CONTRACTS //////////////////////////////////////////////////////////////////////////*/ - IERC20 internal dai; ISablierV2Batch internal batch; + IERC20 internal dai; Defaults internal defaults; ISablierV2LockupDynamic internal lockupDynamic; ISablierV2LockupLinear internal lockupLinear; @@ -84,7 +84,7 @@ abstract contract Base_Test is HELPERS //////////////////////////////////////////////////////////////////////////*/ - /// @dev Approve contract to spend asset from some users. + /// @dev Approve `spender` to spend assets from `from`. function approveContract(IERC20 asset_, address from, address spender) internal { resetPrank({ msgSender: from }); (bool success,) = address(asset_).call(abi.encodeCall(IERC20.approve, (spender, MAX_UINT256))); @@ -112,12 +112,13 @@ abstract contract Base_Test is /// @dev Labels the most relevant contracts. function labelContracts(IERC20 asset_) internal { vm.label({ account: address(asset_), newLabel: IERC20Metadata(address(asset_)).symbol() }); - vm.label({ account: address(merkleLockupFactory), newLabel: "MerkleLockupFactory" }); - vm.label({ account: address(merkleLockupLL), newLabel: "MerkleLockupLL" }); vm.label({ account: address(defaults), newLabel: "Defaults" }); vm.label({ account: address(lockupDynamic), newLabel: "LockupDynamic" }); vm.label({ account: address(lockupLinear), newLabel: "LockupLinear" }); vm.label({ account: address(lockupTranched), newLabel: "LockupTranched" }); + vm.label({ account: address(merkleLockupFactory), newLabel: "MerkleLockupFactory" }); + vm.label({ account: address(merkleLockupLL), newLabel: "MerkleLockupLL" }); + vm.label({ account: address(merkleLockupLT), newLabel: "MerkleLockupLT" }); } /*////////////////////////////////////////////////////////////////////////// diff --git a/test/fork/Fork.t.sol b/test/fork/Fork.t.sol index 1cda8b71..a8aed19d 100644 --- a/test/fork/Fork.t.sol +++ b/test/fork/Fork.t.sol @@ -18,14 +18,14 @@ abstract contract Fork_Test is Base_Test, V2CoreFuzzers { STATE VARIABLES //////////////////////////////////////////////////////////////////////////*/ - IERC20 internal immutable ASSET; + IERC20 internal immutable FORK_ASSET; /*////////////////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////////////////*/ - constructor(IERC20 asset_) { - ASSET = asset_; + constructor(IERC20 forkAsset) { + FORK_ASSET = forkAsset; } /*////////////////////////////////////////////////////////////////////////// @@ -41,21 +41,21 @@ abstract contract Fork_Test is Base_Test, V2CoreFuzzers { // Load the external dependencies. // loadDependencies(); - // TODO: Remove this line once the v2 core contracts are deployed on Mainnet. + // TODO: Remove this line once the V2 Core contracts are deployed on Mainnet. deployDependencies(); // Deploy the defaults contract and allow it to access cheatcodes. - defaults = new Defaults(users, ASSET); + defaults = new Defaults({ users_: users, asset_: FORK_ASSET }); vm.allowCheatcodes(address(defaults)); // Deploy V2 Periphery. deployPeripheryConditionally(); // Label the contracts. - labelContracts(ASSET); + labelContracts(FORK_ASSET); - // Approve the relevant contract. - approveContract({ asset_: ASSET, from: users.alice, spender: address(batch) }); + // Approve the Batch contract. + approveContract({ asset_: FORK_ASSET, from: users.alice, spender: address(batch) }); } /*////////////////////////////////////////////////////////////////////////// @@ -74,8 +74,8 @@ abstract contract Fork_Test is Base_Test, V2CoreFuzzers { vm.assume(user != address(lockupTranched) && recipient != address(lockupTranched)); // Avoid users blacklisted by USDC or USDT. - assumeNoBlacklisted(address(ASSET), user); - assumeNoBlacklisted(address(ASSET), recipient); + assumeNoBlacklisted(address(FORK_ASSET), user); + assumeNoBlacklisted(address(FORK_ASSET), recipient); } /// @dev Loads all dependencies pre-deployed on Mainnet. @@ -85,8 +85,8 @@ abstract contract Fork_Test is Base_Test, V2CoreFuzzers { lockupTranched = ISablierV2LockupTranched(0xAFb979d9afAd1aD27C5eFf4E27226E3AB9e5dCC9); } - /// @dev Deploys the v2 core dependencies. - // TODO: Remove this function once the v2 core contracts are deployed on Mainnet. + /// @dev Deploys the V2 Core dependencies. + // TODO: Remove this function once the V2 Core contracts are deployed on Mainnet. function deployDependencies() private { (lockupDynamic, lockupLinear, lockupTranched,) = new V2CorePrecompiles().deployCore(users.admin); } diff --git a/test/fork/batch/createWithTimestampsLD.t.sol b/test/fork/batch/createWithTimestampsLD.t.sol index e2704d24..f7e47254 100644 --- a/test/fork/batch/createWithTimestampsLD.t.sol +++ b/test/fork/batch/createWithTimestampsLD.t.sol @@ -18,10 +18,6 @@ abstract contract CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is Fork_Tes Fork_Test.setUp(); } - /*////////////////////////////////////////////////////////////////////////// - BATCH-CREATE-WITH-TIMESTAMPS - //////////////////////////////////////////////////////////////////////////*/ - struct CreateWithTimestampsParams { uint128 batchSize; address sender; @@ -31,7 +27,7 @@ abstract contract CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is Fork_Tes LockupDynamic.Segment[] segments; } - function testForkFuzz_CreateWithTimestamps(CreateWithTimestampsParams memory params) external { + function testForkFuzz_CreateWithTimestampsLD(CreateWithTimestampsParams memory params) external { vm.assume(params.segments.length != 0); params.batchSize = boundUint128(params.batchSize, 1, 20); params.startTime = boundUint40(params.startTime, getBlockTimestamp(), getBlockTimestamp() + 24 hours); @@ -47,14 +43,14 @@ abstract contract CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is Fork_Tes uint256 firstStreamId = lockupDynamic.nextStreamId(); uint128 totalTransferAmount = params.perStreamAmount * params.batchSize; - deal({ token: address(ASSET), to: params.sender, give: uint256(totalTransferAmount) }); - approveContract({ asset_: ASSET, from: params.sender, spender: address(batch) }); + deal({ token: address(FORK_ASSET), to: params.sender, give: uint256(totalTransferAmount) }); + approveContract({ asset_: FORK_ASSET, from: params.sender, spender: address(batch) }); LockupDynamic.CreateWithTimestamps memory createWithTimestamps = LockupDynamic.CreateWithTimestamps({ sender: params.sender, recipient: params.recipient, totalAmount: params.perStreamAmount, - asset: ASSET, + asset: FORK_ASSET, cancelable: true, transferable: true, startTime: params.startTime, @@ -65,21 +61,21 @@ abstract contract CreateWithTimestamps_LockupDynamic_Batch_Fork_Test is Fork_Tes BatchBuilder.fillBatch(createWithTimestamps, params.batchSize); expectCallToTransferFrom({ - asset_: address(ASSET), + asset_: address(FORK_ASSET), from: params.sender, to: address(batch), amount: totalTransferAmount }); expectMultipleCallsToCreateWithTimestampsLD({ count: uint64(params.batchSize), params: createWithTimestamps }); expectMultipleCallsToTransferFrom({ - asset_: address(ASSET), + asset_: address(FORK_ASSET), count: uint64(params.batchSize), from: address(batch), to: address(lockupDynamic), amount: params.perStreamAmount }); - uint256[] memory actualStreamIds = batch.createWithTimestampsLD(lockupDynamic, ASSET, batchParams); + uint256[] memory actualStreamIds = batch.createWithTimestampsLD(lockupDynamic, FORK_ASSET, batchParams); uint256[] memory expectedStreamIds = ArrayBuilder.fillStreamIds(firstStreamId, params.batchSize); assertEq(actualStreamIds, expectedStreamIds); } diff --git a/test/fork/batch/createWithTimestampsLL.t.sol b/test/fork/batch/createWithTimestampsLL.t.sol index 8dc3705d..ab3af65c 100644 --- a/test/fork/batch/createWithTimestampsLL.t.sol +++ b/test/fork/batch/createWithTimestampsLL.t.sol @@ -18,10 +18,6 @@ abstract contract CreateWithTimestamps_LockupLinear_Batch_Fork_Test is Fork_Test Fork_Test.setUp(); } - /*////////////////////////////////////////////////////////////////////////// - BATCH-CREATE-WITH-TIMESTAMPS - //////////////////////////////////////////////////////////////////////////*/ - struct CreateWithTimestampsParams { uint128 batchSize; LockupLinear.Range range; @@ -30,11 +26,12 @@ abstract contract CreateWithTimestamps_LockupLinear_Batch_Fork_Test is Fork_Test uint128 perStreamAmount; } - function testForkFuzz_CreateWithTimestamps(CreateWithTimestampsParams memory params) external { + function testForkFuzz_CreateWithTimestampsLL(CreateWithTimestampsParams memory params) external { params.batchSize = boundUint128(params.batchSize, 1, 20); params.perStreamAmount = boundUint128(params.perStreamAmount, 1, MAX_UINT128 / params.batchSize); params.range.start = boundUint40(params.range.start, getBlockTimestamp(), getBlockTimestamp() + 24 hours); - params.range.cliff = boundUint40(params.range.cliff, params.range.start + 1, params.range.start + 52 weeks); + params.range.cliff = + boundUint40(params.range.cliff, params.range.start + 1 seconds, params.range.start + 52 weeks); params.range.end = boundUint40(params.range.end, params.range.cliff + 1 seconds, MAX_UNIX_TIMESTAMP); checkUsers(params.sender, params.recipient); @@ -42,14 +39,14 @@ abstract contract CreateWithTimestamps_LockupLinear_Batch_Fork_Test is Fork_Test uint256 firstStreamId = lockupLinear.nextStreamId(); uint128 totalTransferAmount = params.perStreamAmount * params.batchSize; - deal({ token: address(ASSET), to: params.sender, give: uint256(totalTransferAmount) }); - approveContract({ asset_: ASSET, from: params.sender, spender: address(batch) }); + deal({ token: address(FORK_ASSET), to: params.sender, give: uint256(totalTransferAmount) }); + approveContract({ asset_: FORK_ASSET, from: params.sender, spender: address(batch) }); LockupLinear.CreateWithTimestamps memory createParams = LockupLinear.CreateWithTimestamps({ sender: params.sender, recipient: params.recipient, totalAmount: params.perStreamAmount, - asset: ASSET, + asset: FORK_ASSET, cancelable: true, transferable: true, range: params.range, @@ -59,21 +56,21 @@ abstract contract CreateWithTimestamps_LockupLinear_Batch_Fork_Test is Fork_Test // Asset flow: sender → batch → Sablier expectCallToTransferFrom({ - asset_: address(ASSET), + asset_: address(FORK_ASSET), from: params.sender, to: address(batch), amount: totalTransferAmount }); expectMultipleCallsToCreateWithTimestampsLL({ count: uint64(params.batchSize), params: createParams }); expectMultipleCallsToTransferFrom({ - asset_: address(ASSET), + asset_: address(FORK_ASSET), count: uint64(params.batchSize), from: address(batch), to: address(lockupLinear), amount: params.perStreamAmount }); - uint256[] memory actualStreamIds = batch.createWithTimestampsLL(lockupLinear, ASSET, batchParams); + uint256[] memory actualStreamIds = batch.createWithTimestampsLL(lockupLinear, FORK_ASSET, batchParams); uint256[] memory expectedStreamIds = ArrayBuilder.fillStreamIds(firstStreamId, params.batchSize); assertEq(actualStreamIds, expectedStreamIds); } diff --git a/test/fork/batch/createWithTimestampsLT.t.sol b/test/fork/batch/createWithTimestampsLT.t.sol index a745dd48..63d4d62a 100644 --- a/test/fork/batch/createWithTimestampsLT.t.sol +++ b/test/fork/batch/createWithTimestampsLT.t.sol @@ -18,10 +18,6 @@ abstract contract CreateWithTimestamps_LockupTranched_Batch_Fork_Test is Fork_Te Fork_Test.setUp(); } - /*////////////////////////////////////////////////////////////////////////// - BATCH-CREATE-WITH-TIMESTAMPS - //////////////////////////////////////////////////////////////////////////*/ - struct CreateWithTimestampsParams { uint128 batchSize; address sender; @@ -31,7 +27,7 @@ abstract contract CreateWithTimestamps_LockupTranched_Batch_Fork_Test is Fork_Te LockupTranched.Tranche[] tranches; } - function testForkFuzz_CreateWithTimestamps(CreateWithTimestampsParams memory params) external { + function testForkFuzz_CreateWithTimestampsLT(CreateWithTimestampsParams memory params) external { vm.assume(params.tranches.length != 0); params.batchSize = boundUint128(params.batchSize, 1, 20); params.startTime = boundUint40(params.startTime, getBlockTimestamp(), getBlockTimestamp() + 24 hours); @@ -47,14 +43,14 @@ abstract contract CreateWithTimestamps_LockupTranched_Batch_Fork_Test is Fork_Te uint256 firstStreamId = lockupTranched.nextStreamId(); uint128 totalTransferAmount = params.perStreamAmount * params.batchSize; - deal({ token: address(ASSET), to: params.sender, give: uint256(totalTransferAmount) }); - approveContract({ asset_: ASSET, from: params.sender, spender: address(batch) }); + deal({ token: address(FORK_ASSET), to: params.sender, give: uint256(totalTransferAmount) }); + approveContract({ asset_: FORK_ASSET, from: params.sender, spender: address(batch) }); LockupTranched.CreateWithTimestamps memory createWithTimestamps = LockupTranched.CreateWithTimestamps({ sender: params.sender, recipient: params.recipient, totalAmount: params.perStreamAmount, - asset: ASSET, + asset: FORK_ASSET, cancelable: true, transferable: true, startTime: params.startTime, @@ -65,21 +61,21 @@ abstract contract CreateWithTimestamps_LockupTranched_Batch_Fork_Test is Fork_Te BatchBuilder.fillBatch(createWithTimestamps, params.batchSize); expectCallToTransferFrom({ - asset_: address(ASSET), + asset_: address(FORK_ASSET), from: params.sender, to: address(batch), amount: totalTransferAmount }); expectMultipleCallsToCreateWithTimestampsLT({ count: uint64(params.batchSize), params: createWithTimestamps }); expectMultipleCallsToTransferFrom({ - asset_: address(ASSET), + asset_: address(FORK_ASSET), count: uint64(params.batchSize), from: address(batch), to: address(lockupTranched), amount: params.perStreamAmount }); - uint256[] memory actualStreamIds = batch.createWithTimestampsLT(lockupTranched, ASSET, batchParams); + uint256[] memory actualStreamIds = batch.createWithTimestampsLT(lockupTranched, FORK_ASSET, batchParams); uint256[] memory expectedStreamIds = ArrayBuilder.fillStreamIds(firstStreamId, params.batchSize); assertEq(actualStreamIds, expectedStreamIds); } diff --git a/test/fork/merkle-lockup/MerkleLockupLL.t.sol b/test/fork/merkle-lockup/MerkleLockupLL.t.sol index 664b5685..cea3dd79 100644 --- a/test/fork/merkle-lockup/MerkleLockupLL.t.sol +++ b/test/fork/merkle-lockup/MerkleLockupLL.t.sol @@ -35,13 +35,13 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { } struct Vars { - uint256 actualStreamId; LockupLinear.StreamLL actualStream; - uint128[] amounts; + uint256 actualStreamId; uint256 aggregateAmount; + uint128[] amounts; + MerkleLockup.ConstructorParams baseParams; uint128 clawbackAmount; address expectedLockupLL; - MerkleLockup.ConstructorParams baseParams; LockupLinear.StreamLL expectedStream; uint256 expectedStreamId; uint256[] indexes; @@ -50,7 +50,7 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { ISablierV2MerkleLockupLL merkleLockupLL; bytes32 merkleRoot; address[] recipients; - uint256 recipientsCount; + uint256 recipientCount; } // We need the leaves as a storage variable so that we can use OpenZeppelin's {Arrays.findUpperBound}. @@ -58,25 +58,29 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { function testForkFuzz_MerkleLockupLL(Params memory params) external { vm.assume(params.admin != address(0) && params.admin != users.admin); - vm.assume(params.expiration == 0 || params.expiration > block.timestamp); vm.assume(params.leafData.length > 1); + assumeNoBlacklisted({ token: address(FORK_ASSET), addr: params.admin }); params.posBeforeSort = _bound(params.posBeforeSort, 0, params.leafData.length - 1); - assumeNoBlacklisted({ token: address(ASSET), addr: params.admin }); + + // The expiration must be either zero or greater than the block timestamp. + if (params.expiration != 0) { + params.expiration = boundUint40(params.expiration, getBlockTimestamp() + 1 seconds, MAX_UNIX_TIMESTAMP); + } /*////////////////////////////////////////////////////////////////////////// CREATE //////////////////////////////////////////////////////////////////////////*/ Vars memory vars; - vars.recipientsCount = params.leafData.length; - vars.amounts = new uint128[](vars.recipientsCount); - vars.indexes = new uint256[](vars.recipientsCount); - vars.recipients = new address[](vars.recipientsCount); - for (uint256 i = 0; i < vars.recipientsCount; ++i) { + vars.recipientCount = params.leafData.length; + vars.amounts = new uint128[](vars.recipientCount); + vars.indexes = new uint256[](vars.recipientCount); + vars.recipients = new address[](vars.recipientCount); + for (uint256 i = 0; i < vars.recipientCount; ++i) { vars.indexes[i] = params.leafData[i].index; // Bound each leaf amount so that `aggregateAmount` does not overflow. - vars.amounts[i] = uint128(_bound(params.leafData[i].amount, 1, MAX_UINT256 / vars.recipientsCount - 1)); + vars.amounts[i] = boundUint128(params.leafData[i].amount, 1, uint128(MAX_UINT128 / vars.recipientCount - 1)); vars.aggregateAmount += vars.amounts[i]; // Avoid zero recipient addresses. @@ -84,18 +88,19 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { vars.recipients[i] = address(uint160(boundedRecipientSeed)); } - leaves = new uint256[](vars.recipientsCount); + leaves = new uint256[](vars.recipientCount); leaves = MerkleBuilder.computeLeaves(vars.indexes, vars.recipients, vars.amounts); // Sort the leaves in ascending order to match the production environment. MerkleBuilder.sortLeaves(leaves); vars.merkleRoot = getRoot(leaves.toBytes32()); - vars.expectedLockupLL = computeMerkleLockupLLAddress(params.admin, ASSET, vars.merkleRoot, params.expiration); + vars.expectedLockupLL = + computeMerkleLockupLLAddress(params.admin, FORK_ASSET, vars.merkleRoot, params.expiration); vars.baseParams = defaults.baseParams({ admin: params.admin, - asset_: ASSET, + asset_: FORK_ASSET, merkleRoot: vars.merkleRoot, expiration: params.expiration }); @@ -107,7 +112,7 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { lockupLinear: lockupLinear, streamDurations: defaults.durations(), aggregateAmount: vars.aggregateAmount, - recipientsCount: vars.recipientsCount + recipientCount: vars.recipientCount }); vars.merkleLockupLL = merkleLockupFactory.createMerkleLockupLL({ @@ -115,11 +120,11 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { lockupLinear: lockupLinear, streamDurations: defaults.durations(), aggregateAmount: vars.aggregateAmount, - recipientsCount: vars.recipientsCount + recipientCount: vars.recipientCount }); - // Fund the Merkle Lockup contract. - deal({ token: address(ASSET), to: address(vars.merkleLockupLL), give: vars.aggregateAmount }); + // Fund the MerkleLockup contract. + deal({ token: address(FORK_ASSET), to: address(vars.merkleLockupLL), give: vars.aggregateAmount }); assertGt(address(vars.merkleLockupLL).code.length, 0, "MerkleLockupLL contract not created"); assertEq( @@ -158,16 +163,16 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { vars.actualStream = lockupLinear.getStream(vars.actualStreamId); vars.expectedStream = LockupLinear.StreamLL({ amounts: Lockup.Amounts({ deposited: vars.amounts[params.posBeforeSort], refunded: 0, withdrawn: 0 }), - asset: ASSET, - cliffTime: uint40(block.timestamp) + defaults.CLIFF_DURATION(), - endTime: uint40(block.timestamp) + defaults.TOTAL_DURATION(), + asset: FORK_ASSET, + cliffTime: getBlockTimestamp() + defaults.CLIFF_DURATION(), + endTime: getBlockTimestamp() + defaults.TOTAL_DURATION(), isCancelable: defaults.CANCELABLE(), isDepleted: false, isStream: true, isTransferable: defaults.TRANSFERABLE(), recipient: vars.recipients[params.posBeforeSort], sender: params.admin, - startTime: uint40(block.timestamp), + startTime: getBlockTimestamp(), wasCanceled: false }); @@ -180,11 +185,11 @@ abstract contract MerkleLockupLL_Fork_Test is Fork_Test { //////////////////////////////////////////////////////////////////////////*/ if (params.expiration > 0) { - vars.clawbackAmount = uint128(ASSET.balanceOf(address(vars.merkleLockupLL))); + vars.clawbackAmount = uint128(FORK_ASSET.balanceOf(address(vars.merkleLockupLL))); vm.warp({ newTimestamp: uint256(params.expiration) + 1 seconds }); resetPrank({ msgSender: params.admin }); - expectCallToTransfer({ asset_: address(ASSET), to: params.admin, amount: vars.clawbackAmount }); + expectCallToTransfer({ asset_: address(FORK_ASSET), to: params.admin, amount: vars.clawbackAmount }); vm.expectEmit({ emitter: address(vars.merkleLockupLL) }); emit Clawback({ to: params.admin, admin: params.admin, amount: vars.clawbackAmount }); vars.merkleLockupLL.clawback({ to: params.admin, amount: vars.clawbackAmount }); diff --git a/test/fork/merkle-lockup/MerkleLockupLT.t.sol b/test/fork/merkle-lockup/MerkleLockupLT.t.sol index 9f31a59e..f5d7fb61 100644 --- a/test/fork/merkle-lockup/MerkleLockupLT.t.sol +++ b/test/fork/merkle-lockup/MerkleLockupLT.t.sol @@ -36,13 +36,13 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { struct Vars { uint256 actualStreamId; - LockupTranched.Tranche[] actualTranches; LockupTranched.StreamLT actualStream; + LockupTranched.Tranche[] actualTranches; uint128[] amounts; uint256 aggregateAmount; + MerkleLockup.ConstructorParams baseParams; uint128 clawbackAmount; address expectedLockupLT; - MerkleLockup.ConstructorParams baseParams; LockupTranched.StreamLT expectedStream; uint256 expectedStreamId; uint256[] indexes; @@ -51,7 +51,7 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { ISablierV2MerkleLockupLT merkleLockupLT; bytes32 merkleRoot; address[] recipients; - uint256 recipientsCount; + uint256 recipientCount; } // We need the leaves as a storage variable so that we can use OpenZeppelin's {Arrays.findUpperBound}. @@ -59,25 +59,29 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { function testForkFuzz_MerkleLockupLT(Params memory params) external { vm.assume(params.admin != address(0) && params.admin != users.admin); - vm.assume(params.expiration == 0 || params.expiration > block.timestamp); vm.assume(params.leafData.length > 1); + assumeNoBlacklisted({ token: address(FORK_ASSET), addr: params.admin }); params.posBeforeSort = _bound(params.posBeforeSort, 0, params.leafData.length - 1); - assumeNoBlacklisted({ token: address(ASSET), addr: params.admin }); + + // The expiration must be either zero or greater than the block timestamp. + if (params.expiration != 0) { + params.expiration = boundUint40(params.expiration, getBlockTimestamp() + 1 seconds, MAX_UNIX_TIMESTAMP); + } /*////////////////////////////////////////////////////////////////////////// CREATE //////////////////////////////////////////////////////////////////////////*/ Vars memory vars; - vars.recipientsCount = params.leafData.length; - vars.amounts = new uint128[](vars.recipientsCount); - vars.indexes = new uint256[](vars.recipientsCount); - vars.recipients = new address[](vars.recipientsCount); - for (uint256 i = 0; i < vars.recipientsCount; ++i) { + vars.recipientCount = params.leafData.length; + vars.amounts = new uint128[](vars.recipientCount); + vars.indexes = new uint256[](vars.recipientCount); + vars.recipients = new address[](vars.recipientCount); + for (uint256 i = 0; i < vars.recipientCount; ++i) { vars.indexes[i] = params.leafData[i].index; // Bound each leaf amount so that `aggregateAmount` does not overflow. - vars.amounts[i] = uint128(_bound(params.leafData[i].amount, 1, MAX_UINT256 / vars.recipientsCount - 1)); + vars.amounts[i] = boundUint128(params.leafData[i].amount, 1, uint128(MAX_UINT128 / vars.recipientCount - 1)); vars.aggregateAmount += vars.amounts[i]; // Avoid zero recipient addresses. @@ -85,18 +89,19 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { vars.recipients[i] = address(uint160(boundedRecipientSeed)); } - leaves = new uint256[](vars.recipientsCount); + leaves = new uint256[](vars.recipientCount); leaves = MerkleBuilder.computeLeaves(vars.indexes, vars.recipients, vars.amounts); // Sort the leaves in ascending order to match the production environment. MerkleBuilder.sortLeaves(leaves); vars.merkleRoot = getRoot(leaves.toBytes32()); - vars.expectedLockupLT = computeMerkleLockupLTAddress(params.admin, ASSET, vars.merkleRoot, params.expiration); + vars.expectedLockupLT = + computeMerkleLockupLTAddress(params.admin, FORK_ASSET, vars.merkleRoot, params.expiration); vars.baseParams = defaults.baseParams({ admin: params.admin, - asset_: ASSET, + asset_: FORK_ASSET, merkleRoot: vars.merkleRoot, expiration: params.expiration }); @@ -109,7 +114,7 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { tranchesWithPercentages: defaults.tranchesWithPercentages(), totalDuration: defaults.TOTAL_DURATION(), aggregateAmount: vars.aggregateAmount, - recipientsCount: vars.recipientsCount + recipientCount: vars.recipientCount }); vars.merkleLockupLT = merkleLockupFactory.createMerkleLockupLT({ @@ -117,11 +122,11 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { lockupTranched: lockupTranched, tranchesWithPercentages: defaults.tranchesWithPercentages(), aggregateAmount: vars.aggregateAmount, - recipientsCount: vars.recipientsCount + recipientCount: vars.recipientCount }); - // Fund the Merkle Lockup contract. - deal({ token: address(ASSET), to: address(vars.merkleLockupLT), give: vars.aggregateAmount }); + // Fund the MerkleLockup contract. + deal({ token: address(FORK_ASSET), to: address(vars.merkleLockupLT), give: vars.aggregateAmount }); assertGt(address(vars.merkleLockupLT).code.length, 0, "MerkleLockupLT contract not created"); assertEq( @@ -160,15 +165,15 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { vars.actualStream = lockupTranched.getStream(vars.actualStreamId); vars.expectedStream = LockupTranched.StreamLT({ amounts: Lockup.Amounts({ deposited: vars.amounts[params.posBeforeSort], refunded: 0, withdrawn: 0 }), - asset: ASSET, - endTime: uint40(block.timestamp) + defaults.TOTAL_DURATION(), + asset: FORK_ASSET, + endTime: getBlockTimestamp() + defaults.TOTAL_DURATION(), isCancelable: defaults.CANCELABLE(), isDepleted: false, isStream: true, isTransferable: defaults.TRANSFERABLE(), recipient: vars.recipients[params.posBeforeSort], sender: params.admin, - startTime: uint40(block.timestamp), + startTime: getBlockTimestamp(), tranches: defaults.tranches({ totalAmount: vars.amounts[params.posBeforeSort] }), wasCanceled: false }); @@ -182,11 +187,11 @@ abstract contract MerkleLockupLT_Fork_Test is Fork_Test { //////////////////////////////////////////////////////////////////////////*/ if (params.expiration > 0) { - vars.clawbackAmount = uint128(ASSET.balanceOf(address(vars.merkleLockupLT))); + vars.clawbackAmount = uint128(FORK_ASSET.balanceOf(address(vars.merkleLockupLT))); vm.warp({ newTimestamp: uint256(params.expiration) + 1 seconds }); resetPrank({ msgSender: params.admin }); - expectCallToTransfer({ asset_: address(ASSET), to: params.admin, amount: vars.clawbackAmount }); + expectCallToTransfer({ asset_: address(FORK_ASSET), to: params.admin, amount: vars.clawbackAmount }); vm.expectEmit({ emitter: address(vars.merkleLockupLT) }); emit Clawback({ to: params.admin, admin: params.admin, amount: vars.clawbackAmount }); vars.merkleLockupLT.clawback({ to: params.admin, amount: vars.clawbackAmount }); diff --git a/test/integration/Integration.t.sol b/test/integration/Integration.t.sol index 6cd60157..80c9d0f2 100644 --- a/test/integration/Integration.t.sol +++ b/test/integration/Integration.t.sol @@ -20,7 +20,7 @@ abstract contract Integration_Test is Base_Test { deployDependencies(); // Deploy the defaults contract. - defaults = new Defaults(users, dai); + defaults = new Defaults({ users_: users, asset_: dai }); // Deploy V2 Periphery. deployPeripheryConditionally(); @@ -28,7 +28,7 @@ abstract contract Integration_Test is Base_Test { // Label the contracts. labelContracts(dai); - // Approve the relevant contract. + // Approve the Batch contract. approveContract({ asset_: dai, from: users.alice, spender: address(batch) }); } diff --git a/test/integration/batch/lockup-dynamic/create-with-durations/createWithDurations.t.sol b/test/integration/batch/create-with-durations-ld/createWithDurationsLD.t.sol similarity index 92% rename from test/integration/batch/lockup-dynamic/create-with-durations/createWithDurations.t.sol rename to test/integration/batch/create-with-durations-ld/createWithDurationsLD.t.sol index f1d4bf27..a57072e4 100644 --- a/test/integration/batch/lockup-dynamic/create-with-durations/createWithDurations.t.sol +++ b/test/integration/batch/create-with-durations-ld/createWithDurationsLD.t.sol @@ -4,9 +4,9 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; import { Batch } from "src/types/DataTypes.sol"; -import { Integration_Test } from "../../../Integration.t.sol"; +import { Integration_Test } from "../../Integration.t.sol"; -contract CreateWithDurations_LockupDynamic_Integration_Test is Integration_Test { +contract CreateWithDurationsLD_Integration_Test is Integration_Test { function setUp() public virtual override { Integration_Test.setUp(); } diff --git a/test/integration/batch/lockup-linear/create-with-durations/createWithDurations.tree b/test/integration/batch/create-with-durations-ld/createWithDurationsLD.tree similarity index 89% rename from test/integration/batch/lockup-linear/create-with-durations/createWithDurations.tree rename to test/integration/batch/create-with-durations-ld/createWithDurationsLD.tree index 377110d8..782763df 100644 --- a/test/integration/batch/lockup-linear/create-with-durations/createWithDurations.tree +++ b/test/integration/batch/create-with-durations-ld/createWithDurationsLD.tree @@ -1,4 +1,4 @@ -createWithDurations.t.sol +createWithDurationsLD.t.sol ├── when the batch size is zero │ └── it should revert └── when the batch size is not zero diff --git a/test/integration/batch/lockup-linear/create-with-durations/createWithDurations.t.sol b/test/integration/batch/create-with-durations-ll/createWithDurationsLL.t.sol similarity index 92% rename from test/integration/batch/lockup-linear/create-with-durations/createWithDurations.t.sol rename to test/integration/batch/create-with-durations-ll/createWithDurationsLL.t.sol index a3dd26ee..404b04d2 100644 --- a/test/integration/batch/lockup-linear/create-with-durations/createWithDurations.t.sol +++ b/test/integration/batch/create-with-durations-ll/createWithDurationsLL.t.sol @@ -4,9 +4,9 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; import { Batch } from "src/types/DataTypes.sol"; -import { Integration_Test } from "../../../Integration.t.sol"; +import { Integration_Test } from "../../Integration.t.sol"; -contract CreateWithDurations_LockupLinear_Integration_Test is Integration_Test { +contract CreateWithDurationsLL_Integration_Test is Integration_Test { function setUp() public virtual override { Integration_Test.setUp(); } diff --git a/test/integration/batch/lockup-dynamic/create-with-durations/createWithDurations.tree b/test/integration/batch/create-with-durations-ll/createWithDurationsLL.tree similarity index 89% rename from test/integration/batch/lockup-dynamic/create-with-durations/createWithDurations.tree rename to test/integration/batch/create-with-durations-ll/createWithDurationsLL.tree index 377110d8..c0b50c87 100644 --- a/test/integration/batch/lockup-dynamic/create-with-durations/createWithDurations.tree +++ b/test/integration/batch/create-with-durations-ll/createWithDurationsLL.tree @@ -1,4 +1,4 @@ -createWithDurations.t.sol +createWithDurationsLL.t.sol ├── when the batch size is zero │ └── it should revert └── when the batch size is not zero diff --git a/test/integration/batch/lockup-tranched/create-with-durations/createWithDurations.t.sol b/test/integration/batch/create-with-durations-lt/createWithDurationsLT.t.sol similarity index 92% rename from test/integration/batch/lockup-tranched/create-with-durations/createWithDurations.t.sol rename to test/integration/batch/create-with-durations-lt/createWithDurationsLT.t.sol index 88ac0984..947a8578 100644 --- a/test/integration/batch/lockup-tranched/create-with-durations/createWithDurations.t.sol +++ b/test/integration/batch/create-with-durations-lt/createWithDurationsLT.t.sol @@ -4,9 +4,9 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; import { Batch } from "src/types/DataTypes.sol"; -import { Integration_Test } from "../../../Integration.t.sol"; +import { Integration_Test } from "../../Integration.t.sol"; -contract CreateWithDurations_LockupTranched_Integration_Test is Integration_Test { +contract CreateWithDurationsLT_Integration_Test is Integration_Test { function setUp() public virtual override { Integration_Test.setUp(); } diff --git a/test/integration/batch/lockup-tranched/create-with-durations/createWithDurations.tree b/test/integration/batch/create-with-durations-lt/createWithDurationsLT.tree similarity index 89% rename from test/integration/batch/lockup-tranched/create-with-durations/createWithDurations.tree rename to test/integration/batch/create-with-durations-lt/createWithDurationsLT.tree index 377110d8..8e67caf2 100644 --- a/test/integration/batch/lockup-tranched/create-with-durations/createWithDurations.tree +++ b/test/integration/batch/create-with-durations-lt/createWithDurationsLT.tree @@ -1,4 +1,4 @@ -createWithDurations.t.sol +createWithDurationsLT.t.sol ├── when the batch size is zero │ └── it should revert └── when the batch size is not zero diff --git a/test/integration/batch/lockup-dynamic/create-with-timestamps/createWithTimestamps.t.sol b/test/integration/batch/create-with-timestamps-ld/createWithTimestampsLD.t.sol similarity index 92% rename from test/integration/batch/lockup-dynamic/create-with-timestamps/createWithTimestamps.t.sol rename to test/integration/batch/create-with-timestamps-ld/createWithTimestampsLD.t.sol index 96213c43..d8525d6c 100644 --- a/test/integration/batch/lockup-dynamic/create-with-timestamps/createWithTimestamps.t.sol +++ b/test/integration/batch/create-with-timestamps-ld/createWithTimestampsLD.t.sol @@ -4,9 +4,9 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; import { Batch } from "src/types/DataTypes.sol"; -import { Integration_Test } from "../../../Integration.t.sol"; +import { Integration_Test } from "../../Integration.t.sol"; -contract CreateWithTimestamps_LockupDynamic_Integration_Test is Integration_Test { +contract CreateWithTimestampsLD_Integration_Test is Integration_Test { function setUp() public virtual override { Integration_Test.setUp(); } diff --git a/test/integration/batch/lockup-linear/create-with-timestamps/createWithTimestamps.tree b/test/integration/batch/create-with-timestamps-ld/createWithTimestampsLD.tree similarity index 88% rename from test/integration/batch/lockup-linear/create-with-timestamps/createWithTimestamps.tree rename to test/integration/batch/create-with-timestamps-ld/createWithTimestampsLD.tree index 7769ecd1..78d71fbd 100644 --- a/test/integration/batch/lockup-linear/create-with-timestamps/createWithTimestamps.tree +++ b/test/integration/batch/create-with-timestamps-ld/createWithTimestampsLD.tree @@ -1,4 +1,4 @@ -createWithTimestamps.t.sol +createWithTimestampsLD.t.sol ├── when the batch size is zero │ └── it should revert └── when the batch size is not zero diff --git a/test/integration/batch/lockup-linear/create-with-timestamps/createWithTimestamps.t.sol b/test/integration/batch/create-with-timestamps-ll/createWithTimestamps.t.sol similarity index 92% rename from test/integration/batch/lockup-linear/create-with-timestamps/createWithTimestamps.t.sol rename to test/integration/batch/create-with-timestamps-ll/createWithTimestamps.t.sol index f4834a9d..976e810b 100644 --- a/test/integration/batch/lockup-linear/create-with-timestamps/createWithTimestamps.t.sol +++ b/test/integration/batch/create-with-timestamps-ll/createWithTimestamps.t.sol @@ -4,9 +4,9 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; import { Batch } from "src/types/DataTypes.sol"; -import { Integration_Test } from "../../../Integration.t.sol"; +import { Integration_Test } from "../../Integration.t.sol"; -contract CreateWithTimestamps_LockupLinear_Integration_Test is Integration_Test { +contract CreateWithTimestampsLL_Integration_Test is Integration_Test { function setUp() public virtual override { Integration_Test.setUp(); } diff --git a/test/integration/batch/lockup-tranched/create-with-timestamps/createWithTimestamps.tree b/test/integration/batch/create-with-timestamps-ll/createWithTimestamps.tree similarity index 88% rename from test/integration/batch/lockup-tranched/create-with-timestamps/createWithTimestamps.tree rename to test/integration/batch/create-with-timestamps-ll/createWithTimestamps.tree index 7769ecd1..e9409127 100644 --- a/test/integration/batch/lockup-tranched/create-with-timestamps/createWithTimestamps.tree +++ b/test/integration/batch/create-with-timestamps-ll/createWithTimestamps.tree @@ -1,4 +1,4 @@ -createWithTimestamps.t.sol +createWithTimestampsLL.t.sol ├── when the batch size is zero │ └── it should revert └── when the batch size is not zero diff --git a/test/integration/batch/lockup-tranched/create-with-timestamps/createWithTimestamps.t.sol b/test/integration/batch/create-with-timestamps-lt/createWithTimestampsLT.t.sol similarity index 92% rename from test/integration/batch/lockup-tranched/create-with-timestamps/createWithTimestamps.t.sol rename to test/integration/batch/create-with-timestamps-lt/createWithTimestampsLT.t.sol index 28528ac9..2bd506c5 100644 --- a/test/integration/batch/lockup-tranched/create-with-timestamps/createWithTimestamps.t.sol +++ b/test/integration/batch/create-with-timestamps-lt/createWithTimestampsLT.t.sol @@ -4,9 +4,9 @@ pragma solidity >=0.8.22 <0.9.0; import { Errors } from "src/libraries/Errors.sol"; import { Batch } from "src/types/DataTypes.sol"; -import { Integration_Test } from "../../../Integration.t.sol"; +import { Integration_Test } from "../../Integration.t.sol"; -contract CreateWithTimestamps_LockupTranched_Integration_Test is Integration_Test { +contract CreateWithTimestampsLT_Integration_Test is Integration_Test { function setUp() public virtual override { Integration_Test.setUp(); } diff --git a/test/integration/batch/lockup-dynamic/create-with-timestamps/createWithTimestamps.tree b/test/integration/batch/create-with-timestamps-lt/createWithTimestampsLT.tree similarity index 88% rename from test/integration/batch/lockup-dynamic/create-with-timestamps/createWithTimestamps.tree rename to test/integration/batch/create-with-timestamps-lt/createWithTimestampsLT.tree index 7769ecd1..66ab5a5a 100644 --- a/test/integration/batch/lockup-dynamic/create-with-timestamps/createWithTimestamps.tree +++ b/test/integration/batch/create-with-timestamps-lt/createWithTimestampsLT.tree @@ -1,4 +1,4 @@ -createWithTimestamps.t.sol +createWithTimestampsLT.t.sol ├── when the batch size is zero │ └── it should revert └── when the batch size is not zero diff --git a/test/integration/merkle-lockup/MerkleLockup.t.sol b/test/integration/merkle-lockup/MerkleLockup.t.sol index 19d2d0ef..832e91c7 100644 --- a/test/integration/merkle-lockup/MerkleLockup.t.sol +++ b/test/integration/merkle-lockup/MerkleLockup.t.sol @@ -10,11 +10,11 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { function setUp() public virtual override { Integration_Test.setUp(); - // Create the default Merkle Lockup contracts. + // Create the default MerkleLockup contracts. merkleLockupLL = createMerkleLockupLL(); merkleLockupLT = createMerkleLockupLT(); - // Fund the Merkle Lockup contracts. + // Fund the MerkleLockup contracts. deal({ token: address(dai), to: address(merkleLockupLL), give: defaults.AGGREGATE_AMOUNT() }); deal({ token: address(dai), to: address(merkleLockupLT), give: defaults.AGGREGATE_AMOUNT() }); } @@ -66,7 +66,7 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { lockupLinear: lockupLinear, streamDurations: defaults.durations(), aggregateAmount: defaults.AGGREGATE_AMOUNT(), - recipientsCount: defaults.RECIPIENTS_COUNT() + recipientCount: defaults.RECIPIENT_COUNT() }); } @@ -117,7 +117,7 @@ abstract contract MerkleLockup_Integration_Test is Integration_Test { lockupTranched: lockupTranched, tranchesWithPercentages: defaults.tranchesWithPercentages(), aggregateAmount: defaults.AGGREGATE_AMOUNT(), - recipientsCount: defaults.RECIPIENTS_COUNT() + recipientCount: defaults.RECIPIENT_COUNT() }); } } diff --git a/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol b/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol index 3bed45f8..616fcfad 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol +++ b/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.t.sol @@ -18,7 +18,7 @@ contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); LockupLinear.Durations memory streamDurations = defaults.durations(); uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); - uint256 recipientsCount = defaults.RECIPIENTS_COUNT(); + uint256 recipientCount = defaults.RECIPIENT_COUNT(); baseParams.name = "this string is longer than 32 characters"; @@ -33,32 +33,33 @@ contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test lockupLinear: lockupLinear, streamDurations: streamDurations, aggregateAmount: aggregateAmount, - recipientsCount: recipientsCount + recipientCount: recipientCount }); } - modifier whenCampaignNameIsNotTooLong() { + modifier whenCampaignNameNotTooLong() { _; } - /// @dev This test works because a default Merkle Lockup contract is deployed in {Integration_Test.setUp} - function test_RevertGiven_AlreadyCreated() external whenCampaignNameIsNotTooLong { + /// @dev This test works because a default MerkleLockup contract is deployed in {Integration_Test.setUp} + function test_RevertGiven_CreatedAlready() external whenCampaignNameNotTooLong { MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); LockupLinear.Durations memory streamDurations = defaults.durations(); uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); - uint256 recipientsCount = defaults.RECIPIENTS_COUNT(); + uint256 recipientCount = defaults.RECIPIENT_COUNT(); + // Expect a revert due to CREATE2. vm.expectRevert(); merkleLockupFactory.createMerkleLockupLL({ baseParams: baseParams, lockupLinear: lockupLinear, streamDurations: streamDurations, aggregateAmount: aggregateAmount, - recipientsCount: recipientsCount + recipientCount: recipientCount }); } - modifier givenNotAlreadyCreated() { + modifier givenNotCreatedAlready() { _; } @@ -67,8 +68,8 @@ contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test uint40 expiration ) external - whenCampaignNameIsNotTooLong - givenNotAlreadyCreated + whenCampaignNameNotTooLong + givenNotCreatedAlready { vm.assume(admin != users.admin); address expectedLockupLL = computeMerkleLockupLLAddress(admin, expiration); @@ -87,11 +88,10 @@ contract CreateMerkleLockupLL_Integration_Test is MerkleLockup_Integration_Test lockupLinear: lockupLinear, streamDurations: defaults.durations(), aggregateAmount: defaults.AGGREGATE_AMOUNT(), - recipientsCount: defaults.RECIPIENTS_COUNT() + recipientCount: defaults.RECIPIENT_COUNT() }); address actualLockupLL = address(createMerkleLockupLL(admin, expiration)); - assertGt(actualLockupLL.code.length, 0, "MerkleLockupLL contract not created"); assertEq(actualLockupLL, expectedLockupLL, "MerkleLockupLL contract does not match computed address"); } diff --git a/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.tree b/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.tree index 82eaa983..a504f83e 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.tree +++ b/test/integration/merkle-lockup/factory/create-merkle-lockup-ll/createMerkleLockupLL.tree @@ -1,9 +1,9 @@ createMerkleLockupLL.t.sol ├── when the campaign name is too long -│ └── it should revert +│ └── it should revert └── when the campaign name is not too long - ├── given the campaign has been already created - │ └── it should revert - └── given the campaign has not been already created - ├── it should create the campaign - └── it should emit a {CreateMerkleLockupLL} event + ├── given the campaign has been created already + │ └── it should revert + └── given the campaign has not been created already + ├── it should create the campaign + └── it should emit a {CreateMerkleLockupLL} event diff --git a/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.t.sol b/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.t.sol index 14313d70..dbe768bc 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.t.sol +++ b/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.t.sol @@ -14,71 +14,65 @@ contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test MerkleLockup_Integration_Test.setUp(); } - modifier whenTotalPercentageIsNotOneHundred() { + modifier whenTotalPercentageNotOneHundred() { _; } - function test_RevertWhen_TotalPercentageLessThanOneHundred() external whenTotalPercentageIsNotOneHundred { + function test_RevertWhen_TotalPercentageLessThanOneHundred() external whenTotalPercentageNotOneHundred { MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); - uint256 recipientsCount = defaults.RECIPIENTS_COUNT(); + uint256 recipientCount = defaults.RECIPIENT_COUNT(); MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); tranchesWithPercentages[0].unlockPercentage = ud2x18(0.05e18); + tranchesWithPercentages[1].unlockPercentage = ud2x18(0.2e18); - uint256 totalPercentage = tranchesWithPercentages[0].unlockPercentage.intoUint256() - + tranchesWithPercentages[1].unlockPercentage.intoUint256(); + uint64 totalPercentage = + tranchesWithPercentages[0].unlockPercentage.unwrap() + tranchesWithPercentages[1].unlockPercentage.unwrap(); vm.expectRevert( abi.encodeWithSelector( - Errors.SablierV2MerkleLockupFactory_TotalPercentageNotEqualOneHundred.selector, totalPercentage + Errors.SablierV2MerkleLockupFactory_TotalPercentageNotOneHundred.selector, totalPercentage ) ); - merkleLockupFactory.createMerkleLockupLT({ - baseParams: baseParams, - lockupTranched: lockupTranched, - tranchesWithPercentages: tranchesWithPercentages, - aggregateAmount: aggregateAmount, - recipientsCount: recipientsCount - }); + merkleLockupFactory.createMerkleLockupLT( + baseParams, lockupTranched, tranchesWithPercentages, aggregateAmount, recipientCount + ); } - function test_RevertWhen_TotalPercentageGreaterThanOneHundred() external whenTotalPercentageIsNotOneHundred { + function test_RevertWhen_TotalPercentageGreaterThanOneHundred() external whenTotalPercentageNotOneHundred { MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); - uint256 recipientsCount = defaults.RECIPIENTS_COUNT(); + uint256 recipientCount = defaults.RECIPIENT_COUNT(); MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); tranchesWithPercentages[0].unlockPercentage = ud2x18(0.75e18); + tranchesWithPercentages[1].unlockPercentage = ud2x18(0.8e18); - uint256 totalPercentage = tranchesWithPercentages[0].unlockPercentage.intoUint256() - + tranchesWithPercentages[1].unlockPercentage.intoUint256(); + uint64 totalPercentage = + tranchesWithPercentages[0].unlockPercentage.unwrap() + tranchesWithPercentages[1].unlockPercentage.unwrap(); vm.expectRevert( abi.encodeWithSelector( - Errors.SablierV2MerkleLockupFactory_TotalPercentageNotEqualOneHundred.selector, totalPercentage + Errors.SablierV2MerkleLockupFactory_TotalPercentageNotOneHundred.selector, totalPercentage ) ); - merkleLockupFactory.createMerkleLockupLT({ - baseParams: baseParams, - lockupTranched: lockupTranched, - tranchesWithPercentages: tranchesWithPercentages, - aggregateAmount: aggregateAmount, - recipientsCount: recipientsCount - }); + merkleLockupFactory.createMerkleLockupLT( + baseParams, lockupTranched, tranchesWithPercentages, aggregateAmount, recipientCount + ); } - modifier whenTotalPercentageIsOneHundred() { + modifier whenTotalPercentageOneHundred() { _; } - function test_RevertWhen_CampaignNameTooLong() external whenTotalPercentageIsOneHundred { + function test_RevertWhen_CampaignNameTooLong() external whenTotalPercentageOneHundred { MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); - uint256 recipientsCount = defaults.RECIPIENTS_COUNT(); + uint256 recipientCount = defaults.RECIPIENT_COUNT(); baseParams.name = "this string is longer than 32 characters"; @@ -88,37 +82,30 @@ contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test ) ); - merkleLockupFactory.createMerkleLockupLT({ - baseParams: baseParams, - lockupTranched: lockupTranched, - tranchesWithPercentages: tranchesWithPercentages, - aggregateAmount: aggregateAmount, - recipientsCount: recipientsCount - }); + merkleLockupFactory.createMerkleLockupLT( + baseParams, lockupTranched, tranchesWithPercentages, aggregateAmount, recipientCount + ); } - modifier whenCampaignNameIsNotTooLong() { + modifier whenCampaignNameNotTooLong() { _; } - /// @dev This test works because a default Merkle Lockup contract is deployed in {Integration_Test.setUp} - function test_RevertGiven_AlreadyCreated() external whenTotalPercentageIsOneHundred whenCampaignNameIsNotTooLong { + /// @dev This test works because a default MerkleLockup contract is deployed in {Integration_Test.setUp} + function test_RevertGiven_CreatedAlready() external whenTotalPercentageOneHundred whenCampaignNameNotTooLong { MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); MerkleLockupLT.TrancheWithPercentage[] memory tranchesWithPercentages = defaults.tranchesWithPercentages(); uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT(); - uint256 recipientsCount = defaults.RECIPIENTS_COUNT(); + uint256 recipientCount = defaults.RECIPIENT_COUNT(); + // Expect a revert due to CREATE2. vm.expectRevert(); - merkleLockupFactory.createMerkleLockupLT({ - baseParams: baseParams, - lockupTranched: lockupTranched, - tranchesWithPercentages: tranchesWithPercentages, - aggregateAmount: aggregateAmount, - recipientsCount: recipientsCount - }); + merkleLockupFactory.createMerkleLockupLT( + baseParams, lockupTranched, tranchesWithPercentages, aggregateAmount, recipientCount + ); } - modifier givenNotAlreadyCreated() { + modifier givenNotCreatedAlready() { _; } @@ -127,9 +114,9 @@ contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test uint40 expiration ) external - whenTotalPercentageIsOneHundred - whenCampaignNameIsNotTooLong - givenNotAlreadyCreated + whenTotalPercentageOneHundred + whenCampaignNameNotTooLong + givenNotCreatedAlready { vm.assume(admin != users.admin); address expectedLockupLT = computeMerkleLockupLTAddress(admin, expiration); @@ -149,11 +136,10 @@ contract CreateMerkleLockupLT_Integration_Test is MerkleLockup_Integration_Test tranchesWithPercentages: defaults.tranchesWithPercentages(), totalDuration: defaults.TOTAL_DURATION(), aggregateAmount: defaults.AGGREGATE_AMOUNT(), - recipientsCount: defaults.RECIPIENTS_COUNT() + recipientCount: defaults.RECIPIENT_COUNT() }); address actualLockupLT = address(createMerkleLockupLT(admin, expiration)); - assertGt(actualLockupLT.code.length, 0, "MerkleLockupLT contract not created"); assertEq(actualLockupLT, expectedLockupLT, "MerkleLockupLT contract does not match computed address"); } diff --git a/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.tree b/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.tree index e319653d..108a4625 100644 --- a/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.tree +++ b/test/integration/merkle-lockup/factory/create-merkle-lockup-lt/createMerkleLockupLT.tree @@ -1,15 +1,15 @@ createMerkleLockupLL.t.sol ├── when the total percentage does not equal 100% -│ ├── when the total percentage is less than 100% -│ │ └── it should revert -│ └── when the total percentage is greater than 100% -│ └── it should revert +│ ├── when the total percentage is less than 100% +│ │ └── it should revert +│ └── when the total percentage is greater than 100% +│ └── it should revert └── when the total percentage equals 100% - ├── when the campaign name is too long - │ └── it should revert - └── when the campaign name is not too long - ├── given the campaign has been already created - │ └── it should revert - └── given the campaign has not been already created - ├── it should create the campaign - └── it should emit a {CreateMerkleLockupLT} event + ├── when the campaign name is too long + │ └── it should revert + └── when the campaign name is not too long + ├── given the campaign has been already created + │ └── it should revert + └── given the campaign has not been already created + ├── it should create the campaign + └── it should emit a {CreateMerkleLockupLT} event diff --git a/test/integration/merkle-lockup/ll/claim/claim.t.sol b/test/integration/merkle-lockup/ll/claim/claim.t.sol index 1a77f244..7e2bf72b 100644 --- a/test/integration/merkle-lockup/ll/claim/claim.t.sol +++ b/test/integration/merkle-lockup/ll/claim/claim.t.sol @@ -112,15 +112,15 @@ contract Claim_Integration_Test is MerkleLockup_Integration_Test { LockupLinear.StreamLL memory expectedStream = LockupLinear.StreamLL({ amounts: Lockup.Amounts({ deposited: defaults.CLAIM_AMOUNT(), refunded: 0, withdrawn: 0 }), asset: dai, - cliffTime: uint40(block.timestamp) + defaults.CLIFF_DURATION(), - endTime: uint40(block.timestamp) + defaults.TOTAL_DURATION(), + cliffTime: getBlockTimestamp() + defaults.CLIFF_DURATION(), + endTime: getBlockTimestamp() + defaults.TOTAL_DURATION(), isCancelable: defaults.CANCELABLE(), isDepleted: false, isStream: true, isTransferable: defaults.TRANSFERABLE(), recipient: users.recipient1, sender: users.admin, - startTime: uint40(block.timestamp), + startTime: getBlockTimestamp(), wasCanceled: false }); diff --git a/test/integration/merkle-lockup/ll/claim/claim.tree b/test/integration/merkle-lockup/ll/claim/claim.tree index 35461bb2..b6122178 100644 --- a/test/integration/merkle-lockup/ll/claim/claim.tree +++ b/test/integration/merkle-lockup/ll/claim/claim.tree @@ -16,5 +16,5 @@ claim.t.sol │ └── it should revert └── given the claim is included in the Merkle tree ├── it should mark the index as claimed - ├── it should create a LockupLinear stream + ├── it should create a stream └── it should emit a {Claim} event diff --git a/test/integration/merkle-lockup/ll/constructor/constructor.t.sol b/test/integration/merkle-lockup/ll/constructor/constructor.t.sol index fcf2aba6..59ae4711 100644 --- a/test/integration/merkle-lockup/ll/constructor/constructor.t.sol +++ b/test/integration/merkle-lockup/ll/constructor/constructor.t.sol @@ -16,22 +16,22 @@ contract Constructor_MerkleLockupLL_Integration_Test is MerkleLockup_Integration string actualIpfsCID; string actualName; bool actualCancelable; - bool actualTransferable; LockupLinear.Durations actualDurations; uint40 actualExpiration; address actualLockupLinear; bytes32 actualMerkleRoot; + bool actualTransferable; address expectedAdmin; uint256 expectedAllowance; address expectedAsset; - string expectedIpfsCID; - bytes32 expectedName; bool expectedCancelable; - bool expectedTransferable; LockupLinear.Durations expectedDurations; uint40 expectedExpiration; + string expectedIpfsCID; address expectedLockupLinear; bytes32 expectedMerkleRoot; + bytes32 expectedName; + bool expectedTransferable; } function test_Constructor() external { @@ -44,45 +44,45 @@ contract Constructor_MerkleLockupLL_Integration_Test is MerkleLockup_Integration vars.expectedAdmin = users.admin; assertEq(vars.actualAdmin, vars.expectedAdmin, "admin"); + vars.actualAllowance = dai.allowance(address(constructedLockupLL), address(lockupLinear)); + vars.expectedAllowance = MAX_UINT256; + assertEq(vars.actualAllowance, vars.expectedAllowance, "allowance"); + vars.actualAsset = address(constructedLockupLL.ASSET()); vars.expectedAsset = address(dai); assertEq(vars.actualAsset, vars.expectedAsset, "asset"); - vars.actualName = constructedLockupLL.name(); - vars.expectedName = defaults.NAME_BYTES32(); - assertEq(bytes32(abi.encodePacked(vars.actualName)), vars.expectedName, "name"); - - vars.actualMerkleRoot = constructedLockupLL.MERKLE_ROOT(); - vars.expectedMerkleRoot = defaults.MERKLE_ROOT(); - assertEq(vars.actualMerkleRoot, vars.expectedMerkleRoot, "merkleRoot"); - vars.actualCancelable = constructedLockupLL.CANCELABLE(); vars.expectedCancelable = defaults.CANCELABLE(); assertEq(vars.actualCancelable, vars.expectedCancelable, "cancelable"); - vars.actualTransferable = constructedLockupLL.TRANSFERABLE(); - vars.expectedTransferable = defaults.TRANSFERABLE(); - assertEq(vars.actualTransferable, vars.expectedTransferable, "transferable"); + (vars.actualDurations.cliff, vars.actualDurations.total) = constructedLockupLL.streamDurations(); + vars.expectedDurations = defaults.durations(); + assertEq(vars.actualDurations.cliff, vars.expectedDurations.cliff, "durations.cliff"); + assertEq(vars.actualDurations.total, vars.expectedDurations.total, "durations.total"); vars.actualExpiration = constructedLockupLL.EXPIRATION(); vars.expectedExpiration = defaults.EXPIRATION(); assertEq(vars.actualExpiration, vars.expectedExpiration, "expiration"); + vars.actualIpfsCID = constructedLockupLL.ipfsCID(); + vars.expectedIpfsCID = defaults.IPFS_CID(); + assertEq(vars.actualIpfsCID, vars.expectedIpfsCID, "ipfsCID"); + vars.actualLockupLinear = address(constructedLockupLL.LOCKUP_LINEAR()); vars.expectedLockupLinear = address(lockupLinear); assertEq(vars.actualLockupLinear, vars.expectedLockupLinear, "lockupLinear"); - (vars.actualDurations.cliff, vars.actualDurations.total) = constructedLockupLL.streamDurations(); - vars.expectedDurations = defaults.durations(); - assertEq(vars.actualDurations.cliff, vars.expectedDurations.cliff, "durations.cliff"); - assertEq(vars.actualDurations.total, vars.expectedDurations.total, "durations.total"); + vars.actualMerkleRoot = constructedLockupLL.MERKLE_ROOT(); + vars.expectedMerkleRoot = defaults.MERKLE_ROOT(); + assertEq(vars.actualMerkleRoot, vars.expectedMerkleRoot, "merkleRoot"); - vars.actualAllowance = dai.allowance(address(constructedLockupLL), address(lockupLinear)); - vars.expectedAllowance = MAX_UINT256; - assertEq(vars.actualAllowance, vars.expectedAllowance, "allowance"); + vars.actualName = constructedLockupLL.name(); + vars.expectedName = defaults.NAME_BYTES32(); + assertEq(bytes32(abi.encodePacked(vars.actualName)), vars.expectedName, "name"); - vars.actualIpfsCID = constructedLockupLL.ipfsCID(); - vars.expectedIpfsCID = defaults.IPFS_CID(); - assertEq(vars.actualIpfsCID, vars.expectedIpfsCID, "ipfsCID"); + vars.actualTransferable = constructedLockupLL.TRANSFERABLE(); + vars.expectedTransferable = defaults.TRANSFERABLE(); + assertEq(vars.actualTransferable, vars.expectedTransferable, "transferable"); } } diff --git a/test/integration/merkle-lockup/ll/has-expired/hasExpired.t.sol b/test/integration/merkle-lockup/ll/has-expired/hasExpired.t.sol index 5480efcc..5b463a88 100644 --- a/test/integration/merkle-lockup/ll/has-expired/hasExpired.t.sol +++ b/test/integration/merkle-lockup/ll/has-expired/hasExpired.t.sol @@ -15,20 +15,20 @@ contract HasExpired_Integration_Test is MerkleLockup_Integration_Test { assertFalse(testLockup.hasExpired(), "campaign expired"); } - modifier whenExpirationNotZero() { + modifier givenExpirationNotZero() { _; } - function test_HasExpired_ExpirationLessThanCurrentTime() external view whenExpirationNotZero { + function test_HasExpired_ExpirationLessThanBlockTimestamp() external view givenExpirationNotZero { assertFalse(merkleLockupLL.hasExpired(), "campaign expired"); } - function test_HasExpired_ExpirationEqualToCurrentTime() external whenExpirationNotZero { + function test_HasExpired_ExpirationEqualToBlockTimestamp() external givenExpirationNotZero { vm.warp({ newTimestamp: defaults.EXPIRATION() }); assertTrue(merkleLockupLL.hasExpired(), "campaign not expired"); } - function test_HasExpired_ExpirationGreaterThanCurrentTime() external whenExpirationNotZero { + function test_HasExpired_ExpirationGreaterThanBlockTimestamp() external givenExpirationNotZero { vm.warp({ newTimestamp: defaults.EXPIRATION() + 1 seconds }); assertTrue(merkleLockupLL.hasExpired(), "campaign not expired"); } diff --git a/test/integration/merkle-lockup/ll/has-expired/hasExpired.tree b/test/integration/merkle-lockup/ll/has-expired/hasExpired.tree index 18fd5766..5fd22925 100644 --- a/test/integration/merkle-lockup/ll/has-expired/hasExpired.tree +++ b/test/integration/merkle-lockup/ll/has-expired/hasExpired.tree @@ -2,9 +2,9 @@ hasExpired.t.sol ├── when the expiration is zero │ └── it should return false └── when the expiration is not zero - ├── when the expiration is less than current time + ├── when the expiration is less than the block timestamp │ └── it should return false - ├── when the expiration is equal to the current time + ├── when the expiration is equal to the block timestamp │ └── it should return true - └── when the expiration is greater than current time + └── when the expiration is greater than the block timestamp └── it should return true diff --git a/test/integration/merkle-lockup/lt/claim/claim.t.sol b/test/integration/merkle-lockup/lt/claim/claim.t.sol index 6e50263c..86083471 100644 --- a/test/integration/merkle-lockup/lt/claim/claim.t.sol +++ b/test/integration/merkle-lockup/lt/claim/claim.t.sol @@ -109,91 +109,99 @@ contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test { _; } - // Needed this variable in storage due to how the imported libaries work. + /// @dev Needed this variable in storage due to how the imported libraries work. uint256[] public leaves = new uint256[](4); // same number of recipients as in Defaults - function test_Claim_TrancheAmountsSumNotEqualClaimAmount() + function test_Claim_CalculatedAmountsSumNotEqualClaimAmount() external givenCampaignNotExpired givenNotClaimed givenIncludedInMerkleTree { - // Declare an amount that will cause a rounding error. + // Declare a claim amount that will cause a rounding error. uint128 claimAmount = defaults.CLAIM_AMOUNT() + 1; - uint256 aggregateAmount = defaults.AGGREGATE_AMOUNT() + 1; - // Compute the Merkle tree. + // Compute the test Merkle tree. leaves = defaults.getLeaves(); uint256 leaf = MerkleBuilder.computeLeaf(defaults.INDEX1(), users.recipient1, claimAmount); leaves[0] = leaf; MerkleBuilder.sortLeaves(leaves); - bytes32 merkleRoot = getRoot(leaves.toBytes32()); - // Compute the Merkle proof. + // Compute the test Merkle proof. uint256 pos = Arrays.findUpperBound(leaves, leaf); bytes32[] memory proof = getProof(leaves.toBytes32(), pos); /// Declare the constructor params. MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams(); - baseParams.merkleRoot = merkleRoot; - - // Deploy the new MerkleLockupLT contract. - ISablierV2MerkleLockupLT _merkleLockupLT = merkleLockupFactory.createMerkleLockupLT( - baseParams, lockupTranched, defaults.tranchesWithPercentages(), aggregateAmount, defaults.RECIPIENTS_COUNT() + baseParams.merkleRoot = getRoot(leaves.toBytes32()); + + // Deploy a test MerkleLockupLT contract. + ISablierV2MerkleLockupLT testMerkleLT = merkleLockupFactory.createMerkleLockupLT( + baseParams, + lockupTranched, + defaults.tranchesWithPercentages(), + defaults.AGGREGATE_AMOUNT(), + defaults.RECIPIENT_COUNT() ); // Fund the MerkleLockupLT contract. - deal({ token: address(dai), to: address(_merkleLockupLT), give: aggregateAmount }); + deal({ token: address(dai), to: address(testMerkleLT), give: defaults.AGGREGATE_AMOUNT() }); uint256 expectedStreamId = lockupTranched.nextStreamId(); - - vm.expectEmit({ emitter: address(_merkleLockupLT) }); + vm.expectEmit({ emitter: address(testMerkleLT) }); emit Claim(defaults.INDEX1(), users.recipient1, claimAmount, expectedStreamId); - uint256 actualStreamId = _merkleLockupLT.claim(defaults.INDEX1(), users.recipient1, claimAmount, proof); + uint256 actualStreamId = testMerkleLT.claim(defaults.INDEX1(), users.recipient1, claimAmount, proof); LockupTranched.StreamLT memory actualStream = lockupTranched.getStream(actualStreamId); LockupTranched.StreamLT memory expectedStream = LockupTranched.StreamLT({ amounts: Lockup.Amounts({ deposited: claimAmount, refunded: 0, withdrawn: 0 }), asset: dai, - endTime: uint40(block.timestamp) + defaults.TOTAL_DURATION(), + endTime: getBlockTimestamp() + defaults.TOTAL_DURATION(), isCancelable: defaults.CANCELABLE(), isDepleted: false, isStream: true, isTransferable: defaults.TRANSFERABLE(), recipient: users.recipient1, sender: users.admin, - startTime: uint40(block.timestamp), + startTime: getBlockTimestamp(), tranches: defaults.tranches(claimAmount), wasCanceled: false }); - assertTrue(_merkleLockupLT.hasClaimed(defaults.INDEX1()), "not claimed"); + assertTrue(testMerkleLT.hasClaimed(defaults.INDEX1()), "not claimed"); assertEq(actualStreamId, expectedStreamId, "invalid stream id"); assertEq(actualStream, expectedStream); } - function test_Claim() external givenCampaignNotExpired givenNotClaimed givenIncludedInMerkleTree { - uint256 expectedStreamId = lockupTranched.nextStreamId(); + modifier whenCalculatedAmountsSumEqualsClaimAmount() { + _; + } + function test_Claim() + external + givenCampaignNotExpired + givenNotClaimed + givenIncludedInMerkleTree + whenCalculatedAmountsSumEqualsClaimAmount + { + uint256 expectedStreamId = lockupTranched.nextStreamId(); vm.expectEmit({ emitter: address(merkleLockupLT) }); emit Claim(defaults.INDEX1(), users.recipient1, defaults.CLAIM_AMOUNT(), expectedStreamId); - uint256 actualStreamId = claimLT(); - - LockupTranched.Tranche[] memory tranches = defaults.tranches(); + uint256 actualStreamId = claimLT(); LockupTranched.StreamLT memory actualStream = lockupTranched.getStream(actualStreamId); LockupTranched.StreamLT memory expectedStream = LockupTranched.StreamLT({ amounts: Lockup.Amounts({ deposited: defaults.CLAIM_AMOUNT(), refunded: 0, withdrawn: 0 }), asset: dai, - endTime: uint40(block.timestamp) + defaults.TOTAL_DURATION(), + endTime: getBlockTimestamp() + defaults.TOTAL_DURATION(), isCancelable: defaults.CANCELABLE(), isDepleted: false, isStream: true, isTransferable: defaults.TRANSFERABLE(), recipient: users.recipient1, sender: users.admin, - startTime: uint40(block.timestamp), - tranches: tranches, + startTime: getBlockTimestamp(), + tranches: defaults.tranches(), wasCanceled: false }); diff --git a/test/integration/merkle-lockup/lt/claim/claim.tree b/test/integration/merkle-lockup/lt/claim/claim.tree index ae9d8682..390d2248 100644 --- a/test/integration/merkle-lockup/lt/claim/claim.tree +++ b/test/integration/merkle-lockup/lt/claim/claim.tree @@ -15,12 +15,12 @@ claim.t.sol │ └── when the Merkle proof is not valid │ └── it should revert └── given the claim is included in the Merkle tree - ├── when the sum of the tranches' amounts does not equal the claim amount + ├── when the sum of the calculated amounts does not equal the claim amount │ ├── it should adjust the last tranche amount │ ├── it should mark the index as claimed - │ ├── it should create a LockupTranched stream + │ ├── it should create a stream │ └── it should emit a {Claim} event - └── when the sum of the tranches' amounts equals the claim amount + └── when the sum of the calculated amounts equals the claim amount ├── it should mark the index as claimed - ├── it should create a LockupTranched stream + ├── it should create a stream └── it should emit a {Claim} event diff --git a/test/integration/merkle-lockup/lt/constructor/constructor.t.sol b/test/integration/merkle-lockup/lt/constructor/constructor.t.sol index 7ab96e41..f8de89ac 100644 --- a/test/integration/merkle-lockup/lt/constructor/constructor.t.sol +++ b/test/integration/merkle-lockup/lt/constructor/constructor.t.sol @@ -15,22 +15,22 @@ contract Constructor_MerkleLockupLT_Integration_Test is MerkleLockup_Integration string actualIpfsCID; string actualName; bool actualCancelable; - bool actualTransferable; - MerkleLockupLT.TrancheWithPercentage[] actualTranchesWithPercentages; uint40 actualExpiration; address actualLockupTranched; bytes32 actualMerkleRoot; + MerkleLockupLT.TrancheWithPercentage[] actualTranchesWithPercentages; + bool actualTransferable; address expectedAdmin; uint256 expectedAllowance; address expectedAsset; - string expectedIpfsCID; - bytes32 expectedName; bool expectedCancelable; - bool expectedTransferable; - MerkleLockupLT.TrancheWithPercentage[] expectedTranchesWithPercentages; + string expectedIpfsCID; uint40 expectedExpiration; address expectedLockupTranched; bytes32 expectedMerkleRoot; + bytes32 expectedName; + MerkleLockupLT.TrancheWithPercentage[] expectedTranchesWithPercentages; + bool expectedTransferable; } function test_Constructor() external { @@ -43,44 +43,44 @@ contract Constructor_MerkleLockupLT_Integration_Test is MerkleLockup_Integration vars.expectedAdmin = users.admin; assertEq(vars.actualAdmin, vars.expectedAdmin, "admin"); + vars.actualAllowance = dai.allowance(address(constructedLockupLT), address(lockupTranched)); + vars.expectedAllowance = MAX_UINT256; + assertEq(vars.actualAllowance, vars.expectedAllowance, "allowance"); + vars.actualAsset = address(constructedLockupLT.ASSET()); vars.expectedAsset = address(dai); assertEq(vars.actualAsset, vars.expectedAsset, "asset"); - vars.actualName = constructedLockupLT.name(); - vars.expectedName = defaults.NAME_BYTES32(); - assertEq(bytes32(abi.encodePacked(vars.actualName)), vars.expectedName, "name"); - - vars.actualMerkleRoot = constructedLockupLT.MERKLE_ROOT(); - vars.expectedMerkleRoot = defaults.MERKLE_ROOT(); - assertEq(vars.actualMerkleRoot, vars.expectedMerkleRoot, "merkleRoot"); - vars.actualCancelable = constructedLockupLT.CANCELABLE(); vars.expectedCancelable = defaults.CANCELABLE(); assertEq(vars.actualCancelable, vars.expectedCancelable, "cancelable"); - vars.actualTransferable = constructedLockupLT.TRANSFERABLE(); - vars.expectedTransferable = defaults.TRANSFERABLE(); - assertEq(vars.actualTransferable, vars.expectedTransferable, "transferable"); - vars.actualExpiration = constructedLockupLT.EXPIRATION(); vars.expectedExpiration = defaults.EXPIRATION(); assertEq(vars.actualExpiration, vars.expectedExpiration, "expiration"); + vars.actualIpfsCID = constructedLockupLT.ipfsCID(); + vars.expectedIpfsCID = defaults.IPFS_CID(); + assertEq(vars.actualIpfsCID, vars.expectedIpfsCID, "ipfsCID"); + vars.actualLockupTranched = address(constructedLockupLT.LOCKUP_TRANCHED()); vars.expectedLockupTranched = address(lockupTranched); - assertEq(vars.actualLockupTranched, vars.expectedLockupTranched, "LockupTranched"); + assertEq(vars.actualLockupTranched, vars.expectedLockupTranched, "lockupTranched"); + + vars.actualName = constructedLockupLT.name(); + vars.expectedName = defaults.NAME_BYTES32(); + assertEq(bytes32(abi.encodePacked(vars.actualName)), vars.expectedName, "name"); + + vars.actualMerkleRoot = constructedLockupLT.MERKLE_ROOT(); + vars.expectedMerkleRoot = defaults.MERKLE_ROOT(); + assertEq(vars.actualMerkleRoot, vars.expectedMerkleRoot, "merkleRoot"); vars.actualTranchesWithPercentages = constructedLockupLT.getTranchesWithPercentages(); vars.expectedTranchesWithPercentages = defaults.tranchesWithPercentages(); - assertEq(vars.actualTranchesWithPercentages, vars.expectedTranchesWithPercentages); - - vars.actualAllowance = dai.allowance(address(constructedLockupLT), address(lockupTranched)); - vars.expectedAllowance = MAX_UINT256; - assertEq(vars.actualAllowance, vars.expectedAllowance, "allowance"); + assertEq(vars.actualTranchesWithPercentages, vars.expectedTranchesWithPercentages, "tranchesWithPercentages"); - vars.actualIpfsCID = constructedLockupLT.ipfsCID(); - vars.expectedIpfsCID = defaults.IPFS_CID(); - assertEq(vars.actualIpfsCID, vars.expectedIpfsCID, "ipfsCID"); + vars.actualTransferable = constructedLockupLT.TRANSFERABLE(); + vars.expectedTransferable = defaults.TRANSFERABLE(); + assertEq(vars.actualTransferable, vars.expectedTransferable, "transferable"); } } diff --git a/test/integration/merkle-lockup/lt/has-expired/hasExpired.t.sol b/test/integration/merkle-lockup/lt/has-expired/hasExpired.t.sol index eb5d747d..c1775927 100644 --- a/test/integration/merkle-lockup/lt/has-expired/hasExpired.t.sol +++ b/test/integration/merkle-lockup/lt/has-expired/hasExpired.t.sol @@ -15,20 +15,20 @@ contract HasExpired_Integration_Test is MerkleLockup_Integration_Test { assertFalse(testLockup.hasExpired(), "campaign expired"); } - modifier whenExpirationNotZero() { + modifier givenExpirationNotZero() { _; } - function test_HasExpired_ExpirationLessThanCurrentTime() external view whenExpirationNotZero { + function test_HasExpired_ExpirationLessThanBlockTimestamp() external view givenExpirationNotZero { assertFalse(merkleLockupLT.hasExpired(), "campaign expired"); } - function test_HasExpired_ExpirationEqualToCurrentTime() external whenExpirationNotZero { + function test_HasExpired_ExpirationEqualToBlockTimestamp() external givenExpirationNotZero { vm.warp({ newTimestamp: defaults.EXPIRATION() }); assertTrue(merkleLockupLT.hasExpired(), "campaign not expired"); } - function test_HasExpired_ExpirationGreaterThanCurrentTime() external whenExpirationNotZero { + function test_HasExpired_ExpirationGreaterThanBlockTimestamp() external givenExpirationNotZero { vm.warp({ newTimestamp: defaults.EXPIRATION() + 1 seconds }); assertTrue(merkleLockupLT.hasExpired(), "campaign not expired"); } diff --git a/test/integration/merkle-lockup/lt/has-expired/hasExpired.tree b/test/integration/merkle-lockup/lt/has-expired/hasExpired.tree index 18fd5766..2a359a81 100644 --- a/test/integration/merkle-lockup/lt/has-expired/hasExpired.tree +++ b/test/integration/merkle-lockup/lt/has-expired/hasExpired.tree @@ -1,10 +1,10 @@ hasExpired.t.sol -├── when the expiration is zero +├── given the expiration is zero │ └── it should return false -└── when the expiration is not zero - ├── when the expiration is less than current time +└── given the expiration is not zero + ├── given the expiration is less than the block timestamp │ └── it should return false - ├── when the expiration is equal to the current time + ├── given the expiration is equal to the block timestamp │ └── it should return true - └── when the expiration is greater than current time + └── given the expiration is greater than the block timestamp └── it should return true diff --git a/test/utils/Assertions.sol b/test/utils/Assertions.sol index c78b0410..16866028 100644 --- a/test/utils/Assertions.sol +++ b/test/utils/Assertions.sol @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-or-later +// solhint-disable event-name-camelcase pragma solidity >=0.8.22; import { PRBMathAssertions } from "@prb/math/test/utils/Assertions.sol"; @@ -8,7 +9,7 @@ import { MerkleLockupLT } from "src/types/DataTypes.sol"; abstract contract Assertions is PRBMathAssertions { event log_named_array(string key, MerkleLockupLT.TrancheWithPercentage[] tranchesWithPercentages); - /// @dev Compares two {MerkleLockupLT.TrancheWithPercentage[]} arrays. + /// @dev Compares two {MerkleLockupLT.TrancheWithPercentage} arrays. function assertEq( MerkleLockupLT.TrancheWithPercentage[] memory a, MerkleLockupLT.TrancheWithPercentage[] memory b @@ -22,4 +23,18 @@ abstract contract Assertions is PRBMathAssertions { fail(); } } + + /// @dev Compares two {MerkleLockupLT.TrancheWithPercentage} arrays. + function assertEq( + MerkleLockupLT.TrancheWithPercentage[] memory a, + MerkleLockupLT.TrancheWithPercentage[] memory b, + string memory err + ) + internal + { + if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { + emit log_named_string("Error", err); + assertEq(a, b); + } + } } diff --git a/test/utils/Defaults.sol b/test/utils/Defaults.sol index 0d583cd0..2bfba0f4 100644 --- a/test/utils/Defaults.sol +++ b/test/utils/Defaults.sol @@ -40,7 +40,7 @@ contract Defaults is Merkle { MERKLE-LOCKUP //////////////////////////////////////////////////////////////////////////*/ - uint256 public constant AGGREGATE_AMOUNT = CLAIM_AMOUNT * RECIPIENTS_COUNT; + uint256 public constant AGGREGATE_AMOUNT = CLAIM_AMOUNT * RECIPIENT_COUNT; bool public constant CANCELABLE = false; uint128 public constant CLAIM_AMOUNT = 10_000e18; uint40 public immutable EXPIRATION; @@ -49,12 +49,12 @@ contract Defaults is Merkle { uint256 public constant INDEX3 = 3; uint256 public constant INDEX4 = 4; string public constant IPFS_CID = "QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"; - uint256 public constant RECIPIENTS_COUNT = 4; - bool public constant TRANSFERABLE = false; - uint256[] public LEAVES = new uint256[](RECIPIENTS_COUNT); + uint256[] public LEAVES = new uint256[](RECIPIENT_COUNT); + uint256 public constant RECIPIENT_COUNT = 4; bytes32 public immutable MERKLE_ROOT; string public constant NAME = "Airdrop Campaign"; bytes32 public constant NAME_BYTES32 = bytes32(abi.encodePacked("Airdrop Campaign")); + bool public constant TRANSFERABLE = false; /*////////////////////////////////////////////////////////////////////////// VARIABLES diff --git a/test/utils/Events.sol b/test/utils/Events.sol index 5c9441af..f27d8d13 100644 --- a/test/utils/Events.sol +++ b/test/utils/Events.sol @@ -19,7 +19,7 @@ abstract contract Events { ISablierV2LockupLinear lockupLinear, LockupLinear.Durations streamDurations, uint256 aggregateAmount, - uint256 recipientsCount + uint256 recipientCount ); event CreateMerkleLockupLT( ISablierV2MerkleLockupLT indexed merkleLockupLT, @@ -28,6 +28,6 @@ abstract contract Events { MerkleLockupLT.TrancheWithPercentage[] tranchesWithPercentages, uint256 totalDuration, uint256 aggregateAmount, - uint256 recipientsCount + uint256 recipientCount ); } From c874f2e14d07ed94ccf66768e52c86f3f1017cd9 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Thu, 11 Apr 2024 14:47:50 +0100 Subject: [PATCH 37/61] refactor: LockupLL to LL and LockupLT to LT --- SECURITY.md | 2 +- bun.lockb | Bin 308178 -> 307932 bytes foundry.toml | 4 +- precompiles/Precompiles.sol | 2 +- ...kleLockupLL.s.sol => CreateMerkleLL.s.sol} | 16 ++-- ...kleLockupLT.s.sol => CreateMerkleLT.s.sol} | 20 ++--- shell/prepare-artifacts.sh | 8 +- ...rkleLockupLL.sol => SablierV2MerkleLL.sol} | 16 ++-- ...rkleLockupLT.sol => SablierV2MerkleLT.sol} | 31 +++---- src/SablierV2MerkleLockupFactory.sol | 32 ++++--- ...kleLockupLL.sol => ISablierV2MerkleLL.sol} | 4 +- ...kleLockupLT.sol => ISablierV2MerkleLT.sol} | 8 +- .../ISablierV2MerkleLockupFactory.sol | 38 ++++---- src/types/DataTypes.sol | 2 +- test/Base.t.sol | 50 +++++------ test/fork/assets/USDC.t.sol | 8 +- test/fork/assets/USDT.t.sol | 8 +- .../{MerkleLockupLL.t.sol => MerkleLL.t.sol} | 41 ++++----- .../{MerkleLockupLT.t.sol => MerkleLT.t.sol} | 41 ++++----- .../merkle-lockup/MerkleLockup.t.sol | 84 +++++++++--------- .../createMerkleLL.t.sol} | 22 ++--- .../createMerkleLL.tree} | 4 +- .../createMerkleLT.t.sol} | 36 ++++---- .../createMerkleLT.tree} | 4 +- .../merkle-lockup/ll/claim/claim.t.sol | 16 ++-- .../merkle-lockup/ll/clawback/clawback.t.sol | 10 +-- .../ll/constructor/constructor.t.sol | 30 +++---- .../ll/has-claimed/hasClaimed.t.sol | 6 +- .../ll/has-expired/hasExpired.t.sol | 10 +-- .../merkle-lockup/lt/claim/claim.t.sol | 24 ++--- .../merkle-lockup/lt/clawback/clawback.t.sol | 10 +-- .../lt/constructor/constructor.t.sol | 36 ++++---- .../lt/has-claimed/hasClaimed.t.sol | 6 +- .../lt/has-expired/hasExpired.t.sol | 10 +-- test/utils/Assertions.sol | 21 ++--- test/utils/Defaults.sol | 12 +-- test/utils/Events.sol | 16 ++-- 37 files changed, 332 insertions(+), 356 deletions(-) rename script/{CreateMerkleLockupLL.s.sol => CreateMerkleLL.s.sol} (74%) rename script/{CreateMerkleLockupLT.s.sol => CreateMerkleLT.s.sol} (66%) rename src/{SablierV2MerkleLockupLL.sol => SablierV2MerkleLL.sol} (89%) rename src/{SablierV2MerkleLockupLT.sol => SablierV2MerkleLT.sol} (87%) rename src/interfaces/{ISablierV2MerkleLockupLL.sol => ISablierV2MerkleLL.sol} (95%) rename src/interfaces/{ISablierV2MerkleLockupLT.sol => ISablierV2MerkleLT.sol} (90%) rename test/fork/merkle-lockup/{MerkleLockupLL.t.sol => MerkleLL.t.sol} (83%) rename test/fork/merkle-lockup/{MerkleLockupLT.t.sol => MerkleLT.t.sol} (83%) rename test/integration/merkle-lockup/factory/{create-merkle-lockup-ll/createMerkleLockupLL.t.sol => create-merkle-ll/createMerkleLL.t.sol} (78%) rename test/integration/merkle-lockup/factory/{create-merkle-lockup-ll/createMerkleLockupLL.tree => create-merkle-ll/createMerkleLL.tree} (78%) rename test/integration/merkle-lockup/factory/{create-merkle-lockup-lt/createMerkleLockupLT.t.sol => create-merkle-lt/createMerkleLT.t.sol} (76%) rename test/integration/merkle-lockup/factory/{create-merkle-lockup-lt/createMerkleLockupLT.tree => create-merkle-lt/createMerkleLT.tree} (87%) diff --git a/SECURITY.md b/SECURITY.md index aff203e8..2745721e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -47,7 +47,7 @@ vulnerability, it must adhere to these assumptions as well: - [All assumptions](https://github.com/sablier-labs/v2-core/blob/main/SECURITY.md) in Sablier V2 Core apply to Sablier V2 Periphery as well. -- In `SablierV2MerkleLockupLT`, the tranche unlock percentages and the durations will be same for all airdrop claimers. +- In `SablierV2MerkleLT`, the tranche unlock percentages and the durations will be same for all airdrop claimers. ### Rewards diff --git a/bun.lockb b/bun.lockb index 9f255c844b10cbc14e26acbeb62df45fbaf67e8d..7b09b4318df7b209e70832bf3f7c9527c3952fa4 100755 GIT binary patch delta 36155 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 delta 35995 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