From fe01686160e4016177debd48349d29570b7ccb27 Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Mon, 16 Dec 2024 11:06:33 -0500 Subject: [PATCH 01/23] Add `rebateClaimer()(address)` function on Mainnet Settlers for gas rebate program --- src/chains/Mainnet/Common.sol | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/chains/Mainnet/Common.sol b/src/chains/Mainnet/Common.sol index 147cae55..9a71576b 100644 --- a/src/chains/Mainnet/Common.sol +++ b/src/chains/Mainnet/Common.sol @@ -36,6 +36,8 @@ import { ISolidlyV3Callback } from "../../core/univ3forks/SolidlyV3.sol"; +import {IOwnable} from "../../deployer/TwoStepOwnable.sol"; + // Solidity inheritance is stupid import {SettlerAbstract} from "../../SettlerAbstract.sol"; @@ -51,6 +53,7 @@ abstract contract MainnetMixin is { constructor() { assert(block.chainid == 1 || block.chainid == 31337); + rebateClaimer = IOwnable(0x00000000000004533Fe15556B1E086BB1A72cEae).owner(); } function _dispatch(uint256 i, uint256 action, bytes calldata data) @@ -137,4 +140,6 @@ abstract contract MainnetMixin is function _curveFactory() internal pure override returns (address) { return 0x0c0e5f2fF0ff18a3be9b835635039256dC4B4963; } + + address public immutable rebateClaimer; } From e2b35f450f14d40474145ca27c329210ff3115a6 Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Mon, 16 Dec 2024 11:14:56 -0500 Subject: [PATCH 02/23] Factor-out constant --- src/SettlerBase.sol | 4 +++- src/chains/Blast/Common.sol | 3 ++- src/chains/Mainnet/Common.sol | 3 ++- src/chains/Mode/Common.sol | 3 ++- src/deployer/BlastDeployer.sol | 3 ++- src/deployer/Deployer.sol | 3 ++- src/deployer/DeployerAddress.sol | 4 ++++ src/deployer/ModeDeployer.sol | 5 +++-- src/deployer/SafeModule.sol | 3 ++- test/deployer/Deployer.t.sol | 3 ++- 10 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 src/deployer/DeployerAddress.sol diff --git a/src/SettlerBase.sol b/src/SettlerBase.sol index 2bab07ae..8b8cac9e 100644 --- a/src/SettlerBase.sol +++ b/src/SettlerBase.sol @@ -5,6 +5,8 @@ import {IERC20} from "@forge-std/interfaces/IERC20.sol"; import {IERC721Owner} from "./IERC721Owner.sol"; import {ISignatureTransfer} from "@permit2/interfaces/ISignatureTransfer.sol"; +import {DEPLOYER} from "./deployer/DeployerAddress.sol"; + import {Basic} from "./core/Basic.sol"; import {RfqOrderSettlement} from "./core/RfqOrderSettlement.sol"; import {UniswapV3Fork} from "./core/UniswapV3Fork.sol"; @@ -60,7 +62,7 @@ abstract contract SettlerBase is Basic, RfqOrderSettlement, UniswapV3Fork, Unisw constructor(bytes20 gitCommit, uint256 tokenId) { if (block.chainid != 31337) { emit GitCommit(gitCommit); - assert(IERC721Owner(0x00000000000004533Fe15556B1E086BB1A72cEae).ownerOf(tokenId) == address(this)); + assert(IERC721Owner(DEPLOYER).ownerOf(tokenId) == address(this)); } else { assert(gitCommit == bytes20(0)); } diff --git a/src/chains/Blast/Common.sol b/src/chains/Blast/Common.sol index 26a9449b..f371ed9e 100644 --- a/src/chains/Blast/Common.sol +++ b/src/chains/Blast/Common.sol @@ -36,6 +36,7 @@ import { rogueXV1Factory, rogueXV1InitHash, rogueXV1ForkId, IRoxSpotSwapCallback } from "../../core/univ3forks/RogueXV1.sol"; +import {DEPLOYER} from "../../deployer/DeployerAddress.sol"; import {IOwnable} from "../../deployer/TwoStepOwnable.sol"; import {BLAST, BLAST_USDB, BLAST_WETH, BlastYieldMode, BlastGasMode} from "./IBlast.sol"; @@ -49,7 +50,7 @@ abstract contract BlastMixin is FreeMemory, SettlerBase { BLAST.configure( BlastYieldMode.AUTOMATIC, BlastGasMode.CLAIMABLE, - IOwnable(0x00000000000004533Fe15556B1E086BB1A72cEae).owner() + IOwnable(DEPLOYER).owner() ); BLAST_USDB.configure(BlastYieldMode.VOID); BLAST_WETH.configure(BlastYieldMode.VOID); diff --git a/src/chains/Mainnet/Common.sol b/src/chains/Mainnet/Common.sol index 9a71576b..d7163842 100644 --- a/src/chains/Mainnet/Common.sol +++ b/src/chains/Mainnet/Common.sol @@ -36,6 +36,7 @@ import { ISolidlyV3Callback } from "../../core/univ3forks/SolidlyV3.sol"; +import {DEPLOYER} from "../../deployer/DeployerAddress.sol"; import {IOwnable} from "../../deployer/TwoStepOwnable.sol"; // Solidity inheritance is stupid @@ -53,7 +54,7 @@ abstract contract MainnetMixin is { constructor() { assert(block.chainid == 1 || block.chainid == 31337); - rebateClaimer = IOwnable(0x00000000000004533Fe15556B1E086BB1A72cEae).owner(); + rebateClaimer = IOwnable(DEPLOYER).owner(); } function _dispatch(uint256 i, uint256 action, bytes calldata data) diff --git a/src/chains/Mode/Common.sol b/src/chains/Mode/Common.sol index 2ee850fb..b13c1efa 100644 --- a/src/chains/Mode/Common.sol +++ b/src/chains/Mode/Common.sol @@ -19,6 +19,7 @@ import {IAlgebraCallback} from "../../core/univ3forks/Algebra.sol"; import {swapModeV3Factory, swapModeV3InitHash, swapModeV3ForkId} from "../../core/univ3forks/SwapModeV3.sol"; import {IUniswapV3Callback} from "../../core/univ3forks/UniswapV3.sol"; +import {DEPLOYER} from "../../deployer/DeployerAddress.sol"; import {MODE_SFS} from "./IModeSFS.sol"; // Solidity inheritance is stupid @@ -27,7 +28,7 @@ import {Permit2PaymentAbstract} from "../../core/Permit2PaymentAbstract.sol"; abstract contract ModeMixin is FreeMemory, SettlerBase { constructor() { assert(block.chainid == 34443 || block.chainid == 31337); - MODE_SFS.assign(MODE_SFS.getTokenId(0x00000000000004533Fe15556B1E086BB1A72cEae)); + MODE_SFS.assign(MODE_SFS.getTokenId(DEPLOYER)); } function _isRestrictedTarget(address target) diff --git a/src/deployer/BlastDeployer.sol b/src/deployer/BlastDeployer.sol index 4e31a071..d3d4d736 100644 --- a/src/deployer/BlastDeployer.sol +++ b/src/deployer/BlastDeployer.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity =0.8.25; +import {DEPLOYER} from "./DeployerAddress.sol"; import {Deployer} from "./Deployer.sol"; import {BLAST, BlastYieldMode, BlastGasMode} from "../chains/Blast/IBlast.sol"; @@ -11,7 +12,7 @@ contract BlastDeployer is Deployer { BLAST.configure( BlastYieldMode.AUTOMATIC, BlastGasMode.CLAIMABLE, - BlastDeployer(0x00000000000004533Fe15556B1E086BB1A72cEae).owner() + BlastDeployer(DEPLOYER).owner() ); } diff --git a/src/deployer/Deployer.sol b/src/deployer/Deployer.sol index 2965636c..73037f06 100644 --- a/src/deployer/Deployer.sol +++ b/src/deployer/Deployer.sol @@ -15,6 +15,7 @@ import {ProxyMultiCall} from "../utils/ProxyMultiCall.sol"; import {Feature, wrap, isNull} from "./Feature.sol"; import {Nonce, zero, isNull} from "./Nonce.sol"; import {IDeployer, IERC721ViewMetadata} from "./IDeployer.sol"; +import {DEPLOYER} from "./DeployerAddress.sol"; library NonceList { struct ListElem { @@ -149,7 +150,7 @@ contract Deployer is IDeployer, ERC1967UUPSUpgradeable, Context, ERC1967TwoStepO } function initialize(address initialOwner) public virtual { - require(address(this) == 0x00000000000004533Fe15556B1E086BB1A72cEae || block.chainid == 31337); + require(address(this) == DEPLOYER || block.chainid == 31337); if (_implVersion == 1) { _setPendingOwner(initialOwner); } else { diff --git a/src/deployer/DeployerAddress.sol b/src/deployer/DeployerAddress.sol new file mode 100644 index 00000000..27082984 --- /dev/null +++ b/src/deployer/DeployerAddress.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.8.25; + +address constant DEPLOYER = 0x00000000000004533Fe15556B1E086BB1A72cEae; diff --git a/src/deployer/ModeDeployer.sol b/src/deployer/ModeDeployer.sol index d2760022..1b1893e3 100644 --- a/src/deployer/ModeDeployer.sol +++ b/src/deployer/ModeDeployer.sol @@ -2,6 +2,7 @@ pragma solidity =0.8.25; import {Deployer} from "./Deployer.sol"; +import {DEPLOYER} from "./DeployerAddress.sol"; import {MODE_SFS} from "../chains/Mode/IModeSFS.sol"; /// @custom:security-contact security@0x.org @@ -10,11 +11,11 @@ contract ModeDeployer is Deployer { constructor(uint256 version) Deployer(version) { assert(block.chainid == 34443); - if (0x00000000000004533Fe15556B1E086BB1A72cEae.code.length == 0) { + if (DEPLOYER.code.length == 0) { assert(_implVersion == 1); sfsTokenId = MODE_SFS.register(0xf36b9f50E59870A24F42F9Ba43b2aD0A4b8f2F51); } else { - MODE_SFS.assign(sfsTokenId = MODE_SFS.getTokenId(0x00000000000004533Fe15556B1E086BB1A72cEae)); + MODE_SFS.assign(sfsTokenId = MODE_SFS.getTokenId(DEPLOYER)); } } diff --git a/src/deployer/SafeModule.sol b/src/deployer/SafeModule.sol index eed32ffa..a5172fae 100644 --- a/src/deployer/SafeModule.sol +++ b/src/deployer/SafeModule.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.25; +import {DEPLOYER} from "./DeployerAddress.sol"; import {IDeployer, IDeployerRemove} from "./IDeployer.sol"; import {Feature} from "./Feature.sol"; import {Nonce} from "./Nonce.sol"; @@ -23,7 +24,7 @@ contract ZeroExSettlerDeployerSafeModule is IDeployerRemove { using Revert for bool; ISafeMinimal public immutable safe; - IDeployer public constant deployer = IDeployer(0x00000000000004533Fe15556B1E086BB1A72cEae); + IDeployer public constant deployer = IDeployer(DEPLOYER); constructor(address _safe) { assert(address(this) == 0x1CeC01DC0fFEE5eB5aF47DbEc1809F2A7c601C30 || block.chainid == 31337); diff --git a/test/deployer/Deployer.t.sol b/test/deployer/Deployer.t.sol index c36772bf..fbb22fe6 100644 --- a/test/deployer/Deployer.t.sol +++ b/test/deployer/Deployer.t.sol @@ -7,6 +7,7 @@ import {ERC1967UUPSProxy} from "src/proxy/ERC1967UUPSProxy.sol"; import {AddressDerivation} from "src/utils/AddressDerivation.sol"; import {Create3} from "src/utils/Create3.sol"; import {IERC1967Proxy} from "src/proxy/ERC1967UUPSUpgradeable.sol"; +import {DEPLOYER} from "src/deployer/DeployerAddress.sol"; import "@forge-std/Test.sol"; @@ -19,7 +20,7 @@ contract DeployerTest is Test { function setUp() public { vm.createSelectFork(vm.envString("MAINNET_RPC_URL"), 19921675); - deployer = Deployer(0x00000000000004533Fe15556B1E086BB1A72cEae); + deployer = Deployer(DEPLOYER); vm.label(address(deployer), "Deployer (proxy)"); vm.prank(deployer.owner()); From abd18dd44199648da103e219380cd2c3c5a586a0 Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Mon, 16 Dec 2024 11:15:23 -0500 Subject: [PATCH 03/23] `forge fmt` --- src/chains/Blast/Common.sol | 6 +----- src/deployer/BlastDeployer.sol | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/chains/Blast/Common.sol b/src/chains/Blast/Common.sol index f371ed9e..eb92235a 100644 --- a/src/chains/Blast/Common.sol +++ b/src/chains/Blast/Common.sol @@ -47,11 +47,7 @@ abstract contract BlastMixin is FreeMemory, SettlerBase { constructor() { if (block.chainid != 31337) { assert(block.chainid == 81457); - BLAST.configure( - BlastYieldMode.AUTOMATIC, - BlastGasMode.CLAIMABLE, - IOwnable(DEPLOYER).owner() - ); + BLAST.configure(BlastYieldMode.AUTOMATIC, BlastGasMode.CLAIMABLE, IOwnable(DEPLOYER).owner()); BLAST_USDB.configure(BlastYieldMode.VOID); BLAST_WETH.configure(BlastYieldMode.VOID); } diff --git a/src/deployer/BlastDeployer.sol b/src/deployer/BlastDeployer.sol index d3d4d736..feb80e99 100644 --- a/src/deployer/BlastDeployer.sol +++ b/src/deployer/BlastDeployer.sol @@ -9,11 +9,7 @@ import {BLAST, BlastYieldMode, BlastGasMode} from "../chains/Blast/IBlast.sol"; contract BlastDeployer is Deployer { constructor(uint256 version) Deployer(version) { assert(block.chainid == 81457); - BLAST.configure( - BlastYieldMode.AUTOMATIC, - BlastGasMode.CLAIMABLE, - BlastDeployer(DEPLOYER).owner() - ); + BLAST.configure(BlastYieldMode.AUTOMATIC, BlastGasMode.CLAIMABLE, BlastDeployer(DEPLOYER).owner()); } function initialize(address initialOwner) public override { From 5a65bf57036250b36322f9993a50c58501e06dc7 Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Mon, 16 Dec 2024 11:18:46 -0500 Subject: [PATCH 04/23] Make tests pass by making `rebateClaimer()` dynamically retrieve the deployer owner --- src/chains/Mainnet/Common.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/chains/Mainnet/Common.sol b/src/chains/Mainnet/Common.sol index d7163842..9ec8f176 100644 --- a/src/chains/Mainnet/Common.sol +++ b/src/chains/Mainnet/Common.sol @@ -54,7 +54,6 @@ abstract contract MainnetMixin is { constructor() { assert(block.chainid == 1 || block.chainid == 31337); - rebateClaimer = IOwnable(DEPLOYER).owner(); } function _dispatch(uint256 i, uint256 action, bytes calldata data) @@ -142,5 +141,7 @@ abstract contract MainnetMixin is return 0x0c0e5f2fF0ff18a3be9b835635039256dC4B4963; } - address public immutable rebateClaimer; + function rebateClaimer() external view returns (address) { + return IOwnable(DEPLOYER).owner(); + } } From 81ee13d713f9b1dd49c0c0a6136c950ee4236bcd Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Mon, 16 Dec 2024 11:20:14 -0500 Subject: [PATCH 05/23] `CHANGELOG.md` --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68d5f921..334ff2de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ Master list of UniV3 forks: * Add `DODOV1` action to Mantle * Add `DODOV1` action to Polygon * Add `DODOV1` action to Scroll +* Add `rebateClaimer()(address)` function on Mainnet Settlers for gas rebate program ## 2024-12-14 From dfe3d72385e8e599ba000643ca45aef9b8e15729 Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Mon, 16 Dec 2024 12:39:29 -0500 Subject: [PATCH 06/23] Relax solc version --- src/deployer/DeployerAddress.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/deployer/DeployerAddress.sol b/src/deployer/DeployerAddress.sol index 27082984..1cb28fa2 100644 --- a/src/deployer/DeployerAddress.sol +++ b/src/deployer/DeployerAddress.sol @@ -1,4 +1,4 @@ // SPDX-License-Identifier: MIT -pragma solidity =0.8.25; +pragma solidity ^0.8.25; address constant DEPLOYER = 0x00000000000004533Fe15556B1E086BB1A72cEae; From 8039e8b37515614f0a68ecdbc042afd05f16c64a Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Tue, 17 Dec 2024 15:31:33 -0500 Subject: [PATCH 07/23] Add script for replacing deployment safe signer --- sh/common_safe.sh | 33 ++++- sh/confirm_replace_signer.sh | 254 +++++++++++++++++++++++++++++++++++ 2 files changed, 284 insertions(+), 3 deletions(-) create mode 100755 sh/confirm_replace_signer.sh diff --git a/sh/common_safe.sh b/sh/common_safe.sh index e79559c8..d54ffaea 100644 --- a/sh/common_safe.sh +++ b/sh/common_safe.sh @@ -64,6 +64,15 @@ eip712_json() { fi declare -r -i operation + declare to + if (( $# > 0 )) ; then + to="$1" + shift + else + to="$(target $operation)" + fi + declare -r to + jq -Mc \ ' { @@ -132,7 +141,7 @@ eip712_json() { ' \ --arg verifyingContract "$safe_address" \ --arg chainId "$chainid" \ - --arg to "$(target $operation)" \ + --arg to "$to" \ --arg data "$calldata" \ --arg operation $operation \ --arg nonce $(nonce) \ @@ -152,7 +161,16 @@ eip712_struct_hash() { fi declare -r -i operation - cast keccak "$(cast abi-encode 'foo(bytes32,address,uint256,bytes32,uint8,uint256,uint256,uint256,address,address,uint256)' "$type_hash" "$(target $operation)" 0 "$(cast keccak "$calldata")" $operation 0 0 0 "$(cast address-zero)" "$(cast address-zero)" $(nonce))" + declare to + if (( $# > 0 )) ; then + to="$1" + shift + else + to="$(target $operation)" + fi + declare -r to + + cast keccak "$(cast abi-encode 'foo(bytes32,address,uint256,bytes32,uint8,uint256,uint256,uint256,address,address,uint256)' "$type_hash" "$to" 0 "$(cast keccak "$calldata")" $operation 0 0 0 "$(cast address-zero)" "$(cast address-zero)" $(nonce))" } eip712_hash() { @@ -168,8 +186,17 @@ eip712_hash() { fi declare -r -i operation + declare to + if (( $# > 0 )) ; then + to="$1" + shift + else + to="$(target $operation)" + fi + declare -r to + declare struct_hash - struct_hash="$(eip712_struct_hash "$calldata" $operation)" + struct_hash="$(eip712_struct_hash "$calldata" $operation "$to")" cast keccak "$(cast concat-hex '0x1901' "$domain_separator" "$struct_hash")" } diff --git a/sh/confirm_replace_signer.sh b/sh/confirm_replace_signer.sh new file mode 100755 index 00000000..d4dae0ec --- /dev/null +++ b/sh/confirm_replace_signer.sh @@ -0,0 +1,254 @@ +#!/bin/bash + +## POSIX Bash implementation of realpath +## Copied and modified from https://github.com/mkropat/sh-realpath and https://github.com/AsymLabs/realpath-lib/ +## Copyright (c) 2014 Michael Kropat - MIT License +## Copyright (c) 2013 Asymmetry Laboratories - MIT License + +realpath() { + _resolve_symlinks "$(_canonicalize "$1")" +} + +_directory() { + local out slsh + slsh=/ + out="$1" + out="${out//$slsh$slsh/$slsh}" + if [ "$out" = / ]; then + echo / + return + fi + out="${out%/}" + case "$out" in + */*) + out="${out%/*}" + ;; + *) + out=. + ;; + esac + if [ "$out" ]; then + printf '%s\n' "$out" + else + echo / + fi +} + +_file() { + local out slsh + slsh=/ + out="$1" + out="${out//$slsh$slsh/$slsh}" + if [ "$out" = / ]; then + echo / + return + fi + out="${out%/}" + out="${out##*/}" + printf '%s\n' "$out" +} + +_resolve_symlinks() { + local path pattern context + while [ -L "$1" ]; do + context="$(_directory "$1")" + path="$(POSIXLY_CORRECT=y ls -ld -- "$1" 2>/dev/null)" + pattern='*'"$(_escape "$1")"' -> ' + path="${path#$pattern}" + set -- "$(_canonicalize "$(_prepend_context "$context" "$path")")" "$@" + _assert_no_path_cycles "$@" || return 1 + done + printf '%s\n' "$1" +} + +_escape() { + local out + out='' + local -i i + for ((i=0; i < ${#1}; i+=1)); do + out+='\'"${1:$i:1}" + done + printf '%s\n' "$out" +} + +_prepend_context() { + if [ "$1" = . ]; then + printf '%s\n' "$2" + else + case "$2" in + /* ) printf '%s\n' "$2" ;; + * ) printf '%s\n' "$1/$2" ;; + esac + fi +} + +_assert_no_path_cycles() { + local target path + + if [ $# -gt 16 ]; then + return 1 + fi + + target="$1" + shift + + for path in "$@"; do + if [ "$path" = "$target" ]; then + return 1 + fi + done +} + +_canonicalize() { + local d f + if [ -d "$1" ]; then + (CDPATH= cd -P "$1" 2>/dev/null && pwd -P) + else + d="$(_directory "$1")" + f="$(_file "$1")" + (CDPATH= cd -P "$d" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$f") + fi +} + +## end POSIX Bash implementation of realpath + +set -Eeufo pipefail -o posix + +declare project_root +project_root="$(_directory "$(_directory "$(realpath "${BASH_SOURCE[0]}")")")" +declare -r project_root +cd "$project_root" + +. "$project_root"/sh/common.sh + +declare safe_address +safe_address="$(get_config governance.deploymentSafe)" +declare -r safe_address + +. "$project_root"/sh/common_safe.sh +. "$project_root"/sh/common_safe_owner.sh +. "$project_root"/sh/common_wallet_type.sh + +declare safe_url +safe_url="$(get_config safe.apiUrl)" +declare -r safe_url + +declare old_owner +old_owner="$1" +shift +old_owner="$(cast to-checksum "$old_owner")" +declare -r old_owner + +declare new_owner +new_owner="$1" +shift +new_owner="$(cast to-checksum "$new_owner")" +declare -r new_owner + +declare prev_owner=0x0000000000000000000000000000000000000001 +for i in "${!owners_array[@]}" ; do + if [[ "$(cast to-checksum "${owners_array[$i]}")" = "$old_owner" ]] ; then + break + fi + prev_owner="$(cast to-checksum "${owners_array[$i]}")" +done +declare -r prev_owner + +if [[ "$prev_owner" = "$(cast to-checksum "${owners_array[$((${#owners_array[@]} - 1))]}")" ]] ; then + echo 'Old owner "'"$old_owner"'" not found' >&2 + exit 1 +fi + +declare -r swapOwner_sig='swapOwner(address,address,address)' +declare swapOwner_call +swapOwner_call="$(cast calldata "$swapOwner_sig" "$prev_owner" "$old_owner" "$new_owner")" +declare -r swapOwner_call + +# declare -a calls=() + +# calls+=( +# "$( +# cast concat-hex \ +# 0x00 \ +# "$safe_address" \ +# "$(cast to-uint256 0)" \ +# "$(cast to-uint256 $(( (${#swapOwner_call} - 2) / 2 )) )" \ +# "$swapOwner_call" +# )" +# ) + +declare struct_json +struct_json="$(eip712_json "$swapOwner_call" 0 "$safe_address")" +declare -r struct_json + +# sign the message +declare signature +if [[ $wallet_type = 'frame' ]] ; then + declare typedDataRPC + typedDataRPC="$( + jq -Mc \ + ' + { + "jsonrpc": "2.0", + "method": "eth_signTypedData", + "params": [ + $signer, + . + ], + "id": 1 + } + ' \ + --arg signer "$signer" \ + <<<"$struct_json" + )" + declare -r typedDataRPC + signature="$(curl --fail -s -X POST --url 'http://127.0.0.1:1248' --data "$typedDataRPC")" + if [[ $signature = *error* ]] ; then + echo "$signature" >&2 + exit 1 + fi + signature="$(jq -Mr .result <<<"$signature")" +else + signature="$(cast wallet sign "${wallet_args[@]}" --from "$signer" --data "$struct_json")" +fi +declare -r signature + +# save/submit the signature +if [[ $safe_url = 'NOT SUPPORTED' ]] ; then + declare signature_file + signature_file="$project_root"/settler_confirmation_"$chain_display_name"_"$(git rev-parse --short=8 HEAD)"_"$(tr '[:upper:]' '[:lower:]' <<<"$signer")"_$(nonce).txt + echo "$signature" >"$signature_file" + + echo "Signature saved to '$signature_file'" >&2 +else + declare signing_hash + signing_hash="$(eip712_hash "$swapOwner_call" 0 "$safe_address")" + declare -r signing_hash + + # encode the Safe Transaction Service API call + declare safe_multisig_transaction + safe_multisig_transaction="$( + jq -Mc \ + "$eip712_message_json_template"', + "contractTransactionHash": $signing_hash, + "sender": $sender, + "signature": $signature, + "origin": "0xSettlerCLI" + } + ' \ + --arg to "$safe_address" \ + --arg data "$swapOwner_call" \ + --arg operation 0 \ + --arg nonce $(nonce) \ + --arg signing_hash "$signing_hash" \ + --arg sender "$signer" \ + --arg signature "$signature" \ + --arg safe_address "$safe_address" \ + <<<'{}' + )" + + # call the API + curl --fail "$safe_url"'/v1/safes/'"$safe_address"'/multisig-transactions/' -X POST -H 'Content-Type: application/json' --data "$safe_multisig_transaction" + + echo 'Signature submitted' >&2 +fi From 67c53633d8fa1a20fcbd27f415aa54ac69dc5309 Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Tue, 17 Dec 2024 16:05:11 -0500 Subject: [PATCH 08/23] Add script for submitting transactions for replacing deployment safe signer --- sh/confirm_replace_signer.sh | 6 +- sh/replace_signer.sh | 257 +++++++++++++++++++++++++++++++++++ 2 files changed, 262 insertions(+), 1 deletion(-) create mode 100755 sh/replace_signer.sh diff --git a/sh/confirm_replace_signer.sh b/sh/confirm_replace_signer.sh index d4dae0ec..5ffb42a9 100755 --- a/sh/confirm_replace_signer.sh +++ b/sh/confirm_replace_signer.sh @@ -133,6 +133,10 @@ declare safe_url safe_url="$(get_config safe.apiUrl)" declare -r safe_url +declare chain_display_name +chain_display_name="$(get_config displayName)" +declare -r chain_display_name + declare old_owner old_owner="$1" shift @@ -216,7 +220,7 @@ declare -r signature # save/submit the signature if [[ $safe_url = 'NOT SUPPORTED' ]] ; then declare signature_file - signature_file="$project_root"/settler_confirmation_"$chain_display_name"_"$(git rev-parse --short=8 HEAD)"_"$(tr '[:upper:]' '[:lower:]' <<<"$signer")"_$(nonce).txt + signature_file="$project_root"/replace_deploy_signer_"$chain_display_name"_"$(git rev-parse --short=8 HEAD)"_"$(tr '[:upper:]' '[:lower:]' <<<"$signer")"_$(nonce).txt echo "$signature" >"$signature_file" echo "Signature saved to '$signature_file'" >&2 diff --git a/sh/replace_signer.sh b/sh/replace_signer.sh new file mode 100755 index 00000000..715de750 --- /dev/null +++ b/sh/replace_signer.sh @@ -0,0 +1,257 @@ +#!/bin/bash + +## POSIX Bash implementation of realpath +## Copied and modified from https://github.com/mkropat/sh-realpath and https://github.com/AsymLabs/realpath-lib/ +## Copyright (c) 2014 Michael Kropat - MIT License +## Copyright (c) 2013 Asymmetry Laboratories - MIT License + +realpath() { + _resolve_symlinks "$(_canonicalize "$1")" +} + +_directory() { + local out slsh + slsh=/ + out="$1" + out="${out//$slsh$slsh/$slsh}" + if [ "$out" = / ]; then + echo / + return + fi + out="${out%/}" + case "$out" in + */*) + out="${out%/*}" + ;; + *) + out=. + ;; + esac + if [ "$out" ]; then + printf '%s\n' "$out" + else + echo / + fi +} + +_file() { + local out slsh + slsh=/ + out="$1" + out="${out//$slsh$slsh/$slsh}" + if [ "$out" = / ]; then + echo / + return + fi + out="${out%/}" + out="${out##*/}" + printf '%s\n' "$out" +} + +_resolve_symlinks() { + local path pattern context + while [ -L "$1" ]; do + context="$(_directory "$1")" + path="$(POSIXLY_CORRECT=y ls -ld -- "$1" 2>/dev/null)" + pattern='*'"$(_escape "$1")"' -> ' + path="${path#$pattern}" + set -- "$(_canonicalize "$(_prepend_context "$context" "$path")")" "$@" + _assert_no_path_cycles "$@" || return 1 + done + printf '%s\n' "$1" +} + +_escape() { + local out + out='' + local -i i + for ((i=0; i < ${#1}; i+=1)); do + out+='\'"${1:$i:1}" + done + printf '%s\n' "$out" +} + +_prepend_context() { + if [ "$1" = . ]; then + printf '%s\n' "$2" + else + case "$2" in + /* ) printf '%s\n' "$2" ;; + * ) printf '%s\n' "$1/$2" ;; + esac + fi +} + +_assert_no_path_cycles() { + local target path + + if [ $# -gt 16 ]; then + return 1 + fi + + target="$1" + shift + + for path in "$@"; do + if [ "$path" = "$target" ]; then + return 1 + fi + done +} + +_canonicalize() { + local d f + if [ -d "$1" ]; then + (CDPATH= cd -P "$1" 2>/dev/null && pwd -P) + else + d="$(_directory "$1")" + f="$(_file "$1")" + (CDPATH= cd -P "$d" 2>/dev/null && printf '%s/%s\n' "$(pwd -P)" "$f") + fi +} + +## end POSIX Bash implementation of realpath + +set -Eeufo pipefail -o posix + +declare project_root +project_root="$(_directory "$(_directory "$(realpath "${BASH_SOURCE[0]}")")")" +declare -r project_root +cd "$project_root" + +. "$project_root"/sh/common.sh + +declare safe_address +safe_address="$(get_config governance.deploymentSafe)" +declare -r safe_address + +. "$project_root"/sh/common_safe.sh + +declare signer +IFS='' read -p 'What address will you submit with?: ' -e -r -i 0xEf37aD2BACD70119F141140f7B5E46Cd53a65fc4 signer +declare -r signer + +. "$project_root"/sh/common_wallet_type.sh + +declare -r get_owners_sig='getOwners()(address[])' +declare owners +owners="$(cast abi-decode "$get_owners_sig" "$(cast call --rpc-url "$rpc_url" "$safe_address" "$(cast calldata "$get_owners_sig")")")" +owners="${owners:1:$((${#owners} - 2))}" +owners="${owners//, /;}" +declare -r owners + +declare -a owners_array +IFS=';' read -r -a owners_array <<<"$owners" +declare -r -a owners_array + +declare safe_url +safe_url="$(get_config safe.apiUrl)" +declare -r safe_url + +declare chain_display_name +chain_display_name="$(get_config displayName)" +declare -r chain_display_name + +declare old_owner +old_owner="$1" +shift +old_owner="$(cast to-checksum "$old_owner")" +declare -r old_owner + +declare new_owner +new_owner="$1" +shift +new_owner="$(cast to-checksum "$new_owner")" +declare -r new_owner + +declare prev_owner=0x0000000000000000000000000000000000000001 +for i in "${!owners_array[@]}" ; do + if [[ "$(cast to-checksum "${owners_array[$i]}")" = "$old_owner" ]] ; then + break + fi + prev_owner="$(cast to-checksum "${owners_array[$i]}")" +done +declare -r prev_owner + +if [[ "$prev_owner" = "$(cast to-checksum "${owners_array[$((${#owners_array[@]} - 1))]}")" ]] ; then + echo 'Old owner "'"$old_owner"'" not found' >&2 + exit 1 +fi + +declare -r swapOwner_sig='swapOwner(address,address,address)' +declare swapOwner_call +swapOwner_call="$(cast calldata "$swapOwner_sig" "$prev_owner" "$old_owner" "$new_owner")" +declare -r swapOwner_call + +# set minimum gas price to (mostly for Arbitrum and BNB) +declare -i min_gas_price +min_gas_price="$(get_config minGasPriceGwei)" +min_gas_price=$((min_gas_price * 1000000000)) +declare -r -i min_gas_price +declare -i gas_price +gas_price="$(cast gas-price --rpc-url "$rpc_url")" +if (( gas_price < min_gas_price )) ; then + echo 'Setting gas price to minimum of '$((min_gas_price / 1000000000))' gwei' >&2 + gas_price=$min_gas_price +fi +declare -r -i gas_price +declare -i gas_estimate_multiplier +gas_estimate_multiplier="$(get_config gasMultiplierPercent)" +declare -r -i gas_estimate_multiplier + +declare signing_hash +signing_hash="$(eip712_hash "$swapOwner_call" 0 "$safe_address")" +declare -r signing_hash + +declare -a signatures=() +if [[ $safe_url = 'NOT SUPPORTED' ]] ; then + set +f + for confirmation in "$project_root"/replace_deploy_signer_"$chain_display_name"_"$(git rev-parse --short=8 HEAD)"_*_$(nonce).txt ; do + signatures+=("$(<"$confirmation")") + done + set -f + + if (( ${#signatures[@]} != 2 )) ; then + echo 'Bad number of signatures' >&2 + exit 1 + fi +else + declare signatures_json + signatures_json="$(curl --fail -s "$safe_url"'/v1/multisig-transactions/'"$signing_hash"'/confirmations/?executed=false' -X GET)" + + if (( $(jq -Mr .count <<<"$signatures_json") != 2 )) ; then + echo 'Bad number of signatures' >&2 + exit 1 + fi + + if [ "$(jq -Mr '.results[1].owner' <<<"$signatures_json" | tr '[:upper:]' '[:lower:]')" \< "$(jq -Mr '.results[0].owner' <<<"$signatures_json" | tr '[:upper:]' '[:lower:]')" ] ; then + signatures+=( "$(jq -Mr '.results[1].signature' <<<"$signatures_json")" ) + signatures+=( "$(jq -Mr '.results[0].signature' <<<"$signatures_json")" ) + else + signatures+=( "$(jq -Mr '.results[0].signature' <<<"$signatures_json")" ) + signatures+=( "$(jq -Mr '.results[1].signature' <<<"$signatures_json")" ) + fi +fi +declare -r -a signatures + +declare packed_signatures +packed_signatures="$(cast concat-hex "${signatures[@]}")" +declare -r packed_signatures + +# configure gas limit +declare -a args=( + "$safe_address" "$execTransaction_sig" + # to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, signatures + "$safe_address" 0 "$swapOwner_call" 0 0 0 0 "$(cast address-zero)" "$(cast address-zero)" "$packed_signatures" +) + +# set gas limit and add multiplier/headroom (again mostly for Arbitrum) +declare -i gas_limit +gas_limit="$(cast estimate --from "$signer" --rpc-url "$rpc_url" --gas-price $gas_price --chain $chainid "${args[@]}")" +gas_limit=$((gas_limit * gas_estimate_multiplier / 100)) + +if [[ $wallet_type = 'frame' ]] ; then + cast send --confirmations 10 --from "$signer" --rpc-url 'http://127.0.0.1:1248/' --chain $chainid --gas-price $gas_price --gas-limit $gas_limit "${wallet_args[@]}" $(get_config extraFlags) "${args[@]}" +else + cast send --confirmations 10 --from "$signer" --rpc-url "$rpc_url" --chain $chainid --gas-price $gas_price --gas-limit $gas_limit "${wallet_args[@]}" $(get_config extraFlags) "${args[@]}" +fi From b76cfa321b75644786ae5a81ca96c8aedb4675e2 Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Tue, 17 Dec 2024 16:05:29 -0500 Subject: [PATCH 09/23] Replace compromised Safe signer (LastPass incident) --- script/SafeConfig.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/SafeConfig.sol b/script/SafeConfig.sol index 37883dbb..b03d9e27 100644 --- a/script/SafeConfig.sol +++ b/script/SafeConfig.sol @@ -86,7 +86,7 @@ library SafeConfig { result[0] = 0x24420bC8C760787F3eEF3b809e81f44d31a9c5A2; // Jacob result[1] = 0x000000c397124D0375555F435e201F83B636C26C; // Kyu result[2] = 0x6879fAb591ed0d62537A3Cac9D7cd41218445a84; // Sav - result[3] = 0x755588A2422E4779aC30cBD3774BBB12521d2c15; // Josh + result[3] = 0x052809d05DC83F317b2f578710411e6cbF88AC5a; // Josh result[4] = 0xDCa4ee0070b4aa44b30D8af22F3CBbb2cC859dAf; // Kevin result[5] = 0xD6B66609E5C05210BE0A690aB3b9788BA97aFa60; // Duncan result[6] = 0xEC3E1F7aC9Df42c31570b02068f2e7500915e557; // Andy From c756433487446348b016bddf1d95fa1f73f1778f Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Tue, 17 Dec 2024 20:02:13 -0500 Subject: [PATCH 10/23] Cleanup --- sh/confirm_replace_signer.sh | 17 ++--------------- sh/replace_signer.sh | 4 +++- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/sh/confirm_replace_signer.sh b/sh/confirm_replace_signer.sh index 5ffb42a9..9487e3b8 100755 --- a/sh/confirm_replace_signer.sh +++ b/sh/confirm_replace_signer.sh @@ -151,14 +151,14 @@ declare -r new_owner declare prev_owner=0x0000000000000000000000000000000000000001 for i in "${!owners_array[@]}" ; do - if [[ "$(cast to-checksum "${owners_array[$i]}")" = "$old_owner" ]] ; then + if [[ $(cast to-checksum "${owners_array[$i]}") = "$old_owner" ]] ; then break fi prev_owner="$(cast to-checksum "${owners_array[$i]}")" done declare -r prev_owner -if [[ "$prev_owner" = "$(cast to-checksum "${owners_array[$((${#owners_array[@]} - 1))]}")" ]] ; then +if [[ $prev_owner = "$(cast to-checksum "${owners_array[$((${#owners_array[@]} - 1))]}")" ]] ; then echo 'Old owner "'"$old_owner"'" not found' >&2 exit 1 fi @@ -168,19 +168,6 @@ declare swapOwner_call swapOwner_call="$(cast calldata "$swapOwner_sig" "$prev_owner" "$old_owner" "$new_owner")" declare -r swapOwner_call -# declare -a calls=() - -# calls+=( -# "$( -# cast concat-hex \ -# 0x00 \ -# "$safe_address" \ -# "$(cast to-uint256 0)" \ -# "$(cast to-uint256 $(( (${#swapOwner_call} - 2) / 2 )) )" \ -# "$swapOwner_call" -# )" -# ) - declare struct_json struct_json="$(eip712_json "$swapOwner_call" 0 "$safe_address")" declare -r struct_json diff --git a/sh/replace_signer.sh b/sh/replace_signer.sh index 715de750..40cd0cba 100755 --- a/sh/replace_signer.sh +++ b/sh/replace_signer.sh @@ -218,6 +218,7 @@ if [[ $safe_url = 'NOT SUPPORTED' ]] ; then else declare signatures_json signatures_json="$(curl --fail -s "$safe_url"'/v1/multisig-transactions/'"$signing_hash"'/confirmations/?executed=false' -X GET)" + declare -r signatures_json if (( $(jq -Mr .count <<<"$signatures_json") != 2 )) ; then echo 'Bad number of signatures' >&2 @@ -239,7 +240,7 @@ packed_signatures="$(cast concat-hex "${signatures[@]}")" declare -r packed_signatures # configure gas limit -declare -a args=( +declare -r -a args=( "$safe_address" "$execTransaction_sig" # to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, signatures "$safe_address" 0 "$swapOwner_call" 0 0 0 0 "$(cast address-zero)" "$(cast address-zero)" "$packed_signatures" @@ -249,6 +250,7 @@ declare -a args=( declare -i gas_limit gas_limit="$(cast estimate --from "$signer" --rpc-url "$rpc_url" --gas-price $gas_price --chain $chainid "${args[@]}")" gas_limit=$((gas_limit * gas_estimate_multiplier / 100)) +declare -r -i gas_limit if [[ $wallet_type = 'frame' ]] ; then cast send --confirmations 10 --from "$signer" --rpc-url 'http://127.0.0.1:1248/' --chain $chainid --gas-price $gas_price --gas-limit $gas_limit "${wallet_args[@]}" $(get_config extraFlags) "${args[@]}" From a6d1d95153099a033cfd5f0a2e74fd20fb47f94d Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Wed, 18 Dec 2024 08:04:42 -0500 Subject: [PATCH 11/23] Cleanup; DRY --- sh/common_safe.sh | 37 ++++++++++++++++++++++++++++++++++++ sh/common_safe_owner.sh | 11 ----------- sh/confirm_replace_signer.sh | 16 +--------------- sh/replace_signer.sh | 16 +--------------- 4 files changed, 39 insertions(+), 41 deletions(-) diff --git a/sh/common_safe.sh b/sh/common_safe.sh index d54ffaea..a90bdd6c 100644 --- a/sh/common_safe.sh +++ b/sh/common_safe.sh @@ -13,6 +13,43 @@ nonce() { echo $((${SAFE_NONCE_INCREMENT:-0} + current_safe_nonce)) } +declare -r get_owners_sig='getOwners()(address[])' +declare owners +owners="$(cast abi-decode "$get_owners_sig" "$(cast call --rpc-url "$rpc_url" "$safe_address" "$(cast calldata "$get_owners_sig")")")" +owners="${owners:1:$((${#owners} - 2))}" +owners="${owners//, /;}" +declare -r owners + +declare -a owners_array +IFS=';' read -r -a owners_array <<<"$owners" +declare -r -a owners_array + +prev_owner() { + declare inp="$1" + shift + inp="$(cast to-checksum "$inp")" + declare -r inp + + declare result=0x0000000000000000000000000000000000000001 + declare owner_i + for i in ${!owners_array[@]} ; do + owner_i="$(cast to-checksum "${owners_array[$i]}")" + if [[ $owner_i = "$inp" ]] ; then + break + fi + result="$owner_i" + done + declare -r result + + if [[ $result = "$(cast to-checksum "${owners_array[$((${#owners_array[@]} - 1))]}")" ]] ; then + echo 'Old owner "'"$inp"'" not found' >&2 + return 1 + fi + + echo "$result" +} + + target() { declare -i operation if (( $# > 0 )) ; then diff --git a/sh/common_safe_owner.sh b/sh/common_safe_owner.sh index 8fba2730..29661973 100644 --- a/sh/common_safe_owner.sh +++ b/sh/common_safe_owner.sh @@ -1,14 +1,3 @@ -declare -r get_owners_sig='getOwners()(address[])' -declare owners -owners="$(cast abi-decode "$get_owners_sig" "$(cast call --rpc-url "$rpc_url" "$safe_address" "$(cast calldata "$get_owners_sig")")")" -owners="${owners:1:$((${#owners} - 2))}" -owners="${owners//, /;}" -declare -r owners - -declare -a owners_array -IFS=';' read -r -a owners_array <<<"$owners" -declare -r -a owners_array - declare signer declare -r saved_safe_owner="$project_root"/config/safe_owner.txt diff --git a/sh/confirm_replace_signer.sh b/sh/confirm_replace_signer.sh index 9487e3b8..e1ae8d2c 100755 --- a/sh/confirm_replace_signer.sh +++ b/sh/confirm_replace_signer.sh @@ -149,23 +149,9 @@ shift new_owner="$(cast to-checksum "$new_owner")" declare -r new_owner -declare prev_owner=0x0000000000000000000000000000000000000001 -for i in "${!owners_array[@]}" ; do - if [[ $(cast to-checksum "${owners_array[$i]}") = "$old_owner" ]] ; then - break - fi - prev_owner="$(cast to-checksum "${owners_array[$i]}")" -done -declare -r prev_owner - -if [[ $prev_owner = "$(cast to-checksum "${owners_array[$((${#owners_array[@]} - 1))]}")" ]] ; then - echo 'Old owner "'"$old_owner"'" not found' >&2 - exit 1 -fi - declare -r swapOwner_sig='swapOwner(address,address,address)' declare swapOwner_call -swapOwner_call="$(cast calldata "$swapOwner_sig" "$prev_owner" "$old_owner" "$new_owner")" +swapOwner_call="$(cast calldata "$swapOwner_sig" "$(prev_owner "$old_owner")" "$old_owner" "$new_owner")" declare -r swapOwner_call declare struct_json diff --git a/sh/replace_signer.sh b/sh/replace_signer.sh index 40cd0cba..005efb1f 100755 --- a/sh/replace_signer.sh +++ b/sh/replace_signer.sh @@ -164,23 +164,9 @@ shift new_owner="$(cast to-checksum "$new_owner")" declare -r new_owner -declare prev_owner=0x0000000000000000000000000000000000000001 -for i in "${!owners_array[@]}" ; do - if [[ "$(cast to-checksum "${owners_array[$i]}")" = "$old_owner" ]] ; then - break - fi - prev_owner="$(cast to-checksum "${owners_array[$i]}")" -done -declare -r prev_owner - -if [[ "$prev_owner" = "$(cast to-checksum "${owners_array[$((${#owners_array[@]} - 1))]}")" ]] ; then - echo 'Old owner "'"$old_owner"'" not found' >&2 - exit 1 -fi - declare -r swapOwner_sig='swapOwner(address,address,address)' declare swapOwner_call -swapOwner_call="$(cast calldata "$swapOwner_sig" "$prev_owner" "$old_owner" "$new_owner")" +swapOwner_call="$(cast calldata "$swapOwner_sig" "$(prev_owner "$old_owner")" "$old_owner" "$new_owner")" declare -r swapOwner_call # set minimum gas price to (mostly for Arbitrum and BNB) From b12ccde8ae79d65d399d9bb47a759cdb9c8b279c Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Wed, 18 Dec 2024 08:33:19 -0500 Subject: [PATCH 12/23] DRY --- sh/common_deploy_settler.sh | 4 -- sh/common_safe.sh | 5 +- sh/common_safe_owner.sh | 106 +++++++++++++++++++++++++++++++++++ sh/confirm_new_settler.sh | 67 +--------------------- sh/confirm_replace_signer.sh | 70 +---------------------- 5 files changed, 114 insertions(+), 138 deletions(-) diff --git a/sh/common_deploy_settler.sh b/sh/common_deploy_settler.sh index e35f7395..ccc71117 100644 --- a/sh/common_deploy_settler.sh +++ b/sh/common_deploy_settler.sh @@ -84,7 +84,3 @@ if [[ -n "${deployer_address-}" ]] ; then ) fi fi - -declare safe_url -safe_url="$(get_config safe.apiUrl)" -declare -r safe_url diff --git a/sh/common_safe.sh b/sh/common_safe.sh index a90bdd6c..669c0fbb 100644 --- a/sh/common_safe.sh +++ b/sh/common_safe.sh @@ -1,3 +1,7 @@ +declare safe_url +safe_url="$(get_config safe.apiUrl)" +declare -r safe_url + declare multicall_address multicall_address="$(get_config safe.multiCall)" declare -r multicall_address @@ -49,7 +53,6 @@ prev_owner() { echo "$result" } - target() { declare -i operation if (( $# > 0 )) ; then diff --git a/sh/common_safe_owner.sh b/sh/common_safe_owner.sh index 29661973..be475ab8 100644 --- a/sh/common_safe_owner.sh +++ b/sh/common_safe_owner.sh @@ -32,3 +32,109 @@ if ! contains "${signer-unset}" "${owners_array[@]}" ; then fi declare -r signer + +sign_call() { + declare -r _sign_call_struct_json="$1" + shift + + declare _sign_call_result + if [[ $wallet_type = 'frame' ]] ; then + declare typedDataRPC + typedDataRPC="$( + jq -Mc \ + ' + { + "jsonrpc": "2.0", + "method": "eth_signTypedData", + "params": [ + $signer, + . + ], + "id": 1 + } + ' \ + --arg signer "$signer" \ + <<<"$_sign_call_struct_json" + )" + declare -r typedDataRPC + _sign_call_result="$(curl --fail -s -X POST --url 'http://127.0.0.1:1248' --data "$typedDataRPC")" + if [[ $_sign_call_result = *error* ]] ; then + echo "$_sign_call_result" >&2 + return 1 + fi + _sign_call_result="$(jq -Mr .result <<<"$_sign_call_result")" + else + _sign_call_result="$(cast wallet sign "${wallet_args[@]}" --from "$signer" --data "$_sign_call_struct_json")" + fi + declare -r _sign_call_result + + echo "$_sign_call_result" +} + +save_signature() { + declare -r _save_signature_prefix="$1" + shift + + declare -r _save_signature_call="$1" + shift + + declare -r _save_signature_signature="$1" + shift + + declare -i _save_signature_operation + if (( $# > 0 )) ; then + _save_signature_operation="$1" + shift + else + _save_signature_operation=0 + fi + declare -r -i _save_signature_operation + + declare _save_signature_to + if (( $# > 0 )) ; then + _save_signature_to="$1" + shift + else + _save_signature_to="$(target $_save_signature_operation)" + fi + declare -r _save_signature_to + + if [[ $safe_url = 'NOT SUPPORTED' ]] ; then + declare signature_file + signature_file="$project_root"/"$_save_signature_prefix"_"$chain_display_name"_"$(git rev-parse --short=8 HEAD)"_"$(tr '[:upper:]' '[:lower:]' <<<"$signer")"_$(nonce).txt + echo "$signature" >"$signature_file" + + echo "Signature saved to '$signature_file'" >&2 + else + declare signing_hash + signing_hash="$(eip712_hash "$_save_signature_call" $_save_signature_operation "$_save_signature_to")" + declare -r signing_hash + + # encode the Safe Transaction Service API call + declare safe_multisig_transaction + safe_multisig_transaction="$( + jq -Mc \ + "$eip712_message_json_template"', + "contractTransactionHash": $signing_hash, + "sender": $sender, + "signature": $signature, + "origin": "0xSettlerCLI" + } + ' \ + --arg to "$_save_signature_to" \ + --arg data "$_save_signature_call" \ + --arg operation $_save_signature_operation \ + --arg nonce $(nonce) \ + --arg signing_hash "$signing_hash" \ + --arg sender "$signer" \ + --arg signature "$_save_signature_signature" \ + --arg safe_address "$safe_address" \ + <<<'{}' + )" + + # call the API + curl --fail "$safe_url"'/v1/safes/'"$safe_address"'/multisig-transactions/' -X POST -H 'Content-Type: application/json' --data "$safe_multisig_transaction" + + echo 'Signature submitted' >&2 + fi +} diff --git a/sh/confirm_new_settler.sh b/sh/confirm_new_settler.sh index d3265860..8341154f 100755 --- a/sh/confirm_new_settler.sh +++ b/sh/confirm_new_settler.sh @@ -138,73 +138,10 @@ while (( ${#deploy_calldatas[@]} >= 2 )) ; do declare struct_json struct_json="$(eip712_json "$deploy_calldata" $operation)" - # sign the message declare signature - if [[ $wallet_type = 'frame' ]] ; then - declare typedDataRPC - typedDataRPC="$( - jq -Mc \ - ' - { - "jsonrpc": "2.0", - "method": "eth_signTypedData", - "params": [ - $signer, - . - ], - "id": 1 - } - ' \ - --arg signer "$signer" \ - <<<"$struct_json" - )" - signature="$(curl --fail -s -X POST --url 'http://127.0.0.1:1248' --data "$typedDataRPC")" - if [[ $signature = *error* ]] ; then - echo "$signature" >&2 - exit 1 - fi - signature="$(jq -Mr .result <<<"$signature")" - else - signature="$(cast wallet sign "${wallet_args[@]}" --from "$signer" --data "$struct_json")" - fi + signature="$(sign_call "$struct_json")" - if [[ $safe_url = 'NOT SUPPORTED' ]] ; then - declare signature_file - signature_file="$project_root"/settler_confirmation_"$chain_display_name"_"$(git rev-parse --short=8 HEAD)"_"$(tr '[:upper:]' '[:lower:]' <<<"$signer")"_$(nonce).txt - echo "$signature" >"$signature_file" - - echo "Signature saved to '$signature_file'" >&2 - else - declare signing_hash - signing_hash="$(eip712_hash "$deploy_calldata" $operation)" - - # encode the Safe Transaction Service API call - declare safe_multisig_transaction - safe_multisig_transaction="$( - jq -Mc \ - "$eip712_message_json_template"', - "contractTransactionHash": $signing_hash, - "sender": $sender, - "signature": $signature, - "origin": "0xSettlerCLI" - } - ' \ - --arg to "$(target $operation)" \ - --arg data "$deploy_calldata" \ - --arg operation $operation \ - --arg nonce $(nonce) \ - --arg signing_hash "$signing_hash" \ - --arg sender "$signer" \ - --arg signature "$signature" \ - --arg safe_address "$safe_address" \ - <<<'{}' - )" - - # call the API - curl --fail -s "$safe_url"'/v1/safes/'"$safe_address"'/multisig-transactions/' -X POST -H 'Content-Type: application/json' --data "$safe_multisig_transaction" - - echo 'Signature submitted' >&2 - fi + save_signature settler_confirmation "$deploy_calldata" "$signature" $operation SAFE_NONCE_INCREMENT=$((${SAFE_NONCE_INCREMENT:-0} + 1)) done diff --git a/sh/confirm_replace_signer.sh b/sh/confirm_replace_signer.sh index e1ae8d2c..2c1eab0f 100755 --- a/sh/confirm_replace_signer.sh +++ b/sh/confirm_replace_signer.sh @@ -158,74 +158,8 @@ declare struct_json struct_json="$(eip712_json "$swapOwner_call" 0 "$safe_address")" declare -r struct_json -# sign the message declare signature -if [[ $wallet_type = 'frame' ]] ; then - declare typedDataRPC - typedDataRPC="$( - jq -Mc \ - ' - { - "jsonrpc": "2.0", - "method": "eth_signTypedData", - "params": [ - $signer, - . - ], - "id": 1 - } - ' \ - --arg signer "$signer" \ - <<<"$struct_json" - )" - declare -r typedDataRPC - signature="$(curl --fail -s -X POST --url 'http://127.0.0.1:1248' --data "$typedDataRPC")" - if [[ $signature = *error* ]] ; then - echo "$signature" >&2 - exit 1 - fi - signature="$(jq -Mr .result <<<"$signature")" -else - signature="$(cast wallet sign "${wallet_args[@]}" --from "$signer" --data "$struct_json")" -fi +signature="$(sign_call "$struct_json")" declare -r signature -# save/submit the signature -if [[ $safe_url = 'NOT SUPPORTED' ]] ; then - declare signature_file - signature_file="$project_root"/replace_deploy_signer_"$chain_display_name"_"$(git rev-parse --short=8 HEAD)"_"$(tr '[:upper:]' '[:lower:]' <<<"$signer")"_$(nonce).txt - echo "$signature" >"$signature_file" - - echo "Signature saved to '$signature_file'" >&2 -else - declare signing_hash - signing_hash="$(eip712_hash "$swapOwner_call" 0 "$safe_address")" - declare -r signing_hash - - # encode the Safe Transaction Service API call - declare safe_multisig_transaction - safe_multisig_transaction="$( - jq -Mc \ - "$eip712_message_json_template"', - "contractTransactionHash": $signing_hash, - "sender": $sender, - "signature": $signature, - "origin": "0xSettlerCLI" - } - ' \ - --arg to "$safe_address" \ - --arg data "$swapOwner_call" \ - --arg operation 0 \ - --arg nonce $(nonce) \ - --arg signing_hash "$signing_hash" \ - --arg sender "$signer" \ - --arg signature "$signature" \ - --arg safe_address "$safe_address" \ - <<<'{}' - )" - - # call the API - curl --fail "$safe_url"'/v1/safes/'"$safe_address"'/multisig-transactions/' -X POST -H 'Content-Type: application/json' --data "$safe_multisig_transaction" - - echo 'Signature submitted' >&2 -fi +save_signature replace_signer "$swapOwner_call" "$signature" 0 "$safe_address" From 5a7bbff4b3c0f0fd9e34f305833e0ba087108cbb Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Wed, 18 Dec 2024 08:55:16 -0500 Subject: [PATCH 13/23] DRY --- sh/common.sh | 4 +++ sh/common_deploy_settler.sh | 4 --- sh/common_safe.sh | 1 + sh/common_safe_deployer.sh | 63 ++++++++++++++++++++++++++++++++++++ sh/confirm_replace_signer.sh | 8 ----- sh/deploy_new_settler.sh | 33 ++----------------- sh/replace_signer.sh | 54 ++----------------------------- 7 files changed, 72 insertions(+), 95 deletions(-) create mode 100644 sh/common_safe_deployer.sh diff --git a/sh/common.sh b/sh/common.sh index 4f7507a7..31251bc4 100644 --- a/sh/common.sh +++ b/sh/common.sh @@ -64,6 +64,10 @@ declare -i chainid chainid="$(get_config chainId)" declare -r -i chainid +declare chain_display_name +chain_display_name="$(get_config displayName)" +declare -r chain_display_name + declare rpc_url rpc_url="$(get_api_secret rpcUrl)" declare -r rpc_url diff --git a/sh/common_deploy_settler.sh b/sh/common_deploy_settler.sh index ccc71117..50e9df41 100644 --- a/sh/common_deploy_settler.sh +++ b/sh/common_deploy_settler.sh @@ -1,7 +1,3 @@ -declare chain_display_name -chain_display_name="$(get_config displayName)" -declare -r chain_display_name - forge clean declare flat_taker_source flat_taker_source="$project_root"/src/flat/"$chain_display_name"TakerSubmittedFlat.sol diff --git a/sh/common_safe.sh b/sh/common_safe.sh index 669c0fbb..b3d98fe8 100644 --- a/sh/common_safe.sh +++ b/sh/common_safe.sh @@ -1,3 +1,4 @@ + declare safe_url safe_url="$(get_config safe.apiUrl)" declare -r safe_url diff --git a/sh/common_safe_deployer.sh b/sh/common_safe_deployer.sh new file mode 100644 index 00000000..2c947b7f --- /dev/null +++ b/sh/common_safe_deployer.sh @@ -0,0 +1,63 @@ +retrieve_signatures() { + declare -r _retrieve_signatures_prefix="$1" + shift + + declare -r _retrieve_signatures_call="$1" + shift + + declare -i _retrieve_signatures_operation + if (( $# > 0 )) ; then + _retrieve_signatures_operation="$1" + shift + else + _retrieve_signatures_operation=0 + fi + declare -r -i _retrieve_signatures_operation + + declare _retrieve_signatures_to + if (( $# > 0 )) ; then + _retrieve_signatures_to="$1" + shift + else + _retrieve_signatures_to="$(target $_retrieve_signatures_operation)" + fi + declare -r _retrieve_signatures_to + + declare signing_hash + signing_hash="$(eip712_hash "$_retrieve_signatures_call" $_retrieve_signatures_operation "$_retrieve_signatures_to")" + declare -r signing_hash + + declare -a _retrieve_signatures_result + if [[ $safe_url = 'NOT SUPPORTED' ]] ; then + set +f + declare confirmation + for confirmation in "$project_root"/"$_retrieve_signatures_prefix"_"$chain_display_name"_"$(git rev-parse --short=8 HEAD)"_*_$(nonce).txt ; do + signatures+=("$(<"$confirmation")") + done + set -f + + if (( ${#signatures[@]} != 2 )) ; then + echo 'Bad number of signatures' >&2 + return 1 + fi + else + declare signatures_json + signatures_json="$(curl --fail -s "$safe_url"'/v1/multisig-transactions/'"$signing_hash"'/confirmations/?executed=false' -X GET)" + declare -r signatures_json + + if (( $(jq -Mr .count <<<"$signatures_json") != 2 )) ; then + echo 'Bad number of signatures' >&2 + return 1 + fi + + if [ "$(jq -Mr '.results[1].owner' <<<"$signatures_json" | tr '[:upper:]' '[:lower:]')" \< "$(jq -Mr '.results[0].owner' <<<"$signatures_json" | tr '[:upper:]' '[:lower:]')" ] ; then + signatures+=( "$(jq -Mr '.results[1].signature' <<<"$signatures_json")" ) + signatures+=( "$(jq -Mr '.results[0].signature' <<<"$signatures_json")" ) + else + signatures+=( "$(jq -Mr '.results[0].signature' <<<"$signatures_json")" ) + signatures+=( "$(jq -Mr '.results[1].signature' <<<"$signatures_json")" ) + fi + fi + + cast concat-hex "${signatures[@]}" +} diff --git a/sh/confirm_replace_signer.sh b/sh/confirm_replace_signer.sh index 2c1eab0f..b4934578 100755 --- a/sh/confirm_replace_signer.sh +++ b/sh/confirm_replace_signer.sh @@ -129,14 +129,6 @@ declare -r safe_address . "$project_root"/sh/common_safe_owner.sh . "$project_root"/sh/common_wallet_type.sh -declare safe_url -safe_url="$(get_config safe.apiUrl)" -declare -r safe_url - -declare chain_display_name -chain_display_name="$(get_config displayName)" -declare -r chain_display_name - declare old_owner old_owner="$1" shift diff --git a/sh/deploy_new_settler.sh b/sh/deploy_new_settler.sh index 0a427fb8..92681e1e 100755 --- a/sh/deploy_new_settler.sh +++ b/sh/deploy_new_settler.sh @@ -132,6 +132,7 @@ IFS='' read -p 'What address will you submit with?: ' -e -r -i 0xEf37aD2BACD7011 declare -r signer . "$project_root"/sh/common_wallet_type.sh +. "$project_root"/sh/common_safe_deployer.sh . "$project_root"/sh/common_deploy_settler.sh # set minimum gas price to (mostly for Arbitrum and BNB) @@ -158,38 +159,8 @@ while (( ${#deploy_calldatas[@]} >= 2 )) ; do declare signing_hash signing_hash="$(eip712_hash "$deploy_calldata" $operation)" - declare -a signatures=() - if [[ $safe_url = 'NOT SUPPORTED' ]] ; then - set +f - for confirmation in "$project_root"/settler_confirmation_"$chain_display_name"_"$(git rev-parse --short=8 HEAD)"_*_$(nonce).txt ; do - signatures+=("$(<"$confirmation")") - done - set -f - - if (( ${#signatures[@]} != 2 )) ; then - echo 'Bad number of signatures' >&2 - exit 1 - fi - else - declare signatures_json - signatures_json="$(curl --fail -s "$safe_url"'/v1/multisig-transactions/'"$signing_hash"'/confirmations/?executed=false' -X GET)" - - if (( $(jq -Mr .count <<<"$signatures_json") != 2 )) ; then - echo 'Bad number of signatures' >&2 - exit 1 - fi - - if [ "$(jq -Mr '.results[1].owner' <<<"$signatures_json" | tr '[:upper:]' '[:lower:]')" \< "$(jq -Mr '.results[0].owner' <<<"$signatures_json" | tr '[:upper:]' '[:lower:]')" ] ; then - signatures+=( "$(jq -Mr '.results[1].signature' <<<"$signatures_json")" ) - signatures+=( "$(jq -Mr '.results[0].signature' <<<"$signatures_json")" ) - else - signatures+=( "$(jq -Mr '.results[0].signature' <<<"$signatures_json")" ) - signatures+=( "$(jq -Mr '.results[1].signature' <<<"$signatures_json")" ) - fi - fi - declare packed_signatures - packed_signatures="$(cast concat-hex "${signatures[@]}")" + packed_signatures="$(retrieve_signatures settler_confirmation "$deploy_calldata" $operation)" # configure gas limit declare -a args=( diff --git a/sh/replace_signer.sh b/sh/replace_signer.sh index 005efb1f..1cd5e3de 100755 --- a/sh/replace_signer.sh +++ b/sh/replace_signer.sh @@ -131,27 +131,9 @@ declare signer IFS='' read -p 'What address will you submit with?: ' -e -r -i 0xEf37aD2BACD70119F141140f7B5E46Cd53a65fc4 signer declare -r signer +. "$project_root"/sh/common_safe_deployer.sh . "$project_root"/sh/common_wallet_type.sh -declare -r get_owners_sig='getOwners()(address[])' -declare owners -owners="$(cast abi-decode "$get_owners_sig" "$(cast call --rpc-url "$rpc_url" "$safe_address" "$(cast calldata "$get_owners_sig")")")" -owners="${owners:1:$((${#owners} - 2))}" -owners="${owners//, /;}" -declare -r owners - -declare -a owners_array -IFS=';' read -r -a owners_array <<<"$owners" -declare -r -a owners_array - -declare safe_url -safe_url="$(get_config safe.apiUrl)" -declare -r safe_url - -declare chain_display_name -chain_display_name="$(get_config displayName)" -declare -r chain_display_name - declare old_owner old_owner="$1" shift @@ -189,40 +171,8 @@ declare signing_hash signing_hash="$(eip712_hash "$swapOwner_call" 0 "$safe_address")" declare -r signing_hash -declare -a signatures=() -if [[ $safe_url = 'NOT SUPPORTED' ]] ; then - set +f - for confirmation in "$project_root"/replace_deploy_signer_"$chain_display_name"_"$(git rev-parse --short=8 HEAD)"_*_$(nonce).txt ; do - signatures+=("$(<"$confirmation")") - done - set -f - - if (( ${#signatures[@]} != 2 )) ; then - echo 'Bad number of signatures' >&2 - exit 1 - fi -else - declare signatures_json - signatures_json="$(curl --fail -s "$safe_url"'/v1/multisig-transactions/'"$signing_hash"'/confirmations/?executed=false' -X GET)" - declare -r signatures_json - - if (( $(jq -Mr .count <<<"$signatures_json") != 2 )) ; then - echo 'Bad number of signatures' >&2 - exit 1 - fi - - if [ "$(jq -Mr '.results[1].owner' <<<"$signatures_json" | tr '[:upper:]' '[:lower:]')" \< "$(jq -Mr '.results[0].owner' <<<"$signatures_json" | tr '[:upper:]' '[:lower:]')" ] ; then - signatures+=( "$(jq -Mr '.results[1].signature' <<<"$signatures_json")" ) - signatures+=( "$(jq -Mr '.results[0].signature' <<<"$signatures_json")" ) - else - signatures+=( "$(jq -Mr '.results[0].signature' <<<"$signatures_json")" ) - signatures+=( "$(jq -Mr '.results[1].signature' <<<"$signatures_json")" ) - fi -fi -declare -r -a signatures - declare packed_signatures -packed_signatures="$(cast concat-hex "${signatures[@]}")" +packed_signatures="$(retrieve_signatures replace_signer "$swapOwner_call" 0 "$safe_address")" declare -r packed_signatures # configure gas limit From d457615c2d7ed04395f2b73df7f128877dd4185e Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Wed, 18 Dec 2024 10:37:55 -0500 Subject: [PATCH 14/23] Add `IGNORE_HARDFORK` flag to skip script hardfork checks; do some more cleanup --- sh/common.sh | 16 +++++++++------- sh/common_safe.sh | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/sh/common.sh b/sh/common.sh index 31251bc4..7ded9691 100644 --- a/sh/common.sh +++ b/sh/common.sh @@ -50,14 +50,16 @@ function get_config { jq -Mr ."$chain_name"."$1" < "$project_root"/chain_config.json } -if [[ $(get_config isShanghai) != [Tt]rue ]] ; then - echo 'Chains without the Shanghai hardfork (PUSH0) are not supported' >&2 - exit 1 -fi +if [[ ${IGNORE_HARDFORK-no} != [Yy]es ]] ; then + if [[ $(get_config isShanghai) != [Tt]rue ]] ; then + echo 'Chains without the Shanghai hardfork (PUSH0) are not supported' >&2 + exit 1 + fi -if [[ $(get_config isCancun) != [Tt]rue ]] ; then - echo 'You are on the wrong branch' >&2 - exit 1 + if [[ $(get_config isCancun) != [Tt]rue ]] ; then + echo 'You are on the wrong branch' >&2 + exit 1 + fi fi declare -i chainid diff --git a/sh/common_safe.sh b/sh/common_safe.sh index b3d98fe8..6409d58f 100644 --- a/sh/common_safe.sh +++ b/sh/common_safe.sh @@ -20,7 +20,7 @@ nonce() { declare -r get_owners_sig='getOwners()(address[])' declare owners -owners="$(cast abi-decode "$get_owners_sig" "$(cast call --rpc-url "$rpc_url" "$safe_address" "$(cast calldata "$get_owners_sig")")")" +owners="$(cast call --rpc-url "$rpc_url" "$safe_address" "$get_owners_sig")" owners="${owners:1:$((${#owners} - 2))}" owners="${owners//, /;}" declare -r owners @@ -46,7 +46,7 @@ prev_owner() { done declare -r result - if [[ $result = "$(cast to-checksum "${owners_array[$((${#owners_array[@]} - 1))]}")" ]] ; then + if [[ $result = "$(cast to-checksum "${owners_array[$((${#owners_array} - 1))]}")" ]] ; then echo 'Old owner "'"$inp"'" not found' >&2 return 1 fi From 8ed1684456275f0a483ec35e1bf21b01504c4a4d Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Wed, 18 Dec 2024 13:16:20 -0500 Subject: [PATCH 15/23] WIP: Ink chain --- chain_config.json | 26 +++++++++++++ sh/common.sh | 2 +- src/chains/Ink/Common.sol | 57 ++++++++++++++++++++++++++++ src/chains/Ink/MetaTxn.sol | 56 ++++++++++++++++++++++++++++ src/chains/Ink/TakerSubmitted.sol | 62 +++++++++++++++++++++++++++++++ 5 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 src/chains/Ink/Common.sol create mode 100644 src/chains/Ink/MetaTxn.sol create mode 100644 src/chains/Ink/TakerSubmitted.sol diff --git a/chain_config.json b/chain_config.json index c1f31e32..b5e3853c 100644 --- a/chain_config.json +++ b/chain_config.json @@ -440,5 +440,31 @@ "deployer": "0x00000000000004533Fe15556B1E086BB1A72cEae" }, "etherscanApi": "https://api.sonicscan.org/api" + }, + "ink": { + "chainId": 57073, + "displayName": "Ink", + "isShanghai": null, + "isCancun": null, + "extraFlags": "--legacy", + "gasMultiplierPercent": 200, + "minGasPriceGwei": 1, + "safe": { + "singleton": "0xfb1bffC9d739B8D520DaF37dF666da4C687191EA", + "factory": "0xC22834581EbC8527d974F8a1c97E1bEA4EF910BC", + "fallback": "0x017062a1dE2FE6b99BE3d9d37841FeD19F573804", + "multiCall": "0xA1dabEF33b3B82c7814B6D82A79e50F4AC44102B", + "apiUrl": "https://safe-transaction-ink.safe.global/api" + }, + "governance": { + "upgradeSafe": null, + "deploymentSafe": null, + "pause": null + }, + "deployment": { + "deployer": null, + "allowanceHolder": null + }, + "blockscoutApi": "https://explorer.inkonchain.com/api" } } diff --git a/sh/common.sh b/sh/common.sh index 7ded9691..d803083b 100644 --- a/sh/common.sh +++ b/sh/common.sh @@ -87,7 +87,7 @@ function verify_contract { declare -r _verify_source_path="$1" shift - if (( chainid == 34443 )) ; then # Mode uses Blockscout, not Etherscan + if (( chainid == 34443 )) || (( chainid == 57073 )) ; then # Mode and Ink use Blockscout, not Etherscan forge verify-contract --watch --chain $chainid --verifier blockscout --verifier-url "$(get_config blockscoutApi)" --constructor-args "$_verify_constructor_args" "$_verify_deployed_address" "$_verify_source_path" else forge verify-contract --watch --chain $chainid --verifier custom --verifier-api-key "$(get_api_secret etherscanKey)" --verifier-url "$(get_config etherscanApi)" --constructor-args "$_verify_constructor_args" "$_verify_deployed_address" "$_verify_source_path" diff --git a/src/chains/Ink/Common.sol b/src/chains/Ink/Common.sol new file mode 100644 index 00000000..99ce04be --- /dev/null +++ b/src/chains/Ink/Common.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.8.25; + +import {SettlerBase} from "../../SettlerBase.sol"; + +import {IERC20} from "@forge-std/interfaces/IERC20.sol"; +import {FreeMemory} from "../../utils/FreeMemory.sol"; + +import {ISettlerActions} from "../../ISettlerActions.sol"; +import {ISignatureTransfer} from "@permit2/interfaces/ISignatureTransfer.sol"; +import {UnknownForkId} from "../../core/SettlerErrors.sol"; + +import { + uniswapV3InkFactory, + uniswapV3InitHash, + uniswapV3ForkId, + IUniswapV3Callback +} from "../../core/univ3forks/UniswapV3.sol"; + +// Solidity inheritance is stupid +import {SettlerAbstract} from "../../SettlerAbstract.sol"; + +abstract contract InkMixin is FreeMemory, SettlerBase { + constructor() { + assert(block.chainid == 57073 || block.chainid == 31337); + } + + function _dispatch(uint256 i, uint256 action, bytes calldata data) + internal + virtual + override(SettlerAbstract, SettlerBase) + DANGEROUS_freeMemory + returns (bool) + { + if (super._dispatch(i, action, data)) { + return true; + } else { + return false; + } + return true; + } + + function _uniV3ForkInfo(uint8 forkId) + internal + pure + override + returns (address factory, bytes32 initHash, uint32 callbackSelector) + { + if (forkId == uniswapV3ForkId) { + factory = uniswapV3InkFactory; + initHash = uniswapV3InitHash; + callbackSelector = uint32(IUniswapV3Callback.uniswapV3SwapCallback.selector); + } else { + revert UnknownForkId(forkId); + } + } +} diff --git a/src/chains/Ink/MetaTxn.sol b/src/chains/Ink/MetaTxn.sol new file mode 100644 index 00000000..e7492fd2 --- /dev/null +++ b/src/chains/Ink/MetaTxn.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.8.25; + +import {InkMixin} from "./Common.sol"; +import {SettlerMetaTxn} from "../../SettlerMetaTxn.sol"; + +import {IERC20} from "@forge-std/interfaces/IERC20.sol"; +import {ISignatureTransfer} from "@permit2/interfaces/ISignatureTransfer.sol"; +import {ISettlerActions} from "../../ISettlerActions.sol"; + +// Solidity inheritance is stupid +import {SettlerAbstract} from "../../SettlerAbstract.sol"; +import {SettlerBase} from "../../SettlerBase.sol"; +import {AbstractContext} from "../../Context.sol"; + +/// @custom:security-contact security@0x.org +contract InkSettlerMetaTxn is SettlerMetaTxn, InkMixin { + constructor(bytes20 gitCommit) SettlerMetaTxn(gitCommit) {} + + function _dispatchVIP(uint256 action, bytes calldata data, bytes calldata sig) + internal + override + DANGEROUS_freeMemory + returns (bool) + { + if (super._dispatchVIP(action, data, sig)) { + return true; + } else if (action == uint32(ISettlerActions.METATXN_MAVERICKV2_VIP.selector)) { + ( + address recipient, + bytes32 salt, + bool tokenAIn, + ISignatureTransfer.PermitTransferFrom memory permit, + uint256 minBuyAmount + ) = abi.decode(data, (address, bytes32, bool, ISignatureTransfer.PermitTransferFrom, uint256)); + + sellToMaverickV2VIP(recipient, salt, tokenAIn, permit, sig, minBuyAmount); + } else { + return false; + } + return true; + } + + // Solidity inheritance is stupid + function _dispatch(uint256 i, uint256 action, bytes calldata data) + internal + override(SettlerAbstract, SettlerBase, InkMixin) + returns (bool) + { + return super._dispatch(i, action, data); + } + + function _msgSender() internal view override(SettlerMetaTxn, AbstractContext) returns (address) { + return super._msgSender(); + } +} diff --git a/src/chains/Ink/TakerSubmitted.sol b/src/chains/Ink/TakerSubmitted.sol new file mode 100644 index 00000000..8d267d4f --- /dev/null +++ b/src/chains/Ink/TakerSubmitted.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.8.25; + +import {InkMixin} from "./Common.sol"; +import {Settler} from "../../Settler.sol"; + +import {IERC20} from "@forge-std/interfaces/IERC20.sol"; +import {ISignatureTransfer} from "@permit2/interfaces/ISignatureTransfer.sol"; +import {ISettlerActions} from "../../ISettlerActions.sol"; + +// Solidity inheritance is stupid +import {SettlerAbstract} from "../../SettlerAbstract.sol"; +import {SettlerBase} from "../../SettlerBase.sol"; +import {Permit2PaymentAbstract} from "../../core/Permit2PaymentAbstract.sol"; +import {AbstractContext} from "../../Context.sol"; + +/// @custom:security-contact security@0x.org +contract InkSettler is Settler, InkMixin { + constructor(bytes20 gitCommit) Settler(gitCommit) {} + + function _dispatchVIP(uint256 action, bytes calldata data) internal override DANGEROUS_freeMemory returns (bool) { + if (super._dispatchVIP(action, data)) { + return true; + } else if (action == uint32(ISettlerActions.MAVERICKV2_VIP.selector)) { + ( + address recipient, + bytes32 salt, + bool tokenAIn, + ISignatureTransfer.PermitTransferFrom memory permit, + bytes memory sig, + uint256 minBuyAmount + ) = abi.decode(data, (address, bytes32, bool, ISignatureTransfer.PermitTransferFrom, bytes, uint256)); + + sellToMaverickV2VIP(recipient, salt, tokenAIn, permit, sig, minBuyAmount); + } else { + return false; + } + return true; + } + + // Solidity inheritance is stupid + function _isRestrictedTarget(address target) + internal + pure + override(Settler, Permit2PaymentAbstract) + returns (bool) + { + return super._isRestrictedTarget(target); + } + + function _dispatch(uint256 i, uint256 action, bytes calldata data) + internal + override(SettlerAbstract, SettlerBase, InkMixin) + returns (bool) + { + return super._dispatch(i, action, data); + } + + function _msgSender() internal view override(Settler, AbstractContext) returns (address) { + return super._msgSender(); + } +} From c5b4d531d11e25f502025f54a7ad707f4c4f5fcf Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Wed, 18 Dec 2024 13:28:29 -0500 Subject: [PATCH 16/23] WIP: Ink chain --- README.md | 2 +- api_secrets.json.template | 3 +++ script/SafeConfig.sol | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1f59def0..207790de 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ your integration. * `0x0000000000001fF3684f28c67538d4D072C22734` on chains supporting the Cancun hardfork (Ethereum Mainnet, Ethereum Sepolia, Polygon, Base, Optimism, - Arbitrum, Blast, Bnb, Mode, World Chain, Gnosis, Fantom Sonic) + Arbitrum, Blast, Bnb, Mode, World Chain, Gnosis, Fantom Sonic, Ink) * `0x0000000000005E88410CcDFaDe4a5EfaE4b49562` on chains supporting the Shanghai hardfork (Avalanche, Scroll, Mantle, Taiko) * `0x000000000000175a8b9bC6d539B3708EEd92EA6c` on chains supporting the London diff --git a/api_secrets.json.template b/api_secrets.json.template index c0aef943..dea7cdc0 100644 --- a/api_secrets.json.template +++ b/api_secrets.json.template @@ -64,5 +64,8 @@ "sonic": { "etherscanKey": "", "rpcUrl": "" + }, + "ink": { + "rpcUrl": "" } } diff --git a/script/SafeConfig.sol b/script/SafeConfig.sol index b03d9e27..e03bb761 100644 --- a/script/SafeConfig.sol +++ b/script/SafeConfig.sol @@ -23,6 +23,7 @@ library SafeConfig { || block.chainid == 34443 // mode || block.chainid == 42161 // arbitrum || block.chainid == 43114 // avalanche + || block.chainid == 57073 // ink || block.chainid == 59144 // linea || block.chainid == 81457 // blast || block.chainid == 167000 // taiko @@ -49,6 +50,7 @@ library SafeConfig { || block.chainid == 34443 // mode || block.chainid == 42161 // arbitrum || block.chainid == 43114 // avalanche + || block.chainid == 57073 // ink || block.chainid == 59144 // linea || block.chainid == 81457 // blast || block.chainid == 167000 // taiko From 1a23768adca194ae3910d96e23c336c8d9987f7e Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Wed, 18 Dec 2024 13:33:22 -0500 Subject: [PATCH 17/23] WIP: Ink chain --- chain_config.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chain_config.json b/chain_config.json index b5e3853c..e582a4f3 100644 --- a/chain_config.json +++ b/chain_config.json @@ -444,8 +444,8 @@ "ink": { "chainId": 57073, "displayName": "Ink", - "isShanghai": null, - "isCancun": null, + "isShanghai": true, + "isCancun": true, "extraFlags": "--legacy", "gasMultiplierPercent": 200, "minGasPriceGwei": 1, From 1450632842f6f85a8eff65ccaabe5002a08ac36e Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Wed, 18 Dec 2024 15:28:03 -0500 Subject: [PATCH 18/23] WIP: Sourcify does not support Ink --- sh/common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sh/common.sh b/sh/common.sh index d803083b..fa2f5922 100644 --- a/sh/common.sh +++ b/sh/common.sh @@ -92,7 +92,7 @@ function verify_contract { else forge verify-contract --watch --chain $chainid --verifier custom --verifier-api-key "$(get_api_secret etherscanKey)" --verifier-url "$(get_config etherscanApi)" --constructor-args "$_verify_constructor_args" "$_verify_deployed_address" "$_verify_source_path" fi - if (( chainid != 146 )) && (( chainid != 480 )) && (( chainid != 81457 )) && (( chainid != 167000 )); then # Sourcify doesn't support Sonic, World Chain, Blast, or Taiko + if (( chainid != 146 )) && (( chainid != 480 )) && (( chainid == 57073 )) && (( chainid != 81457 )) && (( chainid != 167000 )); then # Sourcify doesn't support Sonic, World Chain, Blast, Taiko, or Ink forge verify-contract --watch --chain $chainid --verifier sourcify --constructor-args "$_verify_constructor_args" "$_verify_deployed_address" "$_verify_source_path" fi } From 26c60138f5279ec91b0521dfab46a031b3924c21 Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Wed, 18 Dec 2024 15:37:20 -0500 Subject: [PATCH 19/23] Deploy AllowanceHolder to Ink --- chain_config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chain_config.json b/chain_config.json index e582a4f3..1a52358b 100644 --- a/chain_config.json +++ b/chain_config.json @@ -463,7 +463,7 @@ }, "deployment": { "deployer": null, - "allowanceHolder": null + "allowanceHolder": "0x0000000000001fF3684f28c67538d4D072C22734" }, "blockscoutApi": "https://explorer.inkonchain.com/api" } From 0a29ef51e87077e11f646e9a081d9c92f582baa6 Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Wed, 18 Dec 2024 15:48:35 -0500 Subject: [PATCH 20/23] Add UniswapV3 on Ink --- CHANGELOG.md | 2 ++ src/core/univ3forks/UniswapV3.sol | 1 + 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 334ff2de..f433f103 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,8 @@ Master list of UniV3 forks: * Add `DODOV1` action to Polygon * Add `DODOV1` action to Scroll * Add `rebateClaimer()(address)` function on Mainnet Settlers for gas rebate program +* Deploy Settler to Ink chain + * Add UniswapV3 UniV3 fork to Ink ## 2024-12-14 diff --git a/src/core/univ3forks/UniswapV3.sol b/src/core/univ3forks/UniswapV3.sol index 2fb3d454..1946957c 100644 --- a/src/core/univ3forks/UniswapV3.sol +++ b/src/core/univ3forks/UniswapV3.sol @@ -14,6 +14,7 @@ address constant uniswapV3TaikoFactory = 0x75FC67473A91335B5b8F8821277262a13B38c address constant uniswapV3WorldChainFactory = 0x7a5028BDa40e7B173C278C5342087826455ea25a; address constant uniswapV3GnosisFactory = 0xe32F7dD7e3f098D518ff19A22d5f028e076489B1; address constant uniswapV3SonicFactory = 0xcb2436774C3e191c85056d248EF4260ce5f27A9D; +address constant uniswapV3InkFactory = 0x640887A9ba3A9C53Ed27D0F7e8246A4F933f3424; bytes32 constant uniswapV3InitHash = 0xe34f199b19b2b4f47f68442619d555527d244f78a3297ea89325f843f87b8b54; uint8 constant uniswapV3ForkId = 0; From 1b6c4875fcf8d47f580cfc19770195f91fed789d Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Wed, 18 Dec 2024 15:48:43 -0500 Subject: [PATCH 21/23] Compilation errors --- src/chains/Ink/Common.sol | 2 +- src/chains/Ink/MetaTxn.sol | 10 ---------- src/chains/Ink/TakerSubmitted.sol | 11 ----------- 3 files changed, 1 insertion(+), 22 deletions(-) diff --git a/src/chains/Ink/Common.sol b/src/chains/Ink/Common.sol index 99ce04be..5cdcaf7e 100644 --- a/src/chains/Ink/Common.sol +++ b/src/chains/Ink/Common.sol @@ -28,7 +28,7 @@ abstract contract InkMixin is FreeMemory, SettlerBase { function _dispatch(uint256 i, uint256 action, bytes calldata data) internal virtual - override(SettlerAbstract, SettlerBase) + override( /* SettlerAbstract, */ SettlerBase) DANGEROUS_freeMemory returns (bool) { diff --git a/src/chains/Ink/MetaTxn.sol b/src/chains/Ink/MetaTxn.sol index e7492fd2..f372075e 100644 --- a/src/chains/Ink/MetaTxn.sol +++ b/src/chains/Ink/MetaTxn.sol @@ -25,16 +25,6 @@ contract InkSettlerMetaTxn is SettlerMetaTxn, InkMixin { { if (super._dispatchVIP(action, data, sig)) { return true; - } else if (action == uint32(ISettlerActions.METATXN_MAVERICKV2_VIP.selector)) { - ( - address recipient, - bytes32 salt, - bool tokenAIn, - ISignatureTransfer.PermitTransferFrom memory permit, - uint256 minBuyAmount - ) = abi.decode(data, (address, bytes32, bool, ISignatureTransfer.PermitTransferFrom, uint256)); - - sellToMaverickV2VIP(recipient, salt, tokenAIn, permit, sig, minBuyAmount); } else { return false; } diff --git a/src/chains/Ink/TakerSubmitted.sol b/src/chains/Ink/TakerSubmitted.sol index 8d267d4f..7cb3bbea 100644 --- a/src/chains/Ink/TakerSubmitted.sol +++ b/src/chains/Ink/TakerSubmitted.sol @@ -21,17 +21,6 @@ contract InkSettler is Settler, InkMixin { function _dispatchVIP(uint256 action, bytes calldata data) internal override DANGEROUS_freeMemory returns (bool) { if (super._dispatchVIP(action, data)) { return true; - } else if (action == uint32(ISettlerActions.MAVERICKV2_VIP.selector)) { - ( - address recipient, - bytes32 salt, - bool tokenAIn, - ISignatureTransfer.PermitTransferFrom memory permit, - bytes memory sig, - uint256 minBuyAmount - ) = abi.decode(data, (address, bytes32, bool, ISignatureTransfer.PermitTransferFrom, bytes, uint256)); - - sellToMaverickV2VIP(recipient, salt, tokenAIn, permit, sig, minBuyAmount); } else { return false; } From 9c9b96f1c296240ecb248b09bb108c7bf760ca4a Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Wed, 18 Dec 2024 15:54:34 -0500 Subject: [PATCH 22/23] Optimistically populate Ink chain deployment addresses --- chain_config.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/chain_config.json b/chain_config.json index 1a52358b..d56f6daf 100644 --- a/chain_config.json +++ b/chain_config.json @@ -457,12 +457,12 @@ "apiUrl": "https://safe-transaction-ink.safe.global/api" }, "governance": { - "upgradeSafe": null, - "deploymentSafe": null, - "pause": null + "upgradeSafe": "0xf36b9f50E59870A24F42F9Ba43b2aD0A4b8f2F51", + "deploymentSafe": "0x8E5DE7118a596E99B0563D3022039c11927f4827", + "pause": "0x1CeC01DC0fFEE5eB5aF47DbEc1809F2A7c601C30" }, "deployment": { - "deployer": null, + "deployer": "0x00000000000004533Fe15556B1E086BB1A72cEae", "allowanceHolder": "0x0000000000001fF3684f28c67538d4D072C22734" }, "blockscoutApi": "https://explorer.inkonchain.com/api" From 953eda50142c7e9d513e1c6af37d65e1963db1d3 Mon Sep 17 00:00:00 2001 From: Duncan Townsend Date: Wed, 18 Dec 2024 17:37:49 -0500 Subject: [PATCH 23/23] Typo --- sh/common.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sh/common.sh b/sh/common.sh index fa2f5922..2361d016 100644 --- a/sh/common.sh +++ b/sh/common.sh @@ -92,7 +92,7 @@ function verify_contract { else forge verify-contract --watch --chain $chainid --verifier custom --verifier-api-key "$(get_api_secret etherscanKey)" --verifier-url "$(get_config etherscanApi)" --constructor-args "$_verify_constructor_args" "$_verify_deployed_address" "$_verify_source_path" fi - if (( chainid != 146 )) && (( chainid != 480 )) && (( chainid == 57073 )) && (( chainid != 81457 )) && (( chainid != 167000 )); then # Sourcify doesn't support Sonic, World Chain, Blast, Taiko, or Ink + if (( chainid != 146 )) && (( chainid != 480 )) && (( chainid != 57073 )) && (( chainid != 81457 )) && (( chainid != 167000 )); then # Sourcify doesn't support Sonic, World Chain, Blast, Taiko, or Ink forge verify-contract --watch --chain $chainid --verifier sourcify --constructor-args "$_verify_constructor_args" "$_verify_deployed_address" "$_verify_source_path" fi }