From e3b952960d56fc472f081f0431bd60aab7a3e318 Mon Sep 17 00:00:00 2001 From: smol-ninja Date: Mon, 7 Oct 2024 14:01:01 +0100 Subject: [PATCH 1/3] feat: allow void for solvent streams feat: allow refund on voided streams docs: update docs docs(benchmark): include gas cost for voiding a solvent streams test: update concrete, fuzz and invariants --- DIAGRAMS.md | 35 ++++++----- README.md | 4 +- TECHNICAL-DOC.md | 2 +- benchmark/Flow.Gas.t.sol | 11 ++-- benchmark/results/SablierFlow.md | 7 ++- src/SablierFlow.sol | 25 +++++--- src/interfaces/ISablierFlow.sol | 23 +++---- src/libraries/Errors.sol | 3 - src/types/DataTypes.sol | 4 +- tests/fork/Flow.t.sol | 31 +++------ .../integration/concrete/refund/refund.t.sol | 34 +--------- tests/integration/concrete/refund/refund.tree | 63 +++++++++---------- tests/integration/concrete/void/void.t.sol | 62 +++++++++++------- tests/integration/concrete/void/void.tree | 12 ++-- tests/integration/fuzz/void.t.sol | 23 ++++--- tests/invariant/Flow.t.sol | 7 +-- tests/invariant/handlers/FlowHandler.sol | 6 -- 17 files changed, 168 insertions(+), 184 deletions(-) diff --git a/DIAGRAMS.md b/DIAGRAMS.md index 1addb575..acffa71a 100644 --- a/DIAGRAMS.md +++ b/DIAGRAMS.md @@ -7,13 +7,13 @@ There are two types of streams: `STREAMING`, when debt is actively accruing, and `PAUSED`, when debt is not accruing: -| Type | Status | Description | -| ----------- | --------------------- | ----------------------------------------------------------------------- | -| `STREAMING` | `STREAMING_SOLVENT` | Streaming stream when there is no uncovered debt. | -| `STREAMING` | `STREAMING_INSOLVENT` | Streaming stream when there is uncovered debt. | -| `PAUSED` | `PAUSED_SOLVENT` | Paused stream when there is no uncovered debt. | -| `PAUSED` | `PAUSED_INSOLVENT` | Paused stream when there is uncovered debt. | -| `PAUSED` | `VOIDED` | Paused stream with forfeited uncovered debt and it cannot be restarted. | +| Type | Status | Description | +| ----------- | --------------------- | --------------------------------------------------------------------------------------- | +| `STREAMING` | `STREAMING_SOLVENT` | Streaming stream when there is no uncovered debt. | +| `STREAMING` | `STREAMING_INSOLVENT` | Streaming stream when there is uncovered debt. | +| `PAUSED` | `PAUSED_SOLVENT` | Paused stream when there is no uncovered debt. | +| `PAUSED` | `PAUSED_INSOLVENT` | Paused stream when there is uncovered debt. | +| `PAUSED` | `VOIDED` | Paused stream that cannot be restarted. Sets uncovered debt to 0 for insolvent streams. | ### Statuses diagram @@ -35,33 +35,33 @@ stateDiagram-v2 PAUSED_SOLVENT PAUSED_INSOLVENT PAUSED_INSOLVENT --> PAUSED_SOLVENT : deposit - PAUSED_INSOLVENT --> VOIDED : void - VOIDED } STREAMING_SOLVENT --> PAUSED_SOLVENT : pause STREAMING_INSOLVENT --> PAUSED_INSOLVENT : pause PAUSED_SOLVENT --> STREAMING_SOLVENT : restart - STREAMING_INSOLVENT --> VOIDED : void + Paused --> VOIDED : void + Streaming --> VOIDED : void PAUSED_INSOLVENT --> STREAMING_INSOLVENT : restart NULL --> STREAMING_SOLVENT : create (rps > 0) NULL --> PAUSED_SOLVENT : create (rps = 0) NULL:::grey - Streaming:::lightGreen Paused:::lightYellow - STREAMING_SOLVENT:::intenseGreen - STREAMING_INSOLVENT:::intenseGreen PAUSED_INSOLVENT:::intenseYellow PAUSED_SOLVENT:::intenseYellow - VOIDED:::intenseYellow + Streaming:::lightGreen + STREAMING_INSOLVENT:::intenseGreen + STREAMING_SOLVENT:::intenseGreen + VOIDED:::red classDef grey fill:#b0b0b0,stroke:#333,stroke-width:2px,color:#000,font-weight:bold; - classDef lightGreen fill:#98FB98,color:#000,font-weight:bold; classDef intenseGreen fill:#32cd32,stroke:#333,stroke-width:2px,color:#000,font-weight:bold; - classDef lightYellow fill:#ffff99,color:#000,font-weight:bold; classDef intenseYellow fill:#ffd700,color:#000,font-weight:bold; + classDef lightGreen fill:#98FB98,color:#000,font-weight:bold; + classDef lightYellow fill:#ffff99,color:#000,font-weight:bold; + classDef red fill:#ff4e4e,stroke:#333,stroke-width:2px; ``` ### Function calls @@ -86,10 +86,10 @@ flowchart LR CR([CREATE]) ADJRPS([ADJUST_RPS]) DP([DEPOSIT]) - RFD([REFUND]) PS([PAUSE]) RST([RESTART]) VD([VOID]) + RFD([REFUND]) WTD([WITHDRAW]) end @@ -109,6 +109,7 @@ flowchart LR DP -- "update bal (+)" --> BOTH RFD -- "update bal (-)" --> BOTH + RFD -- "update bal (-)" --> VOID PS -- "update sd (+od)
update rps (0)
update st" --> STR diff --git a/README.md b/README.md index 1daaf64f..85decb4f 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,8 @@ premiums, loans interest, token ESOPs etc. If you are looking for vesting and ai 3. **Pause:** A stream can be paused by the sender and can later be restarted without losing track of previously accrued debt. 4. **Refund:** Unstreamed amount can be refunded back to the sender at any time. -5. **Void:** Voiding a stream forfeits the uncovered debt and, thus, cannot be restarted anymore. Only streams with - non-zero uncovered debt can be voided by any part (either the sender or the recipient). +5. **Void:** Voiding a stream implies it cannot be restarted anymore. Voiding an insolvent stream forfeits the uncovered + debt. Either party can void a stream at any time. 6. **Withdraw:** it is publicly callable as long as `to` is set to the recipient. However, a stream’s recipient is allowed to withdraw funds to any address. diff --git a/TECHNICAL-DOC.md b/TECHNICAL-DOC.md index 8e3d54b0..f91cc28b 100644 --- a/TECHNICAL-DOC.md +++ b/TECHNICAL-DOC.md @@ -76,7 +76,7 @@ can only withdraw the available balance. 15. if $isPaused = true \implies rps = 0$ -16. if $isVoided = true \implies isPaused = true$, $ra = 0$ and $ud = 0$ +16. if $isVoided = true \implies isPaused = true$ and $ud = 0$ 17. if $isVoided = false \implies \text{amount streamed with delay} = td + \text{amount withdrawn}$. diff --git a/benchmark/Flow.Gas.t.sol b/benchmark/Flow.Gas.t.sol index 92d9b6d0..b422c4fd 100644 --- a/benchmark/Flow.Gas.t.sol +++ b/benchmark/Flow.Gas.t.sol @@ -73,11 +73,14 @@ contract Flow_Gas_Test is Integration_Test { // {flow.restart} computeGas("restart", abi.encodeCall(flow.restart, (streamId, RATE_PER_SECOND))); - // Warp time to accrue uncovered debt for the next call. - vm.warp(flow.depletionTimeOf(streamId) + 2 days); + // {flow.void} (on a solvent stream) + computeGas("void (solvent stream)", abi.encodeCall(flow.void, (streamId))); - // {flow.void} - computeGas("void", abi.encodeCall(flow.void, (streamId))); + // Warp time to accrue uncovered debt for the next call on an incremented stream ID.. + vm.warp(flow.depletionTimeOf(++streamId) + 2 days); + + // {flow.void} (on an insolvent stream) + computeGas("void (insolvent stream)", abi.encodeCall(flow.void, (streamId))); // {flow.withdraw} (on an insolvent stream) on an incremented stream ID. computeGas( diff --git a/benchmark/results/SablierFlow.md b/benchmark/results/SablierFlow.md index 94f5531a..b10a9f05 100644 --- a/benchmark/results/SablierFlow.md +++ b/benchmark/results/SablierFlow.md @@ -7,9 +7,10 @@ | `deposit` | 30152 | | `depositViaBroker` | 22142 | | `pause` | 9340 | -| `refund` | 11866 | +| `refund` | 11671 | | `restart` | 7106 | -| `void` | 8314 | +| `void (solvent stream)` | 10389 | +| `void (insolvent stream)` | 36795 | | `withdraw (insolvent stream)` | 57634 | -| `withdraw (solvent stream)` | 40046 | +| `withdraw (solvent stream)` | 40047 | | `withdrawMax` | 52200 | diff --git a/src/SablierFlow.sol b/src/SablierFlow.sol index b40f9aec..b679ce90 100644 --- a/src/SablierFlow.sol +++ b/src/SablierFlow.sol @@ -312,7 +312,6 @@ contract SablierFlow is override noDelegateCall notNull(streamId) - notVoided(streamId) onlySender(streamId) updateMetadata(streamId) { @@ -703,15 +702,10 @@ contract SablierFlow is emit ISablierFlow.RestartFlowStream(streamId, msg.sender, ratePerSecond); } - /// @dev Voids a stream that has uncovered debt. + /// @dev Voids a stream. function _void(uint256 streamId) internal { uint128 debtToWriteOff = _uncoveredDebtOf(streamId); - // Check: the stream has debt. - if (debtToWriteOff == 0) { - revert Errors.SablierFlow_UncoveredDebtZero(streamId); - } - // Check: `msg.sender` is either the stream's sender, recipient or an approved third party. if (msg.sender != _streams[streamId].sender && !_isCallerStreamRecipientOrApproved(streamId)) { revert Errors.SablierFlow_Unauthorized({ streamId: streamId, caller: msg.sender }); @@ -719,8 +713,19 @@ contract SablierFlow is uint128 balance = _streams[streamId].balance; - // Effect: update the total debt by setting snapshot debt to the stream balance. - _streams[streamId].snapshotDebt = balance; + // If the stream is solvent, update the total debt normally. + if (debtToWriteOff == 0) { + uint128 ongoingDebt = _ongoingDebtOf(streamId); + if (ongoingDebt > 0) { + // Effect: Update the snapshot debt by adding the ongoing debt. + _streams[streamId].snapshotDebt += ongoingDebt; + } + } + // If the stream is insolvent, write off the uncovered debt. + else { + // Effect: update the total debt by setting snapshot debt to the stream balance. + _streams[streamId].snapshotDebt = balance; + } // Effect: update the snapshot time. _streams[streamId].snapshotTime = uint40(block.timestamp); @@ -737,7 +742,7 @@ contract SablierFlow is sender: _streams[streamId].sender, recipient: _ownerOf(streamId), caller: msg.sender, - newTotalDebt: balance, + newTotalDebt: _streams[streamId].snapshotDebt, writtenOffDebt: debtToWriteOff }); } diff --git a/src/interfaces/ISablierFlow.sol b/src/interfaces/ISablierFlow.sol index 64c3728e..35a372ba 100644 --- a/src/interfaces/ISablierFlow.sol +++ b/src/interfaces/ISablierFlow.sol @@ -16,12 +16,12 @@ interface ISablierFlow is EVENTS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Emitted when the payment rate per second is updated by the sender. + /// @notice Emitted when the rate per second is updated by the sender. /// @param streamId The ID of the stream. /// @param totalDebt The total debt at the time of the update, denoted in token's decimals. - /// @param oldRatePerSecond The old payment rate per second, denoted as a fixed-point number where 1e18 is 1 token + /// @param oldRatePerSecond The old rate per second, denoted as a fixed-point number where 1e18 is 1 token /// per second. - /// @param newRatePerSecond The new payment rate per second, denoted as a fixed-point number where 1e18 is 1 token + /// @param newRatePerSecond The new rate per second, denoted as a fixed-point number where 1e18 is 1 token /// per second. event AdjustFlowStream( uint256 indexed streamId, uint128 totalDebt, UD21x18 oldRatePerSecond, UD21x18 newRatePerSecond @@ -157,7 +157,7 @@ interface ISablierFlow is NON-CONSTANT FUNCTIONS //////////////////////////////////////////////////////////////////////////*/ - /// @notice Changes the stream's payment rate per second. + /// @notice Changes the stream's rate per second. /// /// @dev Emits an {AdjustFlowStream} and {MetadataUpdate} event. /// @@ -171,7 +171,7 @@ interface ISablierFlow is /// - `newRatePerSecond` must not equal to the current rate per second. /// /// @param streamId The ID of the stream to adjust. - /// @param newRatePerSecond The new payment rate per second, denoted as a fixed-point number where 1e18 is 1 token + /// @param newRatePerSecond The new rate per second, denoted as a fixed-point number where 1e18 is 1 token /// per second. function adjustRatePerSecond(uint256 streamId, UD21x18 newRatePerSecond) external; @@ -306,7 +306,7 @@ interface ISablierFlow is /// /// Requirements: /// - Must not be delegate called. - /// - `streamId` must not reference a null or a voided stream. + /// - `streamId` must not reference a null stream. /// - `msg.sender` must be the sender. /// - `amount` must be greater than zero and must not exceed the refundable amount. /// @@ -364,21 +364,22 @@ interface ISablierFlow is /// @param amount The deposit amount, denoted in token's decimals. function restartAndDeposit(uint256 streamId, UD21x18 ratePerSecond, uint128 amount) external; - /// @notice Voids the uncovered debt, and ends the stream. + /// @notice Voids a stream. /// /// @dev Emits a {VoidFlowStream} event. /// /// Notes: - /// - It sets the snapshot debt to the stream's balance so that the uncovered debt becomes zero. - /// - It sets the payment rate per second to zero. - /// - A paused stream can be voided only if its uncovered debt is not zero. + /// - It sets snapshot time to the `block.timestamp` + /// - Voiding an insolvent stream sets the snapshot debt to the stream's balance making the uncovered debt to become + /// zero. + /// - Voiding a solvent stream updates the snapshot debt by adding up ongoing debt. + /// - It sets the rate per second to zero. /// - A voided stream cannot be restarted. /// /// Requirements: /// - Must not be delegate called. /// - `streamId` must not reference a null or a voided stream. /// - `msg.sender` must either be the stream's sender, recipient or an approved third party. - /// - The uncovered debt must be greater than zero. /// /// @param streamId The ID of the stream to void. function void(uint256 streamId) external; diff --git a/src/libraries/Errors.sol b/src/libraries/Errors.sol index 4e0bf5e0..ba70ca29 100644 --- a/src/libraries/Errors.sol +++ b/src/libraries/Errors.sol @@ -69,9 +69,6 @@ library Errors { /// @notice Thrown when `msg.sender` lacks authorization to perform an action. error SablierFlow_Unauthorized(uint256 streamId, address caller); - /// @notice Thrown when voiding a stream with zero uncovered debt. - error SablierFlow_UncoveredDebtZero(uint256 streamId); - /// @notice Thrown when trying to withdraw to an address other than the recipient's. error SablierFlow_WithdrawalAddressNotRecipient(uint256 streamId, address caller, address to); diff --git a/src/types/DataTypes.sol b/src/types/DataTypes.sol index 279708f6..42234f16 100644 --- a/src/types/DataTypes.sol +++ b/src/types/DataTypes.sol @@ -51,8 +51,8 @@ library Flow { /// @param snapshotTime The Unix timestamp used for the ongoing debt calculation. /// @param isStream Boolean indicating if the struct entity exists. /// @param isTransferable Boolean indicating if the stream NFT is transferable. - /// @param isVoided Boolean indicating if the stream is voided. Voiding a stream is a non reversible step. When a - /// stream is voided, its uncovered debt is set to zero and it can not be restarted. + /// @param isVoided Boolean indicating if the stream is voided. Voiding any stream is non-reversible and it cannot + /// be restarted. Voiding an insolvent stream sets its uncovered debt to zero. /// @param token The contract address of the ERC-20 token to stream. /// @param tokenDecimals The decimals of the ERC-20 token to stream. /// @param snapshotDebt The amount of tokens that the sender owed to the recipient at snapshot time, denoted in diff --git a/tests/fork/Flow.t.sol b/tests/fork/Flow.t.sol index e2c13156..e8261ac1 100644 --- a/tests/fork/Flow.t.sol +++ b/tests/fork/Flow.t.sol @@ -528,30 +528,19 @@ contract Flow_Fork_Test is Fork_Test { address sender = flow.getSender(streamId); address recipient = flow.getRecipient(streamId); uint128 uncoveredDebt = flow.uncoveredDebtOf(streamId); + uint128 expectedTotalDebt; resetPrank({ msgSender: sender }); - if (uncoveredDebt == 0) { - if (flow.isPaused(streamId)) { - flow.restart(streamId, RATE_PER_SECOND); - } - - // In case of a big depletion time, refund and withdraw all the funds, and then warp for one second. Warping - // too much in the future would affect the other tests. - uint128 refundableAmount = flow.refundableAmountOf(streamId); - if (refundableAmount > 0) { - // Refund and withdraw all the funds. - flow.refund(streamId, refundableAmount); - } - if (flow.coveredDebtOf(streamId) > 0) { - flow.withdrawMax(streamId, recipient); - } - - vm.warp({ newTimestamp: getBlockTimestamp() + 100 seconds }); - uncoveredDebt = flow.uncoveredDebtOf(streamId); + if (flow.isPaused(streamId)) { + flow.restart(streamId, RATE_PER_SECOND); } - uint128 beforeVoidBalance = flow.getBalance(streamId); + if (uncoveredDebt > 0) { + expectedTotalDebt = flow.getBalance(streamId); + } else { + expectedTotalDebt = flow.totalDebtOf(streamId); + } // It should emit 1 {VoidFlowStream}, 1 {MetadataUpdate} events. vm.expectEmit({ emitter: address(flow) }); @@ -560,7 +549,7 @@ contract Flow_Fork_Test is Fork_Test { recipient: recipient, sender: sender, caller: sender, - newTotalDebt: beforeVoidBalance, + newTotalDebt: expectedTotalDebt, writtenOffDebt: uncoveredDebt }); @@ -576,7 +565,7 @@ contract Flow_Fork_Test is Fork_Test { assertTrue(flow.isPaused(streamId), "Void: paused"); // It should set the total debt to stream balance. - assertEq(flow.totalDebtOf(streamId), beforeVoidBalance, "Void: total debt"); + assertEq(flow.totalDebtOf(streamId), expectedTotalDebt, "Void: total debt"); } /*////////////////////////////////////////////////////////////////////////// diff --git a/tests/integration/concrete/refund/refund.t.sol b/tests/integration/concrete/refund/refund.t.sol index 5f84f215..5c93715f 100644 --- a/tests/integration/concrete/refund/refund.t.sol +++ b/tests/integration/concrete/refund/refund.t.sol @@ -27,40 +27,17 @@ contract Refund_Integration_Concrete_Test is Integration_Test { expectRevert_Null(callData); } - function test_RevertGiven_Voided() external whenNoDelegateCall givenNotNull { - bytes memory callData = abi.encodeCall(flow.refund, (defaultStreamId, REFUND_AMOUNT_6D)); - expectRevert_Voided(callData); - } - - function test_RevertWhen_CallerRecipient() - external - whenNoDelegateCall - givenNotNull - givenNotVoided - whenCallerNotSender - { + function test_RevertWhen_CallerRecipient() external whenNoDelegateCall givenNotNull whenCallerNotSender { bytes memory callData = abi.encodeCall(flow.refund, (defaultStreamId, REFUND_AMOUNT_6D)); expectRevert_CallerRecipient(callData); } - function test_RevertWhen_CallerMaliciousThirdParty() - external - whenNoDelegateCall - givenNotNull - givenNotVoided - whenCallerNotSender - { + function test_RevertWhen_CallerMaliciousThirdParty() external whenNoDelegateCall givenNotNull whenCallerNotSender { bytes memory callData = abi.encodeCall(flow.refund, (defaultStreamId, REFUND_AMOUNT_6D)); expectRevert_CallerMaliciousThirdParty(callData); } - function test_RevertWhen_RefundAmountZero() - external - whenNoDelegateCall - givenNotNull - givenNotVoided - whenCallerSender - { + function test_RevertWhen_RefundAmountZero() external whenNoDelegateCall givenNotNull whenCallerSender { vm.expectRevert(abi.encodeWithSelector(Errors.SablierFlow_RefundAmountZero.selector, defaultStreamId)); flow.refund({ streamId: defaultStreamId, amount: 0 }); } @@ -69,7 +46,6 @@ contract Refund_Integration_Concrete_Test is Integration_Test { external whenNoDelegateCall givenNotNull - givenNotVoided whenCallerSender whenRefundAmountNotZero { @@ -88,7 +64,6 @@ contract Refund_Integration_Concrete_Test is Integration_Test { external whenNoDelegateCall givenNotNull - givenNotVoided whenCallerSender whenRefundAmountNotZero whenNoOverRefund @@ -108,7 +83,6 @@ contract Refund_Integration_Concrete_Test is Integration_Test { external whenNoDelegateCall givenNotNull - givenNotVoided whenCallerSender whenRefundAmountNotZero whenNoOverRefund @@ -130,7 +104,6 @@ contract Refund_Integration_Concrete_Test is Integration_Test { external whenNoDelegateCall givenNotNull - givenNotVoided whenCallerSender whenRefundAmountNotZero whenNoOverRefund @@ -150,7 +123,6 @@ contract Refund_Integration_Concrete_Test is Integration_Test { external whenNoDelegateCall givenNotNull - givenNotVoided whenCallerSender whenRefundAmountNotZero whenNoOverRefund diff --git a/tests/integration/concrete/refund/refund.tree b/tests/integration/concrete/refund/refund.tree index ec8ad68b..6b2e3ba7 100644 --- a/tests/integration/concrete/refund/refund.tree +++ b/tests/integration/concrete/refund/refund.tree @@ -5,38 +5,35 @@ Refund_Integration_Concrete_Test ├── given null │ └── it should revert └── given not null - ├── given voided - │ └── it should revert - └── given not voided - ├── when caller not sender - │ ├── when caller recipient - │ │ └── it should revert - │ └── when caller malicious third party - │ └── it should revert - └── when caller sender - ├── when refund amount zero + ├── when caller not sender + │ ├── when caller recipient + │ │ └── it should revert + │ └── when caller malicious third party + │ └── it should revert + └── when caller sender + ├── when refund amount zero + │ └── it should revert + └── when refund amount not zero + ├── when over refund │ └── it should revert - └── when refund amount not zero - ├── when over refund - │ └── it should revert - └── when no over refund - ├── given paused + └── when no over refund + ├── given paused + │ └── it should make the refund + └── given not paused + ├── when token misses ERC20 return │ └── it should make the refund - └── given not paused - ├── when token misses ERC20 return - │ └── it should make the refund - └── when token not miss ERC20 return - ├── given token not have 18 decimals - │ ├── it should make the refund - │ ├── it should update the stream balance - │ ├── it should decrease the aggregate amount - │ ├── it should perform the ERC20 transfer - │ ├── it should emit 1 {Transfer}, 1 {RefundFromFlowStream}, 1 {MetadataUpdate} event - │ └── it should return the transfer amount - └── given token has 18 decimals - ├── it should make the refund - ├── it should update the stream balance - ├── it should decrease the aggregate amount - ├── it should perform the ERC20 transfer - ├── it should emit 1 {Transfer}, 1 {RefundFromFlowStream}, 1 {MetadataUpdate} event - └── it should return the transfer amount + └── when token not miss ERC20 return + ├── given token not have 18 decimals + │ ├── it should make the refund + │ ├── it should update the stream balance + │ ├── it should decrease the aggregate amount + │ ├── it should perform the ERC20 transfer + │ ├── it should emit 1 {Transfer}, 1 {RefundFromFlowStream}, 1 {MetadataUpdate} event + │ └── it should return the transfer amount + └── given token has 18 decimals + ├── it should make the refund + ├── it should update the stream balance + ├── it should decrease the aggregate amount + ├── it should perform the ERC20 transfer + ├── it should emit 1 {Transfer}, 1 {RefundFromFlowStream}, 1 {MetadataUpdate} event + └── it should return the transfer amount diff --git a/tests/integration/concrete/void/void.t.sol b/tests/integration/concrete/void/void.t.sol index 13f45aa8..6fd1483f 100644 --- a/tests/integration/concrete/void/void.t.sol +++ b/tests/integration/concrete/void/void.t.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.22; import { IERC4906 } from "@openzeppelin/contracts/interfaces/IERC4906.sol"; import { ISablierFlow } from "src/interfaces/ISablierFlow.sol"; -import { Errors } from "src/libraries/Errors.sol"; import { Integration_Test } from "../../Integration.t.sol"; @@ -34,27 +33,29 @@ contract Void_Integration_Concrete_Test is Integration_Test { expectRevert_Voided(callData); } - function test_RevertGiven_StreamHasNoUncoveredDebt() external whenNoDelegateCall givenNotNull givenNotVoided { - vm.expectRevert(abi.encodeWithSelector(Errors.SablierFlow_UncoveredDebtZero.selector, defaultStreamId)); - flow.void(defaultStreamId); - } - - modifier givenStreamHasUncoveredDebt() { - // Simulate the passage of time to accumulate uncovered debt for one month. - vm.warp({ newTimestamp: WARP_SOLVENCY_PERIOD + ONE_MONTH }); - - _; + function test_RevertWhen_CallerNotAuthorized() external whenNoDelegateCall givenNotNull givenNotVoided { + bytes memory callData = abi.encodeCall(flow.void, (defaultStreamId)); + expectRevert_CallerMaliciousThirdParty(callData); } - function test_RevertWhen_CallerNotAuthorized() + function test_GivenStreamHasNoUncoveredDebt() external whenNoDelegateCall givenNotNull givenNotVoided - givenStreamHasUncoveredDebt + whenCallerAuthorized { - bytes memory callData = abi.encodeCall(flow.void, (defaultStreamId)); - expectRevert_CallerMaliciousThirdParty(callData); + // It should void the stream. + // It should set the rate per second to zero. + // It should not change the total debt. + _test_Void(users.recipient); + } + + modifier givenStreamHasUncoveredDebt() { + // Simulate the passage of time to accumulate uncovered debt for one month. + vm.warp({ newTimestamp: WARP_SOLVENCY_PERIOD + ONE_MONTH }); + + _; } function test_WhenCallerSender() @@ -62,13 +63,15 @@ contract Void_Integration_Concrete_Test is Integration_Test { whenNoDelegateCall givenNotNull givenNotVoided - givenStreamHasUncoveredDebt whenCallerAuthorized + givenStreamHasUncoveredDebt { // Make the sender the caller in this test. resetPrank({ msgSender: users.sender }); // It should void the stream. + // It should set the rate per second to zero. + // It should update the total debt to stream balance. _test_Void(users.sender); } @@ -77,8 +80,8 @@ contract Void_Integration_Concrete_Test is Integration_Test { whenNoDelegateCall givenNotNull givenNotVoided - givenStreamHasUncoveredDebt whenCallerAuthorized + givenStreamHasUncoveredDebt { // Approve the operator to handle the stream. flow.approve({ to: users.operator, tokenId: defaultStreamId }); @@ -87,6 +90,8 @@ contract Void_Integration_Concrete_Test is Integration_Test { resetPrank({ msgSender: users.operator }); // It should void the stream. + // It should set the rate per second to zero. + // It should update the total debt to stream balance. _test_Void(users.operator); } @@ -95,17 +100,27 @@ contract Void_Integration_Concrete_Test is Integration_Test { whenNoDelegateCall givenNotNull givenNotVoided - givenStreamHasUncoveredDebt whenCallerAuthorized + givenStreamHasUncoveredDebt { // It should void the stream. + // It should set the rate per second to zero. + // It should update the total debt to stream balance. _test_Void(users.recipient); } function _test_Void(address caller) private { - uint128 streamBalance = flow.getBalance(defaultStreamId); + uint128 expectedTotalDebt; uint128 uncoveredDebt = flow.uncoveredDebtOf(defaultStreamId); + if (uncoveredDebt > 0) { + // Expect the total debt to be stream balance if there is uncovered debt. + expectedTotalDebt = flow.getBalance(defaultStreamId); + } else { + // Otherwise, expect the total debt to remain the same. + expectedTotalDebt = flow.totalDebtOf(defaultStreamId); + } + // It should emit 1 {VoidFlowStream} and 1 {MetadataUpdate} events. vm.expectEmit({ emitter: address(flow) }); emit ISablierFlow.VoidFlowStream({ @@ -113,7 +128,7 @@ contract Void_Integration_Concrete_Test is Integration_Test { recipient: users.recipient, sender: users.sender, caller: caller, - newTotalDebt: streamBalance, + newTotalDebt: expectedTotalDebt, writtenOffDebt: uncoveredDebt }); @@ -131,7 +146,10 @@ contract Void_Integration_Concrete_Test is Integration_Test { // It should void the stream. assertTrue(flow.isVoided(defaultStreamId), "voided"); - // It should set the total debt to the stream balance. - assertEq(flow.totalDebtOf(defaultStreamId), streamBalance, "total debt"); + // Check the new total debt. + assertEq(flow.totalDebtOf(defaultStreamId), expectedTotalDebt, "total debt"); + + // Check the new snapshot time. + assertEq(flow.getSnapshotTime(defaultStreamId), getBlockTimestamp(), "snapshot time"); } } diff --git a/tests/integration/concrete/void/void.tree b/tests/integration/concrete/void/void.tree index 53f37eff..b4466763 100644 --- a/tests/integration/concrete/void/void.tree +++ b/tests/integration/concrete/void/void.tree @@ -8,12 +8,14 @@ Void_Integration_Concrete_Test ├── given voided │ └── it should revert └── given not voided - ├── given stream has no uncovered debt + ├── when caller not authorized │ └── it should revert - └── given stream has uncovered debt - ├── when caller not authorized - │ └── it should revert - └── when caller authorized + └── when caller authorized + ├── given stream has no uncovered debt + │ ├── it should void the stream + │ ├── it should set the rate per second to zero + │ └── it should not change the total debt + └── given stream has uncovered debt ├── when caller sender │ └── it should void the stream ├── when caller approved third party diff --git a/tests/integration/fuzz/void.t.sol b/tests/integration/fuzz/void.t.sol index 214e6144..87fbe0a1 100644 --- a/tests/integration/fuzz/void.t.sol +++ b/tests/integration/fuzz/void.t.sol @@ -4,12 +4,15 @@ pragma solidity >=0.8.22; import { IERC4906 } from "@openzeppelin/contracts/interfaces/IERC4906.sol"; import { ISablierFlow } from "src/interfaces/ISablierFlow.sol"; -import { Errors } from "src/libraries/Errors.sol"; import { Shared_Integration_Fuzz_Test } from "./Fuzz.t.sol"; contract Void_Integration_Fuzz_Test is Shared_Integration_Fuzz_Test { /// @dev It should revert. + /// - It should pause the stream. + /// - It should set rate per second to 0. + /// - It should set ongoing debt to 0 and keep the total debt unchanged. + /// - It should emit the following events: {MetadataUpdate}, {VoidFlowStream} /// /// Given enough runs, all of the following scenarios should be fuzzed: /// - Only two values for caller (stream owner and approved operator). @@ -37,9 +40,6 @@ contract Void_Integration_Fuzz_Test is Shared_Integration_Fuzz_Test { // Prank to either recipient or operator. resetPrank({ msgSender: useRecipientOrOperator(streamId, timeJump) }); - // Expect the relevant error. - vm.expectRevert(abi.encodeWithSelector(Errors.SablierFlow_UncoveredDebtZero.selector, streamId)); - // Void the stream. flow.void(streamId); } @@ -123,6 +123,15 @@ contract Void_Integration_Fuzz_Test is Shared_Integration_Fuzz_Test { // Shared private function. function _test_Void(address caller, uint256 streamId) private { uint128 debtToWriteOff = flow.uncoveredDebtOf(streamId); + uint128 expectedTotalDebt; + + if (debtToWriteOff > 0) { + // Expect the total debt to be the stream balance if there is uncovered debt. + expectedTotalDebt = flow.getBalance(streamId); + } else { + // Otherwise, expect the total debt to remain same. + expectedTotalDebt = flow.totalDebtOf(streamId); + } // Expect the relevant events to be emitted. vm.expectEmit({ emitter: address(flow) }); @@ -131,7 +140,7 @@ contract Void_Integration_Fuzz_Test is Shared_Integration_Fuzz_Test { recipient: users.recipient, sender: users.sender, caller: caller, - newTotalDebt: flow.getBalance(streamId), + newTotalDebt: expectedTotalDebt, writtenOffDebt: debtToWriteOff }); @@ -142,11 +151,11 @@ contract Void_Integration_Fuzz_Test is Shared_Integration_Fuzz_Test { flow.void(streamId); // Assert the checklist. + assertTrue(flow.isVoided(streamId), "voided"); assertTrue(flow.isPaused(streamId), "paused"); assertEq(flow.getRatePerSecond(streamId), 0, "rate per second"); assertEq(flow.ongoingDebtOf(streamId), 0, "ongoing debt"); assertEq(flow.uncoveredDebtOf(streamId), 0, "uncovered debt"); - assertEq(flow.totalDebtOf(streamId), flow.getBalance(streamId), "total debt"); - assertEq(flow.totalDebtOf(streamId), depositedAmount); + assertEq(flow.totalDebtOf(streamId), expectedTotalDebt, "total debt"); } } diff --git a/tests/invariant/Flow.t.sol b/tests/invariant/Flow.t.sol index d5249c25..c1c6d02b 100644 --- a/tests/invariant/Flow.t.sol +++ b/tests/invariant/Flow.t.sol @@ -318,18 +318,13 @@ contract Flow_Invariant_Test is Base_Test { } } - /// @dev If the stream is voided, it should be paused, and refundable amount and uncovered debt should be zero. + /// @dev If the stream is voided, it should be paused, and uncovered debt should be zero. function invariant_StreamVoided_StreamPaused_RefundableAmountZero_UncoveredDebtZero() external view { uint256 lastStreamId = flowStore.lastStreamId(); for (uint256 i = 0; i < lastStreamId; ++i) { uint256 streamId = flowStore.streamIds(i); if (flow.isVoided(streamId)) { assertTrue(flow.isPaused(streamId), "Invariant violation: voided stream is not paused"); - assertEq( - flow.refundableAmountOf(streamId), - 0, - "Invariant violation: voided stream with non-zero refundable amount" - ); assertEq( flow.uncoveredDebtOf(streamId), 0, "Invariant violation: voided stream with non-zero uncovered debt" ); diff --git a/tests/invariant/handlers/FlowHandler.sol b/tests/invariant/handlers/FlowHandler.sol index bee4d912..b16c90e8 100644 --- a/tests/invariant/handlers/FlowHandler.sol +++ b/tests/invariant/handlers/FlowHandler.sol @@ -183,9 +183,6 @@ contract FlowHandler is BaseHandler { updateFlowHandlerStates instrument(currentStreamId, "refund") { - // Voided streams cannot be refunded. - vm.assume(!flow.isVoided(currentStreamId)); - uint128 refundableAmount = flow.refundableAmountOf(currentStreamId); // The protocol doesn't allow zero refund amounts. @@ -251,9 +248,6 @@ contract FlowHandler is BaseHandler { // Voided streams cannot be voided again. vm.assume(!flow.isVoided(currentStreamId)); - // Check if the uncovered debt is greater than zero. - vm.assume(flow.uncoveredDebtOf(currentStreamId) > 0); - // Void the stream. flow.void(currentStreamId); From 82bf967b01106ac4f944be40fedbc38f669b185f Mon Sep 17 00:00:00 2001 From: andreivladbrg Date: Mon, 7 Oct 2024 18:23:31 +0300 Subject: [PATCH 2/3] docs: update diagrams --- DIAGRAMS.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/DIAGRAMS.md b/DIAGRAMS.md index acffa71a..c5f88ab3 100644 --- a/DIAGRAMS.md +++ b/DIAGRAMS.md @@ -25,27 +25,26 @@ stateDiagram-v2 direction LR state Streaming { + direction LR STREAMING_SOLVENT STREAMING_INSOLVENT --> STREAMING_SOLVENT : deposit STREAMING_SOLVENT --> STREAMING_INSOLVENT : time } state Paused { - # direction BT + direction RL PAUSED_SOLVENT - PAUSED_INSOLVENT PAUSED_INSOLVENT --> PAUSED_SOLVENT : deposit + PAUSED_INSOLVENT } - STREAMING_SOLVENT --> PAUSED_SOLVENT : pause - STREAMING_INSOLVENT --> PAUSED_INSOLVENT : pause - PAUSED_SOLVENT --> STREAMING_SOLVENT : restart + Streaming --> Paused : pause + Paused --> Streaming : restart Paused --> VOIDED : void Streaming --> VOIDED : void - PAUSED_INSOLVENT --> STREAMING_INSOLVENT : restart - NULL --> STREAMING_SOLVENT : create (rps > 0) - NULL --> PAUSED_SOLVENT : create (rps = 0) + NULL --> Streaming : create (rps > 0) + NULL --> Paused : create (rps = 0) NULL:::grey Paused:::lightYellow @@ -117,12 +116,12 @@ flowchart LR RST -- "update rps
update st" --> PSED - VD -- "update sd (bal)
update rps (0)
update st" --> BOTH + VD -- "update sd (bal || +od)
update rps (0)
update st" --> BOTH WTD -- "update sd (-)
update st
update bal (-)" --> BOTH WTD -- "update sd (-)" --> VOID - linkStyle 2,3,9,10 stroke:#ff0000,stroke-width:2px + linkStyle 2,3,4,10,11 stroke:#ff0000,stroke-width:2px ``` ## Access Control @@ -212,7 +211,7 @@ classDef green1 fill:#32cd32,stroke:#333,stroke-width:2px; flowchart TD di0{ }:::red1 sd([Uncovered Debt - ud]):::red0 - res_sd(["td- bal"]):::red1 + res_sd(["td - bal"]):::red1 res_zero([0]):::red1 sd --> di0 From 230410e82b6a5cfc1bb887feece4e9837bccaa38 Mon Sep 17 00:00:00 2001 From: andreivladbrg Date: Mon, 7 Oct 2024 18:24:54 +0300 Subject: [PATCH 3/3] address feedback update precompiles --- precompiles/Precompiles.sol | 2 +- src/SablierFlow.sol | 8 +++----- tests/fork/Flow.t.sol | 4 ---- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/precompiles/Precompiles.sol b/precompiles/Precompiles.sol index 9331e56d..389df674 100644 --- a/precompiles/Precompiles.sol +++ b/precompiles/Precompiles.sol @@ -13,7 +13,7 @@ import { ISablierFlow } from "./../src/interfaces/ISablierFlow.sol"; /// but allows for execution in test environments, such as a local development network or a testnet. contract Precompiles { bytes public constant BYTECODE_FLOW = - hex"60a0604052346103a9576147ab6040813803918261001c816103ad565b9384928339810103126103a95780516001600160a01b03811691908290036103a957602001516001600160a01b038116908190036103a95761005e60406103ad565b91601083526f14d8589b1a595c88119b1bddc813919560821b602084015261008660406103ad565b60088152675341422d464c4f5760c01b60208201523060805283519092906001600160401b0381116102ba57600154600181811c9116801561039f575b602082101461029c57601f811161033c575b50602094601f82116001146102d9579481929394955f926102ce575b50508160011b915f199060031b1c1916176001555b82516001600160401b0381116102ba57600254600181811c911680156102b0575b602082101461029c57601f8111610239575b506020601f82116001146101d657819293945f926101cb575b50508160011b915f199060031b1c1916176002555b60016008555f80546001600160a01b031990811684178255600980549091169290921790915560405191907fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf808180a36143d890816103d3823960805181612f470152f35b015190505f80610152565b601f1982169060025f52805f20915f5b81811061022157509583600195969710610209575b505050811b01600255610167565b01515f1960f88460031b161c191690555f80806101fb565b9192602060018192868b0151815501940192016101e6565b60025f527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace601f830160051c81019160208410610292575b601f0160051c01905b8181106102875750610139565b5f815560010161027a565b9091508190610271565b634e487b7160e01b5f52602260045260245ffd5b90607f1690610127565b634e487b7160e01b5f52604160045260245ffd5b015190505f806100f1565b601f1982169560015f52805f20915f5b8881106103245750836001959697981061030c575b505050811b01600155610106565b01515f1960f88460031b161c191690555f80806102fe565b919260206001819286850151815501940192016102e9565b60015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6601f830160051c81019160208410610395575b601f0160051c01905b81811061038a57506100d5565b5f815560010161037d565b9091508190610374565b90607f16906100c3565b5f80fd5b6040519190601f01601f191682016001600160401b038111838210176102ba5760405256fe6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a71461252c5750806306fdde0314612471578063081812fc1461245357806308b8792314612419578063095ea7b3146122fc5780630c5fd195146122bb578063136439dd146121f45780631400ecec146121ba5780631e0104391461216c5780631e897afb146120435780631e99d569146120265780631f0cce5814611f1157806323b872dd14611efa578063379d871a14611e5957806342842e0e14611e3057806342e3e23d146108bb5780634426757014611e0a578063569f4c5914611dba578063597150fa14611d725780635ea2145b14611b465780635f55315214611b0e5780636352211e14611adf578063648bf774146119865780636d0cee751461193857806370a08231146118ce57806375829def146118485780637cad6cd11461171d57806380448da3146116d557806381632a861461162f578063894e9a0d1461148257806395d89b41146113435780639e9e2e13146112bc578063a22cb465146111ea578063a7de07cd1461119c578063a8a482a614611092578063ad35efd41461101a578063b256456914610fe5578063b5b3ca2c14610f15578063b62b31e414610edd578063b88d4fde14610e53578063b8a3be6614610e1e578063b971302a14610dcd578063bc063e1a14610dab578063bc7a2d6c14610d71578063bcbd019e14610c9b578063bdf2a43c14610c52578063c87b56dd14610b3c578063c928801914610962578063d4b80884146108d9578063d975dfed146108bb578063dcd7a5331461086a578063e4b50cb814610819578063e985e9c5146107c0578063ea5ead1914610730578063eb5710d8146106de578063ebb6f79a146105b0578063edfa12df1461053a578063f851a44014610515578063fbf2777e1461049e578063fdd46d60146103f25763ffe3d9f8146102bb575f80fd5b346103ee5760406003193601126103ee576102d461265f565b6102dc612675565b6001600160a01b035f54163381036103bf57506001600160a01b03821691825f52600b6020526001600160801b0360405f20541690811561039357818361035792865f52600b60205260405f206fffffffffffffffffffffffffffffffff198154169055865f52600760205260405f20838154039055613a2e565b6001600160a01b036040519216825260208201527fc9a4a66b97fd7e52e69c5be7b10bdc5341bded817201b9b7136a75068d4e4e0560403392a3005b837ff717901b000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7fc6cce6a4000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b5f80fd5b346103ee5760606003193601126103ee5760043561040e612675565b6104166126b7565b9061041f612f3d565b825f52600c60205260ff600160405f20015460c81c161561047257916104696020927ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce79483613cea565b604051908152a1005b827fe21c1431000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346103ee5760c06003193601126103ee576104b761265f565b6104bf612675565b906104c86126b7565b916104d161268b565b916104da612707565b60a435936001600160801b03851685036103ee5760209561050d9461050694610501612f3d565b61346f565b91826131bc565b604051908152f35b346103ee575f6003193601126103ee5760206001600160a01b035f5416604051908152f35b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c161561058557610574602091613bfe565b6001600160801b0360405191168152f35b7fe21c1431000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346103ee5760406003193601126103ee576004356105cc6126a1565b6105d4612f3d565b815f52600c60205260ff600160405f20015460c81c16156106b257815f52600c60205260ff600160405f20015460d81c1661068657815f52600c6020526001600160a01b03600160405f20015416330361065657816104696020927ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce79461313c565b507fa9ad2a22000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b507f7354d5f1000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b507fe21c1431000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c602052602064ffffffffff600160405f20015460a01c16604051908152f35b346103ee5760406003193601126103ee5760043561074c612675565b610754612f3d565b815f52600c60205260ff600160405f20015460c81c16156106b2577ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce76020836107a86107a0839661342f565b809583613cea565b604051908152a16001600160801b0360405191168152f35b346103ee5760406003193601126103ee576107d961265f565b6001600160a01b036107e9612675565b91165f5260066020526001600160a01b0360405f2091165f52602052602060ff60405f2054166040519015158152f35b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c60205260206001600160a01b03600260405f20015416604051908152f35b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c60205260206001600160801b03600360405f20015416604051908152f35b346103ee5760206003193601126103ee576020610574600435612ae2565b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c161561058557805f52600c60205260405f205460801c1561093757610928602091612d80565b64ffffffffff60405191168152f35b7f167274c9000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346103ee5760806003193601126103ee5760043561097e6126a1565b60407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126103ee576109b0612f3d565b815f52600c60205260ff600160405f20015460c81c16156106b257815f52600c60205260ff600160405f20015460d81c1661068657604051916040830183811067ffffffffffffffff821117610b0f576040526044356001600160a01b03811681036103ee578352606435602084019080825267016345785d8a00008111610ad857506001600160a01b0384511615610ab0577ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce793610a82610a79602095610469945190614210565b859291926131bc565b835f52600c85526001600160801b036001600160a01b0380600260405f2001541693511691169133906140b6565b7f5f946a02000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f54b392b2000000000000000000000000000000000000000000000000000000005f5260045267016345785d8a000060245260445ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b346103ee5760206003193601126103ee57600435610b5981612ec5565b505f6001600160a01b0360095416916044604051809481937fe9dc637500000000000000000000000000000000000000000000000000000000835230600484015260248301525afa8015610c47575f90610bca575b610bc69060405191829160208352602083019061261c565b0390f35b503d805f833e610bda8183612733565b8101906020818303126103ee5780519067ffffffffffffffff82116103ee57019080601f830112156103ee57815191610c1283612774565b91610c206040519384612733565b838352602084830101116103ee57610bc692610c4291602080850191016125fb565b610bae565b6040513d5f823e3d90fd5b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c602052602060405f205460801c15604051908152f35b346103ee5760406003193601126103ee57600435610cb76126a1565b610cbf612f3d565b815f52600c60205260ff600160405f20015460c81c16156106b257815f52600c60205260405f205460801c15610d4557815f52600c6020526001600160a01b03600160405f2001541633036106565781610d3c6020927ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7946131bc565b61046981612f97565b507f167274c9000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c161561058557610574602091613bc4565b346103ee575f6003193601126103ee57602060405167016345785d8a00008152f35b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c60205260206001600160a01b03600160405f20015416604051908152f35b346103ee5760206003193601126103ee576004355f52600c602052602060ff600160405f20015460c81c166040519015158152f35b346103ee5760806003193601126103ee57610e6c61265f565b610e74612675565b6064359167ffffffffffffffff83116103ee57366023840112156103ee57826004013591610ea183612774565b92610eaf6040519485612733565b80845236602482870101116103ee576020815f926024610edb9801838801378501015260443591612b6d565b005b346103ee5760206003193601126103ee576001600160a01b03610efe61265f565b165f52600a602052602060405f2054604051908152f35b346103ee5760406003193601126103ee57610f2e61265f565b602435906001600160a01b035f54163381036103bf575067016345785d8a00008211610fad576001600160a01b031690815f52600a60205260405f205490825f52600a6020528060405f205560405191825260208201527f371789a3d97098f3070492613273a065a7e8a19e009fd1ae92a4b4d4c71ed62d60403392a3005b507f34553172000000000000000000000000000000000000000000000000000000005f5260045267016345785d8a000060245260445ffd5b346103ee5760206003193601126103ee576004355f52600c602052602060ff600160405f20015460d01c166040519015158152f35b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c16156105855761105290612b09565b6040516005821015611065576020918152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b346103ee5760406003193601126103ee576004356110ae6126a1565b6110b6612f3d565b815f52600c60205260ff600160405f20015460c81c16156106b257815f52600c60205260405f205460801c15610d4557815f52600c6020526001600160a01b03600160405f2001541633036106565781807f3c92c3a56d04d4a7bc02d85874ad093d97053f2b046ef83860ecd0df273079b960606020947ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7965f52600c86526001600160801b0360405f205460801c916111708187613a8a565b855f52600c885281600360405f200154169260405193845288840152166040820152a2604051908152a1005b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c602052602060ff600260405f20015460a01c16604051908152f35b346103ee5760406003193601126103ee5761120361265f565b602435908115158092036103ee576001600160a01b031690811561129057335f52600660205260405f20825f5260205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541660ff83161790556040519081527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b507f5b08ba18000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346103ee5760406003193601126103ee576004356112d86126a1565b6112e0612f3d565b815f52600c60205260ff600160405f20015460c81c16156106b257815f52600c60205260ff600160405f20015460d81c1661068657816104696020927ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7946131bc565b346103ee575f6003193601126103ee576040515f6002548060011c90600181168015611478575b60208310811461144b5782855290811561140957506001146113ab575b610bc68361139781850382612733565b60405191829160208352602083019061261c565b91905060025f527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace915f905b8082106113ef57509091508101602001611397611387565b9192600181602092548385880101520191019092916113d7565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b840190910191506113979050611387565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f169161136a565b346103ee5760206003193601126103ee576004355f6101206040516114a681612716565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e0820152826101008201520152805f52600c60205260ff600160405f20015460c81c1615610585575f52600c60205260405f2060405161150e81612716565b81546001600160801b03811692838352602083019160801c82526001810154604084016001600160a01b0382168152606085018260a01c64ffffffffff16815260808601908360c81c60ff161515825260a08701928460d01c60ff161515845260c088019460d81c60ff161515855260028601549660e08901966001600160a01b03891688526101008a019860a01c60ff168952600301546001600160801b03169861012001988952604051998a52516001600160801b031660208a0152516001600160a01b031660408901525164ffffffffff166060880152511515608087015251151560a086015251151560c0850152516001600160a01b031660e08401525160ff16610100830152516001600160801b031661012082015261014090f35b346103ee5760406003193601126103ee5760043561164b6126a1565b611653612f3d565b815f52600c60205260ff600160405f20015460c81c16156106b257815f52600c60205260ff600160405f20015460d81c1661068657815f52600c6020526001600160a01b03600160405f20015416330361065657816104696020927ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce794613296565b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c602052602060405f205460801c604051908152f35b346103ee5760206003193601126103ee576004356001600160a01b0381168091036103ee576001600160a01b035f54163381036103bf575060095490807fffffffffffffffffffffffff00000000000000000000000000000000000000008316176009556001600160a01b036040519216825260208201527fa2548bd4b805e907c1558a47b5858324fe8bb4a2e1ddfca647eecbf65610eebc60403392a26008547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810190811161181b5760407f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c91815190600182526020820152a1005b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b346103ee5760206003193601126103ee5761186161265f565b5f546001600160a01b0381163381036103bf57506001600160a01b037fffffffffffffffffffffffff0000000000000000000000000000000000000000921691829116175f55337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf805f80a3005b346103ee5760206003193601126103ee576001600160a01b036118ef61265f565b16801561190c575f526004602052602060405f2054604051908152f35b7f89c62b64000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600360205260206001600160a01b0360405f205416604051908152f35b346103ee5760406003193601126103ee5761199f61265f565b6119a7612675565b6001600160a01b035f54163381036103bf57506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03831692602082602481875afa918215610c47575f92611aab575b50835f52600760205260405f20548083039280841161181b5714611a7f5781611a567f21252411d5a999da4bc6a490f7143b61ba690edceb4577a2800eab8dfbb1e92c9385611a7a94613a2e565b604051918291339583602090939291936001600160a01b0360408201951681520152565b0390a3005b837ff4c3afcf000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b9091506020813d602011611ad7575b81611ac760209383612733565b810103126103ee57519084611a08565b3d9150611aba565b346103ee5760206003193601126103ee576020611afd600435612ec5565b6001600160a01b0360405191168152f35b346103ee5760206003193601126103ee576001600160a01b03611b2f61265f565b165f526007602052602060405f2054604051908152f35b346103ee5760206003193601126103ee57600435611b62612f3d565b805f52600c60205260ff600160405f20015460c81c161561058557805f52600c60205260ff600160405f20015460d81c16611d4757611ba081613bc4565b6001600160801b03811615611d1b57815f52600c6020526001600160a01b03600160405f2001541633141580611d0b575b610656575f828152600c602090815260408083208054600380830180546001600160801b039384166fffffffffffffffffffffffffffffffff199091168117909155600184018054948290557fffffffff00ffff0000000000ffffffffffffffffffffffffffffffffffffffff90941678ffffffffff00000000000000000000000000000000000000004260a01b16177b01000000000000000000000000000000000000000000000000000000179384905590855294839020548351338152808601969096529516918401919091527ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce794919391926001600160a01b03928316929091169083907f35f9aa46e14f354fc906578188ddcf18c75e64b520fa7ee83ee1926069c59ef49080606081010390a4604051908152a1005b50611d1582614110565b15611bd1565b507ff814d2b0000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f7354d5f1000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346103ee5760a06003193601126103ee57602061050d611d9061265f565b611d98612675565b611da06126b7565b611da861268b565b91611db1612707565b93610501612f3d565b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c602052602060ff600160405f20015460d81c166040519015158152f35b346103ee575f6003193601126103ee5760206001600160a01b0360095416604051908152f35b346103ee57610edb611e41366126cd565b9060405192611e51602085612733565b5f8452612b6d565b346103ee5760406003193601126103ee57600435611e756126a1565b611e7d612f3d565b815f52600c60205260ff600160405f20015460c81c16156106b257815f52600c60205260405f205460801c15610d4557815f52600c6020526001600160a01b03600160405f2001541633036106565781610d3c6020927ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce794613296565b346103ee57610edb611f0b366126cd565b916127ff565b346103ee5760606003193601126103ee57600435611f2d6126a1565b611f356126b7565b90611f3e612f3d565b825f52600c60205260ff600160405f20015460c81c161561047257825f52600c60205260ff600160405f20015460d81c16611ffa57825f52600c6020526001600160a01b03600160405f200154163303611fca5791610469602092611fc47ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7958461313c565b826131bc565b827fa9ad2a22000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b827f7354d5f1000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346103ee575f6003193601126103ee576020600854604051908152f35b346103ee5760206003193601126103ee5760043567ffffffffffffffff81116103ee57366023820112156103ee57806004013567ffffffffffffffff81116103ee573660248260051b840101116103ee57905f907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbd81360301915b83811015610edb5760248160051b83010135838112156103ee57820160248101359067ffffffffffffffff82116103ee5760440181360381136103ee57815f92918392604051928392833781018381520390305af461211b6127d0565b901561212a57506001016120be565b612168906040519182917fd935448500000000000000000000000000000000000000000000000000000000835260206004840152602483019061261c565b0390fd5b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c60205260206001600160801b0360405f205416604051908152f35b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c161561058557610574602091613113565b346103ee5760206003193601126103ee57600435612210612f3d565b805f52600c60205260ff600160405f20015460c81c161561058557805f52600c60205260405f205460801c1561093757805f52600c6020526001600160a01b03600160405f20015416330361228c576020816104697ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce793612f97565b7fa9ad2a22000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b346103ee5760206003193601126103ee576001600160a01b036122dc61265f565b165f52600b60205260206001600160801b0360405f205416604051908152f35b346103ee5760406003193601126103ee5761231561265f565b60243561232181612ec5565b33151580612406575b806123d3575b6123a75781906001600160a01b0380851691167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a45f5260056020526001600160a01b0360405f2091167fffffffffffffffffffffffff00000000000000000000000000000000000000008254161790555f80f35b7fa9fbf51f000000000000000000000000000000000000000000000000000000005f523360045260245ffd5b506001600160a01b0381165f52600660205260405f206001600160a01b0333165f5260205260ff60405f20541615612330565b50336001600160a01b038216141561232a565b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c161561058557610574602091612f11565b346103ee5760206003193601126103ee576020611afd6004356127ae565b346103ee575f6003193601126103ee576040515f6001548060011c90600181168015612522575b60208310811461144b5782855290811561140957506001146124c457610bc68361139781850382612733565b91905060015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6915f905b80821061250857509091508101602001611397611387565b9192600181602092548385880101520191019092916124f0565b91607f1691612498565b346103ee5760206003193601126103ee57600435907fffffffff0000000000000000000000000000000000000000000000000000000082168092036103ee57817f80ac58cd00000000000000000000000000000000000000000000000000000000602093149081156125d1575b81156125a7575b5015158152f35b7f01ffc9a700000000000000000000000000000000000000000000000000000000915014836125a0565b7f5b5e139f0000000000000000000000000000000000000000000000000000000081149150612599565b5f5b83811061260c5750505f910152565b81810151838201526020016125fd565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093612658815180928187528780880191016125fb565b0116010190565b600435906001600160a01b03821682036103ee57565b602435906001600160a01b03821682036103ee57565b606435906001600160a01b03821682036103ee57565b602435906001600160801b03821682036103ee57565b604435906001600160801b03821682036103ee57565b60031960609101126103ee576004356001600160a01b03811681036103ee57906024356001600160a01b03811681036103ee579060443590565b6084359081151582036103ee57565b610140810190811067ffffffffffffffff821117610b0f57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610b0f57604052565b67ffffffffffffffff8111610b0f57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b6127b781612ec5565b505f5260056020526001600160a01b0360405f20541690565b3d156127fa573d906127e182612774565b916127ef6040519384612733565b82523d5f602084013e565b606090565b91906001600160a01b0316918215612ab657815f5260036020526001600160a01b0360405f205416151580612a99575b612a6d57815f5260036020526001600160a01b0360405f20541692331515806129a5575b50907ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce760206001600160a01b039386612938575b805f526004825260405f2060018154019055855f526003825260405f20817fffffffffffffffffffffffff00000000000000000000000000000000000000008254161790558560405191887fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4858152a11680830361290757505050565b7f64283d7b000000000000000000000000000000000000000000000000000000005f5260045260245260445260645ffd5b61296f865f52600560205260405f207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b865f526004825260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8154019055612887565b80612a16575b156129b6575f612853565b82846129e7577f7e273289000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f177e802f000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b503384148015612a44575b806129ab5750825f526005602052336001600160a01b0360405f205416146129ab565b50835f52600660205260405f206001600160a01b0333165f5260205260ff60405f205416612a21565b507f7da2ea2b000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b50815f52600c60205260ff600160405f20015460d01c161561282f565b7f64a0ae92000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b805f52600c60205260ff600160405f20015460c81c161561058557612b069061342f565b90565b805f52600c60205260ff600160405f20015460d81c16612b67576001600160801b03612b3482613bc4565b161515905f52600c60205260405f205460801c15612b5957612b54575f90565b600190565b612b6257600290565b600390565b50600490565b90612b798382846127ff565b803b612b86575b50505050565b602091612be56001600160a01b038093169560405195869485947f150b7a02000000000000000000000000000000000000000000000000000000008652336004870152166024850152604484015260806064840152608483019061261c565b03815f865af15f9181612cb8575b50612c3a5750612c016127d0565b80519081612c3557827f64a0ae92000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b602001fd5b7fffffffff000000000000000000000000000000000000000000000000000000007f150b7a0200000000000000000000000000000000000000000000000000000000911603612c8d57505f808080612b80565b7f64a0ae92000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b9091506020813d602011612d0d575b81612cd460209383612733565b810103126103ee57517fffffffff00000000000000000000000000000000000000000000000000000000811681036103ee57905f612bf3565b3d9150612cc7565b906001600160801b03809116911601906001600160801b03821161181b57565b906001600160801b0316908115612d53576001600160801b03160490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b805f52600c6020526001600160801b0360405f2054168015612ebf57815f52600c6020526001600160801b03600360405f20015416816001600160801b03612dd0612dca86613bfe565b84612d15565b161015612eb857612e24916001600160801b0391845f52600c60205260ff600260405f20015460a01c16601281145f14612e95575090038116600101165b825f52600c60205260405f205460801c90612d35565b6001600160801b038116915f52600c60205264ffffffffff600160405f20015460a01c169164ffffffffff8111612e6557509064ffffffffff809216011690565b7f6dfcc650000000000000000000000000000000000000000000000000000000005f52602860045260245260445ffd5b838092612eac60ff60019460120316600a0a613ca0565b94031601160216612e0e565b5050505f90565b50505f90565b805f5260036020526001600160a01b0360405f205416908115612ee6575090565b7f7e273289000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b612b0690805f52600c602052612f376001600160801b03600360405f2001541691613bfe565b90612d15565b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000163003612f6f57565b7fa1c0d6e5000000000000000000000000000000000000000000000000000000005f5260045ffd5b805f52600c60205260405f205460801c156130c457612fb581613bfe565b6001600160801b038116613081575b505f818152600c6020818152604080842060018101805478ffffffffff00000000000000000000000000000000000000004260a01b167fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff82161790915581546001600160801b03908116835560038086529684902054958552959091015491519190941681526001600160a01b039283169492909316927f3d86c5ea0db69a6591bd7fc8ffb71b4d5ae3666e3020b5382c920d631ebad19c9190a4565b815f52600c6020526001600160801b036130a5600360405f20019282845416612d15565b166fffffffffffffffffffffffffffffffff198254161790555f612fc4565b7fd2657d5a000000000000000000000000000000000000000000000000000000005f526004525f60245260445ffd5b906001600160801b03809116911603906001600160801b03821161181b57565b612b0690805f52600c6020526131366001600160801b0360405f2054169161342f565b906130f3565b805f52600c60205260405f205460801c6131915761315a8282613a8a565b6001600160801b03604051921682527fc2a543cfadbf862642247e28711aaa30e3460384be5712be6557fee3384454fd60203393a3565b7fdc6fbbbc000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6001600160801b03821691821561326a575f828152600c602052604090206002810154815461323c9386936001600160a01b0390931692916001600160801b0391613208918316612d15565b166fffffffffffffffffffffffffffffffff19825416179055805f52600760205260405f20828154019055309033906140b6565b6040519182527fa06c1466b3c9751408a5ac337a2e8808e5ee0ceed1fd70635d041b21174eb6b460203393a3565b507f33f2df5a000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6001600160801b0382168015613403576001600160801b036132b783613113565b168082116133d157825f52600c6020526001600160801b0360405f205416106133885760207fe31f2d40d5780915b1e656a67e11bdf09b0a4a925ec42bbeae220c8ca937ab4991835f52600c825261337f816001600160a01b03600160405f200154168097875f52600c86526001600160a01b03600260405f2001541690885f52600c87526001600160801b038060405f2092818454160316166fffffffffffffffffffffffffffffffff19825416179055805f526007865260405f20838154039055613a2e565b604051908152a3565b90805f52600c6020526001600160801b0360405f205416907fcb5f605f000000000000000000000000000000000000000000000000000000005f5260045260245260445260645ffd5b917fe9771401000000000000000000000000000000000000000000000000000000005f5260045260245260445260645ffd5b507fea66b871000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b805f52600c6020526001600160801b0360405f205416908115612ebf5761345590612f11565b906001600160801b038216811061346a575090565b905090565b6001600160a01b039095949293919516908115613a06576001600160a01b0316926040517f313ce567000000000000000000000000000000000000000000000000000000008152602081600481885afa8015610c47575f906139c9575b60ff915016956012871161399d57600854968786604051926134ed84612716565b5f845260208401956001600160801b031695868152604085019088825260608601994264ffffffffff168b52608087016001815260a088019115159b8c835260c08901935f855260e08a019788526101008a019687526101208a01985f8a525f52600c60205260405f2099516001600160801b03166001600160801b03168a546fffffffffffffffffffffffffffffffff1916178a55516001600160801b03166135bb908a906001600160801b036fffffffffffffffffffffffffffffffff1983549260801b169116179055565b9351600189018054955178ffffffffff000000000000000000000000000000000000000060a09190911b166001600160a01b039092167fffffffffffffff00000000000000000000000000000000000000000000000000909616959095171784555115159083549051151560d01b7aff0000000000000000000000000000000000000000000000000000169160c81b79ff0000000000000000000000000000000000000000000000000016907fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff161717825551151581549060d81b7bff00000000000000000000000000000000000000000000000000000016907fffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffff161790556002840191516001600160a01b03166001600160a01b031682547fffffffffffffffffffffffff0000000000000000000000000000000000000000161782555181549060a01b74ff000000000000000000000000000000000000000016907fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16179055516001600160801b031690600301906001600160801b031681546fffffffffffffffffffffffffffffffff1916179055600187016008556001600160a01b0316928315612ab657865f5260036020526001600160a01b0360405f205416151580613980575b61395457865f5260036020526001600160a01b0360405f2054167ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7602082151592836138e7575b875f526004825260405f20600181540190558a5f526003825260405f20887fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055604051908b89827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4508a8152a16138bb577f2262fa211f8507786a60fefb231f7898b8145fe16350f281c91f121bd2ee118d916060916040519189835260208301526040820152a4565b7f73c6ac6e000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b61391e8b5f52600560205260405f207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b805f526004825260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8154019055613809565b867f7da2ea2b000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b50865f52600c60205260ff600160405f20015460d01c16156137c2565b847fc9f55392000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b506020813d6020116139fe575b816139e360209383612733565b810103126103ee575160ff811681036103ee5760ff906134cc565b3d91506139d6565b7ff9bb0fb3000000000000000000000000000000000000000000000000000000005f5260045ffd5b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000060208201526001600160a01b03929092166024830152604480830193909352918152613a8891613a83606483612733565b614182565b565b91906001600160801b03811692805f52600c60205260405f205460801c8414613b9457613a88929350613abc81613bfe565b6001600160801b038116613b51575b505f908152600c6020526040902060018101805478ffffffffff00000000000000000000000000000000000000004260a01b167fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff90911617905580546001600160801b031660809290921b6fffffffffffffffffffffffffffffffff1916919091179055565b815f52600c6020526001600160801b03613b75600360405f20019282845416612d15565b166fffffffffffffffffffffffffffffffff198254161790555f613acb565b83907fd2657d5a000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b805f52600c602052613be36001600160801b0360405f20541691612f11565b906001600160801b038216811015612ebf57612b06916130f3565b5f818152600c602052604090206001810154905464ffffffffff4281169360809290921c9260a01c1682158015613c96575b613c8e5764ffffffffff915f52600c60205260ff600260405f20015460a01c1693031602906001600160801b03821691820361181b5760128114613c8a5790613c8460ff612b069360120316600a0a613ca0565b90612d35565b5090565b505050505f90565b5080841115613c30565b6001600160801b038111613cba576001600160801b031690565b7f6dfcc650000000000000000000000000000000000000000000000000000000005f52608060045260245260445ffd5b6001600160801b03831692831561408a576001600160a01b03831692831561405e57825f5260036020526001600160a01b0360405f2054168414158061404e575b61401a57613d3883612f11565b94835f52600c6020526001600160801b0360405f205416906001600160801b03871682105f1461400c576001600160801b03825b16808211613fd957505f858152600c60205260409020600301546001600160801b031610613f5657835f52600c602052600360405f20016001600160801b038085818454160316166fffffffffffffffffffffffffffffffff198254161790555b5f848152600c6020908152604080832080546001600160801b03808216899003166fffffffffffffffffffffffffffffffff19909116178155600201546001600160a01b0316808452600a909252822054909791939080613ef8575b50613e79613e636001600160801b0393613e5a8594858a16908d5f52600760205260405f208281540390558d613a2e565b61313689612f11565b93875f52600c6020528260405f205416906130f3565b16911603613ecb57604080513381526001600160801b03938416602082015291909216918101919091527f1a7b0d6c8f96b874563b711cf97793fe3be5dc42dbd1e0720ce40f326918e81790606090a4565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b613e5a9450613e636001600160801b0393613f18613e7993869599614210565b97909798888d5f52600b602052868060405f2092818454160116166fffffffffffffffffffffffffffffffff19825416179055945050935050613e29565b5f848152600c602052604090206003810180546fffffffffffffffffffffffffffffffff19168589036001600160801b031617905560010180547fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff164260a01b78ffffffffff000000000000000000000000000000000000000016179055613dcd565b90857ff9f29859000000000000000000000000000000000000000000000000000000005f5260045260245260445260645ffd5b6001600160801b0387613d6c565b50507f4208ab4c000000000000000000000000000000000000000000000000000000005f526004523360245260445260645ffd5b5061405883614110565b15613d2b565b827f9f32c858000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b507fb4855052000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b9091926001600160a01b03613a889481604051957f23b872dd000000000000000000000000000000000000000000000000000000006020880152166024860152166044840152606483015260648252613a83608483612733565b805f5260036020526001600160a01b0360405f20541690813314918215614156575b50811561413d575090565b90506001600160a01b0361415133926127ae565b161490565b9091505f52600660205260405f206001600160a01b0333165f5260205260ff60405f205416905f614132565b5f806001600160a01b036141ab93169360208151910182865af16141a46127d0565b908361433f565b80519081151591826141ec575b50506141c15750565b7f5274afe7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b81925090602091810103126103ee57602001518015908115036103ee575f806141b8565b9190614225906001600160801b038416614272565b6001600160801b038111614247576001600160801b03612b06911680936130f3565b7f4916adce000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8382098382029182808310920391808303921461432e57670de0b6b3a76400008210156142fe577faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106699394670de0b6b3a7640000910990828211900360ee1b910360121c170290565b84907f5173648d000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b5050670de0b6b3a764000090049150565b9061437c575080511561435457805190602001fd5b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b815115806143c2575b61438d575090565b6001600160a01b03907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b1561438556fea164736f6c634300081a000a"; + hex"60a0604052346103a9576148626040813803918261001c816103ad565b9384928339810103126103a95780516001600160a01b03811691908290036103a957602001516001600160a01b038116908190036103a95761005e60406103ad565b91601083526f14d8589b1a595c88119b1bddc813919560821b602084015261008660406103ad565b60088152675341422d464c4f5760c01b60208201523060805283519092906001600160401b0381116102ba57600154600181811c9116801561039f575b602082101461029c57601f811161033c575b50602094601f82116001146102d9579481929394955f926102ce575b50508160011b915f199060031b1c1916176001555b82516001600160401b0381116102ba57600254600181811c911680156102b0575b602082101461029c57601f8111610239575b506020601f82116001146101d657819293945f926101cb575b50508160011b915f199060031b1c1916176002555b60016008555f80546001600160a01b031990811684178255600980549091169290921790915560405191907fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf808180a361448f90816103d3823960805181612ffe0152f35b015190505f80610152565b601f1982169060025f52805f20915f5b81811061022157509583600195969710610209575b505050811b01600255610167565b01515f1960f88460031b161c191690555f80806101fb565b9192602060018192868b0151815501940192016101e6565b60025f527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace601f830160051c81019160208410610292575b601f0160051c01905b8181106102875750610139565b5f815560010161027a565b9091508190610271565b634e487b7160e01b5f52602260045260245ffd5b90607f1690610127565b634e487b7160e01b5f52604160045260245ffd5b015190505f806100f1565b601f1982169560015f52805f20915f5b8881106103245750836001959697981061030c575b505050811b01600155610106565b01515f1960f88460031b161c191690555f80806102fe565b919260206001819286850151815501940192016102e9565b60015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6601f830160051c81019160208410610395575b601f0160051c01905b81811061038a57506100d5565b5f815560010161037d565b9091508190610374565b90607f16906100c3565b5f80fd5b6040519190601f01601f191682016001600160401b038111838210176102ba5760405256fe6080806040526004361015610012575f80fd5b5f3560e01c90816301ffc9a7146125e35750806306fdde0314612528578063081812fc1461250a57806308b87923146124d0578063095ea7b3146123b35780630c5fd19514612372578063136439dd146122da5780631400ecec146122a05780631e010439146122525780631e897afb146121295780631e99d5691461210c5780631f0cce5814611ff757806323b872dd14611fe0578063379d871a14611f3f57806342842e0e14611f1657806342e3e23d146108bb5780634426757014611ef0578063569f4c5914611ea0578063597150fa14611e585780635ea2145b14611b2c5780635f55315214611af45780636352211e14611ac5578063648bf7741461196c5780636d0cee751461191e57806370a08231146118b457806375829def1461182e5780637cad6cd11461170357806380448da3146116bb57806381632a861461162f578063894e9a0d1461148257806395d89b41146113435780639e9e2e13146112bc578063a22cb465146111ea578063a7de07cd1461119c578063a8a482a614611092578063ad35efd41461101a578063b256456914610fe5578063b5b3ca2c14610f15578063b62b31e414610edd578063b88d4fde14610e53578063b8a3be6614610e1e578063b971302a14610dcd578063bc063e1a14610dab578063bc7a2d6c14610d71578063bcbd019e14610c9b578063bdf2a43c14610c52578063c87b56dd14610b3c578063c928801914610962578063d4b80884146108d9578063d975dfed146108bb578063dcd7a5331461086a578063e4b50cb814610819578063e985e9c5146107c0578063ea5ead1914610730578063eb5710d8146106de578063ebb6f79a146105b0578063edfa12df1461053a578063f851a44014610515578063fbf2777e1461049e578063fdd46d60146103f25763ffe3d9f8146102bb575f80fd5b346103ee5760406003193601126103ee576102d4612716565b6102dc61272c565b6001600160a01b035f54163381036103bf57506001600160a01b03821691825f52600b6020526001600160801b0360405f20541690811561039357818361035792865f52600b60205260405f206fffffffffffffffffffffffffffffffff198154169055865f52600760205260405f20838154039055613ae5565b6001600160a01b036040519216825260208201527fc9a4a66b97fd7e52e69c5be7b10bdc5341bded817201b9b7136a75068d4e4e0560403392a3005b837ff717901b000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7fc6cce6a4000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b5f80fd5b346103ee5760606003193601126103ee5760043561040e61272c565b61041661276e565b9061041f612ff4565b825f52600c60205260ff600160405f20015460c81c161561047257916104696020927ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce79483613da1565b604051908152a1005b827fe21c1431000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346103ee5760c06003193601126103ee576104b7612716565b6104bf61272c565b906104c861276e565b916104d1612742565b916104da6127be565b60a435936001600160801b03851685036103ee5760209561050d9461050694610501612ff4565b613526565b9182613273565b604051908152f35b346103ee575f6003193601126103ee5760206001600160a01b035f5416604051908152f35b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c161561058557610574602091613cb5565b6001600160801b0360405191168152f35b7fe21c1431000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346103ee5760406003193601126103ee576004356105cc612758565b6105d4612ff4565b815f52600c60205260ff600160405f20015460c81c16156106b257815f52600c60205260ff600160405f20015460d81c1661068657815f52600c6020526001600160a01b03600160405f20015416330361065657816104696020927ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7946131f3565b507fa9ad2a22000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b507f7354d5f1000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b507fe21c1431000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c602052602064ffffffffff600160405f20015460a01c16604051908152f35b346103ee5760406003193601126103ee5760043561074c61272c565b610754612ff4565b815f52600c60205260ff600160405f20015460c81c16156106b2577ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce76020836107a86107a083966134e6565b809583613da1565b604051908152a16001600160801b0360405191168152f35b346103ee5760406003193601126103ee576107d9612716565b6001600160a01b036107e961272c565b91165f5260066020526001600160a01b0360405f2091165f52602052602060ff60405f2054166040519015158152f35b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c60205260206001600160a01b03600260405f20015416604051908152f35b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c60205260206001600160801b03600360405f20015416604051908152f35b346103ee5760206003193601126103ee576020610574600435612b99565b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c161561058557805f52600c60205260405f205460801c1561093757610928602091612e37565b64ffffffffff60405191168152f35b7f167274c9000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346103ee5760806003193601126103ee5760043561097e612758565b60407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126103ee576109b0612ff4565b815f52600c60205260ff600160405f20015460c81c16156106b257815f52600c60205260ff600160405f20015460d81c1661068657604051916040830183811067ffffffffffffffff821117610b0f576040526044356001600160a01b03811681036103ee578352606435602084019080825267016345785d8a00008111610ad857506001600160a01b0384511615610ab0577ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce793610a82610a796020956104699451906142c7565b85929192613273565b835f52600c85526001600160801b036001600160a01b0380600260405f20015416935116911691339061416d565b7f5f946a02000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f54b392b2000000000000000000000000000000000000000000000000000000005f5260045267016345785d8a000060245260445ffd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b346103ee5760206003193601126103ee57600435610b5981612f7c565b505f6001600160a01b0360095416916044604051809481937fe9dc637500000000000000000000000000000000000000000000000000000000835230600484015260248301525afa8015610c47575f90610bca575b610bc6906040519182916020835260208301906126d3565b0390f35b503d805f833e610bda81836127ea565b8101906020818303126103ee5780519067ffffffffffffffff82116103ee57019080601f830112156103ee57815191610c128361282b565b91610c2060405193846127ea565b838352602084830101116103ee57610bc692610c4291602080850191016126b2565b610bae565b6040513d5f823e3d90fd5b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c602052602060405f205460801c15604051908152f35b346103ee5760406003193601126103ee57600435610cb7612758565b610cbf612ff4565b815f52600c60205260ff600160405f20015460c81c16156106b257815f52600c60205260405f205460801c15610d4557815f52600c6020526001600160a01b03600160405f2001541633036106565781610d3c6020927ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce794613273565b6104698161304e565b507f167274c9000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c161561058557610574602091613c7b565b346103ee575f6003193601126103ee57602060405167016345785d8a00008152f35b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c60205260206001600160a01b03600160405f20015416604051908152f35b346103ee5760206003193601126103ee576004355f52600c602052602060ff600160405f20015460c81c166040519015158152f35b346103ee5760806003193601126103ee57610e6c612716565b610e7461272c565b6064359167ffffffffffffffff83116103ee57366023840112156103ee57826004013591610ea18361282b565b92610eaf60405194856127ea565b80845236602482870101116103ee576020815f926024610edb9801838801378501015260443591612c24565b005b346103ee5760206003193601126103ee576001600160a01b03610efe612716565b165f52600a602052602060405f2054604051908152f35b346103ee5760406003193601126103ee57610f2e612716565b602435906001600160a01b035f54163381036103bf575067016345785d8a00008211610fad576001600160a01b031690815f52600a60205260405f205490825f52600a6020528060405f205560405191825260208201527f371789a3d97098f3070492613273a065a7e8a19e009fd1ae92a4b4d4c71ed62d60403392a3005b507f34553172000000000000000000000000000000000000000000000000000000005f5260045267016345785d8a000060245260445ffd5b346103ee5760206003193601126103ee576004355f52600c602052602060ff600160405f20015460d01c166040519015158152f35b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c16156105855761105290612bc0565b6040516005821015611065576020918152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b346103ee5760406003193601126103ee576004356110ae612758565b6110b6612ff4565b815f52600c60205260ff600160405f20015460c81c16156106b257815f52600c60205260405f205460801c15610d4557815f52600c6020526001600160a01b03600160405f2001541633036106565781807f3c92c3a56d04d4a7bc02d85874ad093d97053f2b046ef83860ecd0df273079b960606020947ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce7965f52600c86526001600160801b0360405f205460801c916111708187613b41565b855f52600c885281600360405f200154169260405193845288840152166040820152a2604051908152a1005b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c602052602060ff600260405f20015460a01c16604051908152f35b346103ee5760406003193601126103ee57611203612716565b602435908115158092036103ee576001600160a01b031690811561129057335f52600660205260405f20825f5260205260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0081541660ff83161790556040519081527f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c3160203392a3005b507f5b08ba18000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346103ee5760406003193601126103ee576004356112d8612758565b6112e0612ff4565b815f52600c60205260ff600160405f20015460c81c16156106b257815f52600c60205260ff600160405f20015460d81c1661068657816104696020927ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce794613273565b346103ee575f6003193601126103ee576040515f6002548060011c90600181168015611478575b60208310811461144b5782855290811561140957506001146113ab575b610bc683611397818503826127ea565b6040519182916020835260208301906126d3565b91905060025f527f405787fa12a823e0f2b7631cc41b3ba8828b3321ca811111fa75cd3aa3bb5ace915f905b8082106113ef57509091508101602001611397611387565b9192600181602092548385880101520191019092916113d7565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b840190910191506113979050611387565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f169161136a565b346103ee5760206003193601126103ee576004355f6101206040516114a6816127cd565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e0820152826101008201520152805f52600c60205260ff600160405f20015460c81c1615610585575f52600c60205260405f2060405161150e816127cd565b81546001600160801b03811692838352602083019160801c82526001810154604084016001600160a01b0382168152606085018260a01c64ffffffffff16815260808601908360c81c60ff161515825260a08701928460d01c60ff161515845260c088019460d81c60ff161515855260028601549660e08901966001600160a01b03891688526101008a019860a01c60ff168952600301546001600160801b03169861012001988952604051998a52516001600160801b031660208a0152516001600160a01b031660408901525164ffffffffff166060880152511515608087015251151560a086015251151560c0850152516001600160a01b031660e08401525160ff16610100830152516001600160801b031661012082015261014090f35b346103ee5760406003193601126103ee5760043561164b612758565b611653612ff4565b815f52600c60205260ff600160405f20015460c81c16156106b257815f52600c6020526001600160a01b03600160405f20015416330361065657816104696020927ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce79461334d565b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c602052602060405f205460801c604051908152f35b346103ee5760206003193601126103ee576004356001600160a01b0381168091036103ee576001600160a01b035f54163381036103bf575060095490807fffffffffffffffffffffffff00000000000000000000000000000000000000008316176009556001600160a01b036040519216825260208201527fa2548bd4b805e907c1558a47b5858324fe8bb4a2e1ddfca647eecbf65610eebc60403392a26008547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81019081116118015760407f6bd5c950a8d8df17f772f5af37cb3655737899cbf903264b9795592da439661c91815190600182526020820152a1005b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b346103ee5760206003193601126103ee57611847612716565b5f546001600160a01b0381163381036103bf57506001600160a01b037fffffffffffffffffffffffff0000000000000000000000000000000000000000921691829116175f55337fbdd36143ee09de60bdefca70680e0f71189b2ed7acee364b53917ad433fdaf805f80a3005b346103ee5760206003193601126103ee576001600160a01b036118d5612716565b1680156118f2575f526004602052602060405f2054604051908152f35b7f89c62b64000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600360205260206001600160a01b0360405f205416604051908152f35b346103ee5760406003193601126103ee57611985612716565b61198d61272c565b6001600160a01b035f54163381036103bf57506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03831692602082602481875afa918215610c47575f92611a91575b50835f52600760205260405f2054808303928084116118015714611a655781611a3c7f21252411d5a999da4bc6a490f7143b61ba690edceb4577a2800eab8dfbb1e92c9385611a6094613ae5565b604051918291339583602090939291936001600160a01b0360408201951681520152565b0390a3005b837ff4c3afcf000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b9091506020813d602011611abd575b81611aad602093836127ea565b810103126103ee575190846119ee565b3d9150611aa0565b346103ee5760206003193601126103ee576020611ae3600435612f7c565b6001600160a01b0360405191168152f35b346103ee5760206003193601126103ee576001600160a01b03611b15612716565b165f526007602052602060405f2054604051908152f35b346103ee5760206003193601126103ee57600435611b48612ff4565b805f52600c60205260ff600160405f20015460c81c161561058557805f52600c60205260ff600160405f20015460d81c16611e2d57805f52600c6020526001600160a01b03600160405f2001541633141580611e1d575b611dee57602081611bd07ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce793613c7b565b6001600160801b038116611da457611be782613cb5565b6001600160801b038116611d62575b505b5f828152600c84526040902060010180547fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff164260a01b78ffffffffff000000000000000000000000000000000000000016179055815f52600c835260405f206001600160801b038154169055815f52600c8352600160405f20017b010000000000000000000000000000000000000000000000000000007fffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffff825416179055815f52600c83526001600160a01b03600160405f20015416825f5260038452827f35f9aa46e14f354fc906578188ddcf18c75e64b520fa7ee83ee1926069c59ef46001600160a01b0360405f20541693825f52600c87526001600160801b03600360405f20015416611d5660405192839233849160409194936001600160801b0380926001600160a01b03606087019816865216602085015216910152565b0390a4604051908152a1005b825f52600c84526001600160801b03611d85600360405f20019282845416612dcc565b166fffffffffffffffffffffffffffffffff1982541617905584611bf6565b815f52600c83526001600160801b0360405f205416825f52600c84526001600160801b03600360405f200191166fffffffffffffffffffffffffffffffff19825416179055611bf8565b7fa9ad2a22000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b50611e27816141c7565b15611b9f565b7f7354d5f1000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346103ee5760a06003193601126103ee57602061050d611e76612716565b611e7e61272c565b611e8661276e565b611e8e612742565b91611e976127be565b93610501612ff4565b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c602052602060ff600160405f20015460d81c166040519015158152f35b346103ee575f6003193601126103ee5760206001600160a01b0360095416604051908152f35b346103ee57610edb611f2736612784565b9060405192611f376020856127ea565b5f8452612c24565b346103ee5760406003193601126103ee57600435611f5b612758565b611f63612ff4565b815f52600c60205260ff600160405f20015460c81c16156106b257815f52600c60205260405f205460801c15610d4557815f52600c6020526001600160a01b03600160405f2001541633036106565781610d3c6020927ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce79461334d565b346103ee57610edb611ff136612784565b916128b6565b346103ee5760606003193601126103ee57600435612013612758565b61201b61276e565b90612024612ff4565b825f52600c60205260ff600160405f20015460c81c161561047257825f52600c60205260ff600160405f20015460d81c166120e057825f52600c6020526001600160a01b03600160405f2001541633036120b057916104696020926120aa7ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce795846131f3565b82613273565b827fa9ad2a22000000000000000000000000000000000000000000000000000000005f526004523360245260445ffd5b827f7354d5f1000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b346103ee575f6003193601126103ee576020600854604051908152f35b346103ee5760206003193601126103ee5760043567ffffffffffffffff81116103ee57366023820112156103ee57806004013567ffffffffffffffff81116103ee573660248260051b840101116103ee57905f907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbd81360301915b83811015610edb5760248160051b83010135838112156103ee57820160248101359067ffffffffffffffff82116103ee5760440181360381136103ee57815f92918392604051928392833781018381520390305af4612201612887565b901561221057506001016121a4565b61224e906040519182917fd93544850000000000000000000000000000000000000000000000000000000083526020600484015260248301906126d3565b0390fd5b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585575f52600c60205260206001600160801b0360405f205416604051908152f35b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c1615610585576105746020916131ca565b346103ee5760206003193601126103ee576004356122f6612ff4565b805f52600c60205260ff600160405f20015460c81c161561058557805f52600c60205260405f205460801c1561093757805f52600c6020526001600160a01b03600160405f200154163303611dee576020816104697ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce79361304e565b346103ee5760206003193601126103ee576001600160a01b03612393612716565b165f52600b60205260206001600160801b0360405f205416604051908152f35b346103ee5760406003193601126103ee576123cc612716565b6024356123d881612f7c565b331515806124bd575b8061248a575b61245e5781906001600160a01b0380851691167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9255f80a45f5260056020526001600160a01b0360405f2091167fffffffffffffffffffffffff00000000000000000000000000000000000000008254161790555f80f35b7fa9fbf51f000000000000000000000000000000000000000000000000000000005f523360045260245ffd5b506001600160a01b0381165f52600660205260405f206001600160a01b0333165f5260205260ff60405f205416156123e7565b50336001600160a01b03821614156123e1565b346103ee5760206003193601126103ee57600435805f52600c60205260ff600160405f20015460c81c161561058557610574602091612fc8565b346103ee5760206003193601126103ee576020611ae3600435612865565b346103ee575f6003193601126103ee576040515f6001548060011c906001811680156125d9575b60208310811461144b57828552908115611409575060011461257b57610bc683611397818503826127ea565b91905060015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6915f905b8082106125bf57509091508101602001611397611387565b9192600181602092548385880101520191019092916125a7565b91607f169161254f565b346103ee5760206003193601126103ee57600435907fffffffff0000000000000000000000000000000000000000000000000000000082168092036103ee57817f80ac58cd0000000000000000000000000000000000000000000000000000000060209314908115612688575b811561265e575b5015158152f35b7f01ffc9a70000000000000000000000000000000000000000000000000000000091501483612657565b7f5b5e139f0000000000000000000000000000000000000000000000000000000081149150612650565b5f5b8381106126c35750505f910152565b81810151838201526020016126b4565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f60209361270f815180928187528780880191016126b2565b0116010190565b600435906001600160a01b03821682036103ee57565b602435906001600160a01b03821682036103ee57565b606435906001600160a01b03821682036103ee57565b602435906001600160801b03821682036103ee57565b604435906001600160801b03821682036103ee57565b60031960609101126103ee576004356001600160a01b03811681036103ee57906024356001600160a01b03811681036103ee579060443590565b6084359081151582036103ee57565b610140810190811067ffffffffffffffff821117610b0f57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117610b0f57604052565b67ffffffffffffffff8111610b0f57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b61286e81612f7c565b505f5260056020526001600160a01b0360405f20541690565b3d156128b1573d906128988261282b565b916128a660405193846127ea565b82523d5f602084013e565b606090565b91906001600160a01b0316918215612b6d57815f5260036020526001600160a01b0360405f205416151580612b50575b612b2457815f5260036020526001600160a01b0360405f2054169233151580612a5c575b50907ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce760206001600160a01b0393866129ef575b805f526004825260405f2060018154019055855f526003825260405f20817fffffffffffffffffffffffff00000000000000000000000000000000000000008254161790558560405191887fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4858152a1168083036129be57505050565b7f64283d7b000000000000000000000000000000000000000000000000000000005f5260045260245260445260645ffd5b612a26865f52600560205260405f207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b865f526004825260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff815401905561293e565b80612acd575b15612a6d575f61290a565b8284612a9e577f7e273289000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b7f177e802f000000000000000000000000000000000000000000000000000000005f523360045260245260445ffd5b503384148015612afb575b80612a625750825f526005602052336001600160a01b0360405f20541614612a62565b50835f52600660205260405f206001600160a01b0333165f5260205260ff60405f205416612ad8565b507f7da2ea2b000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b50815f52600c60205260ff600160405f20015460d01c16156128e6565b7f64a0ae92000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b805f52600c60205260ff600160405f20015460c81c161561058557612bbd906134e6565b90565b805f52600c60205260ff600160405f20015460d81c16612c1e576001600160801b03612beb82613c7b565b161515905f52600c60205260405f205460801c15612c1057612c0b575f90565b600190565b612c1957600290565b600390565b50600490565b90612c308382846128b6565b803b612c3d575b50505050565b602091612c9c6001600160a01b038093169560405195869485947f150b7a0200000000000000000000000000000000000000000000000000000000865233600487015216602485015260448401526080606484015260848301906126d3565b03815f865af15f9181612d6f575b50612cf15750612cb8612887565b80519081612cec57827f64a0ae92000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b602001fd5b7fffffffff000000000000000000000000000000000000000000000000000000007f150b7a0200000000000000000000000000000000000000000000000000000000911603612d4457505f808080612c37565b7f64a0ae92000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b9091506020813d602011612dc4575b81612d8b602093836127ea565b810103126103ee57517fffffffff00000000000000000000000000000000000000000000000000000000811681036103ee57905f612caa565b3d9150612d7e565b906001600160801b03809116911601906001600160801b03821161180157565b906001600160801b0316908115612e0a576001600160801b03160490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b805f52600c6020526001600160801b0360405f2054168015612f7657815f52600c6020526001600160801b03600360405f20015416816001600160801b03612e87612e8186613cb5565b84612dcc565b161015612f6f57612edb916001600160801b0391845f52600c60205260ff600260405f20015460a01c16601281145f14612f4c575090038116600101165b825f52600c60205260405f205460801c90612dec565b6001600160801b038116915f52600c60205264ffffffffff600160405f20015460a01c169164ffffffffff8111612f1c57509064ffffffffff809216011690565b7f6dfcc650000000000000000000000000000000000000000000000000000000005f52602860045260245260445ffd5b838092612f6360ff60019460120316600a0a613d57565b94031601160216612ec5565b5050505f90565b50505f90565b805f5260036020526001600160a01b0360405f205416908115612f9d575090565b7f7e273289000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b612bbd90805f52600c602052612fee6001600160801b03600360405f2001541691613cb5565b90612dcc565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016300361302657565b7fa1c0d6e5000000000000000000000000000000000000000000000000000000005f5260045ffd5b805f52600c60205260405f205460801c1561317b5761306c81613cb5565b6001600160801b038116613138575b505f818152600c6020818152604080842060018101805478ffffffffff00000000000000000000000000000000000000004260a01b167fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff82161790915581546001600160801b03908116835560038086529684902054958552959091015491519190941681526001600160a01b039283169492909316927f3d86c5ea0db69a6591bd7fc8ffb71b4d5ae3666e3020b5382c920d631ebad19c9190a4565b815f52600c6020526001600160801b0361315c600360405f20019282845416612dcc565b166fffffffffffffffffffffffffffffffff198254161790555f61307b565b7fd2657d5a000000000000000000000000000000000000000000000000000000005f526004525f60245260445ffd5b906001600160801b03809116911603906001600160801b03821161180157565b612bbd90805f52600c6020526131ed6001600160801b0360405f205416916134e6565b906131aa565b805f52600c60205260405f205460801c613248576132118282613b41565b6001600160801b03604051921682527fc2a543cfadbf862642247e28711aaa30e3460384be5712be6557fee3384454fd60203393a3565b7fdc6fbbbc000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6001600160801b038216918215613321575f828152600c60205260409020600281015481546132f39386936001600160a01b0390931692916001600160801b03916132bf918316612dcc565b166fffffffffffffffffffffffffffffffff19825416179055805f52600760205260405f208281540190553090339061416d565b6040519182527fa06c1466b3c9751408a5ac337a2e8808e5ee0ceed1fd70635d041b21174eb6b460203393a3565b507f33f2df5a000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6001600160801b03821680156134ba576001600160801b0361336e836131ca565b1680821161348857825f52600c6020526001600160801b0360405f2054161061343f5760207fe31f2d40d5780915b1e656a67e11bdf09b0a4a925ec42bbeae220c8ca937ab4991835f52600c8252613436816001600160a01b03600160405f200154168097875f52600c86526001600160a01b03600260405f2001541690885f52600c87526001600160801b038060405f2092818454160316166fffffffffffffffffffffffffffffffff19825416179055805f526007865260405f20838154039055613ae5565b604051908152a3565b90805f52600c6020526001600160801b0360405f205416907fcb5f605f000000000000000000000000000000000000000000000000000000005f5260045260245260445260645ffd5b917fe9771401000000000000000000000000000000000000000000000000000000005f5260045260245260445260645ffd5b507fea66b871000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b805f52600c6020526001600160801b0360405f205416908115612f765761350c90612fc8565b906001600160801b0382168110613521575090565b905090565b6001600160a01b039095949293919516908115613abd576001600160a01b0316926040517f313ce567000000000000000000000000000000000000000000000000000000008152602081600481885afa8015610c47575f90613a80575b60ff9150169560128711613a5457600854968786604051926135a4846127cd565b5f845260208401956001600160801b031695868152604085019088825260608601994264ffffffffff168b52608087016001815260a088019115159b8c835260c08901935f855260e08a019788526101008a019687526101208a01985f8a525f52600c60205260405f2099516001600160801b03166001600160801b03168a546fffffffffffffffffffffffffffffffff1916178a55516001600160801b0316613672908a906001600160801b036fffffffffffffffffffffffffffffffff1983549260801b169116179055565b9351600189018054955178ffffffffff000000000000000000000000000000000000000060a09190911b166001600160a01b039092167fffffffffffffff00000000000000000000000000000000000000000000000000909616959095171784555115159083549051151560d01b7aff0000000000000000000000000000000000000000000000000000169160c81b79ff0000000000000000000000000000000000000000000000000016907fffffffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffff161717825551151581549060d81b7bff00000000000000000000000000000000000000000000000000000016907fffffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffff161790556002840191516001600160a01b03166001600160a01b031682547fffffffffffffffffffffffff0000000000000000000000000000000000000000161782555181549060a01b74ff000000000000000000000000000000000000000016907fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff16179055516001600160801b031690600301906001600160801b031681546fffffffffffffffffffffffffffffffff1916179055600187016008556001600160a01b0316928315612b6d57865f5260036020526001600160a01b0360405f205416151580613a37575b613a0b57865f5260036020526001600160a01b0360405f2054167ff8e1a15aba9398e019f0b49df1a4fde98ee17ae345cb5f6b5e2c27f5033e8ce76020821515928361399e575b875f526004825260405f20600181540190558a5f526003825260405f20887fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055604051908b89827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef5f80a4508a8152a1613972577f2262fa211f8507786a60fefb231f7898b8145fe16350f281c91f121bd2ee118d916060916040519189835260208301526040820152a4565b7f73c6ac6e000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b6139d58b5f52600560205260405f207fffffffffffffffffffffffff00000000000000000000000000000000000000008154169055565b805f526004825260405f207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81540190556138c0565b867f7da2ea2b000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b50865f52600c60205260ff600160405f20015460d01c1615613879565b847fc9f55392000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b506020813d602011613ab5575b81613a9a602093836127ea565b810103126103ee575160ff811681036103ee5760ff90613583565b3d9150613a8d565b7ff9bb0fb3000000000000000000000000000000000000000000000000000000005f5260045ffd5b6040517fa9059cbb0000000000000000000000000000000000000000000000000000000060208201526001600160a01b03929092166024830152604480830193909352918152613b3f91613b3a6064836127ea565b614239565b565b91906001600160801b03811692805f52600c60205260405f205460801c8414613c4b57613b3f929350613b7381613cb5565b6001600160801b038116613c08575b505f908152600c6020526040902060018101805478ffffffffff00000000000000000000000000000000000000004260a01b167fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff90911617905580546001600160801b031660809290921b6fffffffffffffffffffffffffffffffff1916919091179055565b815f52600c6020526001600160801b03613c2c600360405f20019282845416612dcc565b166fffffffffffffffffffffffffffffffff198254161790555f613b82565b83907fd2657d5a000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b805f52600c602052613c9a6001600160801b0360405f20541691612fc8565b906001600160801b038216811015612f7657612bbd916131aa565b5f818152600c602052604090206001810154905464ffffffffff4281169360809290921c9260a01c1682158015613d4d575b613d455764ffffffffff915f52600c60205260ff600260405f20015460a01c1693031602906001600160801b0382169182036118015760128114613d415790613d3b60ff612bbd9360120316600a0a613d57565b90612dec565b5090565b505050505f90565b5080841115613ce7565b6001600160801b038111613d71576001600160801b031690565b7f6dfcc650000000000000000000000000000000000000000000000000000000005f52608060045260245260445ffd5b6001600160801b038316928315614141576001600160a01b03831692831561411557825f5260036020526001600160a01b0360405f20541684141580614105575b6140d157613def83612fc8565b94835f52600c6020526001600160801b0360405f205416906001600160801b03871682105f146140c3576001600160801b03825b1680821161409057505f858152600c60205260409020600301546001600160801b03161061400d57835f52600c602052600360405f20016001600160801b038085818454160316166fffffffffffffffffffffffffffffffff198254161790555b5f848152600c6020908152604080832080546001600160801b03808216899003166fffffffffffffffffffffffffffffffff19909116178155600201546001600160a01b0316808452600a909252822054909791939080613faf575b50613f30613f1a6001600160801b0393613f118594858a16908d5f52600760205260405f208281540390558d613ae5565b6131ed89612fc8565b93875f52600c6020528260405f205416906131aa565b16911603613f8257604080513381526001600160801b03938416602082015291909216918101919091527f1a7b0d6c8f96b874563b711cf97793fe3be5dc42dbd1e0720ce40f326918e81790606090a4565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52600160045260245ffd5b613f119450613f1a6001600160801b0393613fcf613f30938695996142c7565b97909798888d5f52600b602052868060405f2092818454160116166fffffffffffffffffffffffffffffffff19825416179055945050935050613ee0565b5f848152600c602052604090206003810180546fffffffffffffffffffffffffffffffff19168589036001600160801b031617905560010180547fffffffffffffff0000000000ffffffffffffffffffffffffffffffffffffffff164260a01b78ffffffffff000000000000000000000000000000000000000016179055613e84565b90857ff9f29859000000000000000000000000000000000000000000000000000000005f5260045260245260445260645ffd5b6001600160801b0387613e23565b50507f4208ab4c000000000000000000000000000000000000000000000000000000005f526004523360245260445260645ffd5b5061410f836141c7565b15613de2565b827f9f32c858000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b507fb4855052000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b9091926001600160a01b03613b3f9481604051957f23b872dd000000000000000000000000000000000000000000000000000000006020880152166024860152166044840152606483015260648252613b3a6084836127ea565b805f5260036020526001600160a01b0360405f2054169081331491821561420d575b5081156141f4575090565b90506001600160a01b036142083392612865565b161490565b9091505f52600660205260405f206001600160a01b0333165f5260205260ff60405f205416905f6141e9565b5f806001600160a01b0361426293169360208151910182865af161425b612887565b90836143f6565b80519081151591826142a3575b50506142785750565b7f5274afe7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b81925090602091810103126103ee57602001518015908115036103ee575f8061426f565b91906142dc906001600160801b038416614329565b6001600160801b0381116142fe576001600160801b03612bbd911680936131aa565b7f4916adce000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b9190917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff838209838202918280831092039180830392146143e557670de0b6b3a76400008210156143b5577faccb18165bd6fe31ae1cf318dc5b51eee0e1ba569b88cd74c1773b91fac106699394670de0b6b3a7640000910990828211900360ee1b910360121c170290565b84907f5173648d000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b5050670de0b6b3a764000090049150565b90614433575080511561440b57805190602001fd5b7f1425ea42000000000000000000000000000000000000000000000000000000005f5260045ffd5b81511580614479575b614444575090565b6001600160a01b03907f9996b315000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b50803b1561443c56fea164736f6c634300081a000a"; bytes public constant BYTECODE_NFT_DESCRIPTOR = hex"60808060405234601557610ae4908161001a8239f35b5f80fdfe60806040526004361015610011575f80fd5b5f3560e01c63e9dc637514610024575f80fd5b346107d65760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126107d65760043573ffffffffffffffffffffffffffffffffffffffff8116036107d6576107cd604061073c61059561044061008c845191826107fb565b61041c81527f3c7376672077696474683d2235303022206865696768743d223530302220737460208201527f796c653d226261636b67726f756e642d636f6c6f723a20233134313631463b22848201527f20786d6c6e733d22687474703a2f2f7777772e77332e6f72672f323030302f7360608201527f7667222076696577426f783d223230202d343030203230302031303030223e3c60808201527f706174682069643d224c6f676f222066696c6c3d2223666666222066696c6c2d60a08201527f6f7061636974793d22312220643d226d3133332e3535392c3132342e3033346360c08201527f2d2e3031332c322e3431322d312e3035392c342e3834382d322e3932332c362e60e08201527f3430322d322e3535382c312e3831392d352e3136382c332e3433392d372e38386101008201527f382c342e3939362d31342e34342c382e3236322d33312e3034372c31322e35366101208201527f352d34372e3637342c31322e3536392d382e3835382e3033362d31372e3833386101408201527f2d312e3237322d32362e3332382d332e3636332d392e3830362d322e3736362d6101608201527f31392e3038372d372e3131332d32372e3536322d31322e3737382d31332e38346101808201527f322d382e3032352c392e3436382d32382e3630362c31362e3135332d33352e326101a08201527f3635683063322e3033352d312e3833382c342e3235322d332e3534362c362e346101c08201527f36332d352e323234683063362e3432392d352e3635352c31362e3231382d322e6101e08201527f3833352c32302e3335382c342e31372c342e3134332c352e3035372c382e38316102008201527f362c392e3634392c31332e39322c31332e373334682e30333763352e3733362c6102208201527f362e3436312c31352e3335372d322e3235332c392e33382d382e34382c302c306102408201527f2d332e3531352d332e3531352d332e3531352d332e3531352d31312e34392d316102608201527f312e3437382d35322e3635362d35322e3636342d36342e3833372d36342e38336102808201527f376c2e3034392d2e303337632d312e3732352d312e3630362d322e3731392d336102a08201527f2e3834372d322e3735312d362e3230346830632d2e3034362d322e3337352c316102c08201527f2e3036322d342e3538322c322e3732362d362e32323968306c2e3138352d2e316102e08201527f34386830632e3039392d2e3036322c2e3232322d2e3134382c2e33372d2e32356103008201527f39683063322e30362d312e3336322c332e3935312d322e3632312c362e3034346103208201527f2d332e3834324335372e3736332d332e3437332c39372e37362d322e3334312c6103408201527f3132382e3633372c31382e3333326331362e3637312c392e3934362d32362e336103608201527f34342c35342e3831332d33382e3635312c34302e3139392d362e3239392d362e6103808201527f3039362d31382e3036332d31372e3734332d31392e3636382d31382e3831312d6103a08201527f362e3031362d342e3034372d31332e3036312c342e3737362d372e3735322c396103c08201527f2e3735316c36382e3235342c36382e33373163312e3732342c312e3630312c326103e08201527f2e3731342c332e38342c322e3733382c362e3139325a22207472616e73666f726104008201527f6d3d227363616c6528312e352c20312e352922202f3e3c2f7376673e000000006104208201526108a3565b610737600260c8855180947f7b226465736372697074696f6e223a202254686973204e46542072657072657360208301527f656e74732061207061796d656e742073747265616d20696e205361626c696572888301527f20466c6f77222c0000000000000000000000000000000000000000000000000060608301527f2265787465726e616c5f75726c223a202268747470733a2f2f7361626c69657260678301527f2e636f6d222c000000000000000000000000000000000000000000000000000060878301527f226e616d65223a20225361626c69657220466c6f77222c000000000000000000608d8301527f22696d616765223a2022646174613a696d6167652f7376672b786d6c3b62617360a48301527f6536342c0000000000000000000000000000000000000000000000000000000060c48301526106e281518092602086860191016107da565b81017f227d0000000000000000000000000000000000000000000000000000000000008382015203017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe28101845201826107fb565b6108a3565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8351926107b2603d8560208101937f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c00000085526107a381518092602086860191016107da565b810103018481018652856107fb565b845195869460208652518092816020880152878701906107da565b01168101030190f35b5f80fd5b5f5b8381106107eb5750505f910152565b81810151838201526020016107dc565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761083c57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b67ffffffffffffffff811161083c57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b90815115610ac157604051916108ba6060846107fb565b604083527f4142434445464748494a4b4c4d4e4f505152535455565758595a61626364656660208401527f6768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2f6040840152805160028101809111610a945760039004908160021b917f3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811603610a945761096b61095583610869565b9261096360405194856107fb565b808452610869565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe06020840191013682379080815182019560208701908151925f83525b888110610a4657505060039394959650525106806001146109f7576002146109ce575090565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff603d91015390565b507ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe81603d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81940153015390565b600360049199969901986001603f8b5182828260121c16870101518453828282600c1c16870101518385015382828260061c16870101516002850153168401015160038201530194976109a8565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9050604051610ad16020826107fb565b5f81529056fea164736f6c634300081a000a"; diff --git a/src/SablierFlow.sol b/src/SablierFlow.sol index b679ce90..de92c6ba 100644 --- a/src/SablierFlow.sol +++ b/src/SablierFlow.sol @@ -702,16 +702,14 @@ contract SablierFlow is emit ISablierFlow.RestartFlowStream(streamId, msg.sender, ratePerSecond); } - /// @dev Voids a stream. + /// @dev See the documentation for the user-facing functions that call this internal function. function _void(uint256 streamId) internal { - uint128 debtToWriteOff = _uncoveredDebtOf(streamId); - // Check: `msg.sender` is either the stream's sender, recipient or an approved third party. if (msg.sender != _streams[streamId].sender && !_isCallerStreamRecipientOrApproved(streamId)) { revert Errors.SablierFlow_Unauthorized({ streamId: streamId, caller: msg.sender }); } - uint128 balance = _streams[streamId].balance; + uint128 debtToWriteOff = _uncoveredDebtOf(streamId); // If the stream is solvent, update the total debt normally. if (debtToWriteOff == 0) { @@ -724,7 +722,7 @@ contract SablierFlow is // If the stream is insolvent, write off the uncovered debt. else { // Effect: update the total debt by setting snapshot debt to the stream balance. - _streams[streamId].snapshotDebt = balance; + _streams[streamId].snapshotDebt = _streams[streamId].balance; } // Effect: update the snapshot time. diff --git a/tests/fork/Flow.t.sol b/tests/fork/Flow.t.sol index e8261ac1..beaaf4c4 100644 --- a/tests/fork/Flow.t.sol +++ b/tests/fork/Flow.t.sol @@ -532,10 +532,6 @@ contract Flow_Fork_Test is Fork_Test { resetPrank({ msgSender: sender }); - if (flow.isPaused(streamId)) { - flow.restart(streamId, RATE_PER_SECOND); - } - if (uncoveredDebt > 0) { expectedTotalDebt = flow.getBalance(streamId); } else {