-
Notifications
You must be signed in to change notification settings - Fork 4
feat: added wallet connect integration via api #357
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
base: main
Are you sure you want to change the base?
Changes from all commits
4ea8c2d
d5b5106
93fe671
2c12df5
76921bf
cefadbe
74143e4
05fc396
374928f
74b3a05
c042e31
1145841
561016a
e3e835e
c3cb994
89fddcd
c4e65a4
a910cac
4cee8a1
2bad439
8bc2054
e22cc85
b86f9ed
b652178
d100d42
aea5079
7a56cfa
7d03734
1aaeb98
2f6622c
2b74b33
bfffabd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,4 +36,4 @@ | |
| "peerDependencies": { | ||
| "@playwright/test": "^1.51.1" | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,7 +29,7 @@ export type BrowserOptions = { | |
| type OptionsBrowserContext = { | ||
| // If contextDataDir is undefined - will be created temp dir for context data. | ||
| // Else contextDataDir is not undefined - will be created user dir for context data in current folder. | ||
| contextDataDir: string; | ||
| contextDataDir?: string; | ||
| browserOptions?: BrowserOptions; | ||
| }; | ||
|
|
||
|
|
@@ -41,7 +41,7 @@ export class BrowserContextService { | |
| private readonly logger = new ConsoleLogger(BrowserContextService.name); | ||
|
|
||
| constructor( | ||
| public walletExtensionStartPath: string, | ||
| public walletExtensionStartPath: string | null, | ||
| public options: OptionsBrowserContext, | ||
| ) { | ||
| this.defaultBrowserOptions = { | ||
|
|
@@ -50,7 +50,9 @@ export class BrowserContextService { | |
| args: [ | ||
| '--lang=en-US', | ||
| '--disable-dev-shm-usage', | ||
| `--disable-extensions-except=${this.walletExtensionStartPath}`, | ||
| this.walletExtensionStartPath | ||
| ? `--disable-extensions-except=${this.walletExtensionStartPath}` | ||
| : '', | ||
| '--js-flags="--max-old-space-size=2048"', | ||
| ], | ||
| permissions: ['clipboard-read', 'clipboard-write'], | ||
|
|
@@ -63,10 +65,6 @@ export class BrowserContextService { | |
| } | ||
|
|
||
| async initBrowserContext() { | ||
| this.logger.debug( | ||
| `Starting a new browser context (temp context: ${!this.options | ||
| .contextDataDir})`, | ||
| ); | ||
| let browserContextPath = ''; | ||
|
|
||
| if (this.options.contextDataDir) { | ||
|
|
@@ -80,6 +78,10 @@ export class BrowserContextService { | |
| }); | ||
| } | ||
|
|
||
| this.logger.debug( | ||
| `Starting a new browser context (temp context: ${browserContextPath})`, | ||
| ); | ||
|
|
||
| let attemptsLeft = 3; | ||
| while (attemptsLeft > 0) { | ||
| try { | ||
|
|
@@ -111,10 +113,12 @@ export class BrowserContextService { | |
| await this.browserContext.addCookies(this.options.browserOptions.cookies); | ||
| } | ||
|
|
||
| let [background] = this.browserContext.serviceWorkers(); | ||
| if (!background) | ||
| background = await this.browserContext.waitForEvent('serviceworker'); | ||
| this.extensionId = background.url().split('/')[2]; | ||
| if (this.walletExtensionStartPath) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can use the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no, browser context doesn’t know anything about wallets |
||
| let [background] = this.browserContext.serviceWorkers(); | ||
| if (!background) | ||
| background = await this.browserContext.waitForEvent('serviceworker'); | ||
| this.extensionId = background.url().split('/')[2]; | ||
| } | ||
| } | ||
|
|
||
| async closePages() { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,7 +53,10 @@ type BrowserServiceOptions = { | |
| export class BrowserService { | ||
| private logger = new ConsoleLogger(BrowserService.name); | ||
| private walletPage: WalletPage< | ||
| WalletConnectTypes.WC | WalletConnectTypes.EOA | WalletConnectTypes.IFRAME | ||
| | WalletConnectTypes.WC | ||
| | WalletConnectTypes.WC_SDK | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we unite the |
||
| | WalletConnectTypes.EOA | ||
| | WalletConnectTypes.IFRAME | ||
| >; | ||
| private browserContextService: BrowserContextService; | ||
| public ethereumNodeService: EthereumNodeService; | ||
|
|
@@ -85,8 +88,13 @@ export class BrowserService { | |
| } else { | ||
| await this.setup(); | ||
| await this.walletPage.setupNetwork(this.options.networkConfig); | ||
| await this.walletPage.changeNetwork(this.options.networkConfig.chainName); | ||
| await this.browserContextService.closePages(); | ||
|
|
||
| if (this.options.walletConfig.WALLET_TYPE !== WalletConnectTypes.WC_SDK) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's move it to the private method? |
||
| await this.walletPage.changeNetwork( | ||
| this.options.networkConfig.chainName, | ||
| ); | ||
| await this.browserContextService.closePages(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -98,10 +106,12 @@ export class BrowserService { | |
| const account = this.ethereumNodeService.getAccount(); | ||
| await this.setup(); | ||
|
|
||
| if (!(await this.walletPage.isWalletAddressExist(account.address))) { | ||
| await this.walletPage.importKey(account.secretKey); | ||
| } else { | ||
| await this.walletPage.changeWalletAccountByAddress(account.address); | ||
| if (this.options.walletConfig.WALLET_TYPE !== WalletConnectTypes.WC_SDK) { | ||
| if (!(await this.walletPage.isWalletAddressExist(account.address))) { | ||
| await this.walletPage.importKey(account.secretKey); | ||
| } else { | ||
| await this.walletPage.changeWalletAccountByAddress(account.address); | ||
| } | ||
| } | ||
|
|
||
| await this.walletPage.setupNetwork({ | ||
|
|
@@ -114,10 +124,22 @@ export class BrowserService { | |
| this.options.nodeConfig.rpcUrlToMock, | ||
| this.browserContextService.browserContext, | ||
| ); | ||
| await this.browserContextService.closePages(); | ||
| if (this.options.walletConfig.WALLET_TYPE !== WalletConnectTypes.WC_SDK) { | ||
| await this.browserContextService.closePages(); | ||
| } | ||
| } | ||
|
|
||
| async setup() { | ||
| if (this.options.walletConfig.WALLET_TYPE === WalletConnectTypes.WC_SDK) { | ||
Vorobeyko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| this.browserContextService = new BrowserContextService(null, { | ||
| browserOptions: this.options.browserOptions, | ||
| }); | ||
|
|
||
| await this.browserContextService.initBrowserContext(); | ||
| this.setWalletPage(); | ||
| await this.walletPage.setup(); | ||
| return; | ||
| } | ||
| const extensionService = new ExtensionService(); | ||
|
|
||
| const extensionPath = await extensionService.getExtensionDirFromId( | ||
|
|
@@ -128,6 +150,7 @@ export class BrowserService { | |
| const contextDataDir = `${DEFAULT_BROWSER_CONTEXT_DIR_NAME}_${ | ||
| mnemonicToAccount(this.options.accountConfig.SECRET_PHRASE).address | ||
| }_isFork-${this.isFork}_${this.options.walletConfig.WALLET_NAME}`; | ||
|
|
||
| this.browserContextService = new BrowserContextService(extensionPath, { | ||
| contextDataDir, | ||
| browserOptions: this.options.browserOptions, | ||
|
|
@@ -148,6 +171,24 @@ export class BrowserService { | |
| } | ||
|
|
||
| private setWalletPage() { | ||
| if (this.options.walletConfig.WALLET_TYPE === WalletConnectTypes.WC_SDK) { | ||
Vorobeyko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| this.walletPage = new WALLET_PAGES[this.options.walletConfig.WALLET_NAME]( | ||
| { | ||
| browserContext: this.browserContextService.browserContext, | ||
| walletConfig: this.options.walletConfig, | ||
| accountConfig: this.options.accountConfig, | ||
| standConfig: { | ||
| chainId: this.options.networkConfig.chainId, | ||
| standUrl: this.options.standUrl, | ||
| rpcUrl: | ||
| this.ethereumNodeService?.state.nodeUrl || | ||
| this.options.networkConfig.rpcUrl, | ||
| }, | ||
| }, | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| const extension = new Extension(this.browserContextService.extensionId); | ||
| const extensionWalletPage = new WALLET_PAGES[ | ||
| this.options.walletConfig.EXTENSION_WALLET_NAME | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| export * from './network'; | ||
| export * from './requestManager'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| import { Chain, HDAccount } from 'viem'; | ||
| import { NetworkConfig } from '../../wallets.constants'; | ||
| import { SUPPORTED_CHAINS, buildChainFromNetwork } from '../constants'; | ||
| import { SignClient } from '@walletconnect/sign-client/dist/types/client'; | ||
|
|
||
| export class NetworkSettings { | ||
| public activeChainId: number; | ||
| public networksByChainId = new Map<number, NetworkConfig>(); | ||
| public chainIdByName = new Map<string, number>(); | ||
|
|
||
| constructor(private client: SignClient, private hdAccount: HDAccount) {} | ||
|
|
||
| private normalizeChainName(name: string) { | ||
| return name.trim().toLowerCase(); | ||
| } | ||
|
|
||
| private getActiveSession() { | ||
| if (!this.client) throw new Error('WC client not initialized'); | ||
| const sessions = this.client.session.getAll(); | ||
| const session = sessions[0]; | ||
| if (!session) throw new Error('No active WC session'); | ||
| return session; | ||
| } | ||
|
|
||
| async setupNetwork(networkConfig: NetworkConfig): Promise<void> { | ||
| this.networksByChainId.set(networkConfig.chainId, networkConfig); | ||
| this.chainIdByName.set( | ||
| this.normalizeChainName(networkConfig.chainName), | ||
| networkConfig.chainId, | ||
| ); | ||
| } | ||
|
|
||
| async addNetwork(networkConfig: NetworkConfig): Promise<void> { | ||
| if (!this.client) throw new Error('WC client not initialized'); | ||
|
|
||
| const session = this.getActiveSession(); | ||
| const ns = session.namespaces?.eip155; | ||
| if (!ns) throw new Error('Session has no eip155 namespace'); | ||
|
|
||
| const addr = this.hdAccount.address; | ||
| const newAccount = `eip155:${networkConfig.chainId}:${addr}`; | ||
|
|
||
| const newNamespaces = { | ||
| ...session.namespaces, | ||
| eip155: { | ||
| accounts: Array.from(new Set([...(ns.accounts ?? []), newAccount])), | ||
| methods: ns.methods ?? [], | ||
| events: ns.events ?? [], | ||
| }, | ||
| }; | ||
|
|
||
| await this.client.update({ | ||
| topic: session.topic, | ||
| namespaces: newNamespaces, | ||
| }); | ||
|
|
||
| this.networksByChainId.set(networkConfig.chainId, networkConfig); | ||
| this.chainIdByName.set( | ||
| this.normalizeChainName(networkConfig.chainName), | ||
| networkConfig.chainId, | ||
| ); | ||
| } | ||
|
|
||
| async changeNetwork(networkName: string): Promise<Chain> { | ||
| if (!this.client) throw new Error('WC client not initialized'); | ||
|
|
||
| const normalized = this.normalizeChainName(networkName); | ||
| const chainId = this.chainIdByName.get(normalized); | ||
| const networkConfig = this.networksByChainId.get(chainId); | ||
|
|
||
| if (!chainId) { | ||
| const known = Array.from(this.chainIdByName.keys()).sort(); | ||
| throw new Error( | ||
| `Unknown network "${networkName}". Registered networks: ${known.join( | ||
| ', ', | ||
| )}`, | ||
| ); | ||
| } | ||
| const session = this.getActiveSession(); | ||
| await this.client.emit({ | ||
| topic: session.topic, | ||
| chainId: `eip155:${chainId}`, | ||
| event: { name: 'chainChanged', data: `0x${chainId.toString(16)}` }, | ||
| }); | ||
| const chain = | ||
| SUPPORTED_CHAINS[chainId] ?? buildChainFromNetwork(networkConfig); | ||
| this.activeChainId = chain.id; | ||
|
|
||
| return chain; | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do you print the path instead of the previous value?