Skip to content

Commit ddbbe1e

Browse files
authored
Merge pull request #24 from KumaCrypto/L04_fix
fix(L04): handle non-native deposit with value & block native deposit with zero value
2 parents 01f863d + 59e923c commit ddbbe1e

File tree

2 files changed

+63
-13
lines changed

2 files changed

+63
-13
lines changed

src/allocators/OnChainAllocator.sol

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -87,43 +87,72 @@ contract OnChainAllocator is IOnChainAllocator {
8787
bytes32 typehash,
8888
bytes32 witness
8989
) public payable returns (bytes32 claimHash, uint256[] memory registeredAmounts, uint256 nonce) {
90-
nonce = _getAndUpdateNonce(msg.sender, recipient);
91-
90+
// Check for empty commitments
9291
if (commitments.length == 0) {
9392
revert InvalidCommitments();
9493
}
9594

95+
nonce = _getAndUpdateNonce(msg.sender, recipient);
96+
97+
// Transformed locks will be stored in idsAndAmounts
9698
uint256[2][] memory idsAndAmounts = new uint256[2][](commitments.length);
9799

100+
// Init minResetPeriod to the max value
98101
uint256 minResetPeriod = type(uint256).max;
99-
uint256 i = 0;
102+
103+
// Process native token (zero address) first
104+
uint256 i;
100105
if (commitments[i].token == address(0)) {
106+
// The Compact will revert if invalid value is provided for native token
107+
// Possible cases:
108+
// 1. The callvalue is zero but the first token is native
109+
// 2. the callvalue is nonzero but the first token is non-native
110+
// 3. the first token is native and the callvalue doesn't equal the first amount
111+
112+
// Handle first and third points
113+
if (commitments[i].amount == 0 || commitments[i].amount != msg.value) {
114+
revert InvalidAmount(commitments[i].amount);
115+
}
116+
101117
minResetPeriod = _checkInput(commitments[i], recipient, expires, minResetPeriod);
118+
102119
idsAndAmounts[i][0] = AL.toId(commitments[i].lockTag, commitments[i].token);
120+
idsAndAmounts[i][1] = msg.value;
103121

104-
if (commitments[i].amount != 0 && commitments[i].amount != msg.value) {
105-
revert InvalidAmount(commitments[i].amount);
122+
unchecked {
123+
++i;
124+
}
125+
} else {
126+
// Handle second point
127+
if (msg.value != 0) {
128+
revert InvalidAmount(msg.value);
106129
}
107-
idsAndAmounts[i][1] = msg.value;
108-
i++;
109130
}
131+
132+
// Process the rest of the commitments
110133
for (; i < commitments.length; i++) {
111134
minResetPeriod = _checkInput(commitments[i], recipient, expires, minResetPeriod);
112-
idsAndAmounts[i][0] = AL.toId(commitments[i].lockTag, commitments[i].token);
135+
136+
address token = commitments[i].token;
137+
// Safe to cast - _checkInput validated that the value fits the uint224
113138
uint224 amount = uint224(commitments[i].amount);
114139

115-
// If the amount is 0, we use the balance of the contract to deposit.
140+
// If the amount is 0, we use the balance of the contract to deposit
116141
if (amount == 0) {
117-
amount = uint224(IERC20(commitments[i].token).balanceOf(address(this)));
142+
amount = uint224(IERC20(token).balanceOf(address(this)));
118143
}
144+
145+
// Store the lock in idsAndAmounts
146+
idsAndAmounts[i][0] = AL.toId(commitments[i].lockTag, token);
119147
idsAndAmounts[i][1] = amount;
120148

121149
// Approve the compact contract to spend the tokens.
122-
if (IERC20(commitments[i].token).allowance(address(this), COMPACT_CONTRACT) < amount) {
123-
SafeTransferLib.safeApproveWithRetry(commitments[i].token, COMPACT_CONTRACT, type(uint256).max);
150+
if (IERC20(token).allowance(address(this), COMPACT_CONTRACT) < amount) {
151+
SafeTransferLib.safeApproveWithRetry(token, COMPACT_CONTRACT, type(uint256).max);
124152
}
125153
}
126-
// Ensure expiration is not bigger then the smallest reset period
154+
155+
// Ensure expiration is less then the smallest reset period
127156
if (expires >= block.timestamp + minResetPeriod) {
128157
revert InvalidExpiration(expires, block.timestamp + minResetPeriod);
129158
}

test/OnChainAllocator.t.sol

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,27 @@ contract OnChainAllocatorTest is Test, TestHelper {
11471147
);
11481148
}
11491149

1150+
function test_allocateAndRegister_revert_InvalidAmount_native_with_zero_deposit() public {
1151+
Lock[] memory commitments = new Lock[](1);
1152+
commitments[0] = _makeLock(address(0), 0 ether);
1153+
1154+
vm.expectRevert(abi.encodeWithSelector(IOnChainAllocator.InvalidAmount.selector, 0));
1155+
allocator.allocateAndRegister{value: 0}(
1156+
recipient, commitments, arbiter, defaultExpiration, BATCH_COMPACT_TYPEHASH, bytes32(0)
1157+
);
1158+
}
1159+
1160+
function test_allocateAndRegister_revert_InvalidAmount_non_native_with_non_zero_native_call() public {
1161+
Lock[] memory commitments = new Lock[](1);
1162+
commitments[0] = _makeLock(address(usdc), 1);
1163+
1164+
uint256 amount = 1;
1165+
vm.expectRevert(abi.encodeWithSelector(IOnChainAllocator.InvalidAmount.selector, amount));
1166+
allocator.allocateAndRegister{value: amount}(
1167+
recipient, commitments, arbiter, defaultExpiration, BATCH_COMPACT_TYPEHASH, bytes32(0)
1168+
);
1169+
}
1170+
11501171
function test_allocateAndRegister_revert_InvalidAmount() public {
11511172
Lock[] memory commitments = new Lock[](1);
11521173
commitments[0] = _makeLock(address(usdc), uint256(type(uint224).max) + 1);

0 commit comments

Comments
 (0)