Skip to content
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: safe permit #39

Merged
merged 10 commits into from
Nov 10, 2023
45 changes: 25 additions & 20 deletions src/CSAccounting.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2023 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.21;

Check warning on line 4 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Found more than One contract per file. 2 contracts found!

Check warning on line 4 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Found more than One contract per file. 2 contracts found!

import { AccessControlEnumerable } from "@openzeppelin/contracts/access/AccessControlEnumerable.sol";

Expand Down Expand Up @@ -77,7 +77,7 @@
bytes32 s;
}
struct BlockedBond {
uint256 ETHAmount;

Check warning on line 80 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Variable name must be in mixedCase

Check warning on line 80 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Variable name must be in mixedCase
uint256 retentionUntil;
}

Expand All @@ -100,7 +100,7 @@
ICSModule private immutable CSM;
IWstETH private immutable WSTETH;

address public FEE_DISTRIBUTOR;

Check warning on line 103 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Variable name must be in mixedCase

Check warning on line 103 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Variable name must be in mixedCase
uint256 public totalBondShares;

uint256 public blockedBondRetentionPeriod;
Expand Down Expand Up @@ -130,13 +130,13 @@
uint256 _blockedBondManagementPeriod
) {
// check zero addresses
require(admin != address(0), "admin is zero address");

Check warning on line 133 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Use Custom Errors instead of require statements

Check warning on line 133 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Use Custom Errors instead of require statements
require(lidoLocator != address(0), "lido locator is zero address");

Check warning on line 134 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Use Custom Errors instead of require statements

Check warning on line 134 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Use Custom Errors instead of require statements
require(

Check warning on line 135 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Use Custom Errors instead of require statements

Check warning on line 135 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Use Custom Errors instead of require statements
communityStakingModule != address(0),
"community staking module is zero address"
);
require(wstETH != address(0), "wstETH is zero address");

Check warning on line 139 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Use Custom Errors instead of require statements

Check warning on line 139 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Use Custom Errors instead of require statements
_validateBlockedBondPeriods(
_blockedBondRetentionPeriod,
_blockedBondManagementPeriod
Expand Down Expand Up @@ -409,7 +409,7 @@

/// @notice Returns the number of keys by the given bond ETH amount
function getKeysCountByBondETH(
uint256 ETHAmount

Check warning on line 412 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Variable name must be in mixedCase

Check warning on line 412 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Variable name must be in mixedCase
) public view returns (uint256) {
return ETHAmount / getRequiredBondETHForKeys(1);
}
Expand Down Expand Up @@ -470,16 +470,18 @@
PermitInput calldata permit
) external onlyExistingNodeOperator(nodeOperatorId) returns (uint256) {
from = (from == address(0)) ? msg.sender : from;
// solhint-disable-next-line func-named-parameters
_lido().permit(
from,
address(this),
permit.value,
permit.deadline,
permit.v,
permit.r,
permit.s
);
if (_lido().allowance(from, address(this)) < permit.value) {
madlabman marked this conversation as resolved.
Show resolved Hide resolved
// solhint-disable-next-line func-named-parameters
_lido().permit(
from,
address(this),
permit.value,
permit.deadline,
permit.v,
permit.r,
permit.s
);
}
return _depositStETH(from, nodeOperatorId, stETHAmount);
}

Expand Down Expand Up @@ -520,16 +522,19 @@
uint256 wstETHAmount,
PermitInput calldata permit
) external onlyExistingNodeOperator(nodeOperatorId) returns (uint256) {
// solhint-disable-next-line func-named-parameters
WSTETH.permit(
from,
address(this),
permit.value,
permit.deadline,
permit.v,
permit.r,
permit.s
);
from = (from == address(0)) ? msg.sender : from;
if (WSTETH.allowance(from, address(this)) < permit.value) {
// solhint-disable-next-line func-named-parameters
WSTETH.permit(
from,
address(this),
permit.value,
permit.deadline,
permit.v,
permit.r,
permit.s
);
}
return _depositWstETH(from, nodeOperatorId, wstETHAmount);
}

