@rango-dev/provider-walletconnect-2 0.0.0-experimental-936229e8-20251208

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +240 -0
  2. package/dist/chunk-BPSEZJUF.js +2 -0
  3. package/dist/chunk-BPSEZJUF.js.map +7 -0
  4. package/dist/chunk-DXVBX5XQ.js +2 -0
  5. package/dist/chunk-DXVBX5XQ.js.map +7 -0
  6. package/dist/chunk-RPL2NMJC.js +2 -0
  7. package/dist/chunk-RPL2NMJC.js.map +7 -0
  8. package/dist/constants.d.ts +55 -0
  9. package/dist/cosmos-I3Z34UI3.js +2 -0
  10. package/dist/cosmos-I3Z34UI3.js.map +7 -0
  11. package/dist/dist-6GI3ECE5.js +139 -0
  12. package/dist/dist-6GI3ECE5.js.map +7 -0
  13. package/dist/evm-MYSQOTV5.js +2 -0
  14. package/dist/evm-MYSQOTV5.js.map +7 -0
  15. package/dist/helpers.d.ts +31 -0
  16. package/dist/index.d.ts +21 -0
  17. package/dist/index.js +2 -0
  18. package/dist/index.js.map +7 -0
  19. package/dist/provider-walletconnect-2.build.json +1 -0
  20. package/dist/session.d.ts +63 -0
  21. package/dist/signer.d.ts +3 -0
  22. package/dist/signers/cosmos.d.ts +14 -0
  23. package/dist/signers/evm.d.ts +15 -0
  24. package/dist/signers/helper.d.ts +2 -0
  25. package/dist/signers/mock.d.ts +2 -0
  26. package/dist/signers/solana.d.ts +14 -0
  27. package/dist/solana-35I33VLX.js +2 -0
  28. package/dist/solana-35I33VLX.js.map +7 -0
  29. package/dist/types.d.ts +24 -0
  30. package/package.json +52 -0
  31. package/readme.md +8 -0
  32. package/src/constants.ts +91 -0
  33. package/src/helpers.ts +242 -0
  34. package/src/index.ts +233 -0
  35. package/src/session.ts +374 -0
  36. package/src/signer.ts +45 -0
  37. package/src/signers/cosmos.ts +248 -0
  38. package/src/signers/evm.ts +163 -0
  39. package/src/signers/helper.ts +77 -0
  40. package/src/signers/mock.ts +3798 -0
  41. package/src/signers/solana.ts +160 -0
  42. package/src/types.ts +30 -0
  43. package/src/wc-types.d.ts +31 -0
