Skip to content
Closed
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
30eeb68
Revert "Abandon Katana chain" (#557)
jparklev May 12, 2026
b0f5378
Katana: delegate to base dispatch and add SushiSwapV3 fork
jparklev May 12, 2026
2f3b0ec
Katana: integration fork test for SushiV3 vbETH/AUSD swap
jparklev May 12, 2026
107c05b
Katana: wire CI integration env + fix stale test comment
jparklev May 12, 2026
1d9a424
Katana: add UNISWAPV3_VIP + slippage revert tests
jparklev May 12, 2026
70c8f60
Simplify Katana SushiV3 changes
jparklev May 12, 2026
d7e8ca2
Add script to redeploy Settlers and reclaim Safe ownership on revived…
jparklev May 19, 2026
9c81b49
revive_chain.sh: drop hardcoded --isolate
jparklev May 20, 2026
09a4c80
Bug! EIP-7825 gas-budget check was a no-op
jparklev May 21, 2026
d8279e9
Extract SafeMultisend script base
jparklev May 21, 2026
546d281
RedeploySettlers: split MultiSend per Safe; allow keyless simulation
jparklev May 21, 2026
08c8093
revive_chain.sh: bc-based balance floor
jparklev May 21, 2026
49dad0c
Katana SushiV3 test: align EVM version with chain_config, use vm.getC…
jparklev May 21, 2026
ccaa774
revive_chain.sh: derive balance floor from sim
jparklev May 22, 2026
54b6169
Address review: EraVm-aware codehash, per-deploy txs, require solvers…
jparklev May 26, 2026
f03592b
revive_chain.sh: derive per-sender balance floor from dry-run sim
jparklev May 26, 2026
ab978ad
Address review: hoist eraVmCompat to SafeMultisend, guard against sil…
jparklev May 26, 2026
c0adf8d
revive_chain.sh: fix balance-floor double-multiplier + partial-parse …
jparklev May 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ jobs:
MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }}
BNB_MAINNET_RPC_URL: ${{ secrets.BNB_MAINNET_RPC_URL }}
PLASMA_MAINNET_RPC_URL: ${{ secrets.PLASMA_MAINNET_RPC_URL }}
KATANA_MAINNET_RPC_URL: ${{ secrets.KATANA_MAINNET_RPC_URL }}
Comment thread
duncancmt marked this conversation as resolved.
ARBITRUM_MAINNET_RPC_URL: ${{ secrets.ARBITRUM_MAINNET_RPC_URL }}
BASE_MAINNET_RPC_URL: ${{ secrets.BASE_MAINNET_RPC_URL }}
MONAD_MAINNET_RPC_URL: ${{ secrets.MONAD_MAINNET_RPC_URL }}
Expand Down
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
### Breaking changes

* SolidlyV3 UniV3 fork removed from Mainnet and Sonic
* Abandon Katana chain (which only had stripped-down Settler anyways)
* `BRIDGE_TO_CCIP` modified to remove the `token` argument that is already included in `ccipSendData`
* `BRIDGE_TO_LAYER_ZERO_OFT` modified to remove the `nativeFee` argument that is already included in `sendData`
* `BRIDGE_ERC20_TO_MAYAN` and `BRIDGE_NATIVE_TO_MAYAN` modified to remove `forwarder` argument. It is now hardcoded.
Expand All @@ -14,6 +13,8 @@

### Non-breaking changes

* Add SushiSwapV3 UniV3 fork on Katana.

## 2026-04-10

### Breaking changes
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ your integration.
* `0x0000000000001fF3684f28c67538d4D072C22734` on chains supporting the Cancun
hardfork (Ethereum mainnet, Ethereum Sepolia testnet, Polygon, Base, Optimism,
Arbitrum, Bnb, World Chain, Fantom Sonic, Ink, Avalanche, Unichain, Berachain,
Scroll, HyperEvm, Plasma, Monad mainnet, Abstract, Linea, Tempo)
Scroll, HyperEvm, Katana, Plasma, Monad mainnet, Abstract, Linea, Tempo)
* `0x0000000000005E88410CcDFaDe4a5EfaE4b49562` on chains supporting the Shanghai
hardfork (Mantle)

Expand Down
4 changes: 4 additions & 0 deletions api_secrets.json.template
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@
"etherscanKey": "",
"rpcUrl": ""
},
"katana": {
"etherscanKey": "",
"rpcUrl": ""
},
"plasma": {
"etherscanKey": "verifyContract",
"rpcUrl": ""
Expand Down
37 changes: 37 additions & 0 deletions chain_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,43 @@
"blockscoutApi": "https://www.hyperscan.com/api",
"etherscanApi": "https://api.etherscan.io/v2/api?chainid=999"
},
"katana": {
"chainId": 747474,
"displayName": "Katana",
"wnative": "0xEE7D8BCFb72bC1880D0Cf19822eB0A2e6577aB62",
"hardfork": {
"shanghai": true,
"cancun": true,
"osaka": false,
Comment thread
duncancmt marked this conversation as resolved.
"eraVm": false
},
"extraFlags": "--legacy",
"extraScriptFlags": "--isolate",
"gasMultiplierPercent": 200,
"minGasPriceGwei": 1,
"safe": {
"toehold": "0x914d7Fec6aaC8cd542e72Bca78B30650d45643d7",
"singleton": "0xfb1bffC9d739B8D520DaF37dF666da4C687191EA",
"factory": "0xC22834581EbC8527d974F8a1c97E1bEA4EF910BC",
"fallback": "0x017062a1dE2FE6b99BE3d9d37841FeD19F573804",
"multiCall": "0xA1dabEF33b3B82c7814B6D82A79e50F4AC44102B",
"apiUrl": "https://api.safe.global/tx-service/katana/api"
},
"governance": {
"upgradeSafe": "0xf36b9f50E59870A24F42F9Ba43b2aD0A4b8f2F51",
"deploymentSafe": "0x8E5DE7118a596E99B0563D3022039c11927f4827",
"pause": "0x1CeC01DC0fFEE5eB5aF47DbEc1809F2A7c601C30",
"daoSafe": "0x23030a6124E871F4744Cb9bc14D519b1f033FFe3"
},
"deployment": {
"allowanceHolder": "0x0000000000001fF3684f28c67538d4D072C22734",
"deployer": "0x00000000000004533Fe15556B1E086BB1A72cEae",
"forwardingMultiCall": "0x00000000000000CF9E3c5A26621af382fA17f24f",
"crossChainFactory": "0x00000000000000304861c3aDfb80dd5ebeC96325"
},
"etherscanApi": "https://api.etherscan.io/v2/api?chainid=747474",
"blockscoutApi": "https://explorer.katanarpc.com/api"
},
"plasma": {
"chainId": 9745,
"displayName": "Plasma",
Expand Down
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ mainnet = "${MAINNET_RPC_URL}"
bnb = "${BNB_MAINNET_RPC_URL}"
plasma = "${PLASMA_MAINNET_RPC_URL}"
arbitrum = "${ARBITRUM_MAINNET_RPC_URL}"
katana = "${KATANA_MAINNET_RPC_URL}"
base = "${BASE_MAINNET_RPC_URL}"
monad = "${MONAD_MAINNET_RPC_URL}"

Expand Down
135 changes: 7 additions & 128 deletions script/DeploySafes.s.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

import {Script} from "@forge-std/Script.sol";
import {SafeMultisend, ISafeExecute, ISafeOwners} from "./SafeMultisend.sol";
import {Vm, VmSafe} from "@forge-std/Vm.sol";
import {AddressDerivation} from "src/utils/AddressDerivation.sol";
import {Create3} from "src/utils/Create3.sol";
Expand Down Expand Up @@ -31,62 +31,17 @@ interface ISafeSetup {
) external;
}

