@arcanahq/sdk-integrations 0.1.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/README.md ADDED
@@ -0,0 +1,287 @@
1
+ # Arcana SDK Integrations
2
+
3
+ Optional wallet and account-abstraction integrations for `@arcanahq/sdk`.
4
+
5
+ The base SDK remains provider-agnostic. This package normalizes Privy, Dynamic,
6
+ and ZeroDev setup into Arcana's device-auth flow without making those providers
7
+ required dependencies of `@arcanahq/sdk`.
8
+
9
+ For environment variables, provider dashboard setup, and complete project setup,
10
+ see [SETUP.md](./SETUP.md).
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ npm install @arcanahq/sdk @arcanahq/sdk-integrations viem
16
+ ```
17
+
18
+ Install only the provider packages your app uses.
19
+
20
+ Privy:
21
+
22
+ ```bash
23
+ npm install @privy-io/react-auth
24
+ ```
25
+
26
+ Dynamic JavaScript SDK:
27
+
28
+ ```bash
29
+ npm install @dynamic-labs-sdk/client @dynamic-labs-sdk/evm
30
+ ```
31
+
32
+ Dynamic React SDK:
33
+
34
+ ```bash
35
+ npm install @dynamic-labs/sdk-react-core @dynamic-labs/ethereum
36
+ ```
37
+
38
+ Dynamic + ZeroDev:
39
+
40
+ ```bash
41
+ npm install @dynamic-labs-sdk/client @dynamic-labs-sdk/evm @dynamic-labs-sdk/zerodev
42
+ ```
43
+
44
+ Local test auth:
45
+
46
+ ```bash
47
+ npm install @arcanahq/sdk-integrations viem
48
+ ```
49
+
50
+ ZeroDev:
51
+
52
+ ```bash
53
+ npm install @zerodev/sdk @zerodev/ecdsa-validator
54
+ ```
55
+
56
+ ## How Arcana Auth Works With Account Abstraction
57
+
58
+ Arcana device registration currently verifies an EIP-712 ECDSA signature by
59
+ recovering the signer address and requiring it to match `user_address`.
60
+
61
+ That means Arcana auth must use the EOA or embedded signer that controls the
62
+ smart account. Do not pass a Kernel/smart-account client as the Arcana auth
63
+ signer. Use ZeroDev/Kernel for onchain account-abstraction transactions after
64
+ Arcana auth is complete.
65
+
66
+ The ZeroDev helpers in this package enforce that: they require
67
+ `ownerWalletClient` or `signerWalletClient` and keep the smart-account address
68
+ only as metadata for your app.
69
+
70
+ ## Privy
71
+
72
+ Privy wallets expose an EIP-1193 provider through `wallet.getEthereumProvider()`.
73
+ This package can use that directly.
74
+
75
+ ```typescript
76
+ import { ArcanaClient } from '@arcanahq/sdk';
77
+ import {
78
+ createPrivyArcanaWalletAdapter,
79
+ registerArcanaDeviceWithAdapter,
80
+ } from '@arcanahq/sdk-integrations/privy';
81
+
82
+ const client = new ArcanaClient({ apiUrl: 'http://localhost:3003' });
83
+ await client.init();
84
+
85
+ const adapter = await createPrivyArcanaWalletAdapter({
86
+ wallet: privyWallet,
87
+ });
88
+
89
+ await registerArcanaDeviceWithAdapter(client, adapter, {
90
+ deviceName: 'Privy wallet',
91
+ });
92
+ ```
93
+
94
+ If you already converted a Privy wallet to a Viem account with Privy's
95
+ `toViemAccount`, pass it as `account`:
96
+
97
+ ```typescript
98
+ const adapter = await createPrivyArcanaWalletAdapter({ account });
99
+ await registerArcanaDeviceWithAdapter(client, adapter);
100
+ ```
101
+
102
+ ## Dynamic
103
+
104
+ Dynamic's current JavaScript SDK exposes Viem wallet clients through
105
+ `createWalletClientForWalletAccount` from `@dynamic-labs-sdk/evm/viem`.
106
+
107
+ ```typescript
108
+ import { getPrimaryWalletAccount } from '@dynamic-labs-sdk/client';
109
+ import { createWalletClientForWalletAccount } from '@dynamic-labs-sdk/evm/viem';
110
+ import {
111
+ ensureArcanaSessionWithDynamic,
112
+ } from '@arcanahq/sdk-integrations/dynamic';
113
+
114
+ const walletAccount = getPrimaryWalletAccount();
115
+ if (!walletAccount) {
116
+ throw new Error('No Dynamic wallet account is connected');
117
+ }
118
+
119
+ const walletClient = await createWalletClientForWalletAccount({
120
+ walletAccount,
121
+ });
122
+
123
+ const session = await ensureArcanaSessionWithDynamic({
124
+ client,
125
+ walletClient,
126
+ deviceName: 'Dynamic wallet',
127
+ });
128
+ console.log(`Arcana ${session.status} for ${session.address}`);
129
+ ```
130
+
131
+ For Dynamic React projects using wallet objects that expose `getWalletClient()`
132
+ or `getEthereumProvider()`, you can pass the wallet directly:
133
+
134
+ ```typescript
135
+ const session = await ensureArcanaSessionWithDynamic({
136
+ client,
137
+ wallet: primaryWallet,
138
+ chainId: 57073,
139
+ deviceName: 'Dynamic wallet',
140
+ });
141
+ ```
142
+
143
+ Use `ensureArcanaSessionWithDynamic` from the sign-in button after Dynamic has a
144
+ connected wallet. It restores an existing Arcana device session for that wallet
145
+ or registers a new one. Keep action guards separate: submit actions only after
146
+ this helper returns a `userId`.
147
+
148
+ ## Local Test Auth
149
+
150
+ Use the test adapter for local automation and agentic E2E runs where OTP or
151
+ hosted embedded-wallet flows would slow the loop down. It exposes the same
152
+ `ArcanaWalletAdapter` shape as Privy and Dynamic, but signs with a deterministic
153
+ local private key.
154
+
155
+ ```typescript
156
+ import {
157
+ createTestArcanaWalletAdapter,
158
+ registerArcanaDeviceWithAdapter,
159
+ } from '@arcanahq/sdk-integrations/test';
160
+
161
+ const adapter = createTestArcanaWalletAdapter({
162
+ chainId: 31337,
163
+ privateKey: import.meta.env.VITE_ARCANA_TEST_PRIVATE_KEY,
164
+ });
165
+
166
+ await registerArcanaDeviceWithAdapter(client, adapter, {
167
+ deviceName: 'Arcana test user',
168
+ });
169
+ ```
170
+
171
+ The default private key is Anvil's first account. Only enable this in local or
172
+ test environments.
173
+
174
+ ## Dynamic + ZeroDev
175
+
176
+ Dynamic's ZeroDev extension can return the EOA signer for a smart wallet account
177
+ with `getSignerForSmartWalletAccount`. Use that signer for Arcana auth, and use
178
+ the Kernel client for onchain UserOps.
179
+
180
+ ```typescript
181
+ import { getPrimaryWalletAccount } from '@dynamic-labs-sdk/client';
182
+ import { createKernelClientForWalletAccount } from '@dynamic-labs-sdk/zerodev';
183
+ import {
184
+ getSignerForSmartWalletAccount,
185
+ } from '@dynamic-labs-sdk/zerodev';
186
+ import {
187
+ createDynamicZeroDevArcanaWalletAdapter,
188
+ registerArcanaDeviceWithAdapter,
189
+ } from '@arcanahq/sdk-integrations/dynamic-zerodev';
190
+
191
+ const smartWalletAccount = getPrimaryWalletAccount();
192
+ if (!smartWalletAccount) {
193
+ throw new Error('No Dynamic smart wallet account is connected');
194
+ }
195
+
196
+ const signerWalletClient = await getSignerForSmartWalletAccount({
197
+ smartWalletAccount,
198
+ });
199
+
200
+ const kernelClient = await createKernelClientForWalletAccount({
201
+ walletAccount: smartWalletAccount,
202
+ });
203
+
204
+ const adapter = createDynamicZeroDevArcanaWalletAdapter({
205
+ signerWalletClient,
206
+ smartAccountAddress: kernelClient.account.address,
207
+ });
208
+
209
+ await registerArcanaDeviceWithAdapter(client, adapter, {
210
+ deviceName: 'Dynamic + ZeroDev',
211
+ });
212
+
213
+ const smartAccountAddress = adapter.getSmartAccountAddress();
214
+ ```
215
+
216
+ ## Privy + ZeroDev
217
+
218
+ Privy smart wallets are controlled by an embedded signer. Use the embedded
219
+ wallet signer for Arcana auth, then use your ZeroDev Kernel client for onchain
220
+ account-abstraction transactions.
221
+
222
+ ```typescript
223
+ import {
224
+ createPrivyArcanaWalletAdapter,
225
+ registerArcanaDeviceWithAdapter,
226
+ } from '@arcanahq/sdk-integrations/privy';
227
+
228
+ const authAdapter = await createPrivyArcanaWalletAdapter({
229
+ wallet: privyEmbeddedWallet,
230
+ });
231
+
232
+ await registerArcanaDeviceWithAdapter(client, authAdapter, {
233
+ deviceName: 'Privy + ZeroDev',
234
+ });
235
+ ```
236
+
237
+ If you build Kernel manually, pass the same owner signer you used for
238
+ `signerToEcdsaValidator` to Arcana:
239
+
240
+ ```typescript
241
+ import {
242
+ createPrivyZeroDevArcanaWalletAdapter,
243
+ registerArcanaDeviceWithAdapter,
244
+ } from '@arcanahq/sdk-integrations/privy-zerodev';
245
+
246
+ const adapter = createPrivyZeroDevArcanaWalletAdapter({
247
+ ownerWalletClient,
248
+ smartAccountAddress: kernelClient.account.address,
249
+ });
250
+
251
+ await registerArcanaDeviceWithAdapter(client, adapter);
252
+ ```
253
+
254
+ ## ZeroDev
255
+
256
+ ZeroDev Kernel accounts are smart accounts owned by a signer. Use the signer for
257
+ Arcana auth:
258
+
259
+ ```typescript
260
+ import {
261
+ createZeroDevArcanaWalletAdapter,
262
+ registerArcanaDeviceWithAdapter,
263
+ } from '@arcanahq/sdk-integrations/zerodev';
264
+
265
+ const adapter = createZeroDevArcanaWalletAdapter({
266
+ ownerWalletClient,
267
+ smartAccountAddress: kernelClient.account.address,
268
+ });
269
+
270
+ await registerArcanaDeviceWithAdapter(client, adapter, {
271
+ deviceName: 'ZeroDev owner signer',
272
+ });
273
+ ```
274
+
275
+ ## Failure Behavior
276
+
277
+ The adapters intentionally fail loudly when required integration pieces are
278
+ missing:
279
+
280
+ - invalid or missing EVM address
281
+ - missing `signTypedData`
282
+ - malformed `eth_accounts` or `eth_chainId` provider responses
283
+ - missing Arcana `deviceAuth`
284
+ - Kernel/smart-account client passed without an owner signer
285
+
286
+ There is no silent fallback from a smart account to an EOA signer. The app must
287
+ choose and pass the correct signer explicitly.
@@ -0,0 +1,15 @@
1
+ import type { ArcanaWalletAdapter, DeviceRegistrationResponse, DomainInfo } from '@arcanahq/sdk';
2
+ export interface ArcanaClientForDeviceRegistration {
3
+ auth: {
4
+ getDomainInfo(): Promise<DomainInfo>;
5
+ };
6
+ deviceAuth?: {
7
+ registerDevice(wallet: ArcanaWalletAdapter, address: `0x${string}`, chainId: number, verifyingContract: `0x${string}`, deviceName?: string): Promise<DeviceRegistrationResponse>;
8
+ };
9
+ }
10
+ export interface RegisterArcanaDeviceOptions {
11
+ chainId?: number;
12
+ verifyingContract?: `0x${string}`;
13
+ deviceName?: string;
14
+ }
15
+ export declare function registerArcanaDeviceWithAdapter(client: ArcanaClientForDeviceRegistration, adapter: ArcanaWalletAdapter, options?: RegisterArcanaDeviceOptions): Promise<DeviceRegistrationResponse>;
@@ -0,0 +1,16 @@
1
+ import { assertHexAddress } from './common.js';
2
+ export async function registerArcanaDeviceWithAdapter(client, adapter, options = {}) {
3
+ if (!client.deviceAuth) {
4
+ throw new Error('Arcana device authentication is not enabled on this client');
5
+ }
6
+ const address = await adapter.getAddress();
7
+ const domainInfo = await client.auth.getDomainInfo();
8
+ const domainVerifyingContract = domainInfo.verifyingContract || domainInfo.verifying_contract;
9
+ const verifyingContract = assertHexAddress(options.verifyingContract ?? domainVerifyingContract, 'Arcana auth verifying contract');
10
+ const adapterChainId = adapter.getChainId ? await adapter.getChainId() : undefined;
11
+ const chainId = options.chainId ?? adapterChainId ?? domainInfo.chainId;
12
+ if (chainId === undefined || !Number.isInteger(chainId)) {
13
+ throw new Error('Arcana device registration requires a chainId');
14
+ }
15
+ return client.deviceAuth.registerDevice(adapter, address, chainId, verifyingContract, options.deviceName);
16
+ }
@@ -0,0 +1,31 @@
1
+ import type { ArcanaTypedDataRequest, ArcanaWalletAdapter } from '@arcanahq/sdk';
2
+ export type HexAddress = `0x${string}`;
3
+ export type HexSignature = `0x${string}`;
4
+ export interface SignTypedDataCapable {
5
+ signTypedData(request: ArcanaTypedDataRequest & {
6
+ account?: HexAddress;
7
+ }): Promise<HexSignature>;
8
+ }
9
+ export interface ViemWalletClientLike extends SignTypedDataCapable {
10
+ account?: {
11
+ address?: string;
12
+ } | HexAddress | null;
13
+ getAddresses?: () => Promise<string[]>;
14
+ getChainId?: () => Promise<number>;
15
+ }
16
+ export interface Eip1193ProviderLike {
17
+ request(args: {
18
+ method: string;
19
+ params?: unknown[];
20
+ }): Promise<unknown>;
21
+ }
22
+ export interface AdapterOptions {
23
+ address?: string;
24
+ chainId?: number;
25
+ }
26
+ export declare function assertHexAddress(address: string | undefined | null, label?: string): HexAddress;
27
+ export declare function resolveAddress(provided: string | undefined, walletClient?: ViemWalletClientLike, provider?: Eip1193ProviderLike): Promise<HexAddress>;
28
+ export declare function resolveChainId(provided: number | undefined, walletClient?: ViemWalletClientLike, provider?: Eip1193ProviderLike): Promise<number | undefined>;
29
+ export declare function stringifyTypedData(request: ArcanaTypedDataRequest): string;
30
+ export declare function createViemArcanaWalletAdapter(walletClient: ViemWalletClientLike, options?: AdapterOptions): ArcanaWalletAdapter;
31
+ export declare function createEip1193ArcanaWalletAdapter(provider: Eip1193ProviderLike, options?: AdapterOptions): ArcanaWalletAdapter;
package/dist/common.js ADDED
@@ -0,0 +1,113 @@
1
+ export function assertHexAddress(address, label = 'address') {
2
+ if (!address || !/^0x[a-fA-F0-9]{40}$/.test(address)) {
3
+ throw new Error(`Arcana integration requires a valid EVM ${label}`);
4
+ }
5
+ return address.toLowerCase();
6
+ }
7
+ export async function resolveAddress(provided, walletClient, provider) {
8
+ if (provided) {
9
+ return assertHexAddress(provided);
10
+ }
11
+ const account = walletClient?.account;
12
+ if (typeof account === 'string') {
13
+ return assertHexAddress(account);
14
+ }
15
+ if (account && typeof account === 'object' && account.address) {
16
+ return assertHexAddress(account.address);
17
+ }
18
+ if (walletClient?.getAddresses) {
19
+ const addresses = await walletClient.getAddresses();
20
+ return assertHexAddress(addresses[0], 'wallet address');
21
+ }
22
+ if (provider) {
23
+ const addresses = await provider.request({ method: 'eth_accounts' });
24
+ if (!Array.isArray(addresses)) {
25
+ throw new Error('Arcana integration expected eth_accounts to return an array');
26
+ }
27
+ return assertHexAddress(addresses[0], 'wallet address');
28
+ }
29
+ throw new Error('Arcana integration could not resolve a wallet address');
30
+ }
31
+ export async function resolveChainId(provided, walletClient, provider) {
32
+ if (provided !== undefined) {
33
+ return provided;
34
+ }
35
+ if (walletClient?.getChainId) {
36
+ return walletClient.getChainId();
37
+ }
38
+ if (provider) {
39
+ const chainId = await provider.request({ method: 'eth_chainId' });
40
+ if (typeof chainId === 'string') {
41
+ const parsed = chainId.startsWith('0x') ? parseInt(chainId, 16) : parseInt(chainId, 10);
42
+ if (!Number.isInteger(parsed)) {
43
+ throw new Error('Arcana integration expected eth_chainId to be parseable as an integer');
44
+ }
45
+ return parsed;
46
+ }
47
+ if (typeof chainId === 'number') {
48
+ return chainId;
49
+ }
50
+ throw new Error('Arcana integration expected eth_chainId to return a string or number');
51
+ }
52
+ return undefined;
53
+ }
54
+ export function stringifyTypedData(request) {
55
+ return JSON.stringify({
56
+ domain: request.domain,
57
+ types: request.types,
58
+ primaryType: request.primaryType,
59
+ message: request.message,
60
+ }, (_key, value) => typeof value === 'bigint' ? value.toString() : value);
61
+ }
62
+ export function createViemArcanaWalletAdapter(walletClient, options = {}) {
63
+ if (!walletClient || typeof walletClient.signTypedData !== 'function') {
64
+ throw new Error('Arcana integration requires a Viem-compatible signer with signTypedData');
65
+ }
66
+ return {
67
+ async getAddress() {
68
+ return resolveAddress(options.address, walletClient);
69
+ },
70
+ async getChainId() {
71
+ const chainId = await resolveChainId(options.chainId, walletClient);
72
+ if (chainId === undefined) {
73
+ throw new Error('Arcana integration could not resolve chainId from the Viem signer');
74
+ }
75
+ return chainId;
76
+ },
77
+ async signTypedData(request) {
78
+ const address = await resolveAddress(options.address, walletClient);
79
+ return walletClient.signTypedData({
80
+ ...request,
81
+ account: address,
82
+ });
83
+ },
84
+ };
85
+ }
86
+ export function createEip1193ArcanaWalletAdapter(provider, options = {}) {
87
+ if (!provider || typeof provider.request !== 'function') {
88
+ throw new Error('Arcana integration requires an EIP-1193 provider with request()');
89
+ }
90
+ return {
91
+ async getAddress() {
92
+ return resolveAddress(options.address, undefined, provider);
93
+ },
94
+ async getChainId() {
95
+ const chainId = await resolveChainId(options.chainId, undefined, provider);
96
+ if (chainId === undefined) {
97
+ throw new Error('Arcana integration could not resolve chainId from the EIP-1193 provider');
98
+ }
99
+ return chainId;
100
+ },
101
+ async signTypedData(request) {
102
+ const address = await resolveAddress(options.address, undefined, provider);
103
+ const signature = await provider.request({
104
+ method: 'eth_signTypedData_v4',
105
+ params: [address, stringifyTypedData(request)],
106
+ });
107
+ if (typeof signature !== 'string' || !signature.startsWith('0x')) {
108
+ throw new Error('Arcana integration expected eth_signTypedData_v4 to return a hex signature');
109
+ }
110
+ return signature;
111
+ },
112
+ };
113
+ }
@@ -0,0 +1,8 @@
1
+ import type { ZeroDevArcanaAdapterOptions, ZeroDevArcanaWalletAdapter } from './zerodev.js';
2
+ export { registerArcanaDeviceWithAdapter } from './authenticate.js';
3
+ export type { ArcanaClientForDeviceRegistration, RegisterArcanaDeviceOptions } from './authenticate.js';
4
+ export { createDynamicArcanaWalletAdapter } from './dynamic.js';
5
+ export type { DynamicArcanaAdapterOptions, DynamicWalletLike } from './dynamic.js';
6
+ export { createZeroDevArcanaWalletAdapter } from './zerodev.js';
7
+ export type { ZeroDevAccountLike, ZeroDevArcanaAdapterOptions, ZeroDevArcanaWalletAdapter, ZeroDevClientLike } from './zerodev.js';
8
+ export declare function createDynamicZeroDevArcanaWalletAdapter(options: ZeroDevArcanaAdapterOptions): ZeroDevArcanaWalletAdapter;
@@ -0,0 +1,7 @@
1
+ import { createZeroDevArcanaWalletAdapter } from './zerodev.js';
2
+ export { registerArcanaDeviceWithAdapter } from './authenticate.js';
3
+ export { createDynamicArcanaWalletAdapter } from './dynamic.js';
4
+ export { createZeroDevArcanaWalletAdapter } from './zerodev.js';
5
+ export function createDynamicZeroDevArcanaWalletAdapter(options) {
6
+ return createZeroDevArcanaWalletAdapter(options);
7
+ }
@@ -0,0 +1,42 @@
1
+ import type { ArcanaWalletAdapter } from '@arcanahq/sdk';
2
+ import { AdapterOptions, Eip1193ProviderLike, ViemWalletClientLike } from './common.js';
3
+ import { ArcanaClientForDeviceRegistration, RegisterArcanaDeviceOptions } from './authenticate.js';
4
+ export { registerArcanaDeviceWithAdapter } from './authenticate.js';
5
+ export type { ArcanaClientForDeviceRegistration, RegisterArcanaDeviceOptions } from './authenticate.js';
6
+ export interface DynamicWalletLike {
7
+ address?: string;
8
+ getAddress?: () => string | Promise<string>;
9
+ getEthereumProvider?: () => Eip1193ProviderLike | Promise<Eip1193ProviderLike>;
10
+ getWalletClient?: () => ViemWalletClientLike | Promise<ViemWalletClientLike>;
11
+ connector?: {
12
+ getSigner?: () => unknown | Promise<unknown>;
13
+ };
14
+ }
15
+ export interface DynamicArcanaAdapterOptions extends AdapterOptions {
16
+ wallet?: DynamicWalletLike;
17
+ walletClient?: ViemWalletClientLike;
18
+ }
19
+ export interface DynamicArcanaAuthClient extends ArcanaClientForDeviceRegistration {
20
+ ensureAuthenticated?(): Promise<boolean>;
21
+ isAuthenticated?(): boolean;
22
+ deviceAuth?: ArcanaClientForDeviceRegistration['deviceAuth'] & {
23
+ activateWallet?(address: string | null): Promise<void>;
24
+ getUserId?(): string | null;
25
+ getWalletAddress?(): string | null;
26
+ };
27
+ }
28
+ export interface DynamicArcanaAuthOptions extends DynamicArcanaAdapterOptions, RegisterArcanaDeviceOptions {
29
+ client: DynamicArcanaAuthClient;
30
+ deviceName?: string;
31
+ ensureChain?: boolean;
32
+ }
33
+ export interface DynamicArcanaAuthResult {
34
+ adapter: ArcanaWalletAdapter;
35
+ address: `0x${string}`;
36
+ userId: string;
37
+ status: 'restored' | 'registered';
38
+ restored: boolean;
39
+ }
40
+ export declare function createDynamicArcanaWalletAdapter(options: DynamicArcanaAdapterOptions): Promise<ArcanaWalletAdapter>;
41
+ export declare function ensureArcanaSessionWithDynamic(options: DynamicArcanaAuthOptions): Promise<DynamicArcanaAuthResult>;
42
+ export declare function authenticateArcanaWithDynamic(options: DynamicArcanaAuthOptions): Promise<DynamicArcanaAuthResult>;
@@ -0,0 +1,64 @@
1
+ import { createEip1193ArcanaWalletAdapter, createViemArcanaWalletAdapter, } from './common.js';
2
+ import { registerArcanaDeviceWithAdapter, } from './authenticate.js';
3
+ export { registerArcanaDeviceWithAdapter } from './authenticate.js';
4
+ export async function createDynamicArcanaWalletAdapter(options) {
5
+ if (options.walletClient) {
6
+ return createViemArcanaWalletAdapter(options.walletClient, options);
7
+ }
8
+ if (!options.wallet) {
9
+ throw new Error('Dynamic Arcana integration requires a Dynamic wallet or Viem walletClient');
10
+ }
11
+ const walletAddress = options.address
12
+ ?? options.wallet.address
13
+ ?? (typeof options.wallet.getAddress === 'function' ? await options.wallet.getAddress() : undefined);
14
+ if (typeof options.wallet.getWalletClient === 'function') {
15
+ const walletClient = await options.wallet.getWalletClient();
16
+ return createViemArcanaWalletAdapter(walletClient, {
17
+ ...options,
18
+ address: walletAddress,
19
+ });
20
+ }
21
+ if (typeof options.wallet.getEthereumProvider === 'function') {
22
+ const provider = await options.wallet.getEthereumProvider();
23
+ return createEip1193ArcanaWalletAdapter(provider, {
24
+ ...options,
25
+ address: walletAddress,
26
+ });
27
+ }
28
+ throw new Error('Dynamic wallet does not expose getWalletClient() or getEthereumProvider(); pass a Dynamic Viem walletClient instead');
29
+ }
30
+ export async function ensureArcanaSessionWithDynamic(options) {
31
+ const { client, chainId, ensureChain = true } = options;
32
+ const adapter = await createDynamicArcanaWalletAdapter(options);
33
+ const address = await adapter.getAddress();
34
+ await client.deviceAuth?.activateWallet?.(address);
35
+ if (client.isAuthenticated?.()) {
36
+ const sessionWallet = client.deviceAuth?.getWalletAddress?.()?.toLowerCase() || '';
37
+ if (!sessionWallet || sessionWallet === address.toLowerCase()) {
38
+ const authenticated = await client.ensureAuthenticated?.();
39
+ const userId = client.deviceAuth?.getUserId?.() || '';
40
+ if (authenticated && userId) {
41
+ return { adapter, address, userId, status: 'restored', restored: true };
42
+ }
43
+ }
44
+ }
45
+ if (ensureChain && chainId !== undefined) {
46
+ const adapterChainId = await adapter.getChainId?.();
47
+ if (adapterChainId !== undefined && adapterChainId !== chainId) {
48
+ throw new Error(`Dynamic wallet is connected to chain ${adapterChainId}; switch to chain ${chainId} and retry`);
49
+ }
50
+ }
51
+ const registration = await registerArcanaDeviceWithAdapter(client, adapter, {
52
+ chainId,
53
+ verifyingContract: options.verifyingContract,
54
+ deviceName: options.deviceName,
55
+ });
56
+ const userId = registration.user_id || client.deviceAuth?.getUserId?.() || '';
57
+ if (!userId) {
58
+ throw new Error('Arcana Dynamic authentication did not return a user id');
59
+ }
60
+ return { adapter, address, userId, status: 'registered', restored: false };
61
+ }
62
+ export async function authenticateArcanaWithDynamic(options) {
63
+ return ensureArcanaSessionWithDynamic(options);
64
+ }
@@ -0,0 +1,12 @@
1
+ export { createEip1193ArcanaWalletAdapter, createViemArcanaWalletAdapter, } from './common.js';
2
+ export type { AdapterOptions, Eip1193ProviderLike, HexAddress, HexSignature, SignTypedDataCapable, ViemWalletClientLike, } from './common.js';
3
+ export { registerArcanaDeviceWithAdapter } from './authenticate.js';
4
+ export type { ArcanaClientForDeviceRegistration, RegisterArcanaDeviceOptions, } from './authenticate.js';
5
+ export { createPrivyArcanaWalletAdapter } from './privy.js';
6
+ export type { PrivyArcanaAdapterOptions, PrivyWalletLike } from './privy.js';
7
+ export { authenticateArcanaWithDynamic, createDynamicArcanaWalletAdapter, ensureArcanaSessionWithDynamic, } from './dynamic.js';
8
+ export type { DynamicArcanaAdapterOptions, DynamicArcanaAuthClient, DynamicArcanaAuthOptions, DynamicArcanaAuthResult, DynamicWalletLike, } from './dynamic.js';
9
+ export { DEFAULT_ARCANA_TEST_PRIVATE_KEY, createTestArcanaWalletAdapter, } from './test.js';
10
+ export type { TestArcanaAdapterOptions } from './test.js';
11
+ export { createZeroDevArcanaWalletAdapter } from './zerodev.js';
12
+ export type { ZeroDevAccountLike, ZeroDevArcanaAdapterOptions, ZeroDevArcanaWalletAdapter, ZeroDevClientLike, } from './zerodev.js';
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { createEip1193ArcanaWalletAdapter, createViemArcanaWalletAdapter, } from './common.js';
2
+ export { registerArcanaDeviceWithAdapter } from './authenticate.js';
3
+ export { createPrivyArcanaWalletAdapter } from './privy.js';
4
+ export { authenticateArcanaWithDynamic, createDynamicArcanaWalletAdapter, ensureArcanaSessionWithDynamic, } from './dynamic.js';
5
+ export { DEFAULT_ARCANA_TEST_PRIVATE_KEY, createTestArcanaWalletAdapter, } from './test.js';
6
+ export { createZeroDevArcanaWalletAdapter } from './zerodev.js';
@@ -0,0 +1,8 @@
1
+ import type { ZeroDevArcanaAdapterOptions, ZeroDevArcanaWalletAdapter } from './zerodev.js';
2
+ export { registerArcanaDeviceWithAdapter } from './authenticate.js';
3
+ export type { ArcanaClientForDeviceRegistration, RegisterArcanaDeviceOptions } from './authenticate.js';
4
+ export { createPrivyArcanaWalletAdapter } from './privy.js';
5
+ export type { PrivyArcanaAdapterOptions, PrivyWalletLike } from './privy.js';
6
+ export { createZeroDevArcanaWalletAdapter } from './zerodev.js';
7
+ export type { ZeroDevAccountLike, ZeroDevArcanaAdapterOptions, ZeroDevArcanaWalletAdapter, ZeroDevClientLike } from './zerodev.js';
8
+ export declare function createPrivyZeroDevArcanaWalletAdapter(options: ZeroDevArcanaAdapterOptions): ZeroDevArcanaWalletAdapter;
@@ -0,0 +1,7 @@
1
+ import { createZeroDevArcanaWalletAdapter } from './zerodev.js';
2
+ export { registerArcanaDeviceWithAdapter } from './authenticate.js';
3
+ export { createPrivyArcanaWalletAdapter } from './privy.js';
4
+ export { createZeroDevArcanaWalletAdapter } from './zerodev.js';
5
+ export function createPrivyZeroDevArcanaWalletAdapter(options) {
6
+ return createZeroDevArcanaWalletAdapter(options);
7
+ }
@@ -0,0 +1,14 @@
1
+ import type { ArcanaWalletAdapter } from '@arcanahq/sdk';
2
+ import { AdapterOptions, Eip1193ProviderLike, ViemWalletClientLike } from './common.js';
3
+ export { registerArcanaDeviceWithAdapter } from './authenticate.js';
4
+ export type { ArcanaClientForDeviceRegistration, RegisterArcanaDeviceOptions } from './authenticate.js';
5
+ export interface PrivyWalletLike {
6
+ address?: string;
7
+ getEthereumProvider?: () => Eip1193ProviderLike | Promise<Eip1193ProviderLike>;
8
+ }
9
+ export interface PrivyArcanaAdapterOptions extends AdapterOptions {
10
+ wallet?: PrivyWalletLike;
11
+ walletClient?: ViemWalletClientLike;
12
+ account?: ViemWalletClientLike;
13
+ }
14
+ export declare function createPrivyArcanaWalletAdapter(options: PrivyArcanaAdapterOptions): Promise<ArcanaWalletAdapter>;
package/dist/privy.js ADDED
@@ -0,0 +1,21 @@
1
+ import { createEip1193ArcanaWalletAdapter, createViemArcanaWalletAdapter, } from './common.js';
2
+ export { registerArcanaDeviceWithAdapter } from './authenticate.js';
3
+ export async function createPrivyArcanaWalletAdapter(options) {
4
+ if (options.walletClient) {
5
+ return createViemArcanaWalletAdapter(options.walletClient, options);
6
+ }
7
+ if (options.account) {
8
+ return createViemArcanaWalletAdapter(options.account, options);
9
+ }
10
+ if (!options.wallet) {
11
+ throw new Error('Privy Arcana integration requires a Privy wallet, Viem walletClient, or Viem account');
12
+ }
13
+ if (typeof options.wallet.getEthereumProvider !== 'function') {
14
+ throw new Error('Privy wallet does not expose getEthereumProvider(); pass toViemAccount(wallet) as account instead');
15
+ }
16
+ const provider = await options.wallet.getEthereumProvider();
17
+ return createEip1193ArcanaWalletAdapter(provider, {
18
+ ...options,
19
+ address: options.address ?? options.wallet.address,
20
+ });
21
+ }