genlayer-js 0.12.0 → 0.13.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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
 
2
2
 
3
+ ## 0.13.0 (2025-07-23)
4
+
5
+
6
+ ### Features
7
+
8
+ * simplify transaction receipt ([#101](https://github.com/genlayerlabs/genlayer-js/issues/101)) ([be78ac6](https://github.com/genlayerlabs/genlayer-js/commit/be78ac6b5871a4d61349bacf87b5fb0da61b1a90))
9
+
10
+ ## 0.12.1 (2025-07-21)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * default transaction hash variant ([#100](https://github.com/genlayerlabs/genlayer-js/issues/100)) ([d8926b1](https://github.com/genlayerlabs/genlayer-js/commit/d8926b16f8983df805da713091297a5d353e9332))
16
+
3
17
  ## 0.12.0 (2025-07-15)
4
18
 
5
19
 
package/README.md CHANGED
@@ -38,6 +38,31 @@ const transactionHash = "0x...";
38
38
  const transaction = await client.getTransaction({ hash: transactionHash })
39
39
  ```
40
40
 
41
+ ### Waiting for Transaction Receipt
42
+ ```typescript
43
+ import { localnet } from 'genlayer-js/chains';
44
+ import { createClient } from "genlayer-js";
45
+ import { TransactionStatus } from "genlayer-js/types";
46
+
47
+ const client = createClient({
48
+ chain: localnet,
49
+ });
50
+
51
+ // Get simplified receipt (default - removes binary data, keeps execution results)
52
+ const receipt = await client.waitForTransactionReceipt({
53
+ hash: "0x...",
54
+ status: TransactionStatus.FINALIZED,
55
+ fullTransaction: false // Default - simplified for readability
56
+ });
57
+
58
+ // Get complete receipt with all fields
59
+ const fullReceipt = await client.waitForTransactionReceipt({
60
+ hash: "0x...",
61
+ status: TransactionStatus.FINALIZED,
62
+ fullTransaction: true // Complete receipt with all internal data
63
+ });
64
+ ```
65
+
41
66
  ### Reading a contract
42
67
  ```typescript
43
68
  import { localnet } from 'genlayer-js/chains';
@@ -74,7 +99,11 @@ const transactionHash = await client.writeContract({
74
99
  value: 0, // value is optional, if you want to send some native token to the contract
75
100
  });
76
101
 
77
- const receipt = await client.waitForTransactionReceipt({ hash: txHash, status: TransactionStatus.FINALIZED}) //or ACCEPTED
102
+ const receipt = await client.waitForTransactionReceipt({
103
+ hash: txHash,
104
+ status: TransactionStatus.FINALIZED, // or ACCEPTED
105
+ fullTransaction: false // False by default - returns simplified receipt for better readability
106
+ })
78
107
 
79
108
  ```
80
109
  ## 🚀 Key Features
package/dist/index.cjs CHANGED
@@ -437,7 +437,7 @@ var contractActions = (client, publicClient) => {
437
437
  args: callArgs,
438
438
  kwargs,
439
439
  leaderOnly = false,
440
- transactionHashVariant = "latest-final" /* LATEST_FINAL */
440
+ transactionHashVariant = "latest-nonfinal" /* LATEST_NONFINAL */
441
441
  } = args;
442
442
  const encodedData = [encode(makeCalldataObject(functionName, callArgs, kwargs)), leaderOnly];
443
443
  const serializedData = serialize(encodedData);
@@ -684,12 +684,55 @@ function resultToUserFriendlyJson(cd64) {
684
684
 
685
685
  // src/transactions/actions.ts
686
686
 
687
+ var FIELDS_TO_REMOVE = [
688
+ "raw",
689
+ "contract_state",
690
+ "base64",
691
+ "consensus_history",
692
+ "tx_data",
693
+ "eq_blocks_outputs",
694
+ "r",
695
+ "s",
696
+ "v",
697
+ "created_timestamp",
698
+ "current_timestamp",
699
+ "tx_execution_hash",
700
+ "random_seed",
701
+ "states",
702
+ "contract_code",
703
+ "appeal_failed",
704
+ "appeal_leader_timeout",
705
+ "appeal_processing_time",
706
+ "appeal_undetermined",
707
+ "appealed",
708
+ "timestamp_appeal",
709
+ "config_rotation_rounds",
710
+ "rotation_count",
711
+ "queue_position",
712
+ "queue_type",
713
+ "leader_timeout_validators",
714
+ "triggered_by",
715
+ "num_of_initial_validators",
716
+ "timestamp_awaiting_finalization",
717
+ "last_vote_timestamp",
718
+ "read_state_block_range",
719
+ "tx_slot",
720
+ "blockHash",
721
+ "blockNumber",
722
+ "to",
723
+ "transactionIndex"
724
+ ];
725
+ var FIELD_NAME_MAPPINGS = {
726
+ statusName: "status_name",
727
+ typeHex: "type"
728
+ };
687
729
  var receiptActions = (client, publicClient) => ({
688
730
  waitForTransactionReceipt: async ({
689
731
  hash,
690
732
  status = "ACCEPTED" /* ACCEPTED */,
691
733
  interval = transactionsConfig.waitInterval,
692
- retries = transactionsConfig.retries
734
+ retries = transactionsConfig.retries,
735
+ fullTransaction = false
693
736
  }) => {
694
737
  const transaction = await client.getTransaction({
695
738
  hash
@@ -701,10 +744,14 @@ var receiptActions = (client, publicClient) => ({
701
744
  const transactionStatusFinalized = _chunkH4ZYXVV2cjs.transactionsStatusNameToNumber["FINALIZED" /* FINALIZED */];
702
745
  const requestedStatus = _chunkH4ZYXVV2cjs.transactionsStatusNameToNumber[status];
703
746
  if (transactionStatusString === requestedStatus || status === "ACCEPTED" /* ACCEPTED */ && transactionStatusString === transactionStatusFinalized) {
747
+ let finalTransaction = transaction;
704
748
  if (client.chain.id === _chunkZKBMABRAcjs.localnet.id) {
705
- return _decodeLocalnetTransaction(transaction);
749
+ finalTransaction = _decodeLocalnetTransaction(transaction);
750
+ }
751
+ if (!fullTransaction) {
752
+ return _simplifyTransactionReceipt(finalTransaction);
706
753
  }
707
- return transaction;
754
+ return finalTransaction;
708
755
  }
709
756
  if (retries === 0) {
710
757
  throw new Error("Transaction status is not " + status);
@@ -714,7 +761,8 @@ var receiptActions = (client, publicClient) => ({
714
761
  hash,
715
762
  status,
716
763
  interval,
717
- retries: retries - 1
764
+ retries: retries - 1,
765
+ fullTransaction
718
766
  });
719
767
  }
720
768
  });
@@ -814,6 +862,77 @@ var _decodeTransaction = (tx) => {
814
862
  };
815
863
  return decodedTx;
816
864
  };
865
+ var _simplifyTransactionReceipt = (tx) => {
866
+ const simplifyObject = (obj, path = "") => {
867
+ if (obj === null || obj === void 0) return obj;
868
+ if (Array.isArray(obj)) {
869
+ return obj.map((item) => simplifyObject(item, path)).filter((item) => item !== void 0);
870
+ }
871
+ if (typeof obj === "object") {
872
+ const result = {};
873
+ for (const [key, value] of Object.entries(obj)) {
874
+ const currentPath = path ? `${path}.${key}` : key;
875
+ if (FIELDS_TO_REMOVE.includes(key)) {
876
+ continue;
877
+ }
878
+ if (key === "node_config" && !path.includes("consensus_data")) {
879
+ continue;
880
+ }
881
+ if (key === "consensus_data" && typeof value === "object" && value !== null) {
882
+ const simplifiedConsensus = {};
883
+ if ("votes" in value) {
884
+ simplifiedConsensus.votes = value.votes;
885
+ }
886
+ if ("leader_receipt" in value && Array.isArray(value.leader_receipt)) {
887
+ simplifiedConsensus.leader_receipt = value.leader_receipt.map((receipt) => {
888
+ const simplifiedReceipt = {};
889
+ ["execution_result", "genvm_result", "mode", "vote", "node_config"].forEach((field) => {
890
+ if (field in receipt) {
891
+ simplifiedReceipt[field] = receipt[field];
892
+ }
893
+ });
894
+ if (receipt.calldata && typeof receipt.calldata === "object" && "readable" in receipt.calldata) {
895
+ simplifiedReceipt.calldata = { readable: receipt.calldata.readable };
896
+ }
897
+ if (receipt.eq_outputs) {
898
+ simplifiedReceipt.eq_outputs = simplifyObject(receipt.eq_outputs, currentPath);
899
+ }
900
+ if (receipt.result) {
901
+ simplifiedReceipt.result = simplifyObject(receipt.result, currentPath);
902
+ }
903
+ return simplifiedReceipt;
904
+ });
905
+ }
906
+ if ("validators" in value && Array.isArray(value.validators)) {
907
+ const simplifiedValidators = value.validators.map((validator) => {
908
+ const simplifiedValidator = {};
909
+ ["execution_result", "genvm_result", "mode", "vote", "node_config"].forEach((field) => {
910
+ if (field in validator) {
911
+ simplifiedValidator[field] = validator[field];
912
+ }
913
+ });
914
+ return simplifiedValidator;
915
+ }).filter((validator) => Object.keys(validator).length > 0);
916
+ if (simplifiedValidators.length > 0) {
917
+ simplifiedConsensus.validators = simplifiedValidators;
918
+ }
919
+ }
920
+ result[key] = simplifiedConsensus;
921
+ continue;
922
+ }
923
+ const simplifiedValue = simplifyObject(value, currentPath);
924
+ const shouldInclude = simplifiedValue !== void 0 && !(typeof simplifiedValue === "object" && simplifiedValue !== null && Object.keys(simplifiedValue).length === 0);
925
+ if (shouldInclude || simplifiedValue === 0) {
926
+ const mappedKey = FIELD_NAME_MAPPINGS[key] || key;
927
+ result[mappedKey] = simplifiedValue;
928
+ }
929
+ }
930
+ return result;
931
+ }
932
+ return obj;
933
+ };
934
+ return simplifyObject({ ...tx });
935
+ };
817
936
  var _decodeLocalnetTransaction = (tx) => {
818
937
  if (!tx.data) return tx;
819
938
  try {
@@ -831,11 +950,20 @@ var _decodeLocalnetTransaction = (tx) => {
831
950
  };
832
951
  }
833
952
  if (receipt.eq_outputs) {
834
- receipt.eq_outputs = Object.fromEntries(
835
- Object.entries(receipt.eq_outputs).map(([key, value]) => {
836
- return [key, resultToUserFriendlyJson(String(value))];
837
- })
838
- );
953
+ const decodedOutputs = {};
954
+ for (const [key, value] of Object.entries(receipt.eq_outputs)) {
955
+ if (typeof value === "object" && value !== null) {
956
+ decodedOutputs[key] = value;
957
+ } else {
958
+ try {
959
+ decodedOutputs[key] = resultToUserFriendlyJson(value);
960
+ } catch (e) {
961
+ console.warn(`Error decoding eq_output ${key}: ${e}`);
962
+ decodedOutputs[key] = value;
963
+ }
964
+ }
965
+ }
966
+ receipt.eq_outputs = decodedOutputs;
839
967
  }
840
968
  });
841
969
  }
package/dist/index.js CHANGED
@@ -437,7 +437,7 @@ var contractActions = (client, publicClient) => {
437
437
  args: callArgs,
438
438
  kwargs,
439
439
  leaderOnly = false,
440
- transactionHashVariant = "latest-final" /* LATEST_FINAL */
440
+ transactionHashVariant = "latest-nonfinal" /* LATEST_NONFINAL */
441
441
  } = args;
442
442
  const encodedData = [encode(makeCalldataObject(functionName, callArgs, kwargs)), leaderOnly];
443
443
  const serializedData = serialize(encodedData);
@@ -684,12 +684,55 @@ function resultToUserFriendlyJson(cd64) {
684
684
 
685
685
  // src/transactions/actions.ts
686
686
  import { fromRlp, fromHex as fromHex2 } from "viem";
687
+ var FIELDS_TO_REMOVE = [
688
+ "raw",
689
+ "contract_state",
690
+ "base64",
691
+ "consensus_history",
692
+ "tx_data",
693
+ "eq_blocks_outputs",
694
+ "r",
695
+ "s",
696
+ "v",
697
+ "created_timestamp",
698
+ "current_timestamp",
699
+ "tx_execution_hash",
700
+ "random_seed",
701
+ "states",
702
+ "contract_code",
703
+ "appeal_failed",
704
+ "appeal_leader_timeout",
705
+ "appeal_processing_time",
706
+ "appeal_undetermined",
707
+ "appealed",
708
+ "timestamp_appeal",
709
+ "config_rotation_rounds",
710
+ "rotation_count",
711
+ "queue_position",
712
+ "queue_type",
713
+ "leader_timeout_validators",
714
+ "triggered_by",
715
+ "num_of_initial_validators",
716
+ "timestamp_awaiting_finalization",
717
+ "last_vote_timestamp",
718
+ "read_state_block_range",
719
+ "tx_slot",
720
+ "blockHash",
721
+ "blockNumber",
722
+ "to",
723
+ "transactionIndex"
724
+ ];
725
+ var FIELD_NAME_MAPPINGS = {
726
+ statusName: "status_name",
727
+ typeHex: "type"
728
+ };
687
729
  var receiptActions = (client, publicClient) => ({
688
730
  waitForTransactionReceipt: async ({
689
731
  hash,
690
732
  status = "ACCEPTED" /* ACCEPTED */,
691
733
  interval = transactionsConfig.waitInterval,
692
- retries = transactionsConfig.retries
734
+ retries = transactionsConfig.retries,
735
+ fullTransaction = false
693
736
  }) => {
694
737
  const transaction = await client.getTransaction({
695
738
  hash
@@ -701,10 +744,14 @@ var receiptActions = (client, publicClient) => ({
701
744
  const transactionStatusFinalized = transactionsStatusNameToNumber["FINALIZED" /* FINALIZED */];
702
745
  const requestedStatus = transactionsStatusNameToNumber[status];
703
746
  if (transactionStatusString === requestedStatus || status === "ACCEPTED" /* ACCEPTED */ && transactionStatusString === transactionStatusFinalized) {
747
+ let finalTransaction = transaction;
704
748
  if (client.chain.id === localnet.id) {
705
- return _decodeLocalnetTransaction(transaction);
749
+ finalTransaction = _decodeLocalnetTransaction(transaction);
750
+ }
751
+ if (!fullTransaction) {
752
+ return _simplifyTransactionReceipt(finalTransaction);
706
753
  }
707
- return transaction;
754
+ return finalTransaction;
708
755
  }
709
756
  if (retries === 0) {
710
757
  throw new Error("Transaction status is not " + status);
@@ -714,7 +761,8 @@ var receiptActions = (client, publicClient) => ({
714
761
  hash,
715
762
  status,
716
763
  interval,
717
- retries: retries - 1
764
+ retries: retries - 1,
765
+ fullTransaction
718
766
  });
719
767
  }
720
768
  });
@@ -814,6 +862,77 @@ var _decodeTransaction = (tx) => {
814
862
  };
815
863
  return decodedTx;
816
864
  };
865
+ var _simplifyTransactionReceipt = (tx) => {
866
+ const simplifyObject = (obj, path = "") => {
867
+ if (obj === null || obj === void 0) return obj;
868
+ if (Array.isArray(obj)) {
869
+ return obj.map((item) => simplifyObject(item, path)).filter((item) => item !== void 0);
870
+ }
871
+ if (typeof obj === "object") {
872
+ const result = {};
873
+ for (const [key, value] of Object.entries(obj)) {
874
+ const currentPath = path ? `${path}.${key}` : key;
875
+ if (FIELDS_TO_REMOVE.includes(key)) {
876
+ continue;
877
+ }
878
+ if (key === "node_config" && !path.includes("consensus_data")) {
879
+ continue;
880
+ }
881
+ if (key === "consensus_data" && typeof value === "object" && value !== null) {
882
+ const simplifiedConsensus = {};
883
+ if ("votes" in value) {
884
+ simplifiedConsensus.votes = value.votes;
885
+ }
886
+ if ("leader_receipt" in value && Array.isArray(value.leader_receipt)) {
887
+ simplifiedConsensus.leader_receipt = value.leader_receipt.map((receipt) => {
888
+ const simplifiedReceipt = {};
889
+ ["execution_result", "genvm_result", "mode", "vote", "node_config"].forEach((field) => {
890
+ if (field in receipt) {
891
+ simplifiedReceipt[field] = receipt[field];
892
+ }
893
+ });
894
+ if (receipt.calldata && typeof receipt.calldata === "object" && "readable" in receipt.calldata) {
895
+ simplifiedReceipt.calldata = { readable: receipt.calldata.readable };
896
+ }
897
+ if (receipt.eq_outputs) {
898
+ simplifiedReceipt.eq_outputs = simplifyObject(receipt.eq_outputs, currentPath);
899
+ }
900
+ if (receipt.result) {
901
+ simplifiedReceipt.result = simplifyObject(receipt.result, currentPath);
902
+ }
903
+ return simplifiedReceipt;
904
+ });
905
+ }
906
+ if ("validators" in value && Array.isArray(value.validators)) {
907
+ const simplifiedValidators = value.validators.map((validator) => {
908
+ const simplifiedValidator = {};
909
+ ["execution_result", "genvm_result", "mode", "vote", "node_config"].forEach((field) => {
910
+ if (field in validator) {
911
+ simplifiedValidator[field] = validator[field];
912
+ }
913
+ });
914
+ return simplifiedValidator;
915
+ }).filter((validator) => Object.keys(validator).length > 0);
916
+ if (simplifiedValidators.length > 0) {
917
+ simplifiedConsensus.validators = simplifiedValidators;
918
+ }
919
+ }
920
+ result[key] = simplifiedConsensus;
921
+ continue;
922
+ }
923
+ const simplifiedValue = simplifyObject(value, currentPath);
924
+ const shouldInclude = simplifiedValue !== void 0 && !(typeof simplifiedValue === "object" && simplifiedValue !== null && Object.keys(simplifiedValue).length === 0);
925
+ if (shouldInclude || simplifiedValue === 0) {
926
+ const mappedKey = FIELD_NAME_MAPPINGS[key] || key;
927
+ result[mappedKey] = simplifiedValue;
928
+ }
929
+ }
930
+ return result;
931
+ }
932
+ return obj;
933
+ };
934
+ return simplifyObject({ ...tx });
935
+ };
817
936
  var _decodeLocalnetTransaction = (tx) => {
818
937
  if (!tx.data) return tx;
819
938
  try {
@@ -831,11 +950,20 @@ var _decodeLocalnetTransaction = (tx) => {
831
950
  };
832
951
  }
833
952
  if (receipt.eq_outputs) {
834
- receipt.eq_outputs = Object.fromEntries(
835
- Object.entries(receipt.eq_outputs).map(([key, value]) => {
836
- return [key, resultToUserFriendlyJson(String(value))];
837
- })
838
- );
953
+ const decodedOutputs = {};
954
+ for (const [key, value] of Object.entries(receipt.eq_outputs)) {
955
+ if (typeof value === "object" && value !== null) {
956
+ decodedOutputs[key] = value;
957
+ } else {
958
+ try {
959
+ decodedOutputs[key] = resultToUserFriendlyJson(value);
960
+ } catch (e) {
961
+ console.warn(`Error decoding eq_output ${key}: ${e}`);
962
+ decodedOutputs[key] = value;
963
+ }
964
+ }
965
+ }
966
+ receipt.eq_outputs = decodedOutputs;
839
967
  }
840
968
  });
841
969
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "genlayer-js",
3
3
  "type": "module",
4
- "version": "0.12.0",
4
+ "version": "0.13.0",
5
5
  "description": "GenLayer JavaScript SDK",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -88,7 +88,7 @@ export const contractActions = (client: GenLayerClient<GenLayerChain>, publicCli
88
88
  args: callArgs,
89
89
  kwargs,
90
90
  leaderOnly = false,
91
- transactionHashVariant = TransactionHashVariant.LATEST_FINAL,
91
+ transactionHashVariant = TransactionHashVariant.LATEST_NONFINAL,
92
92
  } = args;
93
93
 
94
94
  const encodedData = [calldata.encode(makeCalldataObject(functionName, callArgs, kwargs)), leaderOnly];
@@ -20,17 +20,40 @@ import {Abi, PublicClient, fromRlp, fromHex, Hex, Address} from "viem";
20
20
  import * as calldataAbi from "@/abi/calldata";
21
21
  import {localnet} from "@/chains/localnet";
22
22
 
23
+ // Fields to remove from simplified transaction receipts
24
+ const FIELDS_TO_REMOVE = [
25
+ "raw", "contract_state", "base64", "consensus_history", "tx_data",
26
+ "eq_blocks_outputs", "r", "s", "v", "created_timestamp",
27
+ "current_timestamp", "tx_execution_hash", "random_seed", "states",
28
+ "contract_code", "appeal_failed", "appeal_leader_timeout",
29
+ "appeal_processing_time", "appeal_undetermined", "appealed",
30
+ "timestamp_appeal", "config_rotation_rounds", "rotation_count",
31
+ "queue_position", "queue_type", "leader_timeout_validators",
32
+ "triggered_by", "num_of_initial_validators",
33
+ "timestamp_awaiting_finalization", "last_vote_timestamp",
34
+ "read_state_block_range", "tx_slot", "blockHash", "blockNumber",
35
+ "to", "transactionIndex"
36
+ ];
37
+
38
+ // Field name mappings for cross-language compatibility with genlayer-py
39
+ const FIELD_NAME_MAPPINGS: Record<string, string> = {
40
+ statusName: "status_name",
41
+ typeHex: "type"
42
+ };
43
+
23
44
  export const receiptActions = (client: GenLayerClient<GenLayerChain>, publicClient: PublicClient) => ({
24
45
  waitForTransactionReceipt: async ({
25
46
  hash,
26
47
  status = TransactionStatus.ACCEPTED,
27
48
  interval = transactionsConfig.waitInterval,
28
49
  retries = transactionsConfig.retries,
50
+ fullTransaction = false,
29
51
  }: {
30
52
  hash: TransactionHash;
31
53
  status: TransactionStatus;
32
54
  interval?: number;
33
55
  retries?: number;
56
+ fullTransaction?: boolean;
34
57
  }): Promise<GenLayerTransaction> => {
35
58
  const transaction = await client.getTransaction({
36
59
  hash,
@@ -46,10 +69,14 @@ export const receiptActions = (client: GenLayerClient<GenLayerChain>, publicClie
46
69
  transactionStatusString === requestedStatus ||
47
70
  (status === TransactionStatus.ACCEPTED && transactionStatusString === transactionStatusFinalized)
48
71
  ) {
72
+ let finalTransaction = transaction;
49
73
  if (client.chain.id === localnet.id) {
50
- return _decodeLocalnetTransaction(transaction as unknown as GenLayerTransaction);
74
+ finalTransaction = _decodeLocalnetTransaction(transaction as unknown as GenLayerTransaction);
75
+ }
76
+ if (!fullTransaction) {
77
+ return _simplifyTransactionReceipt(finalTransaction as GenLayerTransaction);
51
78
  }
52
- return transaction;
79
+ return finalTransaction;
53
80
  }
54
81
 
55
82
  if (retries === 0) {
@@ -62,6 +89,7 @@ export const receiptActions = (client: GenLayerClient<GenLayerChain>, publicClie
62
89
  status,
63
90
  interval,
64
91
  retries: retries - 1,
92
+ fullTransaction,
65
93
  });
66
94
  },
67
95
  });
@@ -183,6 +211,117 @@ const _decodeTransaction = (tx: GenLayerRawTransaction): GenLayerTransaction =>
183
211
  return decodedTx as GenLayerTransaction;
184
212
  };
185
213
 
214
+ const _simplifyTransactionReceipt = (tx: GenLayerTransaction): GenLayerTransaction => {
215
+ /**
216
+ * Simplify transaction receipt by removing non-essential fields while preserving functionality.
217
+ *
218
+ * Removes: Binary data, internal timestamps, appeal fields, processing details, historical data
219
+ * Preserves: Transaction IDs, status, execution results, node configs, readable data
220
+ */
221
+ const simplifyObject = (obj: any, path = ""): any => {
222
+ if (obj === null || obj === undefined) return obj;
223
+
224
+ if (Array.isArray(obj)) {
225
+ return obj.map(item => simplifyObject(item, path)).filter(item => item !== undefined);
226
+ }
227
+
228
+ if (typeof obj === "object") {
229
+ const result: any = {};
230
+
231
+ for (const [key, value] of Object.entries(obj)) {
232
+ const currentPath = path ? `${path}.${key}` : key;
233
+
234
+ // Always remove these fields
235
+ if (FIELDS_TO_REMOVE.includes(key)) {
236
+ continue;
237
+ }
238
+
239
+ // Remove node_config only from top level (keep it in consensus_data)
240
+ if (key === "node_config" && !path.includes("consensus_data")) {
241
+ continue;
242
+ }
243
+
244
+ // Special handling for consensus_data - keep execution results and votes
245
+ if (key === "consensus_data" && typeof value === "object" && value !== null) {
246
+ const simplifiedConsensus: any = {};
247
+
248
+ // Keep votes
249
+ if ("votes" in value) {
250
+ simplifiedConsensus.votes = value.votes;
251
+ }
252
+
253
+ // Process leader_receipt to keep only essential fields
254
+ if ("leader_receipt" in value && Array.isArray(value.leader_receipt)) {
255
+ simplifiedConsensus.leader_receipt = value.leader_receipt.map((receipt: any) => {
256
+ const simplifiedReceipt: any = {};
257
+
258
+ // Keep essential execution info
259
+ ["execution_result", "genvm_result", "mode", "vote", "node_config"].forEach(field => {
260
+ if (field in receipt) {
261
+ simplifiedReceipt[field] = receipt[field];
262
+ }
263
+ });
264
+
265
+ // Keep readable calldata
266
+ if (receipt.calldata && typeof receipt.calldata === "object" && "readable" in receipt.calldata) {
267
+ simplifiedReceipt.calldata = { readable: receipt.calldata.readable };
268
+ }
269
+
270
+ // Keep readable outputs
271
+ if (receipt.eq_outputs) {
272
+ simplifiedReceipt.eq_outputs = simplifyObject(receipt.eq_outputs, currentPath);
273
+ }
274
+ if (receipt.result) {
275
+ simplifiedReceipt.result = simplifyObject(receipt.result, currentPath);
276
+ }
277
+
278
+ return simplifiedReceipt;
279
+ });
280
+ }
281
+
282
+ // Process validators to keep execution results
283
+ if ("validators" in value && Array.isArray(value.validators)) {
284
+ const simplifiedValidators = value.validators.map((validator: any) => {
285
+ const simplifiedValidator: any = {};
286
+ ["execution_result", "genvm_result", "mode", "vote", "node_config"].forEach(field => {
287
+ if (field in validator) {
288
+ simplifiedValidator[field] = validator[field];
289
+ }
290
+ });
291
+ return simplifiedValidator;
292
+ }).filter((validator: any) => Object.keys(validator).length > 0);
293
+
294
+ if (simplifiedValidators.length > 0) {
295
+ simplifiedConsensus.validators = simplifiedValidators;
296
+ }
297
+ }
298
+
299
+ result[key] = simplifiedConsensus;
300
+ continue;
301
+ }
302
+
303
+ const simplifiedValue = simplifyObject(value, currentPath);
304
+ // Include the value if it's not undefined and not an empty object
305
+ // Special case: include numeric 0 values (like value: 0)
306
+ const shouldInclude = simplifiedValue !== undefined &&
307
+ !(typeof simplifiedValue === "object" && simplifiedValue !== null && Object.keys(simplifiedValue).length === 0);
308
+
309
+ if (shouldInclude || simplifiedValue === 0) {
310
+ // Map field names for cross-language compatibility
311
+ const mappedKey = FIELD_NAME_MAPPINGS[key] || key;
312
+ result[mappedKey] = simplifiedValue;
313
+ }
314
+ }
315
+
316
+ return result;
317
+ }
318
+
319
+ return obj;
320
+ };
321
+
322
+ return simplifyObject({...tx});
323
+ };
324
+
186
325
  const _decodeLocalnetTransaction = (tx: GenLayerTransaction): GenLayerTransaction => {
187
326
  if (!tx.data) return tx;
188
327
  try {
@@ -200,11 +339,20 @@ const _decodeLocalnetTransaction = (tx: GenLayerTransaction): GenLayerTransactio
200
339
  };
201
340
  }
202
341
  if (receipt.eq_outputs) {
203
- receipt.eq_outputs = Object.fromEntries(
204
- Object.entries(receipt.eq_outputs).map(([key, value]) => {
205
- return [key, resultToUserFriendlyJson(String(value))];
206
- }),
207
- );
342
+ const decodedOutputs: any = {};
343
+ for (const [key, value] of Object.entries(receipt.eq_outputs)) {
344
+ if (typeof value === "object" && value !== null) {
345
+ decodedOutputs[key] = value;
346
+ } else {
347
+ try {
348
+ decodedOutputs[key] = resultToUserFriendlyJson(value as string);
349
+ } catch (e) {
350
+ console.warn(`Error decoding eq_output ${key}: ${e}`);
351
+ decodedOutputs[key] = value;
352
+ }
353
+ }
354
+ }
355
+ receipt.eq_outputs = decodedOutputs;
208
356
  }
209
357
  });
210
358
  }
@@ -164,7 +164,7 @@ describe("Client Overrides", () => {
164
164
  to: contractAddress,
165
165
  from: accountAddressString, // Expecting the address string directly
166
166
  data: expect.any(String),
167
- transaction_hash_variant: TransactionHashVariant.LATEST_FINAL,
167
+ transaction_hash_variant: TransactionHashVariant.LATEST_NONFINAL,
168
168
  },
169
169
  ]);
170
170
  });