Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/consts/featureFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface FeatureFlags {
testnetMode: boolean
tokenAndDefiAutoDiscovery: boolean
apiForFunctionSelectors: boolean
erc4337: boolean
/**
* Off by default for privacy: passively bulk-resolving ENS/Namoshi for all
* accounts links them together. When enabled, the wallet keeps every account's
Expand All @@ -20,5 +21,6 @@ export const defaultFeatureFlags: FeatureFlags = {
testnetMode: false,
tokenAndDefiAutoDiscovery: true,
apiForFunctionSelectors: true,
erc4337: true,
keepEnsProfilesUpToDate: false
}
14 changes: 12 additions & 2 deletions src/controllers/estimation/estimation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import ErrorHumanizerError from '../../classes/ErrorHumanizerError'
import { Account, IAccountsController } from '../../interfaces/account'
import { IActivityController } from '../../interfaces/activity'
import { ErrorRef } from '../../interfaces/eventEmitter'
import { IFeatureFlagsController } from '../../interfaces/featureFlags'
import { IKeystoreController } from '../../interfaces/keystore'
import { INetworksController } from '../../interfaces/network'
import { IPortfolioController } from '../../interfaces/portfolio'
Expand Down Expand Up @@ -30,6 +31,8 @@ export class EstimationController extends EventEmitter {

#portfolio: IPortfolioController

#featureFlags: IFeatureFlagsController

status: EstimationStatus = EstimationStatus.Initial

estimation: FullEstimationSummary | null = null
Expand Down Expand Up @@ -65,14 +68,16 @@ export class EstimationController extends EventEmitter {
provider: RPCProvider,
portfolio: IPortfolioController,
bundlerSwitcher: BundlerSwitcher,
activity: IActivityController
activity: IActivityController,
featureFlags: IFeatureFlagsController
) {
super()
this.#keystore = keystore
this.#accounts = accounts
this.#networks = networks
this.#provider = provider
this.#portfolio = portfolio
this.#featureFlags = featureFlags
this.#bundlerSwitcher = bundlerSwitcher
this.#activity = activity
}
Expand Down Expand Up @@ -112,7 +117,12 @@ export class EstimationController extends EventEmitter {
return
}

const baseAcc = getBaseAccount(account, accountState, network)
const baseAcc = getBaseAccount(
account,
accountState,
network,
this.#featureFlags.isFeatureEnabled('erc4337')
)

// Take the fee tokens from two places: the user's tokens and his gasTank
// The gasTank tokens participate on each network as they belong everywhere
Expand Down
84 changes: 84 additions & 0 deletions src/controllers/gasPrice/gasPrice.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { jest } from '@jest/globals'

import { Network } from '../../interfaces/network'
import { RPCProvider } from '../../interfaces/provider'
import { BaseAccount } from '../../libs/account/BaseAccount'
import { gasPriceToBundlerFormat, getGasPriceRecommendations } from '../../libs/gasPrice/gasPrice'
import { getAvailableBunlders } from '../../services/bundlers/getBundler'
import { GasPriceController } from './gasPrice'

jest.mock('../../services/bundlers/getBundler', () => ({
getAvailableBunlders: jest.fn()
}))

jest.mock('../../libs/gasPrice/gasPrice', () => ({
getGasPriceRecommendations: jest.fn(),
gasPriceToBundlerFormat: jest.fn()
}))

const getAvailableBunldersMock = getAvailableBunlders as jest.Mock
const getGasPriceRecommendationsMock = getGasPriceRecommendations as jest.Mock
const gasPriceToBundlerFormatMock = gasPriceToBundlerFormat as jest.Mock

const network = { chainId: 1n, name: 'Ethereum' } as Network
const provider = {} as RPCProvider
const baseAccount = {
supportsBundlerEstimation: () => false
} as BaseAccount

const getSignAccountOpState = () =>
({
estimation: null,
readyToSign: false,
stopRefetching: false
}) as any

describe('GasPriceController', () => {
beforeEach(() => {
getAvailableBunldersMock.mockReset()
getGasPriceRecommendationsMock.mockReset()
gasPriceToBundlerFormatMock.mockReset()
gasPriceToBundlerFormatMock.mockReturnValue({})
getGasPriceRecommendationsMock.mockResolvedValue({ gasPrice: {} } as never)
})

test('does not call bundlers when ERC-4337 is disabled', async () => {
const fetchGasPrices = jest.fn()
getAvailableBunldersMock.mockReturnValue([{ fetchGasPrices }])

const controller = new GasPriceController(
network,
provider,
baseAccount,
getSignAccountOpState,
() => false
)

await controller.fetch()

expect(fetchGasPrices).not.toHaveBeenCalled()
expect(getGasPriceRecommendationsMock).toHaveBeenCalled()
})

test('calls bundlers when ERC-4337 is enabled and the account needs bundler gas prices', async () => {
const gasPrices = {
slow: { maxFeePerGas: '0x1', maxPriorityFeePerGas: '0x1' }
}
const fetchGasPrices = jest.fn().mockResolvedValue(gasPrices as never)
getAvailableBunldersMock.mockReturnValue([{ fetchGasPrices }])

const controller = new GasPriceController(
network,
provider,
baseAccount,
getSignAccountOpState,
() => true
)

await controller.fetch()

expect(fetchGasPrices).toHaveBeenCalledWith(network)
expect(getGasPriceRecommendationsMock).not.toHaveBeenCalled()
expect(controller.gasPrices).toBe(gasPrices)
})
})
16 changes: 14 additions & 2 deletions src/controllers/gasPrice/gasPrice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export class GasPriceController extends EventEmitter {

#baseAccount: BaseAccount

#getIsErc4337Enabled: () => boolean

#getSignAccountOpState: () => {
estimation: EstimationController
readyToSign: boolean
Expand Down Expand Up @@ -50,15 +52,21 @@ export class GasPriceController extends EventEmitter {
estimation: EstimationController
readyToSign: boolean
stopRefetching: boolean
}
},
getIsErc4337Enabled: () => boolean = () => true
) {
super()
this.#network = network
this.#provider = provider
this.#baseAccount = baseAccount
this.#getIsErc4337Enabled = getIsErc4337Enabled
this.#getSignAccountOpState = getSignAccountOpState
}

setBaseAccount(baseAccount: BaseAccount) {
this.#baseAccount = baseAccount
}

async fetch(emitLevelOnFailure: ErrorRef['level'] = 'silent') {
if (this.areGasPricesUsedFromBundlerEstimation) return

Expand All @@ -68,7 +76,11 @@ export class GasPriceController extends EventEmitter {
// estimate, it would fetch the gas price from the bundler estimation itself,
// therefore not being required here
const availableBundlers = getAvailableBunlders(this.#network)
if (availableBundlers.length && !this.#baseAccount.supportsBundlerEstimation()) {
if (
this.#getIsErc4337Enabled() &&
availableBundlers.length &&
!this.#baseAccount.supportsBundlerEstimation()
) {
let timeoutId
const bundlerGasPrices = await Promise.race([
// Promise.any because we want the first success, ignoring errors
Expand Down
16 changes: 12 additions & 4 deletions src/controllers/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ export class MainController extends EventEmitter implements IMainController {
activity: this.activity,
storage: this.storage,
signAccountOpPreference: this.signAccountOpPreference,
featureFlags: this.featureFlags,
phishing: this.phishing,
dapps: this.dapps,
swapProvider: new SwapProviderParallelExecutor(
Expand Down Expand Up @@ -557,6 +558,7 @@ export class MainController extends EventEmitter implements IMainController {
this.callRelayer,
this.storage,
this.signAccountOpPreference,
this.featureFlags,
humanizerInfo as HumanizerMeta,
this.selectedAccount,
this.networks,
Expand Down Expand Up @@ -621,6 +623,7 @@ export class MainController extends EventEmitter implements IMainController {
networks: this.networks,
providers: this.providers,
storage: this.storage,
featureFlags: this.featureFlags,
signAccountOpPreference: this.signAccountOpPreference,
selectedAccount: this.selectedAccount,
keystore: this.keystore,
Expand Down Expand Up @@ -687,10 +690,15 @@ export class MainController extends EventEmitter implements IMainController {
})
})
}
paymasterFactory.init(relayerUrl, fetch, (e: ErrorRef) => {
if (this.requests.currentUserRequest?.kind !== 'calls') return
this.emitError(e)
})
paymasterFactory.init(
relayerUrl,
fetch,
(e: ErrorRef) => {
if (this.requests.currentUserRequest?.kind !== 'calls') return
this.emitError(e)
},
() => this.featureFlags.isFeatureEnabled('erc4337')
)

this.keystore.onUpdate(() => {
if (this.keystore.statuses.unlockWithSecret === 'SUCCESS') {
Expand Down
1 change: 1 addition & 0 deletions src/controllers/requests/requests.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ const prepareTest = async (seedTestDapp = false) => {
networks: mainCtrl.networks,
keystore: mainCtrl.keystore,
portfolio: mainCtrl.portfolio,
featureFlags: mainCtrl.featureFlags,
signAccountOpPreference: mainCtrl.signAccountOpPreference,
externalSignerControllers: {},
activity: mainCtrl.activity,
Expand Down
7 changes: 7 additions & 0 deletions src/controllers/requests/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { AutoLoginStatus, IAutoLoginController } from '../../interfaces/autoLogi
import { Banner } from '../../interfaces/banner'
import { Dapp, DappProviderRequest, IDappsController } from '../../interfaces/dapp'
import { IEventEmitterRegistryController, Statuses } from '../../interfaces/eventEmitter'
import { IFeatureFlagsController } from '../../interfaces/featureFlags'
import { Hex } from '../../interfaces/hex'
import { ExternalSignerController, IKeystoreController } from '../../interfaces/keystore'
import { INetworksController, Network } from '../../interfaces/network'
Expand Down Expand Up @@ -112,6 +113,8 @@ export class RequestsController extends EventEmitter implements IRequestsControl

#portfolio: IPortfolioController

#featureFlags: IFeatureFlagsController

#externalSignerControllers: Partial<{
internal: ExternalSignerController
trezor: ExternalSignerController
Expand Down Expand Up @@ -210,6 +213,7 @@ export class RequestsController extends EventEmitter implements IRequestsControl
relayerUrl,
callRelayer,
portfolio,
featureFlags,
externalSignerControllers,
activity,
phishing,
Expand Down Expand Up @@ -239,6 +243,7 @@ export class RequestsController extends EventEmitter implements IRequestsControl
relayerUrl: string
callRelayer: BindedRelayerCall
portfolio: IPortfolioController
featureFlags: IFeatureFlagsController
externalSignerControllers: Partial<{
internal: ExternalSignerController
trezor: ExternalSignerController
Expand Down Expand Up @@ -275,6 +280,7 @@ export class RequestsController extends EventEmitter implements IRequestsControl
this.#relayerUrl = relayerUrl
this.#callRelayer = callRelayer
this.#portfolio = portfolio
this.#featureFlags = featureFlags
this.#externalSignerControllers = externalSignerControllers
this.#activity = activity
this.#phishing = phishing
Expand Down Expand Up @@ -1907,6 +1913,7 @@ export class RequestsController extends EventEmitter implements IRequestsControl
networks: this.#networks,
keystore: this.#keystore,
portfolio: this.#portfolio,
featureFlags: this.#featureFlags,
signAccountOpPreference: this.#signAccountOpPreference,
externalSignerControllers: this.#externalSignerControllers,
activity: this.#activity,
Expand Down
4 changes: 3 additions & 1 deletion src/controllers/signAccountOp/signAccountOp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,8 @@ const init = async (
provider,
portfolio,
bundlerSwitcher,
activity
activity,
featureFlagsCtrl
)
estimationController.estimation = estimationOrMock
estimationController.hasEstimated = true
Expand Down Expand Up @@ -664,6 +665,7 @@ const init = async (
networks: networksCtrl,
keystore,
portfolio,
featureFlags: featureFlagsCtrl,
signAccountOpPreference,
externalSignerControllers: {},
account,
Expand Down
Loading
Loading