|
| 1 | +// SPDX-License-Identifier: GPL-3.0 |
| 2 | +pragma solidity >=0.8.0 <0.9.0; |
| 3 | + |
| 4 | +import "forge-std/Script.sol"; |
| 5 | + |
| 6 | +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; |
| 7 | + |
| 8 | +import {CoWTWAPFallbackHandler} from "../src/CoWTWAPFallbackHandler.sol"; |
| 9 | +import {GnosisSafe} from "safe/GnosisSafe.sol"; |
| 10 | +import {Enum} from "safe/common/Enum.sol"; |
| 11 | +import {FallbackManager} from "safe/base/FallbackManager.sol"; |
| 12 | +import {SignMessageLib} from "safe/libraries/SignMessageLib.sol"; |
| 13 | +import {MultiSend} from "safe/libraries/MultiSend.sol"; |
| 14 | + |
| 15 | +import {TWAPOrder} from "../src/libraries/TWAPOrder.sol"; |
| 16 | +import {ConditionalOrderLib} from "../src/libraries/ConditionalOrderLib.sol"; |
| 17 | + |
| 18 | +import {CoWTWAPFallbackHandler} from "../src/CoWTWAPFallbackHandler.sol"; |
| 19 | + |
| 20 | +address constant FALLBACK_HANDLER = 0x87b52eD635DF746cA29651581B4d87517AAa9a9F; // deployer cow twap fallback handler |
| 21 | + |
| 22 | +bytes32 constant DOMAIN_SEPARATOR = 0xc078f884a2676e1345748b1feace7b0abee5d00ecadb6e574dcdd109a63e8943; // settlement domain separator |
| 23 | + |
| 24 | +contract DaughterSafeTWAP is Script { |
| 25 | + |
| 26 | + address TARGET_SAFE = vm.envAddress("TARGET_SAFE"); |
| 27 | + uint256 TOTAL_SELL_AMOUNT = vm.envUint("TWAP_TOTAL_SELL_AMOUNT"); |
| 28 | + uint256 TOTAL_MIN_BUY_AMOUNT = vm.envUint("TWAP_TOTAL_MIN_BUY_AMOUNT"); |
| 29 | + uint256 TWAP_NUM_PARTS = vm.envUint("TWAP_NUM_PARTS"); |
| 30 | + |
| 31 | + IERC20 TWAP_SELL_TOKEN = IERC20(vm.envAddress("TWAP_SELL_TOKEN")); |
| 32 | + |
| 33 | + function run() external { |
| 34 | + address SENDING_SAFE = vm.envAddress("SENDING_SAFE"); |
| 35 | + |
| 36 | + TWAPOrder.Data memory twap = TWAPOrder.Data({ |
| 37 | + sellToken: TWAP_SELL_TOKEN, |
| 38 | + buyToken: IERC20(vm.envAddress("TWAP_BUY_TOKEN")), |
| 39 | + receiver: address(vm.envOr("TWAP_RECEIVER", address(0))), |
| 40 | + partSellAmount: TOTAL_SELL_AMOUNT / TWAP_NUM_PARTS, |
| 41 | + minPartLimit: TOTAL_MIN_BUY_AMOUNT / TWAP_NUM_PARTS, |
| 42 | + t0: vm.envOr("TWAP_START_TIME", block.timestamp), |
| 43 | + n: TWAP_NUM_PARTS, |
| 44 | + t: vm.envOr("TWAP_FREQUENCY", uint256(3600)), |
| 45 | + span: 0 |
| 46 | + }); |
| 47 | + |
| 48 | + bytes memory conditionalOrder = abi.encode(twap); |
| 49 | + // hash of the conditional order to sign |
| 50 | + bytes32 typedHash = ConditionalOrderLib.hash(conditionalOrder, DOMAIN_SEPARATOR); |
| 51 | + |
| 52 | + bytes memory fallbackHandlerTx = abi.encodeWithSelector( |
| 53 | + FallbackManager.setFallbackHandler.selector, |
| 54 | + FALLBACK_HANDLER |
| 55 | + ); |
| 56 | + |
| 57 | + bytes memory signMessageTx = abi.encodeWithSelector(SignMessageLib.signMessage.selector, abi.encode(typedHash)); |
| 58 | + |
| 59 | + bytes memory approveTx = abi.encodeWithSelector(TWAP_SELL_TOKEN.approve.selector, vm.envAddress("RELAYER"), TOTAL_SELL_AMOUNT); |
| 60 | + |
| 61 | + bytes memory dispatchTx = abi.encodeWithSelector(CoWTWAPFallbackHandler(address(TARGET_SAFE)).dispatch.selector, conditionalOrder); |
| 62 | + |
| 63 | + // calldata to send multisend |
| 64 | + bytes memory cd = abi.encodeWithSelector( |
| 65 | + MultiSend.multiSend.selector, |
| 66 | + abi.encodePacked( |
| 67 | + // 1. sign the conditional order |
| 68 | + abi.encodePacked( |
| 69 | + uint8(Enum.Operation.DelegateCall), |
| 70 | + address(vm.envAddress("SAFE_SIGN_MESSAGE_LIB")), |
| 71 | + uint256(0), // value 0 |
| 72 | + signMessageTx.length, |
| 73 | + signMessageTx |
| 74 | + ), |
| 75 | + // 2. approve the tokens to be spent by the settlement contract |
| 76 | + abi.encodePacked(Enum.Operation.Call, address(TWAP_SELL_TOKEN), uint256(0), approveTx.length, approveTx), |
| 77 | + // 3. dispatch the conditional order |
| 78 | + abi.encodePacked(Enum.Operation.Call, address(TARGET_SAFE), uint256(0), dispatchTx.length, dispatchTx) |
| 79 | + ) |
| 80 | + ); |
| 81 | + |
| 82 | + // declare a 65 byte array to store the signature |
| 83 | + bytes memory signature = new bytes(65); |
| 84 | + // set the first 32 bytes to the SENDING_SAFE address and set the last byte to 1 |
| 85 | + assembly { |
| 86 | + mstore(add(signature, 32), SENDING_SAFE) |
| 87 | + mstore8(add(signature, 96), 1) |
| 88 | + } |
| 89 | + |
| 90 | + bytes memory fallbackCd = abi.encodeWithSelector( |
| 91 | + GnosisSafe(payable(address(TARGET_SAFE))).execTransaction.selector, |
| 92 | + address(TARGET_SAFE), |
| 93 | + 0, |
| 94 | + fallbackHandlerTx, |
| 95 | + Enum.Operation.Call, |
| 96 | + 0, |
| 97 | + 0, |
| 98 | + 0, |
| 99 | + address(0), |
| 100 | + address(0), |
| 101 | + signature |
| 102 | + ); |
| 103 | + |
| 104 | + // get the calldata to send to the safe |
| 105 | + bytes memory safeCd = abi.encodeWithSelector( |
| 106 | + GnosisSafe(payable(address(TARGET_SAFE))).execTransaction.selector, |
| 107 | + address(vm.envAddress("SAFE_MULTI_SEND")), |
| 108 | + 0, |
| 109 | + cd, |
| 110 | + Enum.Operation.DelegateCall, |
| 111 | + 0, |
| 112 | + 0, |
| 113 | + 0, |
| 114 | + address(0), |
| 115 | + address(0), |
| 116 | + signature |
| 117 | + ); |
| 118 | + |
| 119 | + console.logString("setFallbackHandler calldata:"); |
| 120 | + console.logBytes(fallbackCd); |
| 121 | + console.logString("multiSend calldata:"); |
| 122 | + console.logBytes(safeCd); |
| 123 | + } |
| 124 | +} |
0 commit comments