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

refactor and clean up encoding #7

Merged
merged 1 commit into from
Dec 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions src/interfaces/IMultiSend.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,17 @@
pragma solidity ^0.8.12;

interface IMultiSend {
/// @dev Sends multiple transactions and reverts all if one fails.
/// @param transactions Encoded transactions. Each transaction is encoded as a packed bytes of
/// operation has to be uint8(0) in this version (=> 1 byte),
/// to as a address (=> 20 bytes),
/// value as a uint256 (=> 32 bytes),
/// data length as a uint256 (=> 32 bytes),
/// data as bytes.
/// see abi.encodePacked for more information on packed encoding
/// @notice The code is for most part the same as the normal MultiSend (to keep compatibility),
/// but reverts if a transaction tries to use a delegatecall.
/// @notice This method is payable as delegatecalls keep the msg.value from the previous call
/// If the calling method (e.g. execTransaction) received ETH this would revert otherwise
function multiSend(bytes memory transactions) external payable;
}
18 changes: 18 additions & 0 deletions src/interfaces/IProxyAdmin.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

import "./ITransparentUpgradeableProxy.sol";

interface IProxyAdmin {
function getProxyImplementation(ITransparentUpgradeableProxy proxy) external view returns (address);

function getProxyAdmin(ITransparentUpgradeableProxy proxy) external view returns (address);

function changeProxyAdmin(ITransparentUpgradeableProxy proxy, address newAdmin) external;

function upgrade(ITransparentUpgradeableProxy proxy, address implementation) external;

function upgradeAndCall(ITransparentUpgradeableProxy proxy, address implementation, bytes memory data)
external
payable;
}
14 changes: 14 additions & 0 deletions src/interfaces/ITransparentUpgradeableProxy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

interface ITransparentUpgradeableProxy {
function admin() external view returns (address);

function implementation() external view returns (address);

function changeAdmin(address) external;

function upgradeTo(address) external;

function upgradeToAndCall(address, bytes memory) external payable;
}
8 changes: 8 additions & 0 deletions src/interfaces/IUpgradeableBeacon.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

interface IUpgradeableBeacon {
function implementation() external view returns (address);

function upgradeTo(address newImplementation) external;
}
6 changes: 0 additions & 6 deletions src/templates/MultisigBuilder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,12 @@
pragma solidity ^0.8.12;

import {ZeusScript} from "../utils/ZeusScript.sol";
import {MultisigCall, MultisigCallUtils} from "../utils/MultisigCallUtils.sol";
import {SafeTx, EncGnosisSafe} from "../utils/SafeTxUtils.sol";

/**
* @title MultisigBuilder
* @dev Abstract contract for building arbitrary multisig scripts.
*/
abstract contract MultisigBuilder is ZeusScript {
using MultisigCallUtils for MultisigCall[];

string internal constant multiSendCallOnlyName = "MultiSendCallOnly";

/**
* @notice Constructs a SafeTx object for a Gnosis Safe to ingest. Emits via `ZeusMultisigExecute`
*/
Expand Down
49 changes: 0 additions & 49 deletions src/utils/EncGnosisSafe.sol

This file was deleted.

156 changes: 156 additions & 0 deletions src/utils/Encode.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.12;

// import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";
// import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";

import "../interfaces/IMultiSend.sol";
import "../interfaces/ISafe.sol";
import "../interfaces/IProxyAdmin.sol";
import "../interfaces/IUpgradeableBeacon.sol";

struct MultisigCall {
address to;
uint256 value;
bytes data;
}

library Encode {
/// Used for state vars:
///
/// uint idx;
/// mapping(uint => MultisigCall[]) map;
bytes32 internal constant IDX_SLOT = keccak256("IDX_SLOT");
bytes32 internal constant MAP_SLOT = keccak256("MAP_SLOT");

/// Used to describe calls from a Gnosis Safe
enum Operation {
Call,
DelegateCall
}

/// Constants for Safe.execTransaction inputs we don't usee
uint256 constant SAFE_TX_GAS = 0;
uint256 constant BASE_GAS = 0;
uint256 constant GAS_PRICE = 0;
address constant GAS_TOKEN = address(uint160(0));
address payable constant REFUND_RECEIVER = payable(address(uint160(0)));

/// Dummy types and variables to facilitate syntax, e.g: `Encode.proxyAdmin.upgrade(...)`
enum EncProxyAdmin {
A
}
enum EncUpgradeableBeacon {
A
}
enum EncGnosisSafe {
A
}

EncProxyAdmin internal constant proxyAdmin = EncProxyAdmin.A;
EncUpgradeableBeacon internal constant upgradeableBeacon = EncUpgradeableBeacon.A;
EncGnosisSafe internal constant gnosisSafe = EncGnosisSafe.A;

/// @dev Creates a new, clean `MultisigCall[] storage` pointer, guaranteeing
/// any previous pointers will not be overwritten.
/// Since we're in a library, we have to use assembly+slot pointers, but the
/// high level version of this function equates to:
///
/// uint _idx = storage.idx;
/// storage.idx++;
/// MultisigCall[] storage calls = storage.map[_idx];
/// return calls;
function newMultisigCalls() internal returns (MultisigCall[] storage) {
bytes32 _IDX_SLOT = IDX_SLOT;
bytes32 _MAP_SLOT = MAP_SLOT;

uint256 idx;
assembly {
idx := sload(_IDX_SLOT)
sstore(_IDX_SLOT, add(1, idx))
}

// fn pointer indirection fools the compiler into letting us have
// an uninitialized storage pointer
function() pure returns (mapping(uint => MultisigCall[]) storage) fn;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you likey?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sol devs would rather do this than just contribute to the compiler LMAO

function() pure returns (uint) fn2 = func;
assembly {
fn := fn2
}
mapping(uint256 => MultisigCall[]) storage map = fn();
assembly {
map.slot := _MAP_SLOT
}

return map[idx];
}

function func() internal pure returns (uint256) {
return 0;
}

/// @dev Appends a call to a list of `MultisigCalls`, returning the original storage pointer
/// to facilitate call chaining syntax, e.g:
///
/// calls
/// .append(...)
/// .append(...)
/// .append(...);
function append(MultisigCall[] storage calls, address to, bytes memory data)
internal
returns (MultisigCall[] storage)
{
calls.push(MultisigCall({to: to, value: 0, data: data}));

return calls;
}

/// @dev Encodes a call to `ProxyAdmin.upgrade(proxy, impl)`
function upgrade(EncProxyAdmin, address proxy, address impl) internal pure returns (bytes memory) {
return abi.encodeCall(IProxyAdmin.upgrade, (ITransparentUpgradeableProxy(proxy), impl));
}
Copy link
Contributor

@jbrower95 jbrower95 Dec 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO nit but it feels like it would be more straightforward to leave abi.encodeCall(IProxyAdmin, to, params)

but make zDeployedProxy cast to ITransparentUpgradeableProxy by default...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

im not super sure what you're asking for, but this is less about how ZeusScript uses it and more about how this works when writing upgrade scripts. for upgrade scripts this is much nicer.

i can share examples if you want


/// @dev Encodes a call to `UpgradeableBeacon.upgradeTo(newImpl)`
function upgradeTo(EncUpgradeableBeacon, address newImpl) internal pure returns (bytes memory) {
return abi.encodeCall(IUpgradeableBeacon.upgradeTo, (newImpl));
}

/// @dev Encodes a call to `MultiSend.multiSend(data)`
function multiSend(MultisigCall[] memory calls) internal pure returns (bytes memory) {
bytes memory packedCalls = new bytes(0);

for (uint256 i = 0; i < calls.length; i++) {
packedCalls = abi.encodePacked(
packedCalls,
abi.encodePacked(uint8(0), calls[i].to, calls[i].value, uint256(calls[i].data.length), calls[i].data)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

);
}

return abi.encodeCall(IMultiSend.multiSend, packedCalls);
}

/// @dev Encodes a call to `Safe.execTransaction`
function execTransaction(EncGnosisSafe, address from, address to, Operation op, bytes memory data)
internal
pure
returns (bytes memory)
{
return _encExecTranasction({from: from, to: to, op: op, value: 0, data: data});
}

function _encExecTranasction(address from, address to, Operation op, uint256 value, bytes memory data)
private
pure
returns (bytes memory)
{
bytes1 v = bytes1(uint8(1));
bytes32 r = bytes32(uint256(uint160(from)));
bytes32 s;
bytes memory sig = abi.encodePacked(r, s, v);

return abi.encodeCall(
ISafe.execTransaction,
(to, value, data, uint8(op), SAFE_TX_GAS, BASE_GAS, GAS_PRICE, GAS_TOKEN, REFUND_RECEIVER, sig)
);
}
}
43 changes: 0 additions & 43 deletions src/utils/MultisigCallUtils.sol

This file was deleted.

39 changes: 0 additions & 39 deletions src/utils/SafeTxUtils.sol

This file was deleted.

1 change: 0 additions & 1 deletion src/utils/ZEnvHelpers.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ struct State {
mapping(string => uint16) updatedUInt16s;
mapping(string => uint8) updatedUInt8s;
mapping(string => bool) updatedBools;

////////////////////////////////////
mapping(string => Cleanliness) _dirty;
string[] _modifiedKeys;
Expand Down
4 changes: 2 additions & 2 deletions src/utils/ZeusScript.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
pragma solidity ^0.8.12;

import "./ZEnvHelpers.sol";
import "./Encode.sol";
import {StringUtils} from "./StringUtils.sol";
import {Script} from "forge-std/Script.sol";
import {EncGnosisSafe} from "./EncGnosisSafe.sol";
import {Test} from "forge-std/Test.sol";
import {console} from "forge-std/console.sol";

Expand All @@ -25,7 +25,7 @@ abstract contract ZeusScript is Script, Test {
event ZeusRequireMultisig(address addr, Operation callType);
event ZeusEnvironmentUpdate(string key, EnvironmentVariableType internalType, bytes value);
event ZeusDeploy(string name, address addr, bool singleton);
event ZeusMultisigExecute(address to, uint256 value, bytes data, EncGnosisSafe.Operation op);
event ZeusMultisigExecute(address to, uint256 value, bytes data, Encode.Operation op);

/**
* Environment manipulation - update variables in the current environment's configuration *****
Expand Down
Loading
Loading