accounts 0.12.2 → 0.13.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/CHANGELOG.md +23 -0
- package/README.md +6 -5
- package/dist/cli/adapter.d.ts.map +1 -1
- package/dist/cli/adapter.js +53 -67
- package/dist/cli/adapter.js.map +1 -1
- package/dist/core/AccessKey.d.ts +89 -106
- package/dist/core/AccessKey.d.ts.map +1 -1
- package/dist/core/AccessKey.js +226 -210
- package/dist/core/AccessKey.js.map +1 -1
- package/dist/core/Account.d.ts +0 -9
- package/dist/core/Account.d.ts.map +1 -1
- package/dist/core/Account.js +1 -13
- package/dist/core/Account.js.map +1 -1
- package/dist/core/Adapter.d.ts +10 -2
- package/dist/core/Adapter.d.ts.map +1 -1
- package/dist/core/Provider.d.ts +4 -5
- package/dist/core/Provider.d.ts.map +1 -1
- package/dist/core/Provider.js +66 -76
- package/dist/core/Provider.js.map +1 -1
- package/dist/core/Schema.d.ts +7 -7
- package/dist/core/adapters/dialog.d.ts.map +1 -1
- package/dist/core/adapters/dialog.js +114 -124
- package/dist/core/adapters/dialog.js.map +1 -1
- package/dist/core/adapters/local.d.ts.map +1 -1
- package/dist/core/adapters/local.js +92 -105
- package/dist/core/adapters/local.js.map +1 -1
- package/dist/core/adapters/privy.d.ts +164 -0
- package/dist/core/adapters/privy.d.ts.map +1 -0
- package/dist/core/adapters/privy.js +574 -0
- package/dist/core/adapters/privy.js.map +1 -0
- package/dist/core/adapters/turnkey.d.ts.map +1 -1
- package/dist/core/adapters/turnkey.js +136 -133
- package/dist/core/adapters/turnkey.js.map +1 -1
- package/dist/core/internal/AccessKeyTransaction.d.ts +59 -0
- package/dist/core/internal/AccessKeyTransaction.d.ts.map +1 -0
- package/dist/core/internal/AccessKeyTransaction.js +155 -0
- package/dist/core/internal/AccessKeyTransaction.js.map +1 -0
- package/dist/core/zod/rpc.d.ts +9 -4
- package/dist/core/zod/rpc.d.ts.map +1 -1
- package/dist/core/zod/rpc.js +9 -4
- package/dist/core/zod/rpc.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react-native/adapter.d.ts.map +1 -1
- package/dist/react-native/adapter.js +61 -72
- package/dist/react-native/adapter.js.map +1 -1
- package/dist/server/Kv.d.ts +19 -9
- package/dist/server/Kv.d.ts.map +1 -1
- package/dist/server/Kv.js +32 -9
- package/dist/server/Kv.js.map +1 -1
- package/dist/server/internal/handlers/auth.d.ts.map +1 -1
- package/dist/server/internal/handlers/auth.js +2 -0
- package/dist/server/internal/handlers/auth.js.map +1 -1
- package/dist/server/internal/handlers/relay.d.ts +18 -12
- package/dist/server/internal/handlers/relay.d.ts.map +1 -1
- package/dist/server/internal/handlers/relay.js +115 -2
- package/dist/server/internal/handlers/relay.js.map +1 -1
- package/dist/server/internal/handlers/webAuthn.d.ts +6 -1
- package/dist/server/internal/handlers/webAuthn.d.ts.map +1 -1
- package/dist/server/internal/handlers/webAuthn.js +16 -9
- package/dist/server/internal/handlers/webAuthn.js.map +1 -1
- package/package.json +2 -2
- package/src/cli/adapter.ts +69 -74
- package/src/core/AccessKey.test.ts +525 -559
- package/src/core/AccessKey.ts +323 -327
- package/src/core/Account.test.ts +5 -50
- package/src/core/Account.ts +1 -19
- package/src/core/Adapter.ts +6 -2
- package/src/core/{Provider.test.ts → Provider.localnet.test.ts} +97 -31
- package/src/core/Provider.ts +110 -110
- package/src/core/Schema.test-d.ts +18 -0
- package/src/core/adapters/dialog.test.ts +137 -13
- package/src/core/adapters/dialog.ts +109 -121
- package/src/core/adapters/local.ts +95 -119
- package/src/core/adapters/privy.test-d.ts +128 -0
- package/src/core/adapters/privy.test.ts +634 -0
- package/src/core/adapters/privy.ts +805 -0
- package/src/core/adapters/turnkey.test.ts +117 -13
- package/src/core/adapters/turnkey.ts +132 -149
- package/src/core/internal/AccessKeyTransaction.ts +243 -0
- package/src/core/{mppx.test.ts → mppx.localnet.test.ts} +32 -2
- package/src/core/zod/request.test.ts +36 -0
- package/src/core/zod/rpc.ts +17 -10
- package/src/index.ts +1 -1
- package/src/react-native/adapter.ts +77 -84
- package/src/server/Kv.test.ts +51 -0
- package/src/server/Kv.ts +43 -9
- package/src/server/internal/handlers/auth.test.ts +10 -5
- package/src/server/internal/handlers/auth.ts +3 -0
- package/src/server/internal/handlers/{relay.test.ts → relay.localnet.test.ts} +3 -0
- package/src/server/internal/handlers/relay.ts +138 -13
- package/src/server/internal/handlers/session.test.ts +1 -1
- package/src/server/internal/handlers/webAuthn.test.ts +20 -0
- package/src/server/internal/handlers/webAuthn.ts +24 -12
- /package/src/cli/{Provider.test.ts → Provider.localnet.test.ts} +0 -0
- /package/src/react-native/{Provider.test.ts → Provider.localnet.test.ts} +0 -0
- /package/src/server/internal/handlers/{exchange.test.ts → exchange.localnet.test.ts} +0 -0
package/src/core/Provider.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { announceProvider } from 'mipd'
|
|
2
2
|
import { Mppx, tempo as mppx_tempo } from 'mppx/client'
|
|
3
3
|
import { Address, Hash, Hex, Json, Provider as ox_Provider, RpcResponse } from 'ox'
|
|
4
|
-
import { KeyAuthorization } from 'ox/tempo'
|
|
5
4
|
import { http, parseUnits, type Chain, type Client as ViemClient, type Transport } from 'viem'
|
|
5
|
+
import type { JsonRpcAccount } from 'viem/accounts'
|
|
6
6
|
import { tempo, tempoDevnet, tempoModerato } from 'viem/chains'
|
|
7
7
|
import { parseSiweMessage } from 'viem/siwe'
|
|
8
8
|
import { Actions } from 'viem/tempo'
|
|
@@ -13,6 +13,7 @@ import * as Account from './Account.js'
|
|
|
13
13
|
import type * as Adapter from './Adapter.js'
|
|
14
14
|
import { dialog } from './adapters/dialog.js'
|
|
15
15
|
import * as Client from './Client.js'
|
|
16
|
+
import * as AccessKeyTransaction from './internal/AccessKeyTransaction.js'
|
|
16
17
|
import { withDedupe } from './internal/withDedupe.js'
|
|
17
18
|
import * as Schema from './Schema.js'
|
|
18
19
|
import * as Storage from './Storage.js'
|
|
@@ -25,8 +26,8 @@ export type Provider = ox_Provider.Provider<{ schema: Schema.Ox }> &
|
|
|
25
26
|
ox_Provider.Emitter & {
|
|
26
27
|
/** Configured chains. */
|
|
27
28
|
chains: readonly [Chain, ...Chain[]]
|
|
28
|
-
/** Returns
|
|
29
|
-
getAccount:
|
|
29
|
+
/** Returns the active root account as a viem JSON-RPC account. */
|
|
30
|
+
getAccount(): JsonRpcAccount
|
|
30
31
|
/** Returns local or on-chain publication status for an access key. */
|
|
31
32
|
getAccessKeyStatus(
|
|
32
33
|
options?: getAccessKeyStatus.Options | undefined,
|
|
@@ -271,8 +272,8 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
271
272
|
type FillParams = z.output<typeof Rpc.transactionRequest> & {
|
|
272
273
|
keyAuthorization?: unknown
|
|
273
274
|
}
|
|
275
|
+
const client = getClient({ chainId, feePayer })
|
|
274
276
|
const fill = (params: FillParams) => {
|
|
275
|
-
const client = getClient({ chainId, feePayer })
|
|
276
277
|
const fillRequest = {
|
|
277
278
|
...params,
|
|
278
279
|
chainId: params.chainId ?? client.chain?.id,
|
|
@@ -292,45 +293,38 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
292
293
|
// Inject pending keyAuthorization so the node accounts for
|
|
293
294
|
// key authorization gas during estimation.
|
|
294
295
|
if (!parameters.keyAuthorization) {
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
if (account?.source === 'accessKey') {
|
|
318
|
-
const keyAuth = AccessKey.getPending(account, { store })
|
|
319
|
-
if (keyAuth) {
|
|
296
|
+
const state = store.getState()
|
|
297
|
+
const address =
|
|
298
|
+
parameters.from ?? state.accounts[state.activeAccount]?.address
|
|
299
|
+
if (address) {
|
|
300
|
+
const calls =
|
|
301
|
+
parameters.calls ??
|
|
302
|
+
(parameters.to
|
|
303
|
+
? [
|
|
304
|
+
{
|
|
305
|
+
data: parameters.data,
|
|
306
|
+
to: parameters.to,
|
|
307
|
+
},
|
|
308
|
+
]
|
|
309
|
+
: undefined)
|
|
310
|
+
const transaction = await AccessKeyTransaction.create({
|
|
311
|
+
address,
|
|
312
|
+
calls,
|
|
313
|
+
chainId: parameters.chainId ?? state.chainId,
|
|
314
|
+
client,
|
|
315
|
+
store,
|
|
316
|
+
})
|
|
317
|
+
if (transaction)
|
|
320
318
|
try {
|
|
321
|
-
|
|
319
|
+
return await transaction.fill({
|
|
322
320
|
...parameters,
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
} as never,
|
|
321
|
+
chainId: parameters.chainId ?? state.chainId,
|
|
322
|
+
from: parameters.from ?? address,
|
|
323
|
+
...(feePayer ? { feePayer: true } : {}),
|
|
327
324
|
})
|
|
328
|
-
|
|
329
|
-
} catch (error) {
|
|
330
|
-
AccessKey.invalidate(account, error, { store })
|
|
325
|
+
} catch {
|
|
331
326
|
return await fill(parameters)
|
|
332
327
|
}
|
|
333
|
-
}
|
|
334
328
|
}
|
|
335
329
|
}
|
|
336
330
|
|
|
@@ -574,6 +568,11 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
574
568
|
auth_input as NonNullable<z.output<typeof Rpc.wallet_connect.auth>>,
|
|
575
569
|
)
|
|
576
570
|
: undefined
|
|
571
|
+
if (auth_request && typeof auth_request === 'object' && !auth_request.challenge)
|
|
572
|
+
throw new RpcResponse.InvalidParamsError({
|
|
573
|
+
message:
|
|
574
|
+
'`auth` capability must include either `url` or an explicit `challenge` endpoint.',
|
|
575
|
+
})
|
|
577
576
|
if (auth_request && capabilities?.personalSign)
|
|
578
577
|
throw new RpcResponse.InvalidParamsError({
|
|
579
578
|
message:
|
|
@@ -608,56 +607,61 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
608
607
|
? { message: auth.message }
|
|
609
608
|
: capabilities?.personalSign
|
|
610
609
|
|
|
611
|
-
const {
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
},
|
|
635
|
-
request,
|
|
636
|
-
)
|
|
637
|
-
return await actions.createAccount(
|
|
610
|
+
const {
|
|
611
|
+
accounts,
|
|
612
|
+
auth: auth_capability,
|
|
613
|
+
email,
|
|
614
|
+
keyAuthorization,
|
|
615
|
+
personalSign,
|
|
616
|
+
signature,
|
|
617
|
+
username,
|
|
618
|
+
} = await (async () => {
|
|
619
|
+
if (capabilities?.method === 'register') {
|
|
620
|
+
// If a stored account already has this label, sign in
|
|
621
|
+
// with its credential instead of creating a new one.
|
|
622
|
+
const existing = capabilities.name
|
|
623
|
+
? store
|
|
624
|
+
.getState()
|
|
625
|
+
.accounts.find(
|
|
626
|
+
(a) =>
|
|
627
|
+
'credential' in a &&
|
|
628
|
+
a.label?.toLowerCase() === capabilities.name!.toLowerCase(),
|
|
629
|
+
)
|
|
630
|
+
: undefined
|
|
631
|
+
if (existing && 'credential' in existing)
|
|
632
|
+
return await actions.loadAccounts(
|
|
638
633
|
{
|
|
634
|
+
credentialId: existing.credential?.id,
|
|
639
635
|
digest: capabilities.digest,
|
|
640
636
|
authorizeAccessKey,
|
|
641
|
-
name: capabilities.name ?? 'default',
|
|
642
|
-
userId: capabilities.userId ?? Hex.random(16),
|
|
643
637
|
...(personalSign_request
|
|
644
638
|
? { personalSign: personalSign_request }
|
|
645
639
|
: {}),
|
|
646
640
|
},
|
|
647
641
|
request,
|
|
648
642
|
)
|
|
649
|
-
|
|
650
|
-
return await actions.loadAccounts(
|
|
643
|
+
return await actions.createAccount(
|
|
651
644
|
{
|
|
652
|
-
|
|
653
|
-
digest: capabilities?.digest,
|
|
645
|
+
digest: capabilities.digest,
|
|
654
646
|
authorizeAccessKey,
|
|
655
|
-
|
|
647
|
+
name: capabilities.name ?? 'default',
|
|
648
|
+
userId: capabilities.userId ?? Hex.random(16),
|
|
656
649
|
...(personalSign_request ? { personalSign: personalSign_request } : {}),
|
|
657
650
|
},
|
|
658
651
|
request,
|
|
659
652
|
)
|
|
660
|
-
}
|
|
653
|
+
}
|
|
654
|
+
return await actions.loadAccounts(
|
|
655
|
+
{
|
|
656
|
+
credentialId: capabilities?.credentialId,
|
|
657
|
+
digest: capabilities?.digest,
|
|
658
|
+
authorizeAccessKey,
|
|
659
|
+
selectAccount: capabilities?.selectAccount,
|
|
660
|
+
...(personalSign_request ? { personalSign: personalSign_request } : {}),
|
|
661
|
+
},
|
|
662
|
+
request,
|
|
663
|
+
)
|
|
664
|
+
})()
|
|
661
665
|
|
|
662
666
|
store.setState({
|
|
663
667
|
accounts: resolveAccounts(accounts),
|
|
@@ -713,12 +717,14 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
713
717
|
},
|
|
714
718
|
}
|
|
715
719
|
: {}),
|
|
716
|
-
...(signature && (!auth_request || auth_result)
|
|
720
|
+
...(signature && (!auth_request || auth_result || !verifyUrl)
|
|
717
721
|
? { signature }
|
|
718
722
|
: {}),
|
|
719
723
|
...(email !== undefined ? { email } : {}),
|
|
720
724
|
...(username !== undefined ? { username } : {}),
|
|
721
|
-
...(auth_result
|
|
725
|
+
...((auth_result ?? auth_capability)
|
|
726
|
+
? { auth: auth_result ?? auth_capability }
|
|
727
|
+
: {}),
|
|
722
728
|
...(personalSign
|
|
723
729
|
? { personalSign: { message: personalSign.message } }
|
|
724
730
|
: {}),
|
|
@@ -802,7 +808,7 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
802
808
|
message: '`deposit` not supported by adapter.',
|
|
803
809
|
})
|
|
804
810
|
return (await actions.deposit(
|
|
805
|
-
request._decoded.params[0],
|
|
811
|
+
request._decoded.params?.[0] ?? {},
|
|
806
812
|
request,
|
|
807
813
|
)) satisfies Rpc.wallet_deposit.Encoded['returns']
|
|
808
814
|
}
|
|
@@ -973,15 +979,20 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
973
979
|
),
|
|
974
980
|
{
|
|
975
981
|
chains,
|
|
976
|
-
getAccount
|
|
982
|
+
getAccount() {
|
|
983
|
+
const account = getAccount()
|
|
984
|
+
return { address: account.address, type: 'json-rpc' as const }
|
|
985
|
+
},
|
|
977
986
|
async getAccessKeyStatus(options: getAccessKeyStatus.Options = {}) {
|
|
978
987
|
const state = store.getState()
|
|
979
988
|
const address = options.address ?? state.accounts[state.activeAccount]?.address
|
|
980
|
-
if (!address) return
|
|
989
|
+
if (!address) return 'missing'
|
|
981
990
|
const chainId = options.chainId ?? state.chainId
|
|
991
|
+
const { accessKey, calls } = options
|
|
982
992
|
return await AccessKey.getStatus({
|
|
983
|
-
|
|
984
|
-
|
|
993
|
+
account: address,
|
|
994
|
+
...(accessKey ? { accessKey } : {}),
|
|
995
|
+
...(calls ? { calls } : {}),
|
|
985
996
|
chainId,
|
|
986
997
|
client: provider.getClient({ chainId }),
|
|
987
998
|
store,
|
|
@@ -1031,31 +1042,22 @@ export function create(options: create.Options = {}): create.ReturnType {
|
|
|
1031
1042
|
const polyfill = polyfill_option ?? isFetchWritable()
|
|
1032
1043
|
const getClient = ({ chainId }: { chainId?: number | undefined }) => {
|
|
1033
1044
|
const client = provider.getClient({ chainId })
|
|
1034
|
-
const account =
|
|
1035
|
-
|
|
1045
|
+
const account = store.getState().accounts[store.getState().activeAccount]
|
|
1046
|
+
if (!account) throw new ox_Provider.DisconnectedError({ message: 'No active account.' })
|
|
1047
|
+
return Object.assign(client, {
|
|
1048
|
+
account: {
|
|
1049
|
+
address: account.address,
|
|
1050
|
+
type: 'json-rpc' as const,
|
|
1051
|
+
},
|
|
1052
|
+
})
|
|
1036
1053
|
}
|
|
1037
|
-
|
|
1054
|
+
Mppx.create({
|
|
1038
1055
|
methods: [
|
|
1039
1056
|
mppx_tempo({ ...methodOptions, getClient, mode }),
|
|
1040
1057
|
mppx_tempo.subscription({ getClient }),
|
|
1041
1058
|
],
|
|
1042
1059
|
polyfill,
|
|
1043
1060
|
})
|
|
1044
|
-
mppx.onPaymentResponse(({ challenge, method }) => {
|
|
1045
|
-
if (method.name !== 'tempo' || method.intent !== 'charge') return
|
|
1046
|
-
const amount = challenge.request.amount
|
|
1047
|
-
if (
|
|
1048
|
-
typeof amount !== 'string' &&
|
|
1049
|
-
typeof amount !== 'number' &&
|
|
1050
|
-
typeof amount !== 'bigint' &&
|
|
1051
|
-
typeof amount !== 'boolean'
|
|
1052
|
-
)
|
|
1053
|
-
return
|
|
1054
|
-
if (BigInt(amount) === 0n) return
|
|
1055
|
-
const account = provider.getAccount()
|
|
1056
|
-
if ('source' in account && account.source === 'accessKey')
|
|
1057
|
-
AccessKey.removePending(account, { store })
|
|
1058
|
-
})
|
|
1059
1061
|
}
|
|
1060
1062
|
|
|
1061
1063
|
providerRef = provider
|
|
@@ -1161,7 +1163,7 @@ export declare namespace getAccessKeyStatus {
|
|
|
1161
1163
|
}
|
|
1162
1164
|
|
|
1163
1165
|
/** Access-key publication status. */
|
|
1164
|
-
type ReturnType =
|
|
1166
|
+
type ReturnType = 'missing' | 'pending' | 'published' | 'expired'
|
|
1165
1167
|
}
|
|
1166
1168
|
|
|
1167
1169
|
export declare namespace mpp {
|
|
@@ -1266,14 +1268,16 @@ function resolveAuthEndpoint(
|
|
|
1266
1268
|
}
|
|
1267
1269
|
|
|
1268
1270
|
/**
|
|
1269
|
-
* Pre-resolves the `auth` capability into its absolute
|
|
1270
|
-
*
|
|
1271
|
-
*
|
|
1272
|
-
*
|
|
1273
|
-
*
|
|
1271
|
+
* Pre-resolves the `auth` capability into its absolute object form. Run
|
|
1272
|
+
* once at the dapp-side Provider so forwarding adapters (dialog) carry
|
|
1273
|
+
* absolute URLs to the wallet host — the wallet's `window.location.origin`
|
|
1274
|
+
* belongs to the wallet, not the dapp, and cannot resolve relative paths
|
|
1275
|
+
* correctly.
|
|
1274
1276
|
*
|
|
1275
|
-
*
|
|
1276
|
-
*
|
|
1277
|
+
* Individual endpoints are omitted when the input doesn't supply enough
|
|
1278
|
+
* info to derive them. `logout` is optional in the protocol; `verify` can
|
|
1279
|
+
* also be omitted by wallet-host re-entry so the dapp-origin Provider runs
|
|
1280
|
+
* verification and receives the session cookie.
|
|
1277
1281
|
*/
|
|
1278
1282
|
function absolutizeAuth(
|
|
1279
1283
|
auth: NonNullable<z.output<typeof Rpc.wallet_connect.auth>>,
|
|
@@ -1297,10 +1301,6 @@ function absolutizeAuth(
|
|
|
1297
1301
|
|
|
1298
1302
|
function assertSameAuthOrigin(auth: NonNullable<z.output<typeof Rpc.wallet_connect.auth>>): void {
|
|
1299
1303
|
if (typeof auth !== 'object') return
|
|
1300
|
-
if (!auth.challenge || !auth.verify)
|
|
1301
|
-
throw new RpcResponse.InvalidParamsError({
|
|
1302
|
-
message: '`auth` requires both `challenge` and `verify` endpoints.',
|
|
1303
|
-
})
|
|
1304
1304
|
const urls = [auth.challenge, auth.verify, auth.logout].filter(
|
|
1305
1305
|
(u): u is string => typeof u === 'string',
|
|
1306
1306
|
)
|
|
@@ -156,6 +156,24 @@ describe('Encoded', () => {
|
|
|
156
156
|
}>()
|
|
157
157
|
})
|
|
158
158
|
|
|
159
|
+
test('wallet_deposit', () => {
|
|
160
|
+
expectTypeOf<Rpc.wallet_deposit.Encoded>().toMatchTypeOf<{
|
|
161
|
+
method: 'wallet_deposit'
|
|
162
|
+
params:
|
|
163
|
+
| readonly [
|
|
164
|
+
{
|
|
165
|
+
address?: Hex | undefined
|
|
166
|
+
amount?: string | undefined
|
|
167
|
+
chainId?: Hex | undefined
|
|
168
|
+
displayName?: string | undefined
|
|
169
|
+
token?: Hex | string | undefined
|
|
170
|
+
},
|
|
171
|
+
]
|
|
172
|
+
| undefined
|
|
173
|
+
returns: { receipts?: readonly { transactionHash: Hex }[] | undefined } | undefined
|
|
174
|
+
}>()
|
|
175
|
+
})
|
|
176
|
+
|
|
159
177
|
test('wallet_switchEthereumChain', () => {
|
|
160
178
|
expectTypeOf<Rpc.wallet_switchEthereumChain.Encoded>().toEqualTypeOf<{
|
|
161
179
|
method: 'wallet_switchEthereumChain'
|
|
@@ -2,8 +2,8 @@ import { Provider as ox_Provider } from 'ox'
|
|
|
2
2
|
import { tempoLocalnet } from 'viem/chains'
|
|
3
3
|
import { afterEach, describe, expect, test, vi } from 'vp/test'
|
|
4
4
|
|
|
5
|
-
import * as AccessKey from '../AccessKey.js'
|
|
6
5
|
import * as Dialog from '../Dialog.js'
|
|
6
|
+
import * as AccessKeyTransaction from '../internal/AccessKeyTransaction.js'
|
|
7
7
|
import * as Storage from '../Storage.js'
|
|
8
8
|
import * as Store from '../Store.js'
|
|
9
9
|
import { dialog } from './dialog.js'
|
|
@@ -21,16 +21,24 @@ describe('dialog', () => {
|
|
|
21
21
|
const store = Store.create({ chainId: tempoLocalnet.id, storage })
|
|
22
22
|
const clientRequests: unknown[] = []
|
|
23
23
|
const signRequests: unknown[] = []
|
|
24
|
-
vi.spyOn(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
24
|
+
vi.spyOn(AccessKeyTransaction, 'create').mockResolvedValue({
|
|
25
|
+
fill: async () => ({ capabilities: { sponsored: false }, tx: {} }),
|
|
26
|
+
prepare: async (request) => ({
|
|
27
|
+
request: request as never,
|
|
28
|
+
send: async () => {
|
|
29
|
+
signRequests.push(request)
|
|
30
|
+
clientRequests.push({ method: 'eth_sendRawTransaction', params: ['0xsigned'] })
|
|
31
|
+
return '0xtransaction'
|
|
32
|
+
},
|
|
33
|
+
sendSync: async () => {
|
|
34
|
+
throw new Error('unexpected sendSync')
|
|
35
|
+
},
|
|
36
|
+
sign: async () => {
|
|
37
|
+
signRequests.push(request)
|
|
38
|
+
return '0xsigned'
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
})
|
|
34
42
|
const adapter = dialog({ dialog: Dialog.noop() })({
|
|
35
43
|
getAccount: () => {
|
|
36
44
|
throw new ox_Provider.UnauthorizedError({ message: 'No local signer.' })
|
|
@@ -84,10 +92,76 @@ describe('dialog', () => {
|
|
|
84
92
|
expect(store.getState().requestQueue).toMatchInlineSnapshot(`[]`)
|
|
85
93
|
})
|
|
86
94
|
|
|
95
|
+
test('behavior: loadAccounts forwards auth capabilities returned by the dialog', async () => {
|
|
96
|
+
const storage = Storage.memory()
|
|
97
|
+
const store = Store.create({ chainId: tempoLocalnet.id, storage })
|
|
98
|
+
const adapter = dialog({ dialog: Dialog.noop() })({
|
|
99
|
+
getAccount: () => {
|
|
100
|
+
throw new ox_Provider.UnauthorizedError({ message: 'No local signer.' })
|
|
101
|
+
},
|
|
102
|
+
getClient: () => ({}) as never,
|
|
103
|
+
storage,
|
|
104
|
+
store,
|
|
105
|
+
})
|
|
106
|
+
const request = {
|
|
107
|
+
method: 'wallet_connect' as const,
|
|
108
|
+
params: [
|
|
109
|
+
{
|
|
110
|
+
capabilities: {
|
|
111
|
+
auth: {
|
|
112
|
+
url: 'https://app.example/auth',
|
|
113
|
+
returnToken: true,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
chainId: '0x1079' as const,
|
|
117
|
+
},
|
|
118
|
+
] as const,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const promise = adapter.actions.loadAccounts(undefined, request)
|
|
122
|
+
|
|
123
|
+
await vi.waitFor(() => {
|
|
124
|
+
if (!store.getState().requestQueue[0]) throw new Error('request not queued')
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
const queued = store.getState().requestQueue[0]!
|
|
128
|
+
store.setState({
|
|
129
|
+
requestQueue: [
|
|
130
|
+
{
|
|
131
|
+
request: queued.request,
|
|
132
|
+
result: {
|
|
133
|
+
accounts: [
|
|
134
|
+
{
|
|
135
|
+
address,
|
|
136
|
+
capabilities: {
|
|
137
|
+
auth: { token: 'test-token' },
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
],
|
|
141
|
+
},
|
|
142
|
+
status: 'success',
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
await expect(promise).resolves.toMatchInlineSnapshot(`
|
|
148
|
+
{
|
|
149
|
+
"accounts": [
|
|
150
|
+
{
|
|
151
|
+
"address": "0x0000000000000000000000000000000000000001",
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
"auth": {
|
|
155
|
+
"token": "test-token",
|
|
156
|
+
},
|
|
157
|
+
}
|
|
158
|
+
`)
|
|
159
|
+
})
|
|
160
|
+
|
|
87
161
|
test('behavior: sendTransaction falls through when no access key is selected', async () => {
|
|
88
162
|
const storage = Storage.memory()
|
|
89
163
|
const store = Store.create({ chainId: tempoLocalnet.id, storage })
|
|
90
|
-
vi.spyOn(
|
|
164
|
+
vi.spyOn(AccessKeyTransaction, 'create').mockResolvedValue(undefined)
|
|
91
165
|
const lookups: unknown[] = []
|
|
92
166
|
const adapter = dialog({ dialog: Dialog.noop() })({
|
|
93
167
|
getAccount: (options) => {
|
|
@@ -137,10 +211,60 @@ describe('dialog', () => {
|
|
|
137
211
|
expect(lookups).toMatchInlineSnapshot(`[]`)
|
|
138
212
|
})
|
|
139
213
|
|
|
214
|
+
test('behavior: revokeAccessKey clears the forwarded key from local state', async () => {
|
|
215
|
+
const storage = Storage.memory()
|
|
216
|
+
const store = Store.create({ chainId: tempoLocalnet.id, storage })
|
|
217
|
+
store.setState({
|
|
218
|
+
accessKeys: [
|
|
219
|
+
{
|
|
220
|
+
access: address,
|
|
221
|
+
address: recipient,
|
|
222
|
+
chainId: tempoLocalnet.id,
|
|
223
|
+
keyType: 'p256',
|
|
224
|
+
} as never,
|
|
225
|
+
],
|
|
226
|
+
})
|
|
227
|
+
const adapter = dialog({ dialog: Dialog.noop() })({
|
|
228
|
+
getAccount: () => {
|
|
229
|
+
throw new ox_Provider.UnauthorizedError({ message: 'No local signer.' })
|
|
230
|
+
},
|
|
231
|
+
getClient: () => ({}) as never,
|
|
232
|
+
storage,
|
|
233
|
+
store,
|
|
234
|
+
})
|
|
235
|
+
const request = {
|
|
236
|
+
method: 'wallet_revokeAccessKey' as const,
|
|
237
|
+
params: [{ accessKeyAddress: recipient, address }] as const,
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const promise = adapter.actions.revokeAccessKey!(
|
|
241
|
+
{ accessKeyAddress: recipient, address },
|
|
242
|
+
request,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
await vi.waitFor(() => {
|
|
246
|
+
if (!store.getState().requestQueue[0]) throw new Error('request not queued')
|
|
247
|
+
})
|
|
248
|
+
|
|
249
|
+
const queued = store.getState().requestQueue[0]!
|
|
250
|
+
store.setState({
|
|
251
|
+
requestQueue: [
|
|
252
|
+
{
|
|
253
|
+
request: queued.request,
|
|
254
|
+
result: undefined,
|
|
255
|
+
status: 'success',
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
await expect(promise).resolves.toMatchInlineSnapshot(`undefined`)
|
|
261
|
+
expect(store.getState().accessKeys).toMatchInlineSnapshot(`[]`)
|
|
262
|
+
})
|
|
263
|
+
|
|
140
264
|
test('error: wallet validation errors keep their RPC code', async () => {
|
|
141
265
|
const storage = Storage.memory()
|
|
142
266
|
const store = Store.create({ chainId: tempoLocalnet.id, storage })
|
|
143
|
-
vi.spyOn(
|
|
267
|
+
vi.spyOn(AccessKeyTransaction, 'create').mockResolvedValue(undefined)
|
|
144
268
|
const adapter = dialog({ dialog: Dialog.noop() })({
|
|
145
269
|
getAccount: () => {
|
|
146
270
|
throw new ox_Provider.UnauthorizedError({ message: 'No local signer.' })
|