Get started with a multisig Safe
Install Safe beta.1 and connect a seed-derived owner to an existing Safe.
Install the package, configure the account, then inspect its identity. See Need Help for support.
Prerequisites: Node.js with ESM, npm, an existing Safe using modules 0.2.0, its owner seed, and matching RPC, EntryPoint v0.6 bundler and coordinator. This example uses native gas mode; the Safe needs native funds before execution. Store seeds outside source code and logs.
Install the Package
Install the exact package used by this guide:
npm install --save-exact @tetherto/wdk-wallet-multisig-safe@1.0.0-beta.1Configure the Account
- Supply the RPC, chain ID, bundler URL, Safe address and Safe Transaction Service proxy URL through your application configuration. The proxy must add any required service credentials. For Safe's hosted service, replace
txServiceUrlwithsafeApiKey; do not set both. See coordinator configuration for supported-chain requirements. - Load the owner's seed from secret storage. The environment variable below is a Node example, not a requirement to store browser secrets in environment files.
- Create a
WalletManagerMultisigSafeand derive the known owner path.
Save this setup as safe-account.mjs; subsequent guides import its account and configuration:
import WalletManagerMultisigSafe from '@tetherto/wdk-wallet-multisig-safe'
export function requiredEnv(name) {
const value = process.env[name]
if (!value) throw new Error(`Missing ${name}`)
return value
}
export const config = {
provider: requiredEnv('EVM_RPC_URL'),
chainId: BigInt(requiredEnv('CHAIN_ID')),
bundlerUrl: requiredEnv('BUNDLER_URL'),
txServiceUrl: requiredEnv('SAFE_TX_SERVICE_URL'),
safeModulesVersion: '0.2.0',
safeOptions: { safeAddress: requiredEnv('SAFE_ADDRESS') },
useNativeCoins: true
}
export const wallet = new WalletManagerMultisigSafe(
requiredEnv('WDK_SEED_PHRASE'), config
)
export const account = await wallet.getAccountByPath("0'/0/0")The full owner path is m/44'/60'/0'/0/0. Change the relative path if the Safe's owner uses a different one. Deriving an account does not add that address as a Safe owner.
Inspect Its Identity
Use getAddress() and getSignerAddress() to distinguish the asset-holding Safe from its owner:
import { account } from './safe-account.mjs'
console.log('Safe:', await account.getAddress())
console.log('Owner EOA:', await account.getSignerAddress())Before signing, check getMultisigInfo() and validateSignerIsOwner():
console.log(await account.getMultisigInfo())
await account.validateSignerIsOwner()The expected result is your agreed Safe address, owner EOA, owner list and threshold. Stop if any differ. Continue with proposal management.