Skip to content

Commit 26d22ed

Browse files
authored
fix: Adds griefing protection to permit functionality (#12)
* fix: permit grifting issue while using permit functionality * fix: indent * fix: spell checks
1 parent 5eaab8a commit 26d22ed

File tree

5 files changed

+202
-22
lines changed

5 files changed

+202
-22
lines changed

lib/aave-address-book

Submodule aave-address-book updated 79 files

lib/aave-v3-core

Submodule aave-v3-core updated 87 files

src/BaseTokenWrapper.sol

+24-18
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,18 @@ abstract contract BaseTokenWrapper is Ownable, IBaseTokenWrapper {
5757
uint16 referralCode,
5858
PermitSignature calldata signature
5959
) external returns (uint256) {
60-
IERC20WithPermit(TOKEN_IN).permit(
61-
msg.sender,
62-
address(this),
63-
amount,
64-
signature.deadline,
65-
signature.v,
66-
signature.r,
67-
signature.s
68-
);
60+
// explicitly left try-catch block blank to protect users from permit griefing
61+
try
62+
IERC20WithPermit(TOKEN_IN).permit(
63+
msg.sender,
64+
address(this),
65+
amount,
66+
signature.deadline,
67+
signature.v,
68+
signature.r,
69+
signature.s
70+
)
71+
{} catch {}
6972
return _supplyToken(amount, onBehalfOf, referralCode);
7073
}
7174

@@ -85,15 +88,18 @@ abstract contract BaseTokenWrapper is Ownable, IBaseTokenWrapper {
8588
PermitSignature calldata signature
8689
) external returns (uint256) {
8790
IAToken aTokenOut = IAToken(POOL.getReserveData(TOKEN_OUT).aTokenAddress);
88-
aTokenOut.permit(
89-
msg.sender,
90-
address(this),
91-
amount,
92-
signature.deadline,
93-
signature.v,
94-
signature.r,
95-
signature.s
96-
);
91+
// explicitly left try-catch block blank to protect users from permit griefing
92+
try
93+
aTokenOut.permit(
94+
msg.sender,
95+
address(this),
96+
amount,
97+
signature.deadline,
98+
signature.v,
99+
signature.r,
100+
signature.s
101+
)
102+
{} catch {}
97103
return _withdrawToken(amount, to, aTokenOut);
98104
}
99105

test/BaseTokenWrapper.t.sol

+175-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ interface IERC2612 {
1313
function nonces(address owner) external view returns (uint256);
1414

1515
function DOMAIN_SEPARATOR() external view returns (bytes32);
16+
17+
function permit(
18+
address owner,
19+
address spender,
20+
uint256 amount,
21+
uint256 deadline,
22+
uint8 v,
23+
bytes32 r,
24+
bytes32 s
25+
) external;
1626
}
1727

1828
abstract contract BaseTokenWrapperTest is Test {
@@ -158,7 +168,7 @@ abstract contract BaseTokenWrapperTest is Test {
158168
ALICE,
159169
address(tokenWrapper),
160170
dealAmountScaled,
161-
IAToken(aTokenOut).nonces(ALICE),
171+
IERC2612(address(tokenIn)).nonces(ALICE),
162172
deadline
163173
)
164174
)
@@ -207,6 +217,96 @@ abstract contract BaseTokenWrapperTest is Test {
207217
}
208218
}
209219

