-
-
Save akolotov/9e70b43a75426c8276c061d9176b0968 to your computer and use it in GitHub Desktop.
VerifySafeTx_Gov44.t.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: CC0-1.0 | |
pragma solidity 0.8.15; | |
import "forge-std/Test.sol"; | |
import "forge-std/interfaces/IERC20.sol"; | |
interface IZkBobPool { | |
function direct_deposit_queue() external view returns (address); | |
function denominator() external view returns (uint256); | |
} | |
interface ISafe { | |
function getOwners() external view returns (address[] memory); | |
function getThreshold() external view returns (uint256); | |
function approveHash(bytes32 hashToApprove) external; | |
function execTransaction( | |
address to, | |
uint256 value, | |
bytes calldata data, | |
uint8 operation, | |
uint256 safeTxGas, | |
uint256 baseGas, | |
uint256 gasPrice, | |
address gasToken, | |
address payable refundReceiver, | |
bytes memory signatures | |
) | |
external | |
payable | |
returns (bool success); | |
} | |
contract SafeHelpers is Test { | |
struct SafeTxAPI { | |
bytes data; | |
uint256 nonce; | |
uint8 operation; | |
address safe; | |
uint256 safeTxGas; | |
bytes32 safeTxHash; | |
address to; | |
uint256 value; | |
} | |
function getSortedOwners(address safe) internal view returns (address[] memory owners) { | |
owners = ISafe(safe).getOwners(); | |
// bubble sort owners | |
for (uint256 i = 0; i < owners.length; i++) { | |
for (uint256 j = i + 1; j < owners.length; j++) { | |
if (owners[i] > owners[j]) { | |
address t = owners[i]; | |
owners[i] = owners[j]; | |
owners[j] = t; | |
} | |
} | |
} | |
} | |
function execSafeTx(string memory safeChain, string memory safeTxHash) internal { | |
SafeTxAPI memory safeTx = getSafeTx(safeChain, safeTxHash); | |
address[] memory owners = getSortedOwners(safeTx.safe); | |
uint256 threshold = ISafe(safeTx.safe).getThreshold(); | |
bytes memory signatures; | |
for (uint256 i = 0; i < threshold; i++) { | |
vm.prank(owners[i]); | |
ISafe(safeTx.safe).approveHash(safeTx.safeTxHash); | |
signatures = abi.encodePacked(signatures, uint96(0), owners[i], uint256(0), uint8(1)); | |
} | |
ISafe(safeTx.safe).execTransaction({ | |
to: safeTx.to, | |
value: safeTx.value, | |
data: safeTx.data, | |
operation: safeTx.operation, | |
safeTxGas: safeTx.safeTxGas, | |
baseGas: 0, | |
gasPrice: 0, | |
gasToken: address(0), | |
refundReceiver: payable(address(0)), | |
signatures: signatures | |
}); | |
} | |
function getSafeTx(string memory safeChain, string memory safeTxHash) internal returns (SafeTxAPI memory) { | |
bytes memory cmd = abi.encodePacked( | |
"curl -s https://safe-transaction-", | |
safeChain, | |
".safe.global/api/v1/multisig-transactions/", | |
safeTxHash, | |
"/ -s | jq -cM '{safe,to,value,data,operation,safeTxGas,nonce,safeTxHash} | .value |= tonumber'" | |
); | |
string[] memory inputs = new string[](3); | |
inputs[0] = "bash"; | |
inputs[1] = "-c"; | |
inputs[2] = string(cmd); | |
string memory rawJson = string(vm.ffi(inputs)); | |
return abi.decode(vm.parseJson(rawJson), (SafeTxAPI)); | |
} | |
} | |
contract VerifySafeTx_Gov44 is Test, SafeHelpers { | |
address constant pool = address(0x1CA8C2B9B20E18e86d5b9a72370fC6c91814c97C); | |
address constant usdc = address(0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85); | |
address constant bob = address(0xB0B195aEFA3650A6908f15CdaC7D92F8a5791B0B); | |
function forkOptimism() internal { | |
vm.createSelectFork("https://rpc.ankr.com/optimism", 110_612_000); | |
} | |
function testOptimism() public { | |
forkOptimism(); | |
address dd_queue = IZkBobPool(pool).direct_deposit_queue(); | |
uint256 bobToUSDCshift = 10 ** (IERC20(bob).decimals() - IERC20(usdc).decimals()); | |
assertEq(IERC20(bob).balanceOf(dd_queue), 0); | |
uint256 bob_balance = IERC20(bob).balanceOf(pool); | |
execSafeTx("optimism", "0xb5625c2a40311a5a3a727b1a1f81202465f7f3dbcb13ffb41f5398d924d7c6a9"); | |
execSafeTx("optimism", "0x7f9f061bc17ba96faa79ee40a44ab31f693470384c9b1c2985a874dc1370c997"); | |
assertEq(IZkBobPool(pool).denominator(), (1 << 255) | 1000); | |
uint256 usdc_balance = IERC20(usdc).balanceOf(pool); | |
assertEq(bob_balance / bobToUSDCshift, usdc_balance); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment