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,811 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Address as core_Address,
|
|
3
|
+
Hex,
|
|
4
|
+
Provider as ox_Provider,
|
|
5
|
+
PublicKey,
|
|
6
|
+
RpcResponse,
|
|
7
|
+
Secp256k1,
|
|
8
|
+
Signature,
|
|
9
|
+
WebCryptoP256,
|
|
10
|
+
} from 'ox'
|
|
11
|
+
import { KeyAuthorization, SignatureEnvelope } from 'ox/tempo'
|
|
12
|
+
import { hashMessage, hashTypedData, isAddressEqual, keccak256 } from 'viem'
|
|
13
|
+
import type { Address, LocalAccount } from 'viem/accounts'
|
|
14
|
+
import { prepareTransactionRequest } from 'viem/actions'
|
|
15
|
+
import { Actions, Transaction as TempoTransaction } from 'viem/tempo'
|
|
16
|
+
|
|
17
|
+
import * as AccessKey from '../AccessKey.js'
|
|
18
|
+
import * as Adapter from '../Adapter.js'
|
|
19
|
+
import * as AccessKeyTransaction from '../internal/AccessKeyTransaction.js'
|
|
20
|
+
import * as Store from '../Store.js'
|
|
21
|
+
|
|
22
|
+
const privySessionErrorCodes = new Set([
|
|
23
|
+
'attempted_rpc_call_before_logged_in',
|
|
24
|
+
'attempted_to_read_storage_before_client_initialized',
|
|
25
|
+
'embedded_wallet_before_logged_in',
|
|
26
|
+
'embedded_wallet_does_not_exist',
|
|
27
|
+
'embedded_wallet_request_error',
|
|
28
|
+
'missing_auth_token',
|
|
29
|
+
'missing_privy_token',
|
|
30
|
+
'oauth_session_failed',
|
|
31
|
+
'oauth_session_timeout',
|
|
32
|
+
'session_expired',
|
|
33
|
+
'unauthenticated',
|
|
34
|
+
'unauthorized',
|
|
35
|
+
])
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Creates a Privy adapter backed by `@privy-io/js-sdk-core` Privy sessions and embedded
|
|
39
|
+
* Ethereum wallets.
|
|
40
|
+
*
|
|
41
|
+
* The adapter owns silent reconnect, session-expiry cleanup, and signing. Apps supply
|
|
42
|
+
* the UI-bearing login flow via `loadAccounts` (and optionally a distinct `createAccount`
|
|
43
|
+
* for registration). Callbacks fire only on user-initiated `wallet_connect`/registration —
|
|
44
|
+
* never during silent restore on page reload.
|
|
45
|
+
*
|
|
46
|
+
* Silent restore on page reload pulls wallets directly from the Privy SDK
|
|
47
|
+
* (`client.user.get` + `client.embeddedWallet.getEthereumProvider`), so apps don't
|
|
48
|
+
* need to re-run the login UI when the user returns with a still-valid Privy session.
|
|
49
|
+
*
|
|
50
|
+
* Callbacks only run the Privy auth UI. They may optionally return a subset of
|
|
51
|
+
* embedded wallet addresses to expose; if omitted, the adapter exposes every
|
|
52
|
+
* embedded wallet on the resulting Privy user.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* import Privy from '@privy-io/js-sdk-core'
|
|
57
|
+
*
|
|
58
|
+
* const client = new Privy({ appId: import.meta.env.VITE_PRIVY_APP_ID })
|
|
59
|
+
*
|
|
60
|
+
* const provider = Provider.create({
|
|
61
|
+
* adapter: privy({
|
|
62
|
+
* client,
|
|
63
|
+
* // Optional: omit to route registration through `loadAccounts`.
|
|
64
|
+
* createAccount: async ({ client }) => {
|
|
65
|
+
* await myPrivyRegisterUI(client)
|
|
66
|
+
* },
|
|
67
|
+
* loadAccounts: async ({ client }) => {
|
|
68
|
+
* await myPrivyLoginUI(client)
|
|
69
|
+
* },
|
|
70
|
+
* }),
|
|
71
|
+
* })
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export function privy<const client extends privy.Client>(
|
|
75
|
+
options: privy.Options<client>,
|
|
76
|
+
): Adapter.Adapter {
|
|
77
|
+
const { icon, name = 'Privy', rdns = 'io.privy' } = options
|
|
78
|
+
|
|
79
|
+
return Adapter.define({ icon, name, rdns }, ({ getClient, store }) => {
|
|
80
|
+
let privyClient_promise: Promise<client> | undefined
|
|
81
|
+
let restore_promise: Promise<void> | undefined
|
|
82
|
+
let walletAccounts: readonly privy.EmbeddedWallet[] = []
|
|
83
|
+
|
|
84
|
+
async function getPrivyClient(): Promise<client> {
|
|
85
|
+
privyClient_promise ??= (async () => {
|
|
86
|
+
await options.client.initialize?.()
|
|
87
|
+
return options.client
|
|
88
|
+
})()
|
|
89
|
+
return await privyClient_promise
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function toStoreAccount(account: privy.EmbeddedWallet, label?: string | undefined) {
|
|
93
|
+
return {
|
|
94
|
+
address: core_Address.from(account.address),
|
|
95
|
+
...(label ? { label } : {}),
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function clear() {
|
|
100
|
+
restore_promise = undefined
|
|
101
|
+
walletAccounts = []
|
|
102
|
+
store.setState({ accessKeys: [], accounts: [], activeAccount: 0 })
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function hasValidSession() {
|
|
106
|
+
const token = await (await getPrivyClient()).getAccessToken().catch((error) => {
|
|
107
|
+
if (isSessionError(error)) return null
|
|
108
|
+
throw error
|
|
109
|
+
})
|
|
110
|
+
return !!token
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Loads the user's Privy embedded Ethereum wallets and constructs their
|
|
115
|
+
* EIP-1193 providers. Mirrors `getAllUserEmbeddedEthereumWallets` +
|
|
116
|
+
* `getEntropyDetailsFromUser` from `@privy-io/js-sdk-core`: per the SDK,
|
|
117
|
+
* `entropyId` is the **primary** embedded wallet's address (wallet_index === 0)
|
|
118
|
+
* shared across all wallets of the same user, and `entropyIdVerifier` is
|
|
119
|
+
* hardcoded to `'ethereum-address-verifier'` for Ethereum wallets.
|
|
120
|
+
*/
|
|
121
|
+
async function loadEthereumWallets(
|
|
122
|
+
privyClient: privy.Client,
|
|
123
|
+
): Promise<readonly privy.EmbeddedWallet[]> {
|
|
124
|
+
const { user } = await privyClient.user.get()
|
|
125
|
+
const wallets = (user?.linked_accounts ?? [])
|
|
126
|
+
.filter(
|
|
127
|
+
(account) =>
|
|
128
|
+
account.type === 'wallet' &&
|
|
129
|
+
account.wallet_client_type === 'privy' &&
|
|
130
|
+
account.connector_type === 'embedded' &&
|
|
131
|
+
account.chain_type === 'ethereum' &&
|
|
132
|
+
typeof account.address === 'string',
|
|
133
|
+
)
|
|
134
|
+
.slice()
|
|
135
|
+
.sort((a, b) => {
|
|
136
|
+
// Wallets without a `wallet_index` are sorted to the end so they
|
|
137
|
+
// never accidentally become primary when a sibling has an index.
|
|
138
|
+
const a_index = a.wallet_index ?? Number.POSITIVE_INFINITY
|
|
139
|
+
const b_index = b.wallet_index ?? Number.POSITIVE_INFINITY
|
|
140
|
+
return a_index - b_index
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// Primary is the wallet with `wallet_index === 0`. Fall back to the
|
|
144
|
+
// lowest-indexed wallet only when no wallet declares index 0.
|
|
145
|
+
const primary = wallets.find((wallet) => wallet.wallet_index === 0) ?? wallets[0]
|
|
146
|
+
if (!primary) return []
|
|
147
|
+
const entropyId = primary.address as string
|
|
148
|
+
|
|
149
|
+
return await Promise.all(
|
|
150
|
+
wallets.map(async (wallet) => ({
|
|
151
|
+
address: core_Address.from(wallet.address as string),
|
|
152
|
+
provider: await privyClient.embeddedWallet.getEthereumProvider({
|
|
153
|
+
wallet,
|
|
154
|
+
entropyId,
|
|
155
|
+
entropyIdVerifier: 'ethereum-address-verifier',
|
|
156
|
+
}),
|
|
157
|
+
})),
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function selectWalletAccounts(
|
|
162
|
+
accounts: readonly privy.EmbeddedWallet[],
|
|
163
|
+
addresses: privy.AccountSelection,
|
|
164
|
+
): readonly privy.EmbeddedWallet[] {
|
|
165
|
+
if (!addresses) return accounts
|
|
166
|
+
|
|
167
|
+
return addresses.map((address) => {
|
|
168
|
+
const address_ = core_Address.from(address)
|
|
169
|
+
const account = accounts.find((account) =>
|
|
170
|
+
isAddressEqual(core_Address.from(account.address), address_),
|
|
171
|
+
)
|
|
172
|
+
if (account) return account
|
|
173
|
+
|
|
174
|
+
throw new ox_Provider.UnauthorizedError({
|
|
175
|
+
message: `Privy callback returned address "${address_}" that was not found in the user's embedded wallets.`,
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
async function restore() {
|
|
181
|
+
await Store.waitForHydration(store)
|
|
182
|
+
if (walletAccounts.length > 0) return
|
|
183
|
+
if (restore_promise) return await restore_promise
|
|
184
|
+
|
|
185
|
+
restore_promise = (async () => {
|
|
186
|
+
const state = store.getState()
|
|
187
|
+
const persisted = state.accounts
|
|
188
|
+
if (persisted.length === 0) return
|
|
189
|
+
|
|
190
|
+
if (!(await hasValidSession())) {
|
|
191
|
+
clear()
|
|
192
|
+
throw new ox_Provider.DisconnectedError({ message: 'Privy session expired.' })
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const restored = await loadEthereumWallets(await getPrivyClient()).catch((error) => {
|
|
196
|
+
if (!isSessionError(error)) throw error
|
|
197
|
+
clear()
|
|
198
|
+
throw new ox_Provider.DisconnectedError({ message: 'Privy session expired.' })
|
|
199
|
+
})
|
|
200
|
+
walletAccounts = persisted
|
|
201
|
+
.map((account) =>
|
|
202
|
+
restored.find((walletAccount) =>
|
|
203
|
+
isAddressEqual(core_Address.from(walletAccount.address), account.address),
|
|
204
|
+
),
|
|
205
|
+
)
|
|
206
|
+
.filter((account): account is privy.EmbeddedWallet => !!account)
|
|
207
|
+
|
|
208
|
+
// If the persisted accounts no longer exist in Privy (different user
|
|
209
|
+
// signed in, wallets removed), wipe the stale state so callers see a
|
|
210
|
+
// clean disconnected state instead of ghost accounts without providers.
|
|
211
|
+
if (walletAccounts.length === 0) {
|
|
212
|
+
clear()
|
|
213
|
+
throw new ox_Provider.DisconnectedError({
|
|
214
|
+
message: 'Privy session no longer matches persisted accounts.',
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
store.setState({
|
|
219
|
+
accounts: walletAccounts.map((account) => toStoreAccount(account)),
|
|
220
|
+
activeAccount: Math.min(state.activeAccount, walletAccounts.length - 1),
|
|
221
|
+
})
|
|
222
|
+
})()
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
await restore_promise
|
|
226
|
+
} finally {
|
|
227
|
+
restore_promise = undefined
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async function requireSession() {
|
|
232
|
+
if (await hasValidSession()) return
|
|
233
|
+
clear()
|
|
234
|
+
throw new ox_Provider.DisconnectedError({ message: 'Privy session expired.' })
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async function accountForSigning(address: Address | undefined) {
|
|
238
|
+
await restore()
|
|
239
|
+
await requireSession()
|
|
240
|
+
|
|
241
|
+
const address_ = address ?? store.getState().accounts[store.getState().activeAccount]?.address
|
|
242
|
+
if (!address_) throw new ox_Provider.DisconnectedError({ message: 'No accounts connected.' })
|
|
243
|
+
|
|
244
|
+
const account = walletAccounts.find((account) =>
|
|
245
|
+
isAddressEqual(core_Address.from(account.address), address_),
|
|
246
|
+
)
|
|
247
|
+
if (account) return account
|
|
248
|
+
|
|
249
|
+
if (walletAccounts.length === 0)
|
|
250
|
+
throw new ox_Provider.DisconnectedError({
|
|
251
|
+
message: 'No Privy account connected.',
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
throw new ox_Provider.UnauthorizedError({ message: `Account "${address_}" not found.` })
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async function signPayload(parameters: {
|
|
258
|
+
payload: Hex.Hex
|
|
259
|
+
walletAccount: privy.EmbeddedWallet
|
|
260
|
+
}) {
|
|
261
|
+
const { payload, walletAccount } = parameters
|
|
262
|
+
const result = await walletAccount.provider
|
|
263
|
+
.request({ method: 'secp256k1_sign', params: [payload] })
|
|
264
|
+
.catch((error) => {
|
|
265
|
+
const code = getPrivyErrorCode(error)
|
|
266
|
+
const message = getPrivyErrorMessage(error).toLowerCase()
|
|
267
|
+
const unsupported =
|
|
268
|
+
(typeof code === 'number' && (code === 4200 || code === -32601)) ||
|
|
269
|
+
(typeof code === 'string' && code.toLowerCase().includes('unsupported')) ||
|
|
270
|
+
message.includes('unsupported') ||
|
|
271
|
+
message.includes('method not found')
|
|
272
|
+
if (unsupported)
|
|
273
|
+
throw new ox_Provider.UnsupportedMethodError({
|
|
274
|
+
message:
|
|
275
|
+
'Privy adapter requires raw secp256k1 hash signing via `secp256k1_sign` for Tempo transactions and access keys.',
|
|
276
|
+
})
|
|
277
|
+
if (isSessionError(error)) {
|
|
278
|
+
clear()
|
|
279
|
+
throw new ox_Provider.DisconnectedError({ message: 'Privy session expired.' })
|
|
280
|
+
}
|
|
281
|
+
throw error
|
|
282
|
+
})
|
|
283
|
+
if (typeof result !== 'string' || !Hex.validate(result))
|
|
284
|
+
throw new ox_Provider.ProviderRpcError(
|
|
285
|
+
-32603,
|
|
286
|
+
'Privy provider returned a non-hex secp256k1_sign result.',
|
|
287
|
+
)
|
|
288
|
+
const signature: Hex.Hex = result
|
|
289
|
+
|
|
290
|
+
// Verify Privy returned a signature for the wallet we asked.
|
|
291
|
+
const expected = core_Address.from(walletAccount.address)
|
|
292
|
+
const recovered = (() => {
|
|
293
|
+
try {
|
|
294
|
+
return Secp256k1.recoverAddress({ payload, signature: Signature.fromHex(signature) })
|
|
295
|
+
} catch {
|
|
296
|
+
return undefined
|
|
297
|
+
}
|
|
298
|
+
})()
|
|
299
|
+
if (!recovered || !isAddressEqual(recovered, expected))
|
|
300
|
+
throw new ox_Provider.UnauthorizedError({
|
|
301
|
+
message: `Privy provider returned a signature for "${recovered ?? 'unknown'}" that does not match the requested wallet "${expected}".`,
|
|
302
|
+
})
|
|
303
|
+
return signature
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Builds, signs, and saves an access key authorization for the given Privy
|
|
308
|
+
* wallet. Generates a local P256 key pair when no external key is provided.
|
|
309
|
+
*/
|
|
310
|
+
async function authorizeAccessKeyFor(
|
|
311
|
+
account: privy.EmbeddedWallet,
|
|
312
|
+
options: Adapter.authorizeAccessKey.Parameters,
|
|
313
|
+
) {
|
|
314
|
+
const { expiry, limits, scopes } = options
|
|
315
|
+
const chainId = options.chainId ?? getClient().chain.id
|
|
316
|
+
|
|
317
|
+
const prepared = await (async () => {
|
|
318
|
+
if (options.publicKey || options.address) {
|
|
319
|
+
const address =
|
|
320
|
+
options.address ?? core_Address.fromPublicKey(PublicKey.from(options.publicKey!))
|
|
321
|
+
return {
|
|
322
|
+
keyAuthorization: KeyAuthorization.from({
|
|
323
|
+
address,
|
|
324
|
+
chainId: BigInt(chainId),
|
|
325
|
+
expiry,
|
|
326
|
+
limits,
|
|
327
|
+
scopes,
|
|
328
|
+
type: options.keyType ?? 'secp256k1',
|
|
329
|
+
}),
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (options.keyType && options.keyType !== 'p256')
|
|
334
|
+
throw new RpcResponse.InvalidParamsError({
|
|
335
|
+
message: `\`keyType: "${options.keyType}"\` requires externally generated key material; provide \`publicKey\` or \`address\`.`,
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
const keyPair = await WebCryptoP256.createKeyPair()
|
|
339
|
+
const address = core_Address.fromPublicKey(PublicKey.from(keyPair.publicKey))
|
|
340
|
+
return {
|
|
341
|
+
keyAuthorization: KeyAuthorization.from({
|
|
342
|
+
address,
|
|
343
|
+
chainId: BigInt(chainId),
|
|
344
|
+
expiry,
|
|
345
|
+
limits,
|
|
346
|
+
scopes,
|
|
347
|
+
type: 'p256',
|
|
348
|
+
}),
|
|
349
|
+
keyPair,
|
|
350
|
+
}
|
|
351
|
+
})()
|
|
352
|
+
|
|
353
|
+
const signature = await signPayload({
|
|
354
|
+
payload: KeyAuthorization.getSignPayload(prepared.keyAuthorization),
|
|
355
|
+
walletAccount: account,
|
|
356
|
+
})
|
|
357
|
+
const keyAuthorization = KeyAuthorization.from(prepared.keyAuthorization, {
|
|
358
|
+
signature: SignatureEnvelope.from(signature),
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
AccessKey.add({
|
|
362
|
+
account: core_Address.from(account.address),
|
|
363
|
+
authorization: keyAuthorization,
|
|
364
|
+
...(prepared.keyPair ? { keyPair: prepared.keyPair } : {}),
|
|
365
|
+
store,
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
return KeyAuthorization.toRpc(keyAuthorization)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
async function signTransaction(parameters: Adapter.signTransaction.Parameters) {
|
|
372
|
+
const account = await accountForSigning(parameters.from)
|
|
373
|
+
const { feePayer, ...rest } = parameters
|
|
374
|
+
const viemClient = getClient({
|
|
375
|
+
chainId: parameters.chainId,
|
|
376
|
+
feePayer: feePayer === true ? undefined : feePayer,
|
|
377
|
+
})
|
|
378
|
+
const prepared = await prepareTransactionRequest(viemClient, {
|
|
379
|
+
account: core_Address.from(account.address),
|
|
380
|
+
...rest,
|
|
381
|
+
...(feePayer ? { feePayer: true } : {}),
|
|
382
|
+
type: 'tempo',
|
|
383
|
+
} as never)
|
|
384
|
+
return await signPreparedTransaction(account, prepared)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async function signPreparedTransaction(account: privy.EmbeddedWallet, prepared: unknown) {
|
|
388
|
+
const presign = (() => {
|
|
389
|
+
if (
|
|
390
|
+
prepared &&
|
|
391
|
+
typeof prepared === 'object' &&
|
|
392
|
+
'feePayerSignature' in prepared &&
|
|
393
|
+
prepared.feePayerSignature
|
|
394
|
+
)
|
|
395
|
+
return { ...prepared, feePayerSignature: null }
|
|
396
|
+
return prepared
|
|
397
|
+
})()
|
|
398
|
+
const unsignedTransaction = await TempoTransaction.serialize(presign as never)
|
|
399
|
+
|
|
400
|
+
const signature = await signPayload({
|
|
401
|
+
payload: keccak256(unsignedTransaction),
|
|
402
|
+
walletAccount: account,
|
|
403
|
+
})
|
|
404
|
+
return await TempoTransaction.serialize(
|
|
405
|
+
prepared as never,
|
|
406
|
+
SignatureEnvelope.from(Signature.fromHex(signature)) as never,
|
|
407
|
+
)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
async function prepareTransaction(parameters: Adapter.signTransaction.Parameters) {
|
|
411
|
+
const viemClient = getClient({
|
|
412
|
+
chainId: parameters.chainId,
|
|
413
|
+
feePayer: parameters.feePayer === true ? undefined : parameters.feePayer,
|
|
414
|
+
})
|
|
415
|
+
|
|
416
|
+
const state = store.getState()
|
|
417
|
+
const address = parameters.from ?? state.accounts[state.activeAccount]?.address
|
|
418
|
+
const transaction = address
|
|
419
|
+
? await AccessKeyTransaction.create({
|
|
420
|
+
address,
|
|
421
|
+
calls: parameters.calls,
|
|
422
|
+
chainId: parameters.chainId ?? state.chainId,
|
|
423
|
+
client: viemClient,
|
|
424
|
+
store,
|
|
425
|
+
})
|
|
426
|
+
: undefined
|
|
427
|
+
if (transaction) {
|
|
428
|
+
const { feePayer, ...rest } = parameters
|
|
429
|
+
try {
|
|
430
|
+
return await transaction.prepare({
|
|
431
|
+
...rest,
|
|
432
|
+
...(feePayer ? { feePayer: true } : {}),
|
|
433
|
+
})
|
|
434
|
+
} catch {}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
async function sign() {
|
|
438
|
+
return await signTransaction(parameters)
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return {
|
|
442
|
+
request: undefined as never,
|
|
443
|
+
sign,
|
|
444
|
+
async send() {
|
|
445
|
+
const signed = await sign()
|
|
446
|
+
return await viemClient.request({
|
|
447
|
+
method: 'eth_sendRawTransaction' as never,
|
|
448
|
+
params: [signed],
|
|
449
|
+
})
|
|
450
|
+
},
|
|
451
|
+
async sendSync() {
|
|
452
|
+
const signed = await sign()
|
|
453
|
+
return await viemClient.request({
|
|
454
|
+
method: 'eth_sendRawTransactionSync' as never,
|
|
455
|
+
params: [signed],
|
|
456
|
+
})
|
|
457
|
+
},
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function isSessionError(error: unknown) {
|
|
462
|
+
const code = getPrivyErrorCode(error)
|
|
463
|
+
if (typeof code === 'string') {
|
|
464
|
+
const normalized = code.toLowerCase()
|
|
465
|
+
if (privySessionErrorCodes.has(normalized)) return true
|
|
466
|
+
if (normalized.includes('session')) return true
|
|
467
|
+
if (normalized.includes('before_logged_in')) return true
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const message = getPrivyErrorMessage(error).toLowerCase()
|
|
471
|
+
return (
|
|
472
|
+
message.includes('missing privy token') ||
|
|
473
|
+
message.includes('must be logged in') ||
|
|
474
|
+
message.includes('not authenticated') ||
|
|
475
|
+
message.includes('not logged in') ||
|
|
476
|
+
message.includes('session expired')
|
|
477
|
+
)
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function getPrivyErrorCode(error: unknown): string | number | undefined {
|
|
481
|
+
if (!isObject(error)) return undefined
|
|
482
|
+
|
|
483
|
+
if (typeof error.code === 'string' || typeof error.code === 'number') return error.code
|
|
484
|
+
if (typeof error.error_code === 'string' || typeof error.error_code === 'number')
|
|
485
|
+
return error.error_code
|
|
486
|
+
if (typeof error.errorCode === 'string' || typeof error.errorCode === 'number')
|
|
487
|
+
return error.errorCode
|
|
488
|
+
|
|
489
|
+
return getPrivyErrorCode(error.cause)
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function getPrivyErrorMessage(error: unknown): string {
|
|
493
|
+
if (error instanceof Error) {
|
|
494
|
+
const caused = getPrivyErrorMessage(error.cause)
|
|
495
|
+
return caused ? `${error.message} ${caused}` : error.message
|
|
496
|
+
}
|
|
497
|
+
if (!isObject(error)) return ''
|
|
498
|
+
const own =
|
|
499
|
+
(typeof error.message === 'string' && error.message) ||
|
|
500
|
+
(typeof error.error === 'string' && error.error) ||
|
|
501
|
+
''
|
|
502
|
+
const caused = getPrivyErrorMessage(error.cause)
|
|
503
|
+
if (own && caused) return `${own} ${caused}`
|
|
504
|
+
return own || caused
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function isObject(value: unknown): value is Record<string, unknown> {
|
|
508
|
+
return typeof value === 'object' && value !== null
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return {
|
|
512
|
+
cleanup() {},
|
|
513
|
+
actions: {
|
|
514
|
+
async createAccount(parameters) {
|
|
515
|
+
const { authorizeAccessKey, personalSign } = parameters
|
|
516
|
+
if (personalSign && parameters.digest)
|
|
517
|
+
throw new ox_Provider.ProviderRpcError(
|
|
518
|
+
-32602,
|
|
519
|
+
'`digest` and `personalSign` cannot both be set on `wallet_connect`.',
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
const privyClient = await getPrivyClient()
|
|
523
|
+
const addresses = options.createAccount
|
|
524
|
+
? await options.createAccount({ client: privyClient, parameters })
|
|
525
|
+
: await options.loadAccounts({
|
|
526
|
+
client: privyClient,
|
|
527
|
+
parameters: {
|
|
528
|
+
...(authorizeAccessKey ? { authorizeAccessKey } : {}),
|
|
529
|
+
...(parameters.digest ? { digest: parameters.digest } : {}),
|
|
530
|
+
...(personalSign ? { personalSign } : {}),
|
|
531
|
+
},
|
|
532
|
+
})
|
|
533
|
+
await requireSession()
|
|
534
|
+
walletAccounts = selectWalletAccounts(await loadEthereumWallets(privyClient), addresses)
|
|
535
|
+
// Drop any in-flight `restore()` (from a concurrent `accountForSigning`)
|
|
536
|
+
// so re-entrant `restore()` calls don't `await` a stale IIFE that would
|
|
537
|
+
// later overwrite `walletAccounts` with the intersection against
|
|
538
|
+
// now-replaced persisted accounts.
|
|
539
|
+
restore_promise = undefined
|
|
540
|
+
|
|
541
|
+
const account = walletAccounts[0]
|
|
542
|
+
if (!account)
|
|
543
|
+
throw new ox_Provider.DisconnectedError({
|
|
544
|
+
message: 'Privy returned no wallet.',
|
|
545
|
+
})
|
|
546
|
+
|
|
547
|
+
const digest = personalSign ? hashMessage(personalSign.message) : parameters.digest
|
|
548
|
+
const keyAuthorization = authorizeAccessKey
|
|
549
|
+
? await authorizeAccessKeyFor(account, authorizeAccessKey)
|
|
550
|
+
: undefined
|
|
551
|
+
|
|
552
|
+
return {
|
|
553
|
+
accounts: walletAccounts.map((wallet, index) =>
|
|
554
|
+
toStoreAccount(wallet, index === 0 ? parameters.name : undefined),
|
|
555
|
+
),
|
|
556
|
+
...(personalSign ? { personalSign: { message: personalSign.message } } : {}),
|
|
557
|
+
...(keyAuthorization ? { keyAuthorization } : {}),
|
|
558
|
+
signature: digest
|
|
559
|
+
? await signPayload({
|
|
560
|
+
payload: digest,
|
|
561
|
+
walletAccount: account,
|
|
562
|
+
})
|
|
563
|
+
: undefined,
|
|
564
|
+
}
|
|
565
|
+
},
|
|
566
|
+
async loadAccounts(parameters) {
|
|
567
|
+
const { authorizeAccessKey, personalSign } =
|
|
568
|
+
parameters ?? ({} as Adapter.loadAccounts.Parameters)
|
|
569
|
+
if (personalSign && parameters?.digest)
|
|
570
|
+
throw new ox_Provider.ProviderRpcError(
|
|
571
|
+
-32602,
|
|
572
|
+
'`digest` and `personalSign` cannot both be set on `wallet_connect`.',
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
const privyClient = await getPrivyClient()
|
|
576
|
+
const addresses = await options.loadAccounts({ client: privyClient, parameters })
|
|
577
|
+
await requireSession()
|
|
578
|
+
walletAccounts = selectWalletAccounts(await loadEthereumWallets(privyClient), addresses)
|
|
579
|
+
// Drop any in-flight `restore()` (from a concurrent `accountForSigning`)
|
|
580
|
+
// so re-entrant `restore()` calls don't `await` a stale IIFE that would
|
|
581
|
+
// later overwrite `walletAccounts` with the intersection against
|
|
582
|
+
// now-replaced persisted accounts.
|
|
583
|
+
restore_promise = undefined
|
|
584
|
+
|
|
585
|
+
const digest = personalSign ? hashMessage(personalSign.message) : parameters?.digest
|
|
586
|
+
const account = walletAccounts[0]
|
|
587
|
+
const keyAuthorization =
|
|
588
|
+
authorizeAccessKey && account
|
|
589
|
+
? await authorizeAccessKeyFor(account, authorizeAccessKey)
|
|
590
|
+
: undefined
|
|
591
|
+
|
|
592
|
+
return {
|
|
593
|
+
accounts: walletAccounts.map((account) => toStoreAccount(account)),
|
|
594
|
+
...(personalSign ? { personalSign: { message: personalSign.message } } : {}),
|
|
595
|
+
...(keyAuthorization ? { keyAuthorization } : {}),
|
|
596
|
+
signature:
|
|
597
|
+
digest && account
|
|
598
|
+
? await signPayload({
|
|
599
|
+
payload: digest,
|
|
600
|
+
walletAccount: account,
|
|
601
|
+
})
|
|
602
|
+
: undefined,
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
async authorizeAccessKey(parameters) {
|
|
606
|
+
const account = await accountForSigning(undefined)
|
|
607
|
+
const keyAuthorization = await authorizeAccessKeyFor(account, parameters)
|
|
608
|
+
return { keyAuthorization, rootAddress: core_Address.from(account.address) }
|
|
609
|
+
},
|
|
610
|
+
async revokeAccessKey(parameters) {
|
|
611
|
+
const account = await accountForSigning(parameters.address)
|
|
612
|
+
const account_tempo = {
|
|
613
|
+
address: core_Address.from(account.address),
|
|
614
|
+
source: 'privy',
|
|
615
|
+
signTransaction: async (request: unknown) =>
|
|
616
|
+
await signPreparedTransaction(account, request),
|
|
617
|
+
type: 'local',
|
|
618
|
+
} satisfies {
|
|
619
|
+
address: Address
|
|
620
|
+
signTransaction: (request: unknown) => Promise<Hex.Hex>
|
|
621
|
+
source: 'privy'
|
|
622
|
+
type: 'local'
|
|
623
|
+
}
|
|
624
|
+
try {
|
|
625
|
+
await Actions.accessKey.revoke(getClient(), {
|
|
626
|
+
account: account_tempo as LocalAccount<'privy'>,
|
|
627
|
+
accessKey: parameters.accessKeyAddress,
|
|
628
|
+
})
|
|
629
|
+
} catch (error) {
|
|
630
|
+
if (!AccessKey.isUnavailableError(error)) throw error
|
|
631
|
+
}
|
|
632
|
+
AccessKey.remove({
|
|
633
|
+
accessKey: parameters.accessKeyAddress,
|
|
634
|
+
account: core_Address.from(account.address),
|
|
635
|
+
chainId: store.getState().chainId,
|
|
636
|
+
store,
|
|
637
|
+
})
|
|
638
|
+
},
|
|
639
|
+
async signPersonalMessage(parameters) {
|
|
640
|
+
const account = await accountForSigning(parameters.address)
|
|
641
|
+
return await signPayload({
|
|
642
|
+
payload: hashMessage({ raw: parameters.data }),
|
|
643
|
+
walletAccount: account,
|
|
644
|
+
})
|
|
645
|
+
},
|
|
646
|
+
async signTransaction(parameters) {
|
|
647
|
+
return await (await prepareTransaction(parameters)).sign()
|
|
648
|
+
},
|
|
649
|
+
async signTypedData(parameters) {
|
|
650
|
+
const account = await accountForSigning(parameters.address)
|
|
651
|
+
const typedData = JSON.parse(parameters.data) as {
|
|
652
|
+
domain: Record<string, unknown>
|
|
653
|
+
message: Record<string, unknown>
|
|
654
|
+
primaryType: string
|
|
655
|
+
types: Record<string, unknown>
|
|
656
|
+
}
|
|
657
|
+
return await signPayload({
|
|
658
|
+
payload: hashTypedData(typedData as never),
|
|
659
|
+
walletAccount: account,
|
|
660
|
+
})
|
|
661
|
+
},
|
|
662
|
+
async sendTransaction(parameters) {
|
|
663
|
+
return await (await prepareTransaction(parameters)).send()
|
|
664
|
+
},
|
|
665
|
+
async sendTransactionSync(parameters) {
|
|
666
|
+
return await (await prepareTransaction(parameters)).sendSync()
|
|
667
|
+
},
|
|
668
|
+
async disconnect() {
|
|
669
|
+
try {
|
|
670
|
+
const privyClient = await getPrivyClient()
|
|
671
|
+
const userId = await privyClient.user
|
|
672
|
+
.get()
|
|
673
|
+
.then(({ user }) => user.id)
|
|
674
|
+
.catch(() => undefined)
|
|
675
|
+
await privyClient.auth.logout(userId ? { userId } : undefined)
|
|
676
|
+
} finally {
|
|
677
|
+
clear()
|
|
678
|
+
}
|
|
679
|
+
},
|
|
680
|
+
},
|
|
681
|
+
}
|
|
682
|
+
})
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
export declare namespace privy {
|
|
686
|
+
/** Options for {@link privy}. */
|
|
687
|
+
type Options<client extends Client = Client> = {
|
|
688
|
+
/** Existing Privy client, such as `Privy` from `@privy-io/js-sdk-core`. */
|
|
689
|
+
client: client
|
|
690
|
+
/**
|
|
691
|
+
* Runs the Privy registration UI. May optionally return a subset of the user's
|
|
692
|
+
* embedded wallet addresses to expose to the provider; if omitted, the adapter
|
|
693
|
+
* exposes every embedded wallet on the resulting Privy user.
|
|
694
|
+
*
|
|
695
|
+
* The adapter materializes EIP-1193 providers internally via
|
|
696
|
+
* `client.embeddedWallet.getEthereumProvider` — callbacks should not.
|
|
697
|
+
*
|
|
698
|
+
* Defaults to `loadAccounts` — apps that don't distinguish register vs login
|
|
699
|
+
* can omit this.
|
|
700
|
+
*/
|
|
701
|
+
createAccount?:
|
|
702
|
+
| ((parameters: {
|
|
703
|
+
/** Initialized Privy client. */
|
|
704
|
+
client: client
|
|
705
|
+
/** Provider create-account parameters. */
|
|
706
|
+
parameters: Adapter.createAccount.Parameters
|
|
707
|
+
}) => Promise<AccountSelection>)
|
|
708
|
+
| undefined
|
|
709
|
+
/** Data URI of the provider icon. @default Black 1×1 SVG. */
|
|
710
|
+
icon?: `data:image/${string}` | undefined
|
|
711
|
+
/**
|
|
712
|
+
* Runs the Privy login UI in response to a user-initiated `wallet_connect`.
|
|
713
|
+
* May optionally return a subset of the user's embedded wallet addresses to
|
|
714
|
+
* expose to the provider; if omitted, the adapter exposes every embedded
|
|
715
|
+
* wallet on the Privy user.
|
|
716
|
+
*
|
|
717
|
+
* Silent restore on page reload pulls wallets directly from the Privy SDK
|
|
718
|
+
* (`client.user.get` + `client.embeddedWallet.getEthereumProvider`) and does
|
|
719
|
+
* NOT call this function.
|
|
720
|
+
*/
|
|
721
|
+
loadAccounts: (parameters: {
|
|
722
|
+
/** Initialized Privy client. */
|
|
723
|
+
client: client
|
|
724
|
+
/** Provider load-accounts parameters. */
|
|
725
|
+
parameters?: Adapter.loadAccounts.Parameters | undefined
|
|
726
|
+
}) => Promise<AccountSelection>
|
|
727
|
+
/** Display name of the provider. @default "Privy" */
|
|
728
|
+
name?: string | undefined
|
|
729
|
+
/** Reverse DNS identifier. @default "io.privy" */
|
|
730
|
+
rdns?: string | undefined
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Optional subset of embedded wallet addresses returned from `createAccount` /
|
|
735
|
+
* `loadAccounts`. `void`/`undefined` means "expose every embedded wallet".
|
|
736
|
+
*/
|
|
737
|
+
type AccountSelection = readonly Address[] | void
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* Minimal structural Privy client surface used by the adapter for session checks,
|
|
741
|
+
* silent restore, and disconnect. User-initiated `wallet_connect`/registration
|
|
742
|
+
* is delegated to the app's `loadAccounts` / `createAccount` callbacks.
|
|
743
|
+
*
|
|
744
|
+
* Satisfied by `Privy` from `@privy-io/js-sdk-core` — apps pass the SDK instance
|
|
745
|
+
* directly. The adapter never imports `@privy-io/js-sdk-core` itself; the structural
|
|
746
|
+
* shape keeps the dependency one-way.
|
|
747
|
+
*/
|
|
748
|
+
type Client = {
|
|
749
|
+
/** Auth API; the adapter only needs `logout`. */
|
|
750
|
+
auth: {
|
|
751
|
+
/**
|
|
752
|
+
* Clears the current Privy session. The adapter passes the current user id
|
|
753
|
+
* (when available) so multi-tab/multi-user setups scope the logout correctly.
|
|
754
|
+
*/
|
|
755
|
+
logout: (parameters?: { userId: string } | undefined) => Promise<void> | void
|
|
756
|
+
}
|
|
757
|
+
/** Embedded wallet API used by the adapter to materialize EIP-1193 providers. */
|
|
758
|
+
embeddedWallet: {
|
|
759
|
+
/** Returns an EIP-1193 provider for a Privy embedded Ethereum wallet. */
|
|
760
|
+
getEthereumProvider(parameters: {
|
|
761
|
+
wallet: LinkedAccount
|
|
762
|
+
entropyId: string
|
|
763
|
+
entropyIdVerifier: string
|
|
764
|
+
}): Promise<EthereumProvider> | EthereumProvider
|
|
765
|
+
}
|
|
766
|
+
/** Returns the current Privy access token, or `null` if no session. */
|
|
767
|
+
getAccessToken: () => Promise<string | null>
|
|
768
|
+
/** Initializes the client. Called once by the adapter, before any other method. */
|
|
769
|
+
initialize?: (() => Promise<void> | void) | undefined
|
|
770
|
+
/** User API used by the adapter to scope `auth.logout` and to silently restore wallets. */
|
|
771
|
+
user: {
|
|
772
|
+
/** Returns the currently authenticated Privy user. */
|
|
773
|
+
get: () => Promise<{ user: User }>
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/** Minimal Privy user shape used by the adapter for silent restore. */
|
|
778
|
+
type User = {
|
|
779
|
+
id: string
|
|
780
|
+
linked_accounts?: readonly LinkedAccount[] | undefined
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/** Minimal Privy linked account shape used by the adapter for silent restore. */
|
|
784
|
+
type LinkedAccount = {
|
|
785
|
+
address?: string | undefined
|
|
786
|
+
chain_type?: string | undefined
|
|
787
|
+
connector_type?: string | undefined
|
|
788
|
+
type?: string | undefined
|
|
789
|
+
wallet_client_type?: string | undefined
|
|
790
|
+
wallet_index?: number | undefined
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
/** Minimal EIP-1193 provider surface used by the adapter for `secp256k1_sign`. */
|
|
794
|
+
type EthereumProvider = {
|
|
795
|
+
request(parameters: {
|
|
796
|
+
method: string
|
|
797
|
+
params?: readonly unknown[] | undefined
|
|
798
|
+
}): Promise<unknown>
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Materialized Privy embedded wallet — the `{ address, provider }` shape the
|
|
803
|
+
* adapter caches internally after calling
|
|
804
|
+
* `client.embeddedWallet.getEthereumProvider`. The adapter calls
|
|
805
|
+
* `provider.request({ method: 'secp256k1_sign', params: [hash] })` for signing.
|
|
806
|
+
*/
|
|
807
|
+
type EmbeddedWallet = {
|
|
808
|
+
address: string
|
|
809
|
+
provider: EthereumProvider
|
|
810
|
+
}
|
|
811
|
+
}
|