220+
function testPermitGriefingSupplyTokenWithPermit() public {
221+
IERC20 tokenIn = IERC20(tokenWrapper.TOKEN_IN());
222+
assertEq(
223+
tokenIn.balanceOf(ALICE),
224+
0,
225+
'Unexpected Alice starting tokenIn balance'
226+
);
227+
assertEq(
228+
IAToken(aTokenOut).balanceOf(ALICE),
229+
0,
230+
'Unexpected Alice starting aToken balance'
231+
);
232+
233+
uint256 dealAmountScaled = DEAL_AMOUNT * 10 ** tokenInDecimals;
234+
_dealTokenIn(ALICE, dealAmountScaled);
235+
uint256 estimateFinalBalance = tokenWrapper.getTokenOutForTokenIn(
236+
dealAmountScaled
237+
);
238+
239+
uint256 deadline = block.timestamp + 1;
240+
bytes32 digest = keccak256(
241+
abi.encodePacked(
242+
hex'1901',
243+
IERC2612(address(tokenIn)).DOMAIN_SEPARATOR(),
244+
keccak256(
245+
abi.encode(
246+
PERMIT_TYPEHASH,
247+
ALICE,
248+
address(tokenWrapper),
249+
dealAmountScaled,
250+
IERC2612(address(tokenIn)).nonces(ALICE),
251+
deadline
252+
)
253+
)
254+
)
255+
);
256+
257+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ALICE_KEY, digest);
258+
IBaseTokenWrapper.PermitSignature memory signature = IBaseTokenWrapper
259+
.PermitSignature(deadline, v, r, s);
260+
261+
if (permitSupported) {
262+
vm.startPrank(BOB);
263+
IERC2612(address(tokenIn)).permit(
264+
ALICE,
265+
address(tokenWrapper),
266+
dealAmountScaled,
267+
deadline,
268+
v,
269+
r,
270+
s
271+
);
272+
vm.stopPrank();
273+
vm.startPrank(ALICE);
274+
uint256 suppliedAmount = tokenWrapper.supplyTokenWithPermit(
275+
dealAmountScaled,
276+
ALICE,
277+
REFERRAL_CODE,
278+
signature
279+
);
280+
vm.stopPrank();
281+
282+
assertEq(
283+
tokenIn.balanceOf(ALICE),
284+
0,
285+
'Unexpected ending tokenIn balance'
286+
);
287+
assertEq(
288+
suppliedAmount,
289+
IAToken(aTokenOut).balanceOf(ALICE),
290+
'Unexpected supply return/balance mismatch'
291+
);
292+
assertLe(
293+
estimateFinalBalance - IAToken(aTokenOut).balanceOf(ALICE),
294+
1,
295+
'Unexpected ending aToken balance'
296+
);
297+
} else {
298+
vm.startPrank(ALICE);
299+
vm.expectRevert();
300+
tokenWrapper.supplyTokenWithPermit(
301+
dealAmountScaled,
302+
ALICE,
303+
REFERRAL_CODE,
304+
signature
305+
);
306+
vm.stopPrank();
307+
}
308+
}
309+
210310
function testRevertSupplyTokenZeroAmount() public {
211311
vm.prank(ALICE);
212312
vm.expectRevert('INSUFFICIENT_AMOUNT_TO_SUPPLY');
@@ -425,6 +525,80 @@ abstract contract BaseTokenWrapperTest is Test {
425525
);
426526
}
427527

528+
function testPermitGriefingWithdrawTokenWithPermit() public {
529+
testSupplyToken();
530+
IERC20 tokenIn = IERC20(tokenWrapper.TOKEN_IN());
531+
532+
uint256 aTokenBalance = IAToken(aTokenOut).balanceOf(ALICE);
533+
assertGt(aTokenBalance, 0, 'Unexpected starting aToken balance');
534+
assertEq(
535+
tokenIn.balanceOf(ALICE),
536+
0,
537+
'Unexpected starting tokenIn balance'
538+
);
539+
uint256 estimateFinalBalance = tokenWrapper.getTokenInForTokenOut(
540+
aTokenBalance
541+
);
542+
543+
uint256 deadline = block.timestamp + 100;
544+
bytes32 digest = keccak256(
545+
abi.encodePacked(
546+
hex'1901',
547+
IAToken(aTokenOut).DOMAIN_SEPARATOR(),
548+
keccak256(
549+
abi.encode(
550+
PERMIT_TYPEHASH,
551+
ALICE,
552+
address(tokenWrapper),
553+
aTokenBalance,
554+
IAToken(aTokenOut).nonces(ALICE),
555+
deadline
556+
)
557+
)
558+
)
559+
);
560+
561+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(ALICE_KEY, digest);
562+
IBaseTokenWrapper.PermitSignature memory signature = IBaseTokenWrapper
563+
.PermitSignature(deadline, v, r, s);
564+
565+
vm.startPrank(BOB);
566+
IERC2612(aTokenOut).permit(
567+
ALICE,
568+
address(tokenWrapper),
569+
aTokenBalance,
570+
deadline,
571+
v,
572+
r,
573+
s
574+
);
575+
vm.stopPrank();
576+
577+
vm.startPrank(ALICE);
578+
uint256 withdrawnAmount = tokenWrapper.withdrawTokenWithPermit(
579+
aTokenBalance,
580+
ALICE,
581+
signature
582+
);
583+
vm.stopPrank();
584+
585+
assertEq(
586+
IAToken(aTokenOut).balanceOf(ALICE),
587+
0,
588+
'Unexpected ending aToken balance'
589+
);
590+
assertLe(
591+
estimateFinalBalance - tokenIn.balanceOf(ALICE),
592+
1,
593+
'Unexpected ending tokenIn balance'
594+
);
595+
assertLe(
596+
withdrawnAmount - tokenIn.balanceOf(ALICE),
597+
1,
598+
'Unexpected withdraw return/balance mismatch'
599+
);
600+
}
601+
428602
function testRevertWithdrawTokenZeroAmount() public {
429603
vm.prank(ALICE);
430604
vm.expectRevert('INSUFFICIENT_AMOUNT_TO_WITHDRAW');

0 commit comments

Comments
 (0)