Skip to content

Commit 4c3ddd1

Browse files
authored
FEAT - Add 6963 support to injected wallets module (#2076)
* Add 6963 support to injected module * add option to disable usage * update docs * Update readmes * Helper fnc to check for executable js * Update packages/injected/src/wallets.ts * Merge in dev
1 parent 3a128a4 commit 4c3ddd1

File tree

11 files changed

+202
-21
lines changed

11 files changed

+202
-21
lines changed

docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
"@web3-onboard/gas": "^2.1.8",
6868
"@web3-onboard/gnosis": "^2.1.10",
6969
"@web3-onboard/infinity-wallet": "^2.0.4",
70-
"@web3-onboard/injected-wallets": "^2.10.9",
70+
"@web3-onboard/injected-wallets": "^2.10.12.alpha.2",
7171
"@web3-onboard/keepkey": "^2.3.7",
7272
"@web3-onboard/keystone": "^2.3.7",
7373
"@web3-onboard/ledger": "^2.5.1",

docs/src/routes/docs/[...4]wallets/[...14]injected/+page.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ title: Injected Wallets
44

55
# {$frontmatter.title}
66

7-
This module lets web3-onboard automatically detect Browser Injected Wallets such as Metamask or Coinbase Wallet. We recommend you install this module to get the most out of your w3o implementation. This module supports [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) and [recognizes many injected wallets natively](#injected-wallets-supported-natively).
7+
This module lets web3-onboard automatically detect Browser Injected Wallets such as Metamask or Coinbase Wallet. We recommend you install this module to get the most out of your w3o implementation. This module supports [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) and [recognizes many injected wallets natively](#injected-wallets-supported-natively) as well as supports [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) and recognizes any wallet that has implemented 6963 support.
88

99
Note: Make sure to install the core module before installing other modules to w3o.
1010

@@ -189,6 +189,19 @@ const onboard = Onboard({
189189
})
190190
```
191191

192+
### This module supports any injected wallet that has implemented support for [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963)
193+
194+
This can be disabled by passing in `disable6963Support` as true within the injected module init object.
195+
196+
```ts
197+
const injected = injectedModule({ disable6963Support: true })
198+
199+
const onboard = Onboard({
200+
wallets: [injected],
201+
...
202+
})
203+
```
204+
192205
## Display Unavailable Wallets
193206

194207
You may want to display injected wallets that are not currently available to the user and you can use the `displayUnavailable` option to do that:

packages/core/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
"confirmed",
3333
"Injected Wallet",
3434
"Crypto",
35-
"Crypto Wallet"
35+
"Crypto Wallet",
36+
"onchain"
3637
],
3738
"repository": {
3839
"type": "git",

packages/demo/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
"@web3-onboard/gas": "^2.1.7",
4343
"@web3-onboard/gnosis": "^2.2.1",
4444
"@web3-onboard/infinity-wallet": "^2.0.3",
45-
"@web3-onboard/injected-wallets": "^2.10.12-alpha.2",
45+
"@web3-onboard/injected-wallets": "^2.10.12-alpha.3",
4646
"@web3-onboard/keepkey": "^2.3.7",
4747
"@web3-onboard/keystone": "^2.3.7",
4848
"@web3-onboard/ledger": "^2.6.0-alpha.1",

packages/demo/src/App.svelte

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@
222222
223223
const onboard = Onboard({
224224
wallets: [
225-
metamaskSDKWallet,
225+
// metamaskSDKWallet,
226226
injected,
227227
ledger,
228228
trezor,
@@ -602,6 +602,10 @@
602602
const updateTheme = () => {
603603
onboard.state.actions.updateTheme(selectedTheme)
604604
}
605+
606+
function isSVG(str) {
607+
return str.includes('<svg')
608+
}
605609
</script>
606610
607611
<style>
@@ -816,7 +820,15 @@
816820
{#each $wallets$ as { icon, label, accounts, chains, provider, instance }}
817821
<div class="connected-wallet" data-testid="connected-wallet">
818822
<div class="flex-centered" style="width: 10rem;">
819-
<div style="width: 2rem; height: 2rem">{@html icon}</div>
823+
<div style="width: 2rem; height: 2rem">
824+
{#if isSVG(icon)}
825+
<!-- render svg string -->
826+
{@html icon}
827+
{:else}
828+
<!-- load img url -->
829+
<img style="width: 2rem; height: 2rem" src={icon} alt="logo" />
830+
{/if}
831+
</div>
820832
<span data-testid={label}>{label}</span>
821833
</div>
822834

packages/injected/README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Quickstart
44

5-
To allow all injected wallets that are supported, don't pass in any options:
5+
To allow all injected wallets that are supported natively by web3-onboard or wallets that have implemented [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963) support - don't pass in any options:
66

77
```javascript
88
import Onboard from '@web3-onboard/core'
@@ -40,6 +40,19 @@ const connectedWallets = await onboard.connectWallet()
4040
console.log(connectedWallets)
4141
```
4242

43+
### This module supports any injected wallet that has implemented support for [EIP-6963](https://eips.ethereum.org/EIPS/eip-6963)
44+
45+
This can be disabled by passing in `disable6963Support` as true within the injected module init object.
46+
47+
```ts
48+
const injected = injectedModule({ disable6963Support: true })
49+
50+
const onboard = Onboard({
51+
wallets: [injected],
52+
...
53+
})
54+
```
55+
4356
### Injected Wallets Supported Natively
4457

4558
- Metamask - _Desktop & Mobile_

packages/injected/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@web3-onboard/injected-wallets",
3-
"version": "2.10.12-alpha.2",
3+
"version": "2.10.12-alpha.3",
44
"description": "Injected wallet module for connecting browser extension and mobile wallets to Web3-Onboard. Web3-Onboard makes it simple to connect Ethereum hardware and software wallets to your dapp. Features standardised spec compliant web3 providers for all supported wallets, framework agnostic modern javascript UI with code splitting, CSS customization, multi-chain and multi-account support, reactive wallet state subscriptions and real-time transaction state change notifications.",
55
"keywords": [
66
"Ethereum",
@@ -34,7 +34,9 @@
3434
"DeFi Wallet",
3535
"Fordefi",
3636
"Coin98 Wallet",
37-
"FoxWallet"
37+
"FoxWallet",
38+
"6963",
39+
"onchain"
3840
],
3941
"repository": {
4042
"type": "git",

packages/injected/src/helpers.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,34 @@ export const isWalletAvailable = (
4444
checkProviderIdentity({ provider, device })
4545
)
4646
}
47+
48+
export
49+
function containsExecutableJavaScript(svgString: string): boolean {
50+
if (!svgString) return false
51+
// Regular expression to match <script> tags
52+
const scriptTagRegex = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi
53+
54+
// Regular expression to match event handler attributes (e.g., onclick, onload)
55+
const eventHandlerRegex = /\bon[a-z]+\s*=\s*["']?(?:javascript:)?/gi
56+
57+
// Regular expression to match href or xlink:href attributes containing "javascript:"
58+
const hrefJavaScriptRegex = /\b(href|xlink:href)\s*=\s*["']?javascript:/gi
59+
60+
// Check for <script> tags
61+
if (scriptTagRegex.test(svgString)) {
62+
return true
63+
}
64+
65+
// Check for event handlers
66+
if (eventHandlerRegex.test(svgString)) {
67+
return true
68+
}
69+
70+
// Check for "javascript:" in href or xlink:href
71+
if (hrefJavaScriptRegex.test(svgString)) {
72+
return true
73+
}
74+
75+
// No executable JavaScript found
76+
return false
77+
}

packages/injected/src/index.ts

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,71 @@
11
import uniqBy from 'lodash.uniqby'
2-
import type { WalletInit } from '@web3-onboard/common'
2+
import { createEIP1193Provider, type WalletInit } from '@web3-onboard/common'
33
import { ProviderLabel } from './types.js'
44
import standardWallets from './wallets.js'
5-
import { validateWalletOptions } from './validation.js'
6-
import { defaultWalletUnavailableMsg, isWalletAvailable } from './helpers.js'
5+
import {
6+
validateEIP6963ProviderDetail,
7+
validateWalletOptions
8+
} from './validation.js'
9+
import {
10+
containsExecutableJavaScript,
11+
defaultWalletUnavailableMsg,
12+
isWalletAvailable
13+
} from './helpers.js'
714

815
import type {
916
InjectedWalletOptions,
1017
CustomWindow,
11-
InjectedWalletModule
18+
InjectedWalletModule,
19+
EIP6963AnnounceProviderEvent,
20+
InjectedProvider
1221
} from './types.js'
1322

1423
declare const window: CustomWindow
1524

1625
export { ProviderIdentityFlag, ProviderLabel } from './types.js'
1726

27+
const providers6963: InjectedWalletModule[] = []
28+
function checkFor6963Providers() {
29+
// Add event listener for 'eip6963:announceProvider' event
30+
console.log('even listener added')
31+
window.addEventListener('eip6963:announceProvider', (event: Event) => {
32+
const eipEvent = event as EIP6963AnnounceProviderEvent
33+
const { detail } = eipEvent
34+
if (!detail) return
35+
36+
if (eipEvent) {
37+
const result = validateEIP6963ProviderDetail(detail)
38+
39+
if (result && result.error) throw result.error
40+
}
41+
42+
const { info, provider } = detail
43+
const { name, icon } = info
44+
45+
if (containsExecutableJavaScript(icon)) {
46+
console.error(
47+
`The icon for injected wallet: ${name} contains executable JavaScript and has been blocked.`
48+
)
49+
return
50+
}
51+
52+
// Push the provider information to the providers6963 array
53+
providers6963.push({
54+
label: name,
55+
getIcon: async () => icon,
56+
getInterface: async () => ({
57+
provider: createEIP1193Provider(provider)
58+
}),
59+
platforms: ['all'],
60+
eip6963Provider: createEIP1193Provider(provider) as InjectedProvider,
61+
checkProviderIdentity: ({ provider }) => !!provider
62+
})
63+
})
64+
65+
// Dispatch a custom event to request the provider information
66+
window.dispatchEvent(new CustomEvent('eip6963:requestProvider'))
67+
}
68+
1869
function injected(options?: InjectedWalletOptions): WalletInit {
1970
if (typeof window === 'undefined') return () => null
2071

@@ -24,6 +75,8 @@ function injected(options?: InjectedWalletOptions): WalletInit {
2475
if (result && result.error) throw result.error
2576
}
2677

78+
!options?.disable6963Support && checkFor6963Providers()
79+
2780
return helpers => {
2881
const { device } = helpers
2982

@@ -37,18 +90,25 @@ function injected(options?: InjectedWalletOptions): WalletInit {
3790

3891
// combine custom with standard wallets and dedupe
3992
const allWallets = uniqBy(
40-
[...custom, ...standardWallets],
93+
[...custom, ...standardWallets, ...providers6963],
4194
({ label }) => label
4295
)
4396

4497
const wallets = allWallets.reduce(
4598
(acc: InjectedWalletModule[], wallet: InjectedWalletModule) => {
46-
const { label, platforms, injectedNamespace, checkProviderIdentity } =
47-
wallet
99+
const {
100+
label,
101+
platforms,
102+
injectedNamespace,
103+
checkProviderIdentity,
104+
eip6963Provider
105+
} = wallet
48106

49107
const walletFilters = filter[label]
50108
const filteredWallet = walletFilters === false
51-
const provider = window[injectedNamespace] as CustomWindow['ethereum']
109+
const provider =
110+
eip6963Provider ||
111+
(window[injectedNamespace!] as CustomWindow['ethereum'])
52112

53113
const walletAvailable = isWalletAvailable(
54114
provider,

packages/injected/src/types.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ export enum ProviderIdentityFlag {
6868
SubWallet = 'isSubWallet',
6969
Kayros = 'isKayros',
7070
FoxWallet = 'isFoxWallet',
71-
Lif3Wallet = 'isLif3Wallet',
71+
Lif3Wallet = 'isLif3Wallet'
7272
}
7373

7474
/**
@@ -279,15 +279,47 @@ export interface InjectedWalletOptions {
279279
walletUnavailableMessage?: (wallet: WalletModule) => string
280280
/**Function that can be used to sort the order of wallets that are displayed */
281281
sort?: (wallets: WalletModule[]) => WalletModule[]
282+
/** A boolean that can be passed to disable supporting 6963 (https://eips.ethereum.org/EIPS/eip-6963)
283+
* which will display wallets available on the browser
284+
*/
285+
disable6963Support?: boolean
282286
}
283287

284288
export interface InjectedWalletModule extends WalletModule {
285-
injectedNamespace: InjectedNameSpace
289+
injectedNamespace?: InjectedNameSpace
286290
checkProviderIdentity: (helpers: { provider: any; device: Device }) => boolean
287291
platforms: Platform[]
288292
/**
289293
* A Url to link users to a download page for the wallet
290294
* to be shown if not installed or available on the browser
291295
*/
292296
externalUrl?: string
297+
eip6963Provider?: InjectedProvider
298+
}
299+
300+
// Define a class for the "eip6963:requestProvider" event
301+
export class EIP6963RequestProviderEvent extends Event {
302+
constructor() {
303+
super('eip6963:requestProvider')
304+
}
305+
}
306+
307+
// Define an interface for the "eip6963:announceProvider" event
308+
export interface EIP6963AnnounceProviderEvent extends Event {
309+
type: 'eip6963:announceProvider'
310+
detail: EIP6963ProviderDetail
311+
}
312+
313+
// Define an interface for the provider details
314+
export interface EIP6963ProviderDetail {
315+
info: EIP6963ProviderInfo
316+
provider: EIP1193Provider
317+
}
318+
319+
// Define an interface for the provider information
320+
export interface EIP6963ProviderInfo {
321+
uuid: string // Unique identifier of the wallet extension announcement, keep in mind it changes on every request-announcement cycle
322+
name: string // Name of the wallet extension
323+
icon: string // Icon for the wallet extension
324+
rdns: string // Reverse DNS name of the wallet extension
293325
}

packages/injected/src/validation.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import Joi from 'joi'
2-
import { InjectedWalletOptions } from './types.js'
2+
import { EIP6963ProviderDetail, InjectedWalletOptions } from './types.js'
33
import { validate, type ValidateReturn } from '@web3-onboard/common'
44

55
const walletModule = Joi.object({
@@ -25,9 +25,26 @@ const walletOptions = Joi.object({
2525
displayUnavailable: [Joi.boolean(), Joi.array().items(Joi.string())],
2626
walletUnavailableMessage: Joi.function(),
2727
sort: Joi.function(),
28-
externalUrl: Joi.string()
28+
externalUrl: Joi.string(),
29+
disable6963Support: Joi.boolean()
2930
})
3031

3132
export const validateWalletOptions = (
3233
data: InjectedWalletOptions | Partial<InjectedWalletOptions>
3334
): ValidateReturn => validate(walletOptions, data)
35+
36+
const eip6963ProviderInfo = Joi.object({
37+
uuid: Joi.string().required(),
38+
name: Joi.string().required(),
39+
icon: Joi.string().required(),
40+
rdns: Joi.string().required()
41+
})
42+
43+
const eip6963ProviderDetail = Joi.object({
44+
info: eip6963ProviderInfo.required(),
45+
provider: Joi.object().required()
46+
})
47+
48+
export const validateEIP6963ProviderDetail = (
49+
data: EIP6963ProviderDetail
50+
): ValidateReturn => validate(eip6963ProviderDetail, data)

0 commit comments

Comments
 (0)