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

[WIP] Add Rebasable token to Optimism #58

Draft
wants to merge 65 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
3256d9f
create new contract that represents stETH on L2
kovalgek Oct 10, 2023
e4b2cbd
add wrap/unwrap functions
kovalgek Oct 11, 2023
82038ea
add shares to rebasable token
kovalgek Oct 13, 2023
380caaa
add unit tests
kovalgek Oct 17, 2023
eab8718
add deposit flow for new rebasable token
kovalgek Nov 4, 2023
c3bc0db
add withdraw flow for rebasable token on optimism
kovalgek Nov 5, 2023
eb98d0b
send rate in data and time during deposit flow
kovalgek Nov 9, 2023
800e33f
add possibility to depost 0 tokens
kovalgek Nov 9, 2023
d9ac556
init structs
kovalgek Nov 9, 2023
ddb9ca3
add comments
kovalgek Dec 11, 2023
a73bcf4
add gas test
kovalgek Dec 11, 2023
6d37edc
update gas test
kovalgek Dec 12, 2023
8098fe1
fix tests
kovalgek Dec 16, 2023
3335339
add token rate oracle
kovalgek Dec 20, 2023
86037dc
add tests for new token, renaming
kovalgek Dec 24, 2023
86788b2
simplify oracle, remove warnings
kovalgek Dec 27, 2023
ddffe1f
update unit tests for token rate oracle
kovalgek Dec 28, 2023
7c79d2d
fix integration tests for rebasable token
kovalgek Jan 9, 2024
6174943
Merge branch 'main' into feature/add_stETH_token_to_optimism
kovalgek Jan 9, 2024
f0b891a
add test for push token rate method
kovalgek Jan 9, 2024
e314bb1
unused return values fix
kovalgek Jan 9, 2024
4b56ccf
fix deployment scripts for new oracle and token
kovalgek Jan 9, 2024
3b82da7
add hearbeat to oracle
kovalgek Jan 10, 2024
171db5a
pr fixes from first rount review: apply unstructured storage in token…
kovalgek Jan 22, 2024
76b4ff3
PR fixes: fix comments, rename interface, abstract wst
kovalgek Jan 29, 2024
7abf60f
use real implemnations of non rebasable token and oracle contracts in…
kovalgek Feb 2, 2024
707da9b
update unit tests for bridges
kovalgek Feb 8, 2024
1cb344a
fix evetns + formating
kovalgek Feb 13, 2024
f9463f0
Merge branch 'main' into feature/add_stETH_token_to_optimism
kovalgek Feb 29, 2024
3fd1e94
move token rate to the base contract
kovalgek Mar 6, 2024
66b2501
use inheritance for token rate in l1 bridge, fix e2e tests
kovalgek Mar 6, 2024
7badd9e
add rebasable token e2e tests
kovalgek Mar 6, 2024
72bc8f3
add bridging-to e2e tests for rebasable token
kovalgek Mar 6, 2024
83e292b
add token rate observer
kovalgek Mar 7, 2024
dbf2f92
add observers array
kovalgek Mar 14, 2024
47c50c6
add factory and tests
kovalgek Mar 18, 2024
7e45410
add unit tests for notifier
kovalgek Mar 19, 2024
55111b8
add token rate pusher
kovalgek Mar 22, 2024
d99a52f
update unit tests for token rate oracle
kovalgek Mar 22, 2024
fb3eb40
remove observer array
kovalgek Mar 22, 2024
b167ebd
fix token rate publicher and notifier unit tests
kovalgek Mar 22, 2024
55d2e3f
fix small comments from PR review
kovalgek Mar 22, 2024
13712ca
feat(steth): intermediate work on adding ERC-2612/EIP-1271 permit
arwer13 Mar 27, 2024
6a46704
update optimism sdk version to fix integration tests
kovalgek Mar 29, 2024
b9d5e56
update addresses to run e2e tests
kovalgek Mar 29, 2024
a3f4544
fix integration test
kovalgek Mar 29, 2024
e2c29e0
deployment scripts
kovalgek Apr 1, 2024
d28d53d
Merge branch 'feature/add_stETH_token_to_optimism' into feat/steth-pe…
arwer13 Apr 1, 2024
0d2df1d
add token rate oracle integration tests
kovalgek Apr 1, 2024
8dc246e
fix happy path test for permit for rebasable
arwer13 Apr 2, 2024
ba0b41e
test(rebasable permit): move the rest tests from core stethpermit.tes…
arwer13 Apr 2, 2024
18a1dc5
add e2e tests for oracle
kovalgek Apr 2, 2024
624219c
Merge pull request #74 from lidofinance/feat/steth-permit
kovalgek Apr 2, 2024
5f86091
remove increaseAllowance and decreaseAllowance from tokens
kovalgek Apr 3, 2024
cc868ef
change function names
kovalgek Apr 3, 2024
06d00c7
rename stETH to rebasable token in env
kovalgek Apr 3, 2024
0b22b0f
deploy all script, refactor other deploy scripts
kovalgek Apr 8, 2024
f563fab
add permit to non-rebasable token
kovalgek Apr 9, 2024
3332e30
add unit tests for non-rebasable token
kovalgek Apr 10, 2024
0545e33
PR fixes: fix comments, readme, rename bridges, notifier refactoring
kovalgek Apr 10, 2024
a55b5f0
use mapping for tokens in bridges
kovalgek Apr 15, 2024
f54eecd
fix bridge unit and integration tests, remove mapping in storing toke…
kovalgek Apr 15, 2024
bc1c56f
fix scripts
kovalgek Apr 15, 2024
b62fbee
PR fixes: add comments, rename constracts, more interfaces to contrac…
kovalgek Apr 15, 2024
f387de6
PR fixes: rename tokens, add sanity check to oracle update, add unit …
kovalgek Apr 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
charset = utf-8
indent_style = space
indent_size = 4

