@dynamic-labs-wallet/sui 0.0.0-preview-134.0 → 0.0.1-paolo-1
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/index.cjs.js +244 -56
- package/index.esm.js +240 -52
- package/package.json +7 -4
- package/src/client/client.d.ts +72 -11
- package/src/client/client.d.ts.map +1 -1
- package/src/client/constants.d.ts +2 -0
- package/src/client/constants.d.ts.map +1 -1
- package/src/client/index.d.ts +1 -1
- package/src/client/index.d.ts.map +1 -1
- package/src/index.d.ts +1 -1
- package/src/index.d.ts.map +1 -1
- package/src/utils.d.ts +1 -1
- package/src/utils.d.ts.map +1 -1
package/index.cjs.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var browser = require('@dynamic-labs-wallet/browser');
|
|
4
|
-
var ed25519 = require('@mysten/sui/keypairs/ed25519');
|
|
5
4
|
var cryptography = require('@mysten/sui/cryptography');
|
|
5
|
+
var ed25519 = require('@mysten/sui/keypairs/ed25519');
|
|
6
6
|
var verify = require('@mysten/sui/verify');
|
|
7
|
+
var converter = require('bech32-converting');
|
|
7
8
|
var bcs = require('@mysten/sui/bcs');
|
|
8
9
|
var blake2b = require('@noble/hashes/blake2b');
|
|
9
10
|
|
|
@@ -18,11 +19,6 @@ function _extends() {
|
|
|
18
19
|
return _extends.apply(this, arguments);
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
const ERROR_KEYGEN_FAILED = 'Error with keygen';
|
|
22
|
-
const ERROR_CREATE_WALLET_ACCOUNT = 'Error creating sui wallet account';
|
|
23
|
-
const ERROR_VERIFY_MESSAGE_SIGNATURE = 'Error verifying message signature';
|
|
24
|
-
const ERROR_VERIFY_TRANSACTION_SIGNATURE = 'Error verifying transaction signature';
|
|
25
|
-
|
|
26
22
|
const formatMessage = (message, intentScope)=>{
|
|
27
23
|
if (intentScope === 'TransactionData') {
|
|
28
24
|
const txBytes = Uint8Array.from(Buffer.from(message, 'hex'));
|
|
@@ -41,8 +37,12 @@ const formatMessage = (message, intentScope)=>{
|
|
|
41
37
|
};
|
|
42
38
|
|
|
43
39
|
class DynamicSuiWalletClient extends browser.DynamicWalletClient {
|
|
44
|
-
async createWalletAccount({ thresholdSignatureScheme, password = undefined, onError }) {
|
|
40
|
+
async createWalletAccount({ thresholdSignatureScheme, password = undefined, onError, signedSessionId }) {
|
|
45
41
|
try {
|
|
42
|
+
let ceremonyCeremonyCompleteResolver;
|
|
43
|
+
const ceremonyCompletePromise = new Promise((resolve)=>{
|
|
44
|
+
ceremonyCeremonyCompleteResolver = resolve;
|
|
45
|
+
});
|
|
46
46
|
// Generate key shares for given threshold signature scheme (TSS)
|
|
47
47
|
const { rawPublicKey, clientKeyShares } = await this.keyGen({
|
|
48
48
|
chainName: this.chainName,
|
|
@@ -57,37 +57,45 @@ class DynamicSuiWalletClient extends browser.DynamicWalletClient {
|
|
|
57
57
|
thresholdSignatureScheme,
|
|
58
58
|
clientKeySharesBackupInfo: browser.getClientKeyShareBackupInfo()
|
|
59
59
|
});
|
|
60
|
+
this.logger.debug('walletMap updated for wallet', {
|
|
61
|
+
context: {
|
|
62
|
+
accountAddress,
|
|
63
|
+
walletId,
|
|
64
|
+
walletMap: this.walletMap
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
ceremonyCeremonyCompleteResolver(undefined);
|
|
60
68
|
}
|
|
61
69
|
});
|
|
70
|
+
// Wait for the ceremony to complete before proceeding
|
|
71
|
+
await ceremonyCompletePromise;
|
|
62
72
|
if (!rawPublicKey || !clientKeyShares) {
|
|
63
|
-
throw new Error(ERROR_KEYGEN_FAILED);
|
|
73
|
+
throw new Error(browser.ERROR_KEYGEN_FAILED);
|
|
64
74
|
}
|
|
65
75
|
const { accountAddress, publicKeyHex } = this.deriveAccountAddress({
|
|
66
76
|
rawPublicKey: rawPublicKey
|
|
67
77
|
});
|
|
68
78
|
// Update client key shares in wallet map
|
|
69
79
|
// warning: this might result in race condition if `onCeremonyComplete` executes at the same time
|
|
70
|
-
// TODO: remove this once iframe handling for secret shares is implemented
|
|
71
80
|
await this.setClientKeySharesToLocalStorage({
|
|
72
81
|
accountAddress,
|
|
73
82
|
clientKeyShares,
|
|
74
83
|
overwriteOrMerge: 'overwrite'
|
|
75
84
|
});
|
|
76
|
-
|
|
77
|
-
void this.storeEncryptedBackupByWalletWithRetry({
|
|
85
|
+
await this.storeEncryptedBackupByWalletWithRetry({
|
|
78
86
|
accountAddress,
|
|
79
87
|
clientKeyShares,
|
|
80
|
-
password
|
|
88
|
+
password,
|
|
89
|
+
signedSessionId
|
|
81
90
|
});
|
|
82
91
|
return {
|
|
83
92
|
accountAddress,
|
|
84
|
-
rawPublicKey,
|
|
85
|
-
publicKeyHex
|
|
86
|
-
clientKeyShares
|
|
93
|
+
rawPublicKey: rawPublicKey,
|
|
94
|
+
publicKeyHex
|
|
87
95
|
};
|
|
88
96
|
} catch (error) {
|
|
89
|
-
this.logger.error(ERROR_CREATE_WALLET_ACCOUNT, error);
|
|
90
|
-
throw new Error(ERROR_CREATE_WALLET_ACCOUNT);
|
|
97
|
+
this.logger.error(browser.ERROR_CREATE_WALLET_ACCOUNT, error);
|
|
98
|
+
throw new Error(browser.ERROR_CREATE_WALLET_ACCOUNT);
|
|
91
99
|
}
|
|
92
100
|
}
|
|
93
101
|
async getRawPublicKeyFromClientKeyShares({ chainName, clientKeyShare }) {
|
|
@@ -103,22 +111,28 @@ class DynamicSuiWalletClient extends browser.DynamicWalletClient {
|
|
|
103
111
|
/**
|
|
104
112
|
* Format Ed25519 signature to string that satisfies Sui signature standard
|
|
105
113
|
*/ async formatSignature(signatureEd25519, accountAddress) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
114
|
+
try {
|
|
115
|
+
// get public key from keyshare
|
|
116
|
+
// TODO: handle this more gracefully from the client key shares if possible
|
|
117
|
+
const clientKeyShares = await this.getClientKeySharesFromLocalStorage({
|
|
118
|
+
accountAddress
|
|
119
|
+
});
|
|
120
|
+
const rawPublicKey = await this.getRawPublicKeyFromClientKeyShares({
|
|
121
|
+
chainName: this.chainName,
|
|
122
|
+
clientKeyShare: clientKeyShares[0]
|
|
123
|
+
});
|
|
124
|
+
const rawPublicKeyBytes = Uint8Array.from(Buffer.from(rawPublicKey, 'hex'));
|
|
125
|
+
const suiPublicKey = new ed25519.Ed25519PublicKey(rawPublicKeyBytes);
|
|
126
|
+
const serializedSignature = cryptography.toSerializedSignature({
|
|
127
|
+
signature: signatureEd25519,
|
|
128
|
+
signatureScheme: 'ED25519',
|
|
129
|
+
publicKey: suiPublicKey
|
|
130
|
+
});
|
|
131
|
+
return serializedSignature;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
this.logger.error('Error formatting signature:', error);
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
122
136
|
}
|
|
123
137
|
async verifyMessageSignature({ message, signature, accountAddress }) {
|
|
124
138
|
try {
|
|
@@ -126,29 +140,29 @@ class DynamicSuiWalletClient extends browser.DynamicWalletClient {
|
|
|
126
140
|
const verifiedPublicKey = await verify.verifyPersonalMessageSignature(messageBytes, signature);
|
|
127
141
|
const isVerified = verifiedPublicKey.toSuiAddress().toLowerCase() === accountAddress.toLowerCase();
|
|
128
142
|
if (!isVerified) {
|
|
129
|
-
throw new Error(ERROR_VERIFY_MESSAGE_SIGNATURE);
|
|
143
|
+
throw new Error(browser.ERROR_VERIFY_MESSAGE_SIGNATURE);
|
|
130
144
|
}
|
|
131
145
|
} catch (error) {
|
|
132
146
|
this.logger.error('Error verifying signature:', error);
|
|
133
147
|
throw error;
|
|
134
148
|
}
|
|
135
149
|
}
|
|
136
|
-
async verifyTransactionSignature({
|
|
150
|
+
async verifyTransactionSignature({ transaction, signature, senderAddress }) {
|
|
137
151
|
try {
|
|
138
|
-
const txBytes = Uint8Array.from(Buffer.from(
|
|
152
|
+
const txBytes = Uint8Array.from(Buffer.from(transaction, 'hex'));
|
|
139
153
|
const verifiedPublicKey = await verify.verifyTransactionSignature(txBytes, signature);
|
|
140
|
-
const isVerified = verifiedPublicKey.toSuiAddress().toLowerCase() ===
|
|
154
|
+
const isVerified = verifiedPublicKey.toSuiAddress().toLowerCase() === senderAddress.toLowerCase();
|
|
141
155
|
if (!isVerified) {
|
|
142
|
-
throw new Error(ERROR_VERIFY_TRANSACTION_SIGNATURE);
|
|
156
|
+
throw new Error(browser.ERROR_VERIFY_TRANSACTION_SIGNATURE);
|
|
143
157
|
}
|
|
144
158
|
} catch (error) {
|
|
145
159
|
this.logger.error('Error verifying signature:', error);
|
|
146
160
|
throw error;
|
|
147
161
|
}
|
|
148
162
|
}
|
|
149
|
-
async signMessage({ message, accountAddress, password = undefined }) {
|
|
163
|
+
async signMessage({ message, accountAddress, password = undefined, signedSessionId, mfaToken, onError }) {
|
|
150
164
|
if (!accountAddress) {
|
|
151
|
-
throw new Error(
|
|
165
|
+
throw new Error(browser.ERROR_ACCOUNT_ADDRESS_REQUIRED);
|
|
152
166
|
}
|
|
153
167
|
try {
|
|
154
168
|
const formattedMessage = formatMessage(message, 'PersonalMessage');
|
|
@@ -156,7 +170,13 @@ class DynamicSuiWalletClient extends browser.DynamicWalletClient {
|
|
|
156
170
|
message: formattedMessage,
|
|
157
171
|
accountAddress: accountAddress,
|
|
158
172
|
chainName: this.chainName,
|
|
159
|
-
password
|
|
173
|
+
password,
|
|
174
|
+
signedSessionId,
|
|
175
|
+
mfaToken,
|
|
176
|
+
context: {
|
|
177
|
+
suiMessage: message
|
|
178
|
+
},
|
|
179
|
+
onError
|
|
160
180
|
});
|
|
161
181
|
const formattedSignature = await this.formatSignature(signatureEd25519, accountAddress);
|
|
162
182
|
await this.verifyMessageSignature({
|
|
@@ -166,27 +186,36 @@ class DynamicSuiWalletClient extends browser.DynamicWalletClient {
|
|
|
166
186
|
});
|
|
167
187
|
return formattedSignature;
|
|
168
188
|
} catch (error) {
|
|
169
|
-
this.logger.error(
|
|
170
|
-
throw
|
|
189
|
+
this.logger.error(browser.ERROR_SIGN_MESSAGE, error);
|
|
190
|
+
throw new Error(browser.ERROR_SIGN_MESSAGE);
|
|
171
191
|
}
|
|
172
192
|
}
|
|
173
|
-
async signTransaction({
|
|
174
|
-
if (!
|
|
175
|
-
throw new Error(
|
|
193
|
+
async signTransaction({ transaction, senderAddress, password = undefined, signedSessionId, mfaToken, context, onError }) {
|
|
194
|
+
if (!senderAddress) {
|
|
195
|
+
throw new Error(browser.ERROR_ACCOUNT_ADDRESS_REQUIRED);
|
|
176
196
|
}
|
|
177
197
|
try {
|
|
178
|
-
const formattedMessage = formatMessage(
|
|
198
|
+
const formattedMessage = formatMessage(transaction, 'TransactionData');
|
|
179
199
|
const signatureEd25519 = await this.sign({
|
|
180
200
|
message: formattedMessage,
|
|
181
|
-
accountAddress:
|
|
201
|
+
accountAddress: senderAddress,
|
|
182
202
|
chainName: this.chainName,
|
|
183
|
-
password
|
|
203
|
+
password,
|
|
204
|
+
signedSessionId,
|
|
205
|
+
mfaToken,
|
|
206
|
+
context: _extends({}, context, {
|
|
207
|
+
suiTransaction: {
|
|
208
|
+
chainId: '0x2',
|
|
209
|
+
serializedTransaction: transaction
|
|
210
|
+
}
|
|
211
|
+
}),
|
|
212
|
+
onError
|
|
184
213
|
});
|
|
185
|
-
const formattedSignature = await this.formatSignature(signatureEd25519,
|
|
214
|
+
const formattedSignature = await this.formatSignature(signatureEd25519, senderAddress);
|
|
186
215
|
await this.verifyTransactionSignature({
|
|
187
|
-
|
|
216
|
+
transaction,
|
|
188
217
|
signature: formattedSignature,
|
|
189
|
-
|
|
218
|
+
senderAddress
|
|
190
219
|
});
|
|
191
220
|
return formattedSignature;
|
|
192
221
|
} catch (error) {
|
|
@@ -195,26 +224,185 @@ class DynamicSuiWalletClient extends browser.DynamicWalletClient {
|
|
|
195
224
|
}
|
|
196
225
|
}
|
|
197
226
|
deriveAccountAddress({ rawPublicKey }) {
|
|
198
|
-
const
|
|
227
|
+
const pubKeyBytes = Buffer.from(rawPublicKey, 'hex');
|
|
228
|
+
const publicKey = new ed25519.Ed25519PublicKey(pubKeyBytes);
|
|
199
229
|
const accountAddress = publicKey.toSuiAddress();
|
|
200
230
|
return {
|
|
201
231
|
accountAddress,
|
|
202
|
-
publicKeyHex:
|
|
232
|
+
publicKeyHex: rawPublicKey
|
|
203
233
|
};
|
|
204
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* Converts a Sui private key from Bech32 format to a 64-character hex string.
|
|
237
|
+
* The output is compatible with RFC8032 Ed25519 private key format.
|
|
238
|
+
*
|
|
239
|
+
* @param suiPrivateKey - The Sui private key in Bech32 format starting with "suiprivkey1"
|
|
240
|
+
* @returns An object containing the private key and the private key bytes
|
|
241
|
+
* @throws Error if the input is not a valid Sui private key format
|
|
242
|
+
*/ convertSuiPrivateKey(suiPrivateKey) {
|
|
243
|
+
if (!suiPrivateKey.startsWith('suiprivkey1')) {
|
|
244
|
+
this.logger.debug('Sui private key not in Bech32 format');
|
|
245
|
+
return {
|
|
246
|
+
privateKey: suiPrivateKey,
|
|
247
|
+
privateKeyBytes: Buffer.from(suiPrivateKey, 'hex')
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
try {
|
|
251
|
+
const suiConverter = converter('suiprivkey');
|
|
252
|
+
const hexKey = suiConverter.toHex(suiPrivateKey);
|
|
253
|
+
let cleanHex = hexKey.startsWith('0x00') ? hexKey.slice(4) : hexKey.startsWith('0x') ? hexKey.slice(2) : hexKey;
|
|
254
|
+
if (cleanHex.length > 64) {
|
|
255
|
+
cleanHex = cleanHex.slice(cleanHex.length - 64);
|
|
256
|
+
}
|
|
257
|
+
if (cleanHex.length !== 64) {
|
|
258
|
+
throw new Error(`Invalid output: Expected 64 characters, got ${cleanHex.length}`);
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
privateKey: cleanHex.toLowerCase(),
|
|
262
|
+
privateKeyBytes: Buffer.from(cleanHex, 'hex')
|
|
263
|
+
};
|
|
264
|
+
} catch (error) {
|
|
265
|
+
if (error instanceof Error) {
|
|
266
|
+
throw new Error(`Failed to convert Sui private key: ${error.message}`);
|
|
267
|
+
}
|
|
268
|
+
throw new Error('Failed to convert Sui private key: Unknown error');
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Gets the public key for a given private key
|
|
273
|
+
* @param privateKeyBytes A Buffer containing the Ed25519 private key bytes
|
|
274
|
+
* @returns The public key (Sui address) derived from the private key
|
|
275
|
+
*/ getPublicKeyFromPrivateKey(privateKeyBytes) {
|
|
276
|
+
try {
|
|
277
|
+
const keypair = ed25519.Ed25519Keypair.fromSecretKey(privateKeyBytes);
|
|
278
|
+
const publicKey = keypair.getPublicKey();
|
|
279
|
+
const publicKeyBase58 = publicKey.toSuiAddress();
|
|
280
|
+
return publicKeyBase58;
|
|
281
|
+
} catch (error) {
|
|
282
|
+
this.logger.error('Unable to derive public key from private key. Check private key format', error instanceof Error ? error.message : 'Unknown error');
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Imports the private key for a given account address
|
|
288
|
+
*
|
|
289
|
+
* @param privateKey The private key to import, accepts both Bech32 and hex formats
|
|
290
|
+
* @param chainName The chain name to import the private key for
|
|
291
|
+
* @param thresholdSignatureScheme The threshold signature scheme to use
|
|
292
|
+
* @param password The password for encrypted backup shares
|
|
293
|
+
* @returns The account address, raw public key, and client key shares
|
|
294
|
+
*/ async importPrivateKey({ privateKey, chainName, thresholdSignatureScheme, password = undefined, signedSessionId, onError, publicAddressCheck }) {
|
|
295
|
+
try {
|
|
296
|
+
let ceremonyCeremonyCompleteResolver;
|
|
297
|
+
const ceremonyCompletePromise = new Promise((resolve)=>{
|
|
298
|
+
ceremonyCeremonyCompleteResolver = resolve;
|
|
299
|
+
});
|
|
300
|
+
const { privateKey: formattedPrivateKey, privateKeyBytes } = await this.convertSuiPrivateKey(privateKey);
|
|
301
|
+
const publicKey = this.getPublicKeyFromPrivateKey(privateKeyBytes);
|
|
302
|
+
if (publicAddressCheck && publicKey !== publicAddressCheck) {
|
|
303
|
+
throw new Error(`Public address mismatch: derived address ${publicKey} !== public address ${publicAddressCheck}`);
|
|
304
|
+
}
|
|
305
|
+
const { rawPublicKey, clientKeyShares } = await this.importRawPrivateKey({
|
|
306
|
+
chainName,
|
|
307
|
+
privateKey: formattedPrivateKey,
|
|
308
|
+
thresholdSignatureScheme,
|
|
309
|
+
onCeremonyComplete: (accountAddress, walletId)=>{
|
|
310
|
+
// update wallet map
|
|
311
|
+
this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress] || {}, {
|
|
312
|
+
accountAddress,
|
|
313
|
+
walletId,
|
|
314
|
+
chainName: this.chainName,
|
|
315
|
+
thresholdSignatureScheme,
|
|
316
|
+
clientKeySharesBackupInfo: browser.getClientKeyShareBackupInfo()
|
|
317
|
+
});
|
|
318
|
+
this.logger.debug('walletMap updated for wallet', {
|
|
319
|
+
context: {
|
|
320
|
+
accountAddress,
|
|
321
|
+
walletId,
|
|
322
|
+
walletMap: this.walletMap
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
ceremonyCeremonyCompleteResolver(undefined);
|
|
326
|
+
},
|
|
327
|
+
onError
|
|
328
|
+
});
|
|
329
|
+
// Wait for the ceremony to complete before proceeding
|
|
330
|
+
await ceremonyCompletePromise;
|
|
331
|
+
if (!rawPublicKey || !clientKeyShares) {
|
|
332
|
+
throw new Error(browser.ERROR_IMPORT_PRIVATE_KEY);
|
|
333
|
+
}
|
|
334
|
+
const { accountAddress } = await this.deriveAccountAddress({
|
|
335
|
+
rawPublicKey: rawPublicKey
|
|
336
|
+
});
|
|
337
|
+
if (accountAddress !== publicKey) {
|
|
338
|
+
throw new Error(`Public key mismatch: derived address ${accountAddress} !== public key ${publicKey}`);
|
|
339
|
+
}
|
|
340
|
+
// Update client key shares in wallet map
|
|
341
|
+
// warning: this might result in race condition if `onCeremonyComplete` executes at the same time
|
|
342
|
+
await this.setClientKeySharesToLocalStorage({
|
|
343
|
+
accountAddress,
|
|
344
|
+
clientKeyShares,
|
|
345
|
+
overwriteOrMerge: 'overwrite'
|
|
346
|
+
});
|
|
347
|
+
await this.storeEncryptedBackupByWalletWithRetry({
|
|
348
|
+
accountAddress,
|
|
349
|
+
clientKeyShares,
|
|
350
|
+
password,
|
|
351
|
+
signedSessionId
|
|
352
|
+
});
|
|
353
|
+
return {
|
|
354
|
+
accountAddress,
|
|
355
|
+
rawPublicKey: rawPublicKey,
|
|
356
|
+
clientKeyShares
|
|
357
|
+
};
|
|
358
|
+
} catch (error) {
|
|
359
|
+
this.logger.error(browser.ERROR_IMPORT_PRIVATE_KEY, error);
|
|
360
|
+
onError == null ? void 0 : onError(error);
|
|
361
|
+
throw new Error(browser.ERROR_IMPORT_PRIVATE_KEY);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Exports the private key for a given account address
|
|
366
|
+
*
|
|
367
|
+
* @param accountAddress The account address to export the private key for
|
|
368
|
+
* @param password The password for encrypted backup shares
|
|
369
|
+
* @returns The private key in hex format
|
|
370
|
+
*/ async exportPrivateKey({ accountAddress, password = undefined, signedSessionId, mfaToken }) {
|
|
371
|
+
try {
|
|
372
|
+
const { derivedPrivateKey } = await this.exportKey({
|
|
373
|
+
accountAddress,
|
|
374
|
+
chainName: this.chainName,
|
|
375
|
+
password,
|
|
376
|
+
signedSessionId,
|
|
377
|
+
mfaToken
|
|
378
|
+
});
|
|
379
|
+
if (!derivedPrivateKey) {
|
|
380
|
+
throw new Error('Derived private key is undefined');
|
|
381
|
+
}
|
|
382
|
+
const privateScalarHex = derivedPrivateKey.slice(0, 64);
|
|
383
|
+
return privateScalarHex;
|
|
384
|
+
} catch (error) {
|
|
385
|
+
this.logger.error(browser.ERROR_EXPORT_PRIVATE_KEY, error);
|
|
386
|
+
throw new Error(browser.ERROR_EXPORT_PRIVATE_KEY);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
205
389
|
async getSuiWallets() {
|
|
206
390
|
const wallets = await this.getWallets();
|
|
207
391
|
const suiWallets = wallets.filter((wallet)=>wallet.chainName === 'sui');
|
|
208
392
|
return suiWallets;
|
|
209
393
|
}
|
|
210
|
-
constructor({ environmentId, authToken, baseApiUrl, baseMPCRelayApiUrl, storageKey, debug }){
|
|
394
|
+
constructor({ environmentId, authToken, baseApiUrl, baseMPCRelayApiUrl, storageKey, debug, featureFlags, authMode = browser.AuthMode.HEADER, sdkVersion, forwardMPCClient }){
|
|
211
395
|
super({
|
|
212
396
|
environmentId,
|
|
213
397
|
authToken,
|
|
214
398
|
baseApiUrl,
|
|
215
399
|
baseMPCRelayApiUrl,
|
|
216
400
|
storageKey,
|
|
217
|
-
debug
|
|
401
|
+
debug,
|
|
402
|
+
featureFlags,
|
|
403
|
+
authMode,
|
|
404
|
+
sdkVersion,
|
|
405
|
+
forwardMPCClient
|
|
218
406
|
}), this.chainName = 'SUI';
|
|
219
407
|
}
|
|
220
408
|
}
|
package/index.esm.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { DynamicWalletClient, getClientKeyShareBackupInfo, getMPCChainConfig } from '@dynamic-labs-wallet/browser';
|
|
2
|
-
import { Ed25519PublicKey } from '@mysten/sui/keypairs/ed25519';
|
|
1
|
+
import { DynamicWalletClient, getClientKeyShareBackupInfo, ERROR_KEYGEN_FAILED, ERROR_CREATE_WALLET_ACCOUNT, getMPCChainConfig, ERROR_VERIFY_MESSAGE_SIGNATURE, ERROR_VERIFY_TRANSACTION_SIGNATURE, ERROR_ACCOUNT_ADDRESS_REQUIRED, ERROR_SIGN_MESSAGE, ERROR_IMPORT_PRIVATE_KEY, ERROR_EXPORT_PRIVATE_KEY, AuthMode } from '@dynamic-labs-wallet/browser';
|
|
3
2
|
import { messageWithIntent, toSerializedSignature } from '@mysten/sui/cryptography';
|
|
3
|
+
import { Ed25519PublicKey, Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
|
|
4
4
|
import { verifyPersonalMessageSignature, verifyTransactionSignature } from '@mysten/sui/verify';
|
|
5
|
+
import converter from 'bech32-converting';
|
|
5
6
|
import { bcs } from '@mysten/sui/bcs';
|
|
6
7
|
import { blake2b } from '@noble/hashes/blake2b';
|
|
7
8
|
|
|
@@ -16,11 +17,6 @@ function _extends() {
|
|
|
16
17
|
return _extends.apply(this, arguments);
|
|
17
18
|
}
|
|
18
19
|
|
|
19
|
-
const ERROR_KEYGEN_FAILED = 'Error with keygen';
|
|
20
|
-
const ERROR_CREATE_WALLET_ACCOUNT = 'Error creating sui wallet account';
|
|
21
|
-
const ERROR_VERIFY_MESSAGE_SIGNATURE = 'Error verifying message signature';
|
|
22
|
-
const ERROR_VERIFY_TRANSACTION_SIGNATURE = 'Error verifying transaction signature';
|
|
23
|
-
|
|
24
20
|
const formatMessage = (message, intentScope)=>{
|
|
25
21
|
if (intentScope === 'TransactionData') {
|
|
26
22
|
const txBytes = Uint8Array.from(Buffer.from(message, 'hex'));
|
|
@@ -39,8 +35,12 @@ const formatMessage = (message, intentScope)=>{
|
|
|
39
35
|
};
|
|
40
36
|
|
|
41
37
|
class DynamicSuiWalletClient extends DynamicWalletClient {
|
|
42
|
-
async createWalletAccount({ thresholdSignatureScheme, password = undefined, onError }) {
|
|
38
|
+
async createWalletAccount({ thresholdSignatureScheme, password = undefined, onError, signedSessionId }) {
|
|
43
39
|
try {
|
|
40
|
+
let ceremonyCeremonyCompleteResolver;
|
|
41
|
+
const ceremonyCompletePromise = new Promise((resolve)=>{
|
|
42
|
+
ceremonyCeremonyCompleteResolver = resolve;
|
|
43
|
+
});
|
|
44
44
|
// Generate key shares for given threshold signature scheme (TSS)
|
|
45
45
|
const { rawPublicKey, clientKeyShares } = await this.keyGen({
|
|
46
46
|
chainName: this.chainName,
|
|
@@ -55,8 +55,18 @@ class DynamicSuiWalletClient extends DynamicWalletClient {
|
|
|
55
55
|
thresholdSignatureScheme,
|
|
56
56
|
clientKeySharesBackupInfo: getClientKeyShareBackupInfo()
|
|
57
57
|
});
|
|
58
|
+
this.logger.debug('walletMap updated for wallet', {
|
|
59
|
+
context: {
|
|
60
|
+
accountAddress,
|
|
61
|
+
walletId,
|
|
62
|
+
walletMap: this.walletMap
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
ceremonyCeremonyCompleteResolver(undefined);
|
|
58
66
|
}
|
|
59
67
|
});
|
|
68
|
+
// Wait for the ceremony to complete before proceeding
|
|
69
|
+
await ceremonyCompletePromise;
|
|
60
70
|
if (!rawPublicKey || !clientKeyShares) {
|
|
61
71
|
throw new Error(ERROR_KEYGEN_FAILED);
|
|
62
72
|
}
|
|
@@ -65,23 +75,21 @@ class DynamicSuiWalletClient extends DynamicWalletClient {
|
|
|
65
75
|
});
|
|
66
76
|
// Update client key shares in wallet map
|
|
67
77
|
// warning: this might result in race condition if `onCeremonyComplete` executes at the same time
|
|
68
|
-
// TODO: remove this once iframe handling for secret shares is implemented
|
|
69
78
|
await this.setClientKeySharesToLocalStorage({
|
|
70
79
|
accountAddress,
|
|
71
80
|
clientKeyShares,
|
|
72
81
|
overwriteOrMerge: 'overwrite'
|
|
73
82
|
});
|
|
74
|
-
|
|
75
|
-
void this.storeEncryptedBackupByWalletWithRetry({
|
|
83
|
+
await this.storeEncryptedBackupByWalletWithRetry({
|
|
76
84
|
accountAddress,
|
|
77
85
|
clientKeyShares,
|
|
78
|
-
password
|
|
86
|
+
password,
|
|
87
|
+
signedSessionId
|
|
79
88
|
});
|
|
80
89
|
return {
|
|
81
90
|
accountAddress,
|
|
82
|
-
rawPublicKey,
|
|
83
|
-
publicKeyHex
|
|
84
|
-
clientKeyShares
|
|
91
|
+
rawPublicKey: rawPublicKey,
|
|
92
|
+
publicKeyHex
|
|
85
93
|
};
|
|
86
94
|
} catch (error) {
|
|
87
95
|
this.logger.error(ERROR_CREATE_WALLET_ACCOUNT, error);
|
|
@@ -101,22 +109,28 @@ class DynamicSuiWalletClient extends DynamicWalletClient {
|
|
|
101
109
|
/**
|
|
102
110
|
* Format Ed25519 signature to string that satisfies Sui signature standard
|
|
103
111
|
*/ async formatSignature(signatureEd25519, accountAddress) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
112
|
+
try {
|
|
113
|
+
// get public key from keyshare
|
|
114
|
+
// TODO: handle this more gracefully from the client key shares if possible
|
|
115
|
+
const clientKeyShares = await this.getClientKeySharesFromLocalStorage({
|
|
116
|
+
accountAddress
|
|
117
|
+
});
|
|
118
|
+
const rawPublicKey = await this.getRawPublicKeyFromClientKeyShares({
|
|
119
|
+
chainName: this.chainName,
|
|
120
|
+
clientKeyShare: clientKeyShares[0]
|
|
121
|
+
});
|
|
122
|
+
const rawPublicKeyBytes = Uint8Array.from(Buffer.from(rawPublicKey, 'hex'));
|
|
123
|
+
const suiPublicKey = new Ed25519PublicKey(rawPublicKeyBytes);
|
|
124
|
+
const serializedSignature = toSerializedSignature({
|
|
125
|
+
signature: signatureEd25519,
|
|
126
|
+
signatureScheme: 'ED25519',
|
|
127
|
+
publicKey: suiPublicKey
|
|
128
|
+
});
|
|
129
|
+
return serializedSignature;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
this.logger.error('Error formatting signature:', error);
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
120
134
|
}
|
|
121
135
|
async verifyMessageSignature({ message, signature, accountAddress }) {
|
|
122
136
|
try {
|
|
@@ -131,11 +145,11 @@ class DynamicSuiWalletClient extends DynamicWalletClient {
|
|
|
131
145
|
throw error;
|
|
132
146
|
}
|
|
133
147
|
}
|
|
134
|
-
async verifyTransactionSignature({
|
|
148
|
+
async verifyTransactionSignature({ transaction, signature, senderAddress }) {
|
|
135
149
|
try {
|
|
136
|
-
const txBytes = Uint8Array.from(Buffer.from(
|
|
150
|
+
const txBytes = Uint8Array.from(Buffer.from(transaction, 'hex'));
|
|
137
151
|
const verifiedPublicKey = await verifyTransactionSignature(txBytes, signature);
|
|
138
|
-
const isVerified = verifiedPublicKey.toSuiAddress().toLowerCase() ===
|
|
152
|
+
const isVerified = verifiedPublicKey.toSuiAddress().toLowerCase() === senderAddress.toLowerCase();
|
|
139
153
|
if (!isVerified) {
|
|
140
154
|
throw new Error(ERROR_VERIFY_TRANSACTION_SIGNATURE);
|
|
141
155
|
}
|
|
@@ -144,9 +158,9 @@ class DynamicSuiWalletClient extends DynamicWalletClient {
|
|
|
144
158
|
throw error;
|
|
145
159
|
}
|
|
146
160
|
}
|
|
147
|
-
async signMessage({ message, accountAddress, password = undefined }) {
|
|
161
|
+
async signMessage({ message, accountAddress, password = undefined, signedSessionId, mfaToken, onError }) {
|
|
148
162
|
if (!accountAddress) {
|
|
149
|
-
throw new Error(
|
|
163
|
+
throw new Error(ERROR_ACCOUNT_ADDRESS_REQUIRED);
|
|
150
164
|
}
|
|
151
165
|
try {
|
|
152
166
|
const formattedMessage = formatMessage(message, 'PersonalMessage');
|
|
@@ -154,7 +168,13 @@ class DynamicSuiWalletClient extends DynamicWalletClient {
|
|
|
154
168
|
message: formattedMessage,
|
|
155
169
|
accountAddress: accountAddress,
|
|
156
170
|
chainName: this.chainName,
|
|
157
|
-
password
|
|
171
|
+
password,
|
|
172
|
+
signedSessionId,
|
|
173
|
+
mfaToken,
|
|
174
|
+
context: {
|
|
175
|
+
suiMessage: message
|
|
176
|
+
},
|
|
177
|
+
onError
|
|
158
178
|
});
|
|
159
179
|
const formattedSignature = await this.formatSignature(signatureEd25519, accountAddress);
|
|
160
180
|
await this.verifyMessageSignature({
|
|
@@ -164,27 +184,36 @@ class DynamicSuiWalletClient extends DynamicWalletClient {
|
|
|
164
184
|
});
|
|
165
185
|
return formattedSignature;
|
|
166
186
|
} catch (error) {
|
|
167
|
-
this.logger.error(
|
|
168
|
-
throw
|
|
187
|
+
this.logger.error(ERROR_SIGN_MESSAGE, error);
|
|
188
|
+
throw new Error(ERROR_SIGN_MESSAGE);
|
|
169
189
|
}
|
|
170
190
|
}
|
|
171
|
-
async signTransaction({
|
|
172
|
-
if (!
|
|
173
|
-
throw new Error(
|
|
191
|
+
async signTransaction({ transaction, senderAddress, password = undefined, signedSessionId, mfaToken, context, onError }) {
|
|
192
|
+
if (!senderAddress) {
|
|
193
|
+
throw new Error(ERROR_ACCOUNT_ADDRESS_REQUIRED);
|
|
174
194
|
}
|
|
175
195
|
try {
|
|
176
|
-
const formattedMessage = formatMessage(
|
|
196
|
+
const formattedMessage = formatMessage(transaction, 'TransactionData');
|
|
177
197
|
const signatureEd25519 = await this.sign({
|
|
178
198
|
message: formattedMessage,
|
|
179
|
-
accountAddress:
|
|
199
|
+
accountAddress: senderAddress,
|
|
180
200
|
chainName: this.chainName,
|
|
181
|
-
password
|
|
201
|
+
password,
|
|
202
|
+
signedSessionId,
|
|
203
|
+
mfaToken,
|
|
204
|
+
context: _extends({}, context, {
|
|
205
|
+
suiTransaction: {
|
|
206
|
+
chainId: '0x2',
|
|
207
|
+
serializedTransaction: transaction
|
|
208
|
+
}
|
|
209
|
+
}),
|
|
210
|
+
onError
|
|
182
211
|
});
|
|
183
|
-
const formattedSignature = await this.formatSignature(signatureEd25519,
|
|
212
|
+
const formattedSignature = await this.formatSignature(signatureEd25519, senderAddress);
|
|
184
213
|
await this.verifyTransactionSignature({
|
|
185
|
-
|
|
214
|
+
transaction,
|
|
186
215
|
signature: formattedSignature,
|
|
187
|
-
|
|
216
|
+
senderAddress
|
|
188
217
|
});
|
|
189
218
|
return formattedSignature;
|
|
190
219
|
} catch (error) {
|
|
@@ -193,26 +222,185 @@ class DynamicSuiWalletClient extends DynamicWalletClient {
|
|
|
193
222
|
}
|
|
194
223
|
}
|
|
195
224
|
deriveAccountAddress({ rawPublicKey }) {
|
|
196
|
-
const
|
|
225
|
+
const pubKeyBytes = Buffer.from(rawPublicKey, 'hex');
|
|
226
|
+
const publicKey = new Ed25519PublicKey(pubKeyBytes);
|
|
197
227
|
const accountAddress = publicKey.toSuiAddress();
|
|
198
228
|
return {
|
|
199
229
|
accountAddress,
|
|
200
|
-
publicKeyHex:
|
|
230
|
+
publicKeyHex: rawPublicKey
|
|
201
231
|
};
|
|
202
232
|
}
|
|
233
|
+
/**
|
|
234
|
+
* Converts a Sui private key from Bech32 format to a 64-character hex string.
|
|
235
|
+
* The output is compatible with RFC8032 Ed25519 private key format.
|
|
236
|
+
*
|
|
237
|
+
* @param suiPrivateKey - The Sui private key in Bech32 format starting with "suiprivkey1"
|
|
238
|
+
* @returns An object containing the private key and the private key bytes
|
|
239
|
+
* @throws Error if the input is not a valid Sui private key format
|
|
240
|
+
*/ convertSuiPrivateKey(suiPrivateKey) {
|
|
241
|
+
if (!suiPrivateKey.startsWith('suiprivkey1')) {
|
|
242
|
+
this.logger.debug('Sui private key not in Bech32 format');
|
|
243
|
+
return {
|
|
244
|
+
privateKey: suiPrivateKey,
|
|
245
|
+
privateKeyBytes: Buffer.from(suiPrivateKey, 'hex')
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
try {
|
|
249
|
+
const suiConverter = converter('suiprivkey');
|
|
250
|
+
const hexKey = suiConverter.toHex(suiPrivateKey);
|
|
251
|
+
let cleanHex = hexKey.startsWith('0x00') ? hexKey.slice(4) : hexKey.startsWith('0x') ? hexKey.slice(2) : hexKey;
|
|
252
|
+
if (cleanHex.length > 64) {
|
|
253
|
+
cleanHex = cleanHex.slice(cleanHex.length - 64);
|
|
254
|
+
}
|
|
255
|
+
if (cleanHex.length !== 64) {
|
|
256
|
+
throw new Error(`Invalid output: Expected 64 characters, got ${cleanHex.length}`);
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
privateKey: cleanHex.toLowerCase(),
|
|
260
|
+
privateKeyBytes: Buffer.from(cleanHex, 'hex')
|
|
261
|
+
};
|
|
262
|
+
} catch (error) {
|
|
263
|
+
if (error instanceof Error) {
|
|
264
|
+
throw new Error(`Failed to convert Sui private key: ${error.message}`);
|
|
265
|
+
}
|
|
266
|
+
throw new Error('Failed to convert Sui private key: Unknown error');
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Gets the public key for a given private key
|
|
271
|
+
* @param privateKeyBytes A Buffer containing the Ed25519 private key bytes
|
|
272
|
+
* @returns The public key (Sui address) derived from the private key
|
|
273
|
+
*/ getPublicKeyFromPrivateKey(privateKeyBytes) {
|
|
274
|
+
try {
|
|
275
|
+
const keypair = Ed25519Keypair.fromSecretKey(privateKeyBytes);
|
|
276
|
+
const publicKey = keypair.getPublicKey();
|
|
277
|
+
const publicKeyBase58 = publicKey.toSuiAddress();
|
|
278
|
+
return publicKeyBase58;
|
|
279
|
+
} catch (error) {
|
|
280
|
+
this.logger.error('Unable to derive public key from private key. Check private key format', error instanceof Error ? error.message : 'Unknown error');
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Imports the private key for a given account address
|
|
286
|
+
*
|
|
287
|
+
* @param privateKey The private key to import, accepts both Bech32 and hex formats
|
|
288
|
+
* @param chainName The chain name to import the private key for
|
|
289
|
+
* @param thresholdSignatureScheme The threshold signature scheme to use
|
|
290
|
+
* @param password The password for encrypted backup shares
|
|
291
|
+
* @returns The account address, raw public key, and client key shares
|
|
292
|
+
*/ async importPrivateKey({ privateKey, chainName, thresholdSignatureScheme, password = undefined, signedSessionId, onError, publicAddressCheck }) {
|
|
293
|
+
try {
|
|
294
|
+
let ceremonyCeremonyCompleteResolver;
|
|
295
|
+
const ceremonyCompletePromise = new Promise((resolve)=>{
|
|
296
|
+
ceremonyCeremonyCompleteResolver = resolve;
|
|
297
|
+
});
|
|
298
|
+
const { privateKey: formattedPrivateKey, privateKeyBytes } = await this.convertSuiPrivateKey(privateKey);
|
|
299
|
+
const publicKey = this.getPublicKeyFromPrivateKey(privateKeyBytes);
|
|
300
|
+
if (publicAddressCheck && publicKey !== publicAddressCheck) {
|
|
301
|
+
throw new Error(`Public address mismatch: derived address ${publicKey} !== public address ${publicAddressCheck}`);
|
|
302
|
+
}
|
|
303
|
+
const { rawPublicKey, clientKeyShares } = await this.importRawPrivateKey({
|
|
304
|
+
chainName,
|
|
305
|
+
privateKey: formattedPrivateKey,
|
|
306
|
+
thresholdSignatureScheme,
|
|
307
|
+
onCeremonyComplete: (accountAddress, walletId)=>{
|
|
308
|
+
// update wallet map
|
|
309
|
+
this.walletMap[accountAddress] = _extends({}, this.walletMap[accountAddress] || {}, {
|
|
310
|
+
accountAddress,
|
|
311
|
+
walletId,
|
|
312
|
+
chainName: this.chainName,
|
|
313
|
+
thresholdSignatureScheme,
|
|
314
|
+
clientKeySharesBackupInfo: getClientKeyShareBackupInfo()
|
|
315
|
+
});
|
|
316
|
+
this.logger.debug('walletMap updated for wallet', {
|
|
317
|
+
context: {
|
|
318
|
+
accountAddress,
|
|
319
|
+
walletId,
|
|
320
|
+
walletMap: this.walletMap
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
ceremonyCeremonyCompleteResolver(undefined);
|
|
324
|
+
},
|
|
325
|
+
onError
|
|
326
|
+
});
|
|
327
|
+
// Wait for the ceremony to complete before proceeding
|
|
328
|
+
await ceremonyCompletePromise;
|
|
329
|
+
if (!rawPublicKey || !clientKeyShares) {
|
|
330
|
+
throw new Error(ERROR_IMPORT_PRIVATE_KEY);
|
|
331
|
+
}
|
|
332
|
+
const { accountAddress } = await this.deriveAccountAddress({
|
|
333
|
+
rawPublicKey: rawPublicKey
|
|
334
|
+
});
|
|
335
|
+
if (accountAddress !== publicKey) {
|
|
336
|
+
throw new Error(`Public key mismatch: derived address ${accountAddress} !== public key ${publicKey}`);
|
|
337
|
+
}
|
|
338
|
+
// Update client key shares in wallet map
|
|
339
|
+
// warning: this might result in race condition if `onCeremonyComplete` executes at the same time
|
|
340
|
+
await this.setClientKeySharesToLocalStorage({
|
|
341
|
+
accountAddress,
|
|
342
|
+
clientKeyShares,
|
|
343
|
+
overwriteOrMerge: 'overwrite'
|
|
344
|
+
});
|
|
345
|
+
await this.storeEncryptedBackupByWalletWithRetry({
|
|
346
|
+
accountAddress,
|
|
347
|
+
clientKeyShares,
|
|
348
|
+
password,
|
|
349
|
+
signedSessionId
|
|
350
|
+
});
|
|
351
|
+
return {
|
|
352
|
+
accountAddress,
|
|
353
|
+
rawPublicKey: rawPublicKey,
|
|
354
|
+
clientKeyShares
|
|
355
|
+
};
|
|
356
|
+
} catch (error) {
|
|
357
|
+
this.logger.error(ERROR_IMPORT_PRIVATE_KEY, error);
|
|
358
|
+
onError == null ? void 0 : onError(error);
|
|
359
|
+
throw new Error(ERROR_IMPORT_PRIVATE_KEY);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Exports the private key for a given account address
|
|
364
|
+
*
|
|
365
|
+
* @param accountAddress The account address to export the private key for
|
|
366
|
+
* @param password The password for encrypted backup shares
|
|
367
|
+
* @returns The private key in hex format
|
|
368
|
+
*/ async exportPrivateKey({ accountAddress, password = undefined, signedSessionId, mfaToken }) {
|
|
369
|
+
try {
|
|
370
|
+
const { derivedPrivateKey } = await this.exportKey({
|
|
371
|
+
accountAddress,
|
|
372
|
+
chainName: this.chainName,
|
|
373
|
+
password,
|
|
374
|
+
signedSessionId,
|
|
375
|
+
mfaToken
|
|
376
|
+
});
|
|
377
|
+
if (!derivedPrivateKey) {
|
|
378
|
+
throw new Error('Derived private key is undefined');
|
|
379
|
+
}
|
|
380
|
+
const privateScalarHex = derivedPrivateKey.slice(0, 64);
|
|
381
|
+
return privateScalarHex;
|
|
382
|
+
} catch (error) {
|
|
383
|
+
this.logger.error(ERROR_EXPORT_PRIVATE_KEY, error);
|
|
384
|
+
throw new Error(ERROR_EXPORT_PRIVATE_KEY);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
203
387
|
async getSuiWallets() {
|
|
204
388
|
const wallets = await this.getWallets();
|
|
205
389
|
const suiWallets = wallets.filter((wallet)=>wallet.chainName === 'sui');
|
|
206
390
|
return suiWallets;
|
|
207
391
|
}
|
|
208
|
-
constructor({ environmentId, authToken, baseApiUrl, baseMPCRelayApiUrl, storageKey, debug }){
|
|
392
|
+
constructor({ environmentId, authToken, baseApiUrl, baseMPCRelayApiUrl, storageKey, debug, featureFlags, authMode = AuthMode.HEADER, sdkVersion, forwardMPCClient }){
|
|
209
393
|
super({
|
|
210
394
|
environmentId,
|
|
211
395
|
authToken,
|
|
212
396
|
baseApiUrl,
|
|
213
397
|
baseMPCRelayApiUrl,
|
|
214
398
|
storageKey,
|
|
215
|
-
debug
|
|
399
|
+
debug,
|
|
400
|
+
featureFlags,
|
|
401
|
+
authMode,
|
|
402
|
+
sdkVersion,
|
|
403
|
+
forwardMPCClient
|
|
216
404
|
}), this.chainName = 'SUI';
|
|
217
405
|
}
|
|
218
406
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamic-labs-wallet/sui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.1-paolo-1",
|
|
4
4
|
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"dependencies": {
|
|
6
|
-
"@dynamic-labs-wallet/browser": "0.0.
|
|
7
|
+
"@dynamic-labs-wallet/browser": "0.0.1-paolo-1",
|
|
7
8
|
"@mysten/sui": "1.26.0",
|
|
8
|
-
"@noble/hashes": "1.7.1"
|
|
9
|
+
"@noble/hashes": "1.7.1",
|
|
10
|
+
"bech32-converting": "^1.0.9",
|
|
11
|
+
"@dynamic-labs/sdk-api-core": "^0.0.801"
|
|
9
12
|
},
|
|
10
13
|
"publishConfig": {
|
|
11
14
|
"access": "public"
|
|
@@ -27,7 +30,7 @@
|
|
|
27
30
|
"types": "./index.esm.d.ts",
|
|
28
31
|
"import": "./index.esm.js",
|
|
29
32
|
"require": "./index.cjs.js",
|
|
30
|
-
"default": "./index.
|
|
33
|
+
"default": "./index.esm.js"
|
|
31
34
|
}
|
|
32
35
|
}
|
|
33
36
|
}
|
package/src/client/client.d.ts
CHANGED
|
@@ -1,43 +1,104 @@
|
|
|
1
|
-
import { ClientKeyShare, DynamicWalletClient,
|
|
1
|
+
import { type ClientKeyShare, DynamicWalletClient, type DynamicWalletClientProps, type ThresholdSignatureScheme } from '@dynamic-labs-wallet/browser';
|
|
2
|
+
import type { SignMessageContext } from '@dynamic-labs/sdk-api-core';
|
|
2
3
|
export declare class DynamicSuiWalletClient extends DynamicWalletClient {
|
|
3
4
|
readonly chainName = "SUI";
|
|
4
|
-
constructor({ environmentId, authToken, baseApiUrl, baseMPCRelayApiUrl, storageKey, debug, }: DynamicWalletClientProps);
|
|
5
|
-
createWalletAccount({ thresholdSignatureScheme, password, onError, }: {
|
|
5
|
+
constructor({ environmentId, authToken, baseApiUrl, baseMPCRelayApiUrl, storageKey, debug, featureFlags, authMode, sdkVersion, forwardMPCClient, }: DynamicWalletClientProps);
|
|
6
|
+
createWalletAccount({ thresholdSignatureScheme, password, onError, signedSessionId, }: {
|
|
6
7
|
thresholdSignatureScheme: ThresholdSignatureScheme;
|
|
7
8
|
password?: string;
|
|
8
9
|
onError?: (error: Error) => void;
|
|
10
|
+
signedSessionId: string;
|
|
9
11
|
}): Promise<{
|
|
10
12
|
accountAddress: string;
|
|
11
13
|
publicKeyHex: string;
|
|
12
|
-
rawPublicKey:
|
|
13
|
-
clientKeyShares: ClientKeyShare[];
|
|
14
|
+
rawPublicKey: string | undefined;
|
|
14
15
|
}>;
|
|
15
16
|
getRawPublicKeyFromClientKeyShares({ chainName, clientKeyShare, }: {
|
|
16
17
|
chainName: string;
|
|
17
18
|
clientKeyShare: ClientKeyShare;
|
|
18
|
-
}): Promise<
|
|
19
|
+
}): Promise<string | import("#internal/core").EcdsaPublicKey | Uint8Array | undefined>;
|
|
19
20
|
/**
|
|
20
21
|
* Format Ed25519 signature to string that satisfies Sui signature standard
|
|
21
22
|
*/
|
|
22
23
|
private formatSignature;
|
|
23
24
|
private verifyMessageSignature;
|
|
24
25
|
private verifyTransactionSignature;
|
|
25
|
-
signMessage({ message, accountAddress, password, }: {
|
|
26
|
+
signMessage({ message, accountAddress, password, signedSessionId, mfaToken, onError, }: {
|
|
26
27
|
message: string;
|
|
27
28
|
accountAddress: string;
|
|
28
29
|
password?: string;
|
|
30
|
+
signedSessionId: string;
|
|
31
|
+
mfaToken?: string;
|
|
32
|
+
onError?: (error: Error) => void;
|
|
29
33
|
}): Promise<string>;
|
|
30
|
-
signTransaction({
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
signTransaction({ transaction, senderAddress, password, signedSessionId, mfaToken, context, onError, }: {
|
|
35
|
+
transaction: string;
|
|
36
|
+
senderAddress: string;
|
|
33
37
|
password?: string;
|
|
38
|
+
signedSessionId: string;
|
|
39
|
+
mfaToken?: string;
|
|
40
|
+
context?: SignMessageContext;
|
|
41
|
+
onError?: (error: Error) => void;
|
|
34
42
|
}): Promise<string>;
|
|
35
43
|
deriveAccountAddress({ rawPublicKey }: {
|
|
36
|
-
rawPublicKey:
|
|
44
|
+
rawPublicKey: string;
|
|
37
45
|
}): {
|
|
38
46
|
accountAddress: string;
|
|
39
47
|
publicKeyHex: string;
|
|
40
48
|
};
|
|
49
|
+
/**
|
|
50
|
+
* Converts a Sui private key from Bech32 format to a 64-character hex string.
|
|
51
|
+
* The output is compatible with RFC8032 Ed25519 private key format.
|
|
52
|
+
*
|
|
53
|
+
* @param suiPrivateKey - The Sui private key in Bech32 format starting with "suiprivkey1"
|
|
54
|
+
* @returns An object containing the private key and the private key bytes
|
|
55
|
+
* @throws Error if the input is not a valid Sui private key format
|
|
56
|
+
*/
|
|
57
|
+
convertSuiPrivateKey(suiPrivateKey: string): {
|
|
58
|
+
privateKey: string;
|
|
59
|
+
privateKeyBytes: Buffer;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Gets the public key for a given private key
|
|
63
|
+
* @param privateKeyBytes A Buffer containing the Ed25519 private key bytes
|
|
64
|
+
* @returns The public key (Sui address) derived from the private key
|
|
65
|
+
*/
|
|
66
|
+
getPublicKeyFromPrivateKey(privateKeyBytes: Buffer): string;
|
|
67
|
+
/**
|
|
68
|
+
* Imports the private key for a given account address
|
|
69
|
+
*
|
|
70
|
+
* @param privateKey The private key to import, accepts both Bech32 and hex formats
|
|
71
|
+
* @param chainName The chain name to import the private key for
|
|
72
|
+
* @param thresholdSignatureScheme The threshold signature scheme to use
|
|
73
|
+
* @param password The password for encrypted backup shares
|
|
74
|
+
* @returns The account address, raw public key, and client key shares
|
|
75
|
+
*/
|
|
76
|
+
importPrivateKey({ privateKey, chainName, thresholdSignatureScheme, password, signedSessionId, onError, publicAddressCheck, }: {
|
|
77
|
+
privateKey: string;
|
|
78
|
+
chainName: string;
|
|
79
|
+
thresholdSignatureScheme: ThresholdSignatureScheme;
|
|
80
|
+
password?: string;
|
|
81
|
+
signedSessionId: string;
|
|
82
|
+
onError?: (error: Error) => void;
|
|
83
|
+
publicAddressCheck?: string;
|
|
84
|
+
}): Promise<{
|
|
85
|
+
accountAddress: string;
|
|
86
|
+
rawPublicKey: string | undefined;
|
|
87
|
+
clientKeyShares: ClientKeyShare[];
|
|
88
|
+
}>;
|
|
89
|
+
/**
|
|
90
|
+
* Exports the private key for a given account address
|
|
91
|
+
*
|
|
92
|
+
* @param accountAddress The account address to export the private key for
|
|
93
|
+
* @param password The password for encrypted backup shares
|
|
94
|
+
* @returns The private key in hex format
|
|
95
|
+
*/
|
|
96
|
+
exportPrivateKey({ accountAddress, password, signedSessionId, mfaToken, }: {
|
|
97
|
+
accountAddress: string;
|
|
98
|
+
password?: string;
|
|
99
|
+
signedSessionId: string;
|
|
100
|
+
mfaToken?: string;
|
|
101
|
+
}): Promise<string>;
|
|
41
102
|
getSuiWallets(): Promise<any>;
|
|
42
103
|
}
|
|
43
104
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export declare const ERROR_KEYGEN_FAILED = "Error with keygen";
|
|
2
2
|
export declare const ERROR_CREATE_WALLET_ACCOUNT = "Error creating sui wallet account";
|
|
3
|
+
export declare const ERROR_IMPORT_PRIVATE_KEY = "Error importing private key";
|
|
4
|
+
export declare const ERROR_EXPORT_PRIVATE_KEY = "Error exporting private key";
|
|
3
5
|
export declare const ERROR_SIGN_MESSAGE = "Error signing message";
|
|
4
6
|
export declare const ERROR_ACCOUNT_ADDRESS_REQUIRED = "Account address is required";
|
|
5
7
|
export declare const ERROR_VERIFY_MESSAGE_SIGNATURE = "Error verifying message signature";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/client/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,sBAAsB,CAAC;AAEvD,eAAO,MAAM,2BAA2B,sCAAsC,CAAC;AAE/E,eAAO,MAAM,kBAAkB,0BAA0B,CAAC;AAE1D,eAAO,MAAM,8BAA8B,gCAAgC,CAAC;AAE5E,eAAO,MAAM,8BAA8B,sCACN,CAAC;AAEtC,eAAO,MAAM,kCAAkC,0CACN,CAAC"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/client/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,mBAAmB,sBAAsB,CAAC;AAEvD,eAAO,MAAM,2BAA2B,sCAAsC,CAAC;AAE/E,eAAO,MAAM,wBAAwB,gCAAgC,CAAC;AAEtE,eAAO,MAAM,wBAAwB,gCAAgC,CAAC;AAEtE,eAAO,MAAM,kBAAkB,0BAA0B,CAAC;AAE1D,eAAO,MAAM,8BAA8B,gCAAgC,CAAC;AAE5E,eAAO,MAAM,8BAA8B,sCACN,CAAC;AAEtC,eAAO,MAAM,kCAAkC,0CACN,CAAC"}
|
package/src/client/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './client';
|
|
1
|
+
export * from './client.js';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC"}
|
package/src/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './client';
|
|
1
|
+
export * from './client/index.js';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../packages/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../packages/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
|
package/src/utils.d.ts
CHANGED
package/src/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../packages/src/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAqB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../packages/src/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,0BAA0B,CAAC;AAG/E,eAAO,MAAM,aAAa,YACf,MAAM,eACF,WAAW,KACvB,UAeF,CAAC"}
|