genlayer-js 0.17.1 → 0.18.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
 
2
2
 
3
+ ## 0.18.1 (2025-09-11)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * replace hardcoded gas 21000 ([#115](https://github.com/genlayerlabs/genlayer-js/issues/115)) ([8eacd5d](https://github.com/genlayerlabs/genlayer-js/commit/8eacd5dbba9ae85f0232ec525076582ea8b6b045))
9
+
10
+ ## 0.18.0 (2025-09-04)
11
+
12
+
13
+ ### Features
14
+
15
+ * format in genlayer js to convert to an object ([#107](https://github.com/genlayerlabs/genlayer-js/issues/107)) ([3ca4076](https://github.com/genlayerlabs/genlayer-js/commit/3ca40765bc03e8d7174c8e337e6104ccea455e5a))
16
+
3
17
  ## 0.17.1 (2025-09-03)
4
18
 
5
19
 
@@ -313,6 +313,9 @@ type GenLayerMethod = {
313
313
  } | {
314
314
  method: "eth_getTransactionCount";
315
315
  params: [address: Address, block: string];
316
+ } | {
317
+ method: "eth_estimateGas";
318
+ params: [transactionParams: any];
316
319
  } | {
317
320
  method: "gen_call";
318
321
  params: [requestParams: any];
@@ -332,6 +335,7 @@ type GenLayerClient<TGenLayerChain extends GenLayerChain> = Omit<Client<Transpor
332
335
  [key: string]: CalldataEncodable;
333
336
  };
334
337
  rawReturn?: RawReturn;
338
+ jsonSafeReturn?: boolean;
335
339
  transactionHashVariant?: TransactionHashVariant;
336
340
  }) => Promise<RawReturn extends true ? `0x${string}` : CalldataEncodable>;
337
341
  writeContract: (args: {
@@ -374,6 +378,12 @@ type GenLayerClient<TGenLayerChain extends GenLayerChain> = Omit<Client<Transpor
374
378
  getCurrentNonce: (args: {
375
379
  address: Address;
376
380
  }) => Promise<number>;
381
+ estimateTransactionGas: (transactionParams: {
382
+ from?: Address;
383
+ to: Address;
384
+ data?: `0x${string}`;
385
+ value?: bigint;
386
+ }) => Promise<bigint>;
377
387
  waitForTransactionReceipt: (args: {
378
388
  hash: TransactionHash;
379
389
  status?: TransactionStatus;
@@ -313,6 +313,9 @@ type GenLayerMethod = {
313
313
  } | {
314
314
  method: "eth_getTransactionCount";
315
315
  params: [address: Address, block: string];
316
+ } | {
317
+ method: "eth_estimateGas";
318
+ params: [transactionParams: any];
316
319
  } | {
317
320
  method: "gen_call";
318
321
  params: [requestParams: any];
@@ -332,6 +335,7 @@ type GenLayerClient<TGenLayerChain extends GenLayerChain> = Omit<Client<Transpor
332
335
  [key: string]: CalldataEncodable;
333
336
  };
334
337
  rawReturn?: RawReturn;
338
+ jsonSafeReturn?: boolean;
335
339
  transactionHashVariant?: TransactionHashVariant;
336
340
  }) => Promise<RawReturn extends true ? `0x${string}` : CalldataEncodable>;
337
341
  writeContract: (args: {
@@ -374,6 +378,12 @@ type GenLayerClient<TGenLayerChain extends GenLayerChain> = Omit<Client<Transpor
374
378
  getCurrentNonce: (args: {
375
379
  address: Address;
376
380
  }) => Promise<number>;
381
+ estimateTransactionGas: (transactionParams: {
382
+ from?: Address;
383
+ to: Address;
384
+ data?: `0x${string}`;
385
+ value?: bigint;
386
+ }) => Promise<bigint>;
377
387
  waitForTransactionReceipt: (args: {
378
388
  hash: TransactionHash;
379
389
  status?: TransactionStatus;
package/dist/index.cjs CHANGED
@@ -419,6 +419,7 @@ var calldata = calldata_exports;
419
419
  var transactions = transactions_exports;
420
420
 
421
421
  // src/utils/jsonifier.ts
422
+
422
423
  function b64ToArray(b64) {
423
424
  return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
424
425
  }
@@ -457,6 +458,54 @@ function resultToUserFriendlyJson(cd64) {
457
458
  payload
458
459
  };
459
460
  }
461
+ function toJsonSafeDeep(value) {
462
+ return _toJsonSafeDeep(value, /* @__PURE__ */ new WeakSet());
463
+ }
464
+ function _toJsonSafeDeep(value, seen) {
465
+ if (value === null || value === void 0) {
466
+ return null;
467
+ }
468
+ const primitiveType = typeof value;
469
+ if (primitiveType === "string" || primitiveType === "boolean" || primitiveType === "number") {
470
+ return value;
471
+ }
472
+ if (primitiveType === "bigint") {
473
+ const big = value;
474
+ const abs = big < 0n ? -big : big;
475
+ const maxSafe = 9007199254740991n;
476
+ return abs <= maxSafe ? Number(big) : big.toString();
477
+ }
478
+ if (typeof value === "object") {
479
+ if (seen.has(value)) {
480
+ return null;
481
+ }
482
+ seen.add(value);
483
+ if (value instanceof Uint8Array) {
484
+ return _viem.toHex.call(void 0, value);
485
+ }
486
+ if (value instanceof Array) {
487
+ return value.map((v) => _toJsonSafeDeep(v, seen));
488
+ }
489
+ if (value instanceof Map) {
490
+ const obj = {};
491
+ for (const [k, v] of value.entries()) {
492
+ obj[k] = _toJsonSafeDeep(v, seen);
493
+ }
494
+ return obj;
495
+ }
496
+ if (value instanceof _chunkH4ZYXVV2cjs.CalldataAddress) {
497
+ return _viem.toHex.call(void 0, value.bytes);
498
+ }
499
+ if (Object.getPrototypeOf(value) === Object.prototype) {
500
+ const obj = {};
501
+ for (const [k, v] of Object.entries(value)) {
502
+ obj[k] = _toJsonSafeDeep(v, seen);
503
+ }
504
+ return obj;
505
+ }
506
+ }
507
+ return value;
508
+ }
460
509
 
461
510
  // src/contracts/actions.ts
462
511
  var contractActions = (client, publicClient) => {
@@ -499,6 +548,7 @@ var contractActions = (client, publicClient) => {
499
548
  functionName,
500
549
  args: callArgs,
501
550
  kwargs,
551
+ jsonSafeReturn = false,
502
552
  leaderOnly = false,
503
553
  transactionHashVariant = "latest-nonfinal" /* LATEST_NONFINAL */
504
554
  } = args;
@@ -521,7 +571,11 @@ var contractActions = (client, publicClient) => {
521
571
  return prefixedResult;
522
572
  }
523
573
  const resultBinary = _viem.fromHex.call(void 0, prefixedResult, "bytes");
524
- return decode(resultBinary);
574
+ const decoded = decode(resultBinary);
575
+ if (!jsonSafeReturn) {
576
+ return decoded;
577
+ }
578
+ return toJsonSafeDeep(decoded);
525
579
  },
526
580
  simulateWriteContract: async (args) => {
527
581
  const {
@@ -676,16 +730,28 @@ var _sendTransaction = async ({
676
730
  }
677
731
  const validatedSenderAccount = validateAccount(senderAccount);
678
732
  const nonce = await client.getCurrentNonce({ address: validatedSenderAccount.address });
733
+ let estimatedGas;
734
+ try {
735
+ estimatedGas = await client.estimateTransactionGas({
736
+ from: validatedSenderAccount.address,
737
+ to: _optionalChain([client, 'access', _20 => _20.chain, 'access', _21 => _21.consensusMainContract, 'optionalAccess', _22 => _22.address]),
738
+ data: encodedData,
739
+ value
740
+ });
741
+ } catch (error) {
742
+ console.warn("Gas estimation failed, using fallback value:", error);
743
+ estimatedGas = 200000n;
744
+ }
679
745
  const transactionRequest = await client.prepareTransactionRequest({
680
746
  account: validatedSenderAccount,
681
- to: _optionalChain([client, 'access', _20 => _20.chain, 'access', _21 => _21.consensusMainContract, 'optionalAccess', _22 => _22.address]),
747
+ to: _optionalChain([client, 'access', _23 => _23.chain, 'access', _24 => _24.consensusMainContract, 'optionalAccess', _25 => _25.address]),
682
748
  data: encodedData,
683
749
  type: "legacy",
684
750
  nonce: Number(nonce),
685
751
  value,
686
- gas: 21000n
752
+ gas: estimatedGas
687
753
  });
688
- if (_optionalChain([validatedSenderAccount, 'optionalAccess', _23 => _23.type]) !== "local") {
754
+ if (_optionalChain([validatedSenderAccount, 'optionalAccess', _26 => _26.type]) !== "local") {
689
755
  const formattedRequest = {
690
756
  from: transactionRequest.from,
691
757
  to: transactionRequest.to,
@@ -698,7 +764,7 @@ var _sendTransaction = async ({
698
764
  params: [formattedRequest]
699
765
  });
700
766
  }
701
- if (!_optionalChain([validatedSenderAccount, 'optionalAccess', _24 => _24.signTransaction])) {
767
+ if (!_optionalChain([validatedSenderAccount, 'optionalAccess', _27 => _27.signTransaction])) {
702
768
  throw new Error("Account does not support signTransaction");
703
769
  }
704
770
  const serializedTransaction = await validatedSenderAccount.signTransaction(transactionRequest);
@@ -708,7 +774,7 @@ var _sendTransaction = async ({
708
774
  throw new Error("Transaction reverted");
709
775
  }
710
776
  const newTxEvents = _viem.parseEventLogs.call(void 0, {
711
- abi: _optionalChain([client, 'access', _25 => _25.chain, 'access', _26 => _26.consensusMainContract, 'optionalAccess', _27 => _27.abi]),
777
+ abi: _optionalChain([client, 'access', _28 => _28.chain, 'access', _29 => _29.consensusMainContract, 'optionalAccess', _30 => _30.abi]),
712
778
  eventName: "NewTransaction",
713
779
  logs: receipt.logs
714
780
  });
@@ -921,7 +987,7 @@ var simplifyTransactionReceipt = (tx) => {
921
987
  var decodeLocalnetTransaction = (tx) => {
922
988
  if (!tx.data) return tx;
923
989
  try {
924
- const leaderReceipt = _optionalChain([tx, 'access', _28 => _28.consensus_data, 'optionalAccess', _29 => _29.leader_receipt]);
990
+ const leaderReceipt = _optionalChain([tx, 'access', _31 => _31.consensus_data, 'optionalAccess', _32 => _32.leader_receipt]);
925
991
  if (leaderReceipt) {
926
992
  const receipts = Array.isArray(leaderReceipt) ? leaderReceipt : [leaderReceipt];
927
993
  receipts.forEach((receipt) => {
@@ -952,7 +1018,7 @@ var decodeLocalnetTransaction = (tx) => {
952
1018
  }
953
1019
  });
954
1020
  }
955
- if (_optionalChain([tx, 'access', _30 => _30.data, 'optionalAccess', _31 => _31.calldata]) && typeof tx.data.calldata === "string") {
1021
+ if (_optionalChain([tx, 'access', _33 => _33.data, 'optionalAccess', _34 => _34.calldata]) && typeof tx.data.calldata === "string") {
956
1022
  tx.data.calldata = {
957
1023
  base64: tx.data.calldata,
958
1024
  ...calldataToUserFriendlyJson(b64ToArray(tx.data.calldata))
@@ -1015,8 +1081,8 @@ var transactionActions = (client, publicClient) => ({
1015
1081
  return decodeLocalnetTransaction(transaction2);
1016
1082
  }
1017
1083
  const transaction = await publicClient.readContract({
1018
- address: _optionalChain([client, 'access', _32 => _32.chain, 'access', _33 => _33.consensusDataContract, 'optionalAccess', _34 => _34.address]),
1019
- abi: _optionalChain([client, 'access', _35 => _35.chain, 'access', _36 => _36.consensusDataContract, 'optionalAccess', _37 => _37.abi]),
1084
+ address: _optionalChain([client, 'access', _35 => _35.chain, 'access', _36 => _36.consensusDataContract, 'optionalAccess', _37 => _37.address]),
1085
+ abi: _optionalChain([client, 'access', _38 => _38.chain, 'access', _39 => _39.consensusDataContract, 'optionalAccess', _40 => _40.abi]),
1020
1086
  functionName: "getTransactionData",
1021
1087
  args: [
1022
1088
  hash,
@@ -1025,6 +1091,19 @@ var transactionActions = (client, publicClient) => ({
1025
1091
  ]
1026
1092
  });
1027
1093
  return decodeTransaction(transaction);
1094
+ },
1095
+ estimateTransactionGas: async (transactionParams) => {
1096
+ const formattedParams = {
1097
+ from: transactionParams.from || _optionalChain([client, 'access', _41 => _41.account, 'optionalAccess', _42 => _42.address]),
1098
+ to: transactionParams.to,
1099
+ data: transactionParams.data || "0x",
1100
+ value: transactionParams.value ? `0x${transactionParams.value.toString(16)}` : "0x0"
1101
+ };
1102
+ const gasHex = await client.request({
1103
+ method: "eth_estimateGas",
1104
+ params: [formattedParams]
1105
+ });
1106
+ return BigInt(gasHex);
1028
1107
  }
1029
1108
  });
1030
1109
 
@@ -1057,7 +1136,7 @@ var connect = async (client, network = "studionet", snapSource = "npm") => {
1057
1136
  chainName: selectedNetwork.name,
1058
1137
  rpcUrls: selectedNetwork.rpcUrls.default.http,
1059
1138
  nativeCurrency: selectedNetwork.nativeCurrency,
1060
- blockExplorerUrls: [_optionalChain([selectedNetwork, 'access', _38 => _38.blockExplorers, 'optionalAccess', _39 => _39.default, 'access', _40 => _40.url])]
1139
+ blockExplorerUrls: [_optionalChain([selectedNetwork, 'access', _43 => _43.blockExplorers, 'optionalAccess', _44 => _44.default, 'access', _45 => _45.url])]
1061
1140
  };
1062
1141
  const currentChainId = await window.ethereum.request({ method: "eth_chainId" });
1063
1142
  if (currentChainId !== chainIdHex) {
@@ -1091,10 +1170,10 @@ var metamaskClient = async (snapSource = "npm") => {
1091
1170
  }
1092
1171
  const isFlask = async () => {
1093
1172
  try {
1094
- const clientVersion = await _optionalChain([window, 'access', _41 => _41.ethereum, 'optionalAccess', _42 => _42.request, 'call', _43 => _43({
1173
+ const clientVersion = await _optionalChain([window, 'access', _46 => _46.ethereum, 'optionalAccess', _47 => _47.request, 'call', _48 => _48({
1095
1174
  method: "web3_clientVersion"
1096
1175
  })]);
1097
- return _optionalChain([clientVersion, 'optionalAccess', _44 => _44.includes, 'call', _45 => _45("flask")]);
1176
+ return _optionalChain([clientVersion, 'optionalAccess', _49 => _49.includes, 'call', _50 => _50("flask")]);
1098
1177
  } catch (error) {
1099
1178
  console.error("Error detecting Flask:", error);
1100
1179
  return false;
@@ -1102,7 +1181,7 @@ var metamaskClient = async (snapSource = "npm") => {
1102
1181
  };
1103
1182
  const installedSnaps = async () => {
1104
1183
  try {
1105
- return await _optionalChain([window, 'access', _46 => _46.ethereum, 'optionalAccess', _47 => _47.request, 'call', _48 => _48({
1184
+ return await _optionalChain([window, 'access', _51 => _51.ethereum, 'optionalAccess', _52 => _52.request, 'call', _53 => _53({
1106
1185
  method: "wallet_getSnaps"
1107
1186
  })]);
1108
1187
  } catch (error) {
@@ -1137,10 +1216,10 @@ function walletActions(client) {
1137
1216
  function chainActions(client) {
1138
1217
  return {
1139
1218
  initializeConsensusSmartContract: async (forceReset = false) => {
1140
- if (_optionalChain([client, 'access', _49 => _49.chain, 'optionalAccess', _50 => _50.id]) === _chunkZKBMABRAcjs.testnetAsimov.id) {
1219
+ if (_optionalChain([client, 'access', _54 => _54.chain, 'optionalAccess', _55 => _55.id]) === _chunkZKBMABRAcjs.testnetAsimov.id) {
1141
1220
  return;
1142
1221
  }
1143
- if (!forceReset && _optionalChain([client, 'access', _51 => _51.chain, 'access', _52 => _52.consensusMainContract, 'optionalAccess', _53 => _53.address]) && _optionalChain([client, 'access', _54 => _54.chain, 'access', _55 => _55.consensusMainContract, 'optionalAccess', _56 => _56.abi])) {
1222
+ if (!forceReset && _optionalChain([client, 'access', _56 => _56.chain, 'access', _57 => _57.consensusMainContract, 'optionalAccess', _58 => _58.address]) && _optionalChain([client, 'access', _59 => _59.chain, 'access', _60 => _60.consensusMainContract, 'optionalAccess', _61 => _61.abi])) {
1144
1223
  return;
1145
1224
  }
1146
1225
  const contractsResponse = await fetch(client.chain.rpcUrls.default.http[0], {
@@ -1225,12 +1304,15 @@ var createClient = (config = { chain: _chunkZKBMABRAcjs.localnet }) => {
1225
1304
  ...config.account ? { account: config.account } : {}
1226
1305
  });
1227
1306
  const clientWithBasicActions = baseClient.extend(_viem.publicActions).extend(_viem.walletActions).extend((client) => accountActions(client));
1228
- const clientWithAllActions = {
1307
+ const clientWithTransactionActions = {
1229
1308
  ...clientWithBasicActions,
1230
- ...contractActions(clientWithBasicActions, publicClient),
1309
+ ...transactionActions(clientWithBasicActions, publicClient),
1231
1310
  ...chainActions(clientWithBasicActions),
1232
- ...walletActions(clientWithBasicActions),
1233
- ...transactionActions(clientWithBasicActions, publicClient)
1311
+ ...walletActions(clientWithBasicActions)
1312
+ };
1313
+ const clientWithAllActions = {
1314
+ ...clientWithTransactionActions,
1315
+ ...contractActions(clientWithTransactionActions, publicClient)
1234
1316
  };
1235
1317
  const finalClient = {
1236
1318
  ...clientWithAllActions,
package/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as viem from 'viem';
2
2
  import { Account, Address, Hex } from 'viem';
3
3
  import { G as GenLayerChain } from './chains-BYSCF33g.cjs';
4
- import { G as GenLayerClient, D as DecodedDeployData, a as DecodedCallData, b as GenLayerRawTransaction, c as GenLayerTransaction, C as CalldataEncodable, T as TransactionDataElement } from './index-DrEvzYFA.cjs';
4
+ import { G as GenLayerClient, D as DecodedDeployData, a as DecodedCallData, b as GenLayerRawTransaction, c as GenLayerTransaction, C as CalldataEncodable, T as TransactionDataElement } from './index-C_GfhvDJ.cjs';
5
5
  import * as abitype from 'abitype';
6
6
  import * as viem__types_types_authorization from 'viem/_types/types/authorization';
7
7
  import * as viem_accounts from 'viem/accounts';
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as viem from 'viem';
2
2
  import { Account, Address, Hex } from 'viem';
3
3
  import { G as GenLayerChain } from './chains-BYSCF33g.js';
4
- import { G as GenLayerClient, D as DecodedDeployData, a as DecodedCallData, b as GenLayerRawTransaction, c as GenLayerTransaction, C as CalldataEncodable, T as TransactionDataElement } from './index-BIyk5Fv7.js';
4
+ import { G as GenLayerClient, D as DecodedDeployData, a as DecodedCallData, b as GenLayerRawTransaction, c as GenLayerTransaction, C as CalldataEncodable, T as TransactionDataElement } from './index-BJebZa37.js';
5
5
  import * as abitype from 'abitype';
6
6
  import * as viem__types_types_authorization from 'viem/_types/types/authorization';
7
7
  import * as viem_accounts from 'viem/accounts';
package/dist/index.js CHANGED
@@ -407,7 +407,7 @@ function serialize(data) {
407
407
  }
408
408
 
409
409
  // src/contracts/actions.ts
410
- import { fromHex, toHex as toHex2, zeroAddress, encodeFunctionData, parseEventLogs } from "viem";
410
+ import { fromHex, toHex as toHex3, zeroAddress, encodeFunctionData, parseEventLogs } from "viem";
411
411
 
412
412
  // src/abi/index.ts
413
413
  var abi_exports = {};
@@ -419,6 +419,7 @@ var calldata = calldata_exports;
419
419
  var transactions = transactions_exports;
420
420
 
421
421
  // src/utils/jsonifier.ts
422
+ import { toHex as toHex2 } from "viem";
422
423
  function b64ToArray(b64) {
423
424
  return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
424
425
  }
@@ -457,6 +458,54 @@ function resultToUserFriendlyJson(cd64) {
457
458
  payload
458
459
  };
459
460
  }
461
+ function toJsonSafeDeep(value) {
462
+ return _toJsonSafeDeep(value, /* @__PURE__ */ new WeakSet());
463
+ }
464
+ function _toJsonSafeDeep(value, seen) {
465
+ if (value === null || value === void 0) {
466
+ return null;
467
+ }
468
+ const primitiveType = typeof value;
469
+ if (primitiveType === "string" || primitiveType === "boolean" || primitiveType === "number") {
470
+ return value;
471
+ }
472
+ if (primitiveType === "bigint") {
473
+ const big = value;
474
+ const abs = big < 0n ? -big : big;
475
+ const maxSafe = 9007199254740991n;
476
+ return abs <= maxSafe ? Number(big) : big.toString();
477
+ }
478
+ if (typeof value === "object") {
479
+ if (seen.has(value)) {
480
+ return null;
481
+ }
482
+ seen.add(value);
483
+ if (value instanceof Uint8Array) {
484
+ return toHex2(value);
485
+ }
486
+ if (value instanceof Array) {
487
+ return value.map((v) => _toJsonSafeDeep(v, seen));
488
+ }
489
+ if (value instanceof Map) {
490
+ const obj = {};
491
+ for (const [k, v] of value.entries()) {
492
+ obj[k] = _toJsonSafeDeep(v, seen);
493
+ }
494
+ return obj;
495
+ }
496
+ if (value instanceof CalldataAddress) {
497
+ return toHex2(value.bytes);
498
+ }
499
+ if (Object.getPrototypeOf(value) === Object.prototype) {
500
+ const obj = {};
501
+ for (const [k, v] of Object.entries(value)) {
502
+ obj[k] = _toJsonSafeDeep(v, seen);
503
+ }
504
+ return obj;
505
+ }
506
+ }
507
+ return value;
508
+ }
460
509
 
461
510
  // src/contracts/actions.ts
462
511
  var contractActions = (client, publicClient) => {
@@ -488,7 +537,7 @@ var contractActions = (client, publicClient) => {
488
537
  }
489
538
  const schema = await client.request({
490
539
  method: "gen_getContractSchemaForCode",
491
- params: [toHex2(contractCode)]
540
+ params: [toHex3(contractCode)]
492
541
  });
493
542
  return schema;
494
543
  },
@@ -499,6 +548,7 @@ var contractActions = (client, publicClient) => {
499
548
  functionName,
500
549
  args: callArgs,
501
550
  kwargs,
551
+ jsonSafeReturn = false,
502
552
  leaderOnly = false,
503
553
  transactionHashVariant = "latest-nonfinal" /* LATEST_NONFINAL */
504
554
  } = args;
@@ -521,7 +571,11 @@ var contractActions = (client, publicClient) => {
521
571
  return prefixedResult;
522
572
  }
523
573
  const resultBinary = fromHex(prefixedResult, "bytes");
524
- return decode(resultBinary);
574
+ const decoded = decode(resultBinary);
575
+ if (!jsonSafeReturn) {
576
+ return decoded;
577
+ }
578
+ return toJsonSafeDeep(decoded);
525
579
  },
526
580
  simulateWriteContract: async (args) => {
527
581
  const {
@@ -676,6 +730,18 @@ var _sendTransaction = async ({
676
730
  }
677
731
  const validatedSenderAccount = validateAccount(senderAccount);
678
732
  const nonce = await client.getCurrentNonce({ address: validatedSenderAccount.address });
733
+ let estimatedGas;
734
+ try {
735
+ estimatedGas = await client.estimateTransactionGas({
736
+ from: validatedSenderAccount.address,
737
+ to: client.chain.consensusMainContract?.address,
738
+ data: encodedData,
739
+ value
740
+ });
741
+ } catch (error) {
742
+ console.warn("Gas estimation failed, using fallback value:", error);
743
+ estimatedGas = 200000n;
744
+ }
679
745
  const transactionRequest = await client.prepareTransactionRequest({
680
746
  account: validatedSenderAccount,
681
747
  to: client.chain.consensusMainContract?.address,
@@ -683,7 +749,7 @@ var _sendTransaction = async ({
683
749
  type: "legacy",
684
750
  nonce: Number(nonce),
685
751
  value,
686
- gas: 21000n
752
+ gas: estimatedGas
687
753
  });
688
754
  if (validatedSenderAccount?.type !== "local") {
689
755
  const formattedRequest = {
@@ -1025,6 +1091,19 @@ var transactionActions = (client, publicClient) => ({
1025
1091
  ]
1026
1092
  });
1027
1093
  return decodeTransaction(transaction);
1094
+ },
1095
+ estimateTransactionGas: async (transactionParams) => {
1096
+ const formattedParams = {
1097
+ from: transactionParams.from || client.account?.address,
1098
+ to: transactionParams.to,
1099
+ data: transactionParams.data || "0x",
1100
+ value: transactionParams.value ? `0x${transactionParams.value.toString(16)}` : "0x0"
1101
+ };
1102
+ const gasHex = await client.request({
1103
+ method: "eth_estimateGas",
1104
+ params: [formattedParams]
1105
+ });
1106
+ return BigInt(gasHex);
1028
1107
  }
1029
1108
  });
1030
1109
 
@@ -1225,12 +1304,15 @@ var createClient = (config = { chain: localnet }) => {
1225
1304
  ...config.account ? { account: config.account } : {}
1226
1305
  });
1227
1306
  const clientWithBasicActions = baseClient.extend(publicActions).extend(walletActions2).extend((client) => accountActions(client));
1228
- const clientWithAllActions = {
1307
+ const clientWithTransactionActions = {
1229
1308
  ...clientWithBasicActions,
1230
- ...contractActions(clientWithBasicActions, publicClient),
1309
+ ...transactionActions(clientWithBasicActions, publicClient),
1231
1310
  ...chainActions(clientWithBasicActions),
1232
- ...walletActions(clientWithBasicActions),
1233
- ...transactionActions(clientWithBasicActions, publicClient)
1311
+ ...walletActions(clientWithBasicActions)
1312
+ };
1313
+ const clientWithAllActions = {
1314
+ ...clientWithTransactionActions,
1315
+ ...contractActions(clientWithTransactionActions, publicClient)
1234
1316
  };
1235
1317
  const finalClient = {
1236
1318
  ...clientWithAllActions,
@@ -1,3 +1,3 @@
1
1
  export { Account, Address } from 'viem';
2
- export { d as CalldataAddress, C as CalldataEncodable, i as ContractMethod, h as ContractMethodBase, f as ContractParamsArraySchemaElement, g as ContractParamsSchema, j as ContractSchema, a as DecodedCallData, D as DecodedDeployData, G as GenLayerClient, e as GenLayerMethod, b as GenLayerRawTransaction, c as GenLayerTransaction, H as Hash, M as MethodDescription, N as Network, S as SnapSource, k as TransactionHash, s as TransactionHashVariant, m as TransactionResult, p as TransactionResultNameToNumber, l as TransactionStatus, r as TransactionType, V as VoteType, o as transactionResultNumberToName, n as transactionsStatusNameToNumber, t as transactionsStatusNumberToName, q as voteTypeNameToNumber, v as voteTypeNumberToName } from '../index-DrEvzYFA.cjs';
2
+ export { d as CalldataAddress, C as CalldataEncodable, i as ContractMethod, h as ContractMethodBase, f as ContractParamsArraySchemaElement, g as ContractParamsSchema, j as ContractSchema, a as DecodedCallData, D as DecodedDeployData, G as GenLayerClient, e as GenLayerMethod, b as GenLayerRawTransaction, c as GenLayerTransaction, H as Hash, M as MethodDescription, N as Network, S as SnapSource, k as TransactionHash, s as TransactionHashVariant, m as TransactionResult, p as TransactionResultNameToNumber, l as TransactionStatus, r as TransactionType, V as VoteType, o as transactionResultNumberToName, n as transactionsStatusNameToNumber, t as transactionsStatusNumberToName, q as voteTypeNameToNumber, v as voteTypeNumberToName } from '../index-C_GfhvDJ.cjs';
3
3
  export { G as GenLayerChain } from '../chains-BYSCF33g.cjs';
@@ -1,3 +1,3 @@
1
1
  export { Account, Address } from 'viem';
2
- export { d as CalldataAddress, C as CalldataEncodable, i as ContractMethod, h as ContractMethodBase, f as ContractParamsArraySchemaElement, g as ContractParamsSchema, j as ContractSchema, a as DecodedCallData, D as DecodedDeployData, G as GenLayerClient, e as GenLayerMethod, b as GenLayerRawTransaction, c as GenLayerTransaction, H as Hash, M as MethodDescription, N as Network, S as SnapSource, k as TransactionHash, s as TransactionHashVariant, m as TransactionResult, p as TransactionResultNameToNumber, l as TransactionStatus, r as TransactionType, V as VoteType, o as transactionResultNumberToName, n as transactionsStatusNameToNumber, t as transactionsStatusNumberToName, q as voteTypeNameToNumber, v as voteTypeNumberToName } from '../index-BIyk5Fv7.js';
2
+ export { d as CalldataAddress, C as CalldataEncodable, i as ContractMethod, h as ContractMethodBase, f as ContractParamsArraySchemaElement, g as ContractParamsSchema, j as ContractSchema, a as DecodedCallData, D as DecodedDeployData, G as GenLayerClient, e as GenLayerMethod, b as GenLayerRawTransaction, c as GenLayerTransaction, H as Hash, M as MethodDescription, N as Network, S as SnapSource, k as TransactionHash, s as TransactionHashVariant, m as TransactionResult, p as TransactionResultNameToNumber, l as TransactionStatus, r as TransactionType, V as VoteType, o as transactionResultNumberToName, n as transactionsStatusNameToNumber, t as transactionsStatusNumberToName, q as voteTypeNameToNumber, v as voteTypeNumberToName } from '../index-BJebZa37.js';
3
3
  export { G as GenLayerChain } from '../chains-BYSCF33g.js';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "genlayer-js",
3
3
  "type": "module",
4
- "version": "0.17.1",
4
+ "version": "0.18.1",
5
5
  "description": "GenLayer JavaScript SDK",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -106,13 +106,18 @@ export const createClient = (config: ClientConfig = {chain: localnet}): GenLayer
106
106
  .extend(walletActions)
107
107
  .extend(client => accountActions(client as unknown as GenLayerClient<GenLayerChain>));
108
108
 
109
- // Create a client with all actions except transaction actions
110
- const clientWithAllActions = {
109
+ // First add transaction actions, then contract actions that depend on them
110
+ const clientWithTransactionActions = {
111
111
  ...clientWithBasicActions,
112
- ...contractActions(clientWithBasicActions as unknown as GenLayerClient<GenLayerChain>, publicClient),
112
+ ...transactionActions(clientWithBasicActions as unknown as GenLayerClient<GenLayerChain>, publicClient),
113
113
  ...chainActions(clientWithBasicActions as unknown as GenLayerClient<GenLayerChain>),
114
114
  ...genlayerWalletActions(clientWithBasicActions as unknown as GenLayerClient<GenLayerChain>),
115
- ...transactionActions(clientWithBasicActions as unknown as GenLayerClient<GenLayerChain>, publicClient),
115
+ } as unknown as GenLayerClient<GenLayerChain>;
116
+
117
+ // Then add contract actions that can now access transaction actions
118
+ const clientWithAllActions = {
119
+ ...clientWithTransactionActions,
120
+ ...contractActions(clientWithTransactionActions as unknown as GenLayerClient<GenLayerChain>, publicClient),
116
121
  } as unknown as GenLayerClient<GenLayerChain>;
117
122
 
118
123
  // Add transaction actions last, after all other actions are in place
@@ -11,7 +11,7 @@ import {
11
11
  TransactionHashVariant,
12
12
  } from "@/types";
13
13
  import {fromHex, toHex, zeroAddress, encodeFunctionData, PublicClient, parseEventLogs} from "viem";
14
- import {b64ToArray} from "@/utils/jsonifier";
14
+ import {toJsonSafeDeep, b64ToArray} from "@/utils/jsonifier";
15
15
 
16
16
  export const contractActions = (client: GenLayerClient<GenLayerChain>, publicClient: PublicClient) => {
17
17
  return {
@@ -53,6 +53,7 @@ export const contractActions = (client: GenLayerClient<GenLayerChain>, publicCli
53
53
  args?: CalldataEncodable[];
54
54
  kwargs?: Map<string, CalldataEncodable> | {[key: string]: CalldataEncodable};
55
55
  rawReturn?: RawReturn;
56
+ jsonSafeReturn?: boolean;
56
57
  leaderOnly?: boolean;
57
58
  transactionHashVariant?: TransactionHashVariant;
58
59
  }): Promise<RawReturn extends true ? `0x${string}` : CalldataEncodable> => {
@@ -62,6 +63,7 @@ export const contractActions = (client: GenLayerClient<GenLayerChain>, publicCli
62
63
  functionName,
63
64
  args: callArgs,
64
65
  kwargs,
66
+ jsonSafeReturn = false,
65
67
  leaderOnly = false,
66
68
  transactionHashVariant = TransactionHashVariant.LATEST_NONFINAL,
67
69
  } = args;
@@ -88,7 +90,12 @@ export const contractActions = (client: GenLayerClient<GenLayerChain>, publicCli
88
90
  return prefixedResult;
89
91
  }
90
92
  const resultBinary = fromHex(prefixedResult, "bytes");
91
- return calldata.decode(resultBinary) as any;
93
+ const decoded = calldata.decode(resultBinary) as any;
94
+ if (!jsonSafeReturn) {
95
+ return decoded;
96
+ }
97
+ // If jsonSafeReturn is requested, convert to JSON-safe recursively
98
+ return toJsonSafeDeep(decoded) as any;
92
99
  },
93
100
  simulateWriteContract: async <RawReturn extends boolean | undefined>(args: {
94
101
  account?: Account;
@@ -296,6 +303,18 @@ const _sendTransaction = async ({
296
303
  const validatedSenderAccount = validateAccount(senderAccount);
297
304
 
298
305
  const nonce = await client.getCurrentNonce({address: validatedSenderAccount.address});
306
+ let estimatedGas: bigint;
307
+ try {
308
+ estimatedGas = await client.estimateTransactionGas({
309
+ from: validatedSenderAccount.address,
310
+ to: client.chain.consensusMainContract?.address as Address,
311
+ data: encodedData,
312
+ value: value,
313
+ });
314
+ } catch (error) {
315
+ console.warn("Gas estimation failed, using fallback value:", error);
316
+ estimatedGas = 200_000n;
317
+ }
299
318
  const transactionRequest = await client.prepareTransactionRequest({
300
319
  account: validatedSenderAccount,
301
320
  to: client.chain.consensusMainContract?.address as Address,
@@ -303,7 +322,7 @@ const _sendTransaction = async ({
303
322
  type: "legacy",
304
323
  nonce: Number(nonce),
305
324
  value: value,
306
- gas: 21000n,
325
+ gas: estimatedGas,
307
326
  });
308
327
 
309
328
  if (validatedSenderAccount?.type !== "local") {
@@ -90,6 +90,26 @@ export const transactionActions = (client: GenLayerClient<GenLayerChain>, public
90
90
  })) as unknown as GenLayerRawTransaction;
91
91
  return decodeTransaction(transaction);
92
92
  },
93
+ estimateTransactionGas: async (transactionParams: {
94
+ from?: Address;
95
+ to: Address;
96
+ data?: `0x${string}`;
97
+ value?: bigint;
98
+ }): Promise<bigint> => {
99
+ const formattedParams = {
100
+ from: transactionParams.from || client.account?.address,
101
+ to: transactionParams.to,
102
+ data: transactionParams.data || "0x",
103
+ value: transactionParams.value ? `0x${transactionParams.value.toString(16)}` as `0x${string}` : "0x0" as `0x${string}`,
104
+ };
105
+
106
+ const gasHex = await client.request({
107
+ method: "eth_estimateGas",
108
+ params: [formattedParams],
109
+ }) as string;
110
+
111
+ return BigInt(gasHex);
112
+ },
93
113
  });
94
114
 
95
115
 
@@ -1,5 +1,4 @@
1
- import {GenLayerTransaction, GenLayerRawTransaction, DecodedCallData, DecodedDeployData} from "../types/transactions";
2
- import {transactionsStatusNumberToName, transactionResultNumberToName, voteTypeNumberToName, VoteType} from "../types/transactions";
1
+ import {GenLayerTransaction, GenLayerRawTransaction, DecodedCallData, DecodedDeployData,transactionsStatusNumberToName, transactionResultNumberToName, voteTypeNumberToName, VoteType} from "../types/transactions";
3
2
  import {b64ToArray, calldataToUserFriendlyJson, resultToUserFriendlyJson} from "../utils/jsonifier";
4
3
  import {fromRlp, fromHex, Hex, Address} from "viem";
5
4
  import * as calldataAbi from "../abi/calldata";
@@ -18,6 +18,7 @@ export type GenLayerMethod =
18
18
  | {method: "gen_getContractCode"; params: [address: Address]}
19
19
  | {method: "sim_getTransactionsForAddress"; params: [address: Address, filter?: "all" | "from" | "to"]}
20
20
  | {method: "eth_getTransactionCount"; params: [address: Address, block: string]}
21
+ | {method: "eth_estimateGas"; params: [transactionParams: any]}
21
22
  | {method: "gen_call"; params: [requestParams: any]};
22
23
 
23
24
  /*
@@ -47,6 +48,7 @@ export type GenLayerClient<TGenLayerChain extends GenLayerChain> = Omit<
47
48
  args?: CalldataEncodable[];
48
49
  kwargs?: Map<string, CalldataEncodable> | {[key: string]: CalldataEncodable};
49
50
  rawReturn?: RawReturn;
51
+ jsonSafeReturn?: boolean;
50
52
  transactionHashVariant?: TransactionHashVariant;
51
53
  }) => Promise<RawReturn extends true ? `0x${string}` : CalldataEncodable>;
52
54
  writeContract: (args: {
@@ -79,6 +81,12 @@ export type GenLayerClient<TGenLayerChain extends GenLayerChain> = Omit<
79
81
  }) => Promise<`0x${string}`>;
80
82
  getTransaction: (args: {hash: TransactionHash}) => Promise<GenLayerTransaction>;
81
83
  getCurrentNonce: (args: {address: Address}) => Promise<number>;
84
+ estimateTransactionGas: (transactionParams: {
85
+ from?: Address;
86
+ to: Address;
87
+ data?: `0x${string}`;
88
+ value?: bigint;
89
+ }) => Promise<bigint>;
82
90
  waitForTransactionReceipt: (args: {
83
91
  hash: TransactionHash;
84
92
  status?: TransactionStatus;
@@ -1,4 +1,7 @@
1
1
  import {calldata} from "@/abi";
2
+ import type {CalldataEncodable} from "@/types/calldata";
3
+ import {CalldataAddress} from "@/types/calldata";
4
+ import {toHex} from "viem";
2
5
 
3
6
 
4
7
  export function b64ToArray(b64: string): Uint8Array {
@@ -44,4 +47,73 @@ export function resultToUserFriendlyJson(cd64: string): any {
44
47
  status,
45
48
  payload,
46
49
  };
50
+ }
51
+
52
+ // Deeply converts CalldataEncodable values into JSON-serializable structures.
53
+ // Rules:
54
+ // - bigint: to number if safe, otherwise to decimal string
55
+ // - Uint8Array: to 0x-prefixed hex string
56
+ // - CalldataAddress: to 0x-prefixed hex string
57
+ // - Map: to Array<[key, value]> preserving order
58
+ // - Arrays and plain objects: converted recursively
59
+ export function toJsonSafeDeep<T extends CalldataEncodable>(value: T): any {
60
+ return _toJsonSafeDeep(value, new WeakSet());
61
+ }
62
+
63
+ function _toJsonSafeDeep(value: CalldataEncodable, seen: WeakSet<object>): any {
64
+ if (value === null || value === undefined) {
65
+ return null;
66
+ }
67
+
68
+ const primitiveType = typeof value;
69
+ if (primitiveType === "string" || primitiveType === "boolean" || primitiveType === "number") {
70
+ return value;
71
+ }
72
+
73
+ if (primitiveType === "bigint") {
74
+ const big = value as bigint;
75
+ const abs = big < 0n ? -big : big;
76
+ const maxSafe = 9007199254740991n; // Number.MAX_SAFE_INTEGER
77
+ return abs <= maxSafe ? Number(big) : big.toString();
78
+ }
79
+
80
+ // Objects and structured values
81
+ if (typeof value === "object") {
82
+ if (seen.has(value as object)) {
83
+ // Prevent potential cycles; represent as null
84
+ return null;
85
+ }
86
+ seen.add(value as object);
87
+
88
+ if (value instanceof Uint8Array) {
89
+ return toHex(value);
90
+ }
91
+
92
+ if (value instanceof Array) {
93
+ return value.map((v) => _toJsonSafeDeep(v as CalldataEncodable, seen));
94
+ }
95
+
96
+ if (value instanceof Map) {
97
+ const obj: Record<string, any> = {};
98
+ for (const [k, v] of value.entries()) {
99
+ obj[k] = _toJsonSafeDeep(v as CalldataEncodable, seen);
100
+ }
101
+ return obj;
102
+ }
103
+
104
+ if (value instanceof CalldataAddress) {
105
+ return toHex(value.bytes);
106
+ }
107
+
108
+ if (Object.getPrototypeOf(value) === Object.prototype) {
109
+ const obj: Record<string, any> = {};
110
+ for (const [k, v] of Object.entries(value)) {
111
+ obj[k] = _toJsonSafeDeep(v as CalldataEncodable, seen);
112
+ }
113
+ return obj;
114
+ }
115
+ }
116
+
117
+ // Fallback: return as-is (shouldn't normally reach here)
118
+ return value as any;
47
119
  }