Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Segment[] array, comptroller, protocol and operator fees, structified parameters, via IR, PRBMath types, major tests refactor #220

Merged
merged 74 commits into from
Jan 5, 2023

Conversation

PaulRBerg
Copy link
Member

@PaulRBerg PaulRBerg commented Dec 28, 2022

Description

Implements #11, #22, #117, #202, #203, #206, #207, #208, #209, #210, #211, #214, #217, #218, #219, #223, #225, #226, #230, #231, #236, #237, and #238.

This is the biggest PR that I have ever created in this repository. Documenting the minutiae of all new features and changes I made would be impractical, so I listed only the most significant stuff below.

New Features

Comptroller

We now have a SablierV2Comptroller contract. See #206.

Protocol Fees

We now have protocol fees (#207).

Operator Fees

We now have operator fees in all create functions (#208).

I opted for not implementing separate special functions that have operator fees.

PRBMath Types

New file types/MathTypes that re-exports PRBMath types (#202).

Coverage in CI

See #210 and #217.

Changes

Production

Tests

  • I added CreateStreamArgs and DefaultArgs structs to improve the readability of the arguments passed in the create function tests.
  • I added tests for expecting calls to the ERC-20 contracts (Write tests that expect calls to ERC-20 contracts #209).
  • I dryified all tests (DRY-ify the tests #218), which means that we now have test coverage for the _cancel and _withdraw functions of the SablierV2Pro contract (Cover the "_cancel" and "_withdraw" internal functions of "SablierV2Pro" #117)
  • I fuzzed all function arguments that I could fuzz (Write fuzz tests #22). The only feature that proved intractable for fuzzing were the segment arrays in the pro contract - I may open an issue or a discussion to document my findings later on.
    • By fuzzing arguments, I was able to significantly simplify the complexity of the testing trees. Basically, multiple branches merged into a single fuzzing branch.
  • I merged many test leaves into one leaf, in order to avoid repeating myself during the initialization of the test. A case in point example is the leaf for the createWithRange test.
  • I removed the unit tests that were creating streams with USDC, because I realized that we don't actually need to test tokens with different decimals. We are never actually pulling the token decimals in our code base - what we need to test is simply different deposit amounts, which are testing now.
  • Our CI set up now compiles and tests the production code with --via-ir enabled (Follow Seaport's Foundry set-up to enable "--via-ir" but avoid it when running tests #217)
  • The test contracts are all deployed from the base test contract BaseTest. There is no UnitTest contract anymore.
  • The test contracts are all labeled now.
  • The test contracts are named just linear instead of sablierV2Linear and pro instead of sablierV2Pro.
  • Using the solarray library instead of our custom createDynamicArray helper functions (Ditch the "createDynamicArray" helpers in favor of the "solarray" library #211). I ended up forking solarray and using my fork in our repo, because the canonical repo was lacking many features that we needed (uint40, PRBMath types, etc.)

docs: improve wording in NatSpec
refactor: define "MAX_SEGMENT_COUNT" in "ISablierV2Pro"
feat: add "MAX_GLOBAL_FEE" in "SablierV2"
feat: implement "Ownable" in "SablierV2"
test: add helper functions "bound"
test: add "owner" user
test: deploy the contracts from the "owner" user
test: improve wording in comments
test: rename non-compliant token
test: test "setGlobalFee" function
build: enable "--via-ir" in Foundry config and override default steps
build: set "main" branch for "prb-math" in ".gitmodules"
build: upgrade to latest "forge-std" and "prb-math"
chore: improve wording in NatSpec and code comments
docs: change "Reads" to "Queries" in constant functions' NatSpec
docs: mention "Transfer" event in create functions' NatSpec
feat: add "checkAndCalculateFees" helper in "Helpers" library
feat: add protocol revenues (WIP)
feat: add "SablierV2Comptroller" contract
feat: link comptroller in "SablierV2Linear" contract
feat: set comptroller in "SablierV2Linear" constructor
feat: set comptroller in "SablierV2Pro" constructor
refactor: mint NFT after bumping "nextStreamId"
refactor: load protocol fees from comptroller
refactor: rename "depositAmount" to "grossDepositAmount" in create function
refactor: rename global fees to protocol fees
refactor: rename "MAX_GLOBAL_FEE" to "MAX_FEE"
refactor: rename "Validations" library to "Helpers"
test: import typed "bound" utils from PRBMath and delete local
test: load comptroller in "UnitTest"
test: test "setProtocolFee" function in "SablieV2Comptroller"
chore: improve wording in code comments
docs: improve wording in NatSpec
docs: be more specific in NatSpec for "MAX_FEE"
refactor: rename "createWithDuration" function to "createStream"
refactor: rename "depositAmount" arg to "grossDepositAmount"
refactor: reverse order of "operator" and "operatorFee" args
test: add "DEFAULT_" prefix to all constants
test: add "deployAndDealToken" helper
test: add "CreateStreamArgs" and "DefaultArgs" structs
test: add tests for when protocol and operator fee are too high in "createStream" tests
test: assert NFT has been minted in "token missing return value" testing branch in "createStream" tests
test: delete now stale testing branches in "createStream" tests
test: delete unneeded constants ("STARTING_BLOCK_TIMESTAMP", "CLIFF_DURATION", and others)
test: fuzz all args in "createStream" tests (except for "sender" and "cancelable")
test: fuzz args in "cliffTime > stopTime" testing branch in "createStream" tests
test: fuzz args in "startTime > cliffTime" testing branch in "createStream" tests
test: fuzz token decimals in "createStream" tests
test: order assertions alphabetically
test: rename "DEPOSIT_AMOUNT_DAI" to "GROSS_DEPOSIT_AMOUNT"
test: replace "daiStream" and "usdcStream" with "defaultStream"
test: simplify and improve tests for "createStream" function (formerly "create")
test: use "ERC20" instead of "ERC20GodMode"
test: use default args instead of struct instance in create stream helpers
test: expect event to be emitted in "testCreateStream__TokenMissingReturnValue"
test: add "DEFAULT_OPERATOR_FEE_AMOUNT" constant
docs: be more specific in  NatSpec
perf: use "msg.sender" instead of "owner" in "setComptroller";
refactor: add "override" keyword to "setComptroller"
test: add "DEFAULT_PROTOCOL_FEE" and "DEFAULT_PROTOCOL_FEE_AMOUNT"
test: change prank to "users.owner" in "CallerOwner" modifier
test: fix typo in non-cancelable streams tests
test: improve wording in code comments
test: test "claimProtocolRevenues" function
test: add simple "@dev" NatSpec for all "setUp" functions
test: delete unused imports
docs: add "Notes:" section in NatSpec
docs: improve formatting and wording in NatSpec
refactor: rename internal function "_create" to "_createWithRange"
refactor: rename "createStream" to "createWithDuration"
style: run prettier to fix formatting issues
test: add "boundUint40" helper function
test: add constants "DEFAULT_CLIFF_DURATION" and "DEFAULT_TOTAL_DURATION"
test: add "CreateWithDurationArgs" helper struct
test: add explanatory code comments in "createWithDuration" tests
test: assert stream id has been bumped in "testCreateWithDuration"
test: expect calls to token contract in "testCreateWithDuration"
test: use "bound" instead of "vm.assume" in "createWithDuration" tests
chore: improve wording in code comments
docs: improve wording in NatSpec
ci: fuzz 10,000 times
refactor: reverse order of "returnAmount" and "withdrawAmount" in "Cancel" event
test: add "createDefaultStreamWithStopTime" helper function
test: add more explanatory code comments
test: add test for when the recipient does not revert in "cancel"
test: add test for when the recipient reentries in "cancel"
test: create default stream in "StreamExistent" modifier in "SablierV2Linear" tests
test: delete stale constant "TIME_OFFSET" and use "DEFAULT_TIME_WARP"
test: delete usdc token and "usdcStream"
test: expect calls to token contracts in all "SablierV2Linear" tests
test: explain the role of each user in "Users" struct
test: improve wording in modifiers, test function names and test trees
test: merge leaves in single, bulkier tests
test: move "former recipient" tests higher up the tree
test: refactor "NonReverting" to "Good"
test: refer to recipient as "NFT owner" in NFT existence tests
test: rename "createDefaultDaiStream" to "createDefaultStream"
test: rename "createNonCancelableDaiStream" to "createDefaultStreamNonCancelable"
test: rename "daiStreamId" to "defaultStreamId"
test: rename "decimals" to "tokenDecimals"
test: rename "defaultStreamId" to just "streamId"
test: rename "DEFAULT_TIME_OFFSET" to "DEFAULT_TIME_WARP"
test: rename "nonCancelableDefaultStreamId" to just "streamId"
test: reorder constants and functions alphabetically
test: set default protocol fee in base "setUp" function
test: simplify and improve all tests for "SablierV2Linear" tests
test: test for NFT existence only when necessary
test: update test trees to conform to fuzzing logic
test: use "defaultStream" values instead of "users"
test: use named args in "wihdraw" and "withdrawAll" calls
test: use "ownerOf" instead of "getRecipient" in NFT existence tests
chore: improve wording in code comments
docs: improve wording in NatSpec comments
fix: calculate fees before checking linear args
fix: handle case when "grossDepositAmount" is zero
fix: rename "SablierV2__GrossDepositAmountZero" to "SablierV2__NetDepositAmountZero"
perf: split "getWithdrawableAmount" in two subfunctions in "SablierV2Pro"
refactor: change order of args in "createWithDeltas" and "createWithMilestones"
refactor: do not emit "stopTime" in "CreateProStream" event
refactor: do not return "streamId" in internal create function in "SablierV2Pro"
refactor: record the fee after creating the stream
refactor: rename "createWithDuration" to "createWithDeltas" in "SablierV2Pro"
refactor: rename "createWithRange" to "createWithMilestones" in "SablierV2Pro"
refactor: rename "depositAmount" to "grossDepositAmount" in external create functions
refactor: rename "depositAmount" to "netDepositAmount" in internal create functions
refactor: rename "DepositAmountNotEqualToSegmentAmountsSum" to
"NetDepositAmountNotEqualToSegmentAmountsSum"
refactor: rename "SegmentCountOutOfBounds" to "SegmentCountTooHigh"
refactor: use named args in "safeTransfer" call
test: add more explanatory comments
test: add "calculateStreamedAmount" helper function
test: add "calculateStreamedAmountForMultipleSegments" helper function
test: add "calculateStreamedAmountForOneSegment" helper function
test: add "CreateStreamArgs" and "DefaultArgs" structs in "SablierV2Pro" tests
test: add "CreateStreamArgs" and "DefaultArgs" structs in "SablierV2Pro" tests
test: check "operator" is not "recipient" in "testBurn__CallerApprovedOperator"
test: cluster "vm.assume" assumptions" to speed up fuzzing
test: create default stream in "StreamExistent" modifier in "SablierV2Pro" tests
test: delete dummy NatSpec at the top of every "setUp" function
test: delete "deployAndDealToken" helper function
test: delete token decimals fuzz (useless tests)
test: expect calls to token contracts in all "SablierV2Pro" tests
test: explain how fuzzing works in tests' NatSpec
test: fuzz "eve" in non-sender tests
test: improve wording in testing trees
test: fix "DEFAULT_" constants values
test: fuzz all "SablierV2Pro" tests
test: merge leaves in single, bulkier tests
test: rename "daiStreamId" to "defaultStreamId"
test: rename "defaultStreamId" to just "streamId"
test: rename "nonCancelableDaiStreamId" to "streamId"
test: set default protocol fee in base "setUp" function
test: set "max_test_rejects" to 100,000 in default profile
test: rename "createDefaultDaiStream" to "createDefaultStream"
test: rename "token" to "nonToken" in non-contract tests
test: test protocol fee record in all create function tests
test: test protocol fee is recorded by pre-loading the protocol revenues
test: update test trees to conform to fuzzing logic
test: use "defaultArgs.createWithRange" instead of "defaultStream"
test: use "defaultStream.token" instead of "address(dai)"
test: use "uint256" for "timeWarp", where possible
test: use "ud" instead of "wrap" and "UD60x18.wrap"
refactor: assign to "stream" return arg instead of returning stream
style: run Prettier to fix formatting issues
build: add "solarray" dep
chore: use spaces instead of tabs in ".gitmodules"
test: delete "createDynamicArray" functions
chore: add more explanatory code comments
chore: delete stale script "lint:check"
chore: improve wording in code comments
chore: remove "verbosity = 4" in "ci" profile
ci: add concurrency lock to prevent accidental runs
docs: improve wording in NatSpec
test: add "calculateFeeAmounts" helper function
test: add event emit for "createWithDeltas"
test: delete "UnitTest" contract
test: label test contracts
test: fuzz 1,000 times by default
test: fix test tree for "createWithDeltas"
test: move all test constants in "BaseTest"
test: optimize test vars for IR in create function tests
test: rename "SablierV2ComptrollerTest" to "ComptrollerTest"
refactor: move "DataTypes" to new "types" directory
refactor: rename "WithdrawAllArraysNotEqual" to "WithdrawArraysNotEqual"
@PaulRBerg
Copy link
Member Author

Regarding params - TIL! Thanks for the link. I agree that we should refactor args to params in the internal create functions.

For better understanding the only arguments we have are

Not sure what you meant by this, but you can explain offline.

feat: add "Durations" struct
refactor: rename "createWithDuration" to "createWithDurations" (plural)
test: add "DEFAULT_DURATIONS" constant
test: add more create function helpers
test: call only "createWithDeltas" in "createWithDeltas" tests
test: call only "createWithDurations" in "createWithDurations" tests
docs: explain that the broker params are option
docs: improve wording in NatSpec comments
refactor: bundle "operator" and "operatorFee" in "Broker" struct
refactor: rename "OperatorFeeTooHigh"  to "BrokerFeeTooHigh"
test: add "broker" user
test: delete unneeded "vm.assume" assumption in "createWithRange" tests
test: move broker params at the end
test: pass "err" param to "assertEq" assertions in integration tests
test: update tests to match latest contract API
build: add "test-optimized" profile to test contracts compiled with --via-ir
chore: add "test" and "test:optimized" scripts in "package.json"
chore: delete stale "solhint" script
chore: delete "real-time-finance" tag in "package.json"
ci: test optimized code in "test" job
test: add "eq" and "tryEnvString" helper functions
tets: conditionally deploy contracts normally or from precompiled source
test: improve wording in NatSpec
@PaulRBerg PaulRBerg changed the title Segment[] array, comptroller, protocol and operator fees, structified arguments, PRBMath types, major tests refactor Segment[] array, comptroller, protocol and operator fees, structified parameters, via IR, PRBMath types, major tests refactor Jan 4, 2023
Copy link
Member

@andreivladbrg andreivladbrg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be more efficient I am going to submit this review without getting in detail on tests, but I liked the shared pattern to get rid of the duplicated tests.

Regarding structs I suggest to keep them in their dedicated file and separate them with headers, memory / storage

To solve the size issue in the pro contract is mandatory to move the _calculateWithdrawableAmountForOneSegment and the _calculateWithdrawableAmountForMultipleSegments functions in Helpers lib.

/// users so that they don't have to install openzeppelin-contracts and prb-contracts separately.

import { IERC20 } from "@prb/contracts/token/erc20/IERC20.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets merge this with Math.sol and also add the hooks interfaces, and ofc we have to rename the file.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I am against merging these types with the hooks interfaces. The goal with the Math.sol and the Tokens.sol files is to re-export external types so that users don't have to manually install third-party repos for using Sablier. Whereas the hooks are internal.

At a minimum, I may be open to merging the Math.sol and the Tokens.sol files in one file - but I can't think of any good name for this merged file. Types.sol would be ugly, because of the types/Types.sol path.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a dir called re-exports and add all of them there.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I don't like how that sounds, not at all. End users shouldn't know that these are re-exported types.

Let's sleep on this idea for a while, we will come back to it later. If you have a strong opinion, feel free to create a discussion for tracking.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main point is if a user wants to import all of them they must have 3 separeted paths instead of one:

import { X } from "path/File1.sol";
import { Y } from "path/File2.sol";
import { Z } from "path/File3.sol";

vs

import { X, Y, Y } from "path/File.sol";

Copy link
Member

@andreivladbrg andreivladbrg Jan 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exports.sol
// SPDX-License-Identifier: LGPL-3.0
pragma solidity >=0.8.13;

/*//////////////////////////////////////////////////////////////////////////
                                    MATH
//////////////////////////////////////////////////////////////////////////*/

/// All PRBMath types needed in v2-core. It is provided for convenience to
/// users so that they don't have to install PRBMath separately.

import { SD1x18 } from "@prb/math/SD1x18.sol";
import { SD59x18 } from "@prb/math/SD59x18.sol";
import { UD60x18 } from "@prb/math/UD60x18.sol";

/*//////////////////////////////////////////////////////////////////////////
                                    TOKENS
//////////////////////////////////////////////////////////////////////////*/

/// All token interfaces needed in v2-core. It is provided for convenience to users so that they don't
/// have to install openzeppelin-contracts and prb-contracts separately.

import { IERC20 } from "@prb/contracts/token/erc20/IERC20.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

/*//////////////////////////////////////////////////////////////////////////
                                    HOOKS
//////////////////////////////////////////////////////////////////////////*/

import { ISablierV2Recipient } from "../hooks/ISablierV2Recipient.sol";
import { ISablierV2Sender } from "../hooks/ISablierV2Sender.sol";

Copy link
Member Author

@PaulRBerg PaulRBerg Jan 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding merging Math and Tokens - I see your point, and as I said above, I am sympathetic to it.

But I do not agree regarding the hooks - it's fine to leave them separate, because the hooks are not "types" per se. They have nothing to do with the user-defined value types of PRBMath, or the structs. They are interface files, which serve a specific purpose.

FYI, even forge-std has gone down this path of modularization and selective importing - see the discussion here about the rationale for removing the Components.sol file, which was re-exporting all of the Forge Std components. The TLDR; is that selectively importing files leads to faster compile times, because when you re-export everything in one file, Solidity compiles all of those files even if the user imports just one file.

Also, I don't like any name that contains the term "exports" - let's sleep on this for a while.

@PaulRBerg
Copy link
Member Author

PaulRBerg commented Jan 5, 2023

Thanks @andreivladbrg, your feedback is excellent.

I liked the shared pattern to get rid of the duplicated tests.

Glad you like it.

Regarding structs I suggest to keep them in their dedicated file and separate them with headers, memory / storage

I don't like this separation:

  1. It would lead to a worse UX for integrators, because they would have to know the import path for each struct.
  2. The imports would grow bigger as a result.
  3. It would become more difficult to find the structs. Now, they are aggregated in one file, which makes them easy to search.

To solve the size issue in the pro contract is mandatory to move

I'm in favor of this proposal, but let's merge this PR as is and we will later come back to this idea in case we don't manage to reduce the size of the contracts in other ways.

Another reason why I like this separation is that it makes it possible to perform the pro calculations from outside of the pro contracts - this might be helpful for integrators, or for our periphery contracts.

…ment"

chore: delete unused imports
docs: add missing "@inheritdoc" in "SablierV2Comptroller"
docs: delete stale requirements in NatSpec
refactor: use "ud" instead of "UD60x18.wrap"
test: fix typos in testing tree for "createWithRange"
@andreivladbrg
Copy link
Member

  1. It would lead to a worse UX for integrators, because they would have to know the import path for each struct.
  2. The imports would grow bigger as a result.
  3. It would become more difficult to find the structs. Now, they are aggregated in one file, which makes them easy to search

You really didn't get my point

Separation by headers
// SPDX-License-Identifier: LGPL-3.0
pragma solidity >=0.8.13;

import { IERC20 } from "@prb/contracts/token/erc20/IERC20.sol";
import { SD1x18 } from "@prb/math/SD1x18.sol";

/*//////////////////////////////////////////////////////////////////////////
                                MEMORY STRUCTS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Simple struct that encapsulates the net deposit amount, the protocol fee amount, and the operator fee
/// amount.
/// @custom:field netDeposit The deposit amount net of fees, in units of the token's decimals.
/// @custom:field protocolFee The protocol fee amount, in units of the token's decimals.
/// @custom:field operatorFee The operator fee amount, in units of the token's decimals.
struct CreateAmounts {
    uint128 netDeposit; // ──┐
    uint128 protocolFee; // ─┘
    uint128 operatorFee;
}

/// @dev This struct is needed to avoid the "Stack Too Deep" error.
struct CreateWithMilestonesArgs {
    CreateAmounts amounts;
    Segment[] segments;
    address sender; // ──┐
    uint40 startTime; // │
    bool cancelable; // ─┘
    address recipient;
    address operator;
    IERC20 token;
}

/// @dev This struct is needed to avoid the "Stack Too Deep" error.
struct CreateWithRangeArgs {
    CreateAmounts amounts;
    Range range;
    address sender; // ──┐
    bool cancelable; // ─┘
    address recipient;
    address operator;
    IERC20 token;
}

/*//////////////////////////////////////////////////////////////////////////
                                STORAGE STRUCTS
//////////////////////////////////////////////////////////////////////////*/

/// @notice Simple struct that encapsulates the deposit and the withdrawn amounts.
/// @custom:field deposit The amount of tokens that have been originally deposited in the stream, net of fees and
/// in units of the token's decimals.
/// @custom:field withdrawn The amount of tokens that have been withdrawn from the stream, in units of the token's
/// decimals.
struct Amounts {
    uint128 deposit; // ───┐
    uint128 withdrawn; // ─┘
}

/// @notice Linear stream struct used in the SablierV2Linear contract.
/// @dev The fields are arranged like this to save gas via tight variable packing.
/// @custom:field amounts Simple struct with the deposit and withdrawn amounts.
/// @custom:field segments The arrays of segments used to compose the custom streaming curve.
/// @custom:field sender The address of the sender of the stream.
/// @custom:field isCancelable A boolean that indicates whether the stream is cancelable or not.
/// @custom:field isEntity A boolean that signals the existence of the instance of the struct.
/// @custom:field token The address of the ERC-20 token used for streaming.
struct LinearStream {
    Amounts amounts;
    Range range;
    address sender; // ───┐
    bool isCancelable; // │
    bool isEntity; // ────┘
    IERC20 token;
}

/// @notice Pro stream struct used in the SablierV2Pro contract.
/// @dev The fields are arranged like this to save gas via tight variable packing.
/// @custom:field amounts Simple struct with the deposit and withdrawn amounts.
/// @custom:field segments The arrays of segments used to compose the custom streaming curve.
/// @custom:field sender The address of the sender of the stream.
/// @custom:field isCancelable A boolean that indicates whether the stream is cancelable or not.
/// @custom:field isEntity A boolean that signals the existence of the instance of the struct.
/// @custom:field token The address of the ERC-20 token used for streaming.
struct ProStream {
    Amounts amounts;
    Segment[] segments;
    address sender; // ───┐
    uint40 startTime; //  │
    bool isCancelable; // │
    bool isEntity; // ────┘
    IERC20 token;
}

/// @notice Range struct used as a field in the linear stream.
/// @custom:field cliff The Unix timestamp for when the cliff period will end.
/// @custom:field start The Unix timestamp for when the stream will start.
/// @custom:field stop The Unix timestamp for when the stream will stop.
struct Range {
    uint40 cliff;
    uint40 start;
    uint40 stop;
}

/// @notice Segment struct used in the SablierV2Pro contract.
/// @custom:field amount The amounts of tokens to be streamed in this segment, in units of the token's decimals.
/// @custom:field exponent The exponent in this segment, whose base is the elapsed time as a percentage.
/// @custom:field milestone The Unix timestamp for when this segment ends.
struct Segment {
    uint128 amount;
    SD1x18 exponent;
    uint40 milestone;
}

build: upgrade to latest "prb-test"
test: add "Constants" test contract and move constants there
test: add "isTestOptimizedProfile" helper function
test: call "deploySablierContracts" in integration test
test: delete "eq" helper function
test: delete "immutable" modifier from "DEFAULT_RANGE" and "DEFAULT_SEGMENTS
test: document "SharedTest" with a little bit of NatSpec
test: initialize users in "BaseTest" instead of "UnitTest"
test: move "tryEnvString" to "BaseTest"
test: rename "approveContracts" to "approveSablierContracts"
test: rename "deployContracts" to "deploySablierContracts"
test: separate the labelling of the Sablier contracts and the test contracts
test: use "eqString" from "prb-test" instead of local "eq"
@PaulRBerg
Copy link
Member Author

PaulRBerg commented Jan 5, 2023

I'm sorry, @andreivladbrg, I might have misunderstood you. But the way you have worded your initial suggestion was a bit unclear - you said "I suggest to keep them in their dedicated file" - this is interpretable but I would say it's more easy to interpret as meaning that you meant to create one file for each struct. If you wanted to say to keep all structs in the same file, it would have been better to use the terms "all" and "current file", to communicate the fact that the way they are structured currently is fine.

Anyway. As I said in one of the comments above, I wish to keep the CreateWithRangeArgs and CreateWithMilestones outside of the Structs file, because they are not meant to be imported by end users. Therefore, the memory <> storage dichotomy doesn't help much. Also, the CreateAmounts struct is now emitted in the create events, which might create a bit of confusion with respect to the in-memory classification.

@andreivladbrg
Copy link
Member

Therefore, the memory <> storage dichotomy doesn't help much

I think is very helpful

the CreateAmounts struct is now emitted in the create events, which might create a bit of confusion with respect to the in-memory classification.

There is nothing wrong with this (nothing to be confused by), the params are still in memory and not stored in our contracts.

For me this separation of classifications is just logical.

@andreivladbrg
Copy link
Member

andreivladbrg commented Jan 5, 2023

As I said in one of the comments above, I wish to keep the CreateWithRangeArgs and CreateWithMilestones outside of the Structs file, because they are not meant to be imported by end users.

Why is one struct meant to be imported more than another?

They can just import one if they don't want all of them.

@PaulRBerg
Copy link
Member Author

I think is very helpful

It wouldn't be if the only in-memory struct is CreateAmounts.

For me this separation of classifications is just logical.

It might be logical but it would add a lot of comments and headers for separating just one struct from the rest. Looks odd.

Why is one struct meant to be imported more than another?

The CreateWithRangeArgs and the CreateWithMilestonesArgs are NOT meant to be used by end users - we should not export something that users should never touch. This can easily create confusion, e.g. if end user imports the Structs.sol file wholesale (without using specific symbols).

refactor: remove "comptroller" and "token" from "checkAndCalculateFees" params
@andreivladbrg
Copy link
Member

andreivladbrg commented Jan 5, 2023

It might be logical but it would add a lot of comments and headers for separating just one struct from the rest. Looks odd.

Ofc, the idea is to add the CreateWithRangeArgs and CreateWithMilestonesArgs structs.

This can easily create confusion, e.g. if end user imports the Structs.sol file wholesale (without using specific symbols).

To import all file is a problem of a uneducated user/integrator, we will make sure to specify this exactly in our documentation on how to interact with sablier. For a newbie user would be more confusing having a dedicated struct file and also to see a struct in the core contracts.

@PaulRBerg
Copy link
Member Author

PaulRBerg commented Jan 5, 2023

To import all file is a problem of a uneducated user/integrator

You would be surprised by how often Solidity files are imported in the global scope - specific symbols have only become popular in the recent years. This was due, in part, to the fact that Solidity contracts used to be tested in JavaScript/ Python.

we will make sure to specify this exactly in our documentation

So why give homework to ourselves and have to document something when we can just hide it away from the public API in the first place?

For a newbie user would be more confusing having a dedicated struct file and also to see a struct in the core contracts.

Uhm, no? A third-party integrator doesn't even have to look at the implementation details. They can just pick up the interface files and the types - and off they go.

The actual user-facing create function don't expect a struct as a parameter - if they did, I would have welcomed your suggestion to move the said structs in the Structs.sol file. But that is simply not the case - the CreateWithRangeArgs and CreateWithMilestonesArgs structs are concerned with the internal business logic of the protocol, not with end users.

test: refactor "args" to "params"
test: rename "defaultArgs" to just "params"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants