@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,102 @@
|
|
|
1
|
+
import type { Context } from '@hub3js/core';
|
|
2
|
+
import type { EvmActions } from '@hub3js/evm';
|
|
3
|
+
|
|
4
|
+
import { NamespaceBuilder } from '@hub3js/core';
|
|
5
|
+
import { actions, builders, utils } from '@hub3js/evm';
|
|
6
|
+
import * as commonBuilders from '@hub3js/std/builders';
|
|
7
|
+
import { standardizeAndThrowError } from '@hub3js/std/operators';
|
|
8
|
+
|
|
9
|
+
import { getAdapter } from '../../adapter/registry.js';
|
|
10
|
+
import { WALLET_ID } from '../../constants.js';
|
|
11
|
+
import { getAccountsFromSession } from '../../session/accounts.js';
|
|
12
|
+
import { filterEvmAccounts } from '../../session/evm.js';
|
|
13
|
+
import { chainReferenceToHex, parseChainReference } from '../../utils.js';
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
sessionDeleteSubscriber,
|
|
17
|
+
sessionEventSubscriber,
|
|
18
|
+
sessionUpdateSubscriber,
|
|
19
|
+
} from './hooks.js';
|
|
20
|
+
|
|
21
|
+
const [sessionUpdate, sessionUpdateCleanup] = sessionUpdateSubscriber();
|
|
22
|
+
const [sessionEvent, sessionEventCleanup] = sessionEventSubscriber();
|
|
23
|
+
const [sessionDelete, sessionDeleteCleanup] = sessionDeleteSubscriber();
|
|
24
|
+
|
|
25
|
+
const connect = builders
|
|
26
|
+
.connect()
|
|
27
|
+
.action(async function (_context: Context<EvmActions>, chain?: string) {
|
|
28
|
+
const adapter = getAdapter();
|
|
29
|
+
// `chain` is set when connecting to a specific network (swap auto-switch or hub connect with network).
|
|
30
|
+
const requestedReference = chain ? parseChainReference(chain) : undefined;
|
|
31
|
+
const session = await adapter.ensureConnectedToChain(chain);
|
|
32
|
+
const activeReference =
|
|
33
|
+
requestedReference ?? (await adapter.resolveActiveChainReference());
|
|
34
|
+
|
|
35
|
+
if (!activeReference) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
'Unable to determine EVM chain id from WalletConnect session.'
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const accounts = getAccountsFromSession(session);
|
|
42
|
+
const evmAccounts = filterEvmAccounts(accounts, activeReference);
|
|
43
|
+
|
|
44
|
+
if (!evmAccounts.length) {
|
|
45
|
+
throw new Error('No EVM accounts found in WalletConnect session.');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
accounts: utils.formatAccountsToCAIP(
|
|
50
|
+
evmAccounts.map((account) => account.accounts[0]),
|
|
51
|
+
activeReference
|
|
52
|
+
),
|
|
53
|
+
network: chainReferenceToHex(activeReference),
|
|
54
|
+
};
|
|
55
|
+
})
|
|
56
|
+
.before(sessionUpdate)
|
|
57
|
+
.before(sessionEvent)
|
|
58
|
+
.before(sessionDelete)
|
|
59
|
+
.or(sessionUpdateCleanup)
|
|
60
|
+
.or(sessionEventCleanup)
|
|
61
|
+
.or(sessionDeleteCleanup)
|
|
62
|
+
.or((_, err) => {
|
|
63
|
+
void getAdapter().disconnectSession('evm');
|
|
64
|
+
return err;
|
|
65
|
+
})
|
|
66
|
+
.or(standardizeAndThrowError)
|
|
67
|
+
.build();
|
|
68
|
+
|
|
69
|
+
const canEagerConnect = builders
|
|
70
|
+
.canEagerConnect()
|
|
71
|
+
.action(async () => getAdapter().tryRestoreEagerSession('evm'))
|
|
72
|
+
.build();
|
|
73
|
+
|
|
74
|
+
const canSwitchNetwork = builders
|
|
75
|
+
.canSwitchNetwork()
|
|
76
|
+
.action(actions.canSwitchNetwork())
|
|
77
|
+
.build();
|
|
78
|
+
|
|
79
|
+
const disconnect = commonBuilders
|
|
80
|
+
.disconnect<EvmActions>()
|
|
81
|
+
.before(async () => {
|
|
82
|
+
await getAdapter().disconnectSession('evm');
|
|
83
|
+
})
|
|
84
|
+
.after(sessionUpdateCleanup)
|
|
85
|
+
.after(sessionEventCleanup)
|
|
86
|
+
.after(sessionDeleteCleanup)
|
|
87
|
+
.build();
|
|
88
|
+
|
|
89
|
+
const getChainId = builders
|
|
90
|
+
.getChainId()
|
|
91
|
+
.action(async () => getAdapter().getCurrentChainId())
|
|
92
|
+
.build();
|
|
93
|
+
|
|
94
|
+
const evm = new NamespaceBuilder<EvmActions>('EVM', WALLET_ID)
|
|
95
|
+
.action(connect)
|
|
96
|
+
.action(canEagerConnect)
|
|
97
|
+
.action(canSwitchNetwork)
|
|
98
|
+
.action(disconnect)
|
|
99
|
+
.action(getChainId)
|
|
100
|
+
.build();
|
|
101
|
+
|
|
102
|
+
export { evm };
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import type { WalletConnectAdapter } from '../adapter/adapter.js';
|
|
2
|
+
import type { WalletConnectNamespace } from '../types.js';
|
|
3
|
+
import type {
|
|
4
|
+
Actions,
|
|
5
|
+
Context,
|
|
6
|
+
Subscriber,
|
|
7
|
+
SubscriberCleanUp,
|
|
8
|
+
} from '@hub3js/core';
|
|
9
|
+
import type { SessionTypes, SignClientTypes } from '@walletconnect/types';
|
|
10
|
+
|
|
11
|
+
import { debug } from '@rango-dev/logging-core';
|
|
12
|
+
|
|
13
|
+
import { getAdapter } from '../adapter/registry.js';
|
|
14
|
+
|
|
15
|
+
/** The WC client events a namespace subscribes to. */
|
|
16
|
+
export type SessionEventName =
|
|
17
|
+
| 'session_update'
|
|
18
|
+
| 'session_event'
|
|
19
|
+
| 'session_delete';
|
|
20
|
+
|
|
21
|
+
export type SessionEventHandler<
|
|
22
|
+
Name extends SessionEventName,
|
|
23
|
+
ActionsType extends Actions<ActionsType>
|
|
24
|
+
> = (event: {
|
|
25
|
+
args: SignClientTypes.EventArguments[Name];
|
|
26
|
+
/** The cached session this event was matched against. */
|
|
27
|
+
session: SessionTypes.Struct;
|
|
28
|
+
adapter: WalletConnectAdapter;
|
|
29
|
+
context: Context<ActionsType>;
|
|
30
|
+
}) => void | Promise<void>;
|
|
31
|
+
|
|
32
|
+
function logHandlerError(error: unknown) {
|
|
33
|
+
debug(error instanceof Error ? error : new Error(String(error)));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Builds a hub subscriber for one WC client event, scoped to one namespace.
|
|
38
|
+
*
|
|
39
|
+
* Every namespace hook needs the same four things - resolve the client, only act
|
|
40
|
+
* on events for the session this namespace has cached, register, and unregister
|
|
41
|
+
* the exact same handler reference. Getting the topic guard wrong is what makes a
|
|
42
|
+
* hook act on the other namespace's session, so it lives here rather than being
|
|
43
|
+
* repeated per hook.
|
|
44
|
+
*/
|
|
45
|
+
export function createSessionSubscriber<
|
|
46
|
+
Name extends SessionEventName,
|
|
47
|
+
ActionsType extends Actions<ActionsType>
|
|
48
|
+
>(
|
|
49
|
+
namespace: WalletConnectNamespace,
|
|
50
|
+
event: Name,
|
|
51
|
+
handle: SessionEventHandler<Name, ActionsType>
|
|
52
|
+
): [Subscriber<ActionsType>, SubscriberCleanUp<ActionsType>] {
|
|
53
|
+
let handler: ((args: SignClientTypes.EventArguments[Name]) => void) | null =
|
|
54
|
+
null;
|
|
55
|
+
|
|
56
|
+
return [
|
|
57
|
+
async (context) => {
|
|
58
|
+
const adapter = getAdapter();
|
|
59
|
+
const client = await adapter.getClient();
|
|
60
|
+
|
|
61
|
+
/*
|
|
62
|
+
* A namespace re-runs its `before` subscribers on every connect, so drop the
|
|
63
|
+
* previous registration first - otherwise each reconnect leaves another live
|
|
64
|
+
* handler behind and only the last one is ever removed.
|
|
65
|
+
*/
|
|
66
|
+
if (handler) {
|
|
67
|
+
client.off(event, handler);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
handler = (args) => {
|
|
71
|
+
const session = adapter.getSession(namespace);
|
|
72
|
+
if (!session || args.topic !== session.topic) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/*
|
|
77
|
+
* WC dispatches these from its own async request pipeline with nothing
|
|
78
|
+
* catching downstream, so a throw here (a payload shaped unlike anything
|
|
79
|
+
* the handler expects) would surface as an unhandled rejection and take
|
|
80
|
+
* the rest of that pipeline's processing with it. Async handlers are
|
|
81
|
+
* covered too - their rejection lands nowhere otherwise.
|
|
82
|
+
*/
|
|
83
|
+
try {
|
|
84
|
+
const handled = handle({ args, session, adapter, context });
|
|
85
|
+
if (handled instanceof Promise) {
|
|
86
|
+
void handled.catch(logHandlerError);
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
logHandlerError(error);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
client.on(event, handler);
|
|
94
|
+
},
|
|
95
|
+
(_, err) => {
|
|
96
|
+
const currentHandler = handler;
|
|
97
|
+
if (currentHandler) {
|
|
98
|
+
handler = null;
|
|
99
|
+
void getAdapter()
|
|
100
|
+
.getClient()
|
|
101
|
+
.then((client) => client.off(event, currentHandler));
|
|
102
|
+
}
|
|
103
|
+
return err;
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
}
|
package/src/provider.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Environments } from './types.js';
|
|
2
|
+
|
|
3
|
+
import { ProviderBuilder } from '@hub3js/core';
|
|
4
|
+
|
|
5
|
+
import { WalletConnectAdapter } from './adapter/adapter.js';
|
|
6
|
+
import { setAdapter } from './adapter/registry.js';
|
|
7
|
+
import { metadata, WALLET_ID } from './constants.js';
|
|
8
|
+
import { evm } from './namespaces/evm/namespace.js';
|
|
9
|
+
|
|
10
|
+
const buildProvider = () =>
|
|
11
|
+
new ProviderBuilder(WALLET_ID)
|
|
12
|
+
.init(function (context, environments: Environments) {
|
|
13
|
+
const [, setState] = context.state();
|
|
14
|
+
|
|
15
|
+
if (!environments.WC_PROJECT_ID) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
setAdapter(
|
|
20
|
+
new WalletConnectAdapter({
|
|
21
|
+
projectId: environments.WC_PROJECT_ID,
|
|
22
|
+
meta: environments.meta || [],
|
|
23
|
+
disableModalLink: environments.DISABLE_MODAL_AND_OPEN_LINK,
|
|
24
|
+
themeMode: environments.themeMode,
|
|
25
|
+
modalZIndex: environments.modalZIndex,
|
|
26
|
+
})
|
|
27
|
+
);
|
|
28
|
+
setState('installed', true);
|
|
29
|
+
console.debug('[wallet-connect-2] provider initialized.', context);
|
|
30
|
+
})
|
|
31
|
+
.config('metadata', metadata)
|
|
32
|
+
.add('evm', evm)
|
|
33
|
+
.build();
|
|
34
|
+
|
|
35
|
+
export { buildProvider };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { SessionTypes, SignClientTypes } from '@walletconnect/types';
|
|
2
|
+
|
|
3
|
+
import { AccountId } from 'caip';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `accountsChanged` entries are CAIP-10 (`eip155:1:0x…`) from wallets that follow
|
|
7
|
+
* WC and a bare `0x…` from the ones forwarding the EIP-1193 payload as-is.
|
|
8
|
+
*/
|
|
9
|
+
export function extractAddress(account: string): string {
|
|
10
|
+
return account.includes(':') ? new AccountId(account).address : account;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getAccountsFromSession(session: SessionTypes.Struct) {
|
|
14
|
+
const accounts = Object.values(session.namespaces)
|
|
15
|
+
.map((namespace) => namespace.accounts)
|
|
16
|
+
.flat()
|
|
17
|
+
.map((account) => {
|
|
18
|
+
const { address, chainId } = new AccountId(account);
|
|
19
|
+
return {
|
|
20
|
+
address,
|
|
21
|
+
chainId: chainId.reference,
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
return accounts;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function getAccountsFromEvent(
|
|
28
|
+
event: SignClientTypes.BaseEventArgs<{
|
|
29
|
+
namespaces: SessionTypes.Namespaces;
|
|
30
|
+
}>
|
|
31
|
+
) {
|
|
32
|
+
const accounts = Object.values(event.params.namespaces)
|
|
33
|
+
.map((namespace) => namespace.accounts)
|
|
34
|
+
.flat()
|
|
35
|
+
.map((account) => {
|
|
36
|
+
const { address, chainId } = new AccountId(account);
|
|
37
|
+
return {
|
|
38
|
+
accounts: [address],
|
|
39
|
+
chainId: chainId.reference,
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
return accounts;
|
|
44
|
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import type { SignClientInstance } from '../types.js';
|
|
2
|
+
import type { SessionTypes, SignClientTypes } from '@walletconnect/types';
|
|
3
|
+
|
|
4
|
+
import { debug } from '@rango-dev/logging-core';
|
|
5
|
+
import { CAIP_BITCOIN_CHAIN_ID } from '@rango-dev/wallets-core/namespaces/utxo';
|
|
6
|
+
import { AccountId } from 'caip';
|
|
7
|
+
|
|
8
|
+
import { BitcoinEvents, NAMESPACES } from '../wcConstants.js';
|
|
9
|
+
|
|
10
|
+
export function getBip122AccountsFromSession(
|
|
11
|
+
session: SessionTypes.Struct
|
|
12
|
+
): string[] {
|
|
13
|
+
return session.namespaces[NAMESPACES.BITCOIN]?.accounts ?? [];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function filterBip122Accounts(session: SessionTypes.Struct) {
|
|
17
|
+
return getBip122AccountsFromSession(session).map((account) => ({
|
|
18
|
+
address: new AccountId(account).address,
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type Bip122AddressEntry = {
|
|
23
|
+
address: string;
|
|
24
|
+
intention?: 'payment' | 'ordinal';
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Every address a `bip122_addressesChanged` payload carries, in wallet order.
|
|
29
|
+
* Unlike {@link pickPaymentAddress} it makes no guess about which one is active.
|
|
30
|
+
*/
|
|
31
|
+
export function getAnnouncedAddresses(
|
|
32
|
+
data: Bip122AddressEntry[] | string[] | undefined
|
|
33
|
+
): string[] {
|
|
34
|
+
if (!data?.length) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return data
|
|
39
|
+
.map((entry) => (typeof entry === 'string' ? entry : entry?.address))
|
|
40
|
+
.filter((address): address is string => !!address);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function pickPaymentAddress(
|
|
44
|
+
data: Bip122AddressEntry[] | string[] | undefined
|
|
45
|
+
): string | undefined {
|
|
46
|
+
if (!data?.length) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const first = data[0];
|
|
51
|
+
if (typeof first === 'string') {
|
|
52
|
+
return first;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const entries = data as Bip122AddressEntry[];
|
|
56
|
+
return (
|
|
57
|
+
entries.find((entry) => entry.intention === 'payment')?.address ??
|
|
58
|
+
entries[0]?.address
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function patchBip122SessionAccount(
|
|
63
|
+
session: SessionTypes.Struct,
|
|
64
|
+
address: string
|
|
65
|
+
): SessionTypes.Struct {
|
|
66
|
+
const btcNamespace = session.namespaces[NAMESPACES.BITCOIN];
|
|
67
|
+
if (!btcNamespace) {
|
|
68
|
+
return session;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const caipAccount = new AccountId({
|
|
72
|
+
chainId: {
|
|
73
|
+
namespace: NAMESPACES.BITCOIN,
|
|
74
|
+
reference: CAIP_BITCOIN_CHAIN_ID,
|
|
75
|
+
},
|
|
76
|
+
address,
|
|
77
|
+
}).toString();
|
|
78
|
+
|
|
79
|
+
const accounts = btcNamespace.accounts ?? [];
|
|
80
|
+
const hasAccount = accounts.some(
|
|
81
|
+
(account) =>
|
|
82
|
+
new AccountId(account).address.toLowerCase() === address.toLowerCase()
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (hasAccount) {
|
|
86
|
+
return session;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
...session,
|
|
91
|
+
namespaces: {
|
|
92
|
+
...session.namespaces,
|
|
93
|
+
[NAMESPACES.BITCOIN]: {
|
|
94
|
+
...btcNamespace,
|
|
95
|
+
accounts: [...accounts, caipAccount],
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const BIP122_ACCOUNT_TIMEOUT = 15_000;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Bitcoin wallets often approve bip122 without accounts in the session object.
|
|
105
|
+
* They emit bip122_addressesChanged or session_update shortly after approval.
|
|
106
|
+
*/
|
|
107
|
+
export async function resolveBip122Session(
|
|
108
|
+
client: SignClientInstance,
|
|
109
|
+
session: SessionTypes.Struct
|
|
110
|
+
): Promise<SessionTypes.Struct> {
|
|
111
|
+
const existing = filterBip122Accounts(session);
|
|
112
|
+
if (existing.length) {
|
|
113
|
+
return session;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const freshSession = client.session.get(session.topic) ?? session;
|
|
117
|
+
const freshAccounts = filterBip122Accounts(freshSession);
|
|
118
|
+
if (freshAccounts.length) {
|
|
119
|
+
return freshSession;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const { address, session: resolvedSession } =
|
|
123
|
+
await waitForBip122PaymentAddress(client, freshSession);
|
|
124
|
+
|
|
125
|
+
const patched = patchBip122SessionAccount(resolvedSession, address);
|
|
126
|
+
await persistSessionNamespaces(client, patched);
|
|
127
|
+
return patched;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* The address the wallet reports via `bip122_addressesChanged` lands only on our
|
|
132
|
+
* patched copy, so write it back to `client.session` - the record that restore,
|
|
133
|
+
* teardown and staleness checks all read. Left unpersisted, the stored session
|
|
134
|
+
* keeps `bip122.accounts === []`: `cleanupStaleSessionsForNamespace` reads a live
|
|
135
|
+
* session as stale and disconnects it, and a reload waits again for an event the
|
|
136
|
+
* wallet only emits right after approval.
|
|
137
|
+
*/
|
|
138
|
+
async function persistSessionNamespaces(
|
|
139
|
+
client: SignClientInstance,
|
|
140
|
+
session: SessionTypes.Struct
|
|
141
|
+
): Promise<void> {
|
|
142
|
+
try {
|
|
143
|
+
await client.session.update(session.topic, {
|
|
144
|
+
namespaces: session.namespaces,
|
|
145
|
+
});
|
|
146
|
+
} catch (error) {
|
|
147
|
+
debug(error instanceof Error ? error : new Error(String(error)));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function waitForBip122PaymentAddress(
|
|
152
|
+
client: SignClientInstance,
|
|
153
|
+
session: SessionTypes.Struct,
|
|
154
|
+
timeoutMs = BIP122_ACCOUNT_TIMEOUT
|
|
155
|
+
): Promise<{ address: string; session: SessionTypes.Struct }> {
|
|
156
|
+
return await new Promise((resolve, reject) => {
|
|
157
|
+
const topic = session.topic;
|
|
158
|
+
let settled = false;
|
|
159
|
+
|
|
160
|
+
const finish = (address: string, updatedSession: SessionTypes.Struct) => {
|
|
161
|
+
if (settled) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
settled = true;
|
|
165
|
+
cleanup();
|
|
166
|
+
resolve({ address, session: updatedSession });
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const fail = (error: Error) => {
|
|
170
|
+
if (settled) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
settled = true;
|
|
174
|
+
cleanup();
|
|
175
|
+
reject(error);
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const timer = setTimeout(() => {
|
|
179
|
+
fail(
|
|
180
|
+
new Error(
|
|
181
|
+
'Timed out waiting for Bitcoin account from wallet. Please try reconnecting.'
|
|
182
|
+
)
|
|
183
|
+
);
|
|
184
|
+
}, timeoutMs);
|
|
185
|
+
|
|
186
|
+
const cleanup = () => {
|
|
187
|
+
clearTimeout(timer);
|
|
188
|
+
client.off('session_event', onEvent);
|
|
189
|
+
client.off('session_update', onUpdate);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const onEvent = (args: SignClientTypes.EventArguments['session_event']) => {
|
|
193
|
+
if (args.topic !== topic) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (args.params.event.name !== BitcoinEvents.ADDRESSES_CHANGED) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const address = pickPaymentAddress(
|
|
201
|
+
args.params.event.data as Bip122AddressEntry[]
|
|
202
|
+
);
|
|
203
|
+
if (address) {
|
|
204
|
+
finish(address, client.session.get(topic) ?? session);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const onUpdate = (
|
|
209
|
+
args: SignClientTypes.EventArguments['session_update']
|
|
210
|
+
) => {
|
|
211
|
+
if (args.topic !== topic) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const updatedSession = client.session.get(topic) ?? session;
|
|
216
|
+
const accounts = filterBip122Accounts(updatedSession);
|
|
217
|
+
if (accounts.length) {
|
|
218
|
+
finish(accounts[0].address, updatedSession);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
client.on('session_event', onEvent);
|
|
223
|
+
client.on('session_update', onUpdate);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ISignClient } from '@walletconnect/types';
|
|
2
|
+
import type { BlockchainMeta } from 'rango-types';
|
|
3
|
+
|
|
4
|
+
import { CHAIN_ID_STORAGE } from '../wcConstants.js';
|
|
5
|
+
|
|
6
|
+
export async function persistCurrentChainId(
|
|
7
|
+
client: ISignClient,
|
|
8
|
+
chainId?: string
|
|
9
|
+
) {
|
|
10
|
+
return client.core.storage.setItem(CHAIN_ID_STORAGE, {
|
|
11
|
+
defaultChainId: chainId ? parseInt(chainId) : '',
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function getPersistedChainId(client: ISignClient) {
|
|
16
|
+
try {
|
|
17
|
+
const chainId = (await client.core.storage.getItem(CHAIN_ID_STORAGE))
|
|
18
|
+
?.defaultChainId;
|
|
19
|
+
return !!chainId ? String(chainId) : undefined;
|
|
20
|
+
} catch {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getChainIdByNetworkName(
|
|
26
|
+
network: string,
|
|
27
|
+
meta: BlockchainMeta[]
|
|
28
|
+
): string | undefined {
|
|
29
|
+
const targetBlockchain = meta.find(
|
|
30
|
+
(blockchain) => blockchain.name === network
|
|
31
|
+
);
|
|
32
|
+
const chainIdInHex = targetBlockchain?.chainId;
|
|
33
|
+
if (!chainIdInHex) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return String(parseInt(chainIdInHex));
|
|
38
|
+
}
|