@getpara/react-native-wallet 0.1.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/android/.gradle/8.9/checksums/checksums.lock +0 -0
- package/android/.gradle/8.9/checksums/md5-checksums.bin +0 -0
- package/android/.gradle/8.9/checksums/sha1-checksums.bin +0 -0
- package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/8.9/executionHistory/executionHistory.lock +0 -0
- package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.9/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +2 -0
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/build.gradle +76 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/com/para/reactnativewallet/ParaSignerModule.java +294 -0
- package/android/src/main/java/com/para/reactnativewallet/ParaSignerPackage.java +30 -0
- package/capsule-react-native-wallet.podspec +36 -0
- package/dist/AsyncStorage.d.ts +10 -0
- package/dist/AsyncStorage.js +41 -0
- package/dist/KeychainStorage.d.ts +10 -0
- package/dist/KeychainStorage.js +72 -0
- package/dist/config.d.ts +7 -0
- package/dist/config.js +61 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/react-native/ParaMobile.d.ts +49 -0
- package/dist/react-native/ParaMobile.js +237 -0
- package/dist/react-native/ReactNativeUtils.d.ts +50 -0
- package/dist/react-native/ReactNativeUtils.js +171 -0
- package/dist/shim.d.ts +1 -0
- package/dist/shim.js +49 -0
- package/ios/ParaSignerModule.h +12 -0
- package/ios/ParaSignerModule.m +411 -0
- package/ios/ReactNativeWallet.xcodeproj/project.pbxproj +269 -0
- package/package.json +63 -0
- package/signer.xcframework/Info.plist +44 -0
- package/signer.xcframework/ios-arm64/Signer.framework/Headers/Signer.h +13 -0
- package/signer.xcframework/ios-arm64/Signer.framework/Headers/Signer.objc.h +300 -0
- package/signer.xcframework/ios-arm64/Signer.framework/Headers/Universe.objc.h +29 -0
- package/signer.xcframework/ios-arm64/Signer.framework/Headers/ref.h +35 -0
- package/signer.xcframework/ios-arm64/Signer.framework/Info.plist +18 -0
- package/signer.xcframework/ios-arm64/Signer.framework/Modules/module.modulemap +8 -0
- package/signer.xcframework/ios-arm64/Signer.framework/Signer +0 -0
- package/signer.xcframework/ios-arm64_x86_64-simulator/Signer.framework/Headers/Signer.h +13 -0
- package/signer.xcframework/ios-arm64_x86_64-simulator/Signer.framework/Headers/Signer.objc.h +300 -0
- package/signer.xcframework/ios-arm64_x86_64-simulator/Signer.framework/Headers/Universe.objc.h +29 -0
- package/signer.xcframework/ios-arm64_x86_64-simulator/Signer.framework/Headers/ref.h +35 -0
- package/signer.xcframework/ios-arm64_x86_64-simulator/Signer.framework/Info.plist +18 -0
- package/signer.xcframework/ios-arm64_x86_64-simulator/Signer.framework/Modules/module.modulemap +8 -0
- package/signer.xcframework/ios-arm64_x86_64-simulator/Signer.framework/Signer +0 -0
- package/src/AsyncStorage.ts +30 -0
- package/src/KeychainStorage.ts +61 -0
- package/src/config.ts +70 -0
- package/src/index.tsx +2 -0
- package/src/react-native/ParaMobile.ts +294 -0
- package/src/react-native/ReactNativeUtils.ts +266 -0
- package/src/shim.js +59 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
// Copyright (c) Para Labs Inc. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ConstructorOpts,
|
|
5
|
+
ParaCore,
|
|
6
|
+
Environment,
|
|
7
|
+
PlatformUtils,
|
|
8
|
+
Wallet,
|
|
9
|
+
decryptPrivateKeyAndDecryptShare,
|
|
10
|
+
encryptPrivateKey,
|
|
11
|
+
getAsymmetricKeyPair,
|
|
12
|
+
getDerivedPrivateKeyAndDecrypt,
|
|
13
|
+
getPublicKeyHex,
|
|
14
|
+
getSHA256HashHex,
|
|
15
|
+
parseCredentialCreationRes,
|
|
16
|
+
} from '@getpara/web-sdk';
|
|
17
|
+
import { ReactNativeUtils } from './ReactNativeUtils.js';
|
|
18
|
+
import {
|
|
19
|
+
Passkey,
|
|
20
|
+
PasskeyCreateRequest,
|
|
21
|
+
PasskeyCreateResult,
|
|
22
|
+
PasskeyGetRequest,
|
|
23
|
+
PasskeyGetResult,
|
|
24
|
+
} from 'react-native-passkey';
|
|
25
|
+
import { extractAuth, PublicKeyStatus, WalletScheme } from '@getpara/user-management-client';
|
|
26
|
+
import { setEnv } from '../config.js';
|
|
27
|
+
import base64url from 'base64url';
|
|
28
|
+
import { webcrypto } from 'crypto';
|
|
29
|
+
import { CountryCallingCode } from 'libphonenumber-js';
|
|
30
|
+
|
|
31
|
+
const ES256_ALGORITHM = -7;
|
|
32
|
+
const RS256_ALGORITHM = -257;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Represents a mobile implementation of the Para SDK.
|
|
36
|
+
* @extends ParaCore
|
|
37
|
+
*/
|
|
38
|
+
export class ParaMobile extends ParaCore {
|
|
39
|
+
private relyingPartyId: string;
|
|
40
|
+
/**
|
|
41
|
+
* Creates an instance of ParaMobile.
|
|
42
|
+
* @param {Environment} env - The environment to use (DEV, SANDBOX, BETA, or PROD).
|
|
43
|
+
* @param {string} [apiKey] - The API key for authentication.
|
|
44
|
+
* @param {string} [relyingPartyId] - The relying party ID for WebAuthn.
|
|
45
|
+
* @param {ConstructorOpts} [opts] - Additional constructor options.
|
|
46
|
+
*/
|
|
47
|
+
constructor(env: Environment, apiKey?: string, relyingPartyId?: string, opts?: ConstructorOpts) {
|
|
48
|
+
super(env, apiKey, opts);
|
|
49
|
+
|
|
50
|
+
setEnv(env);
|
|
51
|
+
|
|
52
|
+
if (relyingPartyId) {
|
|
53
|
+
this.relyingPartyId = relyingPartyId;
|
|
54
|
+
} else {
|
|
55
|
+
switch (env) {
|
|
56
|
+
case Environment.DEV:
|
|
57
|
+
throw new Error('relyingPartyId is required');
|
|
58
|
+
case Environment.SANDBOX:
|
|
59
|
+
this.relyingPartyId = 'app.sandbox.usecapsule.com';
|
|
60
|
+
break;
|
|
61
|
+
case Environment.BETA:
|
|
62
|
+
this.relyingPartyId = 'app.beta.usecapsule.com';
|
|
63
|
+
break;
|
|
64
|
+
case Environment.PROD:
|
|
65
|
+
this.relyingPartyId = 'app.usecapsule.com';
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
protected getPlatformUtils(): PlatformUtils {
|
|
72
|
+
return new ReactNativeUtils();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Verifies an email and returns the biometrics ID.
|
|
77
|
+
* @param {string} verificationCode - The verification code sent to the email.
|
|
78
|
+
* @returns {Promise<string>} The biometrics ID.
|
|
79
|
+
*/
|
|
80
|
+
async verifyEmailBiometricsId(verificationCode: string): Promise<string> {
|
|
81
|
+
const webAuthCreateUrl = await super.verifyEmail(verificationCode);
|
|
82
|
+
const segments = webAuthCreateUrl.split('/');
|
|
83
|
+
const segments2 = segments[segments.length - 1]!.split('?');
|
|
84
|
+
const biometricsId = segments2[0]!;
|
|
85
|
+
|
|
86
|
+
return biometricsId;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Verifies a phone number and returns the biometrics ID.
|
|
91
|
+
* @param {string} verificationCode - The verification code sent to the phone.
|
|
92
|
+
* @returns {Promise<string>} The biometrics ID.
|
|
93
|
+
*/
|
|
94
|
+
async verifyPhoneBiometricsId(verificationCode: string): Promise<string> {
|
|
95
|
+
const webAuthCreateUrl = await super.verifyPhone(verificationCode);
|
|
96
|
+
const segments = webAuthCreateUrl.split('/');
|
|
97
|
+
const segments2 = segments[segments.length - 1]!.split('?');
|
|
98
|
+
const biometricsId = segments2[0]!;
|
|
99
|
+
|
|
100
|
+
return biometricsId;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Registers a passkey for the user.
|
|
105
|
+
* @param {string} identifier - The user's email or phone number.
|
|
106
|
+
* @param {string} biometricsId - The biometrics ID obtained from verification.
|
|
107
|
+
* @param {webcrypto.Crypto} crypto - The Web Crypto API instance.
|
|
108
|
+
* @param {'email' | 'phone'} [identifierType='email'] - The type of identifier used.
|
|
109
|
+
* @param {CountryCallingCode} [countryCode] - The country calling code for phone numbers.
|
|
110
|
+
* @returns {Promise<void>}
|
|
111
|
+
*/
|
|
112
|
+
async registerPasskey(
|
|
113
|
+
identifier: string,
|
|
114
|
+
biometricsId: string,
|
|
115
|
+
crypto: webcrypto.Crypto,
|
|
116
|
+
identifierType: 'email' | 'phone' = 'email',
|
|
117
|
+
countryCode?: CountryCallingCode,
|
|
118
|
+
) {
|
|
119
|
+
const userHandle = new Uint8Array(32);
|
|
120
|
+
crypto.getRandomValues(userHandle);
|
|
121
|
+
const userHandleEncoded = base64url.encode(userHandle as any);
|
|
122
|
+
|
|
123
|
+
const displayIdentifier = identifierType === 'email' ? identifier : `${countryCode}${identifier}`;
|
|
124
|
+
|
|
125
|
+
const requestJson: PasskeyCreateRequest = {
|
|
126
|
+
authenticatorSelection: {
|
|
127
|
+
authenticatorAttachment: 'platform' as any,
|
|
128
|
+
requireResidentKey: true,
|
|
129
|
+
residentKey: 'required' as any,
|
|
130
|
+
userVerification: 'required' as any,
|
|
131
|
+
},
|
|
132
|
+
rp: {
|
|
133
|
+
id: this.relyingPartyId,
|
|
134
|
+
name: 'Para',
|
|
135
|
+
},
|
|
136
|
+
user: {
|
|
137
|
+
id: userHandleEncoded,
|
|
138
|
+
name: displayIdentifier,
|
|
139
|
+
displayName: displayIdentifier,
|
|
140
|
+
},
|
|
141
|
+
pubKeyCredParams: [
|
|
142
|
+
{
|
|
143
|
+
type: 'public-key',
|
|
144
|
+
alg: ES256_ALGORITHM,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: 'public-key',
|
|
148
|
+
alg: RS256_ALGORITHM,
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
attestation: 'direct' as any,
|
|
152
|
+
timeout: 60000,
|
|
153
|
+
challenge: base64url.encode('somechallenge'),
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const result: PasskeyCreateResult = await Passkey.create(requestJson);
|
|
157
|
+
let resultJson;
|
|
158
|
+
|
|
159
|
+
if (typeof result === 'string') {
|
|
160
|
+
resultJson = JSON.parse(result);
|
|
161
|
+
} else {
|
|
162
|
+
resultJson = result;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const { cosePublicKey, clientDataJSON } = parseCredentialCreationRes(resultJson, ES256_ALGORITHM);
|
|
166
|
+
|
|
167
|
+
const keyPair = await getAsymmetricKeyPair(this.ctx);
|
|
168
|
+
const publicKeyHex = getPublicKeyHex(keyPair);
|
|
169
|
+
|
|
170
|
+
const encryptionKeyHash = getSHA256HashHex(userHandleEncoded);
|
|
171
|
+
const encryptedPrivateKeyHex = await encryptPrivateKey(keyPair, userHandleEncoded);
|
|
172
|
+
|
|
173
|
+
const session = await this.ctx.client.touchSession();
|
|
174
|
+
await this.ctx.client.patchSessionPublicKey(session.data.partnerId, this.getUserId()!, biometricsId, {
|
|
175
|
+
publicKey: resultJson.id,
|
|
176
|
+
sigDerivedPublicKey: publicKeyHex,
|
|
177
|
+
cosePublicKey,
|
|
178
|
+
clientDataJSON,
|
|
179
|
+
status: PublicKeyStatus.COMPLETE,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
await this.ctx.client.uploadEncryptedWalletPrivateKey(
|
|
183
|
+
this.getUserId()!,
|
|
184
|
+
encryptedPrivateKeyHex,
|
|
185
|
+
encryptionKeyHash,
|
|
186
|
+
resultJson.id,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Logs in the user using either email or phone number.
|
|
192
|
+
* @param {string} [email] - The user's email address.
|
|
193
|
+
* @param {string} [phone] - The user's phone number.
|
|
194
|
+
* @param {CountryCallingCode} [countryCode] - The country calling code for phone numbers.
|
|
195
|
+
* @returns {Promise<Wallet[]>} An array of user wallets.
|
|
196
|
+
*/
|
|
197
|
+
async login(email?: string, phone?: string, countryCode?: CountryCallingCode): Promise<void> {
|
|
198
|
+
await this.logout();
|
|
199
|
+
const auth = extractAuth({ email, phone, countryCode }, { optional: true });
|
|
200
|
+
const { challenge, allowedPublicKeys } = await this.ctx.client.getWebChallenge(auth);
|
|
201
|
+
|
|
202
|
+
const requestJson: PasskeyGetRequest = {
|
|
203
|
+
challenge,
|
|
204
|
+
timeout: 60000,
|
|
205
|
+
rpId: this.relyingPartyId,
|
|
206
|
+
allowCredentials: allowedPublicKeys?.[0] ? [{ type: 'public-key', id: allowedPublicKeys[0] }] : [],
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const result: PasskeyGetResult = await Passkey.get(requestJson);
|
|
210
|
+
|
|
211
|
+
let resultJson;
|
|
212
|
+
|
|
213
|
+
if (typeof result === 'string') {
|
|
214
|
+
resultJson = JSON.parse(result);
|
|
215
|
+
} else {
|
|
216
|
+
resultJson = result;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const session = await this.ctx.client.touchSession();
|
|
220
|
+
const publicKey = resultJson.id;
|
|
221
|
+
const verifyWebChallengeResult = await this.ctx.client.verifyWebChallenge(session.data.partnerId, {
|
|
222
|
+
publicKey,
|
|
223
|
+
signature: {
|
|
224
|
+
clientDataJSON: resultJson.response.clientDataJSON,
|
|
225
|
+
authenticatorData: resultJson.response.authenticatorData,
|
|
226
|
+
signature: resultJson.response.signature,
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const userId = verifyWebChallengeResult.data.userId;
|
|
231
|
+
|
|
232
|
+
await this.setUserId(userId);
|
|
233
|
+
|
|
234
|
+
const { user } = await this.ctx.client.getUser(userId);
|
|
235
|
+
|
|
236
|
+
if (user.phone) {
|
|
237
|
+
await this.setPhoneNumber(user.phone.number, user.phone.countryCode);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (user.email) {
|
|
241
|
+
await this.setEmail(user.email);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (user.farcasterUsername) {
|
|
245
|
+
await this.setFarcasterUsername(user.farcasterUsername);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const encryptedSharesResult = await this.ctx.client.getBiometricKeyshares(userId, resultJson.id);
|
|
249
|
+
|
|
250
|
+
const encryptionKeyHash = getSHA256HashHex(resultJson.response.userHandle);
|
|
251
|
+
const { encryptedPrivateKeys } = await this.ctx.client.getEncryptedWalletPrivateKeys(userId, encryptionKeyHash);
|
|
252
|
+
|
|
253
|
+
let decryptedShares;
|
|
254
|
+
if (encryptedPrivateKeys.length === 0) {
|
|
255
|
+
decryptedShares = await getDerivedPrivateKeyAndDecrypt(
|
|
256
|
+
this.ctx,
|
|
257
|
+
resultJson.response.userHandle,
|
|
258
|
+
encryptedSharesResult.data.keyShares,
|
|
259
|
+
);
|
|
260
|
+
const keyPair = await getAsymmetricKeyPair(this.ctx, resultJson.response.userHandle);
|
|
261
|
+
const encryptedPrivateKeyHex = await encryptPrivateKey(keyPair, resultJson.response.userHandle);
|
|
262
|
+
await this.ctx.client.uploadEncryptedWalletPrivateKey(
|
|
263
|
+
userId,
|
|
264
|
+
encryptedPrivateKeyHex,
|
|
265
|
+
encryptionKeyHash,
|
|
266
|
+
resultJson.id,
|
|
267
|
+
);
|
|
268
|
+
} else {
|
|
269
|
+
decryptedShares = await decryptPrivateKeyAndDecryptShare(
|
|
270
|
+
resultJson.response.userHandle,
|
|
271
|
+
encryptedSharesResult.data.keyShares,
|
|
272
|
+
encryptedPrivateKeys[0].encryptedPrivateKey,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const walletsRes = await this.ctx.client.getWallets(userId);
|
|
277
|
+
const desiredWallets = walletsRes.data.wallets;
|
|
278
|
+
|
|
279
|
+
const walletsToInsert: { [id: string]: Wallet } = {};
|
|
280
|
+
for (let desiredWallet of desiredWallets) {
|
|
281
|
+
const decryptedShare = decryptedShares.find(share => share.walletId === desiredWallet.id)!;
|
|
282
|
+
walletsToInsert[decryptedShare.walletId] = {
|
|
283
|
+
id: decryptedShare.walletId,
|
|
284
|
+
signer: decryptedShare.signer,
|
|
285
|
+
address: desiredWallet.address || undefined,
|
|
286
|
+
publicKey: desiredWallet.publicKey || undefined,
|
|
287
|
+
scheme: desiredWallet.scheme as WalletScheme,
|
|
288
|
+
type: desiredWallet.type || undefined,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
await this.setWallets(walletsToInsert);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
// Copyright (c) Capsule Labs Inc. All rights reserved.
|
|
2
|
+
|
|
3
|
+
import { PlatformUtils, TPregenIdentifierType } from '@getpara/web-sdk';
|
|
4
|
+
import { Ctx } from '@getpara/web-sdk';
|
|
5
|
+
import { SignatureRes } from '@getpara/web-sdk';
|
|
6
|
+
import { BackupKitEmailProps, KeyShareType, WalletScheme, WalletType } from '@getpara/user-management-client';
|
|
7
|
+
import { NativeModules } from 'react-native';
|
|
8
|
+
|
|
9
|
+
import { AsyncStorage } from '../AsyncStorage.js';
|
|
10
|
+
import { KeychainStorage } from '../KeychainStorage.js';
|
|
11
|
+
|
|
12
|
+
const { ParaSignerModule } = NativeModules;
|
|
13
|
+
|
|
14
|
+
async function keygenRequest(ctx: Ctx, userId: string, walletId: string, protocolId: string): Promise<{ signer: string }> {
|
|
15
|
+
const { data } = await ctx.mpcComputationClient!.post('/wallets', {
|
|
16
|
+
userId,
|
|
17
|
+
walletId,
|
|
18
|
+
protocolId,
|
|
19
|
+
});
|
|
20
|
+
return data;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function signMessageRequest(
|
|
24
|
+
ctx: Ctx,
|
|
25
|
+
userId: string,
|
|
26
|
+
walletId: string,
|
|
27
|
+
protocolId: string,
|
|
28
|
+
message: string,
|
|
29
|
+
signer: string,
|
|
30
|
+
): Promise<{ signature: string }> {
|
|
31
|
+
const { data } = await ctx.mpcComputationClient!.post(`/wallets/${walletId}/messages/sign`, {
|
|
32
|
+
userId,
|
|
33
|
+
protocolId,
|
|
34
|
+
message,
|
|
35
|
+
signer,
|
|
36
|
+
});
|
|
37
|
+
return data;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function sendTransactionRequest(
|
|
41
|
+
ctx: Ctx,
|
|
42
|
+
userId: string,
|
|
43
|
+
walletId: string,
|
|
44
|
+
protocolId: string,
|
|
45
|
+
transaction: string,
|
|
46
|
+
signer: string,
|
|
47
|
+
chainId: string,
|
|
48
|
+
): Promise<{ signature: string }> {
|
|
49
|
+
const { data } = await ctx.mpcComputationClient!.post(`/wallets/${walletId}/transactions/send`, {
|
|
50
|
+
userId,
|
|
51
|
+
protocolId,
|
|
52
|
+
transaction,
|
|
53
|
+
signer,
|
|
54
|
+
chainId,
|
|
55
|
+
});
|
|
56
|
+
return data;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export class ReactNativeUtils implements PlatformUtils {
|
|
60
|
+
disableProviderModal?: boolean | undefined;
|
|
61
|
+
localStorage = new AsyncStorage();
|
|
62
|
+
sessionStorage = new AsyncStorage();
|
|
63
|
+
secureStorage = new KeychainStorage();
|
|
64
|
+
isSyncStorage = false;
|
|
65
|
+
|
|
66
|
+
// only used in web for now, can implement functionality if ever needed for mobile
|
|
67
|
+
async generateBlumPrimes(_ctx: Ctx): Promise<{ p: string; q: string }> {
|
|
68
|
+
throw new Error('method not implemented');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async keygen(
|
|
72
|
+
ctx: Ctx,
|
|
73
|
+
userId: string,
|
|
74
|
+
type: Exclude<WalletType, WalletType.SOLANA>,
|
|
75
|
+
_secretKey: string | null,
|
|
76
|
+
_sessionCookie: string,
|
|
77
|
+
_emailProps?: BackupKitEmailProps | undefined,
|
|
78
|
+
): Promise<{ signer: string; walletId: string }> {
|
|
79
|
+
const { walletId, protocolId } = await ctx.client.createWallet(userId, {
|
|
80
|
+
type,
|
|
81
|
+
useTwoSigners: true,
|
|
82
|
+
scheme: ctx.useDKLS ? WalletScheme.DKLS : WalletScheme.CGGMP,
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (ctx.mpcComputationClient && !ctx.useDKLS) {
|
|
86
|
+
const { signer } = await keygenRequest(ctx, userId, walletId, protocolId);
|
|
87
|
+
return { signer, walletId };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const createAccountFn = !ctx.useDKLS ? ParaSignerModule.createAccount : ParaSignerModule.dklsCreateAccount;
|
|
91
|
+
const signer = await createAccountFn(walletId, protocolId, KeyShareType.USER, userId);
|
|
92
|
+
return { signer, walletId };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
refresh(
|
|
96
|
+
_ctx: Ctx,
|
|
97
|
+
_sessionCookie: string,
|
|
98
|
+
_userId: string,
|
|
99
|
+
_walletId: string,
|
|
100
|
+
_share: string,
|
|
101
|
+
_oldPartnerId?: string,
|
|
102
|
+
_newPartnerId?: string,
|
|
103
|
+
): Promise<{
|
|
104
|
+
signer: string;
|
|
105
|
+
}> {
|
|
106
|
+
throw new Error('Method not implemented.');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
preKeygen(
|
|
110
|
+
_ctx: Ctx,
|
|
111
|
+
_partnerId: string,
|
|
112
|
+
_email: string,
|
|
113
|
+
_secretKey: string | null,
|
|
114
|
+
_sessionCookie: string,
|
|
115
|
+
): Promise<{ signer: string; walletId: string }> {
|
|
116
|
+
throw new Error('Method not implemented.');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
getPrivateKey(_ctx: Ctx, _userId: string, _walletId: string, _share: string, _sessionCookie: string): Promise<string> {
|
|
120
|
+
throw new Error('Method not implemented.');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
openPopup(_popupUrl: string): any {
|
|
124
|
+
throw new Error('Method not implemented.');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
private async baseSignTransaction(
|
|
128
|
+
ctx: Ctx,
|
|
129
|
+
userId: string,
|
|
130
|
+
walletId: string,
|
|
131
|
+
protocolId: string,
|
|
132
|
+
share: string,
|
|
133
|
+
rlpEncodedTxBase64: string,
|
|
134
|
+
chainId: string,
|
|
135
|
+
isDKLS?: boolean,
|
|
136
|
+
): Promise<SignatureRes> {
|
|
137
|
+
if (ctx.mpcComputationClient && !isDKLS) {
|
|
138
|
+
const signature = (await sendTransactionRequest(ctx, userId, walletId, protocolId, rlpEncodedTxBase64, share, chainId))
|
|
139
|
+
.signature;
|
|
140
|
+
return { signature };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const sendTransactionFn = isDKLS ? ParaSignerModule.dklsSendTransaction : ParaSignerModule.sendTransaction;
|
|
144
|
+
const signature = await sendTransactionFn(protocolId, share, rlpEncodedTxBase64, userId);
|
|
145
|
+
return { signature: signature.slice(2) };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async sendTransaction(
|
|
149
|
+
ctx: Ctx,
|
|
150
|
+
userId: string,
|
|
151
|
+
walletId: string,
|
|
152
|
+
share: string,
|
|
153
|
+
rlpEncodedTxBase64: string,
|
|
154
|
+
chainId: string,
|
|
155
|
+
_sessionCookie: string,
|
|
156
|
+
isDKLS?: boolean,
|
|
157
|
+
): Promise<SignatureRes> {
|
|
158
|
+
const { protocolId } = (
|
|
159
|
+
await ctx.client.sendTransaction(userId, walletId, {
|
|
160
|
+
transaction: rlpEncodedTxBase64,
|
|
161
|
+
chainId,
|
|
162
|
+
})
|
|
163
|
+
).data;
|
|
164
|
+
return this.baseSignTransaction(ctx, userId, walletId, protocolId, share, rlpEncodedTxBase64, chainId, isDKLS);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async signHash(_address: string, _hash: string): Promise<{ v: number; r: Buffer; s: Buffer }> {
|
|
168
|
+
throw new Error('not implemented');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async signMessage(
|
|
172
|
+
ctx: Ctx,
|
|
173
|
+
userId: string,
|
|
174
|
+
walletId: string,
|
|
175
|
+
share: string,
|
|
176
|
+
messageBase64: string, // base64 message
|
|
177
|
+
_sessionCookie: string,
|
|
178
|
+
isDKLS?: boolean,
|
|
179
|
+
): Promise<SignatureRes> {
|
|
180
|
+
const res = await ctx.client.preSignMessage(userId, walletId, messageBase64);
|
|
181
|
+
|
|
182
|
+
if (ctx.mpcComputationClient && !isDKLS) {
|
|
183
|
+
const signature = (await signMessageRequest(ctx, userId, walletId, res.protocolId, messageBase64, share)).signature;
|
|
184
|
+
return { signature };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const signMessageFn = isDKLS ? ParaSignerModule.dklsSignMessage : ParaSignerModule.signMessage;
|
|
188
|
+
const signature = await signMessageFn(res.protocolId, share, messageBase64, userId);
|
|
189
|
+
|
|
190
|
+
if (signature.startsWith('0x')) {
|
|
191
|
+
return { signature: signature.slice(2) };
|
|
192
|
+
}
|
|
193
|
+
return { signature };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async signTransaction(
|
|
197
|
+
ctx: Ctx,
|
|
198
|
+
userId: string,
|
|
199
|
+
walletId: string,
|
|
200
|
+
share: string,
|
|
201
|
+
rlpEncodedTxBase64: string, // base64 encoding of rlp encoded tx
|
|
202
|
+
chainId: string,
|
|
203
|
+
_sessionCookie: string,
|
|
204
|
+
isDKLS?: boolean,
|
|
205
|
+
): Promise<SignatureRes> {
|
|
206
|
+
const { protocolId } = (
|
|
207
|
+
await ctx.client.signTransaction(userId, walletId, {
|
|
208
|
+
transaction: rlpEncodedTxBase64,
|
|
209
|
+
chainId,
|
|
210
|
+
})
|
|
211
|
+
).data;
|
|
212
|
+
return this.baseSignTransaction(ctx, userId, walletId, protocolId, share, rlpEncodedTxBase64, chainId, isDKLS);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async ed25519Keygen(
|
|
216
|
+
ctx: Ctx,
|
|
217
|
+
userId: string,
|
|
218
|
+
_sessionCookie: string,
|
|
219
|
+
_emailProps?: BackupKitEmailProps,
|
|
220
|
+
): Promise<{
|
|
221
|
+
signer: string;
|
|
222
|
+
walletId: string;
|
|
223
|
+
}> {
|
|
224
|
+
const { walletId, protocolId } = await ctx.client.createWallet(userId, {
|
|
225
|
+
scheme: WalletScheme.ED25519,
|
|
226
|
+
type: WalletType.SOLANA,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
const signer = await ParaSignerModule.ed25519CreateAccount(walletId, protocolId);
|
|
230
|
+
return { signer, walletId };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async ed25519PreKeygen(
|
|
234
|
+
ctx: Ctx,
|
|
235
|
+
pregenIdentifier: string,
|
|
236
|
+
pregenIdentifierType: TPregenIdentifierType,
|
|
237
|
+
_sessionCookie: string,
|
|
238
|
+
): Promise<{
|
|
239
|
+
signer: string;
|
|
240
|
+
walletId: string;
|
|
241
|
+
}> {
|
|
242
|
+
const { walletId, protocolId } = await ctx.client.createWalletPreGen({
|
|
243
|
+
pregenIdentifier,
|
|
244
|
+
pregenIdentifierType,
|
|
245
|
+
scheme: WalletScheme.ED25519,
|
|
246
|
+
type: WalletType.SOLANA,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const signer = await ParaSignerModule.ed25519CreateAccount(walletId, protocolId);
|
|
250
|
+
return { signer, walletId };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async ed25519Sign(
|
|
254
|
+
ctx: Ctx,
|
|
255
|
+
userId: string,
|
|
256
|
+
walletId: string,
|
|
257
|
+
share: string,
|
|
258
|
+
base64Bytes: string,
|
|
259
|
+
_sessionCookie: string,
|
|
260
|
+
): Promise<SignatureRes> {
|
|
261
|
+
const { protocolId } = await ctx.client.preSignMessage(userId, walletId, base64Bytes, WalletScheme.ED25519);
|
|
262
|
+
|
|
263
|
+
const base64Sig = await ParaSignerModule.ed25519Sign(protocolId, share, base64Bytes);
|
|
264
|
+
return { signature: base64Sig };
|
|
265
|
+
}
|
|
266
|
+
}
|
package/src/shim.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// 1) Node-like crypto + Web Crypto API (subtle + getRandomValues)
|
|
2
|
+
import crypto from 'react-native-quick-crypto';
|
|
3
|
+
import { webcrypto } from 'crypto';
|
|
4
|
+
import { Crypto } from '@peculiar/webcrypto';
|
|
5
|
+
|
|
6
|
+
if (typeof global.crypto === 'undefined') {
|
|
7
|
+
// Attach Node-style crypto for randomFillSync, createHash, etc.
|
|
8
|
+
global.crypto = crypto;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// If `crypto.subtle` is missing, patch it with `@peculiar/webcrypto`
|
|
12
|
+
if (!global.crypto.subtle) {
|
|
13
|
+
const peculiarCrypto = new Crypto();
|
|
14
|
+
global.crypto.subtle = peculiarCrypto.subtle;
|
|
15
|
+
global.crypto.getRandomValues = peculiarCrypto.getRandomValues.bind(peculiarCrypto);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Add getRandomValues to webcrypto from peculiar
|
|
19
|
+
const peculiarCrypto = new Crypto();
|
|
20
|
+
webcrypto.getRandomValues = peculiarCrypto.getRandomValues.bind(peculiarCrypto);
|
|
21
|
+
|
|
22
|
+
// 2) Provide TextEncoder / TextDecoder
|
|
23
|
+
import * as FSTED from 'fastestsmallesttextencoderdecoder';
|
|
24
|
+
|
|
25
|
+
if (typeof global.TextEncoder === 'undefined') {
|
|
26
|
+
global.TextEncoder = FSTED.TextEncoder;
|
|
27
|
+
}
|
|
28
|
+
if (typeof global.TextDecoder === 'undefined') {
|
|
29
|
+
global.TextDecoder = FSTED.TextDecoder;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// 3) Provide atob / btoa via react-native-quick-base64
|
|
33
|
+
import { atob, btoa } from 'react-native-quick-base64';
|
|
34
|
+
|
|
35
|
+
if (typeof global.atob === 'undefined') {
|
|
36
|
+
global.atob = atob;
|
|
37
|
+
}
|
|
38
|
+
if (typeof global.btoa === 'undefined') {
|
|
39
|
+
global.btoa = btoa;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 4) Patch node-forge with react-native-modpow for faster RSA ops
|
|
43
|
+
import Forge from 'node-forge';
|
|
44
|
+
import modPow from 'react-native-modpow';
|
|
45
|
+
|
|
46
|
+
Forge.jsbn.BigInteger.prototype.modPow = function nativeModPow(e, m) {
|
|
47
|
+
const result = modPow({
|
|
48
|
+
target: this.toString(16),
|
|
49
|
+
value: e.toString(16),
|
|
50
|
+
modifier: m.toString(16),
|
|
51
|
+
});
|
|
52
|
+
return new Forge.jsbn.BigInteger(result, 16);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// 5) Provide global Buffer
|
|
56
|
+
import { Buffer } from '@craftzdog/react-native-buffer';
|
|
57
|
+
if (typeof global.Buffer === 'undefined') {
|
|
58
|
+
global.Buffer = Buffer;
|
|
59
|
+
}
|