Migrate Safe to LSP·0 on Ethereum/EVM
Step 1 — model the Safe as permissions
Take your Safe’s owner set and threshold and translate it into LSP6 vocabulary. A threshold of “3 of 5” doesn’t directly exist in LSP6 (controllers are individual, not aggregated). Two patterns:
- Recovery contract — deploy a contract that enforces the threshold and is itself
registered as the controller with
EDITPERMISSIONSon the profile. Day-to-day controllers do day-to-day work; the recovery contract handles ownership-level changes. - Day-to-day single controller + cold multisig — flatten the daily workflow to one controller; keep the Safe (or a new threshold contract) as the cold recovery layer.
Step 2 — deploy the Universal Profile
Use the standard lsp-factory.js or equivalent deploy script. Set LSP3 profile metadata.
Add controllers per the design from step 1.
Step 3 — transfer assets
For each asset class:
- Native LYX —
Safe.execTransaction → profile.execute(0, profile, value, "") - ERC-20 / LSP7 —
Safe → token.transfer(profile, balance) - NFTs —
Safe → token.safeTransferFrom(safe, profile, id)(or LSP8 equivalent)
If the Safe holds many assets, write a sweep contract that batches the transfers.
Step 4 — update integrations
Identify every protocol that knows the Safe’s address: vesting contracts, multi-sig DAOs, subscriptions, allowance grants. Update each one to point to the new profile address.
Step 5 — sunset the Safe
Once nothing material lives in the Safe, transfer its remaining gas dust out and consider it historical. Leave it deployed — destroying multisig contracts is unsupported and dangerous.
Gotchas.
- Multi-sig threshold semantics don't map 1:1 to LSP6 — model it as a recovery controller contract that enforces the threshold and is itself registered as an LSP6 controller with EDITPERMISSIONS.
- Asset transfer is many separate transactions unless the Safe owns the assets through a sweep contract that batches outbound moves.
- Anything connected to the Safe's address (vesting schedules, allowance grants, on-chain memberships) needs to be re-pointed to the new profile address — addresses don't migrate, the references do.
- Safe modules have their own permission shape; LSP6 controllers + LSP17 extensions are the LSP-side equivalents. Map each module to its closest LSP primitive deliberately.
Verify before ship.
- All assets transferred to the Universal Profile
- Old Safe paused (no longer holds material assets)
- Controllers + permissions configured on the new profile
- Recovery policy in place — at least one cold controller with EDITPERMISSIONS
- Off-chain integrations updated to the new address