@rango-dev/provider-walletconnect-2 0.57.0 → 0.57.1-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/adapter/adapter.d.ts +39 -0
- package/dist/adapter/adapter.d.ts.map +1 -0
- package/dist/adapter/index.d.ts +3 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/modal-cache.d.ts +14 -0
- package/dist/adapter/modal-cache.d.ts.map +1 -0
- package/dist/adapter/modal.d.ts +25 -0
- package/dist/adapter/modal.d.ts.map +1 -0
- package/dist/adapter/networks.d.ts +60 -0
- package/dist/adapter/networks.d.ts.map +1 -0
- package/dist/adapter/provider-client.d.ts +3 -0
- package/dist/adapter/provider-client.d.ts.map +1 -0
- package/dist/adapter/registry.d.ts +4 -0
- package/dist/adapter/registry.d.ts.map +1 -0
- package/dist/adapter/session-cache.d.ts +17 -0
- package/dist/adapter/session-cache.d.ts.map +1 -0
- package/dist/adapter/task-queue.d.ts +11 -0
- package/dist/adapter/task-queue.d.ts.map +1 -0
- package/dist/chunk-GEAT645O.js +2 -0
- package/dist/chunk-GEAT645O.js.map +7 -0
- package/dist/constants.d.ts +4 -48
- package/dist/constants.d.ts.map +1 -0
- package/dist/evm-3KFL5N77.js +2 -0
- package/dist/evm-3KFL5N77.js.map +7 -0
- package/dist/mod.d.ts +4 -0
- package/dist/mod.d.ts.map +1 -0
- package/dist/mod.js +2 -0
- package/dist/mod.js.map +7 -0
- package/dist/namespaces/evm/hooks.d.ts +18 -0
- package/dist/namespaces/evm/hooks.d.ts.map +1 -0
- package/dist/namespaces/evm/namespace.d.ts +4 -0
- package/dist/namespaces/evm/namespace.d.ts.map +1 -0
- package/dist/namespaces/subscriber.d.ts +24 -0
- package/dist/namespaces/subscriber.d.ts.map +1 -0
- package/dist/provider.d.ts +3 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/session/accounts.d.ts +17 -0
- package/dist/session/accounts.d.ts.map +1 -0
- package/dist/session/bip122.d.ts +23 -0
- package/dist/session/bip122.d.ts.map +1 -0
- package/dist/session/chain-state.d.ts +6 -0
- package/dist/session/chain-state.d.ts.map +1 -0
- package/dist/session/evm-chain.d.ts +29 -0
- package/dist/session/evm-chain.d.ts.map +1 -0
- package/dist/session/evm.d.ts +25 -0
- package/dist/session/evm.d.ts.map +1 -0
- package/dist/session/lifecycle.d.ts +30 -0
- package/dist/session/lifecycle.d.ts.map +1 -0
- package/dist/session/lookup.d.ts +6 -0
- package/dist/session/lookup.d.ts.map +1 -0
- package/dist/session/proposals.d.ts +27 -0
- package/dist/session/proposals.d.ts.map +1 -0
- package/dist/session/restore-cache.d.ts +6 -0
- package/dist/session/restore-cache.d.ts.map +1 -0
- package/dist/session/teardown.d.ts +24 -0
- package/dist/session/teardown.d.ts.map +1 -0
- package/dist/signer.d.ts +2 -2
- package/dist/signer.d.ts.map +1 -0
- package/dist/signers/evm.d.ts +6 -4
- package/dist/signers/evm.d.ts.map +1 -0
- package/dist/types.d.ts +28 -11
- package/dist/types.d.ts.map +1 -0
- package/dist/utils.d.ts +17 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/wcConstants.d.ts +61 -0
- package/dist/wcConstants.d.ts.map +1 -0
- package/package.json +18 -13
- package/readme.md +14 -2
- package/src/adapter/adapter.ts +289 -0
- package/src/adapter/index.ts +2 -0
- package/src/adapter/modal-cache.ts +23 -0
- package/src/adapter/modal.ts +121 -0
- package/src/adapter/networks.ts +85 -0
- package/src/adapter/provider-client.ts +13 -0
- package/src/adapter/registry.ts +17 -0
- package/src/adapter/session-cache.ts +38 -0
- package/src/adapter/task-queue.ts +32 -0
- package/src/constants.ts +41 -76
- package/src/mod.ts +10 -0
- package/src/namespaces/evm/hooks.ts +148 -0
- package/src/namespaces/evm/namespace.ts +102 -0
- package/src/namespaces/subscriber.ts +106 -0
- package/src/provider.ts +35 -0
- package/src/session/accounts.ts +44 -0
- package/src/session/bip122.ts +225 -0
- package/src/session/chain-state.ts +38 -0
- package/src/session/evm-chain.ts +185 -0
- package/src/session/evm.ts +198 -0
- package/src/session/lifecycle.ts +241 -0
- package/src/session/lookup.ts +54 -0
- package/src/session/proposals.ts +96 -0
- package/src/session/restore-cache.ts +38 -0
- package/src/session/teardown.ts +181 -0
- package/src/signer.ts +9 -17
- package/src/signers/evm.ts +19 -17
- package/src/types.ts +31 -12
- package/src/utils.ts +68 -0
- package/src/wcConstants.ts +94 -0
- package/dist/chunk-6V3WUFOU.js +0 -2
- package/dist/chunk-6V3WUFOU.js.map +0 -7
- package/dist/chunk-BPSEZJUF.js +0 -2
- package/dist/chunk-BPSEZJUF.js.map +0 -7
- package/dist/chunk-DXVBX5XQ.js +0 -2
- package/dist/chunk-DXVBX5XQ.js.map +0 -7
- package/dist/dist-6GI3ECE5.js +0 -139
- package/dist/dist-6GI3ECE5.js.map +0 -7
- package/dist/evm-3ID4XY4Y.js +0 -2
- package/dist/evm-3ID4XY4Y.js.map +0 -7
- package/dist/helpers.d.ts +0 -31
- package/dist/index.d.ts +0 -21
- package/dist/index.js +0 -2
- package/dist/index.js.map +0 -7
- package/dist/session.d.ts +0 -63
- package/dist/signers/solana.d.ts +0 -14
- package/dist/solana-MFA7C5XI.js +0 -2
- package/dist/solana-MFA7C5XI.js.map +0 -7
- package/src/helpers.ts +0 -238
- package/src/index.ts +0 -234
- package/src/session.ts +0 -376
- package/src/signers/solana.ts +0 -160
- package/src/wc-types.d.ts +0 -31
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { WalletConnectNamespace } from '../types.js';
|
|
2
|
+
import type { AppKitNetwork } from '@reown/appkit-common';
|
|
3
|
+
import type UniversalProvider from '@walletconnect/universal-provider';
|
|
4
|
+
|
|
5
|
+
import { createAppKit } from '@reown/appkit/core';
|
|
6
|
+
|
|
7
|
+
import { bitcoin, mainnet } from './networks.js';
|
|
8
|
+
|
|
9
|
+
export type AppKitModal = ReturnType<typeof createAppKitModal>;
|
|
10
|
+
|
|
11
|
+
const DEFAULT_MODAL_Z_INDEX = 999_999_999;
|
|
12
|
+
|
|
13
|
+
export type ModalParams = {
|
|
14
|
+
projectId: string;
|
|
15
|
+
universalProvider: UniversalProvider;
|
|
16
|
+
themeMode?: 'light' | 'dark';
|
|
17
|
+
zIndex?: number;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* Every WalletConnect namespace shares one AppKit instance with all networks
|
|
22
|
+
* registered up front. A per-namespace instance had to be rebuilt on each
|
|
23
|
+
* switch, and every rebuild re-ran AppKit init against its module-level
|
|
24
|
+
* singleton controllers - leaving router history from the previous open mixed
|
|
25
|
+
* into the next (a stray back button in the modal header). The active network
|
|
26
|
+
* is chosen per connect with `switchNetwork` instead.
|
|
27
|
+
*
|
|
28
|
+
* The active network is now shared mutable state, so this is only safe while
|
|
29
|
+
* connects are serialized (the adapter's `#sessionQueue`): the switch/open/close
|
|
30
|
+
* for one namespace never interleaves with another's. A connect path that
|
|
31
|
+
* bypassed that queue would race this switch.
|
|
32
|
+
*/
|
|
33
|
+
const NETWORKS: [AppKitNetwork, ...AppKitNetwork[]] = [mainnet, bitcoin];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The network to make active before opening the modal for a namespace. Keyed by
|
|
37
|
+
* `WalletConnectNamespace` so a future namespace is a compile error until mapped.
|
|
38
|
+
*/
|
|
39
|
+
const NAMESPACE_NETWORK: Record<WalletConnectNamespace, AppKitNetwork> = {
|
|
40
|
+
evm: mainnet,
|
|
41
|
+
utxo: bitcoin,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Make a namespace's network active before opening the shared modal.
|
|
46
|
+
*
|
|
47
|
+
* `open()` compares the requested namespace against AppKit's active chain: if
|
|
48
|
+
* they differ it treats the open as a network switch and pushes an extra step
|
|
49
|
+
* onto the router, which leaves a stray back button in the modal header. One
|
|
50
|
+
* shared instance keeps whatever chain the previous connect left active, so we
|
|
51
|
+
* switch it to the target here first - then `open()` sees a matching chain and
|
|
52
|
+
* takes its clean, non-switching path.
|
|
53
|
+
*
|
|
54
|
+
* `ready()` first so AppKit init has registered the networks `switchNetwork`
|
|
55
|
+
* looks up.
|
|
56
|
+
*/
|
|
57
|
+
export async function prepareModalForNamespace(
|
|
58
|
+
web3Modal: AppKitModal,
|
|
59
|
+
namespace: WalletConnectNamespace
|
|
60
|
+
): Promise<void> {
|
|
61
|
+
await web3Modal.ready();
|
|
62
|
+
await web3Modal.switchNetwork(NAMESPACE_NETWORK[namespace]);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function createAppKitModal({
|
|
66
|
+
projectId,
|
|
67
|
+
universalProvider,
|
|
68
|
+
themeMode = 'light',
|
|
69
|
+
zIndex = DEFAULT_MODAL_Z_INDEX,
|
|
70
|
+
}: ModalParams) {
|
|
71
|
+
return createAppKit({
|
|
72
|
+
projectId,
|
|
73
|
+
networks: NETWORKS,
|
|
74
|
+
universalProvider,
|
|
75
|
+
/*
|
|
76
|
+
* AppKit is intentionally kept out of the WalletConnect session lifecycle -
|
|
77
|
+
* it renders the modal (QR + wallet list), nothing more. We drive the
|
|
78
|
+
* handshake ourselves with `client.connect()` (see `session/lifecycle.ts`).
|
|
79
|
+
*
|
|
80
|
+
* Why: AppKit models one unified, multi-chain session with a single active
|
|
81
|
+
* chain and its own reconnect/sync. The hub needs the opposite - EVM and
|
|
82
|
+
* UTXO as independent sessions that connect, disconnect and eager-restore
|
|
83
|
+
* separately (the adapter keeps one cache entry per namespace). Letting
|
|
84
|
+
* AppKit drive via `provider.connect()` accumulates namespaces into a single
|
|
85
|
+
* proposal, so connecting BTC after EVM asks the wallet for Ethereum - it was
|
|
86
|
+
* tried and reverted. `manualWCControl: true` is AppKit's supported flag for
|
|
87
|
+
* exactly this "you render, I connect" split.
|
|
88
|
+
*
|
|
89
|
+
* The visible cost: because we own the lifecycle, `provider.session` is never
|
|
90
|
+
* set. With `enableReconnect: false`, AppKit init runs
|
|
91
|
+
* `unSyncExistingConnection()`, whose `provider.disconnect()` hits a
|
|
92
|
+
* `if (!this.session) throw` guard and AppKit logs the caught error as
|
|
93
|
+
* `UniversalAdapter:disconnect - error: Please call connect() before
|
|
94
|
+
* enable()`. It is expected and harmless - AppKit looking for a session to
|
|
95
|
+
* clean up, finding none.
|
|
96
|
+
*
|
|
97
|
+
* Do NOT "fix" the log. Enabling reconnect makes AppKit actively restore/sync
|
|
98
|
+
* connections it doesn't own, racing our own restore path and serialize
|
|
99
|
+
* queue. Assigning `provider.session` removes the guard so that disconnect
|
|
100
|
+
* (and every other AppKit-initiated disconnect) succeeds for real, tearing
|
|
101
|
+
* down a live session on AppKit's schedule instead of ours. Both trade a
|
|
102
|
+
* cosmetic log for real state conflicts.
|
|
103
|
+
*/
|
|
104
|
+
enableReconnect: false,
|
|
105
|
+
defaultNetwork: mainnet,
|
|
106
|
+
defaultAccountTypes: { bip122: 'payment' },
|
|
107
|
+
manualWCControl: true,
|
|
108
|
+
/*
|
|
109
|
+
* We register only one EVM network (`mainnet`) but the session approves every
|
|
110
|
+
* EVM chain, so a network switch can land on a chain AppKit considers
|
|
111
|
+
* "unsupported" and it auto-opens its UnsupportedChain modal
|
|
112
|
+
* (ChainController.showUnsupportedChainUI). Allow it so AppKit doesn't police
|
|
113
|
+
* the network we manage ourselves.
|
|
114
|
+
*/
|
|
115
|
+
allowUnsupportedChain: true,
|
|
116
|
+
themeMode,
|
|
117
|
+
themeVariables: {
|
|
118
|
+
'--apkt-z-index': zIndex,
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { AppKitNetwork, CaipNetwork } from '@reown/appkit-common';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* The two networks AppKit renders the modal for, defined here instead of
|
|
5
|
+
* imported from `@reown/appkit/networks`.
|
|
6
|
+
*
|
|
7
|
+
* That entry point is `export * from 'viem/chains'` plus AppKit's own non-EVM
|
|
8
|
+
* chains, so importing a single name off it pulls every viem chain definition
|
|
9
|
+
* into the consumer's bundle. One of them (`tempo`) re-exports viem's tempo
|
|
10
|
+
* chainConfig, which reaches `ox`'s salt-mining worker pool - and that module
|
|
11
|
+
* routes `await import(id)` through a variable so bundlers skip
|
|
12
|
+
* `node:worker_threads` in the browser. webpack can't resolve the expression
|
|
13
|
+
* and warns "Critical dependency: the request of a dependency is an
|
|
14
|
+
* expression"; react-scripts promotes warnings to errors on CI, which fails
|
|
15
|
+
* the build for anyone consuming the widget through Create React App.
|
|
16
|
+
*
|
|
17
|
+
* viem publishes no per-chain subpath (`./chains` is the only export), so
|
|
18
|
+
* there is no narrower import to reach for - the definitions are plain data,
|
|
19
|
+
* so we carry the two we need.
|
|
20
|
+
*
|
|
21
|
+
* Keep these in sync with upstream if AppKit's `bitcoin` or viem's `mainnet`
|
|
22
|
+
* changes: `@reown/appkit/dist/esm/src/networks/bitcoin.js` and
|
|
23
|
+
* `viem/_esm/chains/definitions/mainnet.js`.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Mirrors `defineChain` from both `viem/chains` and AppKit's networks module -
|
|
28
|
+
* they are the same three-key spread. Declaring the viem-only formatter fields
|
|
29
|
+
* keeps the objects structurally identical to the ones AppKit used to receive.
|
|
30
|
+
*/
|
|
31
|
+
function defineChain<const chain extends AppKitNetwork>(chain: chain) {
|
|
32
|
+
return {
|
|
33
|
+
formatters: undefined,
|
|
34
|
+
fees: undefined,
|
|
35
|
+
serializers: undefined,
|
|
36
|
+
...chain,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** viem's `mainnet`, the only EVM network we register. */
|
|
41
|
+
export const mainnet = defineChain({
|
|
42
|
+
id: 1,
|
|
43
|
+
caipNetworkId: 'eip155:1',
|
|
44
|
+
chainNamespace: 'eip155',
|
|
45
|
+
name: 'Ethereum',
|
|
46
|
+
nativeCurrency: { name: 'Ether', symbol: 'ETH', decimals: 18 },
|
|
47
|
+
rpcUrls: {
|
|
48
|
+
default: {
|
|
49
|
+
http: ['https://eth.merkle.io'],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
blockExplorers: {
|
|
53
|
+
default: {
|
|
54
|
+
name: 'Etherscan',
|
|
55
|
+
url: 'https://etherscan.io',
|
|
56
|
+
apiUrl: 'https://api.etherscan.io/api',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
contracts: {
|
|
60
|
+
ensUniversalResolver: {
|
|
61
|
+
address: '0xeeeeeeee14d718c2b47d9923deab1335e144eeee',
|
|
62
|
+
blockCreated: 23_085_558,
|
|
63
|
+
},
|
|
64
|
+
multicall3: {
|
|
65
|
+
address: '0xca11bde05977b3631167028862be2a173976ca11',
|
|
66
|
+
blockCreated: 14_353_601,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
} satisfies CaipNetwork);
|
|
70
|
+
|
|
71
|
+
/** AppKit's `bitcoin`, the only UTXO network we register. */
|
|
72
|
+
export const bitcoin = defineChain({
|
|
73
|
+
id: '000000000019d6689c085ae165831e93',
|
|
74
|
+
caipNetworkId: 'bip122:000000000019d6689c085ae165831e93',
|
|
75
|
+
chainNamespace: 'bip122',
|
|
76
|
+
name: 'Bitcoin',
|
|
77
|
+
nativeCurrency: {
|
|
78
|
+
name: 'Bitcoin',
|
|
79
|
+
symbol: 'BTC',
|
|
80
|
+
decimals: 8,
|
|
81
|
+
},
|
|
82
|
+
rpcUrls: {
|
|
83
|
+
default: { http: ['https://rpc.walletconnect.org/v1'] },
|
|
84
|
+
},
|
|
85
|
+
} satisfies CaipNetwork);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import UniversalProvider from '@walletconnect/universal-provider';
|
|
2
|
+
|
|
3
|
+
import { DEFAULT_APP_METADATA, RELAY_URL } from '../wcConstants.js';
|
|
4
|
+
|
|
5
|
+
export async function createUniversalProvider(
|
|
6
|
+
projectId: string
|
|
7
|
+
): Promise<UniversalProvider> {
|
|
8
|
+
return UniversalProvider.init({
|
|
9
|
+
relayUrl: RELAY_URL,
|
|
10
|
+
projectId,
|
|
11
|
+
metadata: DEFAULT_APP_METADATA,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { WalletConnectAdapter } from './adapter.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Holds the singleton adapter instance created in the provider's `init`.
|
|
5
|
+
*
|
|
6
|
+
* This lives in its own leaf module (importing nothing from `provider.ts` or the
|
|
7
|
+
* namespaces) to break the `provider -> namespace -> provider` import cycle that
|
|
8
|
+
* otherwise throws a TDZ error ("Cannot access 'getAdapter' before initialization")
|
|
9
|
+
* when the namespace module evaluates its top-level subscriber factories.
|
|
10
|
+
*/
|
|
11
|
+
let adapter: WalletConnectAdapter;
|
|
12
|
+
|
|
13
|
+
export const getAdapter = () => adapter;
|
|
14
|
+
|
|
15
|
+
export const setAdapter = (instance: WalletConnectAdapter) => {
|
|
16
|
+
adapter = instance;
|
|
17
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { WalletConnectNamespace } from '../types.js';
|
|
2
|
+
import type { SessionTypes } from '@walletconnect/types';
|
|
3
|
+
|
|
4
|
+
export type SessionCache = {
|
|
5
|
+
get(namespace: WalletConnectNamespace): SessionTypes.Struct | null;
|
|
6
|
+
set(namespace: WalletConnectNamespace, session: SessionTypes.Struct): void;
|
|
7
|
+
clear(namespace: WalletConnectNamespace): void;
|
|
8
|
+
/**
|
|
9
|
+
* Drops every namespace holding `topic`. A wallet may approve more than was
|
|
10
|
+
* proposed, so one settled session can back both namespaces - tearing its topic
|
|
11
|
+
* down under one of them would otherwise leave the other pointing at a topic
|
|
12
|
+
* the relay has already dropped.
|
|
13
|
+
*/
|
|
14
|
+
clearTopic(topic: string): void;
|
|
15
|
+
clearAll(): void;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function createSessionCache(): SessionCache {
|
|
19
|
+
const sessions = new Map<WalletConnectNamespace, SessionTypes.Struct>();
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
get: (namespace) => sessions.get(namespace) ?? null,
|
|
23
|
+
|
|
24
|
+
set: (namespace, session) => void sessions.set(namespace, session),
|
|
25
|
+
|
|
26
|
+
clear: (namespace) => void sessions.delete(namespace),
|
|
27
|
+
|
|
28
|
+
clearTopic: (topic) => {
|
|
29
|
+
for (const [namespace, session] of sessions) {
|
|
30
|
+
if (session.topic === topic) {
|
|
31
|
+
sessions.delete(namespace);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
clearAll: () => sessions.clear(),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runs tasks one at a time, in the order `run` was called.
|
|
3
|
+
*
|
|
4
|
+
* A task that rejects passes its rejection to its own caller and does not block
|
|
5
|
+
* the tasks behind it.
|
|
6
|
+
*/
|
|
7
|
+
export class TaskQueue {
|
|
8
|
+
/** Resolves once the task currently at the back of the queue has settled. */
|
|
9
|
+
#tail: Promise<void> = Promise.resolve();
|
|
10
|
+
|
|
11
|
+
async run<T>(task: () => Promise<T>): Promise<T> {
|
|
12
|
+
const ourTurn = this.#tail;
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* Take our place at the back of the queue before awaiting anything, so
|
|
16
|
+
* callers arriving in the same tick line up behind us rather than all
|
|
17
|
+
* reading the same `#tail`. `releaseOurTurn` never rejects, which is what
|
|
18
|
+
* lets the next task run even when this one throws.
|
|
19
|
+
*/
|
|
20
|
+
let releaseOurTurn!: () => void;
|
|
21
|
+
this.#tail = new Promise<void>((resolve) => {
|
|
22
|
+
releaseOurTurn = resolve;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
await ourTurn;
|
|
26
|
+
try {
|
|
27
|
+
return await task();
|
|
28
|
+
} finally {
|
|
29
|
+
releaseOurTurn();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/constants.ts
CHANGED
|
@@ -1,77 +1,42 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
export enum EthereumEvents {
|
|
44
|
-
CHAIN_CHANGED = 'chainChanged',
|
|
45
|
-
ACCOUNTS_CHANGED = 'accountsChanged',
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export const DEFAULT_ETHEREUM_EVENTS: EthereumEvents[] = [
|
|
49
|
-
EthereumEvents.CHAIN_CHANGED,
|
|
50
|
-
EthereumEvents.ACCOUNTS_CHANGED,
|
|
51
|
-
];
|
|
52
|
-
|
|
53
|
-
export const DEFAULT_ETHEREUM_METHODS = [
|
|
54
|
-
EthereumRPCMethods.PERSONAL_SIGN,
|
|
55
|
-
EthereumRPCMethods.SEND_TRANSACTION,
|
|
56
|
-
EthereumRPCMethods.SIGN_TRANSACTION,
|
|
57
|
-
EthereumRPCMethods.SWITCH_CHAIN,
|
|
58
|
-
EthereumRPCMethods.ADD_CHAIN,
|
|
59
|
-
EthereumRPCMethods.GET_CHAIN,
|
|
60
|
-
];
|
|
61
|
-
|
|
62
|
-
export const DEFAULT_SOLANA_METHODS = [
|
|
63
|
-
SolanaRPCMethods.SIGN_TRANSACTION,
|
|
64
|
-
SolanaRPCMethods.SIGN_MESSAGE,
|
|
65
|
-
];
|
|
66
|
-
|
|
67
|
-
// refrence: https://github.com/ChainAgnostic/namespaces/blob/main/solana/caip2.md
|
|
68
|
-
export const DEFAULT_SOLANA_CHAIN_ID = '4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ';
|
|
69
|
-
|
|
70
|
-
export const DEFAULT_APP_METADATA = {
|
|
71
|
-
name: 'Rango Exchange',
|
|
72
|
-
description: 'The Ultimate Cross-Chain Solution',
|
|
73
|
-
url: 'https://app.rango.exchange/',
|
|
74
|
-
icons: ['https://app.rango.exchange/logo-rounded.png'],
|
|
1
|
+
import type { ProviderMetadata } from '@hub3js/core';
|
|
2
|
+
|
|
3
|
+
import { isEvmNamespace } from '@hub3js/evm';
|
|
4
|
+
|
|
5
|
+
import getSigners from './signer.js';
|
|
6
|
+
|
|
7
|
+
export const WALLET_ID = 'wallet-connect-2';
|
|
8
|
+
|
|
9
|
+
export const metadata: ProviderMetadata = {
|
|
10
|
+
name: 'WalletConnect',
|
|
11
|
+
icon: 'https://raw.githubusercontent.com/rango-exchange/assets/main/wallets/walletconnect/icon.svg',
|
|
12
|
+
extensions: {
|
|
13
|
+
homepage: 'https://walletconnect.com/',
|
|
14
|
+
},
|
|
15
|
+
properties: [
|
|
16
|
+
{
|
|
17
|
+
name: 'namespaces',
|
|
18
|
+
value: {
|
|
19
|
+
selection: 'single',
|
|
20
|
+
data: [
|
|
21
|
+
{
|
|
22
|
+
label: 'EVM',
|
|
23
|
+
value: 'EVM',
|
|
24
|
+
id: 'ETH',
|
|
25
|
+
isChainSupported: isEvmNamespace,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'signers',
|
|
32
|
+
value: { getSigners: async () => getSigners() },
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'details',
|
|
36
|
+
value: {
|
|
37
|
+
mobileWallet: true,
|
|
38
|
+
showOnMobile: true,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
],
|
|
75
42
|
};
|
|
76
|
-
|
|
77
|
-
export const RELAY_URL = 'wss://relay.walletconnect.com';
|
package/src/mod.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { defineVersions } from '@hub3js/core/utils';
|
|
2
|
+
|
|
3
|
+
import { buildProvider } from './provider.js';
|
|
4
|
+
|
|
5
|
+
export type { Environments } from './types.js';
|
|
6
|
+
|
|
7
|
+
const versions = () =>
|
|
8
|
+
defineVersions().version('1.0.0', buildProvider()).build();
|
|
9
|
+
|
|
10
|
+
export { versions };
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { EvmActions } from '@hub3js/evm';
|
|
2
|
+
|
|
3
|
+
import { utils } from '@hub3js/evm';
|
|
4
|
+
import { ChainId } from 'caip';
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
extractAddress,
|
|
8
|
+
getAccountsFromEvent,
|
|
9
|
+
} from '../../session/accounts.js';
|
|
10
|
+
import {
|
|
11
|
+
getPersistedChainId,
|
|
12
|
+
persistCurrentChainId,
|
|
13
|
+
} from '../../session/chain-state.js';
|
|
14
|
+
import { filterEvmAccounts } from '../../session/evm.js';
|
|
15
|
+
import { expireWalletConnectTopic } from '../../session/teardown.js';
|
|
16
|
+
import { chainReferenceToHex, parseChainReference } from '../../utils.js';
|
|
17
|
+
import { EthereumEvents, NAMESPACES } from '../../wcConstants.js';
|
|
18
|
+
import { createSessionSubscriber } from '../subscriber.js';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* WC signals an account switch via `session_update` (re-shared namespaces with the
|
|
22
|
+
* newly-selected account first), so the active account is published from this event.
|
|
23
|
+
*/
|
|
24
|
+
export function sessionUpdateSubscriber() {
|
|
25
|
+
return createSessionSubscriber<'session_update', EvmActions>(
|
|
26
|
+
'evm',
|
|
27
|
+
'session_update',
|
|
28
|
+
async ({ args, session, adapter, context }) => {
|
|
29
|
+
/*
|
|
30
|
+
* Keep the cached struct in step with what the wallet just re-shared.
|
|
31
|
+
* `switchEvmNetwork` reads the authorized chains and methods back out
|
|
32
|
+
* of it, so a stale copy rejects a chain the wallet has since added
|
|
33
|
+
* (`wallet_addEthereumChain` lands here as a `session_update`) with
|
|
34
|
+
* "Chain X is not configured".
|
|
35
|
+
*/
|
|
36
|
+
if (args.params.namespaces[NAMESPACES.ETHEREUM]) {
|
|
37
|
+
adapter.cacheSession('evm', {
|
|
38
|
+
...session,
|
|
39
|
+
namespaces: args.params.namespaces,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const allAccounts = getAccountsFromEvent(args);
|
|
44
|
+
if (!allAccounts.length) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const entries = allAccounts.map((accountsWithChain) => ({
|
|
49
|
+
address: accountsWithChain.accounts[0],
|
|
50
|
+
chainId: accountsWithChain.chainId,
|
|
51
|
+
}));
|
|
52
|
+
|
|
53
|
+
const [getState, setState] = context.state();
|
|
54
|
+
const hubNetwork = getState().network;
|
|
55
|
+
const persistedChain = await getPersistedChainId(
|
|
56
|
+
await adapter.getClient()
|
|
57
|
+
);
|
|
58
|
+
const activeReference =
|
|
59
|
+
parseChainReference(hubNetwork ?? undefined) ?? persistedChain;
|
|
60
|
+
|
|
61
|
+
let evmAccounts = filterEvmAccounts(entries, activeReference);
|
|
62
|
+
if (!evmAccounts.length) {
|
|
63
|
+
evmAccounts = filterEvmAccounts(entries);
|
|
64
|
+
}
|
|
65
|
+
if (!evmAccounts.length) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const chainIdHex = chainReferenceToHex(evmAccounts[0].chainId);
|
|
70
|
+
setState(
|
|
71
|
+
'accounts',
|
|
72
|
+
utils.formatAccountsToCAIP([evmAccounts[0].accounts[0]], chainIdHex)
|
|
73
|
+
);
|
|
74
|
+
setState('network', chainIdHex);
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Addresses out of an `accountsChanged` payload. Spec says `string[]`, but wallets
|
|
81
|
+
* forwarding the EIP-1193 event as-is send a bare value, so the shape is coerced
|
|
82
|
+
* before anything reads it.
|
|
83
|
+
*/
|
|
84
|
+
function eventAccounts(data: unknown): string[] {
|
|
85
|
+
return (Array.isArray(data) ? data : [data])
|
|
86
|
+
.filter((entry): entry is string => typeof entry === 'string' && !!entry)
|
|
87
|
+
.map(extractAddress);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Handles WC `session_event` payloads: EIP-1193 `accountsChanged` (publish the new
|
|
92
|
+
* accounts, or disconnect when the wallet revokes them all) and `chainChanged`
|
|
93
|
+
* (update and persist the active chain). Any other event is ignored.
|
|
94
|
+
*/
|
|
95
|
+
export function sessionEventSubscriber() {
|
|
96
|
+
return createSessionSubscriber<'session_event', EvmActions>(
|
|
97
|
+
'evm',
|
|
98
|
+
'session_event',
|
|
99
|
+
({ args, adapter, context }) => {
|
|
100
|
+
const [, setState] = context.state();
|
|
101
|
+
|
|
102
|
+
if (args.params.event.name === EthereumEvents.ACCOUNTS_CHANGED) {
|
|
103
|
+
const accounts = eventAccounts(args.params.event.data);
|
|
104
|
+
/*
|
|
105
|
+
* An empty payload is the wallet revoking access (locked, or the account
|
|
106
|
+
* unlinked from this dApp), not a switch - there is nothing to publish.
|
|
107
|
+
*/
|
|
108
|
+
if (!accounts.length) {
|
|
109
|
+
void context.action('disconnect');
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const chainIdHex = chainReferenceToHex(
|
|
114
|
+
ChainId.parse(args.params.chainId).reference
|
|
115
|
+
);
|
|
116
|
+
setState('network', chainIdHex);
|
|
117
|
+
setState('accounts', utils.formatAccountsToCAIP(accounts, chainIdHex));
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (args.params.event.name === EthereumEvents.CHAIN_CHANGED) {
|
|
122
|
+
const chainIdHex = chainReferenceToHex(args.params.event.data);
|
|
123
|
+
setState('network', chainIdHex);
|
|
124
|
+
void adapter
|
|
125
|
+
.getClient()
|
|
126
|
+
.then(async (client) => persistCurrentChainId(client, chainIdHex));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Reflects a wallet-initiated `session_delete` into the hub: clears the cached
|
|
134
|
+
* session, expires the WC topic, and runs the hub `disconnect` action.
|
|
135
|
+
*/
|
|
136
|
+
export function sessionDeleteSubscriber() {
|
|
137
|
+
return createSessionSubscriber<'session_delete', EvmActions>(
|
|
138
|
+
'evm',
|
|
139
|
+
'session_delete',
|
|
140
|
+
({ args, adapter, context }) => {
|
|
141
|
+
adapter.clearSession('evm');
|
|
142
|
+
void adapter
|
|
143
|
+
.getClient()
|
|
144
|
+
.then(async (client) => expireWalletConnectTopic(client, args.topic));
|
|
145
|
+
context.action('disconnect');
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
}
|