Skip to content

Commit

Permalink
Adapt tests from model to client code
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasmt committed Jul 8, 2024
1 parent 9e0f326 commit 2946832
Show file tree
Hide file tree
Showing 12 changed files with 444 additions and 204 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/lido-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
branches:
- develop
- rvdevelop
- tests-on-client-code
jobs:
test:
runs-on: ubuntu-latest
Expand All @@ -25,7 +26,7 @@
-H "Authorization: Bearer ${{ secrets.RV_COMPUTE_TOKEN }}" \
https://api.github.com/repos/runtimeverification/_kaas_lidofinance_dual-governance/actions/workflows/lido-ci.yml/dispatches \
-d '{
"ref": "rvdevelop",
"ref": "tests-on-client-code",
"inputs": {
"branch_name": "'"${{ github.event.pull_request.head.sha || github.sha }}"'",
"extra_args": "script",
Expand Down
2 changes: 1 addition & 1 deletion contracts/interfaces/IStETH.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface IStETH is IERC20 {

function getPooledEthByShares(uint256 sharesAmount) external view returns (uint256);

function transferShares(address to, uint256 amount) external;
function transferShares(address to, uint256 amount) external returns (uint256);
function transferSharesFrom(
address _sender,
address _recipient,
Expand Down
4 changes: 3 additions & 1 deletion contracts/model/StETHModel.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
pragma solidity 0.8.23;

contract StETHModel {
import "contracts/interfaces/IStETH.sol";

contract StETHModel is IStETH {
uint256 private totalPooledEther;
uint256 private totalShares;
mapping(address => uint256) private shares;
Expand Down
9 changes: 9 additions & 0 deletions contracts/model/WithdrawalQueueModel.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
pragma solidity 0.8.23;

contract WithdrawalQueueModel {
uint256 _lastFinalizedRequestId;

function getLastFinalizedRequestId() external view returns (uint256) {
return _lastFinalizedRequestId;
}
}
119 changes: 119 additions & 0 deletions contracts/model/WstETHAdapted.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// SPDX-FileCopyrightText: 2021 Lido <[email protected]>

// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import "contracts/interfaces/IStETH.sol";

/**
* @title StETH token wrapper with static balances.
* @dev It's an ERC20 token that represents the account's share of the total
* supply of stETH tokens. WstETH token's balance only changes on transfers,
* unlike StETH that is also changed when oracles report staking rewards and
* penalties. It's a "power user" token for DeFi protocols which don't
* support rebasable tokens.
*
* The contract is also a trustless wrapper that accepts stETH tokens and mints
* wstETH in return. Then the user unwraps, the contract burns user's wstETH
* and sends user locked stETH in return.
*
* The contract provides the staking shortcut: user can send ETH with regular
* transfer and get wstETH in return. The contract will send ETH to Lido submit
* method, staking it and wrapping the received stETH.
*
*/
contract WstETHAdapted is ERC20Permit {
IStETH public stETH;

/**
* @param _stETH address of the StETH token to wrap
*/
constructor(IStETH _stETH)
public
ERC20Permit("Wrapped liquid staked Ether 2.0")
ERC20("Wrapped liquid staked Ether 2.0", "wstETH")
{
stETH = _stETH;
}

/**
* @notice Exchanges stETH to wstETH
* @param _stETHAmount amount of stETH to wrap in exchange for wstETH
* @dev Requirements:
* - `_stETHAmount` must be non-zero
* - msg.sender must approve at least `_stETHAmount` stETH to this
* contract.
* - msg.sender must have at least `_stETHAmount` of stETH.
* User should first approve _stETHAmount to the WstETH contract
* @return Amount of wstETH user receives after wrap
*/
function wrap(uint256 _stETHAmount) external returns (uint256) {
require(_stETHAmount > 0, "wstETH: can't wrap zero stETH");
uint256 wstETHAmount = stETH.getSharesByPooledEth(_stETHAmount);
_mint(msg.sender, wstETHAmount);
stETH.transferFrom(msg.sender, address(this), _stETHAmount);
return wstETHAmount;
}

/**
* @notice Exchanges wstETH to stETH
* @param _wstETHAmount amount of wstETH to uwrap in exchange for stETH
* @dev Requirements:
* - `_wstETHAmount` must be non-zero
* - msg.sender must have at least `_wstETHAmount` wstETH.
* @return Amount of stETH user receives after unwrap
*/
function unwrap(uint256 _wstETHAmount) external returns (uint256) {
require(_wstETHAmount > 0, "wstETH: zero amount unwrap not allowed");
uint256 stETHAmount = stETH.getPooledEthByShares(_wstETHAmount);
_burn(msg.sender, _wstETHAmount);
stETH.transfer(msg.sender, stETHAmount);
return stETHAmount;
}

/**
* @notice Shortcut to stake ETH and auto-wrap returned stETH
*/
/*
receive() external payable {
uint256 shares = stETH.submit{value: msg.value}(address(0));
_mint(msg.sender, shares);
}
*/

/**
* @notice Get amount of wstETH for a given amount of stETH
* @param _stETHAmount amount of stETH
* @return Amount of wstETH for a given stETH amount
*/
function getWstETHByStETH(uint256 _stETHAmount) external view returns (uint256) {
return stETH.getSharesByPooledEth(_stETHAmount);
}

/**
* @notice Get amount of stETH for a given amount of wstETH
* @param _wstETHAmount amount of wstETH
* @return Amount of stETH for a given wstETH amount
*/
function getStETHByWstETH(uint256 _wstETHAmount) external view returns (uint256) {
return stETH.getPooledEthByShares(_wstETHAmount);
}

/**
* @notice Get amount of stETH for a one wstETH
* @return Amount of stETH for 1 wstETH
*/
function stEthPerToken() external view returns (uint256) {
return stETH.getPooledEthByShares(1 ether);
}

/**
* @notice Get amount of wstETH for a one stETH
* @return Amount of wstETH for a 1 stETH
*/
function tokensPerStEth() external view returns (uint256) {
return stETH.getSharesByPooledEth(1 ether);
}
}
46 changes: 34 additions & 12 deletions test/kontrol/DualGovernanceSetUp.sol
Original file line number Diff line number Diff line change
@@ -1,26 +1,46 @@
pragma solidity 0.8.23;

import "contracts/model/DualGovernanceModel.sol";
import "contracts/model/EmergencyProtectedTimelockModel.sol";
import "contracts/model/EscrowModel.sol";
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";

import "contracts/Configuration.sol";
import "contracts/DualGovernance.sol";
import "contracts/EmergencyProtectedTimelock.sol";
import "contracts/Escrow.sol";
import "contracts/model/StETHModel.sol";
import "contracts/model/WstETHAdapted.sol";
import "contracts/model/WithdrawalQueueModel.sol";

import "test/kontrol/StorageSetup.sol";

contract DualGovernanceSetUp is StorageSetup {
DualGovernanceModel dualGovernance;
EmergencyProtectedTimelockModel timelock;
using DualGovernanceState for DualGovernanceState.Store;

Configuration config;
DualGovernance dualGovernance;
EmergencyProtectedTimelock timelock;
StETHModel stEth;
EscrowModel signallingEscrow;
EscrowModel rageQuitEscrow;
WstETHAdapted wstEth;
WithdrawalQueueModel withdrawalQueue;
IEscrow signallingEscrow;
IEscrow rageQuitEscrow;

function setUp() public {
stEth = new StETHModel();
uint256 emergencyProtectionTimelock = 0; // Regular deployment mode
dualGovernance = new DualGovernanceModel(address(stEth), emergencyProtectionTimelock);
timelock = dualGovernance.emergencyProtectedTimelock();
signallingEscrow = dualGovernance.signallingEscrow();
rageQuitEscrow = new EscrowModel(address(dualGovernance), address(stEth));
wstEth = new WstETHAdapted(IStETH(stEth));
withdrawalQueue = new WithdrawalQueueModel();

// Placeholder addresses
address adminExecutor = address(uint160(uint256(keccak256("adminExecutor"))));
address emergencyGovernance = address(uint160(uint256(keccak256("emergencyGovernance"))));
address adminProposer = address(uint160(uint256(keccak256("adminProposer"))));

config = new Configuration(adminExecutor, emergencyGovernance, new address[](0));
timelock = new EmergencyProtectedTimelock(address(config));
Escrow escrowMasterCopy = new Escrow(address(stEth), address(wstEth), address(withdrawalQueue), address(config));
dualGovernance =
new DualGovernance(address(config), address(timelock), address(escrowMasterCopy), adminProposer);
signallingEscrow = IEscrow(_loadAddress(address(dualGovernance), 5));
rageQuitEscrow = IEscrow(Clones.clone(address(escrowMasterCopy)));

// ?STORAGE
// ?WORD: totalPooledEther
Expand Down Expand Up @@ -59,5 +79,7 @@ contract DualGovernanceSetUp is StorageSetup {

// ?STORAGE3
kevm.symbolicStorage(address(timelock));

kevm.symbolicStorage(address(withdrawalQueue));
}
}
Loading

0 comments on commit 2946832

Please sign in to comment.