[*.{js,yml,json,cjs}]
indent_size = 2
max_line_length = 120
38 changes: 36 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,41 @@ ETHERSCAN_API_KEY_OPT=
# Bridge/Gateway Deployment
# ############################

# Address of the token to deploy the bridge/gateway for
# Address of the token on L1 to deploy the bridge/gateway for
TOKEN=

# Address of the rebasable token on L1 to deploy the bridge/gateway for
REBASABLE_TOKEN=

# Address of token rate pusher. Required to config TokenRateOracle.
L1_OP_STACK_TOKEN_RATE_PUSHER=

# Gas limit required to complete pushing token rate on L2.
kovalgek marked this conversation as resolved.
Show resolved Hide resolved
# Default is: 300_000.
# This value was calculated by formula:
# l2GasLimit = (gas cost of L2Bridge.finalizeDeposit() + OptimismPortal.minimumGasLimit(depositData.length)) * 1.5
L2_GAS_LIMIT_FOR_PUSHING_TOKEN_RATE=

# A time period when token rate can be considered outdated.
TOKEN_RATE_OUTDATED_DELAY=86400 # default is 86400 (24 hours)

# Address of L1 token bridge proxy.
L1_TOKEN_BRIDGE=

# Address of L2 token bridge proxy.
L2_TOKEN_BRIDGE=

# Address of the non-rebasable token proxy on L2.
L2_TOKEN=
TheDZhon marked this conversation as resolved.
Show resolved Hide resolved

# Address of token rate oracle on L2
L2_TOKEN_RATE_ORACLE=

# Address of bridge executor.
GOV_BRIDGE_EXECUTOR=

# Name of the network environments used by deployment scripts.
# Might be one of: "mainnet", "goerli".
# Might be one of: "mainnet", "sepolia".
NETWORK=mainnet

