diff --git a/typescript/infra/config/environments/mainnet3/owners.ts b/typescript/infra/config/environments/mainnet3/owners.ts
index 5a4e941e3..755a8b491 100644
--- a/typescript/infra/config/environments/mainnet3/owners.ts
+++ b/typescript/infra/config/environments/mainnet3/owners.ts
@@ -42,6 +42,14 @@ export const safes: ChainMap
= {
blast: '0xaCD1865B262C89Fb0b50dcc8fB095330ae8F35b5',
linea: '0xaCD1865B262C89Fb0b50dcc8fB095330ae8F35b5',
mode: '0xaCD1865B262C89Fb0b50dcc8fB095330ae8F35b5',
+ ancient8: '0xD2BFA0F0654E3f2139b8cDC56c32eeC54D32b133',
+ taiko: '0xa4864301d3fa2a3e68256309F9F0F570270a1BD0',
+ fraxtal: '0x66e9f52800E9F89F0569fddc594Acd5EE609f762',
+ sei: '0xCed197FBc360C26C19889745Cf73511b71D03d5D',
+ redstone: '0xa1a50ff5FD859558E1899fEC5C3064483177FA23',
+ mantle: '0x8aFE6EECc6CcB02aA20DA8Fff7d29aadEBbc2DCd',
+ bob: '0x9e2fe7723b018d02cDE4f5cC1A9bC9C65b922Fc8',
+ zetachain: '0x9d399876522Fc5C044D048594de399A2349d6026',
};
export const DEPLOYER = '0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba';
diff --git a/typescript/infra/config/environments/mainnet3/safe/safeSigners.json b/typescript/infra/config/environments/mainnet3/safe/safeSigners.json
new file mode 100644
index 000000000..d66b992ab
--- /dev/null
+++ b/typescript/infra/config/environments/mainnet3/safe/safeSigners.json
@@ -0,0 +1,13 @@
+{
+ "signers": [
+ "0xa7ECcdb9Be08178f896c26b7BbD8C3D4E844d9Ba",
+ "0xc3E966E79eF1aA4751221F55fB8A36589C24C0cA",
+ "0x3b7f8f68A4FD0420FeA2F42a1eFc53422f205599",
+ "0x88436919fAa2310d32A36D20d13E0a441D24fAc3",
+ "0x003DDD9eEAb62013b7332Ab4CC6B10077a8ca961",
+ "0xd00d6A31485C93c597D1d8231eeeE0ed17B9844B",
+ "0x483fd7284A696343FEc0819DDF2cf7E06E8A06E5",
+ "0x5b73A98165778BCCE72979B4EE3faCdb31728b8E",
+ "0x5dd9a0814022A61777938263308EBB336174f13D"
+ ]
+}
diff --git a/typescript/infra/package.json b/typescript/infra/package.json
index a7d4e2a20..3196e4c8e 100644
--- a/typescript/infra/package.json
+++ b/typescript/infra/package.json
@@ -18,6 +18,7 @@
"@hyperlane-xyz/sdk": "4.1.0",
"@hyperlane-xyz/utils": "4.1.0",
"@nomiclabs/hardhat-etherscan": "^3.0.3",
+ "@safe-global/protocol-kit": "^4.0.2",
"@solana/web3.js": "^1.78.0",
"asn1.js": "5.4.1",
"aws-kms-ethers-signer": "^0.1.3",
diff --git a/typescript/infra/scripts/agent-utils.ts b/typescript/infra/scripts/agent-utils.ts
index 2046d4821..93e25ed9f 100644
--- a/typescript/infra/scripts/agent-utils.ts
+++ b/typescript/infra/scripts/agent-utils.ts
@@ -109,6 +109,19 @@ export function withChainRequired(args: Argv) {
return withChain(args).demandOption('chain');
}
+export function withSafeTxServiceUrlRequired(args: Argv) {
+ return args
+ .describe('safeTxServiceUrl', 'Custom safe transaction service url')
+ .demandOption('safeTxServiceUrl');
+}
+
+export function withThreshold(args: Argv) {
+ return args
+ .describe('threshold', 'threshold for multisig')
+ .number('threshold')
+ .default('threshold', 4);
+}
+
export function withChain(args: Argv) {
return args
.describe('chain', 'chain name')
diff --git a/typescript/infra/scripts/safes/create-safe.ts b/typescript/infra/scripts/safes/create-safe.ts
new file mode 100644
index 000000000..bd5cd8807
--- /dev/null
+++ b/typescript/infra/scripts/safes/create-safe.ts
@@ -0,0 +1,79 @@
+import { SafeFactory } from '@safe-global/protocol-kit';
+import { SafeAccountConfig } from '@safe-global/protocol-kit';
+
+import { Contexts } from '../../config/contexts.js';
+import { getChain } from '../../config/registry.js';
+import { Role } from '../../src/roles.js';
+import { readJSONAtPath } from '../../src/utils/utils.js';
+import {
+ getArgs,
+ getKeyForRole,
+ withChainRequired,
+ withSafeTxServiceUrlRequired,
+ withThreshold,
+} from '../agent-utils.js';
+
+const OWNERS_FILE_PATH = 'config/environments/mainnet3/safe/safeSigners.json';
+
+async function main() {
+ const { chain, safeTxServiceUrl, threshold } = await withThreshold(
+ withSafeTxServiceUrlRequired(withChainRequired(getArgs())),
+ ).argv;
+
+ const chainMetadata = await getChain(chain);
+ const rpcUrls = chainMetadata.rpcUrls;
+ const deployerPrivateKey = await getDeployerPrivateKey();
+
+ let safeFactory;
+ try {
+ safeFactory = await SafeFactory.init({
+ provider: rpcUrls[0].http,
+ signer: deployerPrivateKey,
+ });
+ } catch (e) {
+ console.error(`Error initializing SafeFactory: ${e}`);
+ process.exit(1);
+ }
+
+ const ownersConfig = readJSONAtPath(OWNERS_FILE_PATH);
+ const owners = ownersConfig.signers;
+
+ const safeAccountConfig: SafeAccountConfig = {
+ owners,
+ threshold,
+ };
+
+ let safe;
+ try {
+ safe = await safeFactory.deploySafe({ safeAccountConfig });
+ } catch (e) {
+ console.error(`Error deploying Safe: ${e}`);
+ process.exit(1);
+ }
+
+ const safeAddress = await safe.getAddress();
+
+ console.log(`Safe address: ${safeAddress}`);
+ console.log(
+ `Safe url: ${safeTxServiceUrl}/home?safe=${chain}:${safeAddress}`,
+ );
+ console.log('url may not be correct, please check by following the link');
+}
+
+const getDeployerPrivateKey = async () => {
+ const key = await getKeyForRole(
+ 'mainnet3',
+ Contexts.Hyperlane,
+ Role.Deployer,
+ );
+ await key.fetch();
+
+ return key.privateKey;
+};
+
+main()
+ .then()
+ .catch((e) => {
+ console.error(e);
+ process.exit(1);
+ });
diff --git a/yarn.lock b/yarn.lock
index bb9a49a02..50e82ebaf 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -19,6 +19,13 @@ __metadata:
languageName: node
linkType: hard
+"@adraffy/ens-normalize@npm:1.10.1":
+ version: 1.10.1
+ resolution: "@adraffy/ens-normalize@npm:1.10.1"
+ checksum: 4cb938c4abb88a346d50cb0ea44243ab3574330c81d4f5aaaf9dfee584b96189d0faa404de0fcbef5a1b73909ea4ebc3e63d84bd23f9949e5c8d4085207a5091
+ languageName: node
+ linkType: hard
+
"@ampproject/remapping@npm:^2.2.0":
version: 2.2.1
resolution: "@ampproject/remapping@npm:2.2.1"
@@ -5843,6 +5850,7 @@ __metadata:
"@nomiclabs/hardhat-ethers": "npm:^2.2.3"
"@nomiclabs/hardhat-etherscan": "npm:^3.0.3"
"@nomiclabs/hardhat-waffle": "npm:^2.0.6"
+ "@safe-global/protocol-kit": "npm:^4.0.2"
"@solana/web3.js": "npm:^1.78.0"
"@types/chai": "npm:^4.2.21"
"@types/json-stable-stringify": "npm:^1.0.36"
@@ -7054,6 +7062,13 @@ __metadata:
languageName: node
linkType: hard
+"@noble/hashes@npm:^1.3.3":
+ version: 1.4.0
+ resolution: "@noble/hashes@npm:1.4.0"
+ checksum: e156e65794c473794c52fa9d06baf1eb20903d0d96719530f523cc4450f6c721a957c544796e6efd0197b2296e7cd70efeb312f861465e17940a3e3c7e0febc6
+ languageName: node
+ linkType: hard
+
"@noble/secp256k1@npm:1.5.5, @noble/secp256k1@npm:~1.5.2":
version: 1.5.5
resolution: "@noble/secp256k1@npm:1.5.5"
@@ -8067,6 +8082,21 @@ __metadata:
languageName: node
linkType: hard
+"@safe-global/protocol-kit@npm:^4.0.2":
+ version: 4.0.2
+ resolution: "@safe-global/protocol-kit@npm:4.0.2"
+ dependencies:
+ "@noble/hashes": "npm:^1.3.3"
+ "@safe-global/safe-core-sdk-types": "npm:^5.0.2"
+ "@safe-global/safe-deployments": "npm:^1.37.0"
+ abitype: "npm:^1.0.2"
+ ethereumjs-util: "npm:^7.1.5"
+ ethers: "npm:^6.13.1"
+ semver: "npm:^7.6.2"
+ checksum: aff8dfda28e2adc0a965969fb3513354eda7d4bf8de574075ccdac932b48337063f519fcffcf71b53b97658009e390ec7dbea84bd6c0a19ec6ff18c0a4570b68
+ languageName: node
+ linkType: hard
+
"@safe-global/safe-apps-provider@npm:^0.15.2":
version: 0.15.2
resolution: "@safe-global/safe-apps-provider@npm:0.15.2"
@@ -8110,6 +8140,15 @@ __metadata:
languageName: node
linkType: hard
+"@safe-global/safe-core-sdk-types@npm:^5.0.2":
+ version: 5.0.2
+ resolution: "@safe-global/safe-core-sdk-types@npm:5.0.2"
+ dependencies:
+ abitype: "npm:^1.0.2"
+ checksum: 53f0221e1c86b5b04f75cc773527e5e9c59ebb13ec21fe9a9246f6834c45b107e9a165da08ed58d40ed2564952f5360fb4c02051f236bfcc61bbfdb6015e3663
+ languageName: node
+ linkType: hard
+
"@safe-global/safe-deployments@npm:^1.26.0":
version: 1.26.0
resolution: "@safe-global/safe-deployments@npm:1.26.0"
@@ -8119,6 +8158,15 @@ __metadata:
languageName: node
linkType: hard
+"@safe-global/safe-deployments@npm:^1.37.0":
+ version: 1.37.1
+ resolution: "@safe-global/safe-deployments@npm:1.37.1"
+ dependencies:
+ semver: "npm:^7.6.2"
+ checksum: 2cb05ad0d1768264885d9e92d1fcc1340af77f88605ac46fd99b8aa4118d923671e4bb3d380e3dd7caa13ca4e8ed6edae4765dd5394b78966a51f391375d683b
+ languageName: node
+ linkType: hard
+
"@safe-global/safe-gateway-typescript-sdk@npm:^3.5.3":
version: 3.17.0
resolution: "@safe-global/safe-gateway-typescript-sdk@npm:3.17.0"
@@ -9770,6 +9818,13 @@ __metadata:
languageName: node
linkType: hard
+"@types/node@npm:18.15.13":
+ version: 18.15.13
+ resolution: "@types/node@npm:18.15.13"
+ checksum: b9bbe923573797ef7c5fd2641a6793489e25d9369c32aeadcaa5c7c175c85b42eb12d6fe173f6781ab6f42eaa1ebd9576a419eeaa2a1ec810094adb8adaa9a54
+ languageName: node
+ linkType: hard
+
"@types/node@npm:>=13.7.0":
version: 20.8.9
resolution: "@types/node@npm:20.8.9"
@@ -10864,6 +10919,21 @@ __metadata:
languageName: node
linkType: hard
+"abitype@npm:^1.0.2":
+ version: 1.0.5
+ resolution: "abitype@npm:1.0.5"
+ peerDependencies:
+ typescript: ">=5.0.4"
+ zod: ^3 >=3.22.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ zod:
+ optional: true
+ checksum: 1acd0d9687945dd78442b71bd84ff3b9dceae27d15f0d8b14b16554a0c8c9518eeb971ff8e94d507f4d9f05a8a8b91eb8fafd735eaecebac37d5c5a4aac06d8e
+ languageName: node
+ linkType: hard
+
"abort-controller@npm:^3.0.0":
version: 3.0.0
resolution: "abort-controller@npm:3.0.0"
@@ -10991,6 +11061,13 @@ __metadata:
languageName: node
linkType: hard
+"aes-js@npm:4.0.0-beta.5":
+ version: 4.0.0-beta.5
+ resolution: "aes-js@npm:4.0.0-beta.5"
+ checksum: 8f745da2e8fb38e91297a8ec13c2febe3219f8383303cd4ed4660ca67190242ccfd5fdc2f0d1642fd1ea934818fb871cd4cc28d3f28e812e3dc6c3d0f1f97c24
+ languageName: node
+ linkType: hard
+
"aes-js@npm:^3.1.2":
version: 3.1.2
resolution: "aes-js@npm:3.1.2"
@@ -14788,6 +14865,21 @@ __metadata:
languageName: node
linkType: hard
+"ethers@npm:^6.13.1":
+ version: 6.13.1
+ resolution: "ethers@npm:6.13.1"
+ dependencies:
+ "@adraffy/ens-normalize": "npm:1.10.1"
+ "@noble/curves": "npm:1.2.0"
+ "@noble/hashes": "npm:1.3.2"
+ "@types/node": "npm:18.15.13"
+ aes-js: "npm:4.0.0-beta.5"
+ tslib: "npm:2.4.0"
+ ws: "npm:8.17.1"
+ checksum: efc3e8d4d13101cad01823ba524dad797a23f60088ca9f8677bd6dbfad5087e4187ede121e43aa0758d704525976f935860c5d5d27183a4247deaccf7cf19950
+ languageName: node
+ linkType: hard
+
"ethjs-unit@npm:0.1.6":
version: 0.1.6
resolution: "ethjs-unit@npm:0.1.6"
@@ -22928,6 +23020,15 @@ __metadata:
languageName: node
linkType: hard
+"semver@npm:^7.6.2":
+ version: 7.6.2
+ resolution: "semver@npm:7.6.2"
+ bin:
+ semver: bin/semver.js
+ checksum: 296b17d027f57a87ef645e9c725bff4865a38dfc9caf29b26aa084b85820972fbe7372caea1ba6857162fa990702c6d9c1d82297cecb72d56c78ab29070d2ca2
+ languageName: node
+ linkType: hard
+
"send@npm:0.18.0":
version: 0.18.0
resolution: "send@npm:0.18.0"
@@ -24644,6 +24745,13 @@ __metadata:
languageName: node
linkType: hard
+"tslib@npm:2.4.0, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0":
+ version: 2.4.0
+ resolution: "tslib@npm:2.4.0"
+ checksum: d8379e68b36caf082c1905ec25d17df8261e1d68ddc1abfd6c91158a064f6e4402039ae7c02cf4c81d12e3a2a2c7cd8ea2f57b233eb80136a2e3e7279daf2911
+ languageName: node
+ linkType: hard
+
"tslib@npm:^2.0.0, tslib@npm:^2.6.2":
version: 2.6.2
resolution: "tslib@npm:2.6.2"
@@ -24651,13 +24759,6 @@ __metadata:
languageName: node
linkType: hard
-"tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0":
- version: 2.4.0
- resolution: "tslib@npm:2.4.0"
- checksum: d8379e68b36caf082c1905ec25d17df8261e1d68ddc1abfd6c91158a064f6e4402039ae7c02cf4c81d12e3a2a2c7cd8ea2f57b233eb80136a2e3e7279daf2911
- languageName: node
- linkType: hard
-
"tsort@npm:0.0.1":
version: 0.0.1
resolution: "tsort@npm:0.0.1"
@@ -26237,6 +26338,21 @@ __metadata:
languageName: node
linkType: hard
+"ws@npm:8.17.1":
+ version: 8.17.1
+ resolution: "ws@npm:8.17.1"
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: ">=5.0.2"
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+ checksum: 4264ae92c0b3e59c7e309001e93079b26937aab181835fb7af79f906b22cd33b6196d96556dafb4e985742dd401e99139572242e9847661fdbc96556b9e6902d
+ languageName: node
+ linkType: hard
+
"ws@npm:^3.0.0":
version: 3.3.3
resolution: "ws@npm:3.3.3"