@awarizon/web3 1.0.1 → 1.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/dist/index.d.mts +805 -44
- package/dist/index.d.ts +805 -44
- package/dist/index.js +591 -95
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +568 -78
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { createPublicClient, http } from 'viem';
|
|
1
|
+
import { erc20Abi, createPublicClient, http } from 'viem';
|
|
2
2
|
import { WalletEngine } from '@awarizon/wallet-engine';
|
|
3
3
|
export { ChainSwitchError, InvalidMnemonicError, InvalidPrivateKeyError, WalletEngine, WalletNotConnectedError } from '@awarizon/wallet-engine';
|
|
4
|
+
import { TransactionEngine } from '@awarizon/tx-engine';
|
|
5
|
+
export { ContractExecutionError, GasEstimationError, SimulationError, TransactionEngine, TransactionTimeoutError } from '@awarizon/tx-engine';
|
|
4
6
|
import { moonbeam, fantom, gnosis, celo, mantle, scroll, linea, zora, sepolia, avalanche, bsc, optimismSepolia, optimism, arbitrumSepolia, arbitrum, polygonAmoy, polygon, baseSepolia, base, mainnet } from 'viem/chains';
|
|
5
7
|
import { parseABI } from '@awarizon/abi-engine';
|
|
6
8
|
export { DuplicateFunctionError, InvalidABIError, UnsupportedABIItemError, generateAllMethodSignatures, generateMethodSignature, isPayableFunction, isWriteFunction, parseABI } from '@awarizon/abi-engine';
|
|
7
|
-
import { TransactionEngine } from '@awarizon/tx-engine';
|
|
8
|
-
export { ContractExecutionError, GasEstimationError, SimulationError, TransactionEngine, TransactionTimeoutError } from '@awarizon/tx-engine';
|
|
9
9
|
|
|
10
10
|
// src/sdk.ts
|
|
11
11
|
var CHAINS = {
|
|
@@ -58,6 +58,7 @@ var CHAINS = {
|
|
|
58
58
|
fantom,
|
|
59
59
|
moonbeam
|
|
60
60
|
};
|
|
61
|
+
var _chainAliases = Object.keys(CHAINS).sort().join(", ");
|
|
61
62
|
function resolveChain(chain) {
|
|
62
63
|
if (typeof chain === "object" && "id" in chain) {
|
|
63
64
|
return chain;
|
|
@@ -65,10 +66,9 @@ function resolveChain(chain) {
|
|
|
65
66
|
if (typeof chain === "string") {
|
|
66
67
|
const resolved = CHAINS[chain.toLowerCase()] ?? CHAINS[chain];
|
|
67
68
|
if (!resolved) {
|
|
68
|
-
const available = Object.keys(CHAINS).filter((k, i, arr) => arr.indexOf(k) === i).sort().join(", ");
|
|
69
69
|
throw new Error(
|
|
70
70
|
`[awarizon/web3] Unsupported chain: "${chain}".
|
|
71
|
-
Available chains: ${
|
|
71
|
+
Available chains: ${_chainAliases}`
|
|
72
72
|
);
|
|
73
73
|
}
|
|
74
74
|
return resolved;
|
|
@@ -83,37 +83,58 @@ function getSupportedChainIds() {
|
|
|
83
83
|
return true;
|
|
84
84
|
}).map((c) => c.id);
|
|
85
85
|
}
|
|
86
|
-
function
|
|
86
|
+
function trackEvent(telemetry, type, chain, functionName, success, start) {
|
|
87
|
+
telemetry?.track({
|
|
88
|
+
type,
|
|
89
|
+
chain: chain?.name ?? "unknown",
|
|
90
|
+
chainId: chain?.id ?? 0,
|
|
91
|
+
functionName,
|
|
92
|
+
success,
|
|
93
|
+
durationMs: Date.now() - start,
|
|
94
|
+
ts: Date.now()
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function buildContractInstance(address, abi, publicClient, txEngine, telemetry, chain) {
|
|
87
98
|
const parsed = parseABI(abi);
|
|
88
|
-
const
|
|
99
|
+
const eventsMap = new Map(parsed.events.map((e) => [e.name, e]));
|
|
89
100
|
const instance = {
|
|
90
101
|
_address: address,
|
|
91
102
|
_abi: abi
|
|
92
103
|
};
|
|
93
104
|
for (const fn of parsed.readFunctions) {
|
|
94
|
-
instance[fn.name] = buildReadMethod(fn, address, abi, txEngine);
|
|
105
|
+
instance[fn.name] = buildReadMethod(fn, address, abi, txEngine, telemetry, chain);
|
|
95
106
|
}
|
|
96
107
|
for (const fn of parsed.writeFunctions) {
|
|
97
|
-
instance[fn.name] = buildWriteMethod(fn, address, abi, txEngine);
|
|
108
|
+
instance[fn.name] = buildWriteMethod(fn, address, abi, txEngine, telemetry, chain);
|
|
98
109
|
}
|
|
99
|
-
instance["on"] = buildEventSubscription(address, abi,
|
|
110
|
+
instance["on"] = buildEventSubscription(address, abi, eventsMap, publicClient);
|
|
100
111
|
instance["estimateGas"] = async (method, ...args) => {
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
abi,
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
})
|
|
107
|
-
|
|
112
|
+
const start = Date.now();
|
|
113
|
+
try {
|
|
114
|
+
const estimate = await txEngine.estimateGas({ address, abi, functionName: method, args });
|
|
115
|
+
trackEvent(telemetry, "contract.gas_estimate", chain, method, true, start);
|
|
116
|
+
return estimate.gas;
|
|
117
|
+
} catch (error) {
|
|
118
|
+
trackEvent(telemetry, "contract.gas_estimate", chain, method, false, start);
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
108
121
|
};
|
|
109
122
|
return instance;
|
|
110
123
|
}
|
|
111
|
-
function buildReadMethod(fn, address, abi, txEngine) {
|
|
124
|
+
function buildReadMethod(fn, address, abi, txEngine, telemetry, chain) {
|
|
112
125
|
return async (...args) => {
|
|
113
|
-
|
|
126
|
+
const start = Date.now();
|
|
127
|
+
try {
|
|
128
|
+
const result = await txEngine.read({ address, abi, functionName: fn.name, args });
|
|
129
|
+
trackEvent(telemetry, "contract.read", chain, fn.name, true, start);
|
|
130
|
+
return result;
|
|
131
|
+
} catch (error) {
|
|
132
|
+
trackEvent(telemetry, "contract.read", chain, fn.name, false, start);
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
114
135
|
};
|
|
115
136
|
}
|
|
116
|
-
function buildWriteMethod(fn, address, abi, txEngine) {
|
|
137
|
+
function buildWriteMethod(fn, address, abi, txEngine, telemetry, chain) {
|
|
117
138
|
const isPayable = fn.stateMutability === "payable";
|
|
118
139
|
return async (...args) => {
|
|
119
140
|
let callArgs = args;
|
|
@@ -127,14 +148,22 @@ function buildWriteMethod(fn, address, abi, txEngine) {
|
|
|
127
148
|
gas = last.gas;
|
|
128
149
|
}
|
|
129
150
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
151
|
+
const start = Date.now();
|
|
152
|
+
try {
|
|
153
|
+
const result = await txEngine.write({
|
|
154
|
+
address,
|
|
155
|
+
abi,
|
|
156
|
+
functionName: fn.name,
|
|
157
|
+
args: callArgs,
|
|
158
|
+
value,
|
|
159
|
+
gas
|
|
160
|
+
});
|
|
161
|
+
trackEvent(telemetry, "contract.write", chain, fn.name, true, start);
|
|
162
|
+
return result;
|
|
163
|
+
} catch (error) {
|
|
164
|
+
trackEvent(telemetry, "contract.write", chain, fn.name, false, start);
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
138
167
|
};
|
|
139
168
|
}
|
|
140
169
|
function isPayableOptions(val) {
|
|
@@ -144,11 +173,11 @@ function isPayableOptions(val) {
|
|
|
144
173
|
const hasGas = "gas" in obj && (obj.gas === void 0 || typeof obj.gas === "bigint");
|
|
145
174
|
return hasValue || hasGas;
|
|
146
175
|
}
|
|
147
|
-
function buildEventSubscription(address, abi,
|
|
176
|
+
function buildEventSubscription(address, abi, eventsMap, publicClient) {
|
|
148
177
|
return (eventName, callback) => {
|
|
149
|
-
const abiEvent =
|
|
178
|
+
const abiEvent = eventsMap.get(eventName);
|
|
150
179
|
if (!abiEvent) {
|
|
151
|
-
const available =
|
|
180
|
+
const available = [...eventsMap.keys()].join(", ") || "none";
|
|
152
181
|
throw new Error(
|
|
153
182
|
`[awarizon/web3] Event "${eventName}" not found on contract ${address}.
|
|
154
183
|
Available events: ${available}`
|
|
@@ -185,7 +214,7 @@ var ProviderError = class extends Error {
|
|
|
185
214
|
};
|
|
186
215
|
var ContractNotLoadedError = class extends Error {
|
|
187
216
|
constructor(address) {
|
|
188
|
-
super(`[awarizon/web3] Contract at ${address} is not loaded. Call
|
|
217
|
+
super(`[awarizon/web3] Contract at ${address} is not loaded. Call awarizon.contract({ address, abi }) first.`);
|
|
189
218
|
this.code = "CONTRACT_NOT_LOADED";
|
|
190
219
|
this.name = "ContractNotLoadedError";
|
|
191
220
|
}
|
|
@@ -198,104 +227,565 @@ var UnsupportedChainError = class extends Error {
|
|
|
198
227
|
this.chain = chain;
|
|
199
228
|
}
|
|
200
229
|
};
|
|
230
|
+
var ApiKeyRequiredError = class extends Error {
|
|
231
|
+
constructor() {
|
|
232
|
+
super(
|
|
233
|
+
'[awarizon/web3] An API key is required. Pass apiKey: "awz_live_..." to AwarizonWeb3({ ... }). Get yours at https://awarizon.com/dashboard/api-keys'
|
|
234
|
+
);
|
|
235
|
+
this.code = "API_KEY_REQUIRED";
|
|
236
|
+
this.name = "ApiKeyRequiredError";
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
var InvalidApiKeyError = class extends Error {
|
|
240
|
+
constructor(message = "Invalid or revoked API key.") {
|
|
241
|
+
super(`[awarizon/web3] ${message}`);
|
|
242
|
+
this.code = "INVALID_API_KEY";
|
|
243
|
+
this.name = "InvalidApiKeyError";
|
|
244
|
+
}
|
|
245
|
+
};
|
|
201
246
|
|
|
202
|
-
// src/
|
|
247
|
+
// src/telemetry.ts
|
|
248
|
+
var DEFAULT_BASE_URL = "https://awarizon.com";
|
|
249
|
+
var FLUSH_INTERVAL_MS = 1e4;
|
|
250
|
+
var MAX_BATCH_SIZE = 20;
|
|
251
|
+
var VALIDATION_TIMEOUT = 5e3;
|
|
252
|
+
var TelemetryClient = class {
|
|
253
|
+
constructor(apiKey, baseUrl = DEFAULT_BASE_URL) {
|
|
254
|
+
this.validated = false;
|
|
255
|
+
this.validationPromise = null;
|
|
256
|
+
this.queue = [];
|
|
257
|
+
this.flushTimer = null;
|
|
258
|
+
this.unloadListener = null;
|
|
259
|
+
this.apiKey = apiKey;
|
|
260
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Ensures the API key has been validated against the Awarizon API.
|
|
264
|
+
* Idempotent — safe to call on every operation; only hits the network once.
|
|
265
|
+
* Times out after 5 seconds to avoid hanging in degraded network conditions.
|
|
266
|
+
*
|
|
267
|
+
* @throws {InvalidApiKeyError} if the key is invalid, revoked, or unreachable
|
|
268
|
+
*/
|
|
269
|
+
async ensureValidated() {
|
|
270
|
+
if (this.validated) return;
|
|
271
|
+
if (this.validationPromise) return this.validationPromise;
|
|
272
|
+
this.validationPromise = this._validate().catch((err) => {
|
|
273
|
+
this.validationPromise = null;
|
|
274
|
+
throw err;
|
|
275
|
+
});
|
|
276
|
+
return this.validationPromise;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Queue a usage event. Events are batched and sent periodically.
|
|
280
|
+
* Must only be called after ensureValidated() has resolved.
|
|
281
|
+
*/
|
|
282
|
+
track(event) {
|
|
283
|
+
if (!this.validated) return;
|
|
284
|
+
this.queue.push(event);
|
|
285
|
+
if (this.queue.length >= MAX_BATCH_SIZE) this._flush(false);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Flush pending events and stop background timers.
|
|
289
|
+
* Call when tearing down a long-lived SDK instance (e.g. server shutdown).
|
|
290
|
+
*/
|
|
291
|
+
async destroy() {
|
|
292
|
+
this._stopTimers();
|
|
293
|
+
await this._flush(true);
|
|
294
|
+
}
|
|
295
|
+
// ─── Private ─────────────────────────────────────────────────────────────────
|
|
296
|
+
async _validate() {
|
|
297
|
+
const controller = new AbortController();
|
|
298
|
+
const timeout = setTimeout(() => controller.abort(), VALIDATION_TIMEOUT);
|
|
299
|
+
let res;
|
|
300
|
+
try {
|
|
301
|
+
res = await fetch(`${this.baseUrl}/api/v1/auth`, {
|
|
302
|
+
method: "GET",
|
|
303
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
304
|
+
signal: controller.signal
|
|
305
|
+
});
|
|
306
|
+
} catch (err) {
|
|
307
|
+
const msg = controller.signal.aborted ? `Awarizon API did not respond within ${VALIDATION_TIMEOUT / 1e3}s.` : `Could not reach Awarizon API: ${err.message}`;
|
|
308
|
+
throw new InvalidApiKeyError(msg);
|
|
309
|
+
} finally {
|
|
310
|
+
clearTimeout(timeout);
|
|
311
|
+
}
|
|
312
|
+
if (!res.ok) {
|
|
313
|
+
const body = await res.json().catch(() => ({}));
|
|
314
|
+
throw new InvalidApiKeyError(body.error ?? "Invalid or revoked API key.");
|
|
315
|
+
}
|
|
316
|
+
this.validated = true;
|
|
317
|
+
this._startTimers();
|
|
318
|
+
}
|
|
319
|
+
_startTimers() {
|
|
320
|
+
this.flushTimer = setInterval(() => {
|
|
321
|
+
if (this.queue.length > 0) this._flush(false);
|
|
322
|
+
}, FLUSH_INTERVAL_MS);
|
|
323
|
+
const timer = this.flushTimer;
|
|
324
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
325
|
+
if (typeof window !== "undefined" && typeof navigator !== "undefined") {
|
|
326
|
+
this.unloadListener = () => this._flushBeacon();
|
|
327
|
+
window.addEventListener("beforeunload", this.unloadListener);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
_stopTimers() {
|
|
331
|
+
if (this.flushTimer !== null) {
|
|
332
|
+
clearInterval(this.flushTimer);
|
|
333
|
+
this.flushTimer = null;
|
|
334
|
+
}
|
|
335
|
+
if (this.unloadListener !== null && typeof window !== "undefined") {
|
|
336
|
+
window.removeEventListener("beforeunload", this.unloadListener);
|
|
337
|
+
this.unloadListener = null;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
_flush(awaited) {
|
|
341
|
+
if (this.queue.length === 0) return awaited ? Promise.resolve() : void 0;
|
|
342
|
+
const batch = this.queue.splice(0, MAX_BATCH_SIZE);
|
|
343
|
+
const promise = fetch(`${this.baseUrl}/api/v1/usage`, {
|
|
344
|
+
method: "POST",
|
|
345
|
+
headers: {
|
|
346
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
347
|
+
"Content-Type": "application/json"
|
|
348
|
+
},
|
|
349
|
+
body: JSON.stringify({ events: batch })
|
|
350
|
+
}).then(() => {
|
|
351
|
+
}, () => {
|
|
352
|
+
});
|
|
353
|
+
return awaited ? promise : void 0;
|
|
354
|
+
}
|
|
355
|
+
/** Uses sendBeacon for guaranteed delivery on page unload */
|
|
356
|
+
_flushBeacon() {
|
|
357
|
+
if (this.queue.length === 0 || typeof navigator === "undefined") return;
|
|
358
|
+
const batch = this.queue.splice(0);
|
|
359
|
+
navigator.sendBeacon(
|
|
360
|
+
`${this.baseUrl}/api/v1/usage`,
|
|
361
|
+
new Blob([JSON.stringify({ events: batch })], { type: "application/json" })
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
var ERC20_ABI = erc20Abi;
|
|
366
|
+
var ERC721_ABI = [
|
|
367
|
+
{ type: "function", name: "name", stateMutability: "view", inputs: [], outputs: [{ type: "string" }] },
|
|
368
|
+
{ type: "function", name: "symbol", stateMutability: "view", inputs: [], outputs: [{ type: "string" }] },
|
|
369
|
+
{ type: "function", name: "tokenURI", stateMutability: "view", inputs: [{ name: "tokenId", type: "uint256" }], outputs: [{ type: "string" }] },
|
|
370
|
+
{ type: "function", name: "totalSupply", stateMutability: "view", inputs: [], outputs: [{ type: "uint256" }] },
|
|
371
|
+
{ type: "function", name: "ownerOf", stateMutability: "view", inputs: [{ name: "tokenId", type: "uint256" }], outputs: [{ type: "address" }] },
|
|
372
|
+
{ type: "function", name: "balanceOf", stateMutability: "view", inputs: [{ name: "owner", type: "address" }], outputs: [{ type: "uint256" }] },
|
|
373
|
+
{ type: "function", name: "getApproved", stateMutability: "view", inputs: [{ name: "tokenId", type: "uint256" }], outputs: [{ type: "address" }] },
|
|
374
|
+
{ type: "function", name: "isApprovedForAll", stateMutability: "view", inputs: [{ name: "owner", type: "address" }, { name: "operator", type: "address" }], outputs: [{ type: "bool" }] },
|
|
375
|
+
{ type: "function", name: "transferFrom", stateMutability: "nonpayable", inputs: [{ name: "from", type: "address" }, { name: "to", type: "address" }, { name: "tokenId", type: "uint256" }], outputs: [] },
|
|
376
|
+
{ type: "function", name: "safeTransferFrom", stateMutability: "nonpayable", inputs: [{ name: "from", type: "address" }, { name: "to", type: "address" }, { name: "tokenId", type: "uint256" }], outputs: [] },
|
|
377
|
+
{ type: "function", name: "approve", stateMutability: "nonpayable", inputs: [{ name: "to", type: "address" }, { name: "tokenId", type: "uint256" }], outputs: [] },
|
|
378
|
+
{ type: "function", name: "setApprovalForAll", stateMutability: "nonpayable", inputs: [{ name: "operator", type: "address" }, { name: "approved", type: "bool" }], outputs: [] },
|
|
379
|
+
{ type: "event", name: "Transfer", inputs: [{ indexed: true, name: "from", type: "address" }, { indexed: true, name: "to", type: "address" }, { indexed: true, name: "tokenId", type: "uint256" }] },
|
|
380
|
+
{ type: "event", name: "Approval", inputs: [{ indexed: true, name: "owner", type: "address" }, { indexed: true, name: "approved", type: "address" }, { indexed: true, name: "tokenId", type: "uint256" }] },
|
|
381
|
+
{ type: "event", name: "ApprovalForAll", inputs: [{ indexed: true, name: "owner", type: "address" }, { indexed: true, name: "operator", type: "address" }, { name: "approved", type: "bool" }] }
|
|
382
|
+
];
|
|
383
|
+
var ERC1155_ABI = [
|
|
384
|
+
{ type: "function", name: "uri", stateMutability: "view", inputs: [{ name: "id", type: "uint256" }], outputs: [{ type: "string" }] },
|
|
385
|
+
{ type: "function", name: "balanceOf", stateMutability: "view", inputs: [{ name: "account", type: "address" }, { name: "id", type: "uint256" }], outputs: [{ type: "uint256" }] },
|
|
386
|
+
{ type: "function", name: "balanceOfBatch", stateMutability: "view", inputs: [{ name: "accounts", type: "address[]" }, { name: "ids", type: "uint256[]" }], outputs: [{ type: "uint256[]" }] },
|
|
387
|
+
{ type: "function", name: "isApprovedForAll", stateMutability: "view", inputs: [{ name: "account", type: "address" }, { name: "operator", type: "address" }], outputs: [{ type: "bool" }] },
|
|
388
|
+
{ type: "function", name: "setApprovalForAll", stateMutability: "nonpayable", inputs: [{ name: "operator", type: "address" }, { name: "approved", type: "bool" }], outputs: [] },
|
|
389
|
+
{ type: "function", name: "safeTransferFrom", stateMutability: "nonpayable", inputs: [{ name: "from", type: "address" }, { name: "to", type: "address" }, { name: "id", type: "uint256" }, { name: "amount", type: "uint256" }, { name: "data", type: "bytes" }], outputs: [] },
|
|
390
|
+
{ type: "function", name: "safeBatchTransferFrom", stateMutability: "nonpayable", inputs: [{ name: "from", type: "address" }, { name: "to", type: "address" }, { name: "ids", type: "uint256[]" }, { name: "amounts", type: "uint256[]" }, { name: "data", type: "bytes" }], outputs: [] },
|
|
391
|
+
{ type: "event", name: "TransferSingle", inputs: [{ indexed: true, name: "operator", type: "address" }, { indexed: true, name: "from", type: "address" }, { indexed: true, name: "to", type: "address" }, { name: "id", type: "uint256" }, { name: "value", type: "uint256" }] },
|
|
392
|
+
{ type: "event", name: "TransferBatch", inputs: [{ indexed: true, name: "operator", type: "address" }, { indexed: true, name: "from", type: "address" }, { indexed: true, name: "to", type: "address" }, { name: "ids", type: "uint256[]" }, { name: "values", type: "uint256[]" }] },
|
|
393
|
+
{ type: "event", name: "ApprovalForAll", inputs: [{ indexed: true, name: "account", type: "address" }, { indexed: true, name: "operator", type: "address" }, { name: "approved", type: "bool" }] }
|
|
394
|
+
];
|
|
395
|
+
var WalletProxy = class {
|
|
396
|
+
constructor(engine, ensureReady) {
|
|
397
|
+
this.engine = engine;
|
|
398
|
+
this.ensureReady = ensureReady;
|
|
399
|
+
}
|
|
400
|
+
// ── Gated async operations ──────────────────────────────────────────────────
|
|
401
|
+
async create() {
|
|
402
|
+
await this.ensureReady();
|
|
403
|
+
return this.engine.create();
|
|
404
|
+
}
|
|
405
|
+
async importMnemonic(mnemonic, accountIndex) {
|
|
406
|
+
await this.ensureReady();
|
|
407
|
+
return this.engine.importMnemonic(mnemonic, accountIndex);
|
|
408
|
+
}
|
|
409
|
+
async importPrivateKey(privateKey) {
|
|
410
|
+
await this.ensureReady();
|
|
411
|
+
return this.engine.importPrivateKey(privateKey);
|
|
412
|
+
}
|
|
413
|
+
// ── Pass-throughs (sync, no network) ───────────────────────────────────────
|
|
414
|
+
address() {
|
|
415
|
+
return this.engine.address();
|
|
416
|
+
}
|
|
417
|
+
getSigner() {
|
|
418
|
+
return this.engine.getWalletClient();
|
|
419
|
+
}
|
|
420
|
+
getWalletClient() {
|
|
421
|
+
return this.engine.getWalletClient();
|
|
422
|
+
}
|
|
423
|
+
isConnected() {
|
|
424
|
+
return this.engine.isConnected();
|
|
425
|
+
}
|
|
426
|
+
hasExternalWallet() {
|
|
427
|
+
return this.engine.hasExternalWallet();
|
|
428
|
+
}
|
|
429
|
+
hasInternalWallet() {
|
|
430
|
+
return this.engine.hasInternalWallet();
|
|
431
|
+
}
|
|
432
|
+
connectExternal(c) {
|
|
433
|
+
this.engine.connectExternal(c);
|
|
434
|
+
}
|
|
435
|
+
disconnectExternal() {
|
|
436
|
+
this.engine.disconnectExternal();
|
|
437
|
+
}
|
|
438
|
+
disconnect() {
|
|
439
|
+
this.engine.disconnect();
|
|
440
|
+
}
|
|
441
|
+
async switchChain(chain) {
|
|
442
|
+
return this.engine.switchChain(chain);
|
|
443
|
+
}
|
|
444
|
+
};
|
|
203
445
|
var AwarizonWeb3 = class {
|
|
204
446
|
constructor(config) {
|
|
447
|
+
// Contract instances are cached by address — same address returns same instance.
|
|
448
|
+
// Cache is cleared on switchChain() since contracts are chain-specific.
|
|
449
|
+
this._contractCache = /* @__PURE__ */ new Map();
|
|
450
|
+
// Named contract registry — register once, reference by name anywhere.
|
|
451
|
+
this._registry = /* @__PURE__ */ new Map();
|
|
452
|
+
if (!config.apiKey) throw new ApiKeyRequiredError();
|
|
205
453
|
this.chain = resolveChain(config.chain);
|
|
206
454
|
this.publicClient = createPublicClient({
|
|
207
455
|
chain: this.chain,
|
|
208
456
|
transport: http(config.rpcUrl)
|
|
209
457
|
});
|
|
210
|
-
this.
|
|
458
|
+
this._engine = new WalletEngine({
|
|
211
459
|
chain: this.chain,
|
|
212
460
|
publicClient: this.publicClient,
|
|
213
461
|
rpcUrl: config.rpcUrl
|
|
214
462
|
});
|
|
463
|
+
this._txEngine = new TransactionEngine(
|
|
464
|
+
this.publicClient,
|
|
465
|
+
() => this._engine.getWalletClient()
|
|
466
|
+
);
|
|
467
|
+
this._telemetry = new TelemetryClient(config.apiKey, config.baseUrl);
|
|
468
|
+
this.wallet = new WalletProxy(
|
|
469
|
+
this._engine,
|
|
470
|
+
() => this._telemetry.ensureValidated()
|
|
471
|
+
);
|
|
215
472
|
if (config.signer) {
|
|
216
|
-
this.
|
|
473
|
+
this._engine.connectExternal(config.signer);
|
|
217
474
|
}
|
|
218
475
|
}
|
|
219
476
|
// ─── Wallet API surface ─────────────────────────────────────────────────────
|
|
220
|
-
/**
|
|
221
|
-
* Connect an external wallet client (wagmi, WalletConnect, viem, EIP-1193).
|
|
222
|
-
* The connected signer takes priority over any internal wallet.
|
|
223
|
-
*
|
|
224
|
-
* @example
|
|
225
|
-
* sdk.connectWallet(walletClient)
|
|
226
|
-
*/
|
|
227
477
|
connectWallet(walletClient) {
|
|
228
|
-
this.
|
|
478
|
+
this._engine.connectExternal(walletClient);
|
|
229
479
|
return this;
|
|
230
480
|
}
|
|
231
|
-
/**
|
|
232
|
-
* Disconnect the externally-connected wallet.
|
|
233
|
-
* Falls back to the internal wallet if one is loaded.
|
|
234
|
-
*/
|
|
235
481
|
disconnectWallet() {
|
|
236
|
-
this.
|
|
482
|
+
this._engine.disconnectExternal();
|
|
237
483
|
return this;
|
|
238
484
|
}
|
|
239
|
-
/**
|
|
240
|
-
* Switch the active chain.
|
|
241
|
-
* Rebuilds the PublicClient and internal WalletClient for the new chain.
|
|
242
|
-
*/
|
|
243
485
|
async switchChain(chain) {
|
|
486
|
+
await this._telemetry.ensureValidated();
|
|
244
487
|
const resolved = resolveChain(chain);
|
|
245
|
-
await this.
|
|
488
|
+
await this._engine.switchChain(resolved);
|
|
489
|
+
this._contractCache.clear();
|
|
246
490
|
return this;
|
|
247
491
|
}
|
|
248
|
-
/**
|
|
249
|
-
* Returns a summary of the currently active wallet.
|
|
250
|
-
* @throws {WalletNotConnectedError} if no wallet is connected
|
|
251
|
-
*/
|
|
252
492
|
getWalletInfo() {
|
|
253
493
|
return {
|
|
254
|
-
address: this.
|
|
494
|
+
address: this._engine.address(),
|
|
255
495
|
chain: this.chain,
|
|
256
|
-
isExternal: this.
|
|
496
|
+
isExternal: this._engine.hasExternalWallet()
|
|
257
497
|
};
|
|
258
498
|
}
|
|
259
499
|
// ─── Contract API ───────────────────────────────────────────────────────────
|
|
260
500
|
/**
|
|
261
|
-
* Load a deployed
|
|
262
|
-
*
|
|
501
|
+
* Load a deployed contract and return a fully typed, ABI-powered instance.
|
|
502
|
+
* Validates the API key on the first call — subsequent calls are instant.
|
|
263
503
|
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
* @example
|
|
268
|
-
* const token = await sdk.contract({ address: "0x...", abi: ERC20_ABI })
|
|
269
|
-
* const balance = await token.balanceOf(address) // read
|
|
270
|
-
* await token.transfer(to, 100n) // write
|
|
271
|
-
* token.on("Transfer", handler) // events
|
|
504
|
+
* Results are cached by address — calling contract() twice with the same
|
|
505
|
+
* address and ABI object returns the same instance without rebuilding.
|
|
272
506
|
*/
|
|
273
507
|
async contract(config) {
|
|
508
|
+
await this._telemetry.ensureValidated();
|
|
274
509
|
if (!config.address || !config.abi) {
|
|
275
|
-
throw new Error("[awarizon/web3]
|
|
510
|
+
throw new Error("[awarizon/web3] awarizon.contract() requires both address and abi");
|
|
276
511
|
}
|
|
277
|
-
|
|
512
|
+
const key = config.address.toLowerCase();
|
|
513
|
+
const cached = this._contractCache.get(key);
|
|
514
|
+
if (cached && cached.abiRef === config.abi) return cached.instance;
|
|
515
|
+
const instance = buildContractInstance(
|
|
278
516
|
config.address,
|
|
279
517
|
config.abi,
|
|
280
518
|
this.publicClient,
|
|
281
|
-
|
|
519
|
+
this._txEngine,
|
|
520
|
+
this._telemetry,
|
|
521
|
+
this.chain
|
|
282
522
|
);
|
|
523
|
+
this._contractCache.set(key, { abiRef: config.abi, instance });
|
|
524
|
+
return instance;
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Load multiple contracts in parallel. Validates the API key once, then
|
|
528
|
+
* instantiates all contracts concurrently.
|
|
529
|
+
*
|
|
530
|
+
* @example
|
|
531
|
+
* ```ts
|
|
532
|
+
* const [token, staking, nft] = await awarizon.contracts([
|
|
533
|
+
* { address: tokenAddr, abi: ERC20_ABI },
|
|
534
|
+
* { address: stakingAddr, abi: STAKING_ABI },
|
|
535
|
+
* { address: nftAddr, abi: ERC721_ABI },
|
|
536
|
+
* ])
|
|
537
|
+
* ```
|
|
538
|
+
*/
|
|
539
|
+
async contracts(configs) {
|
|
540
|
+
return Promise.all(configs.map((c) => this.contract(c)));
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Call a read-only (view/pure) contract function directly without loading
|
|
544
|
+
* a full contract instance. Best for one-off reads.
|
|
545
|
+
*
|
|
546
|
+
* @example
|
|
547
|
+
* ```ts
|
|
548
|
+
* const balance = await awarizon.read<bigint>({
|
|
549
|
+
* address: tokenAddress,
|
|
550
|
+
* abi: ERC20_ABI,
|
|
551
|
+
* method: "balanceOf",
|
|
552
|
+
* args: [userAddress],
|
|
553
|
+
* })
|
|
554
|
+
* ```
|
|
555
|
+
*/
|
|
556
|
+
async read(params) {
|
|
557
|
+
await this._telemetry.ensureValidated();
|
|
558
|
+
const start = Date.now();
|
|
559
|
+
try {
|
|
560
|
+
const result = await this._txEngine.read({
|
|
561
|
+
address: params.address,
|
|
562
|
+
abi: params.abi,
|
|
563
|
+
functionName: params.method,
|
|
564
|
+
args: params.args ?? []
|
|
565
|
+
});
|
|
566
|
+
this._telemetry.track({
|
|
567
|
+
type: "contract.read",
|
|
568
|
+
chain: this.chain.name,
|
|
569
|
+
chainId: this.chain.id,
|
|
570
|
+
functionName: params.method,
|
|
571
|
+
success: true,
|
|
572
|
+
durationMs: Date.now() - start,
|
|
573
|
+
ts: Date.now()
|
|
574
|
+
});
|
|
575
|
+
return result;
|
|
576
|
+
} catch (err) {
|
|
577
|
+
this._telemetry.track({
|
|
578
|
+
type: "contract.read",
|
|
579
|
+
chain: this.chain.name,
|
|
580
|
+
chainId: this.chain.id,
|
|
581
|
+
functionName: params.method,
|
|
582
|
+
success: false,
|
|
583
|
+
durationMs: Date.now() - start,
|
|
584
|
+
ts: Date.now()
|
|
585
|
+
});
|
|
586
|
+
throw err;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Execute a state-mutating contract function directly without loading a full
|
|
591
|
+
* contract instance. Best for one-off writes.
|
|
592
|
+
*
|
|
593
|
+
* @example
|
|
594
|
+
* ```ts
|
|
595
|
+
* const { hash, receipt } = await awarizon.write({
|
|
596
|
+
* address: tokenAddress,
|
|
597
|
+
* abi: ERC20_ABI,
|
|
598
|
+
* method: "transfer",
|
|
599
|
+
* args: [recipient, 100n],
|
|
600
|
+
* })
|
|
601
|
+
* ```
|
|
602
|
+
*/
|
|
603
|
+
async write(params) {
|
|
604
|
+
await this._telemetry.ensureValidated();
|
|
605
|
+
const start = Date.now();
|
|
606
|
+
try {
|
|
607
|
+
const result = await this._txEngine.write({
|
|
608
|
+
address: params.address,
|
|
609
|
+
abi: params.abi,
|
|
610
|
+
functionName: params.method,
|
|
611
|
+
args: params.args ?? [],
|
|
612
|
+
value: params.value,
|
|
613
|
+
gas: params.gas
|
|
614
|
+
});
|
|
615
|
+
this._telemetry.track({
|
|
616
|
+
type: "contract.write",
|
|
617
|
+
chain: this.chain.name,
|
|
618
|
+
chainId: this.chain.id,
|
|
619
|
+
functionName: params.method,
|
|
620
|
+
success: true,
|
|
621
|
+
durationMs: Date.now() - start,
|
|
622
|
+
ts: Date.now()
|
|
623
|
+
});
|
|
624
|
+
return result;
|
|
625
|
+
} catch (err) {
|
|
626
|
+
this._telemetry.track({
|
|
627
|
+
type: "contract.write",
|
|
628
|
+
chain: this.chain.name,
|
|
629
|
+
chainId: this.chain.id,
|
|
630
|
+
functionName: params.method,
|
|
631
|
+
success: false,
|
|
632
|
+
durationMs: Date.now() - start,
|
|
633
|
+
ts: Date.now()
|
|
634
|
+
});
|
|
635
|
+
throw err;
|
|
636
|
+
}
|
|
283
637
|
}
|
|
284
638
|
/**
|
|
285
|
-
*
|
|
286
|
-
*
|
|
639
|
+
* Batch multiple read calls into a single RPC round-trip using multicall3.
|
|
640
|
+
* Falls back to parallel individual reads on chains where multicall3 is
|
|
641
|
+
* not deployed.
|
|
642
|
+
*
|
|
643
|
+
* @example
|
|
644
|
+
* ```ts
|
|
645
|
+
* const [balance, supply, allowance] = await awarizon.multicall([
|
|
646
|
+
* { address: tokenAddr, abi: ERC20_ABI, method: "balanceOf", args: [userAddr] },
|
|
647
|
+
* { address: tokenAddr, abi: ERC20_ABI, method: "totalSupply" },
|
|
648
|
+
* { address: tokenAddr, abi: ERC20_ABI, method: "allowance", args: [userAddr, spenderAddr] },
|
|
649
|
+
* ])
|
|
650
|
+
* ```
|
|
651
|
+
*/
|
|
652
|
+
async multicall(calls) {
|
|
653
|
+
await this._telemetry.ensureValidated();
|
|
654
|
+
const contracts = calls.map((c) => ({
|
|
655
|
+
address: c.address,
|
|
656
|
+
abi: c.abi,
|
|
657
|
+
functionName: c.method,
|
|
658
|
+
args: c.args ?? []
|
|
659
|
+
}));
|
|
660
|
+
try {
|
|
661
|
+
const results = await this.publicClient.multicall({
|
|
662
|
+
contracts,
|
|
663
|
+
allowFailure: false
|
|
664
|
+
});
|
|
665
|
+
return results;
|
|
666
|
+
} catch {
|
|
667
|
+
return Promise.all(
|
|
668
|
+
contracts.map(
|
|
669
|
+
(c) => this.publicClient.readContract(c)
|
|
670
|
+
)
|
|
671
|
+
);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
// ─── ERC Standard Clients ───────────────────────────────────────────────────
|
|
675
|
+
/**
|
|
676
|
+
* Load an ERC-20 token with fully typed methods — no ABI import needed.
|
|
677
|
+
*
|
|
678
|
+
* ```ts
|
|
679
|
+
* const token = await awarizon.erc20("0x...")
|
|
680
|
+
* const balance = await token.balanceOf(userAddress)
|
|
681
|
+
* const symbol = await token.symbol()
|
|
682
|
+
* await token.transfer(recipient, 100n)
|
|
683
|
+
* ```
|
|
287
684
|
*/
|
|
685
|
+
async erc20(address) {
|
|
686
|
+
const instance = await this.contract({ address, abi: ERC20_ABI });
|
|
687
|
+
return instance;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Load an ERC-721 NFT contract with fully typed methods — no ABI import needed.
|
|
691
|
+
*
|
|
692
|
+
* ```ts
|
|
693
|
+
* const nft = await awarizon.erc721("0x...")
|
|
694
|
+
* const owner = await nft.ownerOf(1n)
|
|
695
|
+
* const uri = await nft.tokenURI(1n)
|
|
696
|
+
* await nft.transferFrom(from, to, 1n)
|
|
697
|
+
* ```
|
|
698
|
+
*/
|
|
699
|
+
async erc721(address) {
|
|
700
|
+
const instance = await this.contract({ address, abi: ERC721_ABI });
|
|
701
|
+
return instance;
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Load an ERC-1155 multi-token contract with fully typed methods — no ABI import needed.
|
|
705
|
+
*
|
|
706
|
+
* ```ts
|
|
707
|
+
* const items = await awarizon.erc1155("0x...")
|
|
708
|
+
* const balance = await items.balanceOf(userAddress, 42n)
|
|
709
|
+
* const uri = await items.uri(42n)
|
|
710
|
+
* await items.safeTransferFrom(from, to, 42n, 1n, "0x")
|
|
711
|
+
* ```
|
|
712
|
+
*/
|
|
713
|
+
async erc1155(address) {
|
|
714
|
+
const instance = await this.contract({ address, abi: ERC1155_ABI });
|
|
715
|
+
return instance;
|
|
716
|
+
}
|
|
717
|
+
// ─── Named Contract Registry ────────────────────────────────────────────────
|
|
718
|
+
/**
|
|
719
|
+
* Register a contract under a human-readable name.
|
|
720
|
+
* After registering, use `awarizon.use(name)` anywhere instead of repeating
|
|
721
|
+
* address + ABI every time.
|
|
722
|
+
*
|
|
723
|
+
* ```ts
|
|
724
|
+
* awarizon.register("USDC", { address: "0x...", abi: erc20Abi })
|
|
725
|
+
* awarizon.register("Vault", { address: "0x...", abi: vaultAbi })
|
|
726
|
+
*
|
|
727
|
+
* const usdc = await awarizon.use("USDC")
|
|
728
|
+
* const vault = await awarizon.use("Vault")
|
|
729
|
+
* ```
|
|
730
|
+
*/
|
|
731
|
+
register(name, config) {
|
|
732
|
+
this._registry.set(name, config);
|
|
733
|
+
return this;
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Retrieve a previously registered contract by name.
|
|
737
|
+
* Throws a descriptive error if the name was never registered.
|
|
738
|
+
*
|
|
739
|
+
* ```ts
|
|
740
|
+
* const usdc = await awarizon.use("USDC")
|
|
741
|
+
* await usdc.transfer(recipient, 100n)
|
|
742
|
+
* ```
|
|
743
|
+
*/
|
|
744
|
+
async use(name) {
|
|
745
|
+
const config = this._registry.get(name);
|
|
746
|
+
if (!config) {
|
|
747
|
+
const registered = [...this._registry.keys()].join(", ") || "none";
|
|
748
|
+
throw new Error(
|
|
749
|
+
`[awarizon/web3] Contract "${name}" is not registered.
|
|
750
|
+
Call awarizon.register("${name}", { address, abi }) first.
|
|
751
|
+
Currently registered: ${registered}`
|
|
752
|
+
);
|
|
753
|
+
}
|
|
754
|
+
return this.contract(config);
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Remove a registered contract from the registry.
|
|
758
|
+
*
|
|
759
|
+
* ```ts
|
|
760
|
+
* awarizon.unregister("USDC")
|
|
761
|
+
* ```
|
|
762
|
+
*/
|
|
763
|
+
unregister(name) {
|
|
764
|
+
this._registry.delete(name);
|
|
765
|
+
return this;
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* Return all registered contract names.
|
|
769
|
+
*/
|
|
770
|
+
registeredContracts() {
|
|
771
|
+
return [...this._registry.keys()];
|
|
772
|
+
}
|
|
773
|
+
// ─── Accessors ──────────────────────────────────────────────────────────────
|
|
288
774
|
get chainId() {
|
|
289
775
|
return this.chain.id;
|
|
290
776
|
}
|
|
777
|
+
get isConnected() {
|
|
778
|
+
return this._engine.isConnected();
|
|
779
|
+
}
|
|
291
780
|
/**
|
|
292
|
-
*
|
|
781
|
+
* Flush pending telemetry and stop background timers.
|
|
782
|
+
* Call when tearing down a long-lived SDK instance (e.g. on server shutdown).
|
|
293
783
|
*/
|
|
294
|
-
|
|
295
|
-
|
|
784
|
+
async destroy() {
|
|
785
|
+
await this._telemetry.destroy();
|
|
296
786
|
}
|
|
297
787
|
};
|
|
298
788
|
|
|
299
|
-
export { AwarizonWeb3, CHAINS, ContractNotLoadedError, NetworkMismatchError, ProviderError, UnsupportedChainError, getSupportedChainIds, resolveChain };
|
|
789
|
+
export { ApiKeyRequiredError, AwarizonWeb3, CHAINS, ContractNotLoadedError, ERC1155_ABI, ERC20_ABI, ERC721_ABI, InvalidApiKeyError, NetworkMismatchError, ProviderError, TelemetryClient, UnsupportedChainError, getSupportedChainIds, resolveChain };
|
|
300
790
|
//# sourceMappingURL=index.mjs.map
|
|
301
791
|
//# sourceMappingURL=index.mjs.map
|