emblem-vault-ai-signers 0.1.1 → 0.1.2
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 +20 -1
- package/dist/ethers.d.ts +17 -4
- package/dist/ethers.js +132 -40
- package/dist/types.d.ts +1 -0
- package/dist/vault.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@ Remote signer adapters for Emblem Vault that plug into popular Ethereum librarie
|
|
|
4
4
|
|
|
5
5
|
- `toViemAccount()` – creates a viem `Account` that signs via Emblem
|
|
6
6
|
- `toEthersWallet()` – creates an ethers v6 `Signer` that signs via Emblem
|
|
7
|
+
- Implements `initialize()`, `getVaultId()`, `setChainId()`, `getChainId()`
|
|
8
|
+
- Adds `signAndBroadcast(tx, waitForReceipt?)` helper (optional)
|
|
7
9
|
- `toWeb3Adapter()` – returns a minimal Web3-style signer adapter (EVM)
|
|
8
10
|
- `toSolanaWeb3Signer()` – returns a stub Solana signer with `publicKey`
|
|
9
11
|
- `toSolanaKitSigner()` – returns a stub Solana signer with `publicKey`
|
|
@@ -39,7 +41,17 @@ const viemClient = createPublicClient({ chain: mainnet, transport: http() });
|
|
|
39
41
|
// ethers v6
|
|
40
42
|
const provider = new JsonRpcProvider(process.env.RPC_URL!);
|
|
41
43
|
const wallet = await client.toEthersWallet(provider);
|
|
42
|
-
|
|
44
|
+
|
|
45
|
+
// Read metadata
|
|
46
|
+
const addr = await wallet.getAddress();
|
|
47
|
+
const vaultId = wallet.getVaultId();
|
|
48
|
+
|
|
49
|
+
// Sign & send via ethers Provider
|
|
50
|
+
await wallet.signMessage("hello");
|
|
51
|
+
await wallet.sendTransaction({ to: "0x...", value: 1n });
|
|
52
|
+
|
|
53
|
+
// Or sign and broadcast, returning tx hash
|
|
54
|
+
const txHash = await wallet.signAndBroadcast({ to: "0x...", value: 1n }, true);
|
|
43
55
|
|
|
44
56
|
// web3.js-like adapter (minimal)
|
|
45
57
|
const web3Adapter = await client.toWeb3Adapter();
|
|
@@ -171,6 +183,13 @@ EmblemVaultClient#toEthersWallet(provider?): Promise<Signer>
|
|
|
171
183
|
EmblemVaultClient#toWeb3Adapter(): Promise<{ address, signMessage, signTypedData, signTransaction }>
|
|
172
184
|
EmblemVaultClient#toSolanaWeb3Signer(): Promise<{ publicKey }>
|
|
173
185
|
EmblemVaultClient#toSolanaKitSigner(): Promise<{ publicKey }>
|
|
186
|
+
|
|
187
|
+
Ethers wallet (v6) adds:
|
|
188
|
+
- initialize(): Promise<void>
|
|
189
|
+
- getVaultId(): string
|
|
190
|
+
- setChainId(n: number): void
|
|
191
|
+
- getChainId(): number
|
|
192
|
+
- signAndBroadcast(tx: TransactionRequest, waitForReceipt?: boolean): Promise<string>
|
|
174
193
|
```
|
|
175
194
|
|
|
176
195
|
Adapters POST to the Emblem API endpoints:
|
package/dist/ethers.d.ts
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
import type { EmblemRemoteConfig, VaultInfo } from "./types";
|
|
2
2
|
import { AbstractSigner } from "ethers";
|
|
3
|
-
import type { Provider, TransactionRequest, TransactionResponse, TypedDataDomain, TypedDataField } from "ethers";
|
|
3
|
+
import type { Provider, TransactionLike, TransactionRequest, TransactionResponse, TypedDataDomain, TypedDataField } from "ethers";
|
|
4
4
|
export declare class EmblemEthersWallet extends AbstractSigner {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
private readonly _config;
|
|
6
|
+
private _address;
|
|
7
|
+
private _vaultId;
|
|
8
|
+
private _chainId;
|
|
9
|
+
constructor(config: EmblemRemoteConfig, provider?: Provider | null, seed?: {
|
|
10
|
+
address?: `0x${string}`;
|
|
11
|
+
vaultId?: string;
|
|
12
|
+
chainId?: number;
|
|
13
|
+
});
|
|
14
|
+
initialize(): Promise<void>;
|
|
8
15
|
getAddress(): Promise<string>;
|
|
16
|
+
getVaultId(): string;
|
|
17
|
+
setChainId(chainId: number): void;
|
|
18
|
+
getChainId(): number;
|
|
9
19
|
connect(provider: Provider): EmblemEthersWallet;
|
|
10
20
|
signMessage(message: string | Uint8Array): Promise<string>;
|
|
11
21
|
signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string>;
|
|
22
|
+
_signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string>;
|
|
12
23
|
signTransaction(tx: TransactionRequest): Promise<string>;
|
|
13
24
|
sendTransaction(tx: TransactionRequest): Promise<TransactionResponse>;
|
|
25
|
+
populateTransaction(transaction: TransactionRequest): Promise<TransactionLike<string>>;
|
|
26
|
+
signAndBroadcast(transaction: TransactionRequest, waitForReceipt?: boolean): Promise<string>;
|
|
14
27
|
}
|
|
15
28
|
export declare function toEthersWallet(config: EmblemRemoteConfig, provider?: Provider | null, infoOverride?: VaultInfo): Promise<EmblemEthersWallet>;
|
package/dist/ethers.js
CHANGED
|
@@ -1,71 +1,163 @@
|
|
|
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
1
|
import { emblemPost } from "./http";
|
|
14
2
|
import { bytesToHex, normalizeTxForEmblem } from "./utils";
|
|
15
3
|
import { fetchVaultInfo } from "./vault";
|
|
16
|
-
import { AbstractSigner, } from "ethers";
|
|
4
|
+
import { AbstractSigner, resolveAddress } from "ethers";
|
|
17
5
|
export class EmblemEthersWallet extends AbstractSigner {
|
|
18
|
-
constructor(
|
|
6
|
+
constructor(config, provider, seed) {
|
|
19
7
|
super(provider ?? null);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
this.
|
|
23
|
-
|
|
24
|
-
|
|
8
|
+
this._address = null;
|
|
9
|
+
this._vaultId = null;
|
|
10
|
+
this._chainId = 1;
|
|
11
|
+
this._config = config;
|
|
12
|
+
if (seed?.address)
|
|
13
|
+
this._address = seed.address;
|
|
14
|
+
if (seed?.vaultId)
|
|
15
|
+
this._vaultId = seed.vaultId;
|
|
16
|
+
if (seed?.chainId)
|
|
17
|
+
this._chainId = seed.chainId;
|
|
18
|
+
}
|
|
19
|
+
async initialize() {
|
|
20
|
+
const info = await fetchVaultInfo(this._config);
|
|
21
|
+
this._address = info.evmAddress;
|
|
22
|
+
this._vaultId = info.vaultId;
|
|
25
23
|
}
|
|
26
24
|
async getAddress() {
|
|
27
|
-
|
|
25
|
+
if (!this._address)
|
|
26
|
+
await this.initialize();
|
|
27
|
+
return this._address;
|
|
28
|
+
}
|
|
29
|
+
getVaultId() {
|
|
30
|
+
if (!this._vaultId)
|
|
31
|
+
throw new Error("Wallet not initialized. Call initialize() first.");
|
|
32
|
+
return this._vaultId;
|
|
33
|
+
}
|
|
34
|
+
setChainId(chainId) {
|
|
35
|
+
this._chainId = chainId;
|
|
36
|
+
}
|
|
37
|
+
getChainId() {
|
|
38
|
+
return this._chainId;
|
|
28
39
|
}
|
|
29
40
|
connect(provider) {
|
|
30
|
-
|
|
41
|
+
if (!provider)
|
|
42
|
+
throw new Error("Provider cannot be null");
|
|
43
|
+
return new EmblemEthersWallet(this._config, provider, { address: this._address ?? undefined, vaultId: this._vaultId ?? undefined, chainId: this._chainId });
|
|
31
44
|
}
|
|
32
45
|
async signMessage(message) {
|
|
33
|
-
|
|
46
|
+
if (!this._vaultId)
|
|
47
|
+
await this.initialize();
|
|
34
48
|
const payload = typeof message === "string" ? message : bytesToHex(message);
|
|
35
|
-
const data = await emblemPost("/sign-eth-message", { vaultId, message: payload },
|
|
49
|
+
const data = await emblemPost("/sign-eth-message", { vaultId: this._vaultId, message: payload }, this._config);
|
|
36
50
|
return data.signature;
|
|
37
51
|
}
|
|
38
52
|
async signTypedData(domain, types, value) {
|
|
39
|
-
|
|
40
|
-
|
|
53
|
+
if (!this._vaultId)
|
|
54
|
+
await this.initialize();
|
|
55
|
+
const cleanTypes = { ...types };
|
|
56
|
+
if (cleanTypes && cleanTypes.EIP712Domain) {
|
|
57
|
+
delete cleanTypes.EIP712Domain;
|
|
58
|
+
}
|
|
59
|
+
const data = await emblemPost("/sign-typed-message", { vaultId: this._vaultId, domain, types: cleanTypes, message: value }, this._config);
|
|
41
60
|
return data.signature;
|
|
42
61
|
}
|
|
62
|
+
async _signTypedData(domain, types, value) {
|
|
63
|
+
return this.signTypedData(domain, types, value);
|
|
64
|
+
}
|
|
43
65
|
async signTransaction(tx) {
|
|
44
|
-
|
|
66
|
+
if (!this._vaultId)
|
|
67
|
+
await this.initialize();
|
|
45
68
|
const from = tx.from;
|
|
46
|
-
|
|
69
|
+
const addr = await this.getAddress();
|
|
70
|
+
if (from && from.toLowerCase() !== addr.toLowerCase()) {
|
|
47
71
|
throw new Error("transaction from does not match signer address");
|
|
48
72
|
}
|
|
49
|
-
// Let provider fill fields if available
|
|
50
73
|
const toSign = this.provider ? await this.populateTransaction(tx) : { ...tx };
|
|
51
|
-
|
|
52
|
-
|
|
74
|
+
if (toSign.from)
|
|
75
|
+
delete toSign.from;
|
|
76
|
+
if (!('to' in toSign) || !toSign.to) {
|
|
77
|
+
throw new Error("Transaction must have a 'to' address");
|
|
78
|
+
}
|
|
79
|
+
if (toSign.nonce === undefined || toSign.nonce === null) {
|
|
80
|
+
throw new Error("Transaction must have a nonce");
|
|
81
|
+
}
|
|
53
82
|
const normalized = normalizeTxForEmblem(toSign);
|
|
54
|
-
const
|
|
55
|
-
const resp = await emblemPost("/sign-eth-tx", { vaultId, transaction: normalized }, __classPrivateFieldGet(this, _EmblemEthersWallet_config, "f"));
|
|
83
|
+
const resp = await emblemPost("/sign-eth-tx", { vaultId: this._vaultId, transaction: normalized, options: { chainId: this._chainId } }, this._config);
|
|
56
84
|
return resp.signedTransaction;
|
|
57
85
|
}
|
|
58
86
|
async sendTransaction(tx) {
|
|
59
|
-
if (!this.provider)
|
|
60
|
-
throw new Error("
|
|
61
|
-
|
|
62
|
-
const populated = await this.populateTransaction(tx);
|
|
63
|
-
const signed = await this.signTransaction(populated);
|
|
87
|
+
if (!this.provider)
|
|
88
|
+
throw new Error("Provider required to send transaction");
|
|
89
|
+
const signed = await this.signTransaction(tx);
|
|
64
90
|
return await this.provider.broadcastTransaction(signed);
|
|
65
91
|
}
|
|
92
|
+
async populateTransaction(transaction) {
|
|
93
|
+
const tx = { ...transaction };
|
|
94
|
+
if (!this.provider)
|
|
95
|
+
throw new Error("Provider required to populate transaction");
|
|
96
|
+
const fromAddress = tx.from ? await resolveAddress(tx.from, this.provider) : await this.getAddress();
|
|
97
|
+
let chainId;
|
|
98
|
+
if (!tx.chainId) {
|
|
99
|
+
const network = await this.provider.getNetwork();
|
|
100
|
+
chainId = network.chainId;
|
|
101
|
+
this._chainId = Number(network.chainId);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
chainId = BigInt(tx.chainId);
|
|
105
|
+
this._chainId = Number(tx.chainId);
|
|
106
|
+
}
|
|
107
|
+
const nonce = tx.nonce != null ? Number(tx.nonce) : await this.provider.getTransactionCount(fromAddress, "pending");
|
|
108
|
+
const toAddress = tx.to ? await resolveAddress(tx.to, this.provider) : null;
|
|
109
|
+
const value = tx.value ? BigInt(tx.value.toString()) : 0n;
|
|
110
|
+
let gasLimit;
|
|
111
|
+
if (!tx.gasLimit) {
|
|
112
|
+
try {
|
|
113
|
+
gasLimit = await this.provider.estimateGas({ ...tx, from: fromAddress });
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
gasLimit = 21000n;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
gasLimit = BigInt(tx.gasLimit.toString());
|
|
121
|
+
}
|
|
122
|
+
let gasPrice = null;
|
|
123
|
+
if (!tx.gasPrice && tx.type !== 2) {
|
|
124
|
+
const feeData = await this.provider.getFeeData();
|
|
125
|
+
gasPrice = feeData.gasPrice ?? null;
|
|
126
|
+
}
|
|
127
|
+
else if (tx.gasPrice) {
|
|
128
|
+
gasPrice = BigInt(tx.gasPrice.toString());
|
|
129
|
+
}
|
|
130
|
+
const populated = {
|
|
131
|
+
from: fromAddress,
|
|
132
|
+
to: toAddress,
|
|
133
|
+
value,
|
|
134
|
+
nonce,
|
|
135
|
+
gasLimit,
|
|
136
|
+
data: tx.data,
|
|
137
|
+
chainId,
|
|
138
|
+
type: tx.type || undefined,
|
|
139
|
+
};
|
|
140
|
+
if (gasPrice !== null)
|
|
141
|
+
populated.gasPrice = gasPrice;
|
|
142
|
+
if (tx.maxFeePerGas)
|
|
143
|
+
populated.maxFeePerGas = BigInt(tx.maxFeePerGas.toString());
|
|
144
|
+
if (tx.maxPriorityFeePerGas)
|
|
145
|
+
populated.maxPriorityFeePerGas = BigInt(tx.maxPriorityFeePerGas.toString());
|
|
146
|
+
return populated;
|
|
147
|
+
}
|
|
148
|
+
async signAndBroadcast(transaction, waitForReceipt = false) {
|
|
149
|
+
if (!this.provider)
|
|
150
|
+
throw new Error("Provider required to send transaction");
|
|
151
|
+
const signed = await this.signTransaction(transaction);
|
|
152
|
+
const resp = await this.provider.broadcastTransaction(signed);
|
|
153
|
+
const hash = resp.hash;
|
|
154
|
+
if (waitForReceipt) {
|
|
155
|
+
await this.provider.waitForTransaction(hash);
|
|
156
|
+
}
|
|
157
|
+
return hash;
|
|
158
|
+
}
|
|
66
159
|
}
|
|
67
|
-
_EmblemEthersWallet_config = new WeakMap(), _EmblemEthersWallet_vaultId = new WeakMap();
|
|
68
160
|
export async function toEthersWallet(config, provider, infoOverride) {
|
|
69
|
-
const info = infoOverride ?? await fetchVaultInfo(config);
|
|
70
|
-
return new EmblemEthersWallet(info.evmAddress, info.vaultId
|
|
161
|
+
const info = infoOverride ?? (await fetchVaultInfo(config));
|
|
162
|
+
return new EmblemEthersWallet(config, provider ?? null, { address: info.evmAddress, vaultId: info.vaultId });
|
|
71
163
|
}
|
package/dist/types.d.ts
CHANGED
package/dist/vault.js
CHANGED