-
Notifications
You must be signed in to change notification settings - Fork 155
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add Zod validation of state chain objects, infer types (#1485)
* add Zod validation of state chain objects, infer types * move state files to TS, add more inferred types * fix persist mock for tests * add types to main state * update test * add mute notification schema * move type export to state main * move legacy file to TS * try new migration format * update legacy migrations and tests * fix gas fees type * fix gas type * move legacy mapping * final migration prototype * finish migration poc * finish cleaning up migrations * test cleanup * fix compilation error * fix state parsing
- Loading branch information
Showing
7 changed files
with
218 additions
and
77 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { z } from 'zod' | ||
import log from 'electron-log' | ||
|
||
import { | ||
v35Chain, | ||
v35ChainSchema, | ||
v35ChainsSchema, | ||
v35Connection, | ||
v35MainSchema, | ||
v35StateSchema | ||
} from './schema' | ||
|
||
const pylonChainIds = ['1', '5', '10', '137', '42161', '11155111'] | ||
const retiredChainIds = ['3', '4', '42'] | ||
const chainsToMigrate = [...pylonChainIds, ...retiredChainIds] | ||
|
||
// because this is the first migration that uses Zod parsing and validation, | ||
// create a version of the schema that removes invalid chains, allowing them to | ||
// also be "false" so that we can filter them out later in a transform. future migrations | ||
// that use this schema can be sure that the chains are all valid afterwards | ||
const ParsedChainSchema = z.union([v35ChainSchema, z.boolean()]).catch(false) | ||
|
||
const EthereumChainsSchema = z.record(z.coerce.number(), ParsedChainSchema).transform((chains) => { | ||
// remove any chains that failed to parse, which will now be set to "false" | ||
// TODO: we can insert default chain data here from the state defaults in the future | ||
return Object.fromEntries( | ||
Object.entries(chains).filter(([id, chain]) => { | ||
if (chain === false) { | ||
log.info(`Migration 35: removing invalid chain ${id} from state`) | ||
return false | ||
} | ||
|
||
return true | ||
}) | ||
) | ||
}) | ||
|
||
const ChainsSchema = v35ChainsSchema.merge( | ||
z.object({ | ||
ethereum: EthereumChainsSchema | ||
}) | ||
) | ||
|
||
const MainSchema = v35MainSchema | ||
.merge( | ||
z.object({ | ||
networks: ChainsSchema | ||
}) | ||
) | ||
.passthrough() | ||
|
||
const StateSchema = v35StateSchema.merge(z.object({ main: MainSchema })) | ||
|
||
const migrate = (initial: unknown) => { | ||
let showMigrationWarning = false | ||
|
||
const updateChain = (chain: v35Chain) => { | ||
const removeRpcConnection = (connection: v35Connection) => { | ||
const isServiceRpc = connection.current === 'infura' || connection.current === 'alchemy' | ||
|
||
if (isServiceRpc) { | ||
log.info(`Migration 35: removing ${connection.current} preset from chain ${chain.id}`) | ||
showMigrationWarning = true | ||
} | ||
|
||
return { | ||
...connection, | ||
current: isServiceRpc ? 'custom' : connection.current, | ||
custom: isServiceRpc ? '' : connection.custom | ||
} | ||
} | ||
|
||
const { primary, secondary } = chain.connection | ||
|
||
const updatedChain = { | ||
...chain, | ||
connection: { | ||
...chain.connection, | ||
primary: removeRpcConnection(primary), | ||
secondary: removeRpcConnection(secondary) | ||
} | ||
} | ||
|
||
return updatedChain | ||
} | ||
|
||
try { | ||
const state = StateSchema.parse(initial) | ||
|
||
const chainEntries = Object.entries(state.main.networks.ethereum) | ||
|
||
const migratedChains = chainEntries | ||
.filter(([id]) => chainsToMigrate.includes(id)) | ||
.map(([id, chain]) => [id, updateChain(chain as v35Chain)]) | ||
|
||
state.main.networks.ethereum = Object.fromEntries([...chainEntries, ...migratedChains]) | ||
state.main.mute.migrateToPylon = !showMigrationWarning | ||
|
||
return state | ||
} catch (e) { | ||
log.error('Migration 35: could not parse state', e) | ||
} | ||
|
||
return initial | ||
} | ||
|
||
export default { | ||
version: 35, | ||
migrate | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { z } from 'zod' | ||
|
||
const v35MuteSchema = z | ||
.object({ | ||
migrateToPylon: z.boolean().default(false) | ||
}) | ||
.passthrough() | ||
.default({}) | ||
|
||
const v35ConnectionSchema = z | ||
.object({ | ||
current: z.enum(['local', 'custom', 'infura', 'alchemy', 'poa']), | ||
custom: z.string().default('') | ||
}) | ||
.passthrough() | ||
|
||
export const v35ChainSchema = z | ||
.object({ | ||
id: z.coerce.number(), | ||
connection: z.object({ | ||
primary: v35ConnectionSchema, | ||
secondary: v35ConnectionSchema | ||
}) | ||
}) | ||
.passthrough() | ||
|
||
const EthereumChainsSchema = z.record(z.coerce.number(), v35ChainSchema) | ||
|
||
export const v35ChainsSchema = z.object({ | ||
ethereum: EthereumChainsSchema | ||
}) | ||
|
||
export const v35MainSchema = z | ||
.object({ | ||
networks: v35ChainsSchema, | ||
mute: v35MuteSchema | ||
}) | ||
.passthrough() | ||
|
||
export const v35StateSchema = z | ||
.object({ | ||
main: v35MainSchema | ||
}) | ||
.passthrough() | ||
|
||
export type v35Connection = z.infer<typeof v35ConnectionSchema> | ||
export type v35Chain = z.infer<typeof v35ChainSchema> |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
import log from 'electron-log' | ||
|
||
import { v35Connection, v35StateSchema } from '../35/schema' | ||
|
||
function removePoaConnection(connection: v35Connection) { | ||
// remove Gnosis chain preset | ||
const isPoa = connection.current === 'poa' | ||
|
||
if (isPoa) { | ||
log.info('Migration 36: removing POA presets from Gnosis chain') | ||
} | ||
|
||
return { | ||
...connection, | ||
current: isPoa ? 'custom' : connection.current, | ||
custom: isPoa ? 'https://rpc.gnosischain.com' : connection.custom | ||
} | ||
} | ||
|
||
const migrate = (initial: unknown) => { | ||
try { | ||
const state = v35StateSchema.parse(initial) | ||
const gnosisChainPresent = '100' in state.main.networks.ethereum | ||
|
||
if (gnosisChainPresent) { | ||
const gnosisChain = state.main.networks.ethereum[100] | ||
|
||
state.main.networks.ethereum[100] = { | ||
...gnosisChain, | ||
connection: { | ||
primary: removePoaConnection(gnosisChain.connection.primary), | ||
secondary: removePoaConnection(gnosisChain.connection.secondary) | ||
} | ||
} | ||
} | ||
|
||
return state | ||
} catch (e) { | ||
log.error('Migration 36: could not parse state', e) | ||
} | ||
|
||
return initial | ||
} | ||
|
||
export default { | ||
version: 36, | ||
migrate | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters