Skip to content

Commit f99d15a

Browse files
committed
feat(core): v1.1.0 — configurable timeout, callContract, writeAndWait, typed getters
- Add TxOptions interface (timeout, gasLimit, gasPrice) - Add stringifyBigInt utility for BigInt JSON serialization - Add callContract, getBalanceRaw, getChainId on base clients - Add writeAndWait convenience on wallet clients - Add typed getters (viemPublicClient, civeWalletClient, etc.) - Change waitForTransaction timeout: 5s → configurable (default 30s) - Update ChainClient interface with new methods - 118/118 tests pass
1 parent 4422bf3 commit f99d15a

File tree

9 files changed

+303
-19
lines changed

9 files changed

+303
-19
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,4 @@ docs/CFXDEVKIT-ORG-PLAN.md
6262
# ── Ansible local overrides (contain real IPs + secrets — never commit) ───────
6363
infra/ansible/inventory.local.ini
6464
infra/ansible/vars/local.yml
65+
plan.md

packages/core/CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,25 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66

77
---
88

9+
## [1.1.0] - 2026-03-25
10+
11+
### Added
12+
- `TxOptions` interface (`timeout`, `gasLimit`, `gasPrice`) for fine-grained tx control
13+
- `stringifyBigInt(value, space?)` utility for safe JSON serialization of BigInt values
14+
- `EspaceClient.callContract<T>()` — typed read-only contract calls on the base client
15+
- `EspaceClient.getBalanceRaw()` — raw bigint balance (no formatting)
16+
- `CoreClient.callContract<T>()`, `CoreClient.getBalanceRaw()`, `CoreClient.getChainId()`
17+
- `EspaceWalletClient.writeAndWait()``writeContract` + `waitForTransaction` in one call
18+
- `CoreWalletClient.writeAndWait()` — same convenience for Core Space
19+
- Typed getters: `viemPublicClient`, `viemWalletClient` (eSpace); `civePublicClient`, `civeWalletClient` (Core)
20+
21+
### Changed
22+
- `waitForTransaction` timeout is now configurable (default 30s, was hardcoded 5s)
23+
- `deployContract` accepts `TxOptions` for gas/timeout overrides
24+
- `ChainClient` interface updated: added `getBalanceRaw`, `getChainId`, `callContract`, optional `timeout` param on `waitForTransaction`
25+
26+
---
27+
928
## [0.1.0] - 2026-02-19
1029

1130
### Added

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@cfxdevkit/core",
3-
"version": "1.0.16",
3+
"version": "1.1.0",
44
"description": "Core Conflux SDK – blockchain client abstractions, wallet tools, and core utilities",
55
"type": "module",
66
"main": "dist/index.js",

packages/core/src/clients/core.ts

Lines changed: 138 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ import type {
6767
TestConfig,
6868
TransactionEvent,
6969
TransactionReceipt,
70+
TxOptions,
7071
WalletClient as UnifiedWalletClient,
7172
UnwatchFunction,
7273
WalletConfig,
@@ -197,11 +198,24 @@ export class CoreClient implements ChainClient {
197198
);
198199
}
199200

200-
async waitForTransaction(hash: string): Promise<TransactionReceipt> {
201+
/**
202+
* Wait for a transaction to be confirmed.
203+
* @param hash - Transaction hash.
204+
* @param timeout - Timeout in milliseconds (default: 30 000). Pass `0` for no timeout.
205+
*/
206+
async waitForTransaction(
207+
hash: string,
208+
timeout?: number
209+
): Promise<TransactionReceipt> {
201210
try {
202-
const receipt = await this.publicClient.waitForTransactionReceipt({
211+
const opts: Record<string, unknown> = {
203212
hash: hash as `0x${string}`,
204-
});
213+
};
214+
const t = timeout ?? 30_000;
215+
if (t > 0) opts.timeout = t;
216+
const receipt = await this.publicClient.waitForTransactionReceipt(
217+
opts as { hash: `0x${string}`; timeout?: number }
218+
);
205219

206220
return {
207221
hash: receipt.transactionHash,
@@ -231,6 +245,75 @@ export class CoreClient implements ChainClient {
231245
}
232246
}
233247

248+
/**
249+
* Get the raw balance as a bigint (in Drip, 1 CFX = 10^18 Drip).
250+
* Use this when you need the exact value for arithmetic.
251+
*/
252+
async getBalanceRaw(address: Address): Promise<bigint> {
253+
if (!isCoreAddress(address)) {
254+
throw new NodeError(
255+
'Invalid Core address format',
256+
'INVALID_ADDRESS',
257+
'core'
258+
);
259+
}
260+
try {
261+
return await this.publicClient.getBalance({ address });
262+
} catch (error) {
263+
throw new NodeError(
264+
`Failed to get balance: ${error instanceof Error ? error.message : String(error)}`,
265+
'BALANCE_ERROR',
266+
'core',
267+
{ address, originalError: error }
268+
);
269+
}
270+
}
271+
272+
/**
273+
* Get the chain ID from the connected node.
274+
*/
275+
async getChainId(): Promise<number> {
276+
try {
277+
const status = await this.publicClient.getStatus();
278+
return Number(status.chainId);
279+
} catch (error) {
280+
throw new NodeError(
281+
`Failed to get chain ID: ${error instanceof Error ? error.message : String(error)}`,
282+
'CHAIN_ID_ERROR',
283+
'core',
284+
{ originalError: error }
285+
);
286+
}
287+
}
288+
289+
/**
290+
* Read a contract function (view/pure). Available on the public client —
291+
* no wallet / private key required.
292+
*/
293+
async callContract<T = unknown>(
294+
address: string,
295+
abi: unknown[],
296+
functionName: string,
297+
args: unknown[] = []
298+
): Promise<T> {
299+
try {
300+
const result = await this.publicClient.readContract({
301+
address: address as Address,
302+
abi,
303+
functionName,
304+
args,
305+
});
306+
return result as T;
307+
} catch (error) {
308+
throw new NodeError(
309+
`Failed to call contract: ${error instanceof Error ? error.message : String(error)}`,
310+
'CONTRACT_CALL_ERROR',
311+
'core',
312+
{ address, functionName, args, originalError: error }
313+
);
314+
}
315+
}
316+
234317
async getTokenBalance(
235318
tokenAddress: Address,
236319
holderAddress?: Address
@@ -510,16 +593,19 @@ export class CoreWalletClient implements UnifiedWalletClient {
510593
}
511594
}
512595

513-
/** @internal */
514-
getInternalClient(): WalletClient {
515-
return this.walletClient;
516-
}
517-
518-
async waitForTransaction(hash: string): Promise<TransactionReceipt> {
596+
/**
597+
* Wait for a transaction to be confirmed.
598+
* @param hash - Transaction hash.
599+
* @param timeout - Timeout in milliseconds (default: 30 000).
600+
*/
601+
async waitForTransaction(
602+
hash: string,
603+
timeout?: number
604+
): Promise<TransactionReceipt> {
519605
try {
520606
const receipt = await this.publicClient.waitForTransactionReceipt({
521607
hash: hash as `0x${string}`,
522-
timeout: 5_000, // 5 second timeout for faster response
608+
timeout: timeout ?? 30_000,
523609
});
524610

525611
return {
@@ -668,7 +754,8 @@ export class CoreWalletClient implements UnifiedWalletClient {
668754
async deployContract(
669755
abi: unknown[],
670756
bytecode: string,
671-
constructorArgs: unknown[] = []
757+
constructorArgs: unknown[] = [],
758+
options?: TxOptions
672759
): Promise<string> {
673760
try {
674761
const hash = await this.walletClient.deployContract({
@@ -677,10 +764,12 @@ export class CoreWalletClient implements UnifiedWalletClient {
677764
abi,
678765
bytecode: bytecode as `0x${string}`,
679766
args: constructorArgs,
767+
gas: options?.gasLimit,
768+
gasPrice: options?.gasPrice,
680769
});
681770

682771
// Wait for transaction to be mined and get the contract address
683-
const receipt = await this.waitForTransaction(hash);
772+
const receipt = await this.waitForTransaction(hash, options?.timeout);
684773

685774
if (!receipt.contractAddress) {
686775
throw new Error('Contract address not found in transaction receipt');
@@ -757,6 +846,43 @@ export class CoreWalletClient implements UnifiedWalletClient {
757846
);
758847
}
759848
}
849+
850+
/**
851+
* Write to a contract and wait for the transaction to be confirmed.
852+
* Convenience wrapper around `writeContract` + `waitForTransaction`.
853+
*/
854+
async writeAndWait(
855+
address: string,
856+
abi: unknown[],
857+
functionName: string,
858+
args: unknown[] = [],
859+
value?: bigint,
860+
options?: TxOptions
861+
): Promise<TransactionReceipt> {
862+
const hash = await this.writeContract(
863+
address,
864+
abi,
865+
functionName,
866+
args,
867+
value
868+
);
869+
return this.waitForTransaction(hash, options?.timeout);
870+
}
871+
872+
/** @internal */
873+
getInternalClient(): WalletClient {
874+
return this.walletClient;
875+
}
876+
877+
/** Access the underlying cive WalletClient for advanced operations. */
878+
get civeWalletClient(): WalletClient {
879+
return this.walletClient;
880+
}
881+
882+
/** Access the underlying cive PublicClient for advanced read operations. */
883+
get civePublicClient(): PublicClient {
884+
return this.publicClient;
885+
}
760886
}
761887

762888
/**

0 commit comments

Comments
 (0)