genlayer-js 0.13.0 → 0.14.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 +7 -0
- package/dist/{index-TWCEN45Z.d.ts → index-CgHl4W-5.d.ts} +1 -1
- package/dist/{index-icLJcrDm.d.cts → index-IViMPpkl.d.cts} +1 -1
- package/dist/index.cjs +105 -98
- package/dist/index.d.cts +13 -4
- package/dist/index.d.ts +13 -4
- package/dist/index.js +103 -96
- package/dist/types/index.d.cts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/abi/calldata/encoder.ts +36 -0
- package/src/contracts/actions.ts +3 -40
- package/src/index.ts +6 -0
- package/src/transactions/actions.ts +6 -280
- package/src/transactions/decoders.ts +276 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import {GenLayerTransaction, GenLayerRawTransaction, DecodedCallData, DecodedDeployData} from "../types/transactions";
|
|
2
|
+
import {transactionsStatusNumberToName, transactionResultNumberToName, voteTypeNumberToName, VoteType} from "../types/transactions";
|
|
3
|
+
import {b64ToArray, calldataToUserFriendlyJson, resultToUserFriendlyJson} from "../utils/jsonifier";
|
|
4
|
+
import {fromRlp, fromHex, Hex, Address} from "viem";
|
|
5
|
+
import * as calldataAbi from "../abi/calldata";
|
|
6
|
+
|
|
7
|
+
// Fields to remove from simplified transaction receipts
|
|
8
|
+
const FIELDS_TO_REMOVE = [
|
|
9
|
+
"raw", "contract_state", "base64", "consensus_history", "tx_data",
|
|
10
|
+
"eq_blocks_outputs", "r", "s", "v", "created_timestamp",
|
|
11
|
+
"current_timestamp", "tx_execution_hash", "random_seed", "states",
|
|
12
|
+
"contract_code", "appeal_failed", "appeal_leader_timeout",
|
|
13
|
+
"appeal_processing_time", "appeal_undetermined", "appealed",
|
|
14
|
+
"timestamp_appeal", "config_rotation_rounds", "rotation_count",
|
|
15
|
+
"queue_position", "queue_type", "leader_timeout_validators",
|
|
16
|
+
"triggered_by", "num_of_initial_validators",
|
|
17
|
+
"timestamp_awaiting_finalization", "last_vote_timestamp",
|
|
18
|
+
"read_state_block_range", "tx_slot", "blockHash", "blockNumber",
|
|
19
|
+
"to", "transactionIndex"
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
// Field name mappings for cross-language compatibility with genlayer-py
|
|
23
|
+
const FIELD_NAME_MAPPINGS: Record<string, string> = {
|
|
24
|
+
statusName: "status_name",
|
|
25
|
+
typeHex: "type"
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const decodeInputData = (
|
|
29
|
+
rlpEncodedAppData: Hex | undefined | null,
|
|
30
|
+
recipient: Address,
|
|
31
|
+
): DecodedDeployData | DecodedCallData | null => {
|
|
32
|
+
if (!rlpEncodedAppData || rlpEncodedAppData === "0x" || rlpEncodedAppData.length <= 2) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const rlpDecodedArray = fromRlp(rlpEncodedAppData) as Hex[];
|
|
37
|
+
|
|
38
|
+
if (rlpDecodedArray.length === 3) {
|
|
39
|
+
return {
|
|
40
|
+
code: fromHex(rlpDecodedArray[0], "string") as `0x${string}`,
|
|
41
|
+
constructorArgs:
|
|
42
|
+
rlpDecodedArray[1] && rlpDecodedArray[1] !== "0x"
|
|
43
|
+
? calldataAbi.decode(fromHex(rlpDecodedArray[1], "bytes"))
|
|
44
|
+
: null,
|
|
45
|
+
leaderOnly: rlpDecodedArray[2] === "0x01",
|
|
46
|
+
type: "deploy",
|
|
47
|
+
contractAddress: recipient,
|
|
48
|
+
};
|
|
49
|
+
} else if (rlpDecodedArray.length === 2) {
|
|
50
|
+
return {
|
|
51
|
+
callData:
|
|
52
|
+
rlpDecodedArray[0] && rlpDecodedArray[0] !== "0x"
|
|
53
|
+
? calldataAbi.decode(fromHex(rlpDecodedArray[0], "bytes"))
|
|
54
|
+
: null,
|
|
55
|
+
leaderOnly: rlpDecodedArray[1] === "0x01",
|
|
56
|
+
type: "call",
|
|
57
|
+
};
|
|
58
|
+
} else {
|
|
59
|
+
console.warn(
|
|
60
|
+
"[decodeInputData] WRITE: Unexpected RLP array length:",
|
|
61
|
+
rlpDecodedArray.length,
|
|
62
|
+
rlpDecodedArray,
|
|
63
|
+
);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
} catch (e) {
|
|
67
|
+
console.error(
|
|
68
|
+
"[decodeInputData] Error during comprehensive decoding:",
|
|
69
|
+
e,
|
|
70
|
+
"Raw RLP App Data:",
|
|
71
|
+
rlpEncodedAppData,
|
|
72
|
+
);
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const decodeTransaction = (tx: GenLayerRawTransaction): GenLayerTransaction => {
|
|
78
|
+
const txDataDecoded = decodeInputData(tx.txData, tx.recipient);
|
|
79
|
+
|
|
80
|
+
const decodedTx = {
|
|
81
|
+
...tx,
|
|
82
|
+
txData: tx.txData,
|
|
83
|
+
txDataDecoded: txDataDecoded,
|
|
84
|
+
|
|
85
|
+
currentTimestamp: tx.currentTimestamp.toString(),
|
|
86
|
+
numOfInitialValidators: tx.numOfInitialValidators.toString(),
|
|
87
|
+
txSlot: tx.txSlot.toString(),
|
|
88
|
+
createdTimestamp: tx.createdTimestamp.toString(),
|
|
89
|
+
lastVoteTimestamp: tx.lastVoteTimestamp.toString(),
|
|
90
|
+
queuePosition: tx.queuePosition.toString(),
|
|
91
|
+
numOfRounds: tx.numOfRounds.toString(),
|
|
92
|
+
|
|
93
|
+
readStateBlockRange: {
|
|
94
|
+
...tx.readStateBlockRange,
|
|
95
|
+
activationBlock: tx.readStateBlockRange.activationBlock.toString(),
|
|
96
|
+
processingBlock: tx.readStateBlockRange.processingBlock.toString(),
|
|
97
|
+
proposalBlock: tx.readStateBlockRange.proposalBlock.toString(),
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
statusName:
|
|
101
|
+
transactionsStatusNumberToName[String(tx.status) as keyof typeof transactionsStatusNumberToName],
|
|
102
|
+
resultName:
|
|
103
|
+
transactionResultNumberToName[String(tx.result) as keyof typeof transactionResultNumberToName],
|
|
104
|
+
|
|
105
|
+
lastRound: {
|
|
106
|
+
...tx.lastRound,
|
|
107
|
+
round: tx.lastRound.round.toString(),
|
|
108
|
+
leaderIndex: tx.lastRound.leaderIndex.toString(),
|
|
109
|
+
votesCommitted: tx.lastRound.votesCommitted.toString(),
|
|
110
|
+
votesRevealed: tx.lastRound.votesRevealed.toString(),
|
|
111
|
+
appealBond: tx.lastRound.appealBond.toString(),
|
|
112
|
+
rotationsLeft: tx.lastRound.rotationsLeft.toString(),
|
|
113
|
+
validatorVotesName: tx.lastRound.validatorVotes.map(
|
|
114
|
+
vote => voteTypeNumberToName[String(vote) as keyof typeof voteTypeNumberToName],
|
|
115
|
+
) as VoteType[],
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
return decodedTx as GenLayerTransaction;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export const simplifyTransactionReceipt = (tx: GenLayerTransaction): GenLayerTransaction => {
|
|
122
|
+
/**
|
|
123
|
+
* Simplify transaction receipt by removing non-essential fields while preserving functionality.
|
|
124
|
+
*
|
|
125
|
+
* Removes: Binary data, internal timestamps, appeal fields, processing details, historical data
|
|
126
|
+
* Preserves: Transaction IDs, status, execution results, node configs, readable data
|
|
127
|
+
*/
|
|
128
|
+
const simplifyObject = (obj: any, path = ""): any => {
|
|
129
|
+
if (obj === null || obj === undefined) return obj;
|
|
130
|
+
|
|
131
|
+
if (Array.isArray(obj)) {
|
|
132
|
+
return obj.map(item => simplifyObject(item, path)).filter(item => item !== undefined);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (typeof obj === "object") {
|
|
136
|
+
const result: any = {};
|
|
137
|
+
|
|
138
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
139
|
+
const currentPath = path ? `${path}.${key}` : key;
|
|
140
|
+
|
|
141
|
+
// Always remove these fields
|
|
142
|
+
if (FIELDS_TO_REMOVE.includes(key)) {
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Remove node_config only from top level (keep it in consensus_data)
|
|
147
|
+
if (key === "node_config" && !path.includes("consensus_data")) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Special handling for consensus_data - keep execution results and votes
|
|
152
|
+
if (key === "consensus_data" && typeof value === "object" && value !== null) {
|
|
153
|
+
const simplifiedConsensus: any = {};
|
|
154
|
+
|
|
155
|
+
// Keep votes
|
|
156
|
+
if ("votes" in value) {
|
|
157
|
+
simplifiedConsensus.votes = value.votes;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Process leader_receipt to keep only essential fields
|
|
161
|
+
if ("leader_receipt" in value && Array.isArray(value.leader_receipt)) {
|
|
162
|
+
simplifiedConsensus.leader_receipt = value.leader_receipt.map((receipt: any) => {
|
|
163
|
+
const simplifiedReceipt: any = {};
|
|
164
|
+
|
|
165
|
+
// Keep essential execution info
|
|
166
|
+
["execution_result", "genvm_result", "mode", "vote", "node_config"].forEach(field => {
|
|
167
|
+
if (field in receipt) {
|
|
168
|
+
simplifiedReceipt[field] = receipt[field];
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Keep readable calldata
|
|
173
|
+
if (receipt.calldata && typeof receipt.calldata === "object" && "readable" in receipt.calldata) {
|
|
174
|
+
simplifiedReceipt.calldata = { readable: receipt.calldata.readable };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Keep readable outputs
|
|
178
|
+
if (receipt.eq_outputs) {
|
|
179
|
+
simplifiedReceipt.eq_outputs = simplifyObject(receipt.eq_outputs, currentPath);
|
|
180
|
+
}
|
|
181
|
+
if (receipt.result) {
|
|
182
|
+
simplifiedReceipt.result = simplifyObject(receipt.result, currentPath);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return simplifiedReceipt;
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Process validators to keep execution results
|
|
190
|
+
if ("validators" in value && Array.isArray(value.validators)) {
|
|
191
|
+
const simplifiedValidators = value.validators.map((validator: any) => {
|
|
192
|
+
const simplifiedValidator: any = {};
|
|
193
|
+
["execution_result", "genvm_result", "mode", "vote", "node_config"].forEach(field => {
|
|
194
|
+
if (field in validator) {
|
|
195
|
+
simplifiedValidator[field] = validator[field];
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
return simplifiedValidator;
|
|
199
|
+
}).filter((validator: any) => Object.keys(validator).length > 0);
|
|
200
|
+
|
|
201
|
+
if (simplifiedValidators.length > 0) {
|
|
202
|
+
simplifiedConsensus.validators = simplifiedValidators;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
result[key] = simplifiedConsensus;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const simplifiedValue = simplifyObject(value, currentPath);
|
|
211
|
+
// Include the value if it's not undefined and not an empty object
|
|
212
|
+
// Special case: include numeric 0 values (like value: 0)
|
|
213
|
+
const shouldInclude = simplifiedValue !== undefined &&
|
|
214
|
+
!(typeof simplifiedValue === "object" && simplifiedValue !== null && Object.keys(simplifiedValue).length === 0);
|
|
215
|
+
|
|
216
|
+
if (shouldInclude || simplifiedValue === 0) {
|
|
217
|
+
// Map field names for cross-language compatibility
|
|
218
|
+
const mappedKey = FIELD_NAME_MAPPINGS[key] || key;
|
|
219
|
+
result[mappedKey] = simplifiedValue;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return obj;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
return simplifyObject({...tx});
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
export const decodeLocalnetTransaction = (tx: GenLayerTransaction): GenLayerTransaction => {
|
|
233
|
+
if (!tx.data) return tx;
|
|
234
|
+
try {
|
|
235
|
+
const leaderReceipt = tx.consensus_data?.leader_receipt;
|
|
236
|
+
if (leaderReceipt) {
|
|
237
|
+
const receipts = Array.isArray(leaderReceipt) ? leaderReceipt : [leaderReceipt];
|
|
238
|
+
receipts.forEach((receipt) => {
|
|
239
|
+
if (receipt.result && typeof receipt.result === "string") {
|
|
240
|
+
receipt.result = resultToUserFriendlyJson(receipt.result);
|
|
241
|
+
}
|
|
242
|
+
if (receipt.calldata && typeof receipt.calldata === "string") {
|
|
243
|
+
receipt.calldata = {
|
|
244
|
+
base64: receipt.calldata as string,
|
|
245
|
+
...calldataToUserFriendlyJson(b64ToArray(receipt.calldata as string)),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
if (receipt.eq_outputs) {
|
|
249
|
+
const decodedOutputs: any = {};
|
|
250
|
+
for (const [key, value] of Object.entries(receipt.eq_outputs)) {
|
|
251
|
+
if (typeof value === "object" && value !== null) {
|
|
252
|
+
decodedOutputs[key] = value;
|
|
253
|
+
} else {
|
|
254
|
+
try {
|
|
255
|
+
decodedOutputs[key] = resultToUserFriendlyJson(value as string);
|
|
256
|
+
} catch (e) {
|
|
257
|
+
console.warn(`Error decoding eq_output ${key}: ${e}`);
|
|
258
|
+
decodedOutputs[key] = value;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
receipt.eq_outputs = decodedOutputs;
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
if (tx.data?.calldata && typeof tx.data.calldata === "string") {
|
|
267
|
+
tx.data.calldata = {
|
|
268
|
+
base64: tx.data.calldata as string,
|
|
269
|
+
...calldataToUserFriendlyJson(b64ToArray(tx.data.calldata as string)),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
} catch (e) {
|
|
273
|
+
console.error("Error in decodeLocalnetTransaction:", e);
|
|
274
|
+
}
|
|
275
|
+
return tx;
|
|
276
|
+
};
|