accounts 0.12.2 → 0.14.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 +35 -0
- package/README.md +6 -5
- package/dist/cli/adapter.d.ts.map +1 -1
- package/dist/cli/adapter.js +76 -69
- 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 +230 -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 +24 -4
- package/dist/core/Adapter.d.ts.map +1 -1
- package/dist/core/Client.d.ts +1 -1
- package/dist/core/Client.d.ts.map +1 -1
- package/dist/core/Client.js.map +1 -1
- package/dist/core/Provider.d.ts +6 -7
- package/dist/core/Provider.d.ts.map +1 -1
- package/dist/core/Provider.js +73 -77
- package/dist/core/Provider.js.map +1 -1
- package/dist/core/Remote.d.ts +2 -0
- package/dist/core/Remote.d.ts.map +1 -1
- package/dist/core/Remote.js +3 -1
- package/dist/core/Remote.js.map +1 -1
- package/dist/core/Schema.d.ts +17 -7
- package/dist/core/Schema.d.ts.map +1 -1
- package/dist/core/adapters/dialog.d.ts.map +1 -1
- package/dist/core/adapters/dialog.js +126 -131
- 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 +578 -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 +71 -4
- package/dist/core/zod/rpc.d.ts.map +1 -1
- package/dist/core/zod/rpc.js +33 -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 +86 -75
- package/dist/react-native/adapter.js.map +1 -1
- package/dist/server/CliAuth.d.ts +55 -0
- package/dist/server/CliAuth.d.ts.map +1 -1
- package/dist/server/CliAuth.js +18 -1
- package/dist/server/CliAuth.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 +3 -1
- package/dist/server/internal/handlers/auth.js.map +1 -1
- package/dist/server/internal/handlers/codeAuth.js +1 -1
- package/dist/server/internal/handlers/codeAuth.js.map +1 -1
- package/dist/server/internal/handlers/exchange.js +1 -1
- package/dist/server/internal/handlers/exchange.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 +116 -3
- 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 +3 -3
- package/src/cli/{Provider.test.ts → Provider.localnet.test.ts} +49 -1
- package/src/cli/adapter.ts +95 -76
- package/src/core/AccessKey.test.ts +563 -560
- package/src/core/AccessKey.ts +327 -327
- package/src/core/Account.test.ts +6 -51
- package/src/core/Account.ts +1 -19
- package/src/core/Adapter.ts +22 -4
- package/src/core/Client.test.ts +1 -1
- package/src/core/Client.ts +1 -1
- package/src/core/Provider.connect.browser.test.ts +1 -1
- package/src/core/{Provider.test.ts → Provider.localnet.test.ts} +98 -32
- package/src/core/Provider.ts +118 -112
- package/src/core/Remote.test.ts +28 -0
- package/src/core/Remote.ts +4 -1
- package/src/core/Schema.test-d.ts +41 -0
- package/src/core/adapters/dialog.test.ts +335 -15
- package/src/core/adapters/dialog.ts +120 -127
- package/src/core/adapters/local.test.ts +1 -1
- 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 +648 -0
- package/src/core/adapters/privy.ts +811 -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.test.ts +139 -0
- package/src/core/zod/rpc.ts +44 -10
- package/src/index.ts +1 -1
- package/src/react-native/{Provider.test.ts → Provider.localnet.test.ts} +65 -0
- package/src/react-native/adapter.ts +107 -87
- package/src/server/CliAuth.test-d.ts +23 -0
- package/src/server/CliAuth.test.ts +47 -0
- package/src/server/CliAuth.ts +20 -1
- package/src/server/Handler.test-d.ts +1 -1
- 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 +4 -1
- package/src/server/internal/handlers/codeAuth.ts +1 -1
- package/src/server/internal/handlers/exchange.ts +1 -1
- package/src/server/internal/handlers/{relay.test.ts → relay.localnet.test.ts} +4 -1
- package/src/server/internal/handlers/relay.ts +139 -14
- 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/server/internal/handlers/{exchange.test.ts → exchange.localnet.test.ts} +0 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { Address, Hex } from 'ox'
|
|
2
|
+
import { KeyAuthorization } from 'ox/tempo'
|
|
3
|
+
import type { Client, Transport } from 'viem'
|
|
4
|
+
import { prepareTransactionRequest } from 'viem/actions'
|
|
5
|
+
import type { PrepareTransactionRequestReturnType } from 'viem/actions'
|
|
6
|
+
import type { Transaction as TempoTransaction } from 'viem/tempo'
|
|
7
|
+
|
|
8
|
+
import * as AccessKey from '../AccessKey.js'
|
|
9
|
+
import * as ExecutionError from '../ExecutionError.js'
|
|
10
|
+
import type * as Store from '../Store.js'
|
|
11
|
+
import type * as Rpc from '../zod/rpc.js'
|
|
12
|
+
|
|
13
|
+
type Call = {
|
|
14
|
+
to?: Address.Address | undefined
|
|
15
|
+
data?: Hex.Hex | undefined
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type Selection = NonNullable<Awaited<ReturnType<typeof AccessKey.select>>>
|
|
19
|
+
|
|
20
|
+
const removalErrorNames = new Set([
|
|
21
|
+
'InvalidSignature',
|
|
22
|
+
'InvalidSignatureFormat',
|
|
23
|
+
'InvalidSignatureType',
|
|
24
|
+
'KeyAlreadyRevoked',
|
|
25
|
+
'KeyExpired',
|
|
26
|
+
'KeyNotFound',
|
|
27
|
+
'SignatureTypeMismatch',
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
/** Creates a lifecycle-aware access-key transaction when a matching key is available. */
|
|
31
|
+
export async function create(options: create.Options): Promise<create.ReturnType> {
|
|
32
|
+
const { address, calls, chainId, client, store } = options
|
|
33
|
+
const selection = await AccessKey.select({
|
|
34
|
+
account: address,
|
|
35
|
+
calls,
|
|
36
|
+
chainId,
|
|
37
|
+
client,
|
|
38
|
+
store,
|
|
39
|
+
})
|
|
40
|
+
if (!selection) return undefined
|
|
41
|
+
return createTransaction({ client, selection, store })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export declare namespace create {
|
|
45
|
+
/** Options for {@link create}. */
|
|
46
|
+
type Options = {
|
|
47
|
+
/** Root account address. */
|
|
48
|
+
address: Address.Address
|
|
49
|
+
/** Calls to match against access key scopes. */
|
|
50
|
+
calls?: readonly Call[] | undefined
|
|
51
|
+
/** Chain ID the access key must be authorized on. */
|
|
52
|
+
chainId: number
|
|
53
|
+
/** Client used to prepare, submit, and check access-key transactions. */
|
|
54
|
+
client: Client<Transport>
|
|
55
|
+
/** Reactive state store. */
|
|
56
|
+
store: Store.Store
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Parameters accepted when preparing an access-key transaction. */
|
|
60
|
+
type PrepareParameters = Omit<
|
|
61
|
+
TempoTransaction.TransactionRequestTempo,
|
|
62
|
+
'account' | 'keyAuthorization' | 'type'
|
|
63
|
+
>
|
|
64
|
+
|
|
65
|
+
/** Prepared transaction request returned by viem. */
|
|
66
|
+
type PreparedRequest = PrepareTransactionRequestReturnType
|
|
67
|
+
|
|
68
|
+
/** Parameters accepted by `eth_fillTransaction`. */
|
|
69
|
+
type FillParameters = Rpc.eth_fillTransaction.Decoded['params'][0]
|
|
70
|
+
|
|
71
|
+
/** Result returned by `eth_fillTransaction`. */
|
|
72
|
+
type FillReturnType = Rpc.eth_fillTransaction.Encoded['returns']
|
|
73
|
+
|
|
74
|
+
/** Result returned by `eth_sendTransactionSync`. */
|
|
75
|
+
type SendSyncReturnType = Rpc.eth_sendTransactionSync.Encoded['returns']
|
|
76
|
+
|
|
77
|
+
/** Prepared access-key transaction with lifecycle-aware execution methods. */
|
|
78
|
+
type Prepared = {
|
|
79
|
+
/** Prepared request that will be signed by the selected access key. */
|
|
80
|
+
request: PreparedRequest
|
|
81
|
+
/** Signs the prepared transaction and marks an attached authorization as pending. */
|
|
82
|
+
sign(): Promise<Hex.Hex>
|
|
83
|
+
/** Signs and submits the transaction asynchronously. */
|
|
84
|
+
send(): Promise<Hex.Hex>
|
|
85
|
+
/** Signs, submits, and waits for the transaction to be accepted. */
|
|
86
|
+
sendSync(): Promise<SendSyncReturnType>
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Lifecycle-aware access-key transaction. */
|
|
90
|
+
type Transaction = {
|
|
91
|
+
/** Fills a transaction, attaching a pending key authorization when needed. */
|
|
92
|
+
fill(parameters: FillParameters): Promise<FillReturnType>
|
|
93
|
+
/** Prepares a transaction, attaching a pending key authorization when needed. */
|
|
94
|
+
prepare(parameters: PrepareParameters): Promise<Prepared>
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Lifecycle-aware access-key transaction, if one is available. */
|
|
98
|
+
type ReturnType = Transaction | undefined
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function createTransaction(options: {
|
|
102
|
+
client: Client<Transport>
|
|
103
|
+
selection: Selection
|
|
104
|
+
store: Store.Store
|
|
105
|
+
}): create.Transaction {
|
|
106
|
+
const { client, selection, store } = options
|
|
107
|
+
return {
|
|
108
|
+
async fill(parameters) {
|
|
109
|
+
try {
|
|
110
|
+
return await fillTransaction(client, {
|
|
111
|
+
...parameters,
|
|
112
|
+
...(!parameters.keyAuthorization && selection.authorization
|
|
113
|
+
? {
|
|
114
|
+
keyAuthorization: {
|
|
115
|
+
address: selection.authorization.address,
|
|
116
|
+
...KeyAuthorization.toRpc(selection.authorization),
|
|
117
|
+
} as never,
|
|
118
|
+
}
|
|
119
|
+
: {}),
|
|
120
|
+
} as never)
|
|
121
|
+
} catch (error) {
|
|
122
|
+
removeForError(error, selection, { store })
|
|
123
|
+
throw error
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
async prepare(parameters) {
|
|
127
|
+
try {
|
|
128
|
+
const request = await prepareTransactionRequest(client, {
|
|
129
|
+
account: selection.account,
|
|
130
|
+
...parameters,
|
|
131
|
+
...(selection.authorization ? { keyAuthorization: selection.authorization } : {}),
|
|
132
|
+
type: 'tempo',
|
|
133
|
+
} as never)
|
|
134
|
+
return createPreparedTransaction({
|
|
135
|
+
client,
|
|
136
|
+
request: request as never,
|
|
137
|
+
selection,
|
|
138
|
+
store,
|
|
139
|
+
})
|
|
140
|
+
} catch (error) {
|
|
141
|
+
removeForError(error, selection, { store })
|
|
142
|
+
throw error
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function createPreparedTransaction(options: {
|
|
149
|
+
client: Client<Transport>
|
|
150
|
+
request: create.PreparedRequest
|
|
151
|
+
selection: Selection
|
|
152
|
+
store: Store.Store
|
|
153
|
+
}): create.Prepared {
|
|
154
|
+
const { client, request, selection, store } = options
|
|
155
|
+
|
|
156
|
+
async function sign() {
|
|
157
|
+
try {
|
|
158
|
+
const signed = await selection.account.signTransaction(request as never)
|
|
159
|
+
if (selection.authorization)
|
|
160
|
+
AccessKey.markPending({
|
|
161
|
+
accessKey: selection.accessKey,
|
|
162
|
+
account: selection.record.access,
|
|
163
|
+
chainId: selection.record.chainId,
|
|
164
|
+
store,
|
|
165
|
+
})
|
|
166
|
+
return signed
|
|
167
|
+
} catch (error) {
|
|
168
|
+
removeForError(error, selection, { store })
|
|
169
|
+
throw error
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
request,
|
|
175
|
+
sign,
|
|
176
|
+
async send() {
|
|
177
|
+
try {
|
|
178
|
+
const signed = await sign()
|
|
179
|
+
return (await client.request({
|
|
180
|
+
method: 'eth_sendRawTransaction' as never,
|
|
181
|
+
params: [signed],
|
|
182
|
+
})) as Hex.Hex
|
|
183
|
+
} catch (error) {
|
|
184
|
+
removeForError(error, selection, { store })
|
|
185
|
+
throw error
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
async sendSync() {
|
|
189
|
+
try {
|
|
190
|
+
const signed = await sign()
|
|
191
|
+
const result = await client.request({
|
|
192
|
+
method: 'eth_sendRawTransactionSync' as never,
|
|
193
|
+
params: [signed],
|
|
194
|
+
})
|
|
195
|
+
AccessKey.markPublished({
|
|
196
|
+
accessKey: selection.accessKey,
|
|
197
|
+
account: selection.record.access,
|
|
198
|
+
chainId: selection.record.chainId,
|
|
199
|
+
store,
|
|
200
|
+
})
|
|
201
|
+
return result as create.SendSyncReturnType
|
|
202
|
+
} catch (error) {
|
|
203
|
+
removeForError(error, selection, { store })
|
|
204
|
+
throw error
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function fillTransaction(
|
|
211
|
+
client: Client<Transport>,
|
|
212
|
+
parameters: create.FillParameters,
|
|
213
|
+
): Promise<create.FillReturnType> {
|
|
214
|
+
const { keyAuthorization, ...rest } = parameters as create.FillParameters & {
|
|
215
|
+
keyAuthorization?: unknown
|
|
216
|
+
}
|
|
217
|
+
const formatter = client.chain?.formatters?.transactionRequest
|
|
218
|
+
const formatted = formatter ? formatter.format({ ...rest } as never, 'fillTransaction') : rest
|
|
219
|
+
return (await client.request({
|
|
220
|
+
method: 'eth_fillTransaction' as never,
|
|
221
|
+
params: [{ ...formatted, ...(keyAuthorization ? { keyAuthorization } : {}) } as never],
|
|
222
|
+
})) as create.FillReturnType
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function removeForError(
|
|
226
|
+
error: unknown,
|
|
227
|
+
selection: Selection,
|
|
228
|
+
options: { store: Store.Store },
|
|
229
|
+
): void {
|
|
230
|
+
if (!shouldRemoveForError(error)) return
|
|
231
|
+
AccessKey.remove({
|
|
232
|
+
accessKey: selection.accessKey,
|
|
233
|
+
account: selection.record.access,
|
|
234
|
+
chainId: selection.record.chainId,
|
|
235
|
+
store: options.store,
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function shouldRemoveForError(error: unknown): boolean {
|
|
240
|
+
if (!(error instanceof Error)) return false
|
|
241
|
+
const parsed = ExecutionError.parse(error)
|
|
242
|
+
return removalErrorNames.has(parsed.errorName)
|
|
243
|
+
}
|
|
@@ -66,7 +66,7 @@ describe('mppx integration', () => {
|
|
|
66
66
|
`)
|
|
67
67
|
})
|
|
68
68
|
|
|
69
|
-
test('pull mode publishes a pending access key
|
|
69
|
+
test('pull mode publishes a pending access key authorization', async () => {
|
|
70
70
|
const provider = Provider.create({
|
|
71
71
|
adapter: headlessWebAuthn(),
|
|
72
72
|
chains: [chain],
|
|
@@ -85,6 +85,9 @@ describe('mppx integration', () => {
|
|
|
85
85
|
|
|
86
86
|
const res = await fetch(`${server.url}/fortune`)
|
|
87
87
|
expect(res.status).toBe(200)
|
|
88
|
+
|
|
89
|
+
const status = await provider.getAccessKeyStatus({ accessKey: key.address })
|
|
90
|
+
expect(status).toMatchInlineSnapshot(`"published"`)
|
|
88
91
|
expect(provider.store.getState().accessKeys[0]!.keyAuthorization).toBeUndefined()
|
|
89
92
|
|
|
90
93
|
const metadata = await Actions.accessKey.getMetadata(client, {
|
|
@@ -94,7 +97,7 @@ describe('mppx integration', () => {
|
|
|
94
97
|
expect(metadata.isRevoked).toMatchInlineSnapshot(`false`)
|
|
95
98
|
})
|
|
96
99
|
|
|
97
|
-
test('pull mode keeps
|
|
100
|
+
test('pull mode keeps access key authorization pending after failed verification', async () => {
|
|
98
101
|
const failingServer = await createServer(async (req, res) => {
|
|
99
102
|
if (req.headers.authorization) {
|
|
100
103
|
res.writeHead(402, { 'Content-Type': 'application/json' })
|
|
@@ -122,14 +125,41 @@ describe('mppx integration', () => {
|
|
|
122
125
|
method: 'wallet_authorizeAccessKey',
|
|
123
126
|
params: [{ expiry: Expiry.days(1) }],
|
|
124
127
|
})
|
|
128
|
+
const key = provider.store.getState().accessKeys[0]!
|
|
125
129
|
|
|
126
130
|
const res = await fetch(`${failingServer.url}/fortune`)
|
|
127
131
|
expect(res.status).toMatchInlineSnapshot(`402`)
|
|
128
132
|
expect(provider.store.getState().accessKeys[0]!.keyAuthorization).toBeDefined()
|
|
133
|
+
|
|
134
|
+
const status = await provider.getAccessKeyStatus({ accessKey: key.address })
|
|
135
|
+
expect(status).toMatchInlineSnapshot(`"pending"`)
|
|
129
136
|
} finally {
|
|
130
137
|
await failingServer.closeAsync()
|
|
131
138
|
}
|
|
132
139
|
})
|
|
140
|
+
|
|
141
|
+
test('push mode publishes pending access key authorization', async () => {
|
|
142
|
+
const provider = Provider.create({
|
|
143
|
+
adapter: headlessWebAuthn(),
|
|
144
|
+
chains: [chain],
|
|
145
|
+
mpp: { mode: 'push' },
|
|
146
|
+
})
|
|
147
|
+
const address = await connect(provider)
|
|
148
|
+
await fund(address)
|
|
149
|
+
|
|
150
|
+
await provider.request({
|
|
151
|
+
method: 'wallet_authorizeAccessKey',
|
|
152
|
+
params: [{ expiry: Expiry.days(1) }],
|
|
153
|
+
})
|
|
154
|
+
const key = provider.store.getState().accessKeys[0]!
|
|
155
|
+
|
|
156
|
+
const res = await fetch(`${server.url}/fortune`)
|
|
157
|
+
expect(res.status).toMatchInlineSnapshot(`200`)
|
|
158
|
+
|
|
159
|
+
const status = await provider.getAccessKeyStatus({ accessKey: key.address })
|
|
160
|
+
expect(status).toMatchInlineSnapshot(`"published"`)
|
|
161
|
+
expect(provider.store.getState().accessKeys[0]!.keyAuthorization).toBeUndefined()
|
|
162
|
+
})
|
|
133
163
|
})
|
|
134
164
|
|
|
135
165
|
async function connect(provider: ReturnType<typeof Provider.create>) {
|
|
@@ -115,6 +115,42 @@ describe('validate', () => {
|
|
|
115
115
|
`)
|
|
116
116
|
})
|
|
117
117
|
|
|
118
|
+
test('default: validates wallet_deposit with amount and token symbol', () => {
|
|
119
|
+
const result = RpcRequest.validate(Schema.Request, {
|
|
120
|
+
method: 'wallet_deposit',
|
|
121
|
+
params: [
|
|
122
|
+
{
|
|
123
|
+
amount: '50',
|
|
124
|
+
displayName: 'DoorDash',
|
|
125
|
+
token: 'USDC',
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
})
|
|
129
|
+
expect(result._decoded).toMatchInlineSnapshot(`
|
|
130
|
+
{
|
|
131
|
+
"method": "wallet_deposit",
|
|
132
|
+
"params": [
|
|
133
|
+
{
|
|
134
|
+
"amount": "50",
|
|
135
|
+
"displayName": "DoorDash",
|
|
136
|
+
"token": "USDC",
|
|
137
|
+
},
|
|
138
|
+
],
|
|
139
|
+
}
|
|
140
|
+
`)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
test('default: validates wallet_deposit without params', () => {
|
|
144
|
+
const result = RpcRequest.validate(Schema.Request, {
|
|
145
|
+
method: 'wallet_deposit',
|
|
146
|
+
})
|
|
147
|
+
expect(result._decoded).toMatchInlineSnapshot(`
|
|
148
|
+
{
|
|
149
|
+
"method": "wallet_deposit",
|
|
150
|
+
}
|
|
151
|
+
`)
|
|
152
|
+
})
|
|
153
|
+
|
|
118
154
|
test('behavior: preserves original request properties', () => {
|
|
119
155
|
const result = RpcRequest.validate(Schema.Request, {
|
|
120
156
|
method: 'eth_accounts',
|
package/src/core/zod/rpc.test.ts
CHANGED
|
@@ -200,6 +200,72 @@ describe('wallet_connect.capabilities.result: auth + personalSign', () => {
|
|
|
200
200
|
})
|
|
201
201
|
})
|
|
202
202
|
|
|
203
|
+
describe('wallet_connect.capabilities.request: showDeposit', () => {
|
|
204
|
+
test('accepts true on the register branch', () => {
|
|
205
|
+
expect(
|
|
206
|
+
z.parse(Rpc.wallet_connect.capabilities.request, {
|
|
207
|
+
method: 'register',
|
|
208
|
+
showDeposit: true,
|
|
209
|
+
}),
|
|
210
|
+
).toEqual({
|
|
211
|
+
method: 'register',
|
|
212
|
+
showDeposit: true,
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
test('accepts false on the register branch', () => {
|
|
217
|
+
expect(
|
|
218
|
+
z.parse(Rpc.wallet_connect.capabilities.request, {
|
|
219
|
+
method: 'register',
|
|
220
|
+
showDeposit: false,
|
|
221
|
+
}),
|
|
222
|
+
).toEqual({
|
|
223
|
+
method: 'register',
|
|
224
|
+
showDeposit: false,
|
|
225
|
+
})
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
test('accepts deposit parameters on the register branch', () => {
|
|
229
|
+
expect(
|
|
230
|
+
z.parse(Rpc.wallet_connect.capabilities.request, {
|
|
231
|
+
method: 'register',
|
|
232
|
+
showDeposit: {
|
|
233
|
+
amount: '50',
|
|
234
|
+
displayName: 'DoorDash',
|
|
235
|
+
token: 'USDC',
|
|
236
|
+
},
|
|
237
|
+
}),
|
|
238
|
+
).toEqual({
|
|
239
|
+
method: 'register',
|
|
240
|
+
showDeposit: {
|
|
241
|
+
amount: '50',
|
|
242
|
+
displayName: 'DoorDash',
|
|
243
|
+
token: 'USDC',
|
|
244
|
+
},
|
|
245
|
+
})
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test('ignores showDeposit on the login branch', () => {
|
|
249
|
+
expect(
|
|
250
|
+
z.parse(Rpc.wallet_connect.capabilities.request, {
|
|
251
|
+
method: 'login',
|
|
252
|
+
showDeposit: true,
|
|
253
|
+
}),
|
|
254
|
+
).toEqual({
|
|
255
|
+
method: 'login',
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
test('rejects invalid deposit parameters', () => {
|
|
260
|
+
expect(() =>
|
|
261
|
+
z.parse(Rpc.wallet_connect.capabilities.request, {
|
|
262
|
+
method: 'register',
|
|
263
|
+
showDeposit: { amount: 50 },
|
|
264
|
+
}),
|
|
265
|
+
).toThrow()
|
|
266
|
+
})
|
|
267
|
+
})
|
|
268
|
+
|
|
203
269
|
describe('wallet_connect_strict.parameters: auth', () => {
|
|
204
270
|
test('accepts string shorthand', () => {
|
|
205
271
|
expect(
|
|
@@ -240,3 +306,76 @@ describe('wallet_connect_strict.parameters: auth', () => {
|
|
|
240
306
|
`)
|
|
241
307
|
})
|
|
242
308
|
})
|
|
309
|
+
|
|
310
|
+
describe('wallet_connect_strict.parameters: showDeposit', () => {
|
|
311
|
+
test('accepts true on the register branch', () => {
|
|
312
|
+
expect(
|
|
313
|
+
z.parse(Rpc.wallet_connect_strict.parameters, {
|
|
314
|
+
capabilities: {
|
|
315
|
+
method: 'register',
|
|
316
|
+
showDeposit: true,
|
|
317
|
+
},
|
|
318
|
+
}),
|
|
319
|
+
).toEqual({
|
|
320
|
+
capabilities: {
|
|
321
|
+
method: 'register',
|
|
322
|
+
showDeposit: true,
|
|
323
|
+
},
|
|
324
|
+
})
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
test('accepts false on the register branch', () => {
|
|
328
|
+
expect(
|
|
329
|
+
z.parse(Rpc.wallet_connect_strict.parameters, {
|
|
330
|
+
capabilities: {
|
|
331
|
+
method: 'register',
|
|
332
|
+
showDeposit: false,
|
|
333
|
+
},
|
|
334
|
+
}),
|
|
335
|
+
).toEqual({
|
|
336
|
+
capabilities: {
|
|
337
|
+
method: 'register',
|
|
338
|
+
showDeposit: false,
|
|
339
|
+
},
|
|
340
|
+
})
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
test('accepts deposit parameters on the register branch', () => {
|
|
344
|
+
expect(
|
|
345
|
+
z.parse(Rpc.wallet_connect_strict.parameters, {
|
|
346
|
+
capabilities: {
|
|
347
|
+
method: 'register',
|
|
348
|
+
showDeposit: {
|
|
349
|
+
amount: '100',
|
|
350
|
+
displayName: 'DoorDash',
|
|
351
|
+
token: 'USDC',
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
}),
|
|
355
|
+
).toEqual({
|
|
356
|
+
capabilities: {
|
|
357
|
+
method: 'register',
|
|
358
|
+
showDeposit: {
|
|
359
|
+
amount: '100',
|
|
360
|
+
displayName: 'DoorDash',
|
|
361
|
+
token: 'USDC',
|
|
362
|
+
},
|
|
363
|
+
},
|
|
364
|
+
})
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
test('ignores showDeposit on the login branch', () => {
|
|
368
|
+
expect(
|
|
369
|
+
z.parse(Rpc.wallet_connect_strict.parameters, {
|
|
370
|
+
capabilities: {
|
|
371
|
+
method: 'login',
|
|
372
|
+
showDeposit: true,
|
|
373
|
+
},
|
|
374
|
+
}),
|
|
375
|
+
).toEqual({
|
|
376
|
+
capabilities: {
|
|
377
|
+
method: 'login',
|
|
378
|
+
},
|
|
379
|
+
})
|
|
380
|
+
})
|
|
381
|
+
})
|
package/src/core/zod/rpc.ts
CHANGED
|
@@ -427,6 +427,30 @@ export namespace wallet_revokeAccessKey {
|
|
|
427
427
|
export namespace wallet_connect {
|
|
428
428
|
export const authorizeAccessKey = z.optional(wallet_authorizeAccessKey.parameters)
|
|
429
429
|
|
|
430
|
+
/**
|
|
431
|
+
* Shows an optional funding prompt after `wallet_connect` registration succeeds.
|
|
432
|
+
*
|
|
433
|
+
* `true` uses the connected account as the deposit target. Object form
|
|
434
|
+
* pre-fills deposit UI hints. The deposit chain comes from the surrounding
|
|
435
|
+
* `wallet_connect` chain context.
|
|
436
|
+
*/
|
|
437
|
+
export const showDeposit = z.optional(
|
|
438
|
+
z.union([
|
|
439
|
+
z.boolean(),
|
|
440
|
+
z.object({
|
|
441
|
+
/** Human-readable amount to pre-fill (e.g. `"50"`). */
|
|
442
|
+
amount: z.optional(z.string()),
|
|
443
|
+
/** Display name shown in the deposit UI (e.g. the app name). */
|
|
444
|
+
displayName: z.optional(z.string()),
|
|
445
|
+
/**
|
|
446
|
+
* Token to pre-fill, accepted as either a contract address or a
|
|
447
|
+
* supported deposit token symbol (case-insensitive, e.g. `"USDC"`).
|
|
448
|
+
*/
|
|
449
|
+
token: z.optional(z.union([u.address(), z.string()])),
|
|
450
|
+
}),
|
|
451
|
+
]),
|
|
452
|
+
)
|
|
453
|
+
|
|
430
454
|
/**
|
|
431
455
|
* SIWE round-trip configuration. Bare string is shorthand for `{ url }`.
|
|
432
456
|
*
|
|
@@ -484,6 +508,7 @@ export namespace wallet_connect {
|
|
|
484
508
|
method: z.literal('register'),
|
|
485
509
|
name: z.optional(z.string()),
|
|
486
510
|
personalSign,
|
|
511
|
+
showDeposit,
|
|
487
512
|
userId: z.optional(z.string()),
|
|
488
513
|
}),
|
|
489
514
|
z.object({
|
|
@@ -558,6 +583,7 @@ export namespace wallet_connect_strict {
|
|
|
558
583
|
const authorizeAccessKey = z.optional(wallet_authorizeAccessKey_strict.parameters)
|
|
559
584
|
const auth = wallet_connect.auth
|
|
560
585
|
const personalSign = wallet_connect.personalSign
|
|
586
|
+
const showDeposit = wallet_connect.showDeposit
|
|
561
587
|
|
|
562
588
|
export const parameters = z.object({
|
|
563
589
|
capabilities: z.optional(
|
|
@@ -569,6 +595,7 @@ export namespace wallet_connect_strict {
|
|
|
569
595
|
method: z.literal('register'),
|
|
570
596
|
name: z.optional(z.string()),
|
|
571
597
|
personalSign,
|
|
598
|
+
showDeposit,
|
|
572
599
|
userId: z.optional(z.string()),
|
|
573
600
|
}),
|
|
574
601
|
z.object({
|
|
@@ -746,16 +773,23 @@ export namespace wallet_switchEthereumChain {
|
|
|
746
773
|
export namespace wallet_deposit {
|
|
747
774
|
export const schema = Schema.defineItem({
|
|
748
775
|
method: z.literal('wallet_deposit'),
|
|
749
|
-
params: z.
|
|
750
|
-
z.
|
|
751
|
-
z.
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
776
|
+
params: z.optional(
|
|
777
|
+
z.readonly(
|
|
778
|
+
z.tuple([
|
|
779
|
+
z.object({
|
|
780
|
+
address: z.optional(u.address()),
|
|
781
|
+
/** Human-readable amount to pre-fill (e.g. `"50"`). */
|
|
782
|
+
amount: z.optional(z.string()),
|
|
783
|
+
chainId: z.optional(u.number()),
|
|
784
|
+
displayName: z.optional(z.string()),
|
|
785
|
+
/**
|
|
786
|
+
* Token to pre-fill, accepted as either a contract address or a
|
|
787
|
+
* supported deposit token symbol (case-insensitive, e.g. `"USDC"`).
|
|
788
|
+
*/
|
|
789
|
+
token: z.optional(z.union([u.address(), z.string()])),
|
|
790
|
+
}),
|
|
791
|
+
]),
|
|
792
|
+
),
|
|
759
793
|
),
|
|
760
794
|
returns: z.optional(
|
|
761
795
|
z.object({
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export * as AccessKey from './core/AccessKey.js'
|
|
2
1
|
export * as Adapter from './core/Adapter.js'
|
|
3
2
|
export * as IntersectionObserver from './core/IntersectionObserver.js'
|
|
4
3
|
export * as Dialog from './core/Dialog.js'
|
|
@@ -15,6 +14,7 @@ export * as TrustedHosts from './core/TrustedHosts.js'
|
|
|
15
14
|
export * as WebAuthnCeremony from './core/WebAuthnCeremony.js'
|
|
16
15
|
export { dialog, dialog as tempoWallet } from './core/adapters/dialog.js'
|
|
17
16
|
export { local } from './core/adapters/local.js'
|
|
17
|
+
export { privy } from './core/adapters/privy.js'
|
|
18
18
|
export { turnkey } from './core/adapters/turnkey.js'
|
|
19
19
|
export { webAuthn } from './core/adapters/webAuthn.js'
|
|
20
20
|
export {
|
|
@@ -27,11 +27,14 @@ async function fund(address: viem_Address) {
|
|
|
27
27
|
|
|
28
28
|
function createOpen(options: { mismatchFirstCall?: boolean | undefined } = {}) {
|
|
29
29
|
let calls = 0
|
|
30
|
+
const urls: string[] = []
|
|
30
31
|
|
|
31
32
|
return {
|
|
32
33
|
calls: () => calls,
|
|
34
|
+
urls: () => urls,
|
|
33
35
|
open: async (url: string) => {
|
|
34
36
|
calls += 1
|
|
37
|
+
urls.push(url)
|
|
35
38
|
|
|
36
39
|
const authUrl = new URL(url)
|
|
37
40
|
const callback = authUrl.searchParams.get('callback')
|
|
@@ -112,4 +115,66 @@ describe('create', () => {
|
|
|
112
115
|
expect(receipt.status).toMatchInlineSnapshot(`"0x1"`)
|
|
113
116
|
expect(browser.calls()).toMatchInlineSnapshot(`2`)
|
|
114
117
|
})
|
|
118
|
+
|
|
119
|
+
test('behavior: forwards showDeposit boolean to the mobile auth URL for registration', async () => {
|
|
120
|
+
const browser = createOpen()
|
|
121
|
+
const provider = Provider.create({
|
|
122
|
+
authorizeAccessKey: () => ({
|
|
123
|
+
expiry: Math.floor(Date.now() / 1000) + 3600,
|
|
124
|
+
}),
|
|
125
|
+
chains: [chain],
|
|
126
|
+
host: 'https://wallet-next.tempo.xyz',
|
|
127
|
+
open: browser.open,
|
|
128
|
+
redirectUri: 'accounts-playground://auth',
|
|
129
|
+
secureStorage: Storage.memory(),
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
await provider.request({
|
|
133
|
+
method: 'wallet_connect',
|
|
134
|
+
params: [{ capabilities: { method: 'register', showDeposit: true } }],
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
expect(new URL(browser.urls()[0]!).searchParams.get('showDeposit')).toMatchInlineSnapshot(
|
|
138
|
+
`"true"`,
|
|
139
|
+
)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
test('behavior: forwards showDeposit params to the mobile auth URL for registration', async () => {
|
|
143
|
+
const browser = createOpen()
|
|
144
|
+
const provider = Provider.create({
|
|
145
|
+
authorizeAccessKey: () => ({
|
|
146
|
+
expiry: Math.floor(Date.now() / 1000) + 3600,
|
|
147
|
+
}),
|
|
148
|
+
chains: [chain],
|
|
149
|
+
host: 'https://wallet-next.tempo.xyz',
|
|
150
|
+
open: browser.open,
|
|
151
|
+
redirectUri: 'accounts-playground://auth',
|
|
152
|
+
secureStorage: Storage.memory(),
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
await provider.request({
|
|
156
|
+
method: 'wallet_connect',
|
|
157
|
+
params: [
|
|
158
|
+
{
|
|
159
|
+
capabilities: {
|
|
160
|
+
method: 'register',
|
|
161
|
+
showDeposit: {
|
|
162
|
+
amount: '50',
|
|
163
|
+
displayName: 'DoorDash',
|
|
164
|
+
token: 'USDC',
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
expect(JSON.parse(new URL(browser.urls()[0]!).searchParams.get('showDeposit')!))
|
|
172
|
+
.toMatchInlineSnapshot(`
|
|
173
|
+
{
|
|
174
|
+
"amount": "50",
|
|
175
|
+
"displayName": "DoorDash",
|
|
176
|
+
"token": "USDC",
|
|
177
|
+
}
|
|
178
|
+
`)
|
|
179
|
+
})
|
|
115
180
|
})
|