@usekova/evm 0.1.0 → 2.0.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,51 @@
1
+ # @usekova/evm
2
+
3
+ EVM anchoring utilities for on-chain covenant verification with ABI encoding and Keccak-256 hashing.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @usekova/evm
9
+ ```
10
+
11
+ ## Key APIs
12
+
13
+ - **EVMClient**: Client for anchoring and verifying covenants on-chain via a user-supplied JSON-RPC provider. No ethers.js dependency.
14
+ - **encodeUint256() / decodeUint256()**: ABI encode/decode uint256 values.
15
+ - **encodeBytes32() / decodeBytes32()**: ABI encode/decode bytes32 values.
16
+ - **encodeAddress() / decodeAddress()**: ABI encode/decode EVM addresses with EIP-55 checksumming.
17
+ - **encodeString()**: ABI encode dynamic strings.
18
+ - **encodeFunctionCall()**: Concatenate a 4-byte selector with ABI-encoded parameters.
19
+ - **computeFunctionSelector()**: Compute the 4-byte Keccak-256 selector for a Solidity function signature.
20
+ - **buildAnchorCalldata() / parseAnchorFromCalldata()**: Encode/decode covenant anchor calldata.
21
+ - **computeAnchorHash()**: Deterministic Keccak-256 hash of a CovenantAnchor.
22
+ - **checksumAddress()**: EIP-55 mixed-case checksum encoding.
23
+ - **isValidAddress()**: Validate EVM addresses.
24
+ - **keccak256()**: Keccak-256 hashing for EVM-compatible use cases.
25
+ - **STELE_REGISTRY_ABI**: JSON ABI for the on-chain Stele registry contract.
26
+
27
+ ## Usage
28
+
29
+ ```typescript
30
+ import { EVMClient, buildAnchorCalldata, computeAnchorHash } from '@usekova/evm';
31
+
32
+ // Plug in any JSON-RPC provider (ethers, viem, raw fetch, etc.)
33
+ const client = new EVMClient(myProvider, '0xRegistryAddress');
34
+
35
+ const anchor = {
36
+ covenantId: '00'.repeat(32),
37
+ constraintsHash: 'ab'.repeat(32),
38
+ issuerAddress: '0x' + '11'.repeat(20),
39
+ beneficiaryAddress: '0x' + '22'.repeat(20),
40
+ timestamp: BigInt(Date.now()),
41
+ chainId: 1,
42
+ };
43
+
44
+ const txHash = await client.anchorCovenant(anchor, fromAddress);
45
+ const receipt = await client.waitForTransaction(txHash);
46
+ const isAnchored = await client.verifyCovenant(anchor.covenantId);
47
+ ```
48
+
49
+ ## Docs
50
+
51
+ See the [Stele SDK root documentation](../../README.md) for the full API reference.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,308 @@
1
1
  /**
2
- * @stele/evm — Solidity contracts and ethers.js bindings for on-chain anchoring.
2
+ * @usekova/evm — EVM anchoring utilities for on-chain covenant verification.
3
+ *
4
+ * Provides ABI encoding/decoding, contract interface definitions, and
5
+ * anchor/verify helpers for EVM-compatible blockchains. No ethers.js dependency.
3
6
  *
4
7
  * @packageDocumentation
5
8
  */
6
- export {};
9
+ /**
10
+ * Compute the Keccak-256 hash of a hex string input.
11
+ * This is the real EVM hash function, not a SHA-256 placeholder.
12
+ * @param input - String to hash
13
+ * @returns 64-character lowercase hex hash string
14
+ */
15
+ declare function keccak256String(input: string): string;
16
+ /**
17
+ * ABI encode a uint256 value as a 64-character hex string, left-padded with zeros.
18
+ * @param value - Non-negative bigint that fits in 256 bits
19
+ * @returns 64-character hex string (no 0x prefix)
20
+ */
21
+ export declare function encodeUint256(value: bigint): string;
22
+ /**
23
+ * ABI encode a bytes32 value. The value is right-padded to 32 bytes per ABI spec.
24
+ * @param hex - Hex string (with or without 0x prefix), at most 64 hex chars
25
+ * @returns 64-character hex string (no 0x prefix)
26
+ */
27
+ export declare function encodeBytes32(hex: string): string;
28
+ /**
29
+ * ABI encode an address (20 bytes) left-padded to 32 bytes.
30
+ * @param address - 20-byte hex address (with or without 0x prefix)
31
+ * @returns 64-character hex string (no 0x prefix)
32
+ */
33
+ export declare function encodeAddress(address: string): string;
34
+ /**
35
+ * ABI encode a dynamic string. Returns length (32 bytes) followed by
36
+ * the UTF-8 data right-padded to the next 32-byte boundary.
37
+ *
38
+ * Note: when used in a function call with mixed static/dynamic types,
39
+ * an offset pointer must be handled separately.
40
+ *
41
+ * @param value - The string to encode
42
+ * @returns Hex string: 64-char length + padded data (no 0x prefix)
43
+ */
44
+ export declare function encodeString(value: string): string;
45
+ /**
46
+ * Decode a 64-character hex string as a uint256 bigint.
47
+ * @param hex - 64-character hex string (with or without 0x prefix)
48
+ */
49
+ export declare function decodeUint256(hex: string): bigint;
50
+ /**
51
+ * Decode a 64-character hex string as bytes32.
52
+ * @param hex - 64-character hex string (with or without 0x prefix)
53
+ * @returns 64-character lowercase hex string (no 0x prefix)
54
+ */
55
+ export declare function decodeBytes32(hex: string): string;
56
+ /**
57
+ * Decode a 64-character hex string as an EVM address.
58
+ * The address is extracted from the rightmost 40 characters (left-padded).
59
+ * @param hex - 64-character hex string (with or without 0x prefix)
60
+ * @returns Checksummed address with 0x prefix
61
+ */
62
+ export declare function decodeAddress(hex: string): string;
63
+ /**
64
+ * Concatenate a 4-byte function selector with ABI-encoded parameters.
65
+ * @param selector - 4-byte function selector (8 hex chars, with or without 0x prefix)
66
+ * @param params - Already ABI-encoded parameter strings (each 64 hex chars for static types)
67
+ * @returns Hex string with 0x prefix: selector + concatenated params
68
+ */
69
+ export declare function encodeFunctionCall(selector: string, ...params: string[]): string;
70
+ /**
71
+ * Compute the 4-byte function selector for a Solidity function signature.
72
+ * Uses Keccak-256 as per the Ethereum ABI specification.
73
+ * @param signature - Canonical function signature, e.g., "transfer(address,uint256)"
74
+ * @returns 8-character hex string (4 bytes, no 0x prefix)
75
+ */
76
+ export declare function computeFunctionSelector(signature: string): string;
77
+ /**
78
+ * Represents an on-chain covenant anchor with all data needed for verification.
79
+ */
80
+ export interface CovenantAnchor {
81
+ /** Unique identifier for the covenant (64-char lowercase hex, maps to bytes32). */
82
+ covenantId: string;
83
+ /** Hash of the covenant's constraint document (64-char lowercase hex, maps to bytes32). */
84
+ constraintsHash: string;
85
+ /** EVM address of the covenant issuer (0x-prefixed, 20 bytes). */
86
+ issuerAddress: string;
87
+ /** EVM address of the covenant beneficiary (0x-prefixed, 20 bytes). */
88
+ beneficiaryAddress: string;
89
+ /** Unix timestamp of when the anchor was created. */
90
+ timestamp: bigint;
91
+ /** EVM chain ID where the anchor exists (e.g., 1 for mainnet). */
92
+ chainId: number;
93
+ }
94
+ /**
95
+ * Encode a CovenantAnchor as EVM calldata for the registry's anchor() function.
96
+ * The chainId is not included in calldata — it is implicit from the chain.
97
+ * @param anchor - The covenant anchor to encode
98
+ * @returns Hex calldata string with 0x prefix
99
+ */
100
+ export declare function buildAnchorCalldata(anchor: CovenantAnchor): string;
101
+ /**
102
+ * Parse EVM calldata back into a CovenantAnchor.
103
+ * The chainId defaults to 1 (mainnet) since it is not encoded in calldata.
104
+ * @param calldata - Hex calldata string (with or without 0x prefix)
105
+ * @returns Decoded CovenantAnchor
106
+ */
107
+ export declare function parseAnchorFromCalldata(calldata: string): CovenantAnchor;
108
+ /**
109
+ * Compute a deterministic hash of a CovenantAnchor.
110
+ * All fields (including chainId) are ABI-encoded, concatenated, and hashed.
111
+ * @param anchor - The covenant anchor to hash
112
+ * @returns 64-character lowercase hex hash string
113
+ */
114
+ export declare function computeAnchorHash(anchor: CovenantAnchor): string;
115
+ /**
116
+ * JSON ABI array for the Stele on-chain registry contract.
117
+ * Defines the methods: anchor(), verify(), and getAnchor().
118
+ */
119
+ export declare const STELE_REGISTRY_ABI: readonly [{
120
+ readonly name: "anchor";
121
+ readonly type: "function";
122
+ readonly inputs: readonly [{
123
+ readonly name: "covenantId";
124
+ readonly type: "bytes32";
125
+ }, {
126
+ readonly name: "constraintsHash";
127
+ readonly type: "bytes32";
128
+ }, {
129
+ readonly name: "issuer";
130
+ readonly type: "address";
131
+ }, {
132
+ readonly name: "beneficiary";
133
+ readonly type: "address";
134
+ }, {
135
+ readonly name: "timestamp";
136
+ readonly type: "uint256";
137
+ }];
138
+ readonly outputs: readonly [];
139
+ readonly stateMutability: "nonpayable";
140
+ }, {
141
+ readonly name: "verify";
142
+ readonly type: "function";
143
+ readonly inputs: readonly [{
144
+ readonly name: "covenantId";
145
+ readonly type: "bytes32";
146
+ }];
147
+ readonly outputs: readonly [{
148
+ readonly name: "";
149
+ readonly type: "bool";
150
+ }];
151
+ readonly stateMutability: "view";
152
+ }, {
153
+ readonly name: "getAnchor";
154
+ readonly type: "function";
155
+ readonly inputs: readonly [{
156
+ readonly name: "covenantId";
157
+ readonly type: "bytes32";
158
+ }];
159
+ readonly outputs: readonly [{
160
+ readonly name: "constraintsHash";
161
+ readonly type: "bytes32";
162
+ }, {
163
+ readonly name: "issuer";
164
+ readonly type: "address";
165
+ }, {
166
+ readonly name: "beneficiary";
167
+ readonly type: "address";
168
+ }, {
169
+ readonly name: "timestamp";
170
+ readonly type: "uint256";
171
+ }];
172
+ readonly stateMutability: "view";
173
+ }];
174
+ /**
175
+ * TypeScript interface matching the Stele on-chain registry contract ABI.
176
+ */
177
+ export interface SteleRegistryInterface {
178
+ /** Anchor a covenant on-chain. */
179
+ anchor(covenantId: string, constraintsHash: string, issuer: string, beneficiary: string, timestamp: bigint): void;
180
+ /** Check whether a covenant has been anchored. */
181
+ verify(covenantId: string): boolean;
182
+ /** Retrieve anchor data for a covenant. */
183
+ getAnchor(covenantId: string): {
184
+ constraintsHash: string;
185
+ issuer: string;
186
+ beneficiary: string;
187
+ timestamp: bigint;
188
+ };
189
+ }
190
+ /**
191
+ * Check if a hex string is a valid EVM address (20 bytes with 0x prefix).
192
+ * @param address - The string to validate
193
+ * @returns true if the address is well-formed
194
+ */
195
+ export declare function isValidAddress(address: string): boolean;
196
+ /**
197
+ * Apply EIP-55 mixed-case checksum encoding to an address.
198
+ * Uses Keccak-256 as specified by EIP-55.
199
+ *
200
+ * @param address - A valid EVM address (0x-prefixed, 40 hex chars)
201
+ * @returns The same address with EIP-55 mixed-case checksum encoding
202
+ */
203
+ export declare function checksumAddress(address: string): string;
204
+ /**
205
+ * Convert a covenant ID (64-char hex string) to 0x-prefixed bytes32 format.
206
+ * @param id - 64-character hex string (with or without 0x prefix)
207
+ * @returns 0x-prefixed lowercase hex string (66 chars total)
208
+ */
209
+ export declare function covenantIdToBytes32(id: string): string;
210
+ /**
211
+ * Minimal JSON-RPC provider interface for EVM interaction.
212
+ * Consumers supply their own transport (HTTP, WebSocket, IPC).
213
+ */
214
+ export interface EVMProvider {
215
+ /** Send a JSON-RPC request and return the result. */
216
+ request(args: {
217
+ method: string;
218
+ params?: unknown[];
219
+ }): Promise<unknown>;
220
+ }
221
+ /**
222
+ * Transaction request for sending to the network.
223
+ */
224
+ export interface TransactionRequest {
225
+ /** Target contract address (0x-prefixed). */
226
+ to: string;
227
+ /** Hex-encoded calldata (0x-prefixed). */
228
+ data: string;
229
+ /** Optional value in wei (hex-encoded). */
230
+ value?: string;
231
+ /** Optional gas limit (hex-encoded). */
232
+ gas?: string;
233
+ /** Sender address (0x-prefixed). */
234
+ from?: string;
235
+ }
236
+ /**
237
+ * Transaction receipt returned after confirmation.
238
+ */
239
+ export interface TransactionReceipt {
240
+ /** Transaction hash. */
241
+ transactionHash: string;
242
+ /** Block number (hex-encoded). */
243
+ blockNumber: string;
244
+ /** Status: '0x1' for success, '0x0' for revert. */
245
+ status: string;
246
+ /** Gas used (hex-encoded). */
247
+ gasUsed: string;
248
+ }
249
+ /**
250
+ * EVM client for anchoring and verifying covenants on-chain.
251
+ *
252
+ * Requires a user-supplied {@link EVMProvider} for actual network interaction.
253
+ * This keeps the package dependency-free while supporting any EVM-compatible chain.
254
+ *
255
+ * @example
256
+ * ```typescript
257
+ * import { EVMClient } from '@usekova/evm';
258
+ *
259
+ * // Plug in any provider (ethers, viem, raw fetch, etc.)
260
+ * const client = new EVMClient(myProvider, '0xRegistryAddress');
261
+ * const txHash = await client.anchorCovenant(anchor);
262
+ * const isAnchored = await client.verifyCovenant(covenantId);
263
+ * ```
264
+ */
265
+ export declare class EVMClient {
266
+ private readonly provider;
267
+ private readonly registryAddress;
268
+ constructor(provider: EVMProvider, registryAddress: string);
269
+ /**
270
+ * Anchor a covenant on-chain by calling the registry's anchor() function.
271
+ *
272
+ * @param anchor - The covenant anchor data to write on-chain.
273
+ * @param from - The sender address (must be unlocked in the provider).
274
+ * @returns The transaction hash.
275
+ */
276
+ anchorCovenant(anchor: CovenantAnchor, from: string): Promise<string>;
277
+ /**
278
+ * Verify whether a covenant has been anchored on-chain.
279
+ *
280
+ * @param covenantId - The 64-char hex covenant ID to check.
281
+ * @returns true if the covenant is anchored, false otherwise.
282
+ */
283
+ verifyCovenant(covenantId: string): Promise<boolean>;
284
+ /**
285
+ * Retrieve anchor data for a covenant from the on-chain registry.
286
+ *
287
+ * @param covenantId - The 64-char hex covenant ID to look up.
288
+ * @returns The anchor data, or null if not found.
289
+ */
290
+ getAnchor(covenantId: string): Promise<CovenantAnchor | null>;
291
+ /**
292
+ * Wait for a transaction to be confirmed.
293
+ *
294
+ * @param txHash - The transaction hash to wait for.
295
+ * @param maxAttempts - Maximum polling attempts (default: 30).
296
+ * @param intervalMs - Polling interval in milliseconds (default: 2000).
297
+ * @returns The transaction receipt, or null if not confirmed within maxAttempts.
298
+ */
299
+ waitForTransaction(txHash: string, maxAttempts?: number, intervalMs?: number): Promise<TransactionReceipt | null>;
300
+ /**
301
+ * Get the current chain ID from the provider.
302
+ * @returns The chain ID as a number.
303
+ */
304
+ getChainId(): Promise<number>;
305
+ }
306
+ /** Export keccak256String for consumers who need EVM-compatible hashing. */
307
+ export { keccak256String as keccak256 };
7
308
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AA2BH;;;;;GAKG;AACH,iBAAS,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ9C;AAsBD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQnD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CASjD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CASrD;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAalD;AAID;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMjD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMjD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAOjD;AAID;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAMhF;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAGjE;AAID;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,mFAAmF;IACnF,UAAU,EAAE,MAAM,CAAC;IACnB,2FAA2F;IAC3F,eAAe,EAAE,MAAM,CAAC;IACxB,kEAAkE;IAClE,aAAa,EAAE,MAAM,CAAC;IACtB,uEAAuE;IACvE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;CACjB;AASD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CASlE;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CA2BxE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAShE;AAID;;;GAGG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCrB,CAAC;AAEX;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,kCAAkC;IAClC,MAAM,CACJ,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,IAAI,CAAC;IAER,kDAAkD;IAClD,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;IAEpC,2CAA2C;IAC3C,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG;QAC7B,eAAe,EAAE,MAAM,CAAC;QACxB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAID;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAMvD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAkBvD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAStD;AAID;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,qDAAqD;IACrD,OAAO,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACzE;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,6CAA6C;IAC7C,EAAE,EAAE,MAAM,CAAC;IACX,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,wBAAwB;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAc;IACvC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;gBAE7B,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM;IAQ1D;;;;;;OAMG;IACG,cAAc,CAAC,MAAM,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAa3E;;;;;OAKG;IACG,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAc1D;;;;;OAKG;IACG,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAkCnE;;;;;;;OAOG;IACG,kBAAkB,CACtB,MAAM,EAAE,MAAM,EACd,WAAW,GAAE,MAAW,EACxB,UAAU,GAAE,MAAa,GACxB,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAgBrC;;;OAGG;IACG,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;CAOpC;AAED,4EAA4E;AAC5E,OAAO,EAAE,eAAe,IAAI,SAAS,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -1,8 +1,389 @@
1
1
  "use strict";
2
- /**
3
- * @stele/evm — Solidity contracts and ethers.js bindings for on-chain anchoring.
4
- *
5
- * @packageDocumentation
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- //# sourceMappingURL=index.js.map
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ EVMClient: () => EVMClient,
24
+ STELE_REGISTRY_ABI: () => STELE_REGISTRY_ABI,
25
+ buildAnchorCalldata: () => buildAnchorCalldata,
26
+ checksumAddress: () => checksumAddress,
27
+ computeAnchorHash: () => computeAnchorHash,
28
+ computeFunctionSelector: () => computeFunctionSelector,
29
+ covenantIdToBytes32: () => covenantIdToBytes32,
30
+ decodeAddress: () => decodeAddress,
31
+ decodeBytes32: () => decodeBytes32,
32
+ decodeUint256: () => decodeUint256,
33
+ encodeAddress: () => encodeAddress,
34
+ encodeBytes32: () => encodeBytes32,
35
+ encodeFunctionCall: () => encodeFunctionCall,
36
+ encodeString: () => encodeString,
37
+ encodeUint256: () => encodeUint256,
38
+ isValidAddress: () => isValidAddress,
39
+ keccak256: () => keccak256String,
40
+ parseAnchorFromCalldata: () => parseAnchorFromCalldata
41
+ });
42
+ module.exports = __toCommonJS(index_exports);
43
+ var import_sha3 = require("@noble/hashes/sha3");
44
+ var MAX_UINT256 = 2n ** 256n - 1n;
45
+ function strip0x(hex) {
46
+ return hex.startsWith("0x") || hex.startsWith("0X") ? hex.slice(2) : hex;
47
+ }
48
+ function keccak256String(input) {
49
+ const bytes = new TextEncoder().encode(input);
50
+ const hash = (0, import_sha3.keccak_256)(bytes);
51
+ let hex = "";
52
+ for (let i = 0; i < hash.length; i++) {
53
+ hex += hash[i].toString(16).padStart(2, "0");
54
+ }
55
+ return hex;
56
+ }
57
+ function keccak256Hex(hexData) {
58
+ const bytes = new Uint8Array(hexData.length / 2);
59
+ for (let i = 0; i < hexData.length; i += 2) {
60
+ bytes[i / 2] = parseInt(hexData.substring(i, i + 2), 16);
61
+ }
62
+ const hash = (0, import_sha3.keccak_256)(bytes);
63
+ let hex = "";
64
+ for (let i = 0; i < hash.length; i++) {
65
+ hex += hash[i].toString(16).padStart(2, "0");
66
+ }
67
+ return hex;
68
+ }
69
+ function encodeUint256(value) {
70
+ if (value < 0n) {
71
+ throw new Error("uint256 cannot be negative");
72
+ }
73
+ if (value > MAX_UINT256) {
74
+ throw new Error("uint256 overflow");
75
+ }
76
+ return value.toString(16).padStart(64, "0");
77
+ }
78
+ function encodeBytes32(hex) {
79
+ const clean = strip0x(hex);
80
+ if (clean.length > 64) {
81
+ throw new Error("bytes32 value exceeds 32 bytes");
82
+ }
83
+ if (clean.length > 0 && !/^[0-9a-fA-F]+$/.test(clean)) {
84
+ throw new Error("Invalid hex string");
85
+ }
86
+ return clean.toLowerCase().padEnd(64, "0");
87
+ }
88
+ function encodeAddress(address) {
89
+ const clean = strip0x(address).toLowerCase();
90
+ if (clean.length !== 40) {
91
+ throw new Error("Invalid address: must be 20 bytes (40 hex chars)");
92
+ }
93
+ if (!/^[0-9a-f]{40}$/.test(clean)) {
94
+ throw new Error("Invalid address: not valid hex");
95
+ }
96
+ return clean.padStart(64, "0");
97
+ }
98
+ function encodeString(value) {
99
+ const bytes = new TextEncoder().encode(value);
100
+ const length = encodeUint256(BigInt(bytes.length));
101
+ if (bytes.length === 0) {
102
+ return length;
103
+ }
104
+ const paddedLength = Math.ceil(bytes.length / 32) * 32;
105
+ let dataHex = "";
106
+ for (let i = 0; i < bytes.length; i++) {
107
+ dataHex += bytes[i].toString(16).padStart(2, "0");
108
+ }
109
+ dataHex = dataHex.padEnd(paddedLength * 2, "0");
110
+ return length + dataHex;
111
+ }
112
+ function decodeUint256(hex) {
113
+ const clean = strip0x(hex);
114
+ if (clean.length !== 64) {
115
+ throw new Error("Expected 64-character hex string for uint256");
116
+ }
117
+ return BigInt("0x" + clean);
118
+ }
119
+ function decodeBytes32(hex) {
120
+ const clean = strip0x(hex);
121
+ if (clean.length !== 64) {
122
+ throw new Error("Expected 64-character hex string for bytes32");
123
+ }
124
+ return clean.toLowerCase();
125
+ }
126
+ function decodeAddress(hex) {
127
+ const clean = strip0x(hex);
128
+ if (clean.length !== 64) {
129
+ throw new Error("Expected 64-character hex string for address");
130
+ }
131
+ const addrHex = clean.slice(24);
132
+ return checksumAddress("0x" + addrHex);
133
+ }
134
+ function encodeFunctionCall(selector, ...params) {
135
+ const cleanSelector = strip0x(selector);
136
+ if (cleanSelector.length !== 8) {
137
+ throw new Error("Function selector must be 4 bytes (8 hex chars)");
138
+ }
139
+ return "0x" + cleanSelector + params.join("");
140
+ }
141
+ function computeFunctionSelector(signature) {
142
+ const hash = keccak256String(signature);
143
+ return hash.slice(0, 8);
144
+ }
145
+ var ANCHOR_SELECTOR = computeFunctionSelector(
146
+ "anchor(bytes32,bytes32,address,address,uint256)"
147
+ );
148
+ function buildAnchorCalldata(anchor) {
149
+ return encodeFunctionCall(
150
+ ANCHOR_SELECTOR,
151
+ encodeBytes32(anchor.covenantId),
152
+ encodeBytes32(anchor.constraintsHash),
153
+ encodeAddress(anchor.issuerAddress),
154
+ encodeAddress(anchor.beneficiaryAddress),
155
+ encodeUint256(anchor.timestamp)
156
+ );
157
+ }
158
+ function parseAnchorFromCalldata(calldata) {
159
+ const data = strip0x(calldata);
160
+ if (data.length < 328) {
161
+ throw new Error("Calldata too short for anchor function");
162
+ }
163
+ const selector = data.slice(0, 8);
164
+ if (selector !== ANCHOR_SELECTOR) {
165
+ throw new Error(
166
+ `Invalid function selector: expected ${ANCHOR_SELECTOR}, got ${selector}`
167
+ );
168
+ }
169
+ const covenantId = decodeBytes32(data.slice(8, 72));
170
+ const constraintsHash = decodeBytes32(data.slice(72, 136));
171
+ const issuerAddress = decodeAddress(data.slice(136, 200));
172
+ const beneficiaryAddress = decodeAddress(data.slice(200, 264));
173
+ const timestamp = decodeUint256(data.slice(264, 328));
174
+ return {
175
+ covenantId,
176
+ constraintsHash,
177
+ issuerAddress,
178
+ beneficiaryAddress,
179
+ timestamp,
180
+ chainId: 1
181
+ };
182
+ }
183
+ function computeAnchorHash(anchor) {
184
+ const encoded = encodeBytes32(anchor.covenantId) + encodeBytes32(anchor.constraintsHash) + encodeAddress(anchor.issuerAddress) + encodeAddress(anchor.beneficiaryAddress) + encodeUint256(anchor.timestamp) + encodeUint256(BigInt(anchor.chainId));
185
+ return keccak256Hex(encoded);
186
+ }
187
+ var STELE_REGISTRY_ABI = [
188
+ {
189
+ name: "anchor",
190
+ type: "function",
191
+ inputs: [
192
+ { name: "covenantId", type: "bytes32" },
193
+ { name: "constraintsHash", type: "bytes32" },
194
+ { name: "issuer", type: "address" },
195
+ { name: "beneficiary", type: "address" },
196
+ { name: "timestamp", type: "uint256" }
197
+ ],
198
+ outputs: [],
199
+ stateMutability: "nonpayable"
200
+ },
201
+ {
202
+ name: "verify",
203
+ type: "function",
204
+ inputs: [{ name: "covenantId", type: "bytes32" }],
205
+ outputs: [{ name: "", type: "bool" }],
206
+ stateMutability: "view"
207
+ },
208
+ {
209
+ name: "getAnchor",
210
+ type: "function",
211
+ inputs: [{ name: "covenantId", type: "bytes32" }],
212
+ outputs: [
213
+ { name: "constraintsHash", type: "bytes32" },
214
+ { name: "issuer", type: "address" },
215
+ { name: "beneficiary", type: "address" },
216
+ { name: "timestamp", type: "uint256" }
217
+ ],
218
+ stateMutability: "view"
219
+ }
220
+ ];
221
+ function isValidAddress(address) {
222
+ if (typeof address !== "string") return false;
223
+ if (!address.startsWith("0x")) return false;
224
+ const hex = address.slice(2);
225
+ if (hex.length !== 40) return false;
226
+ return /^[0-9a-fA-F]{40}$/.test(hex);
227
+ }
228
+ function checksumAddress(address) {
229
+ if (!isValidAddress(address)) {
230
+ throw new Error("Invalid EVM address");
231
+ }
232
+ const lower = address.slice(2).toLowerCase();
233
+ const hash = keccak256String(lower);
234
+ let result = "0x";
235
+ for (let i = 0; i < 40; i++) {
236
+ const c = lower[i];
237
+ if (/[a-f]/.test(c)) {
238
+ const hashNibble = parseInt(hash[i], 16);
239
+ result += hashNibble >= 8 ? c.toUpperCase() : c;
240
+ } else {
241
+ result += c;
242
+ }
243
+ }
244
+ return result;
245
+ }
246
+ function covenantIdToBytes32(id) {
247
+ const clean = strip0x(id);
248
+ if (clean.length !== 64) {
249
+ throw new Error("Covenant ID must be 32 bytes (64 hex chars)");
250
+ }
251
+ if (!/^[0-9a-fA-F]{64}$/.test(clean)) {
252
+ throw new Error("Invalid hex string");
253
+ }
254
+ return "0x" + clean.toLowerCase();
255
+ }
256
+ var EVMClient = class {
257
+ provider;
258
+ registryAddress;
259
+ constructor(provider, registryAddress) {
260
+ if (!isValidAddress(registryAddress)) {
261
+ throw new Error("Invalid registry address");
262
+ }
263
+ this.provider = provider;
264
+ this.registryAddress = registryAddress;
265
+ }
266
+ /**
267
+ * Anchor a covenant on-chain by calling the registry's anchor() function.
268
+ *
269
+ * @param anchor - The covenant anchor data to write on-chain.
270
+ * @param from - The sender address (must be unlocked in the provider).
271
+ * @returns The transaction hash.
272
+ */
273
+ async anchorCovenant(anchor, from) {
274
+ const calldata = buildAnchorCalldata(anchor);
275
+ const txHash = await this.provider.request({
276
+ method: "eth_sendTransaction",
277
+ params: [{
278
+ from,
279
+ to: this.registryAddress,
280
+ data: calldata
281
+ }]
282
+ });
283
+ return txHash;
284
+ }
285
+ /**
286
+ * Verify whether a covenant has been anchored on-chain.
287
+ *
288
+ * @param covenantId - The 64-char hex covenant ID to check.
289
+ * @returns true if the covenant is anchored, false otherwise.
290
+ */
291
+ async verifyCovenant(covenantId) {
292
+ const selector = computeFunctionSelector("verify(bytes32)");
293
+ const calldata = "0x" + selector + encodeBytes32(covenantId);
294
+ const result = await this.provider.request({
295
+ method: "eth_call",
296
+ params: [{
297
+ to: this.registryAddress,
298
+ data: calldata
299
+ }, "latest"]
300
+ });
301
+ return result !== "0x" + "0".repeat(64);
302
+ }
303
+ /**
304
+ * Retrieve anchor data for a covenant from the on-chain registry.
305
+ *
306
+ * @param covenantId - The 64-char hex covenant ID to look up.
307
+ * @returns The anchor data, or null if not found.
308
+ */
309
+ async getAnchor(covenantId) {
310
+ const selector = computeFunctionSelector("getAnchor(bytes32)");
311
+ const calldata = "0x" + selector + encodeBytes32(covenantId);
312
+ const result = await this.provider.request({
313
+ method: "eth_call",
314
+ params: [{
315
+ to: this.registryAddress,
316
+ data: calldata
317
+ }, "latest"]
318
+ });
319
+ const data = result.startsWith("0x") ? result.slice(2) : result;
320
+ if (!data || data === "0".repeat(256)) {
321
+ return null;
322
+ }
323
+ const constraintsHash = decodeBytes32(data.slice(0, 64));
324
+ const issuerAddress = decodeAddress(data.slice(64, 128));
325
+ const beneficiaryAddress = decodeAddress(data.slice(128, 192));
326
+ const timestamp = decodeUint256(data.slice(192, 256));
327
+ return {
328
+ covenantId,
329
+ constraintsHash,
330
+ issuerAddress,
331
+ beneficiaryAddress,
332
+ timestamp,
333
+ chainId: 1
334
+ };
335
+ }
336
+ /**
337
+ * Wait for a transaction to be confirmed.
338
+ *
339
+ * @param txHash - The transaction hash to wait for.
340
+ * @param maxAttempts - Maximum polling attempts (default: 30).
341
+ * @param intervalMs - Polling interval in milliseconds (default: 2000).
342
+ * @returns The transaction receipt, or null if not confirmed within maxAttempts.
343
+ */
344
+ async waitForTransaction(txHash, maxAttempts = 30, intervalMs = 2e3) {
345
+ for (let i = 0; i < maxAttempts; i++) {
346
+ const receipt = await this.provider.request({
347
+ method: "eth_getTransactionReceipt",
348
+ params: [txHash]
349
+ });
350
+ if (receipt) {
351
+ return receipt;
352
+ }
353
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
354
+ }
355
+ return null;
356
+ }
357
+ /**
358
+ * Get the current chain ID from the provider.
359
+ * @returns The chain ID as a number.
360
+ */
361
+ async getChainId() {
362
+ const result = await this.provider.request({
363
+ method: "eth_chainId",
364
+ params: []
365
+ });
366
+ return parseInt(result, 16);
367
+ }
368
+ };
369
+ // Annotate the CommonJS export names for ESM import in node:
370
+ 0 && (module.exports = {
371
+ EVMClient,
372
+ STELE_REGISTRY_ABI,
373
+ buildAnchorCalldata,
374
+ checksumAddress,
375
+ computeAnchorHash,
376
+ computeFunctionSelector,
377
+ covenantIdToBytes32,
378
+ decodeAddress,
379
+ decodeBytes32,
380
+ decodeUint256,
381
+ encodeAddress,
382
+ encodeBytes32,
383
+ encodeFunctionCall,
384
+ encodeString,
385
+ encodeUint256,
386
+ isValidAddress,
387
+ keccak256,
388
+ parseAnchorFromCalldata
389
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,347 @@
1
+ // src/index.ts
2
+ import { keccak_256 } from "@noble/hashes/sha3";
3
+ var MAX_UINT256 = 2n ** 256n - 1n;
4
+ function strip0x(hex) {
5
+ return hex.startsWith("0x") || hex.startsWith("0X") ? hex.slice(2) : hex;
6
+ }
7
+ function keccak256String(input) {
8
+ const bytes = new TextEncoder().encode(input);
9
+ const hash = keccak_256(bytes);
10
+ let hex = "";
11
+ for (let i = 0; i < hash.length; i++) {
12
+ hex += hash[i].toString(16).padStart(2, "0");
13
+ }
14
+ return hex;
15
+ }
16
+ function keccak256Hex(hexData) {
17
+ const bytes = new Uint8Array(hexData.length / 2);
18
+ for (let i = 0; i < hexData.length; i += 2) {
19
+ bytes[i / 2] = parseInt(hexData.substring(i, i + 2), 16);
20
+ }
21
+ const hash = keccak_256(bytes);
22
+ let hex = "";
23
+ for (let i = 0; i < hash.length; i++) {
24
+ hex += hash[i].toString(16).padStart(2, "0");
25
+ }
26
+ return hex;
27
+ }
28
+ function encodeUint256(value) {
29
+ if (value < 0n) {
30
+ throw new Error("uint256 cannot be negative");
31
+ }
32
+ if (value > MAX_UINT256) {
33
+ throw new Error("uint256 overflow");
34
+ }
35
+ return value.toString(16).padStart(64, "0");
36
+ }
37
+ function encodeBytes32(hex) {
38
+ const clean = strip0x(hex);
39
+ if (clean.length > 64) {
40
+ throw new Error("bytes32 value exceeds 32 bytes");
41
+ }
42
+ if (clean.length > 0 && !/^[0-9a-fA-F]+$/.test(clean)) {
43
+ throw new Error("Invalid hex string");
44
+ }
45
+ return clean.toLowerCase().padEnd(64, "0");
46
+ }
47
+ function encodeAddress(address) {
48
+ const clean = strip0x(address).toLowerCase();
49
+ if (clean.length !== 40) {
50
+ throw new Error("Invalid address: must be 20 bytes (40 hex chars)");
51
+ }
52
+ if (!/^[0-9a-f]{40}$/.test(clean)) {
53
+ throw new Error("Invalid address: not valid hex");
54
+ }
55
+ return clean.padStart(64, "0");
56
+ }
57
+ function encodeString(value) {
58
+ const bytes = new TextEncoder().encode(value);
59
+ const length = encodeUint256(BigInt(bytes.length));
60
+ if (bytes.length === 0) {
61
+ return length;
62
+ }
63
+ const paddedLength = Math.ceil(bytes.length / 32) * 32;
64
+ let dataHex = "";
65
+ for (let i = 0; i < bytes.length; i++) {
66
+ dataHex += bytes[i].toString(16).padStart(2, "0");
67
+ }
68
+ dataHex = dataHex.padEnd(paddedLength * 2, "0");
69
+ return length + dataHex;
70
+ }
71
+ function decodeUint256(hex) {
72
+ const clean = strip0x(hex);
73
+ if (clean.length !== 64) {
74
+ throw new Error("Expected 64-character hex string for uint256");
75
+ }
76
+ return BigInt("0x" + clean);
77
+ }
78
+ function decodeBytes32(hex) {
79
+ const clean = strip0x(hex);
80
+ if (clean.length !== 64) {
81
+ throw new Error("Expected 64-character hex string for bytes32");
82
+ }
83
+ return clean.toLowerCase();
84
+ }
85
+ function decodeAddress(hex) {
86
+ const clean = strip0x(hex);
87
+ if (clean.length !== 64) {
88
+ throw new Error("Expected 64-character hex string for address");
89
+ }
90
+ const addrHex = clean.slice(24);
91
+ return checksumAddress("0x" + addrHex);
92
+ }
93
+ function encodeFunctionCall(selector, ...params) {
94
+ const cleanSelector = strip0x(selector);
95
+ if (cleanSelector.length !== 8) {
96
+ throw new Error("Function selector must be 4 bytes (8 hex chars)");
97
+ }
98
+ return "0x" + cleanSelector + params.join("");
99
+ }
100
+ function computeFunctionSelector(signature) {
101
+ const hash = keccak256String(signature);
102
+ return hash.slice(0, 8);
103
+ }
104
+ var ANCHOR_SELECTOR = computeFunctionSelector(
105
+ "anchor(bytes32,bytes32,address,address,uint256)"
106
+ );
107
+ function buildAnchorCalldata(anchor) {
108
+ return encodeFunctionCall(
109
+ ANCHOR_SELECTOR,
110
+ encodeBytes32(anchor.covenantId),
111
+ encodeBytes32(anchor.constraintsHash),
112
+ encodeAddress(anchor.issuerAddress),
113
+ encodeAddress(anchor.beneficiaryAddress),
114
+ encodeUint256(anchor.timestamp)
115
+ );
116
+ }
117
+ function parseAnchorFromCalldata(calldata) {
118
+ const data = strip0x(calldata);
119
+ if (data.length < 328) {
120
+ throw new Error("Calldata too short for anchor function");
121
+ }
122
+ const selector = data.slice(0, 8);
123
+ if (selector !== ANCHOR_SELECTOR) {
124
+ throw new Error(
125
+ `Invalid function selector: expected ${ANCHOR_SELECTOR}, got ${selector}`
126
+ );
127
+ }
128
+ const covenantId = decodeBytes32(data.slice(8, 72));
129
+ const constraintsHash = decodeBytes32(data.slice(72, 136));
130
+ const issuerAddress = decodeAddress(data.slice(136, 200));
131
+ const beneficiaryAddress = decodeAddress(data.slice(200, 264));
132
+ const timestamp = decodeUint256(data.slice(264, 328));
133
+ return {
134
+ covenantId,
135
+ constraintsHash,
136
+ issuerAddress,
137
+ beneficiaryAddress,
138
+ timestamp,
139
+ chainId: 1
140
+ };
141
+ }
142
+ function computeAnchorHash(anchor) {
143
+ const encoded = encodeBytes32(anchor.covenantId) + encodeBytes32(anchor.constraintsHash) + encodeAddress(anchor.issuerAddress) + encodeAddress(anchor.beneficiaryAddress) + encodeUint256(anchor.timestamp) + encodeUint256(BigInt(anchor.chainId));
144
+ return keccak256Hex(encoded);
145
+ }
146
+ var STELE_REGISTRY_ABI = [
147
+ {
148
+ name: "anchor",
149
+ type: "function",
150
+ inputs: [
151
+ { name: "covenantId", type: "bytes32" },
152
+ { name: "constraintsHash", type: "bytes32" },
153
+ { name: "issuer", type: "address" },
154
+ { name: "beneficiary", type: "address" },
155
+ { name: "timestamp", type: "uint256" }
156
+ ],
157
+ outputs: [],
158
+ stateMutability: "nonpayable"
159
+ },
160
+ {
161
+ name: "verify",
162
+ type: "function",
163
+ inputs: [{ name: "covenantId", type: "bytes32" }],
164
+ outputs: [{ name: "", type: "bool" }],
165
+ stateMutability: "view"
166
+ },
167
+ {
168
+ name: "getAnchor",
169
+ type: "function",
170
+ inputs: [{ name: "covenantId", type: "bytes32" }],
171
+ outputs: [
172
+ { name: "constraintsHash", type: "bytes32" },
173
+ { name: "issuer", type: "address" },
174
+ { name: "beneficiary", type: "address" },
175
+ { name: "timestamp", type: "uint256" }
176
+ ],
177
+ stateMutability: "view"
178
+ }
179
+ ];
180
+ function isValidAddress(address) {
181
+ if (typeof address !== "string") return false;
182
+ if (!address.startsWith("0x")) return false;
183
+ const hex = address.slice(2);
184
+ if (hex.length !== 40) return false;
185
+ return /^[0-9a-fA-F]{40}$/.test(hex);
186
+ }
187
+ function checksumAddress(address) {
188
+ if (!isValidAddress(address)) {
189
+ throw new Error("Invalid EVM address");
190
+ }
191
+ const lower = address.slice(2).toLowerCase();
192
+ const hash = keccak256String(lower);
193
+ let result = "0x";
194
+ for (let i = 0; i < 40; i++) {
195
+ const c = lower[i];
196
+ if (/[a-f]/.test(c)) {
197
+ const hashNibble = parseInt(hash[i], 16);
198
+ result += hashNibble >= 8 ? c.toUpperCase() : c;
199
+ } else {
200
+ result += c;
201
+ }
202
+ }
203
+ return result;
204
+ }
205
+ function covenantIdToBytes32(id) {
206
+ const clean = strip0x(id);
207
+ if (clean.length !== 64) {
208
+ throw new Error("Covenant ID must be 32 bytes (64 hex chars)");
209
+ }
210
+ if (!/^[0-9a-fA-F]{64}$/.test(clean)) {
211
+ throw new Error("Invalid hex string");
212
+ }
213
+ return "0x" + clean.toLowerCase();
214
+ }
215
+ var EVMClient = class {
216
+ provider;
217
+ registryAddress;
218
+ constructor(provider, registryAddress) {
219
+ if (!isValidAddress(registryAddress)) {
220
+ throw new Error("Invalid registry address");
221
+ }
222
+ this.provider = provider;
223
+ this.registryAddress = registryAddress;
224
+ }
225
+ /**
226
+ * Anchor a covenant on-chain by calling the registry's anchor() function.
227
+ *
228
+ * @param anchor - The covenant anchor data to write on-chain.
229
+ * @param from - The sender address (must be unlocked in the provider).
230
+ * @returns The transaction hash.
231
+ */
232
+ async anchorCovenant(anchor, from) {
233
+ const calldata = buildAnchorCalldata(anchor);
234
+ const txHash = await this.provider.request({
235
+ method: "eth_sendTransaction",
236
+ params: [{
237
+ from,
238
+ to: this.registryAddress,
239
+ data: calldata
240
+ }]
241
+ });
242
+ return txHash;
243
+ }
244
+ /**
245
+ * Verify whether a covenant has been anchored on-chain.
246
+ *
247
+ * @param covenantId - The 64-char hex covenant ID to check.
248
+ * @returns true if the covenant is anchored, false otherwise.
249
+ */
250
+ async verifyCovenant(covenantId) {
251
+ const selector = computeFunctionSelector("verify(bytes32)");
252
+ const calldata = "0x" + selector + encodeBytes32(covenantId);
253
+ const result = await this.provider.request({
254
+ method: "eth_call",
255
+ params: [{
256
+ to: this.registryAddress,
257
+ data: calldata
258
+ }, "latest"]
259
+ });
260
+ return result !== "0x" + "0".repeat(64);
261
+ }
262
+ /**
263
+ * Retrieve anchor data for a covenant from the on-chain registry.
264
+ *
265
+ * @param covenantId - The 64-char hex covenant ID to look up.
266
+ * @returns The anchor data, or null if not found.
267
+ */
268
+ async getAnchor(covenantId) {
269
+ const selector = computeFunctionSelector("getAnchor(bytes32)");
270
+ const calldata = "0x" + selector + encodeBytes32(covenantId);
271
+ const result = await this.provider.request({
272
+ method: "eth_call",
273
+ params: [{
274
+ to: this.registryAddress,
275
+ data: calldata
276
+ }, "latest"]
277
+ });
278
+ const data = result.startsWith("0x") ? result.slice(2) : result;
279
+ if (!data || data === "0".repeat(256)) {
280
+ return null;
281
+ }
282
+ const constraintsHash = decodeBytes32(data.slice(0, 64));
283
+ const issuerAddress = decodeAddress(data.slice(64, 128));
284
+ const beneficiaryAddress = decodeAddress(data.slice(128, 192));
285
+ const timestamp = decodeUint256(data.slice(192, 256));
286
+ return {
287
+ covenantId,
288
+ constraintsHash,
289
+ issuerAddress,
290
+ beneficiaryAddress,
291
+ timestamp,
292
+ chainId: 1
293
+ };
294
+ }
295
+ /**
296
+ * Wait for a transaction to be confirmed.
297
+ *
298
+ * @param txHash - The transaction hash to wait for.
299
+ * @param maxAttempts - Maximum polling attempts (default: 30).
300
+ * @param intervalMs - Polling interval in milliseconds (default: 2000).
301
+ * @returns The transaction receipt, or null if not confirmed within maxAttempts.
302
+ */
303
+ async waitForTransaction(txHash, maxAttempts = 30, intervalMs = 2e3) {
304
+ for (let i = 0; i < maxAttempts; i++) {
305
+ const receipt = await this.provider.request({
306
+ method: "eth_getTransactionReceipt",
307
+ params: [txHash]
308
+ });
309
+ if (receipt) {
310
+ return receipt;
311
+ }
312
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
313
+ }
314
+ return null;
315
+ }
316
+ /**
317
+ * Get the current chain ID from the provider.
318
+ * @returns The chain ID as a number.
319
+ */
320
+ async getChainId() {
321
+ const result = await this.provider.request({
322
+ method: "eth_chainId",
323
+ params: []
324
+ });
325
+ return parseInt(result, 16);
326
+ }
327
+ };
328
+ export {
329
+ EVMClient,
330
+ STELE_REGISTRY_ABI,
331
+ buildAnchorCalldata,
332
+ checksumAddress,
333
+ computeAnchorHash,
334
+ computeFunctionSelector,
335
+ covenantIdToBytes32,
336
+ decodeAddress,
337
+ decodeBytes32,
338
+ decodeUint256,
339
+ encodeAddress,
340
+ encodeBytes32,
341
+ encodeFunctionCall,
342
+ encodeString,
343
+ encodeUint256,
344
+ isValidAddress,
345
+ keccak256String as keccak256,
346
+ parseAnchorFromCalldata
347
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,28 +1,44 @@
1
1
  {
2
2
  "name": "@usekova/evm",
3
- "version": "0.1.0",
4
- "description": "Solidity contracts and ethers.js bindings for on-chain anchoring",
3
+ "version": "2.0.0",
4
+ "description": "EVM anchoring utilities with ABI encoding, Keccak-256 hashing, and JSON-RPC provider interface",
5
+ "sideEffects": false,
5
6
  "main": "dist/index.js",
6
7
  "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
7
15
  "files": [
8
- "dist"
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
9
19
  ],
10
20
  "scripts": {
11
- "build": "tsc --project tsconfig.json",
21
+ "build": "tsup src/index.ts --format cjs,esm && tsc --emitDeclarationOnly --declaration --outDir dist",
12
22
  "typecheck": "tsc --noEmit",
13
- "test": "vitest run"
23
+ "test": "vitest run --config ../../vitest.config.ts",
24
+ "prepublishOnly": "npm run build"
14
25
  },
15
26
  "dependencies": {
16
- "@usekova/types": "0.1.0",
17
- "@usekova/crypto": "0.1.0",
18
- "@usekova/core": "0.1.0"
27
+ "@usekova/types": "2.0.0",
28
+ "@usekova/crypto": "2.0.0",
29
+ "@usekova/core": "2.0.0",
30
+ "@noble/hashes": "^1.4.0"
31
+ },
32
+ "license": "MIT",
33
+ "engines": {
34
+ "node": ">=18"
19
35
  },
20
36
  "publishConfig": {
21
37
  "access": "public"
22
38
  },
23
39
  "repository": {
24
40
  "type": "git",
25
- "url": "https://github.com/agbusiness195/stele",
41
+ "url": "https://github.com/agbusiness195/Stele.git",
26
42
  "directory": "packages/evm"
27
43
  },
28
44
  "homepage": "https://stele.dev",
@@ -30,5 +46,14 @@
30
46
  "url": "https://github.com/agbusiness195/stele/issues"
31
47
  },
32
48
  "author": "Stele Labs, Inc.",
33
- "license": "MIT"
49
+ "keywords": [
50
+ "stele",
51
+ "covenant",
52
+ "ai-accountability",
53
+ "evm",
54
+ "ethereum",
55
+ "solidity",
56
+ "blockchain"
57
+ ],
58
+ "module": "./dist/index.mjs"
34
59
  }
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;GAIG"}