# Run deployment in the forking network instead of public ones
Expand Down Expand Up @@ -74,6 +104,10 @@ TESTING_ARB_L2_GATEWAY_ROUTER=0x57f54f87C44d816f60b92864e23b8c0897D4d81D
TESTING_OPT_NETWORK=
TESTING_OPT_L1_TOKEN=0xaF8a2F0aE374b03376155BF745A3421Dac711C12
TESTING_OPT_L2_TOKEN=0xAED5F9aaF167923D34174b8E636aaF040A11f6F7
TESTING_OPT_L1_TOKEN_RATE_NOTIFIER=0x554f2C7D58522c050d38Ebea4FF072ED7C4e61cb
TESTING_OPT_L1_REBASABLE_TOKEN=0xB82381A3fBD3FaFA77B3a7bE693342618240067b
TESTING_OPT_L2_REBASABLE_TOKEN=0x6696Cb7bb602FC744254Ad9E07EfC474FBF78857
TESTING_OPT_L2_TOKEN_RATE_ORACLE=0x8ea513d1e5Be31fb5FC2f2971897594720de9E70
TESTING_OPT_L1_ERC20_TOKEN_BRIDGE=0x243b661276670bD17399C488E7287ea4D416115b
TESTING_OPT_L2_ERC20_TOKEN_BRIDGE=0x447CD1794d209Ac4E6B4097B34658bc00C4d0a51

Expand Down
3 changes: 3 additions & 0 deletions .env.wsteth.opt_mainnet
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ ETHERSCAN_API_KEY_OPT=
# Address of the token to deploy the bridge/gateway for
TOKEN=0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0

# Address of the rebasable token to deploy the bridge/gateway for
REBASABLE_TOKEN=

# Name of the network environments used by deployment scripts.
# Might be one of: "mainnet", "goerli".
NETWORK=mainnet
Expand Down
8 changes: 4 additions & 4 deletions .storage-layout
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,12 @@
|------|------|------|--------|-------|----------|

=======================
L1ERC20TokenBridge
L1ERC20ExtendedTokensBridge
=======================

| Name | Type | Slot | Offset | Bytes | Contract |
|--------|---------------------------------------------------|------|--------|-------|--------------------------------------------------------------|
| _roles | mapping(bytes32 => struct AccessControl.RoleData) | 0 | 0 | 32 | contracts/optimism/L1ERC20TokenBridge.sol:L1ERC20TokenBridge |
| _roles | mapping(bytes32 => struct AccessControl.RoleData) | 0 | 0 | 32 | contracts/optimism/L1ERC20ExtendedTokensBridge.sol:L1ERC20ExtendedTokensBridge |

=======================
➡ L1ERC20TokenGateway
Expand All @@ -81,12 +81,12 @@
|------|------|------|--------|-------|----------|

=======================
L2ERC20TokenBridge
L2ERC20ExtendedTokensBridge
=======================

| Name | Type | Slot | Offset | Bytes | Contract |
|--------|---------------------------------------------------|------|--------|-------|--------------------------------------------------------------|
| _roles | mapping(bytes32 => struct AccessControl.RoleData) | 0 | 0 | 32 | contracts/optimism/L2ERC20TokenBridge.sol:L2ERC20TokenBridge |
| _roles | mapping(bytes32 => struct AccessControl.RoleData) | 0 | 0 | 32 | contracts/optimism/L2ERC20ExtendedTokensBridge.sol:L2ERC20ExtendedTokensBridge |

=======================
➡ L2ERC20TokenGateway
Expand Down
26 changes: 20 additions & 6 deletions README.md
kovalgek marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ To retrieve more detailed info about the bridging process, see the specification

- [Lido's Arbitrum Gateway](https://github.com/lidofinance/lido-l2/blob/main/contracts/arbitrum/README.md).
- [Lido's Optimism Bridge](https://github.com/lidofinance/lido-l2/blob/main/contracts/optimism/README.md).
- [wstETH Bridging Guide](https://docs.lido.fi/token-guides/wsteth-bridging-guide/#r-5-bridging-l1-lido-dao-decisions)

## Project setup

Expand Down Expand Up @@ -42,8 +43,17 @@ Fill the newly created `.env` file with the required variables. See the [Project

The configuration of the deployment scripts happens via the ENV variables. The following variables are required:

- [`TOKEN`](#TOKEN) - address of the token to deploy a new bridge on the Ethereum chain.
- [`NETWORK`](#NETWORK) - name of the network environments used by deployment scripts. Allowed values: `mainnet`, `goerli`.
- [`TOKEN`](#TOKEN) - address of the non-rebasable token to deploy a new bridge on the Ethereum chain.
- [`REBASABLE_TOKEN`] (#REBASABLE_TOKEN) - address of the rebasable token to deploy new bridge on the Ethereum chain.
kovalgek marked this conversation as resolved.
Show resolved Hide resolved
- [`L1_OP_STACK_TOKEN_RATE_PUSHER`](#L1_OP_STACK_TOKEN_RATE_PUSHER) - address of token rate pusher. Required to config TokenRateOracle.
- [`L2_GAS_LIMIT_FOR_PUSHING_TOKEN_RATE`](#L2_GAS_LIMIT_FOR_PUSHING_TOKEN_RATE) - gas limit required to complete pushing token rate on L2.This value was calculated by formula: l2GasLimit = (gas cost of L2Bridge.finalizeDeposit() + OptimismPortal.minimumGasLimit(depositData.length)) * 1.5
- [`TOKEN_RATE_OUTDATED_DELAY`](#TOKEN_RATE_OUTDATED_DELAY) - a time period when token rate can be considered outdated. Default is 86400 (24 hours).
- [`L1_TOKEN_BRIDGE`](#L1_TOKEN_BRIDGE) - address of L1 token bridge.
- [`L2_TOKEN_BRIDGE`](#L2_TOKEN_BRIDGE) - address of L2 token bridge.
- [`L2_TOKEN`](#L2_TOKEN) - address of the non-rebasable token on L2.
- [`L2_TOKEN_RATE_ORACLE`](#L2_TOKEN_RATE_ORACLE) - address of token rate oracle on L2.
- [`GOV_BRIDGE_EXECUTOR`](#GOV_BRIDGE_EXECUTOR) - address of bridge executor.
- [`NETWORK`](#NETWORK) - name of the network environments used by deployment scripts. Allowed values: `mainnet`, `sepolia`.
- [`FORKING`](#FORKING) - run deployment in the forking network instead of real ones
- [`ETH_DEPLOYER_PRIVATE_KEY`](#ETH_DEPLOYER_PRIVATE_KEY) - The private key of the deployer account in the Ethereum network is used during the deployment process.
- [`ARB_DEPLOYER_PRIVATE_KEY`](#ARB_DEPLOYER_PRIVATE_KEY) - The private key of the deployer account in the Arbitrum network is used during the deployment process.
Expand Down Expand Up @@ -314,13 +324,17 @@ Below variables used in the Arbitrum/Optimism bridge deployment process.

#### `TOKEN`

Address of the token to deploy a new bridge on the Ethereum chain.
Address of the existing non-rebasable token to deploy a new bridge for on the Ethereum chain.

#### `REBASABLE_TOKEN`

Address of the existing rebasable token to deploy new bridge for on the Ethereum chain.

#### `NETWORK`

> Default value: `mainnet`

Name of the network environments used by deployment scripts. Might be one of: `mainnet`, `goerli`.
Name of the network environments used by deployment scripts. Might be one of: `mainnet`, `sepolia`.

#### `FORKING`

Expand Down Expand Up @@ -442,7 +456,7 @@ The following variables are used in the process of the Integration & E2E testing

#### `TESTING_ARB_NETWORK`

Name of the network environments used for Arbitrum Integration & E2E testing. Might be one of: `mainnet`, `goerli`.
Name of the network environments used for Arbitrum Integration & E2E testing. Might be one of: `mainnet`, `sepolia`.

#### `TESTING_ARB_L1_TOKEN`

Expand Down Expand Up @@ -482,7 +496,7 @@ Address of the L2 gateway router used in the Acceptance Integration & E2E (when

#### `TESTING_OPT_NETWORK`

Name of the network environments used for Optimism Integration & E2E testing. Might be one of: `mainnet`, `goerli`.
Name of the network environments used for Optimism Integration & E2E testing. Might be one of: `mainnet`, `sepolia`.

#### `TESTING_OPT_L1_TOKEN`

Expand Down
8 changes: 4 additions & 4 deletions artifacts-opt.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
{
"artifactPath": "artifacts/contracts/proxy/OssifiableProxy.sol/OssifiableProxy.json",
"sourcePath": "contracts/proxy/OssifiableProxy.sol",
"name": "L2ERC20TokenBridge proxy",
"name": "L2ERC20ExtendedTokensBridge proxy",
"address": "0x8E01013243a96601a86eb3153F0d9Fa4fbFb6957"
},
{
"artifactPath": "artifacts/contracts/optimism/L2ERC20TokenBridge.sol/L2ERC20TokenBridge.json",
"sourcePath": "contracts/optimism/L2ERC20TokenBridge.sol",
"name": "L2ERC20TokenBridge",
"artifactPath": "artifacts/contracts/optimism/L2ERC20ExtendedTokensBridge.sol/L2ERC20ExtendedTokensBridge.json",
"sourcePath": "contracts/optimism/L2ERC20ExtendedTokensBridge.sol",
"name": "L2ERC20ExtendedTokensBridge",
"address": "0x23B96aDD54c479C6784Dd504670B5376B808f4C7",
"txHash": "0x5d69e9c6ec1d634f0d90812c2189c925993d1fffbc9b0b416fdc123e15407c56"
},
Expand Down
2 changes: 1 addition & 1 deletion contracts/arbitrum/L2ERC20TokenGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

pragma solidity 0.8.10;

import {IERC20Bridged} from "../token/interfaces/IERC20Bridged.sol";
import {IERC20Bridged} from "../token/ERC20Bridged.sol";
import {IL2TokenGateway, IInterchainTokenGateway} from "./interfaces/IL2TokenGateway.sol";

import {L2CrossDomainEnabled} from "./L2CrossDomainEnabled.sol";
Expand Down
2 changes: 0 additions & 2 deletions contracts/arbitrum/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,6 @@ Transfers `amount` of token from the `from_` account to `to_` using the allowanc
> - **`addedValue_`** - a number to increase allowance
>
> **Emits:** `Approval(address indexed owner, address indexed spender, uint256 value)`

Atomically increases the allowance granted to `spender` by the caller. Returns a `bool` value indicating whether the operation succeed.

#### `decreaseAllowance(address,uint256)`
Expand All @@ -722,7 +721,6 @@ Atomically increases the allowance granted to `spender` by the caller. Returns a
> - **`subtractedValue_`** - a number to decrease allowance
>
> **Emits:** `Approval(address indexed owner, address indexed spender, uint256 value)`

Atomically decreases the allowance granted to `spender` by the caller. Returns a `bool` value indicating whether the operation succeed.

## `ERC20Bridged`
Expand Down
44 changes: 44 additions & 0 deletions contracts/lib/DepositDataCodec.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-FileCopyrightText: 2024 Lido <info@lido.fi>
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.10;

/// @author kovalgek
/// @notice encodes and decodes DepositData for crosschain transfering.
library DepositDataCodec {

uint8 internal constant RATE_FIELD_SIZE = 12;
uint8 internal constant TIMESTAMP_FIELD_SIZE = 5;

struct DepositData {
uint96 rate;
uint40 timestamp;
bytes data;
}

function encodeDepositData(DepositData memory depositData) internal pure returns (bytes memory) {
bytes memory data = bytes.concat(
abi.encodePacked(depositData.rate),
abi.encodePacked(depositData.timestamp),
abi.encodePacked(depositData.data)
);
return data;
}

function decodeDepositData(bytes calldata buffer) internal pure returns (DepositData memory) {

if (buffer.length < RATE_FIELD_SIZE + TIMESTAMP_FIELD_SIZE) {
revert ErrorDepositDataLength();
}

DepositData memory depositData = DepositData({
rate: uint96(bytes12(buffer[0:RATE_FIELD_SIZE])),
timestamp: uint40(bytes5(buffer[RATE_FIELD_SIZE:RATE_FIELD_SIZE + TIMESTAMP_FIELD_SIZE])),
data: buffer[RATE_FIELD_SIZE + TIMESTAMP_FIELD_SIZE:]
});

return depositData;
}

error ErrorDepositDataLength();
}
57 changes: 57 additions & 0 deletions contracts/lib/ECDSA.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-FileCopyrightText: 2024 Lido <info@lido.fi>
// SPDX-License-Identifier: MIT

// Extracted from:
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/cryptography/ECDSA.sol#L53
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/541e821/contracts/utils/cryptography/ECDSA.sol#L112

pragma solidity 0.8.10;

library ECDSA {
/**
* @dev Returns the address that signed a hashed message (`hash`).
* This address can then be used for verification purposes.
* Receives the `v`, `r` and `s` signature fields separately.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address)
{
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");

Check warning on line 35 in contracts/lib/ECDSA.sol

View workflow job for this annotation

GitHub Actions / solhint

Error message for require is too long: 34 counted / 32 allowed

Check warning on line 35 in contracts/lib/ECDSA.sol

View workflow job for this annotation

GitHub Actions / solhint

Use Custom Errors instead of require statements

// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
require(signer != address(0), "ECDSA: invalid signature");

Check warning on line 39 in contracts/lib/ECDSA.sol

View workflow job for this annotation

GitHub Actions / solhint

Use Custom Errors instead of require statements

return signer;
}

/**
* @dev Overload of `recover` that receives the `r` and `vs` short-signature fields separately.
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
bytes32 s;
uint8 v;
assembly {
s := and(vs, 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
v := add(shr(255, vs), 27)
}
return recover(hash, v, r, s);
}
}
64 changes: 64 additions & 0 deletions contracts/lib/SignatureChecker.sol
kovalgek marked this conversation as resolved.
Show resolved Hide resolved
kovalgek marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: 2024 Lido <info@lido.fi>
// SPDX-License-Identifier: MIT

pragma solidity 0.8.10;

import {ECDSA} from "./ECDSA.sol";

/// @dev A copy of SignatureUtils.sol contract from Lido Core Protocol
/// https://github.com/lidofinance/lido-dao/blob/master/contracts/common/lib/SignatureUtils.sol
library SignatureChecker {
/**
* @dev The selector of the ERC1271's `isValidSignature(bytes32 hash, bytes signature)` function,
* serving at the same time as the magic value that the function should return upon success.
*
* See https://eips.ethereum.org/EIPS/eip-1271.
*
* bytes4(keccak256("isValidSignature(bytes32,bytes)")
*/
bytes4 internal constant ERC1271_IS_VALID_SIGNATURE_SELECTOR = 0x1626ba7e;

/**
* @dev Checks signature validity.
*
* If the signer address doesn't contain any code, assumes that the address is externally owned
* and the signature is a ECDSA signature generated using its private key. Otherwise, issues a
* static call to the signer address to check the signature validity using the ERC-1271 standard.
*/
function isValidSignature(
address signer,
bytes32 msgHash,
uint8 v,
bytes32 r,
bytes32 s
) internal view returns (bool) {
if (_hasCode(signer)) {
bytes memory sig = abi.encodePacked(r, s, v);
// Solidity <0.5 generates a regular CALL instruction even if the function being called
// is marked as `view`, and the only way to perform a STATICCALL is to use assembly
bytes memory data = abi.encodeWithSelector(ERC1271_IS_VALID_SIGNATURE_SELECTOR, msgHash, sig);
bytes32 retval;
/// @solidity memory-safe-assembly
assembly {
// allocate memory for storing the return value
let outDataOffset := mload(0x40)
mstore(0x40, add(outDataOffset, 32))
// issue a static call and load the result if the call succeeded
let success := staticcall(gas(), signer, add(data, 32), mload(data), outDataOffset, 32)
if and(eq(success, 1), eq(returndatasize(), 32)) {
retval := mload(outDataOffset)
}
}
return retval == bytes32(ERC1271_IS_VALID_SIGNATURE_SELECTOR);
} else {
return ECDSA.recover(msgHash, v, r, s) == signer;
}
}

function _hasCode(address addr) internal view returns (bool) {
uint256 size;
/// @solidity memory-safe-assembly
assembly { size := extcodesize(addr) }
return size > 0;
}
}
Loading
Loading