interface ISafeExecute {
enum Operation {
Call,
DelegateCall
}

function execTransaction(
address to,
uint256 value,
bytes calldata data,
Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address refundReceiver,
bytes calldata signatures
) external payable returns (bool);
}

interface ISafeOwners {
function addOwnerWithThreshold(address owner, uint256 _threshold) external;
function removeOwner(address prevOwner, address owner, uint256 _threshold) external;
function changeThreshold(uint256 _threshold) external;
function getOwners() external view returns (address[] memory);
}

interface ISafeModule {
function enableModule(address module) external;
}

interface ISafeMulticall {
/// @dev Sends multiple transactions and reverts all if one fails.
/// @param transactions Encoded transactions. Each transaction is encoded as a packed bytes of
/// operation has to be uint8(0) in this version (=> 1 byte),
/// to as a address (=> 20 bytes),
/// value as a uint256 (=> 32 bytes),
/// data length as a uint256 (=> 32 bytes),
/// data as bytes.
/// see abi.encodePacked for more information on packed encoding
/// @notice The code is for most part the same as the normal MultiSend (to keep compatibility),
/// but reverts if a transaction tries to use a delegatecall.
/// @notice This method is payable as delegatecalls keep the msg.value from the previous call
/// If the calling method (e.g. execTransaction) received ETH this would revert otherwise
function multiSend(bytes memory transactions) external payable;
}

