Skip to content
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
4021c8e
move `react-connect-kit` types to `types` pkg
rossbulat Apr 8, 2025
4d15d69
add extensionAccounts utils to `connect-observables`
rossbulat Apr 8, 2025
5487cfd
use new extension account utils, discontinue `Extensions` class
rossbulat Apr 8, 2025
31a8590
Update library/react-connect-kit/src/ExtensionAccountsProvider/index.tsx
rossbulat Apr 8, 2025
ba78fec
Update library/react-connect-kit/src/ExtensionAccountsProvider/index.tsx
rossbulat Apr 8, 2025
32f4b8e
Update library/observables-connect/src/accounts/index.ts
rossbulat Apr 8, 2025
32000a8
update type
rossbulat Apr 8, 2025
75da995
rename
rossbulat Apr 8, 2025
e1649c6
mv more logic to observable package
rossbulat Apr 8, 2025
243d3a0
refactor extension logic
rossbulat Apr 8, 2025
3a32655
add `observables` file
rossbulat Apr 8, 2025
bc9ed2d
implement new generic extension connect logic
rossbulat Apr 8, 2025
09a9541
mv consts to src
rossbulat Apr 8, 2025
11c3f0b
mv consts to src
rossbulat Apr 8, 2025
dbdef83
disable extension account logic for now
rossbulat Apr 8, 2025
e7791f0
connect kit working with latest exports
rossbulat Apr 8, 2025
febe1ac
group exports
rossbulat Apr 8, 2025
ba54553
tidy up extensions context
rossbulat Apr 8, 2025
07a28ff
use `connectExtensions`
rossbulat Apr 8, 2025
53a127e
amend export scopes
rossbulat Apr 8, 2025
a17e9a3
separate `get` mdoule for getting accounts
rossbulat Apr 8, 2025
66e443e
refactoring
rossbulat Apr 9, 2025
6d0cba6
apply package updates
rossbulat Apr 9, 2025
911493b
bump react-connect-kit
rossbulat Apr 9, 2025
1d2adc8
misc fixes
rossbulat Apr 9, 2025
6616c1f
more logs, don't check local
rossbulat Apr 9, 2025
e8bbac4
make ids not optional, more logs
rossbulat Apr 9, 2025
0c35c52
misc fixes & refactors
rossbulat Apr 9, 2025
748d653
moving files, renames
rossbulat Apr 9, 2025
fffac1b
bring back dappName
rossbulat Apr 9, 2025
aa05108
verify injected ready, allSettled for enable
rossbulat Apr 9, 2025
33312be
remove type
rossbulat Apr 9, 2025
ba7377e
remove unsubs from react
rossbulat Apr 9, 2025
c8ef6a8
accounts from observable
rossbulat Apr 9, 2025
192037c
remove account logic from provider
rossbulat Apr 9, 2025
9257cdb
tidy ups, renames
rossbulat Apr 9, 2025
e41f74a
abstract initial connect, simplify context
rossbulat Apr 9, 2025
7f93a5e
abstract connect function
rossbulat Apr 9, 2025
134c58e
Update library/observables-connect/src/accounts/reconnect.ts
rossbulat Apr 9, 2025
237d82f
Update library/observables-connect/src/extensions/enable.ts
rossbulat Apr 9, 2025
27154c8
bumps
rossbulat Apr 9, 2025
b95987a
revise new api structure, imports
rossbulat Apr 10, 2025
b05a53c
rm fn
rossbulat Apr 10, 2025
3f7098f
bump deps
rossbulat Apr 10, 2025
fb68e98
remove external account check
rossbulat Apr 10, 2025
f4b333a
bumps
rossbulat Apr 10, 2025
85c82f7
shorten folder names
rossbulat Apr 10, 2025
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: 1 addition & 1 deletion library/factories/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
},
"devDependencies": {
"@types/react": "^19.0.12",
"@w3ux/types": "^2.1.0",
"@w3ux/types": "^2.1.2",
"gulp": "^5.0.0",
"gulp-sourcemaps": "^3.0.0",
"gulp-strip-comments": "^2.6.0",
Expand Down
2 changes: 1 addition & 1 deletion library/hooks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
},
"devDependencies": {
"@types/react": "^19.0.12",
"@w3ux/types": "^2.1.0",
"@w3ux/types": "^2.1.2",
"gulp": "^5.0.0",
"gulp-sourcemaps": "^3.0.0",
"gulp-strip-comments": "^2.6.0",
Expand Down
7 changes: 4 additions & 3 deletions library/observables-connect/package.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
{
"name": "@w3ux/observables-connect",
"license": "GPL-3.0-only",
"version": "0.9.1",
"version": "0.9.30",
"type": "module",
"scripts": {
"clear": "rm -rf node_modules dist tsconfig.tsbuildinfo",
"build": "gulp --silent"
},
"dependencies": {
"@w3ux/extension-assets": "^2.2.0"
"@w3ux/extension-assets": "^2.2.0",
"@w3ux/utils": "^2.0.9"
},
"devDependencies": {
"@w3ux/types": "^2.1.0",
"@w3ux/types": "^2.1.6",
"gulp": "^5.0.0",
"gulp-sourcemaps": "^3.0.0",
"gulp-strip-comments": "^2.6.0",
Expand Down
48 changes: 48 additions & 0 deletions library/observables-connect/src/accounts/get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* @license Copyright 2024 w3ux authors & contributors
SPDX-License-Identifier: GPL-3.0-only */

import type { ExtensionAccount, ExtensionEnableResults } from '@w3ux/types'
import { formatExtensionAccounts } from './util'

// Connects to provided extensions and gets all accounts
export const getAccountsFromExtensions = async (
extensions: ExtensionEnableResults,
ss58: number
): Promise<ExtensionAccount[]> => {
try {
const results = await Promise.allSettled(
Array.from(extensions.values()).map(({ extension }) =>
extension.accounts.get()
)
)

const allAccounts: ExtensionAccount[] = []
const extensionEntries = Array.from(extensions.entries())
for (let i = 0; i < results.length; i++) {
const result = results[i]
const source = extensionEntries[i][0]
const signer = extensionEntries[i][1].extension.signer

if (result.status === 'fulfilled') {
const { value } = result

// This is duplicating what `handleExtensionAccountsUpdate` is doing to accounts: --
const accounts = formatExtensionAccounts(value, ss58)
.filter(
({ address }) => !allAccounts.find((a) => address === a.address)
)
.map(({ address, name }) => ({
address,
name,
source,
signer,
}))
allAccounts.push(...accounts)
}
}
return allAccounts
} catch (e) {
console.error('Error during account formatting: ', e)
return []
}
}
6 changes: 6 additions & 0 deletions library/observables-connect/src/accounts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* @license Copyright 2024 w3ux authors & contributors
SPDX-License-Identifier: GPL-3.0-only */

export * from './get'
export * from './unsubs'
export * from './util'
17 changes: 17 additions & 0 deletions library/observables-connect/src/accounts/unsubs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/* @license Copyright 2024 w3ux authors & contributors
SPDX-License-Identifier: GPL-3.0-only */

// Extension account subscription unsubs
export const unsubs: Record<string, () => void> = {}

// Add an extension id to unsub state
export const addUnsub = (id: string, unsub: () => void) => {
unsubs[id] = unsub
}

// Unsubscribe to all unsubs
export const unsubAll = () => {
Object.values(unsubs).forEach((unsub) => {
unsub()
})
}
100 changes: 100 additions & 0 deletions library/observables-connect/src/accounts/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/* @license Copyright 2024 w3ux authors & contributors
SPDX-License-Identifier: GPL-3.0-only */

import type {
ExtensionAccount,
ProcessExtensionAccountsResult,
} from '@w3ux/types'
import { formatAccountSs58, isValidAddress } from '@w3ux/utils'
import { defaultProcessExtensionResult } from '../consts'
import { _extensionAccounts } from '../observables'

// Gets accounts to be imported and commits them to state

interface Config {
source: string
ss58: number
}
export const processExtensionAccounts = (
config: Config,
signer: unknown,
newAccounts: ExtensionAccount[]
): ProcessExtensionAccountsResult => {
const { source, ss58 } = config
if (!newAccounts.length) {
return defaultProcessExtensionResult
}

// Get valid accounts from extension
newAccounts = formatExtensionAccounts(newAccounts, ss58)

// Find any accounts that have been removed from this extension
const removedAccounts = _extensionAccounts
.getValue()
.filter((j) => j.source === source)
.filter((j) => !newAccounts.find((i) => i.address === j.address))

// Remove accounts that have already been imported
newAccounts = newAccounts.filter(
({ address }) =>
!_extensionAccounts
.getValue()
.find((j) => j.address === address && j.source !== 'external')
)

// Format accounts properties
newAccounts = newAccounts.map(({ address, name }) => ({
address,
name,
source,
signer,
}))

// Update observable state
updateAccounts({
add: newAccounts,
remove: removedAccounts,
})

return {
newAccounts,
removedAccounts: [...removedAccounts],
}
}

// Formats accounts to correct ss58 and removes invalid accounts
export const formatExtensionAccounts = (
accounts: ExtensionAccount[],
ss58: number
) => {
accounts = accounts
// Remove accounts that do not contain correctly formatted addresses
.filter(({ address }) => isValidAddress(address))
// Reformat addresses to ensure default ss58 format
.map((account) => {
const formattedAddress = formatAccountSs58(account.address, ss58)
if (!formattedAddress) {
return null
}
account.address = formattedAddress
return account
})
// Remove null entries resulting from invalid formatted addresses
.filter((account) => account !== null)

return accounts
}

// Updates accounts observable based on removed and added accounts
export const updateAccounts = ({
add,
remove,
}: {
add: ExtensionAccount[]
remove: ExtensionAccount[]
}) => {
const newAccounts = [..._extensionAccounts.getValue()]
.concat(add)
.filter((a) => remove.find((s) => s.address === a.address) === undefined)
_extensionAccounts.next(newAccounts)
}
12 changes: 12 additions & 0 deletions library/observables-connect/src/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* @license Copyright 2024 w3ux authors & contributors
SPDX-License-Identifier: GPL-3.0-only */

import type { ProcessExtensionAccountsResult } from '@w3ux/types'

export const defaultProcessExtensionResult: ProcessExtensionAccountsResult = {
newAccounts: [],
removedAccounts: [],
}

// Local storage active extensions key
export const activeExtensionsKey = 'active_extensions'
49 changes: 49 additions & 0 deletions library/observables-connect/src/extensions/connect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* @license Copyright 2024 w3ux authors & contributors
SPDX-License-Identifier: GPL-3.0-only */

import type { ExtensionAccount } from '@w3ux/types'
import { addUnsub } from '../accounts/unsubs'
import { processExtensionAccounts } from '../accounts/util'
import { canConnect } from '../util'
import { initExtensions } from './index'

// Connects to a single extension and processes its accounts
export const connectExtension = async (
dappName: string,
ss58: number,
id: string
): Promise<boolean> => {
if (canConnect(id)) {
const { connected } = await initExtensions(dappName, [id])
if (connected.size === 0) {
return
}
const { extension } = connected.get(id)
const canSubscribe = typeof extension.accounts.subscribe === 'function'

const handleAccounts = (accounts: ExtensionAccount[]) => {
processExtensionAccounts(
{
source: id,
ss58,
},
extension.signer,
accounts
)
}

// If account subscriptions are not supported, simply get the account(s) from the extension,
// otherwise, subscribe to accounts
if (!canSubscribe) {
const accounts = await extension.accounts.get()
handleAccounts(accounts)
} else {
const unsub = extension.accounts.subscribe((accounts) => {
handleAccounts(accounts)
})
addUnsub(id, unsub)
}
return true
}
return false
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
SPDX-License-Identifier: GPL-3.0-only */

import extensions from '@w3ux/extension-assets'
import type { ExtensionStatus } from '@w3ux/types'
import { _extensionsStatus, _gettingExtensions } from './index'
import { _extensionsStatus, _gettingExtensions } from '../observables'

// Gets extensions from injectedWeb3
export const getExtensions = async () => {
Expand All @@ -30,41 +29,37 @@ export const getExtensions = async () => {
_gettingExtensions.next(false)
}

const interval = 200
const maxChecks = 10
const minVerifications = 2

// Getter for the currently installed extensions
let counter = 0
const interval = 300
const maxChecks = 10
let verifications = 0
injectedWeb3Interval = setInterval(() => {
counter++
if (counter === maxChecks) {
handleCompleted(false)
} else {
// `injectedWeb3` is present
const injectedWeb3 = window?.injectedWeb3 || null
if (injectedWeb3 !== null) {
const injected = window?.injectedWeb3

// Check if injected exists and all extensions have a valid enable function
const ready =
injected !== undefined &&
Object.entries(injected).every(
([, ext]) => ext && typeof ext.enable === 'function'
)

// Increment verifications if the extensions are ready
if (ready) {
verifications++
} else {
verifications = 0
}

if (counter > 2 && verifications >= minVerifications) {
handleCompleted(true)
}
}
}, interval)
}

// Gets an extension status
export const getStatus = (id: string): ExtensionStatus =>
_extensionsStatus.getValue()[id] || undefined

// Sets an extension status
export const setStatus = (id: string, status: ExtensionStatus) => {
const newValue = { ..._extensionsStatus.getValue() }
newValue[id] = status
_extensionsStatus.next(newValue)
}

// Removes an extension status
export const removeStatus = (id: string) => {
const { [id]: _, ...rest } = _extensionsStatus.getValue()
_extensionsStatus.next(rest)
}

// Whether an extension can be connected
export const canConnect = (id: string) =>
![undefined, 'connected'].includes(_extensionsStatus.getValue()[id])
Loading