@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 +287 -0
- package/dist/authenticate.d.ts +15 -0
- package/dist/authenticate.js +16 -0
- package/dist/common.d.ts +31 -0
- package/dist/common.js +113 -0
- package/dist/dynamic-zerodev.d.ts +8 -0
- package/dist/dynamic-zerodev.js +7 -0
- package/dist/dynamic.d.ts +42 -0
- package/dist/dynamic.js +64 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +6 -0
- package/dist/privy-zerodev.d.ts +8 -0
- package/dist/privy-zerodev.js +7 -0
- package/dist/privy.d.ts +14 -0
- package/dist/privy.js +21 -0
- package/dist/test.d.ts +12 -0
- package/dist/test.js +25 -0
- package/dist/zerodev.d.ts +32 -0
- package/dist/zerodev.js +27 -0
- package/package.json +113 -0
- package/src/__tests__/adapters.test.ts +286 -0
- package/src/authenticate.ts +56 -0
- package/src/common.ts +162 -0
- package/src/dynamic-zerodev.ts +15 -0
- package/src/dynamic.ts +135 -0
- package/src/index.ts +48 -0
- package/src/privy-zerodev.ts +15 -0
- package/src/privy.ts +48 -0
- package/src/test.ts +43 -0
- package/src/zerodev.ts +73 -0
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
|
+
}
|
package/dist/common.d.ts
ADDED
|
@@ -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>;
|
package/dist/dynamic.js
ADDED
|
@@ -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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|
package/dist/privy.d.ts
ADDED
|
@@ -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
|
+
}
|