emblem-vault-ai-signers 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,210 @@
1
+ # Emblem Vault AI Signers
2
+
3
+ Remote signer adapters for Emblem Vault that plug into popular Ethereum libraries:
4
+
5
+ - `toViemAccount()` – creates a viem `Account` that signs via Emblem
6
+ - `toEthersWallet()` – creates an ethers v6 `Signer` that signs via Emblem
7
+ - `toWeb3Adapter()` – returns a minimal Web3-style signer adapter (EVM)
8
+ - `toSolanaWeb3Signer()` – returns a stub Solana signer with `publicKey`
9
+ - `toSolanaKitSigner()` – returns a stub Solana signer with `publicKey`
10
+
11
+ > Note: The ethers adapter targets ethers v6.
12
+
13
+ ## Install
14
+
15
+ ```
16
+ npm install emblem-vault-ai-signers
17
+ # and bring your own peers
18
+ npm install ethers viem
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ```ts
24
+ import { createEmblemClient } from "emblem-vault-ai-signers";
25
+ import { createPublicClient, http } from "viem";
26
+ import { mainnet } from "viem/chains";
27
+ import { JsonRpcProvider } from "ethers";
28
+
29
+ const client = createEmblemClient({
30
+ apiKey: "your-x-api-key",
31
+ // baseUrl: "https://api.emblemvault.ai" // optional (tests use https://dev-api.emblemvault.ai)
32
+ });
33
+
34
+ // viem
35
+ const account = await client.toViemAccount();
36
+ const viemClient = createPublicClient({ chain: mainnet, transport: http() });
37
+ // e.g. viemClient.signMessage({ account, message: "hello" })
38
+
39
+ // ethers v6
40
+ const provider = new JsonRpcProvider(process.env.RPC_URL!);
41
+ const wallet = await client.toEthersWallet(provider);
42
+ // e.g. await wallet.sendTransaction({ to: "0x...", value: 1n })
43
+
44
+ // web3.js-like adapter (minimal)
45
+ const web3Adapter = await client.toWeb3Adapter();
46
+ await web3Adapter.signMessage("hello");
47
+
48
+ // Solana stubs (address only; signing not yet implemented)
49
+ const solWeb3 = await client.toSolanaWeb3Signer();
50
+ console.log(solWeb3.publicKey);
51
+ const solKit = await client.toSolanaKitSigner();
52
+ console.log(solKit.publicKey);
53
+ ```
54
+
55
+ ## Replace Private Keys (Examples)
56
+
57
+ Below are quick swaps showing how to remove local private keys and route signing through Emblem.
58
+
59
+ ### viem
60
+
61
+ Old (local private key):
62
+ ```ts
63
+ import { privateKeyToAccount } from "viem/accounts";
64
+ import { createWalletClient, http } from "viem";
65
+ import { sepolia } from "viem/chains";
66
+
67
+ const account = privateKeyToAccount(process.env.PK as `0x${string}`);
68
+ const wallet = createWalletClient({ chain: sepolia, transport: http(process.env.RPC_URL!), account });
69
+ await wallet.sendTransaction({ to: "0x...", value: 1n });
70
+ ```
71
+
72
+ New (Emblem remote signer):
73
+ ```ts
74
+ import { createEmblemClient } from "emblem-vault-ai-signers";
75
+ import { createWalletClient, http } from "viem";
76
+ import { sepolia } from "viem/chains";
77
+
78
+ const client = createEmblemClient({ apiKey: process.env.EMBLEM_API_KEY!, baseUrl: process.env.EMBLEM_BASE_URL });
79
+ const account = await client.toViemAccount();
80
+ const wallet = createWalletClient({ chain: sepolia, transport: http(process.env.RPC_URL!), account });
81
+
82
+ // Message & typed data
83
+ await wallet.signMessage({ account, message: "hello" });
84
+ await wallet.signTypedData({
85
+ account,
86
+ domain: { name: "App", version: "1", chainId: 11155111 },
87
+ types: { Test: [{ name: "x", type: "uint256" }] },
88
+ primaryType: "Test",
89
+ message: { x: 42 },
90
+ });
91
+
92
+ // Transactions
93
+ await wallet.sendTransaction({ account, to: "0x...", value: 1n });
94
+ ```
95
+
96
+ ### ethers v6
97
+
98
+ Old (local private key):
99
+ ```ts
100
+ import { Wallet, JsonRpcProvider } from "ethers";
101
+ const provider = new JsonRpcProvider(process.env.RPC_URL!);
102
+ const wallet = new Wallet(process.env.PK!, provider);
103
+ await wallet.sendTransaction({ to: "0x...", value: 1n });
104
+ ```
105
+
106
+ New (Emblem remote signer):
107
+ ```ts
108
+ import { createEmblemClient } from "emblem-vault-ai-signers";
109
+ import { JsonRpcProvider } from "ethers";
110
+
111
+ const client = createEmblemClient({ apiKey: process.env.EMBLEM_API_KEY!, baseUrl: process.env.EMBLEM_BASE_URL });
112
+ const provider = new JsonRpcProvider(process.env.RPC_URL!);
113
+ const wallet = await client.toEthersWallet(provider);
114
+
115
+ await wallet.signMessage("hello");
116
+ await wallet.sendTransaction({ to: "0x...", value: 1n });
117
+ ```
118
+
119
+ ### web3.js (minimal adapter)
120
+
121
+ Emblem includes a small adapter that returns signatures and raw transactions you can broadcast.
122
+
123
+ ```ts
124
+ import { createEmblemClient } from "emblem-vault-ai-signers";
125
+ import { JsonRpcProvider } from "ethers"; // for broadcasting raw tx
126
+
127
+ const client = createEmblemClient({ apiKey: process.env.EMBLEM_API_KEY!, baseUrl: process.env.EMBLEM_BASE_URL });
128
+ const adapter = await client.toWeb3Adapter();
129
+
130
+ // Sign message / typed data
131
+ const sig1 = await adapter.signMessage("hello");
132
+ const sig2 = await adapter.signTypedData(
133
+ { name: "App", version: "1", chainId: 11155111 },
134
+ { Test: [{ name: "x", type: "uint256" }] },
135
+ { x: 42 }
136
+ );
137
+
138
+ // Sign transaction, then broadcast using ethers provider
139
+ const { rawTransaction } = await adapter.signTransaction({
140
+ to: "0x...",
141
+ value: 1n,
142
+ chainId: 11155111,
143
+ gas: 21000n,
144
+ maxFeePerGas: 1n,
145
+ maxPriorityFeePerGas: 1n,
146
+ });
147
+ const provider = new JsonRpcProvider(process.env.RPC_URL!);
148
+ await provider.broadcastTransaction(rawTransaction);
149
+ ```
150
+
151
+ ### Solana (stubs)
152
+
153
+ These expose the Solana address derived from the vault. Signing is not implemented yet.
154
+
155
+ ```ts
156
+ const solWeb3 = await client.toSolanaWeb3Signer();
157
+ console.log(solWeb3.publicKey);
158
+ ```
159
+
160
+ ## API
161
+
162
+ ```ts
163
+ type EmblemRemoteConfig = {
164
+ apiKey: string;
165
+ baseUrl?: string; // default https://api.emblemvault.ai
166
+ };
167
+
168
+ createEmblemClient(config): EmblemVaultClient
169
+ EmblemVaultClient#toViemAccount(): Promise<Account>
170
+ EmblemVaultClient#toEthersWallet(provider?): Promise<Signer>
171
+ EmblemVaultClient#toWeb3Adapter(): Promise<{ address, signMessage, signTypedData, signTransaction }>
172
+ EmblemVaultClient#toSolanaWeb3Signer(): Promise<{ publicKey }>
173
+ EmblemVaultClient#toSolanaKitSigner(): Promise<{ publicKey }>
174
+ ```
175
+
176
+ Adapters POST to the Emblem API endpoints:
177
+
178
+ - `POST /sign-eth-message` – `{ vaultId, message }`
179
+ - `POST /sign-typed-message` – `{ vaultId, domain, types, message }`
180
+ - `POST /sign-eth-tx` – `{ vaultId, transaction }` (expects ethers-serializable fields)
181
+
182
+ On first use, both adapters query `GET /vault/info` with header `x-api-key` to obtain:
183
+
184
+ - Vault ID
185
+ - Solana Address
186
+ - EVM Address
187
+
188
+ Transactions are normalized to hex/number-like fields before submission.
189
+
190
+ ## Testing
191
+
192
+ - Copy `.env.example` to `.env` and set:
193
+ - `EMBLEM_API_KEY` – your dev API key
194
+ - `EMBLEM_BASE_URL` – usually `https://dev-api.emblemvault.ai`
195
+ - Run `npm test`
196
+
197
+ Notes:
198
+ - Tests mock `fetch` and do not hit the network.
199
+ - Node 18+ (or a fetch polyfill) is required at runtime.
200
+
201
+ ### Integration tests
202
+
203
+ Integration tests hit the real API using your `.env` values and verify signatures with viem/ethers.
204
+
205
+ - Ensure `.env` contains working credentials (typically dev base URL)
206
+ - Run: `npm run test:integration`
207
+
208
+ These tests:
209
+ - Sign and verify messages and typed data
210
+ - Sign transactions and verify the recovered `from` address
@@ -0,0 +1,15 @@
1
+ import type { EmblemRemoteConfig, VaultInfo } from "./types";
2
+ import { AbstractSigner } from "ethers";
3
+ import type { Provider, TransactionRequest, TransactionResponse, TypedDataDomain, TypedDataField } from "ethers";
4
+ export declare class EmblemEthersWallet extends AbstractSigner {
5
+ #private;
6
+ readonly address: `0x${string}`;
7
+ constructor(address: `0x${string}`, vaultId: string, config: EmblemRemoteConfig, provider?: Provider | null);
8
+ getAddress(): Promise<string>;
9
+ connect(provider: Provider): EmblemEthersWallet;
10
+ signMessage(message: string | Uint8Array): Promise<string>;
11
+ signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string>;
12
+ signTransaction(tx: TransactionRequest): Promise<string>;
13
+ sendTransaction(tx: TransactionRequest): Promise<TransactionResponse>;
14
+ }
15
+ export declare function toEthersWallet(config: EmblemRemoteConfig, provider?: Provider | null, infoOverride?: VaultInfo): Promise<EmblemEthersWallet>;
package/dist/ethers.js ADDED
@@ -0,0 +1,71 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _EmblemEthersWallet_config, _EmblemEthersWallet_vaultId;
13
+ import { emblemPost } from "./http";
14
+ import { bytesToHex, normalizeTxForEmblem } from "./utils";
15
+ import { fetchVaultInfo } from "./vault";
16
+ import { AbstractSigner, } from "ethers";
17
+ export class EmblemEthersWallet extends AbstractSigner {
18
+ constructor(address, vaultId, config, provider) {
19
+ super(provider ?? null);
20
+ _EmblemEthersWallet_config.set(this, void 0);
21
+ _EmblemEthersWallet_vaultId.set(this, void 0);
22
+ this.address = address;
23
+ __classPrivateFieldSet(this, _EmblemEthersWallet_vaultId, vaultId, "f");
24
+ __classPrivateFieldSet(this, _EmblemEthersWallet_config, config, "f");
25
+ }
26
+ async getAddress() {
27
+ return this.address;
28
+ }
29
+ connect(provider) {
30
+ return new EmblemEthersWallet(this.address, __classPrivateFieldGet(this, _EmblemEthersWallet_vaultId, "f"), __classPrivateFieldGet(this, _EmblemEthersWallet_config, "f"), provider);
31
+ }
32
+ async signMessage(message) {
33
+ const vaultId = __classPrivateFieldGet(this, _EmblemEthersWallet_vaultId, "f");
34
+ const payload = typeof message === "string" ? message : bytesToHex(message);
35
+ const data = await emblemPost("/sign-eth-message", { vaultId, message: payload }, __classPrivateFieldGet(this, _EmblemEthersWallet_config, "f"));
36
+ return data.signature;
37
+ }
38
+ async signTypedData(domain, types, value) {
39
+ const vaultId = __classPrivateFieldGet(this, _EmblemEthersWallet_vaultId, "f");
40
+ const data = await emblemPost("/sign-typed-message", { vaultId, domain, types, message: value }, __classPrivateFieldGet(this, _EmblemEthersWallet_config, "f"));
41
+ return data.signature;
42
+ }
43
+ async signTransaction(tx) {
44
+ // Ensure `from` (if present) matches this signer
45
+ const from = tx.from;
46
+ if (from && from.toLowerCase() !== this.address.toLowerCase()) {
47
+ throw new Error("transaction from does not match signer address");
48
+ }
49
+ // Let provider fill fields if available
50
+ const toSign = this.provider ? await this.populateTransaction(tx) : { ...tx };
51
+ // Ethers serializers do not include `from`
52
+ delete toSign.from;
53
+ const normalized = normalizeTxForEmblem(toSign);
54
+ const vaultId = __classPrivateFieldGet(this, _EmblemEthersWallet_vaultId, "f");
55
+ const resp = await emblemPost("/sign-eth-tx", { vaultId, transaction: normalized }, __classPrivateFieldGet(this, _EmblemEthersWallet_config, "f"));
56
+ return resp.signedTransaction;
57
+ }
58
+ async sendTransaction(tx) {
59
+ if (!this.provider) {
60
+ throw new Error("EmblemEthersWallet requires a provider to send transactions");
61
+ }
62
+ const populated = await this.populateTransaction(tx);
63
+ const signed = await this.signTransaction(populated);
64
+ return await this.provider.broadcastTransaction(signed);
65
+ }
66
+ }
67
+ _EmblemEthersWallet_config = new WeakMap(), _EmblemEthersWallet_vaultId = new WeakMap();
68
+ export async function toEthersWallet(config, provider, infoOverride) {
69
+ const info = infoOverride ?? await fetchVaultInfo(config);
70
+ return new EmblemEthersWallet(info.evmAddress, info.vaultId, config, provider ?? null);
71
+ }
package/dist/http.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { EmblemRemoteConfig } from "./types";
2
+ export declare function emblemPost<T = any>(path: string, body: any, { apiKey, baseUrl }: EmblemRemoteConfig): Promise<T>;
3
+ export declare function emblemGet<T = any>(path: string, { apiKey, baseUrl }: EmblemRemoteConfig): Promise<T>;
package/dist/http.js ADDED
@@ -0,0 +1,28 @@
1
+ export async function emblemPost(path, body, { apiKey, baseUrl = "https://api.emblemvault.ai" }) {
2
+ const res = await fetch(`${baseUrl}${path}`, {
3
+ method: "POST",
4
+ headers: {
5
+ "content-type": "application/json",
6
+ "x-api-key": apiKey,
7
+ },
8
+ body: JSON.stringify(body),
9
+ });
10
+ if (!res.ok) {
11
+ const text = await res.text().catch(() => "");
12
+ throw new Error(`Emblem signer error ${res.status}: ${text || res.statusText}`);
13
+ }
14
+ return res.json();
15
+ }
16
+ export async function emblemGet(path, { apiKey, baseUrl = "https://api.emblemvault.ai" }) {
17
+ const res = await fetch(`${baseUrl}${path}`, {
18
+ method: "GET",
19
+ headers: {
20
+ "x-api-key": apiKey,
21
+ },
22
+ });
23
+ if (!res.ok) {
24
+ const text = await res.text().catch(() => "");
25
+ throw new Error(`Emblem signer error ${res.status}: ${text || res.statusText}`);
26
+ }
27
+ return res.json();
28
+ }
@@ -0,0 +1,36 @@
1
+ import type { Provider } from "ethers";
2
+ import type { EmblemRemoteConfig } from "./types";
3
+ export type { EmblemRemoteConfig, Hex, VaultInfo } from "./types";
4
+ import { EmblemEthersWallet } from "./ethers";
5
+ import { EmblemSolanaSigner } from "./solana";
6
+ import { EmblemWeb3Adapter } from "./web3";
7
+ export declare class EmblemVaultClient {
8
+ private readonly config;
9
+ private _infoPromise?;
10
+ constructor(config: EmblemRemoteConfig);
11
+ /** Lazily fetch and cache vault info */
12
+ private getInfo;
13
+ toViemAccount(): Promise<{
14
+ address: import("abitype").Address;
15
+ nonceManager?: import("viem").NonceManager | undefined;
16
+ sign?: ((parameters: {
17
+ hash: import("viem").Hash;
18
+ }) => Promise<import("viem").Hex>) | undefined | undefined;
19
+ signAuthorization?: ((parameters: import("viem").AuthorizationRequest) => Promise<import("viem/accounts").SignAuthorizationReturnType>) | undefined | undefined;
20
+ signMessage: ({ message }: {
21
+ message: import("viem").SignableMessage;
22
+ }) => Promise<import("viem").Hex>;
23
+ signTransaction: <serializer extends import("viem").SerializeTransactionFn<import("viem").TransactionSerializable> = import("viem").SerializeTransactionFn<import("viem").TransactionSerializable>, transaction extends Parameters<serializer>[0] = Parameters<serializer>[0]>(transaction: transaction, options?: {
24
+ serializer?: serializer | undefined;
25
+ } | undefined) => Promise<import("viem").Hex>;
26
+ signTypedData: <const typedData extends import("abitype").TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: import("viem").TypedDataDefinition<typedData, primaryType>) => Promise<import("viem").Hex>;
27
+ publicKey: import("viem").Hex;
28
+ source: string;
29
+ type: "local";
30
+ }>;
31
+ toEthersWallet(provider?: Provider | null): Promise<EmblemEthersWallet>;
32
+ toSolanaWeb3Signer(): Promise<EmblemSolanaSigner>;
33
+ toSolanaKitSigner(): Promise<EmblemSolanaSigner>;
34
+ toWeb3Adapter(): Promise<EmblemWeb3Adapter>;
35
+ }
36
+ export declare function createEmblemClient(config: EmblemRemoteConfig): EmblemVaultClient;
package/dist/index.js ADDED
@@ -0,0 +1,39 @@
1
+ import { toViemAccount } from "./viem";
2
+ import { toEthersWallet } from "./ethers";
3
+ import { toSolanaKitSigner, toSolanaWeb3Signer } from "./solana";
4
+ import { toWeb3Adapter } from "./web3";
5
+ import { fetchVaultInfo } from "./vault";
6
+ export class EmblemVaultClient {
7
+ constructor(config) {
8
+ this.config = config;
9
+ }
10
+ /** Lazily fetch and cache vault info */
11
+ getInfo() {
12
+ if (!this._infoPromise)
13
+ this._infoPromise = fetchVaultInfo(this.config);
14
+ return this._infoPromise;
15
+ }
16
+ async toViemAccount() {
17
+ const info = await this.getInfo();
18
+ return toViemAccount(this.config, info);
19
+ }
20
+ async toEthersWallet(provider) {
21
+ const info = await this.getInfo();
22
+ return toEthersWallet(this.config, provider ?? null, info);
23
+ }
24
+ async toSolanaWeb3Signer() {
25
+ const info = await this.getInfo();
26
+ return toSolanaWeb3Signer(this.config, info);
27
+ }
28
+ async toSolanaKitSigner() {
29
+ const info = await this.getInfo();
30
+ return toSolanaKitSigner(this.config, info);
31
+ }
32
+ async toWeb3Adapter() {
33
+ const info = await this.getInfo();
34
+ return toWeb3Adapter(this.config, info);
35
+ }
36
+ }
37
+ export function createEmblemClient(config) {
38
+ return new EmblemVaultClient(config);
39
+ }
@@ -0,0 +1,9 @@
1
+ import type { EmblemRemoteConfig, VaultInfo } from "./types";
2
+ export declare class EmblemSolanaSigner {
3
+ readonly publicKey: string;
4
+ constructor(publicKey: string);
5
+ signMessage(_message: Uint8Array | string): Promise<string>;
6
+ signTransaction(_tx: unknown): Promise<string>;
7
+ }
8
+ export declare function toSolanaWeb3Signer(config: EmblemRemoteConfig, infoOverride?: VaultInfo): Promise<EmblemSolanaSigner>;
9
+ export declare function toSolanaKitSigner(config: EmblemRemoteConfig, infoOverride?: VaultInfo): Promise<EmblemSolanaSigner>;
package/dist/solana.js ADDED
@@ -0,0 +1,20 @@
1
+ import { fetchVaultInfo } from "./vault";
2
+ export class EmblemSolanaSigner {
3
+ constructor(publicKey) {
4
+ this.publicKey = publicKey;
5
+ }
6
+ async signMessage(_message) {
7
+ throw new Error("Solana signing via Emblem is not implemented yet");
8
+ }
9
+ async signTransaction(_tx) {
10
+ throw new Error("Solana transaction signing via Emblem is not implemented yet");
11
+ }
12
+ }
13
+ export async function toSolanaWeb3Signer(config, infoOverride) {
14
+ const info = infoOverride ?? (await fetchVaultInfo(config));
15
+ return new EmblemSolanaSigner(info.address);
16
+ }
17
+ export async function toSolanaKitSigner(config, infoOverride) {
18
+ const info = infoOverride ?? (await fetchVaultInfo(config));
19
+ return new EmblemSolanaSigner(info.address);
20
+ }
@@ -0,0 +1,14 @@
1
+ export type Hex = `0x${string}`;
2
+ /** Config for Emblem remote signer */
3
+ export type EmblemRemoteConfig = {
4
+ /** x-api-key for your authenticate middleware */
5
+ apiKey: string;
6
+ /** Base URL for the Emblem signer API */
7
+ baseUrl?: string;
8
+ };
9
+ export type VaultInfo = {
10
+ vaultId: string;
11
+ address: string;
12
+ evmAddress: `0x${string}`;
13
+ created_by?: string;
14
+ };
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,9 @@
1
+ import type { Hex } from "./types";
2
+ export declare function toHexIfBigInt(v: any): any;
3
+ /**
4
+ * viem txs sometimes have bigint / hex / optional fields. Ethers serializers
5
+ * accept hex strings for numeric fields. Normalize where helpful.
6
+ */
7
+ export declare function normalizeTxForEmblem(tx: any): any;
8
+ export declare function isHexString(value: any): value is Hex;
9
+ export declare function bytesToHex(bytes: ArrayLike<number>): Hex;
package/dist/utils.js ADDED
@@ -0,0 +1,50 @@
1
+ export function toHexIfBigInt(v) {
2
+ return typeof v === "bigint" ? ("0x" + v.toString(16)) : v;
3
+ }
4
+ /**
5
+ * viem txs sometimes have bigint / hex / optional fields. Ethers serializers
6
+ * accept hex strings for numeric fields. Normalize where helpful.
7
+ */
8
+ export function normalizeTxForEmblem(tx) {
9
+ const out = { ...tx };
10
+ if (out.value !== undefined)
11
+ out.value = toHexIfBigInt(out.value);
12
+ if (out.gas !== undefined) {
13
+ out.gasLimit = toHexIfBigInt(out.gas);
14
+ delete out.gas;
15
+ }
16
+ if (out.gasLimit !== undefined)
17
+ out.gasLimit = toHexIfBigInt(out.gasLimit);
18
+ if (out.gasPrice !== undefined)
19
+ out.gasPrice = toHexIfBigInt(out.gasPrice);
20
+ if (out.maxFeePerGas !== undefined)
21
+ out.maxFeePerGas = toHexIfBigInt(out.maxFeePerGas);
22
+ if (out.maxPriorityFeePerGas !== undefined)
23
+ out.maxPriorityFeePerGas = toHexIfBigInt(out.maxPriorityFeePerGas);
24
+ if (out.nonce !== undefined)
25
+ out.nonce = Number(out.nonce);
26
+ if (out.chainId !== undefined)
27
+ out.chainId = Number(out.chainId);
28
+ // Some backends only accept legacy fields; fold EIP-1559 into gasPrice and drop unsupported keys
29
+ if (out.maxFeePerGas !== undefined || out.maxPriorityFeePerGas !== undefined) {
30
+ if (out.gasPrice === undefined && out.maxFeePerGas !== undefined) {
31
+ out.gasPrice = out.maxFeePerGas;
32
+ }
33
+ delete out.maxFeePerGas;
34
+ delete out.maxPriorityFeePerGas;
35
+ }
36
+ // Remove fields commonly unsupported by legacy serializers
37
+ delete out.type;
38
+ delete out.accessList;
39
+ return out;
40
+ }
41
+ export function isHexString(value) {
42
+ return typeof value === "string" && /^0x[0-9a-fA-F]*$/.test(value);
43
+ }
44
+ export function bytesToHex(bytes) {
45
+ let out = "0x";
46
+ for (let i = 0; i < bytes.length; i++) {
47
+ out += bytes[i].toString(16).padStart(2, "0");
48
+ }
49
+ return out;
50
+ }
@@ -0,0 +1,2 @@
1
+ import type { EmblemRemoteConfig, VaultInfo } from "./types";
2
+ export declare function fetchVaultInfo(config: EmblemRemoteConfig): Promise<VaultInfo>;
package/dist/vault.js ADDED
@@ -0,0 +1,17 @@
1
+ import { emblemGet, emblemPost } from "./http";
2
+ export async function fetchVaultInfo(config) {
3
+ let data;
4
+ try {
5
+ data = await emblemGet("/vault/info", config);
6
+ }
7
+ catch (err) {
8
+ // Some environments may require POST for this endpoint; try POST fallback
9
+ data = await emblemPost("/vault/info", {}, config);
10
+ }
11
+ return {
12
+ vaultId: data.vaultId,
13
+ address: data.address,
14
+ evmAddress: data.evmAddress,
15
+ created_by: data.created_by,
16
+ };
17
+ }
package/dist/viem.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ import type { EmblemRemoteConfig, VaultInfo } from "./types";
2
+ import type { TypedDataDefinition } from "viem";
3
+ export declare function toViemAccount(config: EmblemRemoteConfig, infoOverride?: VaultInfo): Promise<{
4
+ address: import("abitype").Address;
5
+ nonceManager?: import("viem").NonceManager | undefined;
6
+ sign?: ((parameters: {
7
+ hash: import("viem").Hash;
8
+ }) => Promise<import("viem").Hex>) | undefined | undefined;
9
+ signAuthorization?: ((parameters: import("viem").AuthorizationRequest) => Promise<import("viem/accounts").SignAuthorizationReturnType>) | undefined | undefined;
10
+ signMessage: ({ message }: {
11
+ message: import("viem").SignableMessage;
12
+ }) => Promise<import("viem").Hex>;
13
+ signTransaction: <serializer extends import("viem").SerializeTransactionFn<import("viem").TransactionSerializable> = import("viem").SerializeTransactionFn<import("viem").TransactionSerializable>, transaction extends Parameters<serializer>[0] = Parameters<serializer>[0]>(transaction: transaction, options?: {
14
+ serializer?: serializer | undefined;
15
+ } | undefined) => Promise<import("viem").Hex>;
16
+ signTypedData: <const typedData extends import("abitype").TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: TypedDataDefinition<typedData, primaryType>) => Promise<import("viem").Hex>;
17
+ publicKey: import("viem").Hex;
18
+ source: string;
19
+ type: "local";
20
+ }>;
package/dist/viem.js ADDED
@@ -0,0 +1,42 @@
1
+ import { emblemPost } from "./http";
2
+ import { bytesToHex, isHexString, normalizeTxForEmblem } from "./utils";
3
+ import { toAccount } from "viem/accounts";
4
+ import { fetchVaultInfo } from "./vault";
5
+ export async function toViemAccount(config, infoOverride) {
6
+ const info = infoOverride ?? await fetchVaultInfo(config);
7
+ const { evmAddress, vaultId } = info;
8
+ return toAccount({
9
+ address: evmAddress,
10
+ async signMessage({ message }) {
11
+ let payload;
12
+ if (typeof message === "string") {
13
+ payload = message;
14
+ }
15
+ else if (message && typeof message.raw !== "undefined") {
16
+ const raw = message.raw;
17
+ payload = typeof raw === "string" ? raw : bytesToHex(raw);
18
+ }
19
+ else if (message instanceof Uint8Array) {
20
+ payload = bytesToHex(message);
21
+ }
22
+ else if (isHexString(message)) {
23
+ payload = message;
24
+ }
25
+ else {
26
+ payload = String(message);
27
+ }
28
+ const data = await emblemPost("/sign-eth-message", { vaultId, message: payload }, config);
29
+ return data.signature;
30
+ },
31
+ async signTypedData(typedData) {
32
+ const { domain, types, message } = typedData;
33
+ const data = await emblemPost("/sign-typed-message", { vaultId, domain, types, message }, config);
34
+ return data.signature;
35
+ },
36
+ async signTransaction(tx, _opts) {
37
+ const normalizedTx = normalizeTxForEmblem(tx);
38
+ const data = await emblemPost("/sign-eth-tx", { vaultId, transaction: normalizedTx }, config);
39
+ return data.signedTransaction;
40
+ },
41
+ });
42
+ }
package/dist/web3.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ import type { EmblemRemoteConfig, Hex, VaultInfo } from "./types";
2
+ export declare class EmblemWeb3Adapter {
3
+ #private;
4
+ readonly address: `0x${string}`;
5
+ constructor(address: `0x${string}`, vaultId: string, config: EmblemRemoteConfig);
6
+ signMessage(message: string | Uint8Array): Promise<Hex>;
7
+ signTypedData(domain: any, types: any, message: any): Promise<Hex>;
8
+ signTransaction(tx: any): Promise<{
9
+ rawTransaction: Hex;
10
+ }>;
11
+ }
12
+ export declare function toWeb3Adapter(config: EmblemRemoteConfig, infoOverride?: VaultInfo): Promise<EmblemWeb3Adapter>;
package/dist/web3.js ADDED
@@ -0,0 +1,43 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _EmblemWeb3Adapter_vaultId, _EmblemWeb3Adapter_config;
13
+ import { emblemPost } from "./http";
14
+ import { bytesToHex, normalizeTxForEmblem } from "./utils";
15
+ import { fetchVaultInfo } from "./vault";
16
+ export class EmblemWeb3Adapter {
17
+ constructor(address, vaultId, config) {
18
+ _EmblemWeb3Adapter_vaultId.set(this, void 0);
19
+ _EmblemWeb3Adapter_config.set(this, void 0);
20
+ this.address = address;
21
+ __classPrivateFieldSet(this, _EmblemWeb3Adapter_vaultId, vaultId, "f");
22
+ __classPrivateFieldSet(this, _EmblemWeb3Adapter_config, config, "f");
23
+ }
24
+ async signMessage(message) {
25
+ const payload = typeof message === "string" ? message : bytesToHex(message);
26
+ const data = await emblemPost("/sign-eth-message", { vaultId: __classPrivateFieldGet(this, _EmblemWeb3Adapter_vaultId, "f"), message: payload }, __classPrivateFieldGet(this, _EmblemWeb3Adapter_config, "f"));
27
+ return data.signature;
28
+ }
29
+ async signTypedData(domain, types, message) {
30
+ const data = await emblemPost("/sign-typed-message", { vaultId: __classPrivateFieldGet(this, _EmblemWeb3Adapter_vaultId, "f"), domain, types, message }, __classPrivateFieldGet(this, _EmblemWeb3Adapter_config, "f"));
31
+ return data.signature;
32
+ }
33
+ async signTransaction(tx) {
34
+ const normalized = normalizeTxForEmblem(tx);
35
+ const resp = await emblemPost("/sign-eth-tx", { vaultId: __classPrivateFieldGet(this, _EmblemWeb3Adapter_vaultId, "f"), transaction: normalized }, __classPrivateFieldGet(this, _EmblemWeb3Adapter_config, "f"));
36
+ return { rawTransaction: resp.signedTransaction };
37
+ }
38
+ }
39
+ _EmblemWeb3Adapter_vaultId = new WeakMap(), _EmblemWeb3Adapter_config = new WeakMap();
40
+ export async function toWeb3Adapter(config, infoOverride) {
41
+ const info = infoOverride ?? (await fetchVaultInfo(config));
42
+ return new EmblemWeb3Adapter(info.evmAddress, info.vaultId, config);
43
+ }
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "emblem-vault-ai-signers",
3
+ "version": "0.1.0",
4
+ "description": "Emblem Vault remote signer adapters for viem and ethers",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc -p tsconfig.json",
20
+ "clean": "rm -rf dist",
21
+ "test": "vitest run --reporter=verbose",
22
+ "test:watch": "vitest --reporter=verbose",
23
+ "test:ci": "vitest run --reporter=default",
24
+ "test:integration": "vitest -c vitest.int.config.ts run --reporter=verbose"
25
+ },
26
+ "keywords": [
27
+ "emblem",
28
+ "vault",
29
+ "signer",
30
+ "ethers",
31
+ "viem"
32
+ ],
33
+ "author": "",
34
+ "license": "MIT",
35
+ "peerDependencies": {
36
+ "ethers": "^6.10.0",
37
+ "viem": "^2.0.0"
38
+ },
39
+ "devDependencies": {
40
+ "typescript": "^5.3.3",
41
+ "vitest": "^1.6.0",
42
+ "dotenv": "^16.4.5",
43
+ "ethers": "^6.10.0",
44
+ "viem": "^2.0.0"
45
+ }
46
+ }