-
Notifications
You must be signed in to change notification settings - Fork 49
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
feat: add SablierMerkleInstant
#999
Conversation
cf7e16e
to
683e0a0
Compare
@andreivladbrg feel free to review it. Sorry that the PR has become huge due to refactoring of Lockup contract. I will be working on #1002 to dry'fy the shared tests across merkle contracts. |
4bf8a98
to
617b120
Compare
refactor: abstract contract SablierMerkleLockup to SablierMerkleBase refactor: library MerkleLockup to MerkleBase refactor: contract MerkleLockupFactory to MerkleFactory test: add test for MerkleInstant contract chore: add CreateMerkleInstant script test: use super.setUp() where setUp() is redundant test: rename merkle-lockup folder to merkle test: rename MerkleLockup_Integration_Test contract to Merkle_Shared_Integration_Test test: fix event assersion in merkleLL and merkleLT refactor: rename MerkleLockup to Merkle Lockup in NatSpecs Co-authored-by: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com>
617b120
to
7df7417
Compare
630cf2b
to
e680dc0
Compare
Signed-off-by: smol-ninja <shubhamy2015@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work, and sorry for the delayed review.
I haven't looked at tests yet, but I will leave this review for src
We can dry-fy the claim functions by implementing a _claim function in the base contract:
Claim function
function _claim(uint256 index, address recipient, uint128 amount, bytes32[] calldata merkleProof) internal {
// Check: the campaign has not expired.
if (hasExpired()) {
revert Errors.SablierMerkleBase_CampaignExpired({ blockTimestamp: block.timestamp, expiration: EXPIRATION });
}
// Check: the index has not been claimed.
if (_claimedBitMap.get(index)) {
revert Errors.SablierMerkleBase_StreamClaimed(index);
}
// Generate the Merkle tree leaf by hashing the corresponding parameters. Hashing twice prevents second
// preimage attacks.
bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(index, recipient, amount))));
// Check: the input claim is included in the Merkle tree.
if (!MerkleProof.verify(merkleProof, MERKLE_ROOT, leaf)) {
revert Errors.SablierMerkleBase_InvalidProof();
}
// Effect: set the `_firstClaimTime` if its zero.
if (_firstClaimTime == 0) {
_firstClaimTime = uint40(block.timestamp);
}
// Effect: mark the index as claimed.
_claimedBitMap.set(index);
}
Or, we can even implement the claim
external function in Base contract, and add basically all the logic from SablierMerkleInstant.claim
expect for the token transfer.
And at the end of the function, it will call an internal virtual function _claim
that is going to be implemented by the child contract. In case of SablierMerkleInstant
it will simply transfer the tokens, in case of Merkle Lockup, it would create a stream.
The pros:
- the code is muchh clearner
The cons:
- no returned stream id
- the CEI is not respected in
MerkleLT
(still safe tho)
Curious about your opinion on this @smol-ninja .
refactor: rename DeployMerkleLockupFactory to DeployMerkleFactory Co-authored-by: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com>
Thanks @andreivladbrg for the review. I like your second approach to implement the |
After giving it more thought, I realized that in the second approach, there would neither be a stream ID returned nor would there be one in the Claim event (as you also mentioned). And isn't it useful to have stream ID at these places (for integrators and smart contract calls)? As for the first approach, we can move these two lines to the base contract: 1. bytes32 leaf = keccak256(bytes.concat(keccak256(abi.encode(index, recipient, amount))))
2. _claimedBitMap.set(index) and it wouldn't affect the CEI pattern that much because there is anyway one more Effect (
I am happy to move the leaf to |
not that much, as it already emitted in the create event
the first suggestion doesn't affect the CEI at all, i was talking about the second one
that's exactly what the first suggestion is about |
one other aspect about the second suggestion, we can include a function claim(
uint256 index,
address recipient,
uint128 amount,
bytes32[] calldata merkleProof,
bytes memory data // optional data
)
external
{
// claim logic
_claim(index, recipient, amount, merkleProof, data);
}
function _claim(
uint256 index,
address recipient,
uint128 amount,
bytes32[] calldata merkleProof,
bytes memory /* data */ // ignore the data
)
internal
{ } do you find it useful? the idea is to not modify the future API version |
But then why do we have returned value in batch lockup functions and even the create functions in lockup contracts. I suppose its so that if a contract (integrator) calls them on behalf of the users, they can use return value to keep track of the streams. Smart contracts don't have access to events so its beneficial to them. Same argument in favour of having stream ID returned in claim. |
yeah, but the point you've raised was about the event, wasn't it? i don't want to deny the con, but i highly doubt that the merkle-campaign are meant to be integrated at the contract level, as we've built them to have an e2e product for your UI also, it is not the end of the world, as the integrators can still can obtain the id: uint256 streamId = merkleLL.LOCKUP_LINEAR().nextStreamId();
merkleLL.claim(params); |
Actually both: even and return value. But mostly return value.
Haha. Now, I am convinced that we should DRY'ify the
About this, I am not in favour of adding |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests look good 👍
Before merging this to staging, let's first create a PR against this one with the dry-fy shared one
The PR is already open for review: #1009 |
Let me know if all looks good in this PR. If so, could you please approve it? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
including the changes made in #1011 it looks good
* feat: add SablierMerkleInstant refactor: abstract contract SablierMerkleLockup to SablierMerkleBase refactor: library MerkleLockup to MerkleBase refactor: contract MerkleLockupFactory to MerkleFactory test: add test for MerkleInstant contract chore: add CreateMerkleInstant script test: use super.setUp() where setUp() is redundant test: rename merkle-lockup folder to merkle test: rename MerkleLockup_Integration_Test contract to Merkle_Shared_Integration_Test test: fix event assersion in merkleLL and merkleLT refactor: rename MerkleLockup to Merkle Lockup in NatSpecs Co-authored-by: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com> * test: include asset transfer log in merkle instant claims * test(refactor): rename Merkle_Shared_Integration_Test back to Merkle_Integration_Test * test: refactor merkle-lockup folder to merkle-campaign Signed-off-by: smol-ninja <shubhamy2015@gmail.com> * refactor: polish code for createMerkleLL and createMerkleLT scripts refactor: rename DeployMerkleLockupFactory to DeployMerkleFactory Co-authored-by: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com> * test(fork): remove unneeded vars --------- Signed-off-by: smol-ninja <shubhamy2015@gmail.com> Co-authored-by: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com> Co-authored-by: andreivladbrg <andreivladbrg@gmail.com>
* feat: add SablierMerkleInstant refactor: abstract contract SablierMerkleLockup to SablierMerkleBase refactor: library MerkleLockup to MerkleBase refactor: contract MerkleLockupFactory to MerkleFactory test: add test for MerkleInstant contract chore: add CreateMerkleInstant script test: use super.setUp() where setUp() is redundant test: rename merkle-lockup folder to merkle test: rename MerkleLockup_Integration_Test contract to Merkle_Shared_Integration_Test test: fix event assersion in merkleLL and merkleLT refactor: rename MerkleLockup to Merkle Lockup in NatSpecs Co-authored-by: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com> * test: include asset transfer log in merkle instant claims * test(refactor): rename Merkle_Shared_Integration_Test back to Merkle_Integration_Test * test: refactor merkle-lockup folder to merkle-campaign Signed-off-by: smol-ninja <shubhamy2015@gmail.com> * refactor: polish code for createMerkleLL and createMerkleLT scripts refactor: rename DeployMerkleLockupFactory to DeployMerkleFactory Co-authored-by: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com> * test(fork): remove unneeded vars --------- Signed-off-by: smol-ninja <shubhamy2015@gmail.com> Co-authored-by: Andrei Vlad Birgaoanu <99738872+andreivladbrg@users.noreply.github.com> Co-authored-by: andreivladbrg <andreivladbrg@gmail.com>
Changelog
SablierMerkleInstant
- A merkle lockup contract to facilitate instant airdrop campaigns #995ISablierMerkleLockup
toISablierMerkleBase
MerkleLockup
toMerkleBase
ISablierMerkleLockupFactory
toISablierMerkleFactory
merkle-lockup
directory tomerkle-campaign
MerkleLockup_Integration_Test
contract toMerkleCampaign_Integration_Test
. In a separate PR, I will DRY'ify the shared test.merkleLL
andmerkleLT
fork tests, similar to missingexpectEmit
inMerkleLL
andMerkleLT
fork tests v2-periphery#382.PR Dependencies