-
-
Save k1rill-fedoseev/4e3ad34b58df31ae34a6a306fc2f01ac to your computer and use it in GitHub Desktop.
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 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_Gov41 is Test, SafeHelpers { | |
address constant bob = address(0xB0B195aEFA3650A6908f15CdaC7D92F8a5791B0B); | |
address constant user1 = address(0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266); | |
function forkARB() internal { | |
vm.createSelectFork("https://rpc.ankr.com/arbitrum", 141_334_400); | |
} | |
function forkBNB() internal { | |
vm.createSelectFork("https://rpc.ankr.com/bsc", 32_677_150); | |
} | |
function forkMainnet() internal { | |
vm.createSelectFork("https://rpc.ankr.com/eth", 18_368_900); | |
} | |
function forkOP() internal { | |
vm.createSelectFork("https://rpc.ankr.com/optimism", 110_966_400); | |
} | |
function forkPolygon() internal { | |
vm.createSelectFork("https://rpc.ankr.com/polygon", 48_818_450); | |
} | |
function testARB() public { | |
forkARB(); | |
execSafeTx("arbitrum", "0xdbe514ab2d21d0428bb42e89755d1ed43d705ad9d67684ae4eb78282c634711e"); | |
assertApproxEqAbs(IERC20(bob).totalSupply(), 10_000 ether, 1 ether); | |
} | |
function testBNB() public { | |
forkBNB(); | |
execSafeTx("bsc", "0x0da4a9906438ed3c616c0dafa5722bf0dd34b876b110a0c4a03ab91f56fe3918"); | |
assertApproxEqAbs(IERC20(bob).totalSupply(), 10_000 ether, 100 ether); | |
} | |
function testMainnet() public { | |
forkMainnet(); | |
execSafeTx("mainnet", "0x6598e5c898ebafdd7430ac1490d16442060985e1fdf14ca49b131e3f8d55c602"); | |
assertApproxEqAbs(IERC20(bob).totalSupply(), 20_000 ether, 100 ether); | |
} | |
function testOP() public { | |
forkOP(); | |
execSafeTx("optimism", "0xde713e31812e929ec9d250b5c75d2055ba94f6b134fad4e94e8bcc2cac25dec3"); | |
assertApproxEqAbs(IERC20(bob).totalSupply(), 100_000 ether, 100 ether); | |
} | |
function testPolygon() public { | |
forkPolygon(); | |
execSafeTx("polygon", "0x6c1172b196533945c02db26d2c32da10192a4a602eb99107831efd7f495beb3f"); | |
assertApproxEqAbs(IERC20(bob).totalSupply(), 200_000 ether, 10_000 ether); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment