Skip to content

Commit

Permalink
Merge pull request #7 from ind-igo/return-values
Browse files Browse the repository at this point in the history
feat: Simulate when txn is added for return values
  • Loading branch information
ind-igo authored Aug 20, 2024
2 parents 4ce820b + 9da57ba commit a0170c3
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 127 deletions.
5 changes: 2 additions & 3 deletions script/TestAuthBatch.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@ interface IRolesAdmin {
/// @notice A test for Gnosis Safe batching script
/// @dev GOERLI
contract TestAuthBatch is BatchScript {
address safe = 0x84C0C005cF574D0e5C602EA7b366aE9c707381E0;
address deployer = 0x1A5309F208f161a393E8b5A253de8Ab894A67188;

/// @notice The main script entrypoint
function run(bool send_) external {
function run(bool send_) external isBatch(0x84C0C005cF574D0e5C602EA7b366aE9c707381E0) {
IRolesAdmin rolesAdmin = IRolesAdmin(0x54FfCA586cD1B01E96a5682DF93a55d7Ef91EFF0);

// Start batch
Expand All @@ -33,6 +32,6 @@ contract TestAuthBatch is BatchScript {
));

// Execute batch
executeBatch(safe, send_);
executeBatch(send_);
}
}
8 changes: 2 additions & 6 deletions script/TestBatch.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,10 @@ interface IKernel {
contract TestBatch is BatchScript {
address localBridgeAddr = 0xefffab0Aa61828c4af926E039ee754e3edE10dAc; // Goerli bridge
address remoteBridgeAddr = 0xB01432c01A9128e3d1d70583eA873477B2a1f5e1; // Arb goerli bridge
address safe = 0x84C0C005cF574D0e5C602EA7b366aE9c707381E0;
uint16 lzChainId = 10143;

/// @notice The main script entrypoint
function run(bool send_) external {
// vm.startBroadcast();
function run(bool send_) external isBatch(0x84C0C005cF574D0e5C602EA7b366aE9c707381E0) {

IKernel kernel = IKernel(0xDb7cf68154bd422dF5196D90285ceA057786b4c3);
ICrossChainBridge bridge = ICrossChainBridge(localBridgeAddr);
Expand All @@ -50,8 +48,6 @@ contract TestBatch is BatchScript {
addToBatch(address(bridge), txn2);

// Execute batch
executeBatch(safe, send_);

// vm.stopBroadcast();
executeBatch(send_);
}
}
149 changes: 77 additions & 72 deletions src/BatchScript.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ pragma solidity >=0.6.2 <0.9.0;
import {Script, console2, StdChains, stdJson, stdMath, StdStorage, stdStorageSafe, VmSafe} from "forge-std/Script.sol";

import {Surl} from "../lib/surl/src/Surl.sol";
import {DelegatePrank} from "./lib/DelegatePrank.sol";

// ⭐️ SCRIPT
abstract contract BatchScript is Script, DelegatePrank {
abstract contract BatchScript is Script {
using stdJson for string;
using Surl for *;

Expand Down Expand Up @@ -67,6 +66,9 @@ abstract contract BatchScript is Script, DelegatePrank {
bytes32 private constant LOCAL = keccak256("local");
bytes32 private constant LEDGER = keccak256("ledger");

// Address to send transaction from
address private safe;

enum Operation {
CALL,
DELEGATECALL
Expand All @@ -89,53 +91,9 @@ abstract contract BatchScript is Script, DelegatePrank {

bytes[] public encodedTxns;

// Public functions

// Adds an encoded transaction to the batch.
// Encodes the transaction as packed bytes of:
// - `operation` as a `uint8` with `0` for a `call` or `1` for a `delegatecall` (=> 1 byte),
// - `to` as an `address` (=> 20 bytes),
// - `value` as in msg.value, sent as a `uint256` (=> 32 bytes),
// - length of `data` as a `uint256` (=> 32 bytes),
// - `data` as `bytes`.
function addToBatch(
address to_,
uint256 value_,
bytes memory data_
) public {
encodedTxns.push(
abi.encodePacked(Operation.CALL, to_, value_, data_.length, data_)
);
}
// Modifiers

// Convenience funtion to add an encoded transaction to the batch, but passes
// 0 as the `value` (equivalent to msg.value) field.
function addToBatch(address to_, bytes memory data_) public {
encodedTxns.push(
abi.encodePacked(
Operation.CALL,
to_,
uint256(0),
data_.length,
data_
)
);
}

// Simulate then send the batch to the Safe API. If `send_` is `false`, the
// batch will only be simulated.
function executeBatch(address safe_, bool send_) public {
_initialize();
Batch memory batch = _createBatch(safe_);
_simulateBatch(safe_, batch);
if (send_) {
batch = _signBatch(safe_, batch);
_sendBatch(safe_, batch);
}
}

// Internal functions
function _initialize() private {
modifier isBatch(address safe_) {
// Set the chain ID
Chain memory chain = getChain(vm.envString("CHAIN"));
chainId = chain.chainId;
Expand All @@ -157,6 +115,9 @@ abstract contract BatchScript is Script, DelegatePrank {
revert("Unsupported chain");
}

// Store the provided safe address
safe = safe_;

// Load wallet information
walletType = keccak256(abi.encodePacked(vm.envString("WALLET_TYPE")));
if (walletType == LOCAL) {
Expand All @@ -166,10 +127,69 @@ abstract contract BatchScript is Script, DelegatePrank {
} else {
revert("Unsupported wallet type");
}

// Run batch
_;
}

// Functions to consume in a script

// Adds an encoded transaction to the batch.
// Encodes the transaction as packed bytes of:
// - `operation` as a `uint8` with `0` for a `call` or `1` for a `delegatecall` (=> 1 byte),
// - `to` as an `address` (=> 20 bytes),
// - `value` as in msg.value, sent as a `uint256` (=> 32 bytes),
// - length of `data` as a `uint256` (=> 32 bytes),
// - `data` as `bytes`.
function addToBatch(
address to_,
uint256 value_,
bytes memory data_
) internal returns (bytes memory) {
// Add transaction to batch array
encodedTxns.push(abi.encodePacked(Operation.CALL, to_, value_, data_.length, data_));

// Simulate transaction and get return value
vm.prank(safe);
(bool success, bytes memory data) = to_.call{value: value_}(data_);
if (success) {
return data;
} else {
revert(string(data));
}
}

// Convenience funtion to add an encoded transaction to the batch, but passes
// 0 as the `value` (equivalent to msg.value) field.
function addToBatch(address to_, bytes memory data_) internal returns (bytes memory) {
// Add transaction to batch array
encodedTxns.push(abi.encodePacked(Operation.CALL, to_, uint256(0), data_.length, data_));

// Simulate transaction and get return value
vm.prank(safe);
(bool success, bytes memory data) = to_.call(data_);
if (success) {
return data;
} else {
revert(string(data));
}
}

// Simulate then send the batch to the Safe API. If `send_` is `false`, the
// batch will only be simulated.
function executeBatch(bool send_) internal {
Batch memory batch = _createBatch(safe);
// _simulateBatch(safe, batch);
if (send_) {
batch = _signBatch(safe, batch);
_sendBatch(safe, batch);
}
}

// Private functions

// Encodes the stored encoded transactions into a single Multisend transaction
function _createBatch(address safe_) internal returns (Batch memory batch) {
function _createBatch(address safe_) private returns (Batch memory batch) {
// Set initial batch fields
batch.to = SAFE_MULTISEND_ADDRESS;
batch.value = 0;
Expand All @@ -195,7 +215,7 @@ abstract contract BatchScript is Script, DelegatePrank {
function _signBatch(
address safe_,
Batch memory batch_
) internal returns (Batch memory) {
) private returns (Batch memory) {
// Get the typed data to sign
string memory typedData = _getTypedData(safe_, batch_);

Expand Down Expand Up @@ -239,22 +259,7 @@ abstract contract BatchScript is Script, DelegatePrank {
return batch_;
}

function _simulateBatch(address safe_, Batch memory batch_) internal {
require(batch_.to.code.length > 0, "No code at address");
vm.allowCheatcodes(safe_);
(bool success, bytes memory data) = delegatePrank(
safe_,
batch_.to,
batch_.data
);
if (success) {
console2.log("Batch simulated successfully");
} else {
revert(string(data));
}
}

function _sendBatch(address safe_, Batch memory batch_) internal {
function _sendBatch(address safe_, Batch memory batch_) private {
string memory endpoint = _getSafeAPIEndpoint(safe_);

// Create json payload for API call to Gnosis transaction service
Expand Down Expand Up @@ -294,7 +299,7 @@ abstract contract BatchScript is Script, DelegatePrank {
function _getTransactionHash(
address safe_,
Batch memory batch_
) internal view returns (bytes32) {
) private view returns (bytes32) {
return
keccak256(
abi.encodePacked(
Expand Down Expand Up @@ -324,7 +329,7 @@ abstract contract BatchScript is Script, DelegatePrank {
function _getTypedData(
address safe_,
Batch memory batch_
) internal returns (string memory) {
) private returns (string memory) {
// Create EIP712 structured data for the batch transaction to sign externally via cast

// EIP712Domain Field Types
Expand Down Expand Up @@ -406,7 +411,7 @@ abstract contract BatchScript is Script, DelegatePrank {

function _stripSlashQuotes(
string memory str_
) internal returns (string memory) {
) private returns (string memory) {
// Remove slash quotes from string
string memory command = string.concat(
"sed 's/",
Expand All @@ -431,7 +436,7 @@ abstract contract BatchScript is Script, DelegatePrank {
return string(res);
}

function _getNonce(address safe_) internal returns (uint256) {
function _getNonce(address safe_) private returns (uint256) {
string memory endpoint = string.concat(
SAFE_API_BASE_URL,
vm.toString(safe_),
Expand All @@ -448,7 +453,7 @@ abstract contract BatchScript is Script, DelegatePrank {

function _getSafeAPIEndpoint(
address safe_
) internal view returns (string memory) {
) private view returns (string memory) {
return
string.concat(
SAFE_API_BASE_URL,
Expand All @@ -457,7 +462,7 @@ abstract contract BatchScript is Script, DelegatePrank {
);
}

function _getHeaders() internal pure returns (string[] memory) {
function _getHeaders() private pure returns (string[] memory) {
string[] memory headers = new string[](1);
headers[0] = "Content-Type: application/json";
return headers;
Expand Down
46 changes: 0 additions & 46 deletions src/lib/DelegatePrank.sol

This file was deleted.

0 comments on commit a0170c3

Please sign in to comment.