Skip to content

Commit

Permalink
ProviderProxy WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
corbanbrook committed Jul 7, 2023
1 parent 398e6f4 commit eb6e342
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 81 deletions.
71 changes: 52 additions & 19 deletions packages/provider/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
JsonRpcRequest,
JsonRpcResponseCallback,
JsonRpcSender,
maybeChainId
maybeChainId,
findNetworkConfig
} from '@0xsequence/network'

import { resolveArrayProperties, Signer } from '@0xsequence/wallet'
Expand Down Expand Up @@ -188,21 +189,13 @@ export class Web3Signer extends Signer implements TypedDataSigner {
async getWalletConfig(chainId?: ChainIdLike): Promise<commons.config.Config> {
const reqChainId = maybeChainId(chainId) || this.defaultChainId
if (!reqChainId) throw new Error('chainId is required')
return (await this.provider.send(
'sequence_getWalletConfig',
[reqChainId],
reqChainId
))[0]
return (await this.provider.send('sequence_getWalletConfig', [reqChainId], reqChainId))[0]
}

async getWalletState(chainId?: ChainIdLike): Promise<AccountStatus> {
const reqChainId = maybeChainId(chainId) || this.defaultChainId
if (!reqChainId) throw new Error('chainId is required')
return (await this.provider.send(
'sequence_getWalletState',
[reqChainId],
reqChainId
))[0].status
if (!reqChainId) throw new Error('chainId is required')
return (await this.provider.send('sequence_getWalletState', [reqChainId], reqChainId))[0].status
}

async getNetworks(): Promise<NetworkConfig[]> {
Expand All @@ -219,7 +212,10 @@ export class Web3Signer extends Signer implements TypedDataSigner {
throw new Error(`walletConfig returned zero results for authChainId {authChainId}`)
}

return universal.genericCoderFor(config.version).config.signersOf(config).map((s) => s.address)
return universal
.genericCoderFor(config.version)
.config.signersOf(config)
.map(s => s.address)
}

// signMessage matches implementation from ethers JsonRpcSigner for compatibility, but with
Expand All @@ -233,10 +229,7 @@ export class Web3Signer extends Signer implements TypedDataSigner {
// NOTE: as of ethers v5.5, it switched to using personal_sign, see
// https://github.com/ethers-io/ethers.js/pull/1542 and see
// https://github.com/WalletConnect/walletconnect-docs/issues/32 for additional info.
return provider!.send(
sequenceVerified ? 'sequence_sign' : 'personal_sign',
[ethers.utils.hexlify(data), address]
)
return provider!.send(sequenceVerified ? 'sequence_sign' : 'personal_sign', [ethers.utils.hexlify(data), address])
}

// signTypedData matches implementation from ethers JsonRpcSigner for compatibility, but with
Expand Down Expand Up @@ -331,7 +324,9 @@ export class Web3Signer extends Signer implements TypedDataSigner {

// updateConfig..
// NOTE: this is not supported by the remote wallet by default.
async updateConfig(newConfig?: commons.config.Config): Promise<[commons.config.Config, ethers.providers.TransactionResponse | undefined]> {
async updateConfig(
newConfig?: commons.config.Config
): Promise<[commons.config.Config, ethers.providers.TransactionResponse | undefined]> {
// sequence_updateConfig
const [config, tx] = await this.provider.send('sequence_updateConfig', [newConfig], this.defaultChainId)
if (tx === null) {
Expand Down Expand Up @@ -513,7 +508,9 @@ const hexlifyTransaction = (
}

class UncheckedJsonRpcSigner extends Web3Signer {
sendTransaction(transaction: Deferrable<ethers.providers.TransactionRequest>): Promise<commons.transaction.TransactionResponse> {
sendTransaction(
transaction: Deferrable<ethers.providers.TransactionRequest>
): Promise<commons.transaction.TransactionResponse> {
return this.sendUncheckedTransaction(transaction).then(hash => {
return <commons.transaction.TransactionResponse>{
chainId: 0,
Expand All @@ -530,3 +527,39 @@ class UncheckedJsonRpcSigner extends Web3Signer {
})
}
}

export class ProviderProxy extends Web3Provider {
chainId: number

private providers: Record<number, Web3Provider>

private get _provider(): Web3Provider {
return this.providers[this.chainId]
}

constructor(providers: Record<number, Web3Provider>, chainId: number) {
super(undefined as any, chainId)
this.providers = providers
this.chainId = chainId
}

async send(method: string, params: Array<any>): Promise<any> {
if (method === 'wallet_switchEthereumChain') {
this.chainId = params[0].chainId

this.emit('chainChanged', this.chainId)

return null
}

return this._provider.send(method, params)
}

async getNetwork() {
return this._provider.getNetwork()
}

async getChainId(): Promise<number> {
return this._provider.getChainId()
}
}
144 changes: 82 additions & 62 deletions packages/provider/src/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ import {
updateNetworkConfig,
ensureValidNetworks,
sortNetworks,
findSupportedNetwork
findSupportedNetwork,
networks,
ChainId
} from '@0xsequence/network'
import { getDefaultConnectionInfo, logger } from '@0xsequence/utils'
import { Web3Provider, Web3Signer } from './provider'
import { ProviderProxy, Web3Provider, Web3Signer } from './provider'
import {
MuxMessageProvider,
WindowMessageProvider,
Expand Down Expand Up @@ -99,17 +101,25 @@ export class Wallet implements WalletProvider {
private networks: NetworkConfig[]
private providers: { [chainId: number]: Web3Provider }

constructor(network?: string | number, config?: Partial<ProviderConfig>) {
private providerProxy: ProviderProxy

constructor(chainId?: ChainIdLike, config?: Partial<ProviderConfig>) {
// config is a Partial, so that we may intersect it with the DefaultProviderConfig,
// which allows easy overriding and control of the config.
this.config = { ...DefaultProviderConfig }
if (config) {
this.config = { ...this.config, ...config }
}
if (network) {
this.config.defaultNetworkId = network
} else if (!this.config.defaultNetworkId) {
this.config.defaultNetworkId = 'mainnet'
if (chainId) {
const network = findSupportedNetwork(chainId)

if (network) {
this.config.defaultNetworkId = network.chainId
}
}

if (!this.config.defaultNetworkId) {
this.config.defaultNetworkId = ChainId.MAINNET
}

if (config?.localStorage) {
Expand All @@ -119,6 +129,7 @@ export class Wallet implements WalletProvider {
this.transport = {}
this.networks = []
this.providers = {}
this.providerProxy = new ProviderProxy(this.providers, this.config.defaultNetworkId)
this.connectedSites = new LocalStore('@sequence.connectedSites', [])
this.utils = new WalletUtils(this)
this.init()
Expand Down Expand Up @@ -445,13 +456,21 @@ export class Wallet implements WalletProvider {
throw new Error('networks have not been set by session. connect first.')
}

const network = findNetworkConfig(this.networks, this.config.defaultNetworkId!)
const provider = this.getProvider()

if (!network) {
throw new Error('networks must have a default chain specified')
if (!provider) {
throw new Error('provider not found')
}

return network.chainId
return provider?.getChainId()

// const network = findNetworkConfig(this.networks, this.config.defaultNetworkId!)

// if (!network) {
// throw new Error('networks must have a default chain specified')
// }

// return network.chainId
}

openWallet = async (path?: string, intent?: OpenWalletIntent, networkId?: string | number): Promise<boolean> => {
Expand Down Expand Up @@ -488,62 +507,23 @@ export class Wallet implements WalletProvider {
}
}

let network: NetworkConfig | undefined = findNetworkConfig(this.networks, this.config.defaultNetworkId!)!
if (chainId) {
network = findNetworkConfig(this.networks, chainId)
if (!network) {
throw new Error(`network ${chainId} is not in the network list`)
}
}
const network: NetworkConfig | undefined = findNetworkConfig(this.networks, chainId || this.providerProxy.chainId)!

// return memoized network provider
if (this.providers[network.chainId]) {
return this.providers[network.chainId]
if (!network) {
throw new Error(`network ${chainId} is not in the network list`)
}

// builder web3 provider stack
let provider: Web3Provider

// network.provider may be set by the ProviderConfig override
const rpcProvider = new providers.JsonRpcProvider(getDefaultConnectionInfo(network.rpcUrl), network.chainId)

if (network.isDefaultChain) {
// communicating with defaultChain will prioritize the wallet message transport
const router = new JsonRpcRouter(
[
loggingProviderMiddleware,
exceptionProviderMiddleware,
new EagerProvider({ accountAddress: this.session!.accountAddress, walletContext: this.session!.walletContext }),
new SigningProvider(this.transport!.provider!),
this.transport.cachedProvider!
],
new JsonRpcSender(rpcProvider)
)
// if the provider does not exist, create it
if (!this.providers[network.chainId]) {
this.providers[network.chainId] = this.createProvider(network)
}

provider = new Web3Provider(router, network.chainId)
if (chainId) {
// return memoized network provider
return this.providers[network.chainId]
} else {
// communicating with another chain will bind to that network, but will forward
// any signing-related requests to the wallet message transport
const router = new JsonRpcRouter(
[
loggingProviderMiddleware,
exceptionProviderMiddleware,
new EagerProvider({
accountAddress: this.session!.accountAddress,
walletContext: this.session!.walletContext,
chainId: network.chainId
}),
new SigningProvider(this.transport.provider!),
new CachedProvider({ defaultChainId: network.chainId })
],
new JsonRpcSender(rpcProvider)
)

provider = new Web3Provider(router, network.chainId)
return this.providerProxy
}

this.providers[network.chainId] = provider
return provider
}

getAllProviders(): { [chainId: number]: Web3Provider } {
Expand Down Expand Up @@ -583,6 +563,46 @@ export class Wallet implements WalletProvider {
this.transport.messageProvider?.unregister()
}

private createProvider(network: NetworkConfig) {
// network.provider may be set by the ProviderConfig override
const rpcProvider = new providers.JsonRpcProvider(getDefaultConnectionInfo(network.rpcUrl), network.chainId)

if (network.isDefaultChain) {
// communicating with defaultChain will prioritize the wallet message transport
const router = new JsonRpcRouter(
[
loggingProviderMiddleware,
exceptionProviderMiddleware,
new EagerProvider({ accountAddress: this.session!.accountAddress, walletContext: this.session!.walletContext }),
new SigningProvider(this.transport!.provider!),
this.transport.cachedProvider!
],
new JsonRpcSender(rpcProvider)
)

return new Web3Provider(router, network.chainId)
} else {
// communicating with another chain will bind to that network, but will forward
// any signing-related requests to the wallet message transport
const router = new JsonRpcRouter(
[
loggingProviderMiddleware,
exceptionProviderMiddleware,
new EagerProvider({
accountAddress: this.session!.accountAddress,
walletContext: this.session!.walletContext,
chainId: network.chainId
}),
new SigningProvider(this.transport.provider!),
new CachedProvider({ defaultChainId: network.chainId })
],
new JsonRpcSender(rpcProvider)
)

return new Web3Provider(router, network.chainId)
}
}

private saveSession = async (session: WalletSession) => {
logger.debug('wallet provider: saving session')
const data = JSON.stringify(session)
Expand Down Expand Up @@ -700,7 +720,7 @@ export interface ProviderConfig {
// defaultNetworkId is the primary network of a dapp and the default network a
// provider will communicate. Note: this setting is also configurable from the
// Wallet constructor's first argument.
defaultNetworkId?: string | number
defaultNetworkId?: number

// transports for dapp to wallet jron-rpc communication
transports?: {
Expand Down

0 comments on commit eb6e342

Please sign in to comment.