Expand Down Expand Up @@ -630,7 +635,7 @@
bytes32[] memory rewardsProof,
uint256 nodeOperatorId,
uint256 cumulativeFeeShares,
uint256 ETHAmount

Check warning on line 638 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Variable name must be in mixedCase

Check warning on line 638 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Variable name must be in mixedCase
)
external
onlyExistingNodeOperator(nodeOperatorId)
Expand Down Expand Up @@ -713,7 +718,7 @@
function compensateBlockedBondETH(
uint256 nodeOperatorId
) external payable onlyExistingNodeOperator(nodeOperatorId) {
require(msg.value > 0, "value should be greater than zero");

Check warning on line 721 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Use Custom Errors instead of require statements

Check warning on line 721 in src/CSAccounting.sol

View workflow job for this annotation

GitHub Actions / Linters

Use Custom Errors instead of require statements
payable(LIDO_LOCATOR.elRewardsVault()).transfer(msg.value);
emit BlockedBondCompensated(nodeOperatorId, msg.value);
_reduceBlockedBondETH(nodeOperatorId, msg.value);
Expand Down
88 changes: 88 additions & 0 deletions test/CSAccounting.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,49 @@ contract CSAccountingTest is
);
}

function test_depositStETHWithPermit_alreadyPermitted() public {
_createNodeOperator({ ongoingVals: 16, withdrawnVals: 0 });
vm.deal(user, 32 ether);
vm.prank(user);
stETH.submit{ value: 32 ether }({ _referal: address(0) });

vm.expectEmit(true, true, true, true, address(accounting));
emit StETHBondDeposited(0, user, 32 ether);

vm.mockCall(
address(stETH),
abi.encodeWithSelector(
stETH.permit.selector,
user,
address(accounting)
),
abi.encode(32 ether)
);

vm.recordLogs();

vm.prank(stranger);
accounting.depositStETHWithPermit(
user,
0,
32 ether,
CSAccounting.PermitInput({
value: 32 ether,
deadline: type(uint256).max,
// mock permit signature
v: 0,
r: 0,
s: 0
})
);

assertEq(
vm.getRecordedLogs().length,
1,
"should emit only one event about deposit"
);
}

function test_depositWstETHWithPermit() public {
_createNodeOperator({ ongoingVals: 16, withdrawnVals: 0 });
vm.deal(user, 32 ether);
Expand Down Expand Up @@ -461,6 +504,51 @@ contract CSAccountingTest is
);
}

function test_depositWstETHWithPermit_alreadyPermitted() public {
_createNodeOperator({ ongoingVals: 16, withdrawnVals: 0 });
vm.deal(user, 32 ether);
vm.startPrank(user);
stETH.submit{ value: 32 ether }({ _referal: address(0) });
uint256 wstETHAmount = wstETH.wrap(32 ether);
vm.stopPrank();

vm.expectEmit(true, true, true, true, address(accounting));
emit WstETHBondDeposited(0, user, wstETHAmount);

vm.mockCall(
address(wstETH),
abi.encodeWithSelector(
wstETH.permit.selector,
user,
address(accounting)
),
abi.encode(32 ether)
);

vm.recordLogs();

vm.prank(stranger);
accounting.depositWstETHWithPermit(
user,
0,
wstETHAmount,
CSAccounting.PermitInput({
value: 32 ether,
deadline: type(uint256).max,
// mock permit signature
v: 0,
r: 0,
s: 0
})
);

assertEq(
vm.getRecordedLogs().length,
1,
"should emit only one event about deposit"
);
}

function test_deposit_RevertIfNotExistedOperator() public {
vm.expectRevert("node operator does not exist");
accounting.depositStETH(user, 0, 32 ether);
Expand Down
Loading