@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/src/common.ts
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { ArcanaTypedDataRequest, ArcanaWalletAdapter } from '@arcanahq/sdk';
|
|
2
|
+
|
|
3
|
+
export type HexAddress = `0x${string}`;
|
|
4
|
+
export type HexSignature = `0x${string}`;
|
|
5
|
+
|
|
6
|
+
export interface SignTypedDataCapable {
|
|
7
|
+
signTypedData(request: ArcanaTypedDataRequest & { account?: HexAddress }): Promise<HexSignature>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ViemWalletClientLike extends SignTypedDataCapable {
|
|
11
|
+
account?: { address?: string } | HexAddress | null;
|
|
12
|
+
getAddresses?: () => Promise<string[]>;
|
|
13
|
+
getChainId?: () => Promise<number>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface Eip1193ProviderLike {
|
|
17
|
+
request(args: { method: string; params?: unknown[] }): Promise<unknown>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface AdapterOptions {
|
|
21
|
+
address?: string;
|
|
22
|
+
chainId?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function assertHexAddress(address: string | undefined | null, label: string = 'address'): HexAddress {
|
|
26
|
+
if (!address || !/^0x[a-fA-F0-9]{40}$/.test(address)) {
|
|
27
|
+
throw new Error(`Arcana integration requires a valid EVM ${label}`);
|
|
28
|
+
}
|
|
29
|
+
return address.toLowerCase() as HexAddress;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function resolveAddress(
|
|
33
|
+
provided: string | undefined,
|
|
34
|
+
walletClient?: ViemWalletClientLike,
|
|
35
|
+
provider?: Eip1193ProviderLike
|
|
36
|
+
): Promise<HexAddress> {
|
|
37
|
+
if (provided) {
|
|
38
|
+
return assertHexAddress(provided);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const account = walletClient?.account;
|
|
42
|
+
if (typeof account === 'string') {
|
|
43
|
+
return assertHexAddress(account);
|
|
44
|
+
}
|
|
45
|
+
if (account && typeof account === 'object' && account.address) {
|
|
46
|
+
return assertHexAddress(account.address);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (walletClient?.getAddresses) {
|
|
50
|
+
const addresses = await walletClient.getAddresses();
|
|
51
|
+
return assertHexAddress(addresses[0], 'wallet address');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (provider) {
|
|
55
|
+
const addresses = await provider.request({ method: 'eth_accounts' });
|
|
56
|
+
if (!Array.isArray(addresses)) {
|
|
57
|
+
throw new Error('Arcana integration expected eth_accounts to return an array');
|
|
58
|
+
}
|
|
59
|
+
return assertHexAddress(addresses[0] as string | undefined, 'wallet address');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
throw new Error('Arcana integration could not resolve a wallet address');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export async function resolveChainId(
|
|
66
|
+
provided: number | undefined,
|
|
67
|
+
walletClient?: ViemWalletClientLike,
|
|
68
|
+
provider?: Eip1193ProviderLike
|
|
69
|
+
): Promise<number | undefined> {
|
|
70
|
+
if (provided !== undefined) {
|
|
71
|
+
return provided;
|
|
72
|
+
}
|
|
73
|
+
if (walletClient?.getChainId) {
|
|
74
|
+
return walletClient.getChainId();
|
|
75
|
+
}
|
|
76
|
+
if (provider) {
|
|
77
|
+
const chainId = await provider.request({ method: 'eth_chainId' });
|
|
78
|
+
if (typeof chainId === 'string') {
|
|
79
|
+
const parsed = chainId.startsWith('0x') ? parseInt(chainId, 16) : parseInt(chainId, 10);
|
|
80
|
+
if (!Number.isInteger(parsed)) {
|
|
81
|
+
throw new Error('Arcana integration expected eth_chainId to be parseable as an integer');
|
|
82
|
+
}
|
|
83
|
+
return parsed;
|
|
84
|
+
}
|
|
85
|
+
if (typeof chainId === 'number') {
|
|
86
|
+
return chainId;
|
|
87
|
+
}
|
|
88
|
+
throw new Error('Arcana integration expected eth_chainId to return a string or number');
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function stringifyTypedData(request: ArcanaTypedDataRequest): string {
|
|
94
|
+
return JSON.stringify({
|
|
95
|
+
domain: request.domain,
|
|
96
|
+
types: request.types,
|
|
97
|
+
primaryType: request.primaryType,
|
|
98
|
+
message: request.message,
|
|
99
|
+
}, (_key, value) => typeof value === 'bigint' ? value.toString() : value);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function createViemArcanaWalletAdapter(
|
|
103
|
+
walletClient: ViemWalletClientLike,
|
|
104
|
+
options: AdapterOptions = {}
|
|
105
|
+
): ArcanaWalletAdapter {
|
|
106
|
+
if (!walletClient || typeof walletClient.signTypedData !== 'function') {
|
|
107
|
+
throw new Error('Arcana integration requires a Viem-compatible signer with signTypedData');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
async getAddress() {
|
|
112
|
+
return resolveAddress(options.address, walletClient);
|
|
113
|
+
},
|
|
114
|
+
async getChainId() {
|
|
115
|
+
const chainId = await resolveChainId(options.chainId, walletClient);
|
|
116
|
+
if (chainId === undefined) {
|
|
117
|
+
throw new Error('Arcana integration could not resolve chainId from the Viem signer');
|
|
118
|
+
}
|
|
119
|
+
return chainId;
|
|
120
|
+
},
|
|
121
|
+
async signTypedData(request) {
|
|
122
|
+
const address = await resolveAddress(options.address, walletClient);
|
|
123
|
+
return walletClient.signTypedData({
|
|
124
|
+
...request,
|
|
125
|
+
account: address,
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function createEip1193ArcanaWalletAdapter(
|
|
132
|
+
provider: Eip1193ProviderLike,
|
|
133
|
+
options: AdapterOptions = {}
|
|
134
|
+
): ArcanaWalletAdapter {
|
|
135
|
+
if (!provider || typeof provider.request !== 'function') {
|
|
136
|
+
throw new Error('Arcana integration requires an EIP-1193 provider with request()');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
async getAddress() {
|
|
141
|
+
return resolveAddress(options.address, undefined, provider);
|
|
142
|
+
},
|
|
143
|
+
async getChainId() {
|
|
144
|
+
const chainId = await resolveChainId(options.chainId, undefined, provider);
|
|
145
|
+
if (chainId === undefined) {
|
|
146
|
+
throw new Error('Arcana integration could not resolve chainId from the EIP-1193 provider');
|
|
147
|
+
}
|
|
148
|
+
return chainId;
|
|
149
|
+
},
|
|
150
|
+
async signTypedData(request) {
|
|
151
|
+
const address = await resolveAddress(options.address, undefined, provider);
|
|
152
|
+
const signature = await provider.request({
|
|
153
|
+
method: 'eth_signTypedData_v4',
|
|
154
|
+
params: [address, stringifyTypedData(request)],
|
|
155
|
+
});
|
|
156
|
+
if (typeof signature !== 'string' || !signature.startsWith('0x')) {
|
|
157
|
+
throw new Error('Arcana integration expected eth_signTypedData_v4 to return a hex signature');
|
|
158
|
+
}
|
|
159
|
+
return signature as HexSignature;
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createZeroDevArcanaWalletAdapter } from './zerodev.js';
|
|
2
|
+
import type { ZeroDevArcanaAdapterOptions, ZeroDevArcanaWalletAdapter } from './zerodev.js';
|
|
3
|
+
|
|
4
|
+
export { registerArcanaDeviceWithAdapter } from './authenticate.js';
|
|
5
|
+
export type { ArcanaClientForDeviceRegistration, RegisterArcanaDeviceOptions } from './authenticate.js';
|
|
6
|
+
export { createDynamicArcanaWalletAdapter } from './dynamic.js';
|
|
7
|
+
export type { DynamicArcanaAdapterOptions, DynamicWalletLike } from './dynamic.js';
|
|
8
|
+
export { createZeroDevArcanaWalletAdapter } from './zerodev.js';
|
|
9
|
+
export type { ZeroDevAccountLike, ZeroDevArcanaAdapterOptions, ZeroDevArcanaWalletAdapter, ZeroDevClientLike } from './zerodev.js';
|
|
10
|
+
|
|
11
|
+
export function createDynamicZeroDevArcanaWalletAdapter(
|
|
12
|
+
options: ZeroDevArcanaAdapterOptions
|
|
13
|
+
): ZeroDevArcanaWalletAdapter {
|
|
14
|
+
return createZeroDevArcanaWalletAdapter(options);
|
|
15
|
+
}
|
package/src/dynamic.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { ArcanaWalletAdapter } from '@arcanahq/sdk';
|
|
2
|
+
import {
|
|
3
|
+
AdapterOptions,
|
|
4
|
+
Eip1193ProviderLike,
|
|
5
|
+
ViemWalletClientLike,
|
|
6
|
+
createEip1193ArcanaWalletAdapter,
|
|
7
|
+
createViemArcanaWalletAdapter,
|
|
8
|
+
} from './common.js';
|
|
9
|
+
import {
|
|
10
|
+
ArcanaClientForDeviceRegistration,
|
|
11
|
+
RegisterArcanaDeviceOptions,
|
|
12
|
+
registerArcanaDeviceWithAdapter,
|
|
13
|
+
} from './authenticate.js';
|
|
14
|
+
|
|
15
|
+
export { registerArcanaDeviceWithAdapter } from './authenticate.js';
|
|
16
|
+
export type { ArcanaClientForDeviceRegistration, RegisterArcanaDeviceOptions } from './authenticate.js';
|
|
17
|
+
|
|
18
|
+
export interface DynamicWalletLike {
|
|
19
|
+
address?: string;
|
|
20
|
+
getAddress?: () => string | Promise<string>;
|
|
21
|
+
getEthereumProvider?: () => Eip1193ProviderLike | Promise<Eip1193ProviderLike>;
|
|
22
|
+
getWalletClient?: () => ViemWalletClientLike | Promise<ViemWalletClientLike>;
|
|
23
|
+
connector?: {
|
|
24
|
+
getSigner?: () => unknown | Promise<unknown>;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface DynamicArcanaAdapterOptions extends AdapterOptions {
|
|
29
|
+
wallet?: DynamicWalletLike;
|
|
30
|
+
walletClient?: ViemWalletClientLike;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface DynamicArcanaAuthClient extends ArcanaClientForDeviceRegistration {
|
|
34
|
+
ensureAuthenticated?(): Promise<boolean>;
|
|
35
|
+
isAuthenticated?(): boolean;
|
|
36
|
+
deviceAuth?: ArcanaClientForDeviceRegistration['deviceAuth'] & {
|
|
37
|
+
activateWallet?(address: string | null): Promise<void>;
|
|
38
|
+
getUserId?(): string | null;
|
|
39
|
+
getWalletAddress?(): string | null;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface DynamicArcanaAuthOptions extends DynamicArcanaAdapterOptions, RegisterArcanaDeviceOptions {
|
|
44
|
+
client: DynamicArcanaAuthClient;
|
|
45
|
+
deviceName?: string;
|
|
46
|
+
ensureChain?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface DynamicArcanaAuthResult {
|
|
50
|
+
adapter: ArcanaWalletAdapter;
|
|
51
|
+
address: `0x${string}`;
|
|
52
|
+
userId: string;
|
|
53
|
+
status: 'restored' | 'registered';
|
|
54
|
+
restored: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function createDynamicArcanaWalletAdapter(
|
|
58
|
+
options: DynamicArcanaAdapterOptions
|
|
59
|
+
): Promise<ArcanaWalletAdapter> {
|
|
60
|
+
if (options.walletClient) {
|
|
61
|
+
return createViemArcanaWalletAdapter(options.walletClient, options);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!options.wallet) {
|
|
65
|
+
throw new Error('Dynamic Arcana integration requires a Dynamic wallet or Viem walletClient');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const walletAddress = options.address
|
|
69
|
+
?? options.wallet.address
|
|
70
|
+
?? (typeof options.wallet.getAddress === 'function' ? await options.wallet.getAddress() : undefined);
|
|
71
|
+
|
|
72
|
+
if (typeof options.wallet.getWalletClient === 'function') {
|
|
73
|
+
const walletClient = await options.wallet.getWalletClient();
|
|
74
|
+
return createViemArcanaWalletAdapter(walletClient, {
|
|
75
|
+
...options,
|
|
76
|
+
address: walletAddress,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (typeof options.wallet.getEthereumProvider === 'function') {
|
|
81
|
+
const provider = await options.wallet.getEthereumProvider();
|
|
82
|
+
return createEip1193ArcanaWalletAdapter(provider, {
|
|
83
|
+
...options,
|
|
84
|
+
address: walletAddress,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
throw new Error('Dynamic wallet does not expose getWalletClient() or getEthereumProvider(); pass a Dynamic Viem walletClient instead');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export async function ensureArcanaSessionWithDynamic(
|
|
92
|
+
options: DynamicArcanaAuthOptions
|
|
93
|
+
): Promise<DynamicArcanaAuthResult> {
|
|
94
|
+
const { client, chainId, ensureChain = true } = options;
|
|
95
|
+
const adapter = await createDynamicArcanaWalletAdapter(options);
|
|
96
|
+
const address = await adapter.getAddress();
|
|
97
|
+
|
|
98
|
+
await client.deviceAuth?.activateWallet?.(address);
|
|
99
|
+
|
|
100
|
+
if (client.isAuthenticated?.()) {
|
|
101
|
+
const sessionWallet = client.deviceAuth?.getWalletAddress?.()?.toLowerCase() || '';
|
|
102
|
+
if (!sessionWallet || sessionWallet === address.toLowerCase()) {
|
|
103
|
+
const authenticated = await client.ensureAuthenticated?.();
|
|
104
|
+
const userId = client.deviceAuth?.getUserId?.() || '';
|
|
105
|
+
if (authenticated && userId) {
|
|
106
|
+
return { adapter, address, userId, status: 'restored', restored: true };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (ensureChain && chainId !== undefined) {
|
|
112
|
+
const adapterChainId = await adapter.getChainId?.();
|
|
113
|
+
if (adapterChainId !== undefined && adapterChainId !== chainId) {
|
|
114
|
+
throw new Error(`Dynamic wallet is connected to chain ${adapterChainId}; switch to chain ${chainId} and retry`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const registration = await registerArcanaDeviceWithAdapter(client, adapter, {
|
|
119
|
+
chainId,
|
|
120
|
+
verifyingContract: options.verifyingContract,
|
|
121
|
+
deviceName: options.deviceName,
|
|
122
|
+
});
|
|
123
|
+
const userId = registration.user_id || client.deviceAuth?.getUserId?.() || '';
|
|
124
|
+
if (!userId) {
|
|
125
|
+
throw new Error('Arcana Dynamic authentication did not return a user id');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return { adapter, address, userId, status: 'registered', restored: false };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function authenticateArcanaWithDynamic(
|
|
132
|
+
options: DynamicArcanaAuthOptions
|
|
133
|
+
): Promise<DynamicArcanaAuthResult> {
|
|
134
|
+
return ensureArcanaSessionWithDynamic(options);
|
|
135
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createEip1193ArcanaWalletAdapter,
|
|
3
|
+
createViemArcanaWalletAdapter,
|
|
4
|
+
} from './common.js';
|
|
5
|
+
export type {
|
|
6
|
+
AdapterOptions,
|
|
7
|
+
Eip1193ProviderLike,
|
|
8
|
+
HexAddress,
|
|
9
|
+
HexSignature,
|
|
10
|
+
SignTypedDataCapable,
|
|
11
|
+
ViemWalletClientLike,
|
|
12
|
+
} from './common.js';
|
|
13
|
+
|
|
14
|
+
export { registerArcanaDeviceWithAdapter } from './authenticate.js';
|
|
15
|
+
export type {
|
|
16
|
+
ArcanaClientForDeviceRegistration,
|
|
17
|
+
RegisterArcanaDeviceOptions,
|
|
18
|
+
} from './authenticate.js';
|
|
19
|
+
|
|
20
|
+
export { createPrivyArcanaWalletAdapter } from './privy.js';
|
|
21
|
+
export type { PrivyArcanaAdapterOptions, PrivyWalletLike } from './privy.js';
|
|
22
|
+
|
|
23
|
+
export {
|
|
24
|
+
authenticateArcanaWithDynamic,
|
|
25
|
+
createDynamicArcanaWalletAdapter,
|
|
26
|
+
ensureArcanaSessionWithDynamic,
|
|
27
|
+
} from './dynamic.js';
|
|
28
|
+
export type {
|
|
29
|
+
DynamicArcanaAdapterOptions,
|
|
30
|
+
DynamicArcanaAuthClient,
|
|
31
|
+
DynamicArcanaAuthOptions,
|
|
32
|
+
DynamicArcanaAuthResult,
|
|
33
|
+
DynamicWalletLike,
|
|
34
|
+
} from './dynamic.js';
|
|
35
|
+
|
|
36
|
+
export {
|
|
37
|
+
DEFAULT_ARCANA_TEST_PRIVATE_KEY,
|
|
38
|
+
createTestArcanaWalletAdapter,
|
|
39
|
+
} from './test.js';
|
|
40
|
+
export type { TestArcanaAdapterOptions } from './test.js';
|
|
41
|
+
|
|
42
|
+
export { createZeroDevArcanaWalletAdapter } from './zerodev.js';
|
|
43
|
+
export type {
|
|
44
|
+
ZeroDevAccountLike,
|
|
45
|
+
ZeroDevArcanaAdapterOptions,
|
|
46
|
+
ZeroDevArcanaWalletAdapter,
|
|
47
|
+
ZeroDevClientLike,
|
|
48
|
+
} from './zerodev.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { createZeroDevArcanaWalletAdapter } from './zerodev.js';
|
|
2
|
+
import type { ZeroDevArcanaAdapterOptions, ZeroDevArcanaWalletAdapter } from './zerodev.js';
|
|
3
|
+
|
|
4
|
+
export { registerArcanaDeviceWithAdapter } from './authenticate.js';
|
|
5
|
+
export type { ArcanaClientForDeviceRegistration, RegisterArcanaDeviceOptions } from './authenticate.js';
|
|
6
|
+
export { createPrivyArcanaWalletAdapter } from './privy.js';
|
|
7
|
+
export type { PrivyArcanaAdapterOptions, PrivyWalletLike } from './privy.js';
|
|
8
|
+
export { createZeroDevArcanaWalletAdapter } from './zerodev.js';
|
|
9
|
+
export type { ZeroDevAccountLike, ZeroDevArcanaAdapterOptions, ZeroDevArcanaWalletAdapter, ZeroDevClientLike } from './zerodev.js';
|
|
10
|
+
|
|
11
|
+
export function createPrivyZeroDevArcanaWalletAdapter(
|
|
12
|
+
options: ZeroDevArcanaAdapterOptions
|
|
13
|
+
): ZeroDevArcanaWalletAdapter {
|
|
14
|
+
return createZeroDevArcanaWalletAdapter(options);
|
|
15
|
+
}
|
package/src/privy.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ArcanaWalletAdapter } from '@arcanahq/sdk';
|
|
2
|
+
import {
|
|
3
|
+
AdapterOptions,
|
|
4
|
+
Eip1193ProviderLike,
|
|
5
|
+
ViemWalletClientLike,
|
|
6
|
+
createEip1193ArcanaWalletAdapter,
|
|
7
|
+
createViemArcanaWalletAdapter,
|
|
8
|
+
} from './common.js';
|
|
9
|
+
|
|
10
|
+
export { registerArcanaDeviceWithAdapter } from './authenticate.js';
|
|
11
|
+
export type { ArcanaClientForDeviceRegistration, RegisterArcanaDeviceOptions } from './authenticate.js';
|
|
12
|
+
|
|
13
|
+
export interface PrivyWalletLike {
|
|
14
|
+
address?: string;
|
|
15
|
+
getEthereumProvider?: () => Eip1193ProviderLike | Promise<Eip1193ProviderLike>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PrivyArcanaAdapterOptions extends AdapterOptions {
|
|
19
|
+
wallet?: PrivyWalletLike;
|
|
20
|
+
walletClient?: ViemWalletClientLike;
|
|
21
|
+
account?: ViemWalletClientLike;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function createPrivyArcanaWalletAdapter(
|
|
25
|
+
options: PrivyArcanaAdapterOptions
|
|
26
|
+
): Promise<ArcanaWalletAdapter> {
|
|
27
|
+
if (options.walletClient) {
|
|
28
|
+
return createViemArcanaWalletAdapter(options.walletClient, options);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (options.account) {
|
|
32
|
+
return createViemArcanaWalletAdapter(options.account, options);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (!options.wallet) {
|
|
36
|
+
throw new Error('Privy Arcana integration requires a Privy wallet, Viem walletClient, or Viem account');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (typeof options.wallet.getEthereumProvider !== 'function') {
|
|
40
|
+
throw new Error('Privy wallet does not expose getEthereumProvider(); pass toViemAccount(wallet) as account instead');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const provider = await options.wallet.getEthereumProvider();
|
|
44
|
+
return createEip1193ArcanaWalletAdapter(provider, {
|
|
45
|
+
...options,
|
|
46
|
+
address: options.address ?? options.wallet.address,
|
|
47
|
+
});
|
|
48
|
+
}
|
package/src/test.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
2
|
+
import type { ArcanaWalletAdapter } from '@arcanahq/sdk';
|
|
3
|
+
import type { HexAddress } from './common.js';
|
|
4
|
+
import { assertHexAddress } from './common.js';
|
|
5
|
+
|
|
6
|
+
export const DEFAULT_ARCANA_TEST_PRIVATE_KEY =
|
|
7
|
+
'0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' as const;
|
|
8
|
+
|
|
9
|
+
export interface TestArcanaAdapterOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Deterministic private key used only for local/test auth. Defaults to the
|
|
12
|
+
* first Anvil account so local agents can authenticate without OTP flows.
|
|
13
|
+
*/
|
|
14
|
+
privateKey?: `0x${string}`;
|
|
15
|
+
/** Chain id to report to Arcana device registration. */
|
|
16
|
+
chainId?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createTestArcanaWalletAdapter(
|
|
20
|
+
options: TestArcanaAdapterOptions = {}
|
|
21
|
+
): ArcanaWalletAdapter {
|
|
22
|
+
const privateKey = options.privateKey ?? DEFAULT_ARCANA_TEST_PRIVATE_KEY;
|
|
23
|
+
const account = privateKeyToAccount(privateKey);
|
|
24
|
+
const address = assertHexAddress(account.address, 'test wallet address');
|
|
25
|
+
const chainId = options.chainId ?? 31337;
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
getAddress(): HexAddress {
|
|
29
|
+
return address;
|
|
30
|
+
},
|
|
31
|
+
getChainId(): number {
|
|
32
|
+
return chainId;
|
|
33
|
+
},
|
|
34
|
+
async signTypedData(request) {
|
|
35
|
+
return account.signTypedData({
|
|
36
|
+
domain: request.domain,
|
|
37
|
+
types: request.types,
|
|
38
|
+
primaryType: request.primaryType,
|
|
39
|
+
message: request.message,
|
|
40
|
+
} as Parameters<typeof account.signTypedData>[0]);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
package/src/zerodev.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { ArcanaWalletAdapter } from '@arcanahq/sdk';
|
|
2
|
+
import {
|
|
3
|
+
AdapterOptions,
|
|
4
|
+
ViemWalletClientLike,
|
|
5
|
+
assertHexAddress,
|
|
6
|
+
createViemArcanaWalletAdapter,
|
|
7
|
+
} from './common.js';
|
|
8
|
+
|
|
9
|
+
export { registerArcanaDeviceWithAdapter } from './authenticate.js';
|
|
10
|
+
export type { ArcanaClientForDeviceRegistration, RegisterArcanaDeviceOptions } from './authenticate.js';
|
|
11
|
+
|
|
12
|
+
export interface ZeroDevAccountLike {
|
|
13
|
+
address?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ZeroDevClientLike extends ViemWalletClientLike {
|
|
17
|
+
account?: ZeroDevAccountLike | `0x${string}` | null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ZeroDevArcanaAdapterOptions extends AdapterOptions {
|
|
21
|
+
/**
|
|
22
|
+
* EOA or embedded wallet signer that controls the Kernel account.
|
|
23
|
+
*
|
|
24
|
+
* Arcana device registration currently verifies ECDSA recovery against
|
|
25
|
+
* user_address, so the signer must be the controlling wallet, not the Kernel
|
|
26
|
+
* smart-account address.
|
|
27
|
+
*/
|
|
28
|
+
ownerWalletClient?: ViemWalletClientLike;
|
|
29
|
+
/** Alias used by Dynamic's ZeroDev helper docs. */
|
|
30
|
+
signerWalletClient?: ViemWalletClientLike;
|
|
31
|
+
/** Optional smart account address for app bookkeeping. It is not used for Arcana auth. */
|
|
32
|
+
smartAccountAddress?: string;
|
|
33
|
+
/** @deprecated Pass ownerWalletClient/signerWalletClient. Kernel signatures are not valid for Arcana auth yet. */
|
|
34
|
+
kernelClient?: ZeroDevClientLike;
|
|
35
|
+
/** @deprecated Use smartAccountAddress. */
|
|
36
|
+
smartAccount?: ZeroDevAccountLike;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ZeroDevArcanaWalletAdapter extends ArcanaWalletAdapter {
|
|
40
|
+
getSmartAccountAddress(): `0x${string}` | null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function createZeroDevArcanaWalletAdapter(
|
|
44
|
+
options: ZeroDevArcanaAdapterOptions
|
|
45
|
+
): ZeroDevArcanaWalletAdapter {
|
|
46
|
+
const ownerWalletClient = options.ownerWalletClient ?? options.signerWalletClient;
|
|
47
|
+
if (!ownerWalletClient) {
|
|
48
|
+
throw new Error('ZeroDev Arcana integration requires ownerWalletClient/signerWalletClient for Arcana auth; Kernel smart-account signatures are not accepted by Arcana device registration yet');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (options.kernelClient && !options.ownerWalletClient && !options.signerWalletClient) {
|
|
52
|
+
throw new Error('ZeroDev kernelClient was provided without an owner signer. Pass ownerWalletClient/signerWalletClient for Arcana auth');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const smartAccountAddress = options.smartAccountAddress
|
|
56
|
+
?? (typeof options.kernelClient?.account === 'string' ? options.kernelClient.account : options.kernelClient?.account?.address)
|
|
57
|
+
?? options.smartAccount?.address
|
|
58
|
+
?? null;
|
|
59
|
+
|
|
60
|
+
const adapter = createViemArcanaWalletAdapter(ownerWalletClient, {
|
|
61
|
+
...options,
|
|
62
|
+
address: options.address,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
...adapter,
|
|
67
|
+
getSmartAccountAddress() {
|
|
68
|
+
return smartAccountAddress
|
|
69
|
+
? assertHexAddress(smartAccountAddress, 'ZeroDev smart account address')
|
|
70
|
+
: null;
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|