@bananapus/721-hook-v6 0.0.6 → 0.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/JB721TiersHook.sol +20 -24
- package/test/unit/pay_Unit.t.sol +84 -0
package/package.json
CHANGED
package/src/JB721TiersHook.sol
CHANGED
|
@@ -711,9 +711,7 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, ERC721, IJB721TiersHook {
|
|
|
711
711
|
// If the payer is the beneficiary, combine their NFT credits with the amount paid.
|
|
712
712
|
uint256 unusedPayCredits;
|
|
713
713
|
if (context.payer == context.beneficiary) {
|
|
714
|
-
|
|
715
|
-
leftoverAmount += payCredits;
|
|
716
|
-
}
|
|
714
|
+
leftoverAmount += payCredits;
|
|
717
715
|
} else {
|
|
718
716
|
// Otherwise, the payer's NFT credits won't be used, and we keep track of the unused credits.
|
|
719
717
|
unusedPayCredits = payCredits;
|
|
@@ -755,28 +753,26 @@ contract JB721TiersHook is JBOwnable, ERC2771Context, ERC721, IJB721TiersHook {
|
|
|
755
753
|
if (leftoverAmount != 0 && !allowOverspending) revert JB721TiersHook_Overspending(leftoverAmount);
|
|
756
754
|
|
|
757
755
|
// Update NFT credits if they changed.
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
if (newPayCredits
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
});
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
payCreditsOf[context.beneficiary] = newPayCredits;
|
|
756
|
+
uint256 newPayCredits = leftoverAmount + unusedPayCredits;
|
|
757
|
+
|
|
758
|
+
if (newPayCredits != payCredits) {
|
|
759
|
+
if (newPayCredits > payCredits) {
|
|
760
|
+
emit AddPayCredits({
|
|
761
|
+
amount: newPayCredits - payCredits,
|
|
762
|
+
newTotalCredits: newPayCredits,
|
|
763
|
+
account: context.beneficiary,
|
|
764
|
+
caller: _msgSender()
|
|
765
|
+
});
|
|
766
|
+
} else {
|
|
767
|
+
emit UsePayCredits({
|
|
768
|
+
amount: payCredits - newPayCredits,
|
|
769
|
+
newTotalCredits: newPayCredits,
|
|
770
|
+
account: context.beneficiary,
|
|
771
|
+
caller: _msgSender()
|
|
772
|
+
});
|
|
779
773
|
}
|
|
774
|
+
|
|
775
|
+
payCreditsOf[context.beneficiary] = newPayCredits;
|
|
780
776
|
}
|
|
781
777
|
|
|
782
778
|
// Distribute any forwarded funds to tier split groups.
|
package/test/unit/pay_Unit.t.sol
CHANGED
|
@@ -1530,4 +1530,88 @@ contract Test_afterPayRecorded_Unit is UnitTestSetup {
|
|
|
1530
1530
|
// Check: has the holder's balance returned to 0?
|
|
1531
1531
|
assertEq(hook.balanceOf(holder), 0);
|
|
1532
1532
|
}
|
|
1533
|
+
|
|
1534
|
+
function test_afterPayRecorded_revertOnCreditOverflow_samePayerBeneficiary() public {
|
|
1535
|
+
// Mock the directory call.
|
|
1536
|
+
mockAndExpect(
|
|
1537
|
+
address(mockJBDirectory),
|
|
1538
|
+
abi.encodeWithSelector(IJBDirectory.isTerminalOf.selector, projectId, mockTerminalAddress),
|
|
1539
|
+
abi.encode(true)
|
|
1540
|
+
);
|
|
1541
|
+
|
|
1542
|
+
// Set the beneficiary's pay credits to max uint256.
|
|
1543
|
+
stdstore.target(address(hook)).sig("payCreditsOf(address)").with_key(beneficiary)
|
|
1544
|
+
.checked_write(type(uint256).max);
|
|
1545
|
+
|
|
1546
|
+
// Pay 1 wei where payer == beneficiary. No metadata → no NFT mints.
|
|
1547
|
+
// `leftoverAmount += payCredits` overflows: 1 + type(uint256).max.
|
|
1548
|
+
vm.expectRevert(stdError.arithmeticError);
|
|
1549
|
+
vm.prank(mockTerminalAddress);
|
|
1550
|
+
hook.afterPayRecordedWith(
|
|
1551
|
+
JBAfterPayRecordedContext({
|
|
1552
|
+
payer: beneficiary,
|
|
1553
|
+
projectId: projectId,
|
|
1554
|
+
rulesetId: 0,
|
|
1555
|
+
amount: JBTokenAmount({
|
|
1556
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
1557
|
+
value: 1,
|
|
1558
|
+
decimals: 18,
|
|
1559
|
+
currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
1560
|
+
}),
|
|
1561
|
+
forwardedAmount: JBTokenAmount({
|
|
1562
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
1563
|
+
value: 0,
|
|
1564
|
+
decimals: 18,
|
|
1565
|
+
currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
1566
|
+
}),
|
|
1567
|
+
weight: 10 ** 18,
|
|
1568
|
+
newlyIssuedTokenCount: 0,
|
|
1569
|
+
beneficiary: beneficiary,
|
|
1570
|
+
hookMetadata: new bytes(0),
|
|
1571
|
+
payerMetadata: new bytes(0)
|
|
1572
|
+
})
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
function test_afterPayRecorded_revertOnCreditOverflow_differentPayerBeneficiary() public {
|
|
1577
|
+
// Mock the directory call.
|
|
1578
|
+
mockAndExpect(
|
|
1579
|
+
address(mockJBDirectory),
|
|
1580
|
+
abi.encodeWithSelector(IJBDirectory.isTerminalOf.selector, projectId, mockTerminalAddress),
|
|
1581
|
+
abi.encode(true)
|
|
1582
|
+
);
|
|
1583
|
+
|
|
1584
|
+
// Set the beneficiary's pay credits to max uint256.
|
|
1585
|
+
stdstore.target(address(hook)).sig("payCreditsOf(address)").with_key(beneficiary)
|
|
1586
|
+
.checked_write(type(uint256).max);
|
|
1587
|
+
|
|
1588
|
+
// Pay 1 wei where payer != beneficiary. No metadata → no NFT mints, overspending allowed.
|
|
1589
|
+
// leftoverAmount=1, unusedPayCredits=type(uint256).max → overflow in `leftoverAmount + unusedPayCredits`.
|
|
1590
|
+
vm.expectRevert(stdError.arithmeticError);
|
|
1591
|
+
vm.prank(mockTerminalAddress);
|
|
1592
|
+
hook.afterPayRecordedWith(
|
|
1593
|
+
JBAfterPayRecordedContext({
|
|
1594
|
+
payer: address(0xdead),
|
|
1595
|
+
projectId: projectId,
|
|
1596
|
+
rulesetId: 0,
|
|
1597
|
+
amount: JBTokenAmount({
|
|
1598
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
1599
|
+
value: 1,
|
|
1600
|
+
decimals: 18,
|
|
1601
|
+
currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
1602
|
+
}),
|
|
1603
|
+
forwardedAmount: JBTokenAmount({
|
|
1604
|
+
token: JBConstants.NATIVE_TOKEN,
|
|
1605
|
+
value: 0,
|
|
1606
|
+
decimals: 18,
|
|
1607
|
+
currency: uint32(uint160(JBConstants.NATIVE_TOKEN))
|
|
1608
|
+
}),
|
|
1609
|
+
weight: 10 ** 18,
|
|
1610
|
+
newlyIssuedTokenCount: 0,
|
|
1611
|
+
beneficiary: beneficiary,
|
|
1612
|
+
hookMetadata: new bytes(0),
|
|
1613
|
+
payerMetadata: new bytes(0)
|
|
1614
|
+
})
|
|
1615
|
+
);
|
|
1616
|
+
}
|
|
1533
1617
|
}
|