contract DeploySafes is Script {
contract DeploySafes is SafeMultisend {
bytes32 internal constant singletonHash = 0x21842597390c4c6e3c1239e434a682b054bd9548eee5e9b1d6a4482731023c0f;
bytes32 internal constant singletonHashEraVm = 0xe2ca068330339d608367d83a0b25545efe39e619098597699ab8ff828cb1ddd8;
bytes32 internal constant factoryHash = 0x337d7f54be11b6ed55fef7b667ea5488db53db8320a05d1146aa4bd169a39a9b;
bytes32 internal constant factoryHashEraVm = 0x55daa5d390d283edbc5fa835bd53befce45179c758feaac8c149a95850d0a6b6;
bytes32 internal constant fallbackHash = 0x03e69f7ce809e81687c69b19a7d7cca45b6d551ffdec73d9bb87178476de1abf;
bytes32 internal constant fallbackHashEraVm = 0x017e9a83d5513f503fb85274f4d1ad1811040d7caa31772750ffb08638c28fbb;
bytes32 internal constant multicallHash = 0xa9865ac2d9c7a1591619b188c4d88167b50df6cc0c5327fcbd1c8c75f7c066ad;
bytes32 internal constant multicallHashEraVm = 0x064ddbf252714bcd4cb79f679e8c12df96d998ce07bbb13b3118c1dbf4a31942;
uint256 internal constant safeDeploymentSaltNonce = 0;

// This is derived from calling `proxyCreationCode()` on the factory and then decoding the EraVm-style encoded
Expand All @@ -107,69 +62,6 @@ contract DeploySafes is Script {
SafeBytecodes safeBytecodes;
}

function _encodeMultisend(bytes[] memory calls) internal view returns (bytes memory result) {
// The Gnosis multicall contract uses a very obnoxious packed encoding
// that is very similar to, but not exactly the same as
// `abi.encodePacked`
assembly ("memory-safe") {
result := mload(0x40)
mstore(add(0x04, result), 0x8d80ff0a) // selector for `multiSend(bytes)`
mstore(add(0x24, result), 0x20)
let bytes_length_ptr := add(0x44, result)
mstore(bytes_length_ptr, 0x00)
for {
let i := add(0x20, calls)
let end := add(i, shl(0x05, mload(calls)))
let dst := add(0x20, bytes_length_ptr)
} lt(i, end) { i := add(0x20, i) } {
let src := mload(i)
let len := mload(src)
src := add(0x20, src)

// We're using the old identity precompile version instead of
// the MCOPY opcode version because I don't want to have to deal
// with maintaining two versions of this
if or(xor(returndatasize(), len), iszero(staticcall(gas(), 0x04, src, len, dst, len))) {
invalid()
}

dst := add(dst, len)
mstore(bytes_length_ptr, add(len, mload(bytes_length_ptr)))
}
mstore(result, add(0x44, mload(bytes_length_ptr)))
mstore(0x40, add(0x20, add(mload(result), result)))
}
}

function _encodeMultisend(address safe, bytes memory call) internal pure returns (bytes memory) {
return abi.encodePacked(
uint8(ISafeExecute.Operation.Call),
safe,
uint256(0), // value
call.length,
call
);
}

function _encodeChangeOwners(address safe, uint256 threshold, address oldOwner, address[] memory newOwners)
internal
view
returns (bytes[] memory)
{
bytes[] memory subCalls = new bytes[](newOwners.length + 1);
for (uint256 i; i < newOwners.length; i++) {
bytes memory data =
abi.encodeCall(ISafeOwners.addOwnerWithThreshold, (newOwners[newOwners.length - i - 1], 1));
subCalls[i] = _encodeMultisend(safe, data);
}
{
bytes memory data =
abi.encodeCall(ISafeOwners.removeOwner, (newOwners[newOwners.length - 1], oldOwner, threshold));
subCalls[newOwners.length] = _encodeMultisend(safe, data);
}
return subCalls;
}

modifier eraVmCompat(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this modifier should move into the base contract?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulled it into SafeMultisend here ab978ad. RedeploySettlers doesn't use it anywhere yet, since Katana isn't EraVm. Want me to also wire it here so future revives that do will be ready?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, please! The more homogeneity there is, the lower the cognitive burden, and the lower the chance of mistakes.

bool isEraVm,
uint256 privateKey,
Expand Down Expand Up @@ -405,10 +297,7 @@ contract DeploySafes is Script {
safeFallback.codehash == (safeCompatConfig.isEraVm ? fallbackHashEraVm : fallbackHash),
"Safe fallback codehash"
);
require(
safeMulticall.codehash == (safeCompatConfig.isEraVm ? multicallHashEraVm : multicallHash),
"Safe multicall codehash"
);
_assertMulticallCodehash(safeMulticall);

require(Feature.unwrap(takerSubmittedFeature) == 2, "wrong taker-submitted feature (tokenId)");
require(Feature.unwrap(metaTxFeature) == 3, "wrong metatransaction feature (tokenId)");
Expand Down Expand Up @@ -619,14 +508,9 @@ contract DeploySafes is Script {
deploySetupCalls[3] = _encodeMultisend(deployerProxy, intentDeployCall);
deploySetupCalls[4] = _encodeMultisend(deployerProxy, bridgeDeployCall);
{
address prevSolver = 0x0000000000000000000000000000000000000001;
for (uint256 i; i < solvers.length; i++) {
address solver = solvers[i];
deploySetupCalls[i + 5] = _encodeMultisend(
predictedIntentSettler,
abi.encodeWithSignature("setSolver(address,address,bool)", prevSolver, solver, true)
);
prevSolver = solver;
bytes[] memory solverCalls = _encodeSolversMultisend(predictedIntentSettler, solvers);
for (uint256 i; i < solverCalls.length; i++) {
deploySetupCalls[i + 5] = solverCalls[i];
}
}
for (uint256 i; i < changeOwnersCalls.length; i++) {
Expand Down Expand Up @@ -719,12 +603,7 @@ contract DeploySafes is Script {
gasSplits[10] = gasleft();
_stopBroadcast(safeCompatConfig);

{
uint256 gasPrev = gasSplits[0];
for (uint256 i = 1; i < gasSplits.length; i++) {
require(gasPrev + 15728639 > (gasPrev = gasSplits[i]), "transaction is likely to exceed EIP-7825 limit");
}
}
_assertEip7825(gasSplits);

require(deployedModule == iceColdCoffee, "deployment/prediction mismatch");
require(deployedDeploymentSafe == deploymentSafe, "deployed safe/predicted safe mismatch");
Expand Down
Loading
Loading