Skip to content

Commit

Permalink
add Zod validation of state chain objects, infer types (#1485)
Browse files Browse the repository at this point in the history
* 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
mholtzman committed Jul 26, 2023
1 parent d5ca7db commit ac299ca
Show file tree
Hide file tree
Showing 61 changed files with 1,573 additions and 363 deletions.
161 changes: 3 additions & 158 deletions @types/frame/state.d.ts
Original file line number Diff line number Diff line change
@@ -1,151 +1,8 @@
type State = {
main: {
_version: number
networks: {
ethereum: Record<number, Network>
}
networksMeta: {
ethereum: Record<number, NetworkMetadata>
}
mute: Record<MutableNotificationType, boolean>
}
}

interface Connection {
on: boolean
connected: boolean
current: string
status: string
network: string
custom: string
}

interface Chain {
id: number
type: 'ethereum'
}

interface Network {
id: number
name: string
layer: string
isTestnet: boolean
explorer: string
on: boolean
connection: {
primary: Connection
secondary: Connection
}
}

interface NetworkMetadata {
blockHeight: number
gas: GasData
icon: string
primaryColor: keyof ColorwayPalette
nativeCurrency: NativeCurrency
}

interface Session {
requests: number
startedAt: number
endedAt?: number
lastUpdatedAt: number
}

interface Origin {
chain: Chain
name: string
session: Session
}

interface Permission {
origin: string
provider: boolean // whether or not to grant access
handlerId?: string
}

interface NativeCurrency {
symbol: string
icon: string
name: string
decimals: number
usd?: Rate
}

type MutableNotificationType =
| 'alphaWarning'
| 'welcomeWarning'
| 'externalLinkWarning'
| 'explorerWarning'
| 'signerRelockChange'
| 'gasFeeWarning'
| 'betaDisclosure'
| 'onboardingWindow'
| 'migrateToPylon'
| 'signerCompatibilityWarning'

interface GasData {
fees: GasFees
price: {
selected: string
levels: GasLevels
fees: GasFees | null
}
}

interface GasFees {
nextBaseFee: string
maxBaseFeePerGas: string
maxPriorityFeePerGas: string
maxFeePerGas: string
}

interface GasLevels {
slow?: string
standard: string
fast?: string
asap?: string
custom?: string
}

type HexAmount = string

type Color = { r: number; g: number; b: number }
type ColorwayPalette = {
accent1: Color
accent2: Color
accent3: Color
accent4: Color
accent5: Color
accent6: Color
accent7: Color
accent8: Color
}

interface WithTokenId {
address: string
chainId: number
}

interface Balance extends WithTokenId {
name: string
symbol: string
balance: HexAmount
decimals: number
displayBalance: string
}

interface Rate {
price: number
change24hr: number
}

interface Token extends WithTokenId {
name: string
symbol: string
decimals: number
logoURI?: string
enum Colorway {
light = 'light',
dark = 'dark'
}

type InventoryAsset = {
Expand Down Expand Up @@ -174,18 +31,6 @@ interface Frame {
views: Record<string, ViewMetadata>
}

interface Dapp {
id?: string
ens: string
status?: string
config: Record<string, string>
content?: string // IPFS hash
manifest?: any
current?: any
openWhenReady: boolean
checkStatusRetryCount: number
}

type SignerType = 'ring' | 'seed' | 'trezor' | 'ledger' | 'lattice'
type AccountStatus = 'ok'

Expand Down
21 changes: 14 additions & 7 deletions app/dash/Chains/Chain/Connection/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import { capitalize } from '../../../../../resources/utils'

import link from '../../../../../resources/link'
import svg from '../../../../../resources/svg'
import { NETWORK_PRESETS } from '../../../../../resources/constants'

function mapToPreset(chainId, key) {
return { text: key, value: `ethereum:${chainId}:${key}` }
}

const ConnectionIndicator = ({ className, connection }) => {
const isConnected = connection.status === 'connected'
Expand Down Expand Up @@ -122,20 +127,22 @@ class ChainModule extends React.Component {

render() {
const { id, type } = this.props
const toPreset = (key) => mapToPreset(id, key)

const connection = this.store('main.networks', type, id, 'connection')
if (!connection) return null

const networkMeta = this.store('main.networksMeta.ethereum', id)
const networkPresets = this.store('main.networkPresets', type)
const renderStatus = this.renderConnectionStatus.bind(this, type, id)

let presets = networkPresets[id] || {}
presets = Object.keys(presets).map((i) => ({ text: i, value: `${type}:${id}:${i}` }))
presets = presets.concat(
Object.keys(networkPresets.default).map((i) => ({ text: i, value: `${type}:${id}:${i}` }))
)
presets.push({ text: 'Custom', value: `${type}:${id}:custom` })
const networkPresets = NETWORK_PRESETS.ethereum[id] || {}
const defaultPresets = NETWORK_PRESETS.ethereum.default

const presets = [
...Object.keys(networkPresets).map(toPreset),
...Object.keys(defaultPresets).map(toPreset),
toPreset('custom')
]

const customFocusHandler = (inputName) => {
const stateKey = `${inputName}Custom`
Expand Down
10 changes: 1 addition & 9 deletions app/dash/Chains/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react'
import Restore from 'react-restore'
import link from '../../../resources/link'

import Chain from './Chain'
import link from '../../../resources/link'

class Settings extends React.Component {
constructor(props, context) {
Expand Down Expand Up @@ -113,15 +113,7 @@ class Settings extends React.Component {
}

renderChains() {
const { type, id } = { type: 'ethereum', id: 1 } // TODO: this.store('main.currentNetwork')
const networks = this.store('main.networks')
const networkPresets = this.store('main.networkPresets', type)
let presets = networkPresets[id] || {}
presets = Object.keys(presets).map((i) => ({ text: i, value: type + ':' + id + ':' + i }))
presets = presets.concat(
Object.keys(networkPresets.default).map((i) => ({ text: i, value: type + ':' + id + ':' + i }))
)
presets.push({ text: 'Custom', value: type + ':' + id + ':' + 'custom' })
const networkOptions = []
Object.keys(networks).forEach((type) => {
Object.keys(networks[type]).forEach((id) => {
Expand Down
11 changes: 1 addition & 10 deletions app/dash/Main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,18 +241,9 @@ class Settings extends React.Component {
}

render() {
const { type, id } = { type: 'ethereum', id: 1 } // TODO
const networks = this.store('main.networks')
// const connection = networks[type][id].connection
const networkPresets = this.store('main.networkPresets', type)
let presets = networkPresets[id] || {}
presets = Object.keys(presets).map((i) => ({ text: i, value: type + ':' + id + ':' + i }))
presets = presets.concat(
Object.keys(networkPresets.default).map((i) => ({ text: i, value: type + ':' + id + ':' + i }))
)
presets.push({ text: 'Custom', value: type + ':' + id + ':' + 'custom' })

const networkOptions = []

Object.keys(networks).forEach((type) => {
Object.keys(networks[type]).forEach((id) => {
networkOptions.push({ text: networks[type][id].name, value: type + ':' + id })
Expand Down
4 changes: 3 additions & 1 deletion main/accounts/Account/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ import provider from '../../provider'
import { ApprovalType } from '../../../resources/constants'

import reveal from '../../reveal'
import type { PermitSignatureRequest, TypedMessage } from '../types'
import { isTransactionRequest, isTypedMessageSignatureRequest } from '../../../resources/domain/request'
import Erc20Contract from '../../contracts/erc20'

import type { PermitSignatureRequest, TypedMessage } from '../types'
import type { Permission } from '../../store/state'

const nebula = nebulaApi()

const storeApi = {
Expand Down
3 changes: 2 additions & 1 deletion main/accounts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import type { DecodedCallData } from '../contracts'
import type { Chain } from '../chains'
import type { TransactionData } from '../../resources/domain/transaction'
import type { Action } from '../transaction/actions'
import { TokenData } from '../contracts/erc20'
import type { TokenData } from '../contracts/erc20'
import type { Token } from '../store/state'

export enum ReplacementType {
Speed = 'speed',
Expand Down
2 changes: 2 additions & 0 deletions main/api/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { updateOrigin, isTrusted, parseOrigin } from './origins'
import validPayload from './validPayload'
import protectedMethods from './protectedMethods'

import type { Permission } from '../store/state'

const logTraffic = process.env.LOG_TRAFFIC

interface PendingRequest {
Expand Down
2 changes: 2 additions & 0 deletions main/api/origins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import queryString from 'query-string'
import accounts, { AccessRequest } from '../accounts'
import store from '../store'

import type { Permission } from '../store/state'

const dev = process.env.NODE_ENV === 'development'

const activeExtensionChecks: Record<string, Promise<boolean>> = {}
Expand Down
2 changes: 2 additions & 0 deletions main/chains/gas/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { intToHex } from '@ethereumjs/util'

import type { GasFees } from '../../store/state'

interface GasCalculator {
calculateGas: (blocks: Block[]) => GasFees
}
Expand Down
4 changes: 2 additions & 2 deletions main/chains/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const { default: BlockMonitor } = require('./blocks')
const { default: chainConfig } = require('./config')
const { default: GasMonitor } = require('../transaction/gasMonitor')
const { createGasCalculator } = require('./gas')
const { NETWORK_PRESETS } = require('../../resources/constants')

// These chain IDs are known to not support EIP-1559 and will be forced
// not to use that mechanism
Expand Down Expand Up @@ -248,8 +249,7 @@ class ChainConnection extends EventEmitter {
this.network = connection.network
}

const presets = store('main.networkPresets', this.type)
const currentPresets = Object.assign({}, presets.default, presets[this.chainId])
const currentPresets = { ...NETWORK_PRESETS.ethereum.default, ...NETWORK_PRESETS.ethereum[this.chainId] }

const { primary, secondary } = store('main.networks', this.type, this.chainId, 'connection')
const secondaryTarget =
Expand Down
1 change: 1 addition & 0 deletions main/contracts/erc20.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { addHexPrefix } from '@ethereumjs/util'
import provider from '../provider'
import { BigNumber } from 'ethers'
import { erc20Interface } from '../../resources/contracts'

export interface TokenData {
decimals?: number
name: string
Expand Down
2 changes: 2 additions & 0 deletions main/dapps/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import server from './server'
import extractColors from '../windows/extractColors'
import { dappPathExists, getDappCacheDir, isDappVerified } from './verify'

import type { Dapp } from '../store/state'

const nebula = nebulaApi()

class DappStream extends Readable {
Expand Down
6 changes: 4 additions & 2 deletions main/externalData/assets/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import log from 'electron-log'

import Pylon, { AssetType } from '@framelabs/pylon-client'
import { AssetId } from '@framelabs/pylon-client/dist/assetId'
import { UsdRate } from '../../provider/assets'

import type { AssetId } from '@framelabs/pylon-client/dist/assetId'
import type { UsdRate } from '../../provider/assets'
import type { NativeCurrency, Rate, Token } from '../../store/state'

interface RateUpdate {
id: AssetId
Expand Down
4 changes: 3 additions & 1 deletion main/externalData/balances/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import path from 'path'
import { ChildProcess, fork } from 'child_process'
import { EventEmitter } from 'stream'

import { CurrencyBalance, TokenBalance } from './scan'
import { toTokenId } from '../../../resources/domain/balance'

import type { CurrencyBalance, TokenBalance } from './scan'
import type { Token } from '../../store/state'

const BOOTSTRAP_TIMEOUT_SECONDS = 20

interface WorkerMessage {
Expand Down
6 changes: 4 additions & 2 deletions main/externalData/balances/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { toTokenId } from '../../../resources/domain/balance'
import BalancesWorkerController from './controller'
import { CurrencyBalance, TokenBalance } from './scan'

import type { Balance, Chain, Token, WithTokenId } from '../../store/state'

const RESTART_WAIT = 5 // seconds

// time to wait in between scans, in seconds
Expand All @@ -16,11 +18,11 @@ const scanInterval = {
export default function (store: Store) {
const storeApi = {
getActiveAddress: () => (store('selected.current') || '') as Address,
getNetwork: (id: number) => (store('main.networks.ethereum', id) || {}) as Network,
getNetwork: (id: number) => (store('main.networks.ethereum', id) || {}) as Chain,
getNativeCurrencySymbol: (id: number) =>
store('main.networksMeta.ethereum', id, 'nativeCurrency', 'symbol') as string,
getConnectedNetworks: () => {
const networks = Object.values(store('main.networks.ethereum') || {}) as Network[]
const networks = Object.values(store('main.networks.ethereum') || {}) as Chain[]
return networks.filter(
(n) => (n.connection.primary || {}).connected || (n.connection.secondary || {}).connected
)
Expand Down
Loading

0 comments on commit ac299ca

Please sign in to comment.