Skip to content

Commit

Permalink
test: increase coverage by testing when there is a rounding error in
Browse files Browse the repository at this point in the history
tranches' amounts calculation
  • Loading branch information
andreivladbrg committed Mar 10, 2024
1 parent 1b49ebf commit 0587327
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 5 deletions.
74 changes: 72 additions & 2 deletions test/integration/merkle-lockup/lt/claim/claim.t.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.22 <0.9.0;

import { Arrays } from "@openzeppelin/contracts/utils/Arrays.sol";
import { Lockup, LockupTranched } from "@sablier/v2-core/src/types/DataTypes.sol";

import { Errors } from "src/libraries/Errors.sol";
import { MerkleLockup } from "src/types/DataTypes.sol";
import { ISablierV2MerkleLockupLT } from "src/interfaces/ISablierV2MerkleLockupLT.sol";

import { Merkle } from "../../../../utils/Murky.sol";
import { MerkleBuilder } from "../../../../utils/MerkleBuilder.sol";

import { MerkleLockup_Integration_Test } from "../../MerkleLockup.t.sol";

contract Claim_Integration_Test is MerkleLockup_Integration_Test {
contract Claim_Integration_Test is Merkle, MerkleLockup_Integration_Test {
using MerkleBuilder for uint256[];

function setUp() public virtual override {
MerkleLockup_Integration_Test.setUp();
}
Expand Down Expand Up @@ -101,7 +109,69 @@ contract Claim_Integration_Test is MerkleLockup_Integration_Test {
_;
}

function test_Claim() external givenCampaignNotExpired givenNotClaimed givenIncludedInMerkleTree {
// Needed this variables in storage due to how the imported libaries work.
uint256[] public leaves = new uint256[](4); // same number of recipients as in the defaults

function test_Claim_TrancheAmountCalculationRoundingError()
external
givenCampaignNotExpired
givenNotClaimed
givenIncludedInMerkleTree
{
// Declare an amount that will cause a rounding error.
uint128 claimAmount = 340_282_366_920_938_463_463_374_607_431_768_211_453;
uint256 aggregateAmount = defaults.CLAIM_AMOUNT() * 3 + uint256(claimAmount);

// Compute the Merkle tree.
leaves = defaults.getLeaves();
leaves[0] = MerkleBuilder.computeLeaf(defaults.INDEX1(), users.recipient1, claimAmount);
MerkleBuilder.sortLeaves(leaves);
bytes32 merkleRoot = getRoot(leaves.toBytes32());

// Compute the Merkle proof.
uint256 leaf = MerkleBuilder.computeLeaf(defaults.INDEX1(), users.recipient1, claimAmount);
uint256 pos = Arrays.findUpperBound(leaves, leaf);
bytes32[] memory proof = getProof(leaves.toBytes32(), pos);

/// Declare the constructor params.
MerkleLockup.ConstructorParams memory baseParams = defaults.baseParams();
baseParams.merkleRoot = merkleRoot;

// Deploy the new MerkleLockupLT contract.
ISablierV2MerkleLockupLT _merkleLockupLT = merkleLockupFactory.createMerkleLockupLT(
baseParams, lockupTranched, defaults.tranchesWithPercentages(), aggregateAmount, defaults.RECIPIENTS_COUNT()
);

// Fund the MerkleLockupLT contract.
deal({ token: address(dai), to: address(_merkleLockupLT), give: aggregateAmount });

uint256 expectedStreamId = lockupTranched.nextStreamId();

vm.expectEmit({ emitter: address(_merkleLockupLT) });
emit Claim(defaults.INDEX1(), users.recipient1, claimAmount, expectedStreamId);
uint256 actualStreamId = _merkleLockupLT.claim(defaults.INDEX1(), users.recipient1, claimAmount, proof);

LockupTranched.StreamLT memory actualStream = lockupTranched.getStream(actualStreamId);
LockupTranched.StreamLT memory expectedStream = LockupTranched.StreamLT({
amounts: Lockup.Amounts({ deposited: claimAmount, refunded: 0, withdrawn: 0 }),
asset: dai,
endTime: uint40(block.timestamp) + defaults.TOTAL_DURATION(),
isCancelable: defaults.CANCELABLE(),
isDepleted: false,
isStream: true,
isTransferable: defaults.TRANSFERABLE(),
sender: users.admin,
startTime: uint40(block.timestamp),
tranches: defaults.tranches(claimAmount),
wasCanceled: false
});

assertTrue(_merkleLockupLT.hasClaimed(defaults.INDEX1()), "not claimed");
assertEq(actualStreamId, expectedStreamId, "invalid stream id");
assertEq(actualStream, expectedStream);
}

function test_Claim() external {
uint256 expectedStreamId = lockupTranched.nextStreamId();

vm.expectEmit({ emitter: address(merkleLockupLT) });
Expand Down
12 changes: 9 additions & 3 deletions test/integration/merkle-lockup/lt/claim/claim.tree
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ claim.t.sol
│ └── when the Merkle proof is not valid
│ └── it should revert
└── given the claim is included in the Merkle tree
├── it should mark the index as claimed
├── it should create a LockupTranched stream
└── it should emit a {Claim} event
├── when the calculation of the tranches' amounts causes a rounding error
│ ├── it should adjust the last tranche amount
│ ├── it should mark the index as claimed
│ ├── it should create a LockupTranched stream
│ └── it should emit a {Claim} event
└── when the calculation of the tranches' amounts does not cause a rounding error
├── it should mark the index as claimed
├── it should create a LockupTranched stream
└── it should emit a {Claim} event
4 changes: 4 additions & 0 deletions test/utils/Defaults.sol
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ contract Defaults is Merkle {
MERKLE_ROOT = getRoot(LEAVES.toBytes32());
}

function getLeaves() public view returns (uint256[] memory) {
return LEAVES;
}

/*//////////////////////////////////////////////////////////////////////////
MERKLE-LOCKUP
//////////////////////////////////////////////////////////////////////////*/
Expand Down

0 comments on commit 0587327

Please sign in to comment.