@@ -0,0 +1,248 @@
1
+ import type { AminoSignResponse } from '@cosmjs/launchpad';
2
+ import type { SignClient } from '@walletconnect/sign-client/dist/types/client';
3
+ import type { SessionTypes } from '@walletconnect/types';
4
+ import type { CosmosTransaction, GenericSigner } from 'rango-types';
5
+
6
+ import { BroadcastMode, makeSignDoc } from '@cosmjs/launchpad';
7
+ import { cosmos } from '@keplr-wallet/cosmos';
8
+ import { getsignedTx } from '@rango-dev/signer-cosmos';
9
+ import { uint8ArrayToHex } from '@rango-dev/wallets-shared';
10
+ import { AccountId, ChainId } from 'caip';
11
+ import { formatDirectSignDoc, stringifySignDocValues } from 'cosmos-wallet';
12
+ import { SignerError, SignerErrorCode } from 'rango-types';
13
+
14
+ import { CosmosRPCMethods, NAMESPACES } from '../constants.js';
15
+
16
+ import { sendTx } from './helper.js';
17
+ import { supportedChains } from './mock.js';
18
+
19
+ const NAMESPACE_NAME = NAMESPACES.COSMOS;
20
+ type DirectSignResponse = {
21
+ signature: {
22
+ pub_key: {
23
+ type: string;
24
+ value: string;
25
+ };
26
+ signature: string;
27
+ };
28
+ signed: {
29
+ chainId: string;
30
+ accountNumber: string;
31
+ authInfoBytes: string;
32
+ bodyBytes: string;
33
+ };
34
+ };
35
+ class COSMOSSigner implements GenericSigner<CosmosTransaction> {
36
+ private client: SignClient;
37
+ private session: SessionTypes.Struct;
38
+
39
+ constructor(client: SignClient, session: SessionTypes.Struct) {
40
+ this.client = client;
41
+ this.session = session;
42
+ }
43
+
44
+ public async signMessage(): Promise<string> {
45
+ throw SignerError.UnimplementedError('signMessage');
46
+ }
47
+
48
+ async signAndSendTx(
49
+ tx: CosmosTransaction,
50
+ address: string,
51
+ chainId: string | null
52
+ ): Promise<{ hash: string }> {
53
+ console.log({ tx, address, chainId });
54
+
55
+ const requestedFor = this.isNetworkAndAccountExistInSession({
56
+ address,
57
+ chainId,
58
+ });
59
+ try {
60
+ const { memo, sequence, account_number, chainId, msgs, fee, signType } =
61
+ tx.data;
62
+ const msgsWithoutType = msgs.map((m) => ({
63
+ ...m,
64
+ __type: undefined,
65
+ '@type': undefined,
66
+ }));
67
+ if (!chainId) {
68
+ throw SignerError.AssertionFailed('chainId is undefined from server');
69
+ }
70
+ if (!account_number) {
71
+ throw SignerError.AssertionFailed(
72
+ 'account_number is undefined from server'
73
+ );
74
+ }
75
+ if (!sequence) {
76
+ throw SignerError.AssertionFailed('sequence is undefined from server');
77
+ }
78
+
79
+ if (signType === 'AMINO') {
80
+ const signDoc = makeSignDoc(
81
+ msgsWithoutType,
82
+ fee as any,
83
+ chainId,
84
+ memo || undefined,
85
+ account_number,
86
+ sequence
87
+ );
88
+ let signResponse;
89
+ try {
90
+ signResponse = await this.client.request<AminoSignResponse>({
91
+ topic: this.session.topic,
92
+ chainId: requestedFor.caipChainId,
93
+ request: {
94
+ method: CosmosRPCMethods.SIGN_AMINO,
95
+ params: {
96
+ signDoc,
97
+ signerAddress: tx.fromWalletAddress,
98
+ },
99
+ },
100
+ });
101
+ } catch (err) {
102
+ throw new SignerError(SignerErrorCode.SIGN_TX_ERROR, undefined, err);
103
+ }
104
+
105
+ const signedTx = getsignedTx(tx, signResponse);
106
+ const result = await sendTx(
107
+ chainId,
108
+ signedTx,
109
+ BroadcastMode.Async,
110
+ supportedChains
111
+ );
112
+ return { hash: uint8ArrayToHex(result) };
113
+ } else if (signType === 'DIRECT') {
114
+ let getAccounts;
115
+ try {
116
+ getAccounts = await this.client.request<
117
+ Array<{
118
+ address: string;
119
+ algo: string;
120
+ pubkey: string;
121
+ }>
122
+ >({
123
+ topic: this.session.topic,
124
+ chainId: requestedFor.caipChainId,
125
+ request: {
126
+ method: CosmosRPCMethods.GET_ACCOUNTS,
127
+ params: {},
128
+ },
129
+ });
130
+ } catch (err) {
131
+ throw new SignerError(SignerErrorCode.SIGN_TX_ERROR, undefined, err);
132
+ }
133
+ const pubkey =
134
+ getAccounts?.find(
135
+ (account) => account.address === tx.fromWalletAddress
136
+ )?.pubkey || '';
137
+
138
+ const bodyBytes = cosmos.tx.v1beta1.TxBody.encode({
139
+ messages: tx.data.protoMsgs.map((m) => ({
140
+ type_url: m.type_url,
141
+ value: new Uint8Array(m.value),
142
+ })),
143
+ memo,
144
+ }).finish();
145
+ console.log({ tx, bodyBytes });
146
+
147
+ const signDoc = formatDirectSignDoc(
148
+ fee?.amount || [],
149
+ pubkey,
150
+ parseInt(fee?.gas as string),
151
+ account_number,
152
+ parseInt(sequence),
153
+ uint8ArrayToHex(bodyBytes),
154
+ chainId
155
+ );
156
+ let signResponse;
157
+ try {
158
+ signResponse = await this.client.request<DirectSignResponse>({
159
+ topic: this.session.topic,
160
+ chainId: requestedFor.caipChainId,
161
+ request: {
162
+ method: CosmosRPCMethods.SIGN_DIRECT,
163
+ params: {
164
+ signDoc: stringifySignDocValues(signDoc),
165
+ signerAddress: tx.fromWalletAddress,
166
+ },
167
+ },
168
+ });
169
+ } catch (err) {
170
+ throw new SignerError(SignerErrorCode.SIGN_TX_ERROR, undefined, err);
171
+ }
172
+ console.log({ signResponse });
173
+ const signedTx = cosmos.tx.v1beta1.TxRaw.encode({
174
+ bodyBytes: new TextEncoder().encode(signResponse.signed.bodyBytes),
175
+ authInfoBytes: new TextEncoder().encode(
176
+ signResponse.signed.authInfoBytes
177
+ ),
178
+ signatures: [Buffer.from(signResponse.signature.signature, 'base64')],
179
+ }).finish();
180
+ console.log({ signedTx });
181
+ const result = await sendTx(
182
+ chainId,
183
+ signedTx,
184
+ BroadcastMode.Async,
185
+ supportedChains
186
+ );
187
+
188
+ console.log({ result });
189
+
190
+ return { hash: uint8ArrayToHex(result) };
191
+ }
192
+ throw new SignerError(
193
+ SignerErrorCode.OPERATION_UNSUPPORTED,
194
+ `Sign type for cosmos not supported, type: ${signType}`
195
+ );
196
+ } catch (err) {
197
+ if (SignerError.isSignerError(err)) {
198
+ throw err;
199
+ } else {
200
+ throw new SignerError(SignerErrorCode.SEND_TX_ERROR, undefined, err);
201
+ }
202
+ }
203
+ }
204
+
205
+ private isNetworkAndAccountExistInSession(requestedFor: {
206
+ address: string;
207
+ chainId: string | null;
208
+ }) {
209
+ const { address, chainId } = requestedFor;
210
+
211
+ if (!chainId) {
212
+ throw new Error(
213
+ 'You need to set your chain for signing message/transaction.'
214
+ );
215
+ }
216
+
217
+ const caipAddress = new AccountId({
218
+ chainId: {
219
+ namespace: NAMESPACE_NAME,
220
+ reference: chainId,
221
+ },
222
+ address,
223
+ });
224
+ const addresses = this.session.namespaces[NAMESPACE_NAME]?.accounts.map(
225
+ (address) => address.toLowerCase()
226
+ );
227
+
228
+ if (!addresses || !addresses.includes(caipAddress.toString())) {
229
+ console.warn(
230
+ 'Available adresses and requested address:',
231
+ addresses,
232
+ caipAddress.toString()
233
+ );
234
+ throw new Error(
235
+ `Your requested address doesn't exist on your wallect connect session. Please reconnect your wallet.`
236
+ );
237
+ }
238
+
239
+ const caipChainId = new ChainId({
240
+ namespace: NAMESPACE_NAME,
241
+ reference: chainId,
242
+ });
243
+
244
+ return { chainId, address, caipChainId: caipChainId.toString() };
245
+ }
246
+ }
247
+
248
+ export default COSMOSSigner;
@@ -0,0 +1,163 @@
1
+ import type { SignClient } from '@walletconnect/sign-client/dist/types/client';
2
+ import type { SessionTypes } from '@walletconnect/types';
3
+ import type { EvmTransaction } from 'rango-types/mainApi';
4
+
5
+ import { cleanEvmError, DefaultEvmSigner } from '@rango-dev/signer-evm';
6
+ import * as encoding from '@walletconnect/encoding';
7
+ import { AccountId, ChainId } from 'caip';
8
+ import { type GenericSigner } from 'rango-types';
9
+
10
+ import { EthereumRPCMethods, NAMESPACES } from '../constants.js';
11
+
12
+ const NAMESPACE_NAME = NAMESPACES.ETHEREUM;
13
+
14
+ class EVMSigner implements GenericSigner<EvmTransaction> {
15
+ private client: SignClient;
16
+ private session: SessionTypes.Struct;
17
+
18
+ constructor(client: SignClient, session: SessionTypes.Struct) {
19
+ this.client = client;
20
+ this.session = session;
21
+ }
22
+
23
+ public async signMessage(
24
+ msg: string,
25
+ address: string,
26
+ chainId: string | null
27
+ ): Promise<string> {
28
+ const requestedFor = this.isNetworkAndAccountExistInSession({
29
+ address,
30
+ chainId,
31
+ });
32
+
33
+ const caipChainId = new ChainId({
34
+ namespace: NAMESPACE_NAME,
35
+ reference: requestedFor.chainId,
36
+ });
37
+ const hexMsg = encoding.utf8ToHex(msg, true);
38
+
39
+ const params = [hexMsg, address];
40
+
41
+ let signature: string;
42
+ try {
43
+ // Send message to wallet (using relayer)
44
+ signature = await this.client.request({
45
+ topic: this.session.topic,
46
+ chainId: caipChainId.toString(),
47
+ request: {
48
+ method: EthereumRPCMethods.PERSONAL_SIGN,
49
+ params,
50
+ },
51
+ });
52
+ } catch (error) {
53
+ throw cleanEvmError(error);
54
+ }
55
+
56
+ /*
57
+ * TODO: We can also verify the signature here
58
+ * Check web-examples: dapps/react-dapp-v2/src/contexts/JsonRpcContext.tsx
59
+ */
60
+
61
+ return signature;
62
+ }
63
+
64
+ async signAndSendTx(
65
+ tx: EvmTransaction,
66
+ address: string,
67
+ chainId: string | null
68
+ ): Promise<{ hash: string }> {
69
+ try {
70
+ const requestedFor = this.isNetworkAndAccountExistInSession({
71
+ address,
72
+ chainId,
73
+ });
74
+ const transaction = DefaultEvmSigner.buildTx(tx);
75
+ const hash: string = await this.client.request({
76
+ topic: this.session.topic,
77
+ chainId: requestedFor.caipChainId,
78
+ request: {
79
+ method: EthereumRPCMethods.SEND_TRANSACTION,
80
+ params: [transaction],
81
+ },
82
+ });
83
+ // Some wallets e.g. Rainbow wallet are returning invalid hash (e.g. 'null') in case of the rejection
84
+ if (!hash?.startsWith('0x')) {
85
+ throw new Error(
86
+ `Received an invalid hash on signing the transaction. (hash=${hash})`
87
+ );
88
+ }
89
+ return {
90
+ hash,
91
+ };
92
+ } catch (error: any) {
93
+ const modifiedError = cleanEvmError(error);
94
+ const session = this.session;
95
+ const context = {
96
+ namspaces: session?.namespaces,
97
+ peering: session?.peer?.metadata,
98
+ code: error?.code,
99
+ };
100
+ modifiedError.context = context;
101
+ throw modifiedError;
102
+ }
103
+ }
104
+
105
+ private isNetworkAndAccountExistInSession(requestedFor: {
106
+ address: string;
107
+ chainId: string | null;
108
+ }) {
109
+ const { address, chainId } = requestedFor;
110
+
111
+ if (!chainId) {
112
+ console.log('isNetworkAndAccountExistInSession', requestedFor);
113
+ throw new Error(
114
+ 'You need to set your chain for signing message/transaction.'
115
+ );
116
+ }
117
+
118
+ /*
119
+ * TODO: We need to make sure we are using a single format for chain ids, it should be hex or number.
120
+ * This is a quick fix for evm.
121
+ */
122
+ const chainIdNumber = chainId.startsWith('0x')
123
+ ? String(parseInt(chainId))
124
+ : chainId;
125
+
126
+ const caipAddress = new AccountId({
127
+ chainId: {
128
+ namespace: NAMESPACE_NAME,
129
+ reference: chainIdNumber,
130
+ },
131
+ address,
132
+ });
133
+ const addresses = this.session.namespaces[NAMESPACE_NAME]?.accounts.map(
134
+ (address) => address.toLowerCase()
135
+ );
136
+
137
+ if (!addresses || !addresses.includes(caipAddress.toString())) {
138
+ console.warn(
139
+ 'Available adresses and requested address:',
140
+ addresses,
141
+ caipAddress.toString(),
142
+ chainId,
143
+ address
144
+ );
145
+ throw new Error(
146
+ `Your requested address doesn't exist on your wallect connect session. Please reconnect your wallet.`
147
+ );
148
+ }
149
+
150
+ const caipChainId = new ChainId({
151
+ namespace: NAMESPACE_NAME,
152
+ reference: chainIdNumber,
153
+ });
154
+
155
+ return {
156
+ chainId: chainIdNumber,
157
+ address,
158
+ caipChainId: caipChainId.toString(),
159
+ };
160
+ }
161
+ }
162
+
163
+ export default EVMSigner;
@@ -0,0 +1,77 @@
1
+ import type { BlockchainMeta } from 'rango-types';
2
+
3
+ import { TendermintTxTracer } from '@keplr-wallet/cosmos';
4
+ import { simpleFetch } from '@keplr-wallet/simple-fetch';
5
+ import { cosmosBlockchains } from 'rango-types';
6
+
7
+ export async function sendTx(
8
+ chainId: string,
9
+ tx: unknown,
10
+ mode: 'async' | 'sync' | 'block',
11
+ supportedChains: BlockchainMeta[]
12
+ ): Promise<Uint8Array> {
13
+ console.log({ chainId, tx, mode });
14
+
15
+ const cosmos = cosmosBlockchains(supportedChains);
16
+ const chainInfo = cosmos.find((item) => item.chainId === chainId)?.info;
17
+ const isProtoTx = Buffer.isBuffer(tx) || tx instanceof Uint8Array;
18
+
19
+ console.log({ chainInfo, isProtoTx });
20
+
21
+ if (!chainInfo) {
22
+ throw new Error('Chain info is undefined from server');
23
+ }
24
+ const params = isProtoTx
25
+ ? {
26
+ tx_bytes: Buffer.from(tx as any).toString('base64'),
27
+ mode: (() => {
28
+ switch (mode) {
29
+ case 'async':
30
+ return 'BROADCAST_MODE_ASYNC';
31
+ case 'block':
32
+ return 'BROADCAST_MODE_BLOCK';
33
+ case 'sync':
34
+ return 'BROADCAST_MODE_SYNC';
35
+ default:
36
+ return 'BROADCAST_MODE_UNSPECIFIED';
37
+ }
38
+ })(),
39
+ }
40
+ : {
41
+ tx,
42
+ mode: mode,
43
+ };
44
+
45
+ try {
46
+ const result = await simpleFetch<any>(
47
+ chainInfo.rest,
48
+ isProtoTx ? '/cosmos/tx/v1beta1/txs' : '/txs',
49
+ {
50
+ method: 'POST',
51
+ headers: {
52
+ 'content-type': 'application/json',
53
+ },
54
+ body: JSON.stringify(params),
55
+ }
56
+ );
57
+
58
+ const txResponse = isProtoTx ? result.data['tx_response'] : result.data;
59
+
60
+ if (txResponse.code != null && txResponse.code !== 0) {
61
+ throw new Error(txResponse['raw_log']);
62
+ }
63
+
64
+ const txHash = Buffer.from(txResponse.txhash, 'hex');
65
+
66
+ const txTracer = new TendermintTxTracer(chainInfo.rpc, '/websocket');
67
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
68
+ txTracer.traceTx(txHash).then(() => {
69
+ txTracer.close();
70
+ });
71
+
72
+ return txHash;
73
+ } catch (e) {
74
+ console.log(e);
75
+ throw e;
76
+ }
77
+ }