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
|
@@ -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.ts
CHANGED
|
@@ -746,16 +746,23 @@ export namespace wallet_switchEthereumChain {
|
|
|
746
746
|
export namespace wallet_deposit {
|
|
747
747
|
export const schema = Schema.defineItem({
|
|
748
748
|
method: z.literal('wallet_deposit'),
|
|
749
|
-
params: z.
|
|
750
|
-
z.
|
|
751
|
-
z.
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
749
|
+
params: z.optional(
|
|
750
|
+
z.readonly(
|
|
751
|
+
z.tuple([
|
|
752
|
+
z.object({
|
|
753
|
+
address: z.optional(u.address()),
|
|
754
|
+
/** Human-readable amount to pre-fill (e.g. `"50"`). */
|
|
755
|
+
amount: z.optional(z.string()),
|
|
756
|
+
chainId: z.optional(u.number()),
|
|
757
|
+
displayName: z.optional(z.string()),
|
|
758
|
+
/**
|
|
759
|
+
* Token to pre-fill, accepted as either a contract address or a
|
|
760
|
+
* supported deposit token symbol (case-insensitive, e.g. `"USDC"`).
|
|
761
|
+
*/
|
|
762
|
+
token: z.optional(z.union([u.address(), z.string()])),
|
|
763
|
+
}),
|
|
764
|
+
]),
|
|
765
|
+
),
|
|
759
766
|
),
|
|
760
767
|
returns: z.optional(
|
|
761
768
|
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 {
|
|
@@ -8,11 +8,11 @@ import {
|
|
|
8
8
|
RpcResponse,
|
|
9
9
|
} from 'ox'
|
|
10
10
|
import { KeyAuthorization } from 'ox/tempo'
|
|
11
|
-
import { prepareTransactionRequest } from 'viem/actions'
|
|
12
11
|
import { Actions, Account as TempoAccount, Secp256k1 } from 'viem/tempo'
|
|
13
12
|
|
|
14
13
|
import * as AccessKey from '../core/AccessKey.js'
|
|
15
14
|
import * as Adapter from '../core/Adapter.js'
|
|
15
|
+
import * as AccessKeyTransaction from '../core/internal/AccessKeyTransaction.js'
|
|
16
16
|
import type * as Storage from '../core/Storage.js'
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -58,19 +58,19 @@ export function reactNative(options: reactNative.Options): Adapter.Adapter {
|
|
|
58
58
|
const keyAuthorization = deserialized as KeyAuthorization.Signed
|
|
59
59
|
|
|
60
60
|
if (keyAuthorization.address.toLowerCase() === keyAddress.toLowerCase())
|
|
61
|
-
AccessKey.
|
|
62
|
-
address,
|
|
63
|
-
keyAuthorization,
|
|
61
|
+
AccessKey.add({
|
|
62
|
+
account: address,
|
|
63
|
+
authorization: keyAuthorization,
|
|
64
64
|
privateKey: entry.key,
|
|
65
65
|
store,
|
|
66
66
|
})
|
|
67
67
|
else
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
})
|
|
68
|
+
AccessKey.remove({
|
|
69
|
+
account: address,
|
|
70
|
+
accessKey: keyAuthorization.address,
|
|
71
|
+
chainId: Number(keyAuthorization.chainId),
|
|
72
|
+
store,
|
|
73
|
+
})
|
|
74
74
|
|
|
75
75
|
return {
|
|
76
76
|
account,
|
|
@@ -127,9 +127,9 @@ export function reactNative(options: reactNative.Options): Adapter.Adapter {
|
|
|
127
127
|
) {
|
|
128
128
|
if (!managedKey) return
|
|
129
129
|
|
|
130
|
-
AccessKey.
|
|
131
|
-
address,
|
|
132
|
-
keyAuthorization,
|
|
130
|
+
AccessKey.add({
|
|
131
|
+
account: address,
|
|
132
|
+
authorization: keyAuthorization,
|
|
133
133
|
privateKey: managedKey.key,
|
|
134
134
|
store,
|
|
135
135
|
})
|
|
@@ -189,27 +189,37 @@ export function reactNative(options: reactNative.Options): Adapter.Adapter {
|
|
|
189
189
|
return result.keyAuthorization
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
-
async function
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
192
|
+
async function prepareManagedTransaction(
|
|
193
|
+
client: ReturnType<typeof getClient>,
|
|
194
|
+
parameters: AccessKeyTransaction.create.PrepareParameters,
|
|
195
|
+
options: {
|
|
196
|
+
calls?: AccessKeyTransaction.create.Options['calls'] | undefined
|
|
197
|
+
chainId?: number | undefined
|
|
198
|
+
} = {},
|
|
197
199
|
) {
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
200
|
+
const state = store.getState()
|
|
201
|
+
const address = parameters.from ?? state.accounts[state.activeAccount]?.address
|
|
202
|
+
if (!address) throw new core_Provider.DisconnectedError({ message: 'No active account.' })
|
|
203
|
+
const managedKey = await loadManagedKey(address)
|
|
204
|
+
if (managedKey && !(await isManagedKeyAuthorized(address, managedKey)))
|
|
205
|
+
await reauthorizeManagedKey(address, managedKey)
|
|
206
|
+
const transaction = await AccessKeyTransaction.create({
|
|
207
|
+
address,
|
|
208
|
+
calls: options.calls,
|
|
209
|
+
chainId: options.chainId ?? state.chainId,
|
|
210
|
+
client,
|
|
211
|
+
store,
|
|
212
|
+
})
|
|
213
|
+
if (!transaction)
|
|
214
|
+
throw new core_Provider.UnauthorizedError({
|
|
215
|
+
message: `Account "${address}" cannot sign with an access key.`,
|
|
216
|
+
})
|
|
217
|
+
return await transaction.prepare(parameters)
|
|
218
|
+
}
|
|
206
219
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
AccessKey.remove(account, { store })
|
|
211
|
-
throw error
|
|
212
|
-
}
|
|
220
|
+
async function loadManagedAccount(address: Adapter.signPersonalMessage.Parameters['address']) {
|
|
221
|
+
await loadManagedKey(address)
|
|
222
|
+
return getAccount({ address, signable: true })
|
|
213
223
|
}
|
|
214
224
|
|
|
215
225
|
async function authorize(request: {
|
|
@@ -333,74 +343,57 @@ export function reactNative(options: reactNative.Options): Adapter.Adapter {
|
|
|
333
343
|
async sendTransaction(parameters) {
|
|
334
344
|
const { feePayer, ...rest } = parameters
|
|
335
345
|
const client = getClient(typeof feePayer === 'string' ? { feePayer } : {})
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
}),
|
|
346
|
+
const prepared = await prepareManagedTransaction(
|
|
347
|
+
client,
|
|
348
|
+
{
|
|
349
|
+
...rest,
|
|
350
|
+
...(feePayer ? { feePayer: true } : {}),
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
calls: parameters.calls as AccessKeyTransaction.create.Options['calls'],
|
|
354
|
+
chainId: parameters.chainId,
|
|
355
|
+
},
|
|
347
356
|
)
|
|
348
|
-
|
|
349
|
-
const result = await client.request({
|
|
350
|
-
method: 'eth_sendRawTransaction' as never,
|
|
351
|
-
params: [signed],
|
|
352
|
-
})
|
|
353
|
-
AccessKey.removePending(account, { store })
|
|
354
|
-
return result
|
|
357
|
+
return await prepared.send()
|
|
355
358
|
},
|
|
356
359
|
async sendTransactionSync(parameters) {
|
|
357
360
|
const { feePayer, ...rest } = parameters
|
|
358
361
|
const client = getClient(typeof feePayer === 'string' ? { feePayer } : {})
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
}),
|
|
362
|
+
const prepared = await prepareManagedTransaction(
|
|
363
|
+
client,
|
|
364
|
+
{
|
|
365
|
+
...rest,
|
|
366
|
+
...(feePayer ? { feePayer: true } : {}),
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
calls: parameters.calls as AccessKeyTransaction.create.Options['calls'],
|
|
370
|
+
chainId: parameters.chainId,
|
|
371
|
+
},
|
|
370
372
|
)
|
|
371
|
-
|
|
372
|
-
const result = await client.request({
|
|
373
|
-
method: 'eth_sendRawTransactionSync' as never,
|
|
374
|
-
params: [signed],
|
|
375
|
-
})
|
|
376
|
-
AccessKey.removePending(account, { store })
|
|
377
|
-
return result
|
|
373
|
+
return await prepared.sendSync()
|
|
378
374
|
},
|
|
379
375
|
async signPersonalMessage({ address, data }) {
|
|
380
|
-
await
|
|
381
|
-
const account = getAccount({ address, signable: true })
|
|
376
|
+
const account = await loadManagedAccount(address)
|
|
382
377
|
return await account.signMessage({ message: { raw: data } })
|
|
383
378
|
},
|
|
384
379
|
async signTransaction(parameters) {
|
|
385
380
|
const { feePayer, ...rest } = parameters
|
|
386
381
|
const client = getClient(typeof feePayer === 'string' ? { feePayer } : {})
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}),
|
|
382
|
+
const prepared = await prepareManagedTransaction(
|
|
383
|
+
client,
|
|
384
|
+
{
|
|
385
|
+
...rest,
|
|
386
|
+
...(feePayer ? { feePayer: true } : {}),
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
calls: parameters.calls as AccessKeyTransaction.create.Options['calls'],
|
|
390
|
+
chainId: parameters.chainId,
|
|
391
|
+
},
|
|
398
392
|
)
|
|
399
|
-
return await
|
|
393
|
+
return await prepared.sign()
|
|
400
394
|
},
|
|
401
395
|
async signTypedData({ address, data }) {
|
|
402
|
-
await
|
|
403
|
-
const account = getAccount({ address, signable: true })
|
|
396
|
+
const account = await loadManagedAccount(address)
|
|
404
397
|
return await account.signTypedData(JSON.parse(data) as never)
|
|
405
398
|
},
|
|
406
399
|
},
|