diff --git a/CHANGELOG.md b/CHANGELOG.md index 8684f0b5..6c9d444a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,9 +13,12 @@ Master list of UniV3 forks: 10. BladeSwap (Algebra-like) 11. Fenix (Algebra-like) 12. ZebraV3 + 13. Lynex (Algebra-like) --- +* Deployed Settler to Linea +* Added Lynex Algebra-style UniV3 fork to Linea * Update Velodrome Slipstream factory address (and inithash) to migrated one ## 2024-07-15 diff --git a/README.md b/README.md index d8654ae9..bf79ee73 100644 --- a/README.md +++ b/README.md @@ -1038,8 +1038,10 @@ like deploying a new `Settler`. `0x1CeC01DC0fFEE5eB5aF47DbEc1809F2A7c601C30` (ice cold coffees) is the address of the pauser contract. It's at the same address on all chains unless somebody screwed up the vanity addresses and didn't update this document. On Linea, the address of the pauser contract is -`0x2F01FA51F49526da65E70A60EFDDD8e12077D120` because whoever did their EVM -compatibility documentation documented the opposite of the truth 🤬. +`0xBE71A746C7AE0f9D18E6DB4f71d09732B0Ee5b9c` because whoever did their EVM +compatibility documentation documented the opposite of the truth 🤬. When Linea +adopts the Shanghai hardfork (`PUSH0`), remove the preceeding sentence from this +document. 0. Go to that address on the relevant block explorer. diff --git a/chain_config.json b/chain_config.json index dc9deee9..4b585931 100644 --- a/chain_config.json +++ b/chain_config.json @@ -337,10 +337,12 @@ }, "governance": { "upgradeSafe": "0xf36b9f50E59870A24F42F9Ba43b2aD0A4b8f2F51", - "deploymentSafe": "0x8E5DE7118a596E99B0563D3022039c11927f4827" + "deploymentSafe": "0x8E5DE7118a596E99B0563D3022039c11927f4827", + "pause": "0xBE71A746C7AE0f9D18E6DB4f71d09732B0Ee5b9c" }, "deployment": { - "allowanceHolder": "VANITY ADDRESS IS BURNED" + "deployer": "0x00000000000004533Fe15556B1E086BB1A72cEae", + "allowanceHolder": "0x000000000000175a8b9bC6d539B3708EEd92EA6c" }, "etherscanApi": "https://api.lineascan.build/api" }, diff --git a/secrets.json.template b/secrets.json.template index 1adf067c..359f438e 100644 --- a/secrets.json.template +++ b/secrets.json.template @@ -18,5 +18,10 @@ "address": "0x0000000000005E88410CcDFaDe4a5EfaE4b49562", "deployer": "0x4f05532AD8c7C87C22Bd793661DB6A174e09cbb5", "key": "" + }, + "allowanceHolderLondon": { + "address": "0x000000000000175a8b9bC6d539B3708EEd92EA6c", + "deployer": "0x5c50b56D83Db01245d4B7db9eAc9199c9CdbAEE1", + "key": "" } } diff --git a/sh/address.sh b/sh/address.sh index caca5ffe..6d8a0a3e 100755 --- a/sh/address.sh +++ b/sh/address.sh @@ -172,7 +172,11 @@ function create3 { declare -r salt # this is the "create3" shim inithash - declare -r inithash='0x3bf3f97f0be1e2c00023033eefeb4fc062ac552ff36778b17060d90b6764902f' + if [[ $(get_config isShanghai) != [Tt]rue ]] ; then + declare -r inithash='0x1774bbdc4a308eaf5967722c7a4708ea7a3097859cb8768a10611448c29981c3' + else + declare -r inithash='0x3bf3f97f0be1e2c00023033eefeb4fc062ac552ff36778b17060d90b6764902f' + fi declare shim shim="$(cast concat-hex 0xff "$factory" "$salt" "$inithash")" diff --git a/sh/common_safe.sh b/sh/common_safe.sh index 2a31996f..bc5d4e6d 100644 --- a/sh/common_safe.sh +++ b/sh/common_safe.sh @@ -12,75 +12,12 @@ declare deployer_address deployer_address="$(get_config deployment.deployer)" declare -r deployer_address -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 -r nonce_sig='nonce()(uint256)' declare -i nonce nonce="$(cast abi-decode "$nonce_sig" "$(cast call --rpc-url "$rpc_url" "$safe_address" "$(cast calldata "$nonce_sig")")")" nonce=$((${SAFE_NONCE_INCREMENT:-0} + nonce)) declare -r -i nonce -declare -a owners_array -IFS=';' read -r -a owners_array <<<"$owners" -declare -r -a owners_array - -PS3='Who are you?: ' -declare signer -select signer in "${owners_array[@]}" ; do break ; done -declare -r signer - -if [[ ${signer:-unset} = 'unset' ]] ; then - echo 'I do not know who that is' >&2 - exit 1 -fi - -PS3='What kind of wallet are you using? ' -declare wallet_type -select wallet_type in ledger trezor hot frame ; do break ; done -declare -r wallet_Type - -if [[ ${wallet_type:-unset} = 'unset' ]] ; then - exit 1 -fi - -declare -a wallet_args -case $wallet_type in - 'ledger') - wallet_args=(--ledger) - ;; - 'trezor') - wallet_args=(--trezor) - ;; - 'hot') - wallet_args=(--interactive) - ;; - 'frame') - wallet_args=(--unlocked) - ;; - *) - echo 'Unrecognized wallet type: '"$wallet_type" >&2 - exit 1 - ;; -esac - -if [[ $wallet_type = 'ledger' ]] ; then - IFS='' read -r -e -p 'Ledger wallet HD path (BIP32) [default '"44'/60'/0'/0"']: ' - if [[ ${REPLY:-unset} = 'unset' ]] ; then - wallet_args+=( - --mnemonic-derivation-path "44'/60'/0'/0" - ) - else - wallet_args+=( - --mnemonic-derivation-path "$REPLY" - ) - fi -fi - # calls encoded as operation (always zero) 1 byte # target address 20 bytes # value 32 bytes diff --git a/sh/common_safe_owner.sh b/sh/common_safe_owner.sh new file mode 100644 index 00000000..14cf343b --- /dev/null +++ b/sh/common_safe_owner.sh @@ -0,0 +1,22 @@ +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 + +PS3='Who are you?: ' +declare signer +select signer in "${owners_array[@]}" ; do break ; done +declare -r signer + +echo "$signer" >&2 + +if [[ ${signer:-unset} = 'unset' ]] ; then + echo 'I do not know who that is' >&2 + exit 1 +fi diff --git a/sh/common_secrets.sh b/sh/common_secrets.sh index 3e81e9d6..68607157 100644 --- a/sh/common_secrets.sh +++ b/sh/common_secrets.sh @@ -14,7 +14,7 @@ if [[ $(ls -l secrets.json | cut -d' ' -f1 | cut -d. -f1) != '-rw-------' ]] ; t exit 1 fi -if ! sha256sum -c <<<'24290900be9575d1fb6349098b1c11615a2eac8091bc486bec6cf67239b7846a secrets.json' >/dev/null ; then +if ! sha256sum -c <<<'bb82de121880f1182dbae410b341749e5ac1355954ae6c03151a1826e7bba745 secrets.json' >/dev/null ; then echo 'Secrets are wrong' >&2 exit 1 fi diff --git a/sh/common_wallet_type.sh b/sh/common_wallet_type.sh new file mode 100644 index 00000000..9cc7f538 --- /dev/null +++ b/sh/common_wallet_type.sh @@ -0,0 +1,41 @@ +PS3='What kind of wallet are you using? ' +declare wallet_type +select wallet_type in ledger trezor hot frame ; do break ; done +declare -r wallet_Type + +if [[ ${wallet_type:-unset} = 'unset' ]] ; then + exit 1 +fi + +declare -a wallet_args +case $wallet_type in + 'ledger') + wallet_args=(--ledger) + ;; + 'trezor') + wallet_args=(--trezor) + ;; + 'hot') + wallet_args=(--interactive) + ;; + 'frame') + wallet_args=(--unlocked) + ;; + *) + echo 'Unrecognized wallet type: '"$wallet_type" >&2 + exit 1 + ;; +esac + +if [[ $wallet_type = 'ledger' ]] ; then + IFS='' read -r -e -p 'Ledger wallet HD path (BIP32) [default '"44'/60'/0'/0"']: ' + if [[ ${REPLY:-unset} = 'unset' ]] ; then + wallet_args+=( + --mnemonic-derivation-path "44'/60'/0'/0" + ) + else + wallet_args+=( + --mnemonic-derivation-path "$REPLY" + ) + fi +fi diff --git a/sh/confirm_new_feature.sh b/sh/confirm_new_feature.sh index fb78c2dd..6e6536e9 100755 --- a/sh/confirm_new_feature.sh +++ b/sh/confirm_new_feature.sh @@ -126,6 +126,8 @@ safe_address="$(get_config governance.upgradeSafe)" 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 -r -i feature="$1" shift diff --git a/sh/confirm_new_settler.sh b/sh/confirm_new_settler.sh index 0697c727..0951673a 100755 --- a/sh/confirm_new_settler.sh +++ b/sh/confirm_new_settler.sh @@ -126,6 +126,8 @@ 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 . "$project_root"/sh/common_deploy_settler.sh declare deploy_calldata @@ -168,6 +170,19 @@ else fi declare -r signature +declare safe_url +safe_url="$(get_config safe.apiUrl)" +declare -r safe_url + +if [[ $safe_url = 'NOT SUPPORTED' ]] ; then + declare signature_file + signature_file="$project_root"/settler_confirmation_"$chain_display_name"_"$(git rev-parse --short HEAD)"_"$(tr '[:lower:]' '[:upper:]' <<<"$signer")".txt + declare -r signature_file + echo "$signature" >"$signature_file" + echo "Signature saved to '$signature_file'" >&2 + exit 1 +fi + declare signing_hash signing_hash="$(eip712_hash "$deploy_calldata" 1)" declare -r signing_hash @@ -200,6 +215,6 @@ safe_multisig_transaction="$( declare -r safe_multisig_transaction # call the API -curl --fail -s "$(get_config safe.apiUrl)"'/v1/safes/'"$safe_address"'/multisig-transactions/' -X POST -H 'Content-Type: application/json' --data "$safe_multisig_transaction" +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 diff --git a/sh/deploy_allowanceholder.sh b/sh/deploy_allowanceholder.sh index 7fdb3acb..9e5b208e 100755 --- a/sh/deploy_allowanceholder.sh +++ b/sh/deploy_allowanceholder.sh @@ -143,7 +143,9 @@ export FOUNDRY_OPTIMIZER_RUNS=1000000 forge clean forge create --private-key "$(get_secret allowanceHolder key)" --chain "$(get_config chainId)" --rpc-url "$rpc_url" --gas-price "$gas_price" --gas-limit 4000000 --etherscan-api-key "$(get_api_secret etherscanKey)" --verifier-url "$(get_config etherscanApi)" --verify $(get_config extraFlags) src/allowanceholder/AllowanceHolder.sol:AllowanceHolder -forge verify-contract --watch --chain "$(get_config chainId)" --verifier sourcify --optimizer-runs 1000000 --constructor-args 0x "$(get_secret allowanceHolder address)" src/allowanceholder/AllowanceHolder.sol:AllowanceHolder +if (( chainid != 81457 )) && (( chainid != 59144 )) ; then # sourcify doesn't support Blast or Linea + forge verify-contract --watch --chain "$(get_config chainId)" --verifier sourcify --optimizer-runs 1000000 --constructor-args 0x "$(get_secret allowanceHolder address)" src/allowanceholder/AllowanceHolder.sol:AllowanceHolder +fi echo 'Deployment is complete' >&2 echo 'Add the following to your chain_config.json' >&2 diff --git a/sh/deploy_new_settler.sh b/sh/deploy_new_settler.sh index 766e0cbd..b8547766 100755 --- a/sh/deploy_new_settler.sh +++ b/sh/deploy_new_settler.sh @@ -126,6 +126,12 @@ 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 signer +declare -r signer + +. "$project_root"/sh/common_wallet_type.sh . "$project_root"/sh/common_deploy_settler.sh declare deploy_calldata @@ -136,39 +142,45 @@ declare signing_hash signing_hash="$(eip712_hash "$deploy_calldata" 1)" declare -r signing_hash -declare signatures -signatures="$(curl --fail -s "$(get_config safe.apiUrl)"'/v1/multisig-transactions/'"$signing_hash"'/confirmations/?executed=false' -X GET)" -declare -r signatures +declare safe_url +safe_url="$(get_config safe.apiUrl)" +declare -r safe_url -if (( $(jq -Mr .count <<<"$signatures") != 1 )) ; then - echo 'Bad number of signatures' >&2 - exit 1 -fi +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 HEAD)"_*.txt ; do + signatures+=("$(<"$confirmation")") + done + set -f -declare other_signer -other_signer="$(jq -Mr '.results[0].owner' <<<"$signatures")" -declare -r other_signer -declare other_signature -other_signature="$(jq -Mr '.results[0].signature' <<<"$signatures")" -declare -r other_signature + 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 -declare signer_lower -signer_lower="$(tr '[:upper:]' '[:lower:]' <<<"$signer")" -declare -r signer_lower -declare other_signer_lower -other_signer_lower="$(tr '[:upper:]' '[:lower:]' <<<"$other_signer")" -declare -r other_signer_lower + if (( $(jq -Mr .count <<<"$signatures_json") != 2 )) ; then + echo 'Bad number of signatures' >&2 + exit 1 + fi -declare signature -signature="$(cast concat-hex "$(cast to-uint256 "$signer")" "$(cast hash-zero)" 0x01)" -declare -r signature + if [ "$(jq -Mr '.results[1].owner' <<<"$signatures_json" | tr '[:lower:]' '[:upper:]')" \< "$(jq -Mr '.results[0].owner' <<<"$signatures_json" | tr '[:lower:]' '[:upper:]')" ] ; 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 -if [ "$other_signer_lower" \< "$signer_lower" ] ; then - packed_signatures="$(cast concat-hex "$other_signature" "$signature")" -else - packed_signatures="$(cast concat-hex "$signature" "$other_signature")" -fi +packed_signatures="$(cast concat-hex "${signatures[@]}")" +declare -r packed_signatures # set minimum gas price to (mostly for Arbitrum and BNB) declare -i min_gas_price diff --git a/sh/upgrade_deployer.sh b/sh/upgrade_deployer.sh index 6ef61c6f..901a40fa 100755 --- a/sh/upgrade_deployer.sh +++ b/sh/upgrade_deployer.sh @@ -187,7 +187,7 @@ if [[ ${1:-unset} = 'deploy' ]] ; then echo '' >&2 declare -a gas_price_args - if (( chainid != 56 )) ; then + if (( chainid != 56 )) && (( chainid != 534352 )) ; then gas_price_args=( --gas-price $gas_price --priority-gas-price $gas_price ) @@ -208,6 +208,8 @@ if [[ ${1:-unset} = 'confirm' ]] ; then declare -r safe_address . "$project_root"/sh/common_safe.sh + . "$project_root"/sh/common_safe_owner.sh + . "$project_root"/sh/common_wallet_type.sh if [[ ${deployed_address-unset} = 'unset' ]] ; then declare deployed_address diff --git a/src/chains/Linea.sol b/src/chains/Linea.sol new file mode 100644 index 00000000..0e259bf0 --- /dev/null +++ b/src/chains/Linea.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import {SettlerBase} from "../SettlerBase.sol"; +import {Settler} from "../Settler.sol"; +import {SettlerMetaTxn} from "../SettlerMetaTxn.sol"; + +import {FreeMemory} from "../utils/FreeMemory.sol"; + +import {ISettlerActions} from "../ISettlerActions.sol"; +import {ISignatureTransfer} from "permit2/src/interfaces/ISignatureTransfer.sol"; +import {UnknownForkId} from "../core/SettlerErrors.sol"; + +import {uniswapV3InitHash, IUniswapV3Callback} from "../core/univ3forks/UniswapV3.sol"; +import {sushiswapV3Factory, sushiswapV3ForkId} from "../core/univ3forks/SushiswapV3.sol"; +import { + pancakeSwapV3Factory, + pancakeSwapV3InitHash, + pancakeSwapV3ForkId, + IPancakeSwapV3Callback +} from "../core/univ3forks/PancakeSwapV3.sol"; +import {IAlgebraCallback} from "../core/univ3forks/Algebra.sol"; +import {lynexFactory, lynexInitHash, lynexForkId} from "../core/univ3forks/Lynex.sol"; + +// Solidity inheritance is stupid +import {SettlerAbstract} from "../SettlerAbstract.sol"; +import {AbstractContext} from "../Context.sol"; +import {Permit2PaymentAbstract} from "../core/Permit2PaymentAbstract.sol"; + +abstract contract LineaMixin is FreeMemory, SettlerBase { + constructor() { + assert(block.chainid == 59144 || block.chainid == 31337); + } + + function _dispatch(uint256 i, bytes4 action, bytes calldata data) + internal + virtual + override + DANGEROUS_freeMemory + returns (bool) + { + return super._dispatch(i, action, data); + } + + function _uniV3ForkInfo(uint8 forkId) + internal + pure + override + returns (address factory, bytes32 initHash, uint32 callbackSelector) + { + if (forkId == pancakeSwapV3ForkId) { + factory = pancakeSwapV3Factory; + initHash = pancakeSwapV3InitHash; + callbackSelector = uint32(IPancakeSwapV3Callback.pancakeV3SwapCallback.selector); + } else if (forkId == sushiswapV3ForkId) { + factory = sushiswapV3Factory; + initHash = uniswapV3InitHash; + callbackSelector = uint32(IUniswapV3Callback.uniswapV3SwapCallback.selector); + } else if (forkId == lynexForkId) { + factory = lynexFactory; + initHash = lynexInitHash; + callbackSelector = uint32(IAlgebraCallback.algebraSwapCallback.selector); + } else { + revert UnknownForkId(forkId); + } + } +} + +/// @custom:security-contact security@0x.org +contract LineaSettler is Settler, LineaMixin { + constructor(bytes20 gitCommit) SettlerBase(gitCommit) {} + + function _dispatchVIP(bytes4 action, bytes calldata data) internal override DANGEROUS_freeMemory returns (bool) { + return super._dispatchVIP(action, data); + } + + // Solidity inheritance is stupid + function _isRestrictedTarget(address target) + internal + pure + override(Settler, Permit2PaymentAbstract) + returns (bool) + { + return super._isRestrictedTarget(target); + } + + function _dispatch(uint256 i, bytes4 action, bytes calldata data) + internal + override(SettlerAbstract, SettlerBase, LineaMixin) + returns (bool) + { + return super._dispatch(i, action, data); + } + + function _msgSender() internal view override(Settler, AbstractContext) returns (address) { + return super._msgSender(); + } +} + +/// @custom:security-contact security@0x.org +contract LineaSettlerMetaTxn is SettlerMetaTxn, LineaMixin { + constructor(bytes20 gitCommit) SettlerBase(gitCommit) {} + + function _dispatchVIP(bytes4 action, bytes calldata data, bytes calldata sig) + internal + override + DANGEROUS_freeMemory + returns (bool) + { + return super._dispatchVIP(action, data, sig); + } + + // Solidity inheritance is stupid + function _dispatch(uint256 i, bytes4 action, bytes calldata data) + internal + override(SettlerAbstract, SettlerBase, LineaMixin) + returns (bool) + { + return super._dispatch(i, action, data); + } + + function _msgSender() internal view override(SettlerMetaTxn, AbstractContext) returns (address) { + return super._msgSender(); + } +} diff --git a/src/core/univ3forks/Lynex.sol b/src/core/univ3forks/Lynex.sol new file mode 100644 index 00000000..7a132351 --- /dev/null +++ b/src/core/univ3forks/Lynex.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +address constant lynexFactory = 0x9A89490F1056A7BC607EC53F93b921fE666A2C48; +bytes32 constant lynexInitHash = 0xc65e01e65f37c1ec2735556a24a9c10e4c33b2613ad486dd8209d465524bc3f4; +uint8 constant lynexForkId = 13; diff --git a/src/utils/Create3.sol b/src/utils/Create3.sol index 7c449f35..cf43d869 100644 --- a/src/utils/Create3.sol +++ b/src/utils/Create3.sol @@ -107,42 +107,51 @@ library Create3 { uint8 private constant _SHIM1_LENGTH = 0x06; uint8 private constant _SHIM_LENGTH = 0x26; bytes32 private constant _SHIM_INITHASH = 0x3bf3f97f0be1e2c00023033eefeb4fc062ac552ff36778b17060d90b6764902f; + bytes32 private constant _SHIM_RUNTIME_HASH = 0xa9549013530fb1542c6fac59b531052d9fd0c0433910571c379618caa172f2cb; uint256 private constant _SHIM0_LONDON = 0x7f36583d54601d573d553d3d37363d34f03d816017573dfd5b5260203df35b30; uint48 private constant _SHIM1_LONDON = 0xff3d52593df3; - bytes32 private constant _SHIM_INITHASH_LONDON = 0x4181fd95643bb6bf1be20faa449de3be679a53ec38d829a0a789397a5d5d4887; - - function _createFromCalldata(bytes32 salt, bytes calldata initCode, uint256 value, uint256 shim0, uint48 shim1) - private - returns (address deployed) - { + bytes32 private constant _SHIM_INITHASH_LONDON = 0x1774bbdc4a308eaf5967722c7a4708ea7a3097859cb8768a10611448c29981c3; + bytes32 private constant _SHIM_RUNTIME_HASH_LONDON = + 0x4181fd95643bb6bf1be20faa449de3be679a53ec38d829a0a789397a5d5d4887; + + function _createFromCalldata( + bytes32 salt, + bytes calldata initCode, + uint256 value, + uint256 shim0, + uint48 shim1, + bytes32 shimRuntimeHash + ) private returns (address deployed) { + address shim; assembly ("memory-safe") { mstore(_SHIM1_LENGTH, shim1) mstore(0x00, shim0) - let shim := create2(0x00, 0x00, _SHIM_LENGTH, salt) + shim := create2(0x00, 0x00, _SHIM_LENGTH, salt) if iszero(shim) { revert(0x00, 0x00) } + if iszero(eq(extcodehash(shim), shimRuntimeHash)) { revert(0x00, 0x00) } let ptr := mload(0x40) calldatacopy(ptr, initCode.offset, initCode.length) if iszero(call(gas(), shim, value, ptr, initCode.length, 0x00, 0x20)) { revert(0x00, 0x00) } deployed := mload(0x00) - // This causes the shim to selfdestruct. On some chains, selfdestruct reverts, consuming - // all available gas. We swallow this revert with `pop` and the 51k gas limit gives a - // 10x multiplier over the expected gas consumption of this call without being *too* - // wasteful when `SELFDESTRUCT` is unimplemented. + // This causes the shim to selfdestruct. On some chains, `SELFDESTRUCT` reverts, + // consuming all available gas. We swallow this revert with `pop` and the 51k gas limit + // gives a 10x multiplier over the expected gas consumption of this call without being + // *too* wasteful when `SELFDESTRUCT` is unimplemented. pop(call(51220, shim, 0x00, 0x00, 0x00, 0x00, 0x00)) } } function createFromCalldata(bytes32 salt, bytes calldata initCode, uint256 value) internal returns (address) { - return _createFromCalldata(salt, initCode, value, _SHIM0, _SHIM1); + return _createFromCalldata(salt, initCode, value, _SHIM0, _SHIM1, _SHIM_RUNTIME_HASH); } function createFromCalldataLondon(bytes32 salt, bytes calldata initCode, uint256 value) internal returns (address) { - return _createFromCalldata(salt, initCode, value, _SHIM0_LONDON, _SHIM1_LONDON); + return _createFromCalldata(salt, initCode, value, _SHIM0_LONDON, _SHIM1_LONDON, _SHIM_RUNTIME_HASH_LONDON); } function createFromCalldata(bytes32 salt, bytes calldata initCode) internal returns (address) { @@ -153,27 +162,38 @@ library Create3 { return createFromCalldataLondon(salt, initCode, 0); } - function _createFromMemory(bytes32 salt, bytes memory initCode, uint256 value, uint256 shim0, uint48 shim1) - private - returns (address deployed) - { + function _createFromMemory( + bytes32 salt, + bytes memory initCode, + uint256 value, + uint256 shim0, + uint48 shim1, + bytes32 shimRuntimeHash + ) private returns (address deployed) { + address shim; assembly ("memory-safe") { mstore(_SHIM1_LENGTH, shim1) mstore(0x00, shim0) - let shim := create2(0x00, 0x00, _SHIM_LENGTH, salt) + shim := create2(0x00, 0x00, _SHIM_LENGTH, salt) if iszero(shim) { revert(0x00, 0x00) } + if iszero(eq(extcodehash(shim), shimRuntimeHash)) { revert(0x00, 0x00) } if iszero(call(gas(), shim, value, add(0x20, initCode), mload(initCode), 0x00, 0x20)) { revert(0x00, 0x00) } deployed := mload(0x00) - pop(call(gas(), shim, 0x00, 0x00, 0x00, 0x00, 0x00)) + + // This causes the shim to selfdestruct. On some chains, `SELFDESTRUCT` reverts, + // consuming all available gas. We swallow this revert with `pop` and the 51k gas limit + // gives a 10x multiplier over the expected gas consumption of this call without being + // *too* wasteful when `SELFDESTRUCT` is unimplemented. + pop(call(51220, shim, 0x00, 0x00, 0x00, 0x00, 0x00)) } } function createFromMemory(bytes32 salt, bytes memory initCode, uint256 value) internal returns (address) { - return _createFromMemory(salt, initCode, value, _SHIM0, _SHIM1); + return _createFromMemory(salt, initCode, value, _SHIM0, _SHIM1, _SHIM_RUNTIME_HASH); } function createFromMemoryLondon(bytes32 salt, bytes memory initCode, uint256 value) internal returns (address) { - return _createFromMemory(salt, initCode, value, _SHIM0_LONDON, _SHIM1_LONDON); + return _createFromMemory(salt, initCode, value, _SHIM0_LONDON, _SHIM1_LONDON, _SHIM_RUNTIME_HASH_LONDON); } function createFromMemory(bytes32 salt, bytes memory initCode) internal returns (address) {