@portal-hq/web 3.6.2-alpha → 3.7.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/hypernative.d.ts +346 -0
- package/lib/commonjs/index.js +144 -2
- package/lib/commonjs/index.test.js +119 -2
- package/lib/commonjs/integrations/security/hypernative/index.js +101 -0
- package/lib/commonjs/integrations/security/hypernative/index.test.js +151 -0
- package/lib/commonjs/integrations/security/index.js +16 -0
- package/lib/commonjs/integrations/trading/zero-x/index.js +17 -4
- package/lib/commonjs/integrations/trading/zero-x/index.test.js +61 -15
- package/lib/commonjs/mpc/index.js +156 -5
- package/lib/commonjs/mpc/index.test.js +794 -5
- package/lib/commonjs/passkeys/index.js +394 -0
- package/lib/commonjs/passkeys/types.js +2 -0
- package/lib/commonjs/provider/index.js +5 -2
- package/lib/esm/index.js +144 -2
- package/lib/esm/index.test.js +119 -2
- package/lib/esm/integrations/security/hypernative/index.js +98 -0
- package/lib/esm/integrations/security/hypernative/index.test.js +146 -0
- package/lib/esm/integrations/security/index.js +10 -0
- package/lib/esm/integrations/trading/zero-x/index.js +17 -4
- package/lib/esm/integrations/trading/zero-x/index.test.js +62 -16
- package/lib/esm/mpc/index.js +156 -5
- package/lib/esm/mpc/index.test.js +795 -6
- package/lib/esm/passkeys/index.js +390 -0
- package/lib/esm/passkeys/types.js +1 -0
- package/lib/esm/provider/index.js +5 -2
- package/lifi-types.d.ts +1236 -0
- package/package.json +6 -3
- package/src/__mocks/constants.ts +422 -5
- package/src/__mocks/portal/mpc.ts +1 -0
- package/src/index.test.ts +179 -3
- package/src/index.ts +212 -4
- package/src/integrations/security/hypernative/index.test.ts +196 -0
- package/src/integrations/security/hypernative/index.ts +106 -0
- package/src/integrations/security/index.ts +14 -0
- package/src/integrations/trading/zero-x/index.test.ts +98 -19
- package/src/integrations/trading/zero-x/index.ts +29 -9
- package/src/mpc/index.test.ts +944 -7
- package/src/mpc/index.ts +200 -10
- package/src/passkeys/index.ts +536 -0
- package/src/passkeys/types.ts +78 -0
- package/src/provider/index.ts +5 -0
- package/tsconfig.json +7 -1
- package/types.d.ts +45 -12
- package/yieldxyz-types.d.ts +778 -0
- package/zero-x.d.ts +204 -0
package/src/index.ts
CHANGED
|
@@ -17,9 +17,10 @@ import {
|
|
|
17
17
|
NFTAsset,
|
|
18
18
|
OrgBackupShares,
|
|
19
19
|
SendAssetParams,
|
|
20
|
-
SendAssetResponse,
|
|
21
20
|
type BackupConfigs,
|
|
21
|
+
type BackupOptions,
|
|
22
22
|
type BackupResponse,
|
|
23
|
+
type BackupShareResult,
|
|
23
24
|
type Balance,
|
|
24
25
|
type ClientResponse,
|
|
25
26
|
type ClientResponseWallet,
|
|
@@ -29,6 +30,7 @@ import {
|
|
|
29
30
|
type GDriveConfig,
|
|
30
31
|
type NFT,
|
|
31
32
|
type PasskeyConfig,
|
|
33
|
+
type PasskeyOptions,
|
|
32
34
|
type PortalOptions,
|
|
33
35
|
type ProgressCallback,
|
|
34
36
|
type QuoteArgs,
|
|
@@ -45,6 +47,9 @@ import Mpc from './mpc'
|
|
|
45
47
|
import Provider, { RequestMethod } from './provider'
|
|
46
48
|
import Yield from './integrations/yield'
|
|
47
49
|
import Trading from './integrations/trading'
|
|
50
|
+
import Security from './integrations/security'
|
|
51
|
+
import PasskeyService from './passkeys'
|
|
52
|
+
|
|
48
53
|
|
|
49
54
|
class Portal {
|
|
50
55
|
public address?: string
|
|
@@ -58,6 +63,7 @@ class Portal {
|
|
|
58
63
|
public mpc: Mpc
|
|
59
64
|
public yield: Yield
|
|
60
65
|
public trading: Trading
|
|
66
|
+
public security: Security
|
|
61
67
|
public mpcHost: string
|
|
62
68
|
public mpcVersion: string
|
|
63
69
|
public provider: Provider
|
|
@@ -66,6 +72,8 @@ class Portal {
|
|
|
66
72
|
private errorCallbacks: ((reason: string) => any | Promise<any>)[] = []
|
|
67
73
|
private rpcConfig: RpcConfig
|
|
68
74
|
private readyCallbacks: (() => any | Promise<any>)[] = []
|
|
75
|
+
private passkeyService?: PasskeyService
|
|
76
|
+
private passkeyServiceDefaultDomain?: string
|
|
69
77
|
|
|
70
78
|
public get ready() {
|
|
71
79
|
return this.mpc.ready
|
|
@@ -113,6 +121,8 @@ class Portal {
|
|
|
113
121
|
|
|
114
122
|
this.trading = new Trading({ mpc: this.mpc })
|
|
115
123
|
|
|
124
|
+
this.security = new Security({ mpc: this.mpc })
|
|
125
|
+
|
|
116
126
|
this.provider = new Provider({
|
|
117
127
|
portal: this,
|
|
118
128
|
chainId: chainId ? Number(chainId) : undefined,
|
|
@@ -192,6 +202,141 @@ class Portal {
|
|
|
192
202
|
return address
|
|
193
203
|
}
|
|
194
204
|
|
|
205
|
+
public async generateBackupShare(
|
|
206
|
+
progress: ProgressCallback = () => {
|
|
207
|
+
// Noop
|
|
208
|
+
},
|
|
209
|
+
): Promise<BackupShareResult> {
|
|
210
|
+
const response = await this.mpc.backup(
|
|
211
|
+
{
|
|
212
|
+
backupMethod: BackupMethods.custom,
|
|
213
|
+
backupConfigs: {},
|
|
214
|
+
host: this.host,
|
|
215
|
+
mpcVersion: this.mpcVersion,
|
|
216
|
+
featureFlags: this.featureFlags,
|
|
217
|
+
},
|
|
218
|
+
progress,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
if (!response.encryptionKey) {
|
|
222
|
+
throw new Error(
|
|
223
|
+
'[Portal] Custom backup did not return an encryption key. Please ensure you are using the latest iframe bundle.',
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
cipherText: response.cipherText,
|
|
229
|
+
encryptionKey: response.encryptionKey,
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
public async registerPasskeyAndStoreEncryptionKey(
|
|
234
|
+
cipherText: string,
|
|
235
|
+
encryptionKey: string,
|
|
236
|
+
options: PasskeyOptions = {},
|
|
237
|
+
): Promise<void> {
|
|
238
|
+
const { service, customDomain, relyingPartyId, relyingPartyName } =
|
|
239
|
+
this.resolvePasskeyOptions(options)
|
|
240
|
+
|
|
241
|
+
await service.registerPasskeyAndStoreKey({
|
|
242
|
+
customDomain,
|
|
243
|
+
relyingPartyName,
|
|
244
|
+
encryptionKey,
|
|
245
|
+
relyingPartyId,
|
|
246
|
+
cipherText,
|
|
247
|
+
})
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
public async authenticatePasskeyAndRetrieveKey(
|
|
251
|
+
options: PasskeyOptions = {},
|
|
252
|
+
): Promise<string> {
|
|
253
|
+
const { service, customDomain, relyingPartyId, relyingPartyName } =
|
|
254
|
+
this.resolvePasskeyOptions(options)
|
|
255
|
+
|
|
256
|
+
return await service.authenticatePasskeyAndRetrieveKey({
|
|
257
|
+
customDomain,
|
|
258
|
+
relyingPartyName,
|
|
259
|
+
relyingPartyId,
|
|
260
|
+
})
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Register a passkey without tying it to an encryption key.
|
|
265
|
+
* The encryption key can be stored later using authenticatePasskeyAndWriteKey.
|
|
266
|
+
*/
|
|
267
|
+
public async registerPasskey(
|
|
268
|
+
options: PasskeyOptions = {},
|
|
269
|
+
): Promise<void> {
|
|
270
|
+
const { service, customDomain, relyingPartyId, relyingPartyName } =
|
|
271
|
+
this.resolvePasskeyOptions(options)
|
|
272
|
+
|
|
273
|
+
await service.registerPasskey({
|
|
274
|
+
customDomain,
|
|
275
|
+
relyingPartyName,
|
|
276
|
+
relyingPartyId,
|
|
277
|
+
})
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Authenticate with passkey and store an encryption key.
|
|
282
|
+
* Used after registerPasskey to associate an encryption key with the passkey.
|
|
283
|
+
*/
|
|
284
|
+
public async authenticatePasskeyAndWriteKey(
|
|
285
|
+
encryptionKey: string,
|
|
286
|
+
options: PasskeyOptions = {},
|
|
287
|
+
): Promise<void> {
|
|
288
|
+
const { service, customDomain, relyingPartyId, relyingPartyName } =
|
|
289
|
+
this.resolvePasskeyOptions(options)
|
|
290
|
+
|
|
291
|
+
await service.authenticatePasskeyAndWriteKey({
|
|
292
|
+
customDomain,
|
|
293
|
+
relyingPartyName,
|
|
294
|
+
relyingPartyId,
|
|
295
|
+
encryptionKey,
|
|
296
|
+
})
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
public async backupWithPasskey(
|
|
300
|
+
options: BackupOptions = {},
|
|
301
|
+
progress: ProgressCallback = () => {
|
|
302
|
+
// Noop
|
|
303
|
+
},
|
|
304
|
+
): Promise<void> {
|
|
305
|
+
const {
|
|
306
|
+
usePopup = true,
|
|
307
|
+
customDomain,
|
|
308
|
+
relyingPartyName,
|
|
309
|
+
backupMethod = BackupMethods.passkey,
|
|
310
|
+
} = options
|
|
311
|
+
|
|
312
|
+
if (usePopup) {
|
|
313
|
+
await this.backupWallet(backupMethod, progress, {})
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (backupMethod !== BackupMethods.passkey) {
|
|
318
|
+
throw new Error(
|
|
319
|
+
`[Portal] Direct passkey backup currently supports only BackupMethods.passkey (received ${backupMethod}).`,
|
|
320
|
+
)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const { cipherText, encryptionKey } = await this.generateBackupShare(
|
|
324
|
+
progress,
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
await this.registerPasskeyAndStoreEncryptionKey(
|
|
328
|
+
cipherText,
|
|
329
|
+
encryptionKey,
|
|
330
|
+
{
|
|
331
|
+
customDomain,
|
|
332
|
+
relyingPartyName,
|
|
333
|
+
usePopup,
|
|
334
|
+
},
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
await this.storedClientBackupShare(true, BackupMethods.passkey)
|
|
338
|
+
}
|
|
339
|
+
|
|
195
340
|
public async backupWallet(
|
|
196
341
|
backupMethod: BackupMethods,
|
|
197
342
|
progress: ProgressCallback = () => {
|
|
@@ -441,7 +586,7 @@ class Portal {
|
|
|
441
586
|
public async sendAsset(
|
|
442
587
|
chain: string,
|
|
443
588
|
params: SendAssetParams,
|
|
444
|
-
): Promise<
|
|
589
|
+
): Promise<string> {
|
|
445
590
|
try {
|
|
446
591
|
// Convert the chain to a chain ID
|
|
447
592
|
const chainId = this.convertChainToChainId(chain)
|
|
@@ -465,6 +610,7 @@ class Portal {
|
|
|
465
610
|
chainId,
|
|
466
611
|
method: 'eth_sendTransaction',
|
|
467
612
|
params: [buildTxResponse.transaction],
|
|
613
|
+
sponsorGas: params.sponsorGas,
|
|
468
614
|
})
|
|
469
615
|
break
|
|
470
616
|
}
|
|
@@ -473,6 +619,7 @@ class Portal {
|
|
|
473
619
|
chainId,
|
|
474
620
|
method: 'sol_signAndSendTransaction',
|
|
475
621
|
params: [buildTxResponse.transaction],
|
|
622
|
+
sponsorGas: params.sponsorGas,
|
|
476
623
|
})
|
|
477
624
|
break
|
|
478
625
|
}
|
|
@@ -899,7 +1046,7 @@ class Portal {
|
|
|
899
1046
|
args: QuoteArgs,
|
|
900
1047
|
chainId: string,
|
|
901
1048
|
): Promise<QuoteResponse> {
|
|
902
|
-
return this.mpc?.getQuote(
|
|
1049
|
+
return this.mpc?.getQuote(chainId, args, apiKey)
|
|
903
1050
|
}
|
|
904
1051
|
|
|
905
1052
|
/**
|
|
@@ -909,7 +1056,7 @@ class Portal {
|
|
|
909
1056
|
apiKey: string,
|
|
910
1057
|
chainId: string,
|
|
911
1058
|
): Promise<Record<string, string>> {
|
|
912
|
-
return this.mpc?.getSources(
|
|
1059
|
+
return this.mpc?.getSources(chainId, apiKey)
|
|
913
1060
|
}
|
|
914
1061
|
|
|
915
1062
|
/*******************************
|
|
@@ -1030,6 +1177,66 @@ class Portal {
|
|
|
1030
1177
|
|
|
1031
1178
|
return chainId
|
|
1032
1179
|
}
|
|
1180
|
+
|
|
1181
|
+
private ensurePasskeyService(): PasskeyService {
|
|
1182
|
+
const defaultDomain = this.getDefaultPasskeyDomain()
|
|
1183
|
+
|
|
1184
|
+
if (
|
|
1185
|
+
!this.passkeyService ||
|
|
1186
|
+
this.passkeyServiceDefaultDomain !== defaultDomain
|
|
1187
|
+
) {
|
|
1188
|
+
this.passkeyService = new PasskeyService({
|
|
1189
|
+
defaultDomain,
|
|
1190
|
+
getJwt: () => this.mpc!.getPasskeyJwt(),
|
|
1191
|
+
})
|
|
1192
|
+
this.passkeyServiceDefaultDomain = defaultDomain
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
return this.passkeyService
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
private getDefaultPasskeyDomain(): string {
|
|
1199
|
+
return this.passkeyConfig?.webAuthnHost ?? 'backup.web.portalhq.io'
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
private extractRpId(domain?: string): string {
|
|
1203
|
+
const targetDomain = domain ?? this.getDefaultPasskeyDomain()
|
|
1204
|
+
|
|
1205
|
+
try {
|
|
1206
|
+
const normalized = targetDomain.startsWith('http')
|
|
1207
|
+
? targetDomain
|
|
1208
|
+
: `https://${targetDomain}`
|
|
1209
|
+
return new URL(normalized).hostname
|
|
1210
|
+
} catch {
|
|
1211
|
+
return targetDomain
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
private resolvePasskeyOptions(options: PasskeyOptions): {
|
|
1216
|
+
service: PasskeyService
|
|
1217
|
+
customDomain: string | undefined
|
|
1218
|
+
relyingPartyId: string
|
|
1219
|
+
relyingPartyName: string
|
|
1220
|
+
} {
|
|
1221
|
+
if (options.usePopup) {
|
|
1222
|
+
throw new Error(
|
|
1223
|
+
'[Portal] This method does not support the popup flow. Use usePopup: false.',
|
|
1224
|
+
)
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
const service = this.ensurePasskeyService()
|
|
1228
|
+
const domain = options.customDomain ?? this.getDefaultPasskeyDomain()
|
|
1229
|
+
const relyingPartyId = options.relyingPartyId ?? this.extractRpId(domain)
|
|
1230
|
+
const relyingPartyName =
|
|
1231
|
+
options.relyingPartyName ?? this.passkeyConfig?.relyingParty ?? 'Portal'
|
|
1232
|
+
|
|
1233
|
+
return {
|
|
1234
|
+
service,
|
|
1235
|
+
customDomain: options.customDomain,
|
|
1236
|
+
relyingPartyId,
|
|
1237
|
+
relyingPartyName,
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1033
1240
|
}
|
|
1034
1241
|
|
|
1035
1242
|
export {
|
|
@@ -1079,6 +1286,7 @@ export enum BackupMethods {
|
|
|
1079
1286
|
gdrive = 'GDRIVE',
|
|
1080
1287
|
password = 'PASSWORD',
|
|
1081
1288
|
passkey = 'PASSKEY',
|
|
1289
|
+
custom = 'CUSTOM',
|
|
1082
1290
|
unknown = 'UNKNOWN',
|
|
1083
1291
|
}
|
|
1084
1292
|
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import Hypernative from '.'
|
|
6
|
+
import Mpc from '../../../mpc'
|
|
7
|
+
import portalMock from '../../../__mocks/portal/portal'
|
|
8
|
+
import {
|
|
9
|
+
mockHost,
|
|
10
|
+
mockScanAddressesRequest,
|
|
11
|
+
mockScanAddressesResponse,
|
|
12
|
+
mockScanEVMTxRequest,
|
|
13
|
+
mockScanEVMTxResponse,
|
|
14
|
+
mockScanEip712TxRequest,
|
|
15
|
+
mockScanEip712TxResponse,
|
|
16
|
+
mockScanSolanaTxRequest,
|
|
17
|
+
mockScanSolanaTxResponse,
|
|
18
|
+
mockScanNftRequest,
|
|
19
|
+
mockScanNftResponse,
|
|
20
|
+
mockScanTokenRequest,
|
|
21
|
+
mockScanTokenResponse,
|
|
22
|
+
mockScanUrlRequest,
|
|
23
|
+
mockScanUrlResponse,
|
|
24
|
+
} from '../../../__mocks/constants'
|
|
25
|
+
|
|
26
|
+
describe('Hypernative', () => {
|
|
27
|
+
let hypernative: Hypernative
|
|
28
|
+
let mpc: Mpc
|
|
29
|
+
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
jest.clearAllMocks()
|
|
32
|
+
|
|
33
|
+
portalMock.host = mockHost
|
|
34
|
+
mpc = new Mpc({
|
|
35
|
+
portal: portalMock,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
hypernative = new Hypernative({ mpc })
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe('scanEVMTx', () => {
|
|
42
|
+
it('should call mpc.scanEVMTx with the correct arguments', async () => {
|
|
43
|
+
const spy = jest
|
|
44
|
+
.spyOn(mpc, 'scanEVMTx')
|
|
45
|
+
.mockResolvedValue(mockScanEVMTxResponse)
|
|
46
|
+
|
|
47
|
+
const result = await hypernative.scanEVMTx(mockScanEVMTxRequest)
|
|
48
|
+
|
|
49
|
+
expect(spy).toHaveBeenCalledWith(mockScanEVMTxRequest)
|
|
50
|
+
expect(result).toEqual(mockScanEVMTxResponse)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('should propagate errors from mpc.scanEVMTx', async () => {
|
|
54
|
+
const error = new Error('Test error')
|
|
55
|
+
jest.spyOn(mpc, 'scanEVMTx').mockRejectedValue(error)
|
|
56
|
+
|
|
57
|
+
await expect(
|
|
58
|
+
hypernative.scanEVMTx(mockScanEVMTxRequest),
|
|
59
|
+
).rejects.toThrow('Test error')
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
describe('scanEip712Tx', () => {
|
|
64
|
+
it('should call mpc.scanEip712Tx with the correct arguments', async () => {
|
|
65
|
+
const spy = jest
|
|
66
|
+
.spyOn(mpc, 'scanEip712Tx')
|
|
67
|
+
.mockResolvedValue(mockScanEip712TxResponse)
|
|
68
|
+
|
|
69
|
+
const result = await hypernative.scanEip712Tx(mockScanEip712TxRequest)
|
|
70
|
+
|
|
71
|
+
expect(spy).toHaveBeenCalledWith(mockScanEip712TxRequest)
|
|
72
|
+
expect(result).toEqual(mockScanEip712TxResponse)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should propagate errors from mpc.scanEip712Tx', async () => {
|
|
76
|
+
const error = new Error('Test error')
|
|
77
|
+
jest.spyOn(mpc, 'scanEip712Tx').mockRejectedValue(error)
|
|
78
|
+
|
|
79
|
+
await expect(
|
|
80
|
+
hypernative.scanEip712Tx(mockScanEip712TxRequest),
|
|
81
|
+
).rejects.toThrow('Test error')
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe('scanSolanaTx', () => {
|
|
86
|
+
it('should call mpc.scanSolanaTx with the correct arguments', async () => {
|
|
87
|
+
const spy = jest
|
|
88
|
+
.spyOn(mpc, 'scanSolanaTx')
|
|
89
|
+
.mockResolvedValue(mockScanSolanaTxResponse)
|
|
90
|
+
|
|
91
|
+
const result = await hypernative.scanSolanaTx(mockScanSolanaTxRequest)
|
|
92
|
+
|
|
93
|
+
expect(spy).toHaveBeenCalledWith(mockScanSolanaTxRequest)
|
|
94
|
+
expect(result).toEqual(mockScanSolanaTxResponse)
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('should propagate errors from mpc.scanSolanaTx', async () => {
|
|
98
|
+
const error = new Error('Test error')
|
|
99
|
+
jest.spyOn(mpc, 'scanSolanaTx').mockRejectedValue(error)
|
|
100
|
+
|
|
101
|
+
await expect(
|
|
102
|
+
hypernative.scanSolanaTx(mockScanSolanaTxRequest),
|
|
103
|
+
).rejects.toThrow('Test error')
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
describe('scanAddresses', () => {
|
|
108
|
+
it('should call mpc.scanAddresses with the correct arguments', async () => {
|
|
109
|
+
const spy = jest
|
|
110
|
+
.spyOn(mpc, 'scanAddresses')
|
|
111
|
+
.mockResolvedValue(mockScanAddressesResponse)
|
|
112
|
+
|
|
113
|
+
const { addresses, ...options } = mockScanAddressesRequest
|
|
114
|
+
const result = await hypernative.scanAddresses(addresses, options)
|
|
115
|
+
|
|
116
|
+
expect(spy).toHaveBeenCalledWith(mockScanAddressesRequest)
|
|
117
|
+
expect(result).toEqual(mockScanAddressesResponse)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('should propagate errors from mpc.scanAddresses', async () => {
|
|
121
|
+
const error = new Error('Test error')
|
|
122
|
+
jest.spyOn(mpc, 'scanAddresses').mockRejectedValue(error)
|
|
123
|
+
|
|
124
|
+
const { addresses, ...options } = mockScanAddressesRequest
|
|
125
|
+
await expect(
|
|
126
|
+
hypernative.scanAddresses(addresses, options),
|
|
127
|
+
).rejects.toThrow('Test error')
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
describe('scanNFTs', () => {
|
|
132
|
+
it('should call mpc.scanNFTs with the correct arguments', async () => {
|
|
133
|
+
const spy = jest
|
|
134
|
+
.spyOn(mpc, 'scanNFTs')
|
|
135
|
+
.mockResolvedValue(mockScanNftResponse)
|
|
136
|
+
|
|
137
|
+
const result = await hypernative.scanNFTs(mockScanNftRequest)
|
|
138
|
+
|
|
139
|
+
expect(spy).toHaveBeenCalledWith(mockScanNftRequest)
|
|
140
|
+
expect(result).toEqual(mockScanNftResponse)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('should propagate errors from mpc.scanNFTs', async () => {
|
|
144
|
+
const error = new Error('Test error')
|
|
145
|
+
jest.spyOn(mpc, 'scanNFTs').mockRejectedValue(error)
|
|
146
|
+
|
|
147
|
+
await expect(hypernative.scanNFTs(mockScanNftRequest)).rejects.toThrow(
|
|
148
|
+
'Test error',
|
|
149
|
+
)
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
describe('scanTokens', () => {
|
|
154
|
+
it('should call mpc.scanTokens with the correct arguments', async () => {
|
|
155
|
+
const spy = jest
|
|
156
|
+
.spyOn(mpc, 'scanTokens')
|
|
157
|
+
.mockResolvedValue(mockScanTokenResponse)
|
|
158
|
+
|
|
159
|
+
const result = await hypernative.scanTokens(mockScanTokenRequest)
|
|
160
|
+
|
|
161
|
+
expect(spy).toHaveBeenCalledWith(mockScanTokenRequest)
|
|
162
|
+
expect(result).toEqual(mockScanTokenResponse)
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('should propagate errors from mpc.scanTokens', async () => {
|
|
166
|
+
const error = new Error('Test error')
|
|
167
|
+
jest.spyOn(mpc, 'scanTokens').mockRejectedValue(error)
|
|
168
|
+
|
|
169
|
+
await expect(
|
|
170
|
+
hypernative.scanTokens(mockScanTokenRequest),
|
|
171
|
+
).rejects.toThrow('Test error')
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
describe('scanURL', () => {
|
|
176
|
+
it('should call mpc.scanUrl with the correct arguments', async () => {
|
|
177
|
+
const spy = jest
|
|
178
|
+
.spyOn(mpc, 'scanUrl')
|
|
179
|
+
.mockResolvedValue(mockScanUrlResponse)
|
|
180
|
+
|
|
181
|
+
const result = await hypernative.scanURL(mockScanUrlRequest)
|
|
182
|
+
|
|
183
|
+
expect(spy).toHaveBeenCalledWith(mockScanUrlRequest)
|
|
184
|
+
expect(result).toEqual(mockScanUrlResponse)
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('should propagate errors from mpc.scanUrl', async () => {
|
|
188
|
+
const error = new Error('Test error')
|
|
189
|
+
jest.spyOn(mpc, 'scanUrl').mockRejectedValue(error)
|
|
190
|
+
|
|
191
|
+
await expect(hypernative.scanURL(mockScanUrlRequest)).rejects.toThrow(
|
|
192
|
+
'Test error',
|
|
193
|
+
)
|
|
194
|
+
})
|
|
195
|
+
})
|
|
196
|
+
})
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import Mpc from '../../../mpc'
|
|
2
|
+
import {
|
|
3
|
+
ScreenAddressApiResponse,
|
|
4
|
+
ScanEVMRequest,
|
|
5
|
+
ScanEVMResponse,
|
|
6
|
+
ScanEip712Request,
|
|
7
|
+
ScanEip712Response,
|
|
8
|
+
ScanSolanaRequest,
|
|
9
|
+
ScanSolanaResponse,
|
|
10
|
+
ScanNftsRequest,
|
|
11
|
+
ScanNftsResponse,
|
|
12
|
+
ScanTokensRequest,
|
|
13
|
+
ScanTokensResponse,
|
|
14
|
+
ScanUrlRequest,
|
|
15
|
+
ScanUrlResponse,
|
|
16
|
+
ScreenAddressRequestOptions,
|
|
17
|
+
} from '../../../../hypernative'
|
|
18
|
+
|
|
19
|
+
export default class Hypernative {
|
|
20
|
+
private mpc: Mpc
|
|
21
|
+
|
|
22
|
+
constructor({ mpc }: { mpc: Mpc }) {
|
|
23
|
+
this.mpc = mpc
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Scans an EIP-155 transaction for security risks.
|
|
28
|
+
* @param data - The parameters for the EIP-155 transaction scan request.
|
|
29
|
+
* @returns A `ScanEVMResponse` promise.
|
|
30
|
+
* @throws An error if the operation fails.
|
|
31
|
+
*/
|
|
32
|
+
public async scanEVMTx(
|
|
33
|
+
data: ScanEVMRequest,
|
|
34
|
+
): Promise<ScanEVMResponse> {
|
|
35
|
+
return this.mpc?.scanEVMTx(data)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Scans an EIP-712 typed message for security risks.
|
|
40
|
+
* @param data - The parameters for the EIP-712 message scan request.
|
|
41
|
+
* @returns A `ScanEip712Response` promise.
|
|
42
|
+
* @throws An error if the operation fails.
|
|
43
|
+
*/
|
|
44
|
+
public async scanEip712Tx(
|
|
45
|
+
data: ScanEip712Request,
|
|
46
|
+
): Promise<ScanEip712Response> {
|
|
47
|
+
return this.mpc?.scanEip712Tx(data)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Scans a Solana transaction for security risks.
|
|
52
|
+
* @param data - The parameters for the Solana transaction scan request.
|
|
53
|
+
* @returns A `ScanSolanaResponse` promise.
|
|
54
|
+
* @throws An error if the operation fails.
|
|
55
|
+
*/
|
|
56
|
+
public async scanSolanaTx(
|
|
57
|
+
data: ScanSolanaRequest,
|
|
58
|
+
): Promise<ScanSolanaResponse> {
|
|
59
|
+
return this.mpc?.scanSolanaTx(data)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Scans addresses for security risks and flags.
|
|
64
|
+
* @param data - The parameters for the address scan request.
|
|
65
|
+
* @returns A `ScreenAddressApiResponse` promise.
|
|
66
|
+
* @throws An error if the operation fails.
|
|
67
|
+
*/
|
|
68
|
+
public async scanAddresses(
|
|
69
|
+
addresses: string[],
|
|
70
|
+
options?: ScreenAddressRequestOptions,
|
|
71
|
+
): Promise<ScreenAddressApiResponse> {
|
|
72
|
+
return this.mpc?.scanAddresses({ addresses, ...options })
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Scans NFTs for security risks.
|
|
77
|
+
* @param data - The parameters for the NFT scan request.
|
|
78
|
+
* @returns A `ScanNftResponse` promise.
|
|
79
|
+
* @throws An error if the operation fails.
|
|
80
|
+
*/
|
|
81
|
+
public async scanNFTs(nfts: ScanNftsRequest): Promise<ScanNftsResponse> {
|
|
82
|
+
return this.mpc?.scanNFTs(nfts)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Scans tokens for security risks.
|
|
87
|
+
* @param data - The parameters for the token scan request.
|
|
88
|
+
* @returns A `ScanTokenResponse` promise.
|
|
89
|
+
* @throws An error if the operation fails.
|
|
90
|
+
*/
|
|
91
|
+
public async scanTokens(
|
|
92
|
+
tokens: ScanTokensRequest,
|
|
93
|
+
): Promise<ScanTokensResponse> {
|
|
94
|
+
return this.mpc?.scanTokens(tokens)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Scans a URL for malicious content.
|
|
99
|
+
* @param data - The parameters for the URL scan request.
|
|
100
|
+
* @returns A `ScanUrlResponse` promise.
|
|
101
|
+
* @throws An error if the operation fails.
|
|
102
|
+
*/
|
|
103
|
+
public async scanURL(url: ScanUrlRequest): Promise<ScanUrlResponse> {
|
|
104
|
+
return this.mpc?.scanUrl(url)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import Mpc from '../../mpc'
|
|
2
|
+
import Hypernative from './hypernative'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* This class is a container for the Hypernative class.
|
|
6
|
+
* In the future, Security domain logic should be here.
|
|
7
|
+
*/
|
|
8
|
+
export default class Security {
|
|
9
|
+
public hypernative: Hypernative
|
|
10
|
+
|
|
11
|
+
constructor({ mpc }: { mpc: Mpc }) {
|
|
12
|
+
this.hypernative = new Hypernative({ mpc })
|
|
13
|
+
}
|
|
14
|
+
}
|