Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/add gridplus lattice1 #3744

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions .env.defaults
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ USE_MAINNET_FORK=false
ARBITRUM_FORK_RPC=https://rpc.tenderly.co/fork/2fc2cf12-5c58-439f-9b5e-967bfd02191a
TESTNET_TAHO_DEPLOYER_ADDRESS=0x55B180c3470dA8E31761d45468e4E61DbE13Eb9B
TESTNET_TAHO_ADDRESS=0x78f04eC76df38Fcb37971Efa8EcbcB33f52dae0F
MOCKED_GRIDPLUS_ONBOARDING=false
2 changes: 1 addition & 1 deletion .github/workflows/pledge-signer-sync/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "1.0.0",
"private": true,
"engines": {
"node": ">=16.0.0 <17.0.0"
"node": ">=16.0.0 <19.0.0"
},
"dependencies": {
"firebase": "^9.9.0",
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pledge-signer-sync/pledge-export.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// @ts-check
/* eslint-disable import/no-unresolved */
/* eslint-disable no-console */ // need logging
/* eslint-disable no-await-in-loop */ // need to process items in sequence
import { getAuth, signInWithEmailAndPassword } from "firebase/auth"
Expand Down
21 changes: 11 additions & 10 deletions .github/workflows/pledge-signer-sync/pledge-sync.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// @ts-check
/* eslint-disable import/no-unresolved */
/* eslint-disable no-console */ // need logging
/* eslint-disable no-await-in-loop */ // need to process items in sequence
import { getAuth, signInWithEmailAndPassword } from "firebase/auth"
Expand Down Expand Up @@ -103,16 +104,16 @@ const syncGalxe = async () => {
const payload = {
operationName: "credentialItems",
query: `
mutation credentialItems($credId: ID!, $operation: Operation!, $items: [String!]!)
{
credentialItems(input: {
credId: $credId
operation: $operation
items: $items
})
{
name
}
mutation credentialItems($credId: ID!, $operation: Operation!, $items: [String!]!)
{
credentialItems(input: {
credId: $credId
operation: $operation
items: $items
})
{
name
}
}
`,
variables: {
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v16.20.0
v18.20.3
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ ui/_locales/**/*.json
!.github
ci/cache
.vscode
size-plugin.json
size-plugin.json
90 changes: 87 additions & 3 deletions background/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
ServiceCreatorFunction,
IslandService,
LedgerService,
GridplusService,
SigningService,
NFTsService,
WalletConnectService,
Expand Down Expand Up @@ -200,6 +201,8 @@ import { makeFlashbotsProviderCreator } from "./services/chain/serial-fallback-p
import { AnalyticsPreferences, DismissableItem } from "./services/preferences"
import { newPricePoints } from "./redux-slices/prices"
import NotificationsService from "./services/notifications"
import { resetGridPlusState } from "./redux-slices/gridplus"
import { GridPlusAddress } from "./services/gridplus"

// This sanitizer runs on store and action data before serializing for remote
// redux devtools. The goal is to end up with an object that is directly
Expand Down Expand Up @@ -337,10 +340,13 @@ export default class Main extends BaseService<never> {

const ledgerService = LedgerService.create()

const gridplusService = GridplusService.create()

const signingService = SigningService.create(
internalSignerService,
ledgerService,
chainService,
gridplusService,
)

const analyticsService = AnalyticsService.create(
Expand Down Expand Up @@ -406,6 +412,7 @@ export default class Main extends BaseService<never> {
await islandService,
await telemetryService,
await ledgerService,
await gridplusService,
await signingService,
await analyticsService,
await nftsService,
Expand Down Expand Up @@ -477,6 +484,11 @@ export default class Main extends BaseService<never> {
*/
private ledgerService: LedgerService,

/**
* A promise to the GridPlus service, handling the communication
*/
private gridplusService: GridplusService,

/**
* A promise to the signing service which will route operations between the UI
* and the exact signing services.
Expand Down Expand Up @@ -615,6 +627,7 @@ export default class Main extends BaseService<never> {
this.islandService.startService(),
this.telemetryService.startService(),
this.ledgerService.startService(),
this.gridplusService.startService(),
this.signingService.startService(),
this.analyticsService.startService(),
this.nftsService.startService(),
Expand All @@ -639,6 +652,7 @@ export default class Main extends BaseService<never> {
this.islandService.stopService(),
this.telemetryService.stopService(),
this.ledgerService.stopService(),
this.gridplusService.stopService(),
this.signingService.stopService(),
this.analyticsService.stopService(),
this.nftsService.stopService(),
Expand All @@ -662,6 +676,7 @@ export default class Main extends BaseService<never> {
this.connectIslandService()
this.connectTelemetryService()
this.connectLedgerService()
this.connectGridplusService()
this.connectSigningService()
this.connectAnalyticsService()
this.connectWalletConnectService()
Expand Down Expand Up @@ -790,6 +805,67 @@ export default class Main extends BaseService<never> {
return this.ledgerService.refreshConnectedLedger()
}

async connectGridplus({
deviceId,
password,
}: {
deviceId?: string
password?: string
}) {
return this.gridplusService.setupClient({ deviceId, password })
}

async pairGridplusDevice({ pairingCode }: { pairingCode: string }) {
return this.gridplusService.pairDevice({ pairingCode })
}

async fetchGridPlusAddresses({
n,
startPath,
}: {
n?: number
startPath?: number[]
}) {
return this.gridplusService.fetchAddresses({ n, startPath })
}

async importGridPlusAddresses({
addresses,
}: {
addresses: GridPlusAddress[]
}) {
const trackedNetworks = await this.chainService.getTrackedNetworks()
await Promise.all(
addresses.map(async (address) => {
await this.gridplusService.importAddresses({ address })
await Promise.all(
trackedNetworks.map(async (network) => {
const addressNetwork = {
address: address.address,
network,
}
await this.chainService.addAccountToTrack(addressNetwork)
this.abilitiesService.getNewAccountAbilities(address.address)
this.store.dispatch(loadAccount(addressNetwork))
}),
)
}),
)
this.store.dispatch(
setNewSelectedAccount({
address: addresses[0].address,
network:
await this.internalEthereumProviderService.getCurrentOrDefaultNetworkForOrigin(
TAHO_INTERNAL_ORIGIN,
),
}),
)
}

async readActiveGridPlusAddresses(): Promise<GridPlusAddress[]> {
return this.gridplusService.readAddresses()
}

async getAccountEthBalanceUncached(
addressNetwork: AddressOnNetwork,
): Promise<bigint> {
Expand Down Expand Up @@ -1165,6 +1241,10 @@ export default class Main extends BaseService<never> {
this.ledgerService.emitter.on("address", ({ address }) =>
this.signingService.addTrackedAddress(address, "ledger"),
)

this.gridplusService.emitter.on("address", ({ address }) =>
this.signingService.addTrackedAddress(address, "gridplus"),
)
}

async connectLedgerService(): Promise<void> {
Expand Down Expand Up @@ -1197,6 +1277,10 @@ export default class Main extends BaseService<never> {
})
}

async connectGridplusService(): Promise<void> {
this.store.dispatch(resetGridPlusState())
}

async connectInternalSignerService(): Promise<void> {
this.internalSignerService.emitter.on("internalSigners", (signers) => {
this.store.dispatch(updateInternalSigners(signers))
Expand Down Expand Up @@ -1825,9 +1909,9 @@ export default class Main extends BaseService<never> {
AnalyticsEvent.NEW_ACCOUNT_TO_TRACK,
{
description: `
This event is fired when any address on a network is added to the tracked list.
Note: this does not track recovery phrase(ish) import! But when an address is used
This event is fired when any address on a network is added to the tracked list.

Note: this does not track recovery phrase(ish) import! But when an address is used
on a network for the first time (read-only or recovery phrase/ledger/keyring/private key).
`,
},
Expand Down
6 changes: 4 additions & 2 deletions background/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
"watch": "webpack --mode=development --watch"
},
"dependencies": {
"@ethereumjs/common": "^2.4.0",
"@ethereumjs/tx": "^3.3.0",
"@ethereumjs/common": "^4.1.0",
"@ethereumjs/tx": "^5.0.0",
"@ethersproject/abstract-provider": "5.7.0",
"@ethersproject/abstract-signer": "5.7.0",
"@ethersproject/networks": "5.7.1",
Expand Down Expand Up @@ -58,9 +58,11 @@
"dexie": "^3.0.4",
"emittery": "^0.9.2",
"ethers": "5.7.2",
"gridplus-sdk": "^2.5.2",
"immer": "^9.0.1",
"jsondiffpatch": "^0.4.1",
"lodash": "^4.17.21",
"rlp": "^3.0.0",
"sinon": "^14.0.0",
"siwe": "^1.1.0",
"util": "^0.12.4",
Expand Down
2 changes: 2 additions & 0 deletions background/redux-slices/accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const enum AccountType {
PrivateKey = "private-key",
Imported = "imported",
Ledger = "ledger",
GridPlus = "gridplus",
Internal = "internal",
}

Expand All @@ -41,6 +42,7 @@ export const accountTypes = [
AccountType.Imported,
AccountType.PrivateKey,
AccountType.Ledger,
AccountType.GridPlus,
AccountType.ReadOnly,
]

Expand Down
98 changes: 98 additions & 0 deletions background/redux-slices/gridplus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { createSlice } from "@reduxjs/toolkit"
import { createBackgroundAsyncThunk } from "./utils"
import { type GridPlusAddress } from "../services/gridplus"

const MOCKED_ONBOARDING = process.env.MOCKED_GRIDPLUS_ONBOARDING === "true"

export type GridPlusState = {
importableAddresses: string[]
activeAddresses: GridPlusAddress[]
}

export const initialState: GridPlusState = {
importableAddresses: [],
activeAddresses: [],
}

const gridplusSlice = createSlice({
name: "gridplus",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Everywhere we refer to gridplus should be grid-plus, including filenames, as capital letters/separate words translate to - when we move outside of camel-casing. Anywhere we then import gridplus should be gridPlus, etc, for consistency.

initialState,
reducers: {
resetGridPlusState: (immerState) => {
immerState.importableAddresses = []
immerState.activeAddresses = []
},
setImportableAddresses: (
immerState,
{ payload: importableAddresses }: { payload: string[] },
) => {
immerState.importableAddresses = importableAddresses
},
setActiveAddresses: (
immerState,
{ payload: activeAddresses }: { payload: GridPlusAddress[] },
) => {
immerState.activeAddresses = activeAddresses
},
},
})

export const {
resetGridPlusState,
setImportableAddresses,
setActiveAddresses,
} = gridplusSlice.actions

export default gridplusSlice.reducer

export const connectGridplus = createBackgroundAsyncThunk(
"gridplus/connect",
async (
{ deviceId, password }: { deviceId?: string; password?: string },
{ extra: { main } },
) => main.connectGridplus({ deviceId, password }),
)

export const pairGridplusDevice = createBackgroundAsyncThunk(
"gridplus/pairDevice",
async ({ pairingCode }: { pairingCode: string }, { extra: { main } }) =>
main.pairGridplusDevice({ pairingCode }),
)

export const fetchGridPlusAddresses = createBackgroundAsyncThunk(
"gridplus/fetchAddresses",
async (
{
n = 10,
startPath = [0x80000000 + 44, 0x80000000 + 60, 0x80000000, 0, 0],
}: { n?: number; startPath?: number[] },
{ dispatch, extra: { main } },
) => {
if (MOCKED_ONBOARDING) {
return dispatch(
setImportableAddresses(["0xdfb2682febe6ea96682b1018702958980449b7db"]),
)
}
return dispatch(
setImportableAddresses(
await main.fetchGridPlusAddresses({ n, startPath }),
),
)
},
)

export const importGridPlusAddresses = createBackgroundAsyncThunk(
"gridplus/importAddresses",
async (
{ addresses }: { addresses: GridPlusAddress[] },
{ extra: { main } },
) => main.importGridPlusAddresses({ addresses }),
)

export const initializeActiveAddresses = createBackgroundAsyncThunk(
"gridplus/initializeActiveAddresses",
async (_, { extra: { main }, dispatch }) => {
const activeAddresses = await main.readActiveGridPlusAddresses()
return dispatch(setActiveAddresses(activeAddresses))
},
)
2 changes: 2 additions & 0 deletions background/redux-slices/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import signingReducer from "./signing"
import earnReducer from "./earn"
import nftsReducer from "./nfts"
import pricesReducer from "./prices"
import gridplusReducer from "./gridplus"

const mainReducer = combineReducers({
account: accountsReducer,
Expand All @@ -34,6 +35,7 @@ const mainReducer = combineReducers({
abilities: abilitiesReducer,
nfts: nftsReducer,
prices: pricesReducer,
gridplus: gridplusReducer,
})

export default mainReducer
Expand Down
Loading