Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
6ad72677
Unverified
Commit
6ad72677
authored
Apr 01, 2023
by
Mark Tyneway
Committed by
GitHub
Apr 01, 2023
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #5321 from ethereum-optimism/fix/build
fix: unrevert + fix build
parents
f68fe55d
d35020d9
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
1269 additions
and
0 deletions
+1269
-0
Enum.sol
packages/contracts-bedrock/scripts/upgrades/Enum.sol
+13
-0
IGnosisSafe.sol
packages/contracts-bedrock/scripts/upgrades/IGnosisSafe.sol
+106
-0
LibSort.sol
packages/contracts-bedrock/scripts/upgrades/LibSort.sol
+708
-0
PostSherlock.s.sol
...ges/contracts-bedrock/scripts/upgrades/PostSherlock.s.sol
+442
-0
No files found.
packages/contracts-bedrock/scripts/upgrades/Enum.sol
0 → 100644
View file @
6ad72677
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
/**
* @title Enum - Collection of enums used in Safe contracts.
* @author Richard Meissner - @rmeissner
*/
abstract contract Enum {
enum Operation {
Call,
DelegateCall
}
}
packages/contracts-bedrock/scripts/upgrades/IGnosisSafe.sol
0 → 100644
View file @
6ad72677
pragma solidity ^0.8.10;
import { Enum } from "./Enum.sol";
interface IGnosisSafe {
event AddedOwner(address owner);
event ApproveHash(bytes32 indexed approvedHash, address indexed owner);
event ChangedFallbackHandler(address handler);
event ChangedGuard(address guard);
event ChangedThreshold(uint256 threshold);
event DisabledModule(address module);
event EnabledModule(address module);
event ExecutionFailure(bytes32 txHash, uint256 payment);
event ExecutionFromModuleFailure(address indexed module);
event ExecutionFromModuleSuccess(address indexed module);
event ExecutionSuccess(bytes32 txHash, uint256 payment);
event RemovedOwner(address owner);
event SafeReceived(address indexed sender, uint256 value);
event SafeSetup(
address indexed initiator, address[] owners, uint256 threshold, address initializer, address fallbackHandler
);
event SignMsg(bytes32 indexed msgHash);
function VERSION() external view returns (string memory);
function addOwnerWithThreshold(address owner, uint256 _threshold) external;
function approveHash(bytes32 hashToApprove) external;
function approvedHashes(address, bytes32) external view returns (uint256);
function changeThreshold(uint256 _threshold) external;
function checkNSignatures(bytes32 dataHash, bytes memory data, bytes memory signatures, uint256 requiredSignatures)
external
view;
function checkSignatures(bytes32 dataHash, bytes memory data, bytes memory signatures) external view;
function disableModule(address prevModule, address module) external;
function domainSeparator() external view returns (bytes32);
function enableModule(address module) external;
function encodeTransactionData(
address to,
uint256 value,
bytes memory data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address refundReceiver,
uint256 _nonce
) external view returns (bytes memory);
function execTransaction(
address to,
uint256 value,
bytes memory data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address refundReceiver,
bytes memory signatures
) external payable returns (bool success);
function execTransactionFromModule(address to, uint256 value, bytes memory data, Enum.Operation operation)
external
returns (bool success);
function execTransactionFromModuleReturnData(address to, uint256 value, bytes memory data, Enum.Operation operation)
external
returns (bool success, bytes memory returnData);
function getChainId() external view returns (uint256);
function getModulesPaginated(address start, uint256 pageSize)
external
view
returns (address[] memory array, address next);
function getOwners() external view returns (address[] memory);
function getStorageAt(uint256 offset, uint256 length) external view returns (bytes memory);
function getThreshold() external view returns (uint256);
function getTransactionHash(
address to,
uint256 value,
bytes memory data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address refundReceiver,
uint256 _nonce
) external view returns (bytes32);
function isModuleEnabled(address module) external view returns (bool);
function isOwner(address owner) external view returns (bool);
function nonce() external view returns (uint256);
function removeOwner(address prevOwner, address owner, uint256 _threshold) external;
function requiredTxGas(address to, uint256 value, bytes memory data, Enum.Operation operation) external returns (uint256);
function setFallbackHandler(address handler) external;
function setGuard(address guard) external;
function setup(
address[] memory _owners,
uint256 _threshold,
address to,
bytes memory data,
address fallbackHandler,
address paymentToken,
uint256 payment,
address paymentReceiver
) external;
function signedMessages(bytes32) external view returns (uint256);
function simulateAndRevert(address targetContract, bytes memory calldataPayload) external;
function swapOwner(address prevOwner, address oldOwner, address newOwner) external;
}
packages/contracts-bedrock/scripts/upgrades/LibSort.sol
0 → 100644
View file @
6ad72677
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @notice Optimized sorts and operations for sorted arrays.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Sort.sol)
library LibSort {
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INSERTION SORT */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// - Faster on small arrays (32 or lesser elements).
// - Faster on almost sorted arrays.
// - Smaller bytecode.
// - May be suitable for view functions intended for off-chain querying.
/// @dev Sorts the array in-place with insertion sort.
function insertionSort(uint256[] memory a) internal pure {
/// @solidity memory-safe-assembly
assembly {
let n := mload(a) // Length of `a`.
mstore(a, 0) // For insertion sort's inner loop to terminate.
let h := add(a, shl(5, n)) // High slot.
let s := 0x20
let w := not(31)
for { let i := add(a, s) } 1 {} {
i := add(i, s)
if gt(i, h) { break }
let k := mload(i) // Key.
let j := add(i, w) // The slot before the current slot.
let v := mload(j) // The value of `j`.
if iszero(gt(v, k)) { continue }
for {} 1 {} {
mstore(add(j, s), v)
j := add(j, w) // `sub(j, 0x20)`.
v := mload(j)
if iszero(gt(v, k)) { break }
}
mstore(add(j, s), k)
}
mstore(a, n) // Restore the length of `a`.
}
}
/// @dev Sorts the array in-place with insertion sort.
function insertionSort(int256[] memory a) internal pure {
_convertTwosComplement(a);
insertionSort(_toUints(a));
_convertTwosComplement(a);
}
/// @dev Sorts the array in-place with insertion sort.
function insertionSort(address[] memory a) internal pure {
insertionSort(_toUints(a));
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTRO-QUICKSORT */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// - Faster on larger arrays (more than 32 elements).
// - Robust performance.
// - Larger bytecode.
/// @dev Sorts the array in-place with intro-quicksort.
function sort(uint256[] memory a) internal pure {
/// @solidity memory-safe-assembly
assembly {
let w := not(31)
let s := 0x20
let n := mload(a) // Length of `a`.
mstore(a, 0) // For insertion sort's inner loop to terminate.
// Let the stack be the start of the free memory.
let stack := mload(0x40)
for {} iszero(lt(n, 2)) {} {
// Push `l` and `h` to the stack.
// The `shl` by 5 is equivalent to multiplying by `0x20`.
let l := add(a, s)
let h := add(a, shl(5, n))
let j := l
// forgefmt: disable-next-item
for {} iszero(or(eq(j, h), gt(mload(j), mload(add(j, s))))) {} {
j := add(j, s)
}
// If the array is already sorted.
if eq(j, h) { break }
j := h
// forgefmt: disable-next-item
for {} iszero(gt(mload(j), mload(add(j, w)))) {} {
j := add(j, w) // `sub(j, 0x20)`.
}
// If the array is reversed sorted.
if eq(j, l) {
for {} 1 {} {
let t := mload(l)
mstore(l, mload(h))
mstore(h, t)
h := add(h, w) // `sub(h, 0x20)`.
l := add(l, s)
if iszero(lt(l, h)) { break }
}
break
}
// Push `l` and `h` onto the stack.
mstore(stack, l)
mstore(add(stack, s), h)
stack := add(stack, 0x40)
break
}
for { let stackBottom := mload(0x40) } iszero(eq(stack, stackBottom)) {} {
// Pop `l` and `h` from the stack.
stack := sub(stack, 0x40)
let l := mload(stack)
let h := mload(add(stack, s))
// Do insertion sort if `h - l <= 0x20 * 12`.
// Threshold is fine-tuned via trial and error.
if iszero(gt(sub(h, l), 0x180)) {
// Hardcode sort the first 2 elements.
let i := add(l, s)
if iszero(lt(mload(l), mload(i))) {
let t := mload(i)
mstore(i, mload(l))
mstore(l, t)
}
for {} 1 {} {
i := add(i, s)
if gt(i, h) { break }
let k := mload(i) // Key.
let j := add(i, w) // The slot before the current slot.
let v := mload(j) // The value of `j`.
if iszero(gt(v, k)) { continue }
for {} 1 {} {
mstore(add(j, s), v)
j := add(j, w)
v := mload(j)
if iszero(gt(v, k)) { break }
}
mstore(add(j, s), k)
}
continue
}
// Pivot slot is the average of `l` and `h`.
let p := add(shl(5, shr(6, add(l, h))), and(31, l))
// Median of 3 with sorting.
{
let e0 := mload(l)
let e2 := mload(h)
let e1 := mload(p)
if iszero(lt(e0, e1)) {
let t := e0
e0 := e1
e1 := t
}
if iszero(lt(e0, e2)) {
let t := e0
e0 := e2
e2 := t
}
if iszero(lt(e1, e2)) {
let t := e1
e1 := e2
e2 := t
}
mstore(p, e1)
mstore(h, e2)
mstore(l, e0)
}
// Hoare's partition.
{
// The value of the pivot slot.
let x := mload(p)
p := h
for { let i := l } 1 {} {
for {} 1 {} {
i := add(i, s)
if iszero(gt(x, mload(i))) { break }
}
let j := p
for {} 1 {} {
j := add(j, w)
if iszero(lt(x, mload(j))) { break }
}
p := j
if iszero(lt(i, p)) { break }
// Swap slots `i` and `p`.
let t := mload(i)
mstore(i, mload(p))
mstore(p, t)
}
}
// If slice on right of pivot is non-empty, push onto stack.
{
mstore(stack, add(p, s))
// Skip `mstore(add(stack, 0x20), h)`, as it is already on the stack.
stack := add(stack, shl(6, lt(add(p, s), h)))
}
// If slice on left of pivot is non-empty, push onto stack.
{
mstore(stack, l)
mstore(add(stack, s), p)
stack := add(stack, shl(6, gt(p, l)))
}
}
mstore(a, n) // Restore the length of `a`.
}
}
/// @dev Sorts the array in-place with intro-quicksort.
function sort(int256[] memory a) internal pure {
_convertTwosComplement(a);
sort(_toUints(a));
_convertTwosComplement(a);
}
/// @dev Sorts the array in-place with intro-quicksort.
function sort(address[] memory a) internal pure {
sort(_toUints(a));
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* OTHER USEFUL OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// For performance, the `uniquifySorted` methods will not revert if the
// array is not sorted -- it will simply remove consecutive duplicate elements.
/// @dev Removes duplicate elements from a ascendingly sorted memory array.
function uniquifySorted(uint256[] memory a) internal pure {
/// @solidity memory-safe-assembly
assembly {
// If the length of `a` is greater than 1.
if iszero(lt(mload(a), 2)) {
let x := add(a, 0x20)
let y := add(a, 0x40)
let end := add(a, shl(5, add(mload(a), 1)))
for {} 1 {} {
if iszero(eq(mload(x), mload(y))) {
x := add(x, 0x20)
mstore(x, mload(y))
}
y := add(y, 0x20)
if eq(y, end) { break }
}
mstore(a, shr(5, sub(x, a)))
}
}
}
/// @dev Removes duplicate elements from a ascendingly sorted memory array.
function uniquifySorted(int256[] memory a) internal pure {
uniquifySorted(_toUints(a));
}
/// @dev Removes duplicate elements from a ascendingly sorted memory array.
function uniquifySorted(address[] memory a) internal pure {
uniquifySorted(_toUints(a));
}
/// @dev Returns whether `a` contains `needle`,
/// and the index of the nearest element less than or equal to `needle`.
function searchSorted(uint256[] memory a, uint256 needle)
internal
pure
returns (bool found, uint256 index)
{
(found, index) = _searchSorted(a, needle, 0);
}
/// @dev Returns whether `a` contains `needle`,
/// and the index of the nearest element less than or equal to `needle`.
function searchSorted(int256[] memory a, int256 needle)
internal
pure
returns (bool found, uint256 index)
{
(found, index) = _searchSorted(_toUints(a), uint256(needle), 1 << 255);
}
/// @dev Returns whether `a` contains `needle`,
/// and the index of the nearest element less than or equal to `needle`.
function searchSorted(address[] memory a, address needle)
internal
pure
returns (bool found, uint256 index)
{
(found, index) = _searchSorted(_toUints(a), uint256(uint160(needle)), 0);
}
/// @dev Reverses the array in-place.
function reverse(uint256[] memory a) internal pure {
/// @solidity memory-safe-assembly
assembly {
if iszero(lt(mload(a), 2)) {
let s := 0x20
let w := not(31)
let h := add(a, shl(5, mload(a)))
for { a := add(a, s) } 1 {} {
let t := mload(a)
mstore(a, mload(h))
mstore(h, t)
h := add(h, w)
a := add(a, s)
if iszero(lt(a, h)) { break }
}
}
}
}
/// @dev Reverses the array in-place.
function reverse(int256[] memory a) internal pure {
reverse(_toUints(a));
}
/// @dev Reverses the array in-place.
function reverse(address[] memory a) internal pure {
reverse(_toUints(a));
}
/// @dev Returns whether the array is sorted in ascending order.
function isSorted(uint256[] memory a) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
if iszero(lt(mload(a), 2)) {
let end := add(a, shl(5, mload(a)))
for { a := add(a, 0x20) } 1 {} {
let p := mload(a)
a := add(a, 0x20)
result := iszero(gt(p, mload(a)))
if iszero(mul(result, xor(a, end))) { break }
}
}
}
}
/// @dev Returns whether the array is sorted in ascending order.
function isSorted(int256[] memory a) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
if iszero(lt(mload(a), 2)) {
let end := add(a, shl(5, mload(a)))
for { a := add(a, 0x20) } 1 {} {
let p := mload(a)
a := add(a, 0x20)
result := iszero(sgt(p, mload(a)))
if iszero(mul(result, xor(a, end))) { break }
}
}
}
}
/// @dev Returns whether the array is sorted in ascending order.
function isSorted(address[] memory a) internal pure returns (bool result) {
result = isSorted(_toUints(a));
}
/// @dev Returns whether the array is strictly ascending (sorted and uniquified).
function isSortedAndUniquified(uint256[] memory a) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
if iszero(lt(mload(a), 2)) {
let end := add(a, shl(5, mload(a)))
for { a := add(a, 0x20) } 1 {} {
let p := mload(a)
a := add(a, 0x20)
result := lt(p, mload(a))
if iszero(mul(result, xor(a, end))) { break }
}
}
}
}
/// @dev Returns whether the array is strictly ascending (sorted and uniquified).
function isSortedAndUniquified(int256[] memory a) internal pure returns (bool result) {
/// @solidity memory-safe-assembly
assembly {
result := 1
if iszero(lt(mload(a), 2)) {
let end := add(a, shl(5, mload(a)))
for { a := add(a, 0x20) } 1 {} {
let p := mload(a)
a := add(a, 0x20)
result := slt(p, mload(a))
if iszero(mul(result, xor(a, end))) { break }
}
}
}
}
/// @dev Returns whether the array is strictly ascending (sorted and uniquified).
function isSortedAndUniquified(address[] memory a) internal pure returns (bool result) {
result = isSortedAndUniquified(_toUints(a));
}
/// @dev Returns the sorted set difference of `a` and `b`.
/// Note: Behaviour is undefined if inputs are not sorted and uniquified.
function difference(uint256[] memory a, uint256[] memory b)
internal
pure
returns (uint256[] memory c)
{
c = _difference(a, b, 0);
}
/// @dev Returns the sorted set difference between `a` and `b`.
/// Note: Behaviour is undefined if inputs are not sorted and uniquified.
function difference(int256[] memory a, int256[] memory b)
internal
pure
returns (int256[] memory c)
{
c = _toInts(_difference(_toUints(a), _toUints(b), 1 << 255));
}
/// @dev Returns the sorted set difference between `a` and `b`.
/// Note: Behaviour is undefined if inputs are not sorted and uniquified.
function difference(address[] memory a, address[] memory b)
internal
pure
returns (address[] memory c)
{
c = _toAddresses(_difference(_toUints(a), _toUints(b), 0));
}
/// @dev Returns the sorted set intersection between `a` and `b`.
/// Note: Behaviour is undefined if inputs are not sorted and uniquified.
function intersection(uint256[] memory a, uint256[] memory b)
internal
pure
returns (uint256[] memory c)
{
c = _intersection(a, b, 0);
}
/// @dev Returns the sorted set intersection between `a` and `b`.
/// Note: Behaviour is undefined if inputs are not sorted and uniquified.
function intersection(int256[] memory a, int256[] memory b)
internal
pure
returns (int256[] memory c)
{
c = _toInts(_intersection(_toUints(a), _toUints(b), 1 << 255));
}
/// @dev Returns the sorted set intersection between `a` and `b`.
/// Note: Behaviour is undefined if inputs are not sorted and uniquified.
function intersection(address[] memory a, address[] memory b)
internal
pure
returns (address[] memory c)
{
c = _toAddresses(_intersection(_toUints(a), _toUints(b), 0));
}
/// @dev Returns the sorted set union of `a` and `b`.
/// Note: Behaviour is undefined if inputs are not sorted and uniquified.
function union(uint256[] memory a, uint256[] memory b)
internal
pure
returns (uint256[] memory c)
{
c = _union(a, b, 0);
}
/// @dev Returns the sorted set union of `a` and `b`.
/// Note: Behaviour is undefined if inputs are not sorted and uniquified.
function union(int256[] memory a, int256[] memory b)
internal
pure
returns (int256[] memory c)
{
c = _toInts(_union(_toUints(a), _toUints(b), 1 << 255));
}
/// @dev Returns the sorted set union between `a` and `b`.
/// Note: Behaviour is undefined if inputs are not sorted and uniquified.
function union(address[] memory a, address[] memory b)
internal
pure
returns (address[] memory c)
{
c = _toAddresses(_union(_toUints(a), _toUints(b), 0));
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* PRIVATE HELPERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Reinterpret cast to an uint256 array.
function _toUints(int256[] memory a) private pure returns (uint256[] memory casted) {
/// @solidity memory-safe-assembly
assembly {
casted := a
}
}
/// @dev Reinterpret cast to an uint256 array.
function _toUints(address[] memory a) private pure returns (uint256[] memory casted) {
/// @solidity memory-safe-assembly
assembly {
// As any address written to memory will have the upper 96 bits
// of the word zeroized (as per Solidity spec), we can directly
// compare these addresses as if they are whole uint256 words.
casted := a
}
}
/// @dev Reinterpret cast to an int array.
function _toInts(uint256[] memory a) private pure returns (int256[] memory casted) {
/// @solidity memory-safe-assembly
assembly {
casted := a
}
}
/// @dev Reinterpret cast to an address array.
function _toAddresses(uint256[] memory a) private pure returns (address[] memory casted) {
/// @solidity memory-safe-assembly
assembly {
casted := a
}
}
/// @dev Converts an array of signed two-complement integers
/// to unsigned integers suitable for sorting.
function _convertTwosComplement(int256[] memory a) private pure {
/// @solidity memory-safe-assembly
assembly {
let w := shl(255, 1)
for { let end := add(a, shl(5, mload(a))) } iszero(eq(a, end)) {} {
a := add(a, 0x20)
mstore(a, add(mload(a), w))
}
}
}
/// @dev Returns whether `a` contains `needle`,
/// and the index of the nearest element less than or equal to `needle`.
function _searchSorted(uint256[] memory a, uint256 needle, uint256 signed)
private
pure
returns (bool found, uint256 index)
{
/// @solidity memory-safe-assembly
assembly {
let m := 0 // Middle slot.
let s := 0x20
let l := add(a, s) // Slot of the start of search.
let h := add(a, shl(5, mload(a))) // Slot of the end of search.
for { needle := add(signed, needle) } 1 {} {
// Average of `l` and `h`.
m := add(shl(5, shr(6, add(l, h))), and(31, l))
let t := add(signed, mload(m))
found := eq(t, needle)
if or(gt(l, h), found) { break }
// Decide whether to search the left or right half.
if iszero(gt(needle, t)) {
h := sub(m, s)
continue
}
l := add(m, s)
}
// `m` will be less than `add(a, 0x20)` in the case of an empty array,
// or when the value is less than the smallest value in the array.
let t := iszero(lt(m, add(a, s)))
index := shr(5, mul(sub(m, add(a, s)), t))
found := and(found, t)
}
}
/// @dev Returns the sorted set difference of `a` and `b`.
/// Note: Behaviour is undefined if inputs are not sorted and uniquified.
function _difference(uint256[] memory a, uint256[] memory b, uint256 signed)
private
pure
returns (uint256[] memory c)
{
/// @solidity memory-safe-assembly
assembly {
let s := 0x20
let aEnd := add(a, shl(5, mload(a)))
let bEnd := add(b, shl(5, mload(b)))
c := mload(0x40) // Set `c` to the free memory pointer.
a := add(a, s)
b := add(b, s)
let k := c
for {} iszero(or(gt(a, aEnd), gt(b, bEnd))) {} {
let u := mload(a)
let v := mload(b)
if iszero(xor(u, v)) {
a := add(a, s)
b := add(b, s)
continue
}
if iszero(lt(add(u, signed), add(v, signed))) {
b := add(b, s)
continue
}
k := add(k, s)
mstore(k, u)
a := add(a, s)
}
for {} iszero(gt(a, aEnd)) {} {
k := add(k, s)
mstore(k, mload(a))
a := add(a, s)
}
mstore(c, shr(5, sub(k, c))) // Store the length of `c`.
mstore(0x40, add(k, s)) // Allocate the memory for `c`.
}
}
/// @dev Returns the sorted set intersection between `a` and `b`.
/// Note: Behaviour is undefined if inputs are not sorted and uniquified.
function _intersection(uint256[] memory a, uint256[] memory b, uint256 signed)
private
pure
returns (uint256[] memory c)
{
/// @solidity memory-safe-assembly
assembly {
let s := 0x20
let aEnd := add(a, shl(5, mload(a)))
let bEnd := add(b, shl(5, mload(b)))
c := mload(0x40) // Set `c` to the free memory pointer.
a := add(a, s)
b := add(b, s)
let k := c
for {} iszero(or(gt(a, aEnd), gt(b, bEnd))) {} {
let u := mload(a)
let v := mload(b)
if iszero(xor(u, v)) {
k := add(k, s)
mstore(k, u)
a := add(a, s)
b := add(b, s)
continue
}
if iszero(lt(add(u, signed), add(v, signed))) {
b := add(b, s)
continue
}
a := add(a, s)
}
mstore(c, shr(5, sub(k, c))) // Store the length of `c`.
mstore(0x40, add(k, s)) // Allocate the memory for `c`.
}
}
/// @dev Returns the sorted set union of `a` and `b`.
/// Note: Behaviour is undefined if inputs are not sorted and uniquified.
function _union(uint256[] memory a, uint256[] memory b, uint256 signed)
private
pure
returns (uint256[] memory c)
{
/// @solidity memory-safe-assembly
assembly {
let s := 0x20
let aEnd := add(a, shl(5, mload(a)))
let bEnd := add(b, shl(5, mload(b)))
c := mload(0x40) // Set `c` to the free memory pointer.
a := add(a, s)
b := add(b, s)
let k := c
for {} iszero(or(gt(a, aEnd), gt(b, bEnd))) {} {
let u := mload(a)
let v := mload(b)
if iszero(xor(u, v)) {
k := add(k, s)
mstore(k, u)
a := add(a, s)
b := add(b, s)
continue
}
if iszero(lt(add(u, signed), add(v, signed))) {
k := add(k, s)
mstore(k, v)
b := add(b, s)
continue
}
k := add(k, s)
mstore(k, u)
a := add(a, s)
}
for {} iszero(gt(a, aEnd)) {} {
k := add(k, s)
mstore(k, mload(a))
a := add(a, s)
}
for {} iszero(gt(b, bEnd)) {} {
k := add(k, s)
mstore(k, mload(b))
b := add(b, s)
}
mstore(c, shr(5, sub(k, c))) // Store the length of `c`.
mstore(0x40, add(k, s)) // Allocate the memory for `c`.
}
}
}
packages/contracts-bedrock/scripts/upgrades/PostSherlock.s.sol
0 → 100644
View file @
6ad72677
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { console } from "forge-std/console.sol";
import { Script } from "forge-std/Script.sol";
import { IMulticall3 } from "forge-std/interfaces/IMulticall3.sol";
import { IGnosisSafe } from "./IGnosisSafe.sol";
import { LibSort } from "./LibSort.sol";
import { Enum } from "./Enum.sol";
import { ProxyAdmin } from "../../contracts/universal/ProxyAdmin.sol";
import { Constants } from "../../contracts/libraries/Constants.sol";
import { SystemConfig } from "../../contracts/L1/SystemConfig.sol";
import { ResourceMetering } from "../../contracts/L1/ResourceMetering.sol";
import { Semver } from "../../contracts/universal/Semver.sol";
/**
* @title PostSherlock
* @notice Upgrade script for upgrading the L1 contracts after the sherlock audit.
* Assumes that a gnosis safe is used as the privileged account and the same
* gnosis safe is the owner of the system config and the proxy admin.
* This could be optimized by checking for the number of approvals up front
* and not submitting the final approval as `execTransaction` can be called when
* there are `threshold - 1` approvals.
* Uses the "approved hashes" method of interacting with the gnosis safe. Allows
* for the most simple user experience when using automation and no indexer.
* Run the command without the `--broadcast` flag and it will print a tenderly URL.
*/
contract PostSherlock is Script {
/**
* @notice Interface for multicall3.
*/
IMulticall3 private constant multicall = IMulticall3(MULTICALL3_ADDRESS);
/**
* @notice Mainnet chain id.
*/
uint256 constant MAINNET = 1;
/**
* @notice Goerli chain id.
*/
uint256 constant GOERLI = 5;
/**
* @notice Represents a set of L1 contracts. Used to represent a set of
* implementations and also a set of proxies.
*/
struct ContractSet {
address L1CrossDomainMessenger;
address L1StandardBridge;
address L2OutputOracle;
address OptimismMintableERC20Factory;
address OptimismPortal;
address SystemConfig;
address L1ERC721Bridge;
}
/**
* @notice A mapping of chainid to a ContractSet of implementations.
*/
mapping(uint256 => ContractSet) internal implementations;
/**
* @notice A mapping of chainid to ContractSet of proxy addresses.
*/
mapping(uint256 => ContractSet) internal proxies;
/**
* @notice An array of approvals, used to generate the execution transaction.
*/
address[] internal approvals;
/**
* @notice The expected versions for the contracts to be upgraded to.
*/
string constant internal L1CrossDomainMessenger_Version = "1.1.0";
string constant internal L1StandardBridge_Version = "1.1.0";
string constant internal L2OutputOracle_Version = "1.2.0";
string constant internal OptimismMintableERC20Factory_Version = "1.1.0";
string constant internal OptimismPortal_Version = "1.3.0";
string constant internal SystemConfig_Version = "1.2.0";
string constant internal L1ERC721Bridge_Version = "1.1.0";
/**
* @notice Place the contract addresses in storage so they can be used when building calldata.
*/
function setUp() external {
implementations[GOERLI] = ContractSet({
L1CrossDomainMessenger: 0xfa37a4b2D49E21De63fa2b13D6dB213081E020b3,
L1StandardBridge: 0x79179704077E3324CC745A24a5CcC2a80A9B6842,
L2OutputOracle: 0x47bBB9054823f27B9B6A71F5cb0eBc785692FF2E,
OptimismMintableERC20Factory: 0xF516Fa87f89E4AC7C299aE28263e9EB851dE4781,
OptimismPortal: 0xa24A444C6ceeb1d4Fc19D1B78913C22B9d03BbC9,
SystemConfig: 0x2FFfe603caA9FA2C20E7F349138475a43284a6b1,
L1ERC721Bridge: 0xb460323429B08B9d1d427e6b8A450532988d5fe8
});
proxies[GOERLI] = ContractSet({
L1CrossDomainMessenger: 0x5086d1eEF304eb5284A0f6720f79403b4e9bE294,
L1StandardBridge: 0x636Af16bf2f682dD3109e60102b8E1A089FedAa8,
L2OutputOracle: 0xE6Dfba0953616Bacab0c9A8ecb3a9BBa77FC15c0,
OptimismMintableERC20Factory: 0x883dcF8B05364083D849D8bD226bC8Cb4c42F9C5,
OptimismPortal: 0x5b47E1A08Ea6d985D6649300584e6722Ec4B1383,
SystemConfig: 0xAe851f927Ee40dE99aaBb7461C00f9622ab91d60,
L1ERC721Bridge: 0x8DD330DdE8D9898d43b4dc840Da27A07dF91b3c9
});
}
/**
* @notice The entrypoint to this script.
*/
function run(address _safe, address _proxyAdmin) external returns (bool) {
vm.startBroadcast();
bool success = _run(_safe, _proxyAdmin);
if (success) _postCheck();
return success;
}
/**
* @notice The implementation of the upgrade. Split into its own function
* to allow for testability. This is subject to a race condition if
* the nonce changes by a different transaction finalizing while not
* all of the signers have used this script.
*/
function _run(address _safe, address _proxyAdmin) public returns (bool) {
// Ensure that the required contracts exist
require(address(multicall).code.length > 0, "multicall3 not deployed");
require(_safe.code.length > 0, "no code at safe address");
require(_proxyAdmin.code.length > 0, "no code at proxy admin address");
IGnosisSafe safe = IGnosisSafe(payable(_safe));
uint256 nonce = safe.nonce();
bytes memory data = buildCalldata(_proxyAdmin);
// Compute the safe transaction hash
bytes32 hash = safe.getTransactionHash({
to: address(multicall),
value: 0,
data: data,
operation: Enum.Operation.DelegateCall,
safeTxGas: 0,
baseGas: 0,
gasPrice: 0,
gasToken: address(0),
refundReceiver: address(0),
_nonce: nonce
});
// Send a transaction to approve the hash
safe.approveHash(hash);
logSimulationLink({
_to: address(safe),
_from: msg.sender,
_data: abi.encodeCall(safe.approveHash, (hash))
});
uint256 threshold = safe.getThreshold();
address[] memory owners = safe.getOwners();
for (uint256 i; i < owners.length; i++) {
address owner = owners[i];
uint256 approved = safe.approvedHashes(owner, hash);
if (approved == 1) {
approvals.push(owner);
}
}
if (approvals.length >= threshold) {
bytes memory signatures = buildSignatures();
bool success = safe.execTransaction({
to: address(multicall),
value: 0,
data: data,
operation: Enum.Operation.DelegateCall,
safeTxGas: 0,
baseGas: 0,
gasPrice: 0,
gasToken: address(0),
refundReceiver: payable(address(0)),
signatures: signatures
});
logSimulationLink({
_to: address(safe),
_from: msg.sender,
_data: abi.encodeCall(
safe.execTransaction,
(
address(multicall),
0,
data,
Enum.Operation.DelegateCall,
0,
0,
0,
address(0),
payable(address(0)),
signatures
)
)
});
require(success, "call not successful");
return true;
} else {
console.log("not enough approvals");
}
// Reset the approvals because they are only used transiently.
assembly {
sstore(approvals.slot, 0)
}
return false;
}
/**
* @notice Log a tenderly simulation link. The TENDERLY_USERNAME and TENDERLY_PROJECT
* environment variables will be used if they are present. The vm is staticcall'ed
* because of a compiler issue with the higher level ABI.
*/
function logSimulationLink(address _to, bytes memory _data, address _from) public view {
(, bytes memory projData) = VM_ADDRESS.staticcall(
abi.encodeWithSignature("envOr(string,string)", "TENDERLY_PROJECT", "TENDERLY_PROJECT")
);
string memory proj = abi.decode(projData, (string));
(, bytes memory userData) = VM_ADDRESS.staticcall(
abi.encodeWithSignature("envOr(string,string)", "TENDERLY_USERNAME", "TENDERLY_USERNAME")
);
string memory username = abi.decode(userData, (string));
string memory str = string.concat(
"https://dashboard.tenderly.co/",
username,
"/",
proj,
"/simulator/new?network=",
vm.toString(block.chainid),
"&contractAddress=",
vm.toString(_to),
"&rawFunctionInput=",
vm.toString(_data),
"&from=",
vm.toString(_from)
);
console.log(str);
}
/**
* @notice Follow up assertions to ensure that the script ran to completion.
*/
function _postCheck() internal view {
ContractSet memory prox = getProxies();
require(_versionHash(prox.L1CrossDomainMessenger) == keccak256(bytes(L1CrossDomainMessenger_Version)));
require(_versionHash(prox.L1StandardBridge) == keccak256(bytes(L1StandardBridge_Version)));
require(_versionHash(prox.L2OutputOracle) == keccak256(bytes(L2OutputOracle_Version)));
require(_versionHash(prox.OptimismMintableERC20Factory) == keccak256(bytes(OptimismMintableERC20Factory_Version)));
require(_versionHash(prox.OptimismPortal) == keccak256(bytes(OptimismPortal_Version)));
require(_versionHash(prox.SystemConfig) == keccak256(bytes(SystemConfig_Version)));
require(_versionHash(prox.L1ERC721Bridge) == keccak256(bytes(L1ERC721Bridge_Version)));
ResourceMetering.ResourceConfig memory rcfg = SystemConfig(prox.SystemConfig).resourceConfig();
ResourceMetering.ResourceConfig memory dflt = Constants.DEFAULT_RESOURCE_CONFIG();
require(keccak256(abi.encode(rcfg)) == keccak256(abi.encode(dflt)));
}
/**
* @notice Helper function used to compute the hash of Semver's version string to be used in a
* comparison.
*/
function _versionHash(address _addr) internal view returns (bytes32) {
return keccak256(bytes(Semver(_addr).version()));
}
/**
* @notice Test coverage of the logic. Should only run on goerli but other chains
* could be added.
*/
function test_script_succeeds() skipWhenNotForking external {
address safe;
address proxyAdmin;
if (block.chainid == GOERLI) {
safe = 0xBc1233d0C3e6B5d53Ab455cF65A6623F6dCd7e4f;
proxyAdmin = 0x01d3670863c3F4b24D7b107900f0b75d4BbC6e0d;
}
require(safe != address(0) && proxyAdmin != address(0));
address[] memory owners = IGnosisSafe(payable(safe)).getOwners();
for (uint256 i; i < owners.length; i++) {
address owner = owners[i];
vm.startBroadcast(owner);
bool success = _run(safe, proxyAdmin);
vm.stopBroadcast();
if (success) {
console.log("tx success");
break;
}
}
_postCheck();
}
/**
* @notice Builds the signatures by tightly packing them together.
* Ensures that they are sorted.
*/
function buildSignatures() internal view returns (bytes memory) {
address[] memory addrs = new address[](approvals.length);
for (uint256 i; i < approvals.length; i++) {
addrs[i] = approvals[i];
}
LibSort.sort(addrs);
bytes memory signatures;
uint8 v = 1;
bytes32 s = bytes32(0);
for (uint256 i; i < addrs.length; i++) {
bytes32 r = bytes32(uint256(uint160(addrs[i])));
signatures = bytes.concat(signatures, abi.encodePacked(r, s, v));
}
return signatures;
}
/**
* @notice Builds the calldata that the multisig needs to make for the upgrade to happen.
* A total of 8 calls are made, 7 upgrade implementations and 1 sets the resource
* config to the default value in the SystemConfig contract.
*/
function buildCalldata(address _proxyAdmin) internal view returns (bytes memory) {
IMulticall3.Call3[] memory calls = new IMulticall3.Call3[](8);
ContractSet memory impl = getImplementations();
ContractSet memory prox = getProxies();
// Upgrade the L1CrossDomainMessenger
calls[0] = IMulticall3.Call3({
target: _proxyAdmin,
allowFailure: false,
callData: abi.encodeCall(
ProxyAdmin.upgrade,
(payable(prox.L1CrossDomainMessenger), impl.L1CrossDomainMessenger)
)
});
// Upgrade the L1StandardBridge
calls[1] = IMulticall3.Call3({
target: _proxyAdmin,
allowFailure: false,
callData: abi.encodeCall(
ProxyAdmin.upgrade,
(payable(prox.L1StandardBridge), impl.L1StandardBridge)
)
});
// Upgrade the L2OutputOracle
calls[2] = IMulticall3.Call3({
target: _proxyAdmin,
allowFailure: false,
callData: abi.encodeCall(
ProxyAdmin.upgrade,
(payable(prox.L2OutputOracle), impl.L2OutputOracle)
)
});
// Upgrade the OptimismMintableERC20Factory
calls[3] = IMulticall3.Call3({
target: _proxyAdmin,
allowFailure: false,
callData: abi.encodeCall(
ProxyAdmin.upgrade,
(payable(prox.OptimismMintableERC20Factory), impl.OptimismMintableERC20Factory)
)
});
// Upgrade the OptimismPortal
calls[4] = IMulticall3.Call3({
target: _proxyAdmin,
allowFailure: false,
callData: abi.encodeCall(
ProxyAdmin.upgrade,
(payable(prox.OptimismPortal), impl.OptimismPortal)
)
});
// Upgrade the SystemConfig
calls[5] = IMulticall3.Call3({
target: _proxyAdmin,
allowFailure: false,
callData: abi.encodeCall(
ProxyAdmin.upgrade,
(payable(prox.SystemConfig), impl.SystemConfig)
)
});
// Upgrade the L1ERC721Bridge
calls[6] = IMulticall3.Call3({
target: _proxyAdmin,
allowFailure: false,
callData: abi.encodeCall(
ProxyAdmin.upgrade,
(payable(prox.L1ERC721Bridge), impl.L1ERC721Bridge)
)
});
// Set the default resource config
ResourceMetering.ResourceConfig memory rcfg = Constants.DEFAULT_RESOURCE_CONFIG();
calls[7] = IMulticall3.Call3({
target: prox.SystemConfig,
allowFailure: false,
callData: abi.encodeCall(SystemConfig.setResourceConfig, (rcfg))
});
return abi.encodeCall(IMulticall3.aggregate3, (calls));
}
/**
* @notice Returns the ContractSet that represents the implementations for a given network.
*/
function getImplementations() internal view returns (ContractSet memory) {
ContractSet memory set = implementations[block.chainid];
require(set.L1CrossDomainMessenger != address(0), "no implementations for this network");
return set;
}
/**
* @notice Returns the ContractSet that represents the proxies for a given network.
*/
function getProxies() internal view returns (ContractSet memory) {
ContractSet memory set = proxies[block.chainid];
require(set.L1CrossDomainMessenger != address(0), "no proxies for this network");
return set;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment