Skip to content

Commit

Permalink
fix(Multichain): Safe address prediction and creation on zkSync (#4334)
Browse files Browse the repository at this point in the history
  • Loading branch information
schmanu authored Oct 8, 2024
1 parent 8e42093 commit 2d811ea
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 9 deletions.
30 changes: 30 additions & 0 deletions src/components/new-safe/create/logic/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,5 +320,35 @@ describe('create/logic', () => {
factoryAddress: getProxyFactoryDeployment({ version: '1.4.1', network: '137' })?.defaultAddress,
})
})

it('should use l2 masterCopy and no migration on zkSync', () => {
const safeSetup = {
owners: [faker.finance.ethereumAddress()],
threshold: 1,
}
expect(
createNewUndeployedSafeWithoutSalt(
'1.3.0',
safeSetup,
chainBuilder()
.with({ chainId: '324' })
// Multichain and 1.4.1 creation is toggled off
.with({ features: [FEATURES.COUNTERFACTUAL] as any })
.with({ l2: true })
.build(),
),
).toEqual({
safeAccountConfig: {
...safeSetup,
fallbackHandler: getFallbackHandlerDeployment({ version: '1.3.0', network: '324' })?.networkAddresses['324'],
to: ZERO_ADDRESS,
data: EMPTY_DATA,
paymentReceiver: ECOSYSTEM_ID_ADDRESS,
},
safeVersion: '1.3.0',
masterCopy: getSafeL2SingletonDeployment({ version: '1.3.0', network: '324' })?.networkAddresses['324'],
factoryAddress: getProxyFactoryDeployment({ version: '1.3.0', network: '324' })?.networkAddresses['324'],
})
})
})
})
3 changes: 1 addition & 2 deletions src/components/new-safe/create/logic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ export const computeNewSafeAddress = async (
saltNonce: props.saltNonce,
safeVersion: safeVersion ?? getLatestSafeVersion(chain),
},
isL1SafeSingleton: true,
})
}

Expand Down Expand Up @@ -211,7 +210,7 @@ export const createNewUndeployedSafeWithoutSalt = (
version: safeVersion,
network: chain.chainId,
})
const fallbackHandlerAddress = fallbackHandlerDeployment?.defaultAddress
const fallbackHandlerAddress = fallbackHandlerDeployment?.networkAddresses[chain.chainId]
const safeL2Deployment = getSafeL2SingletonDeployment({ version: safeVersion, network: chain.chainId })
const safeL2Address = safeL2Deployment?.networkAddresses[chain.chainId]

Expand Down
26 changes: 21 additions & 5 deletions src/components/new-safe/create/logic/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,20 @@ import { sameAddress } from '@/utils/addresses'
import { createWeb3ReadOnly, getRpcServiceUrl } from '@/hooks/wallets/web3'
import { type ReplayedSafeProps } from '@/store/slices'
import { predictAddressBasedOnReplayData } from '@/components/welcome/MyAccounts/utils/multiChainSafe'
import chains from '@/config/chains'
import { computeNewSafeAddress } from '.'

export const getAvailableSaltNonce = async (
customRpcs: {
[chainId: string]: string
},
replayedSafe: ReplayedSafeProps,
chains: ChainInfo[],
chainInfos: ChainInfo[],
// All addresses from the sidebar disregarding the chain. This is an optimization to reduce RPC calls
knownSafeAddresses: string[],
): Promise<string> => {
let isAvailableOnAllChains = true
const allRPCs = chains.map((chain) => {
const allRPCs = chainInfos.map((chain) => {
const rpcUrl = customRpcs?.[chain.chainId] || getRpcServiceUrl(chain.rpcUri)
// Turn into Eip1993Provider
return {
Expand All @@ -24,7 +26,7 @@ export const getAvailableSaltNonce = async (
}
})

for (const chain of chains) {
for (const chain of chainInfos) {
const rpcUrl = allRPCs.find((rpc) => chain.chainId === rpc.chainId)?.rpcUrl
if (!rpcUrl) {
throw new Error(`No RPC available for ${chain.chainName}`)
Expand All @@ -33,7 +35,21 @@ export const getAvailableSaltNonce = async (
if (!web3ReadOnly) {
throw new Error('Could not initiate RPC')
}
const safeAddress = await predictAddressBasedOnReplayData(replayedSafe, web3ReadOnly)
let safeAddress: string
if (chain.chainId === chains['zksync']) {
// ZK-sync is using a different create2 method which is supported by the SDK
safeAddress = await computeNewSafeAddress(
rpcUrl,
{
safeAccountConfig: replayedSafe.safeAccountConfig,
saltNonce: replayedSafe.saltNonce,
},
chain,
replayedSafe.safeVersion,
)
} else {
safeAddress = await predictAddressBasedOnReplayData(replayedSafe, web3ReadOnly)
}
const isKnown = knownSafeAddresses.some((knownAddress) => sameAddress(knownAddress, safeAddress))
if (isKnown || (await isSmartContract(safeAddress, web3ReadOnly))) {
// We found a chain where the nonce is used up
Expand All @@ -47,7 +63,7 @@ export const getAvailableSaltNonce = async (
return getAvailableSaltNonce(
customRpcs,
{ ...replayedSafe, saltNonce: (Number(replayedSafe.saltNonce) + 1).toString() },
chains,
chainInfos,
knownSafeAddresses,
)
}
Expand Down
20 changes: 18 additions & 2 deletions src/components/new-safe/create/steps/ReviewStep/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getTotalFeeFormatted } from '@/hooks/useGasPrice'
import type { StepRenderProps } from '@/components/new-safe/CardStepper/useCardStepper'
import type { NewSafeFormData } from '@/components/new-safe/create'
import {
computeNewSafeAddress,
createNewSafe,
createNewUndeployedSafeWithoutSalt,
relaySafeCreation,
Expand Down Expand Up @@ -47,9 +48,10 @@ import { selectRpc } from '@/store/settingsSlice'
import { AppRoutes } from '@/config/routes'
import { type ReplayedSafeProps } from '@/store/slices'
import { predictAddressBasedOnReplayData } from '@/components/welcome/MyAccounts/utils/multiChainSafe'
import { createWeb3ReadOnly } from '@/hooks/wallets/web3'
import { createWeb3ReadOnly, getRpcServiceUrl } from '@/hooks/wallets/web3'
import { type DeploySafeProps } from '@safe-global/protocol-kit'
import { updateAddressBook } from '../../logic/address-book'
import chains from '@/config/chains'

export const NetworkFee = ({
totalFee,
Expand Down Expand Up @@ -227,7 +229,21 @@ const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps<NewSafe
const provider = createWeb3ReadOnly(chain, customRpcUrl)
if (!provider) return

const safeAddress = await predictAddressBasedOnReplayData(replayedSafeWithNonce, provider)
let safeAddress: string

if (chain.chainId === chains['zksync']) {
safeAddress = await computeNewSafeAddress(
customRpcUrl || getRpcServiceUrl(chain.rpcUri),
{
safeAccountConfig: replayedSafeWithNonce.safeAccountConfig,
saltNonce: nextAvailableNonce,
},
chain,
replayedSafeWithNonce.safeVersion,
)
} else {
safeAddress = await predictAddressBasedOnReplayData(replayedSafeWithNonce, provider)
}

for (const network of data.networks) {
await createSafe(network, replayedSafeWithNonce, safeAddress)
Expand Down

0 comments on commit 2d811ea

Please sign in to comment.