@getpara/graz-connector 2.0.0-dev.12 → 2.0.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/dist/cjs/connector.js +348 -0
- package/dist/cjs/index.js +30 -0
- package/dist/cjs/package.json +3 -0
- package/dist/{connector.js → esm/connector.js} +2 -6
- package/dist/{connector.d.ts → types/connector.d.ts} +2 -12
- package/package.json +24 -13
- package/__tests__/connector.test.tsx +0 -491
- package/__tests__/connectorModal.test.tsx +0 -148
- package/scripts/build.mjs +0 -36
- package/src/connector.ts +0 -355
- package/src/index.ts +0 -2
- package/tsconfig.json +0 -13
- package/vitest.config.js +0 -22
- /package/dist/{chunk-M66XENHI.js → esm/chunk-M66XENHI.js} +0 -0
- /package/dist/{index.js → esm/index.js} +0 -0
- /package/dist/{package.json → esm/package.json} +0 -0
- /package/dist/{index.d.ts → types/index.d.ts} +0 -0
package/src/connector.ts
DELETED
|
@@ -1,355 +0,0 @@
|
|
|
1
|
-
import { fromBech32 } from '@cosmjs/encoding';
|
|
2
|
-
import { SignDoc } from 'cosmjs-types/cosmos/tx/v1beta1/tx.js';
|
|
3
|
-
import { Wallet, SignDoc as GrazSignDoc } from 'graz';
|
|
4
|
-
import { ParaAminoSigner, ParaProtoSigner } from '@getpara/cosmjs-v0-integration';
|
|
5
|
-
import { ParaWeb, Wallet as ParaWallet } from '@getpara/web-sdk';
|
|
6
|
-
import { Algo, DirectSignResponse, OfflineDirectSigner } from '@cosmjs/proto-signing';
|
|
7
|
-
import { AccountData, AminoSignResponse, OfflineAminoSigner, StdSignature, StdSignDoc } from '@cosmjs/amino';
|
|
8
|
-
import { ChainInfo, KeplrSignOptions } from '@keplr-wallet/types';
|
|
9
|
-
|
|
10
|
-
export type ParaGrazConnectorEvents = {
|
|
11
|
-
onEnabled?: (chainIds: string[], connector: ParaGrazConnector) => void;
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export interface ParaGrazConfig {
|
|
15
|
-
paraWeb: ParaWeb;
|
|
16
|
-
events?: ParaGrazConnectorEvents;
|
|
17
|
-
noModal?: boolean;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function toArray<T>(v: T | T[]): T[] {
|
|
21
|
-
return Array.isArray(v) ? v : [v];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
class ParaOfflineSigner implements OfflineDirectSigner {
|
|
25
|
-
constructor(
|
|
26
|
-
protected readonly chainId: string,
|
|
27
|
-
protected readonly connector: ParaGrazConnector,
|
|
28
|
-
) {}
|
|
29
|
-
|
|
30
|
-
protected get para() {
|
|
31
|
-
return this.connector.getParaWebClient();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
protected get prefix() {
|
|
35
|
-
return this.connector.getBech32Prefix(this.chainId);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
protected async wallet() {
|
|
39
|
-
return this.connector.getFirstWallet();
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async getAccounts(): Promise<readonly AccountData[]> {
|
|
43
|
-
const key = await this.connector.getKey(this.chainId);
|
|
44
|
-
return [
|
|
45
|
-
{
|
|
46
|
-
address: key.bech32Address,
|
|
47
|
-
algo: key.algo as Algo,
|
|
48
|
-
pubkey: key.pubKey,
|
|
49
|
-
},
|
|
50
|
-
];
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async signDirect(signerAddress: string, signDoc: SignDoc): Promise<DirectSignResponse> {
|
|
54
|
-
if (this.chainId !== signDoc.chainId) {
|
|
55
|
-
throw new Error(`Chain ID mismatch: expected ${this.chainId}, got ${signDoc.chainId}`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const accounts = await this.getAccounts();
|
|
59
|
-
if (accounts.every(a => a.address !== signerAddress)) {
|
|
60
|
-
throw new Error(`Signer address ${signerAddress} not found in wallet`);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const signer = new ParaProtoSigner(this.para, this.prefix, (await this.wallet()).id);
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
const result = await signer.signDirect(signerAddress, signDoc);
|
|
67
|
-
return {
|
|
68
|
-
signed: {
|
|
69
|
-
bodyBytes: result.signed.bodyBytes,
|
|
70
|
-
authInfoBytes: result.signed.authInfoBytes,
|
|
71
|
-
chainId: result.signed.chainId,
|
|
72
|
-
accountNumber: result.signed.accountNumber,
|
|
73
|
-
},
|
|
74
|
-
signature: result.signature,
|
|
75
|
-
};
|
|
76
|
-
} catch (err) {
|
|
77
|
-
throw new Error(`Direct signing failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export class ParaGrazConnector implements Omit<Wallet, 'experimentalSuggestChain'> {
|
|
83
|
-
protected paraWebClient: ParaWeb;
|
|
84
|
-
protected enabledChainIds = new Set<string>();
|
|
85
|
-
protected readonly events?: ParaGrazConnectorEvents;
|
|
86
|
-
protected noModal?: boolean;
|
|
87
|
-
|
|
88
|
-
constructor(
|
|
89
|
-
protected readonly config: ParaGrazConfig,
|
|
90
|
-
protected readonly chains: ChainInfo[] | null = null,
|
|
91
|
-
) {
|
|
92
|
-
if (!config?.paraWeb) {
|
|
93
|
-
throw new Error('ParaWeb instance required in config');
|
|
94
|
-
}
|
|
95
|
-
this.events = config.events;
|
|
96
|
-
this.paraWebClient = config.paraWeb;
|
|
97
|
-
this.noModal = config.noModal;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
protected async ensureChainEnabled(chainId: string): Promise<void> {
|
|
101
|
-
if (!this.enabledChainIds.has(chainId)) {
|
|
102
|
-
throw new Error(`Chain ${chainId} not enabled. Call enable() first`);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (!(await this.paraWebClient.isFullyLoggedIn())) {
|
|
106
|
-
throw new Error('Para wallet not authenticated');
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
protected async waitForLogin(timeoutMs = 60_000): Promise<void> {
|
|
111
|
-
const deadline = Date.now() + timeoutMs;
|
|
112
|
-
let delay = 500;
|
|
113
|
-
const MAX_DELAY = 5_000;
|
|
114
|
-
|
|
115
|
-
while (true) {
|
|
116
|
-
if (await this.paraWebClient.isFullyLoggedIn()) {
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (Date.now() >= deadline) {
|
|
121
|
-
throw new Error(`Login timeout after ${timeoutMs / 1000}s`);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
await new Promise(r => setTimeout(r, delay));
|
|
125
|
-
delay = Math.min(delay * 1.5, MAX_DELAY);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
protected async waitForAccounts(timeoutMs = 5_000): Promise<ParaWallet[]> {
|
|
130
|
-
const deadline = Date.now() + timeoutMs;
|
|
131
|
-
let delay = 250;
|
|
132
|
-
const MAX_DELAY = 1_000;
|
|
133
|
-
|
|
134
|
-
while (true) {
|
|
135
|
-
const wallets = this.paraWebClient.getWalletsByType('COSMOS');
|
|
136
|
-
if (wallets.length) {
|
|
137
|
-
return wallets;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (Date.now() >= deadline) {
|
|
141
|
-
throw new Error('No Cosmos wallets found');
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
await new Promise(r => setTimeout(r, delay));
|
|
145
|
-
delay = Math.min(delay * 1.5, MAX_DELAY);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
protected async hasCosmosWallet(): Promise<boolean> {
|
|
150
|
-
const isLoggedIn = await this.paraWebClient.isFullyLoggedIn();
|
|
151
|
-
const wallets = this.paraWebClient.getWalletsByType('COSMOS');
|
|
152
|
-
return isLoggedIn && wallets.length > 0;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
async enable(chainIdsInput: string | string[]): Promise<void> {
|
|
156
|
-
const chainIds = toArray(chainIdsInput);
|
|
157
|
-
const previousEnabled = new Set(this.enabledChainIds);
|
|
158
|
-
|
|
159
|
-
try {
|
|
160
|
-
chainIds.forEach(id => this.enabledChainIds.add(id));
|
|
161
|
-
|
|
162
|
-
if (await this.hasCosmosWallet()) {
|
|
163
|
-
this.events?.onEnabled?.(chainIds, this);
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (!this.noModal) {
|
|
168
|
-
throw new Error('Modal not supported. Use @getpara/graz-integration or set noModal: true');
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
await this.waitForLogin();
|
|
172
|
-
await this.waitForAccounts();
|
|
173
|
-
this.events?.onEnabled?.(chainIds, this);
|
|
174
|
-
} catch (err) {
|
|
175
|
-
this.enabledChainIds = previousEnabled;
|
|
176
|
-
|
|
177
|
-
if (err instanceof Error) {
|
|
178
|
-
throw err;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
throw new Error('Failed to enable Para wallet');
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
async disconnect(): Promise<void> {
|
|
186
|
-
try {
|
|
187
|
-
await this.paraWebClient.logout();
|
|
188
|
-
} catch (err) {
|
|
189
|
-
throw new Error('Disconnect failed');
|
|
190
|
-
} finally {
|
|
191
|
-
this.enabledChainIds.clear();
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async getFirstWallet(): Promise<ParaWallet> {
|
|
196
|
-
try {
|
|
197
|
-
const [wallet] = await this.waitForAccounts();
|
|
198
|
-
return wallet;
|
|
199
|
-
} catch (err) {
|
|
200
|
-
throw new Error('No Para wallet available');
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
getBech32Prefix(chainId: string): string {
|
|
205
|
-
const prefix = this.chains?.find(c => c.chainId === chainId)?.bech32Config?.bech32PrefixAccAddr || 'cosmos';
|
|
206
|
-
return prefix;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
getParaWebClient(): ParaWeb {
|
|
210
|
-
return this.paraWebClient;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
getConfig(): ParaGrazConfig {
|
|
214
|
-
return this.config;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
protected buildHybridSigner(chainId: string): OfflineAminoSigner & OfflineDirectSigner {
|
|
218
|
-
const aminoSigner = this.getOfflineSignerOnlyAmino(chainId);
|
|
219
|
-
const directSigner = new ParaOfflineSigner(chainId, this);
|
|
220
|
-
return {
|
|
221
|
-
getAccounts: () => directSigner.getAccounts(),
|
|
222
|
-
signAmino: (signer: string, signDoc: StdSignDoc) => aminoSigner.signAmino(signer, signDoc),
|
|
223
|
-
signDirect: (signer: string, signDoc: SignDoc) => directSigner.signDirect(signer, signDoc),
|
|
224
|
-
} as unknown as OfflineAminoSigner & OfflineDirectSigner;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
async getKey(chainId: string) {
|
|
228
|
-
try {
|
|
229
|
-
await this.ensureChainEnabled(chainId);
|
|
230
|
-
const wallet = await this.getFirstWallet();
|
|
231
|
-
const signer = new ParaProtoSigner(this.paraWebClient, this.getBech32Prefix(chainId), wallet.id);
|
|
232
|
-
const [account] = await signer.getAccounts();
|
|
233
|
-
|
|
234
|
-
if (!account) {
|
|
235
|
-
throw new Error(`No Cosmos accounts for chain ${chainId}`);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return {
|
|
239
|
-
name: 'Para Wallet',
|
|
240
|
-
algo: account.algo,
|
|
241
|
-
pubKey: account.pubkey,
|
|
242
|
-
address: fromBech32(account.address).data,
|
|
243
|
-
bech32Address: account.address,
|
|
244
|
-
isKeystone: false,
|
|
245
|
-
isNanoLedger: false,
|
|
246
|
-
};
|
|
247
|
-
} catch (err) {
|
|
248
|
-
if (err instanceof Error) {
|
|
249
|
-
throw err;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
throw new Error(`Failed to get key for chain ${chainId}`);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
getOfflineSignerOnlyAmino(chainId: string): OfflineAminoSigner {
|
|
257
|
-
void this.ensureChainEnabled(chainId);
|
|
258
|
-
const wallet = this.paraWebClient.getWalletsByType('COSMOS')[0];
|
|
259
|
-
|
|
260
|
-
if (!wallet) {
|
|
261
|
-
throw new Error('No Cosmos wallet for Amino signing');
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
return new ParaAminoSigner(this.paraWebClient, this.getBech32Prefix(chainId), wallet.id);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
getOfflineSigner(chainId: string): OfflineAminoSigner & OfflineDirectSigner {
|
|
268
|
-
void this.ensureChainEnabled(chainId);
|
|
269
|
-
return this.buildHybridSigner(chainId);
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
async getOfflineSignerAuto(chainId: string): Promise<OfflineAminoSigner | OfflineDirectSigner> {
|
|
273
|
-
void this.ensureChainEnabled(chainId);
|
|
274
|
-
return this.buildHybridSigner(chainId);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
async signAmino(
|
|
278
|
-
chainId: string,
|
|
279
|
-
signer: string,
|
|
280
|
-
signDoc: StdSignDoc,
|
|
281
|
-
_signOptions?: KeplrSignOptions,
|
|
282
|
-
): Promise<AminoSignResponse> {
|
|
283
|
-
await this.ensureChainEnabled(chainId);
|
|
284
|
-
|
|
285
|
-
try {
|
|
286
|
-
const wallet = await this.getFirstWallet();
|
|
287
|
-
const signerImpl = new ParaAminoSigner(this.paraWebClient, this.getBech32Prefix(chainId), wallet.id);
|
|
288
|
-
const response = await signerImpl.signAmino(signer, signDoc);
|
|
289
|
-
return response;
|
|
290
|
-
} catch (err) {
|
|
291
|
-
throw new Error(`Amino signing failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
async signDirect(
|
|
296
|
-
chainId: string,
|
|
297
|
-
signer: string,
|
|
298
|
-
signDoc: GrazSignDoc,
|
|
299
|
-
_signOptions?: KeplrSignOptions,
|
|
300
|
-
): Promise<DirectSignResponse> {
|
|
301
|
-
await this.ensureChainEnabled(chainId);
|
|
302
|
-
|
|
303
|
-
try {
|
|
304
|
-
const wallet = await this.getFirstWallet();
|
|
305
|
-
const signerImpl = new ParaProtoSigner(this.paraWebClient, this.getBech32Prefix(chainId), wallet.id);
|
|
306
|
-
const convertedSignDoc: SignDoc = {
|
|
307
|
-
bodyBytes: signDoc.bodyBytes ?? new Uint8Array(),
|
|
308
|
-
authInfoBytes: signDoc.authInfoBytes ?? new Uint8Array(),
|
|
309
|
-
chainId: signDoc.chainId,
|
|
310
|
-
accountNumber: typeof signDoc.accountNumber === 'bigint' ? signDoc.accountNumber : BigInt(signDoc.accountNumber),
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
const result = await signerImpl.signDirect(signer, convertedSignDoc);
|
|
314
|
-
return {
|
|
315
|
-
signed: {
|
|
316
|
-
bodyBytes: result.signed.bodyBytes,
|
|
317
|
-
authInfoBytes: result.signed.authInfoBytes,
|
|
318
|
-
chainId: result.signed.chainId,
|
|
319
|
-
accountNumber: result.signed.accountNumber,
|
|
320
|
-
},
|
|
321
|
-
signature: result.signature,
|
|
322
|
-
};
|
|
323
|
-
} catch (err) {
|
|
324
|
-
throw new Error(`Direct signing failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
async signArbitrary(chainId: string, signer: string, data: string | Uint8Array): Promise<StdSignature> {
|
|
329
|
-
await this.ensureChainEnabled(chainId);
|
|
330
|
-
|
|
331
|
-
const encodedData =
|
|
332
|
-
typeof data === 'string' ? Buffer.from(data, 'utf-8').toString('base64') : Buffer.from(data).toString('base64');
|
|
333
|
-
|
|
334
|
-
const signDoc = {
|
|
335
|
-
chain_id: '',
|
|
336
|
-
account_number: '0',
|
|
337
|
-
sequence: '0',
|
|
338
|
-
fee: { gas: '0', amount: [] },
|
|
339
|
-
msgs: [
|
|
340
|
-
{
|
|
341
|
-
type: 'sign/MsgSignData',
|
|
342
|
-
value: { signer, data: encodedData },
|
|
343
|
-
},
|
|
344
|
-
],
|
|
345
|
-
memo: '',
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
try {
|
|
349
|
-
const response = await this.signAmino(chainId, signer, signDoc);
|
|
350
|
-
return response.signature;
|
|
351
|
-
} catch (err) {
|
|
352
|
-
throw new Error(`Arbitrary signing failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
}
|
package/src/index.ts
DELETED
package/tsconfig.json
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "./dist",
|
|
5
|
-
"lib": ["dom", "dom.iterable", "esnext"],
|
|
6
|
-
"jsx": "react-jsx",
|
|
7
|
-
"module": "ESNext",
|
|
8
|
-
"declaration": true,
|
|
9
|
-
"declarationDir": "./dist"
|
|
10
|
-
},
|
|
11
|
-
"include": ["src/**/*.ts", "src/**/*.tsx"],
|
|
12
|
-
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx", "__tests__"]
|
|
13
|
-
}
|
package/vitest.config.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { mergeConfig } from 'vite';
|
|
2
|
-
import baseConfig from '../../vitest.config.js';
|
|
3
|
-
|
|
4
|
-
export default mergeConfig(baseConfig, {
|
|
5
|
-
test: {
|
|
6
|
-
environment: 'jsdom',
|
|
7
|
-
coverage: {
|
|
8
|
-
provider: 'v8',
|
|
9
|
-
reporter: ['html', 'text'],
|
|
10
|
-
all: true,
|
|
11
|
-
include: ['src/**/*.{ts,tsx,js,jsx}'],
|
|
12
|
-
thresholds: {
|
|
13
|
-
lines: 98,
|
|
14
|
-
functions: 90,
|
|
15
|
-
branches: 98,
|
|
16
|
-
statements: 98,
|
|
17
|
-
},
|
|
18
|
-
reportOnFailure: true,
|
|
19
|
-
exclude: ['src/index.ts'],
|
|
20
|
-
},
|
|
21
|
-
},
|
|
22
|
-
});
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|