Skip to content

Commit b7a7491

Browse files
authored
feat: add fail reasons (#16)
1 parent 281384a commit b7a7491

File tree

4 files changed

+155
-6
lines changed

4 files changed

+155
-6
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## Unreleased
9+
10+
### Added
11+
12+
- Added fail reason (`failReason`) to failed transaction
13+
814
## [0.5.0] - 2025-01-17
915

1016
### Added

src/errors.json

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
{
2+
"2": {
3+
"description": "Stack underflow"
4+
},
5+
"3": {
6+
"description": "Stack overflow"
7+
},
8+
"4": {
9+
"description": "Integer overflow"
10+
},
11+
"5": {
12+
"description": "Integer out of expected range"
13+
},
14+
"6": {
15+
"description": "Invalid opcode"
16+
},
17+
"7": {
18+
"description": "Type check error"
19+
},
20+
"8": {
21+
"description": "Cell overflow"
22+
},
23+
"9": {
24+
"description": "Cell underflow"
25+
},
26+
"10": {
27+
"description": "Dictionary error"
28+
},
29+
"11": {
30+
"description": "'Unknown' error"
31+
},
32+
"12": {
33+
"description": "Fatal error"
34+
},
35+
"13": {
36+
"description": "Out of gas error"
37+
},
38+
"14": {
39+
"description": "Virtualization error"
40+
},
41+
"32": {
42+
"description": "Action list is invalid"
43+
},
44+
"33": {
45+
"description": "Action list is too long"
46+
},
47+
"34": {
48+
"description": "Action is invalid or not supported"
49+
},
50+
"35": {
51+
"description": "Invalid source address in outbound description"
52+
},
53+
"36": {
54+
"description": "Invalid destination address in outbound message"
55+
},
56+
"37": {
57+
"description": "Not enough Toncoin"
58+
},
59+
"38": {
60+
"description": "Not enough extra currencies"
61+
},
62+
"39": {
63+
"description": "Outbound message does not fit into a cell after rewriting"
64+
},
65+
"40": {
66+
"description": "Cannot process a message"
67+
},
68+
"41": {
69+
"description": "Library reference is null"
70+
},
71+
"42": {
72+
"description": "Library change action error"
73+
},
74+
"43": {
75+
"description": "Exceeded maximum number of cells in the library or the maximum depth of the Merkle tree"
76+
},
77+
"50": {
78+
"description": "Account state size exceeded limits"
79+
},
80+
81+
"128": {
82+
"description": "Null reference exception"
83+
},
84+
"129": {
85+
"description": "Invalid serialization prefix"
86+
},
87+
"130": {
88+
"description": "Invalid incoming message"
89+
},
90+
"131": {
91+
"description": "Constraints error"
92+
},
93+
"132": {
94+
"description": "Access denied"
95+
},
96+
"133": {
97+
"description": "Contract stopped"
98+
},
99+
"134": {
100+
"description": "Invalid argument"
101+
},
102+
"135": {
103+
"description": "Code of a contract was not found"
104+
},
105+
"136": {
106+
"description": "Invalid standard address"
107+
},
108+
"138": {
109+
"description": "Not a basechain address"
110+
},
111+
112+
"65535": {
113+
"description": "Unrecognized message opcode"
114+
}
115+
}

src/test/transaction.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { AccountStatus, Address, Cell, CurrencyCollection, Transaction } from "@ton/core";
22
import { inspect } from "node-inspect-extracted";
33
import { CompareResult } from "./interface";
4+
import errors from "../errors.json";
5+
6+
export type FailReason = {
7+
description: string;
8+
}
49

510
export type FlatTransaction = {
611
from?: Address
@@ -28,6 +33,16 @@ export type FlatTransaction = {
2833
success?: boolean
2934
}
3035

36+
const typedErrors: Partial<Record<string, FailReason>> = errors;
37+
38+
export function extractFailReason(tx: FlatTransaction): FailReason | undefined {
39+
if (tx.success) {
40+
return;
41+
}
42+
43+
return typedErrors[String(tx.exitCode)] ?? typedErrors[String(tx.actionResultCode)];
44+
}
45+
3146
type WithFunctions<T> = {
3247
[K in keyof T]: T[K] | ((x: T[K]) => boolean)
3348
}
@@ -52,6 +67,19 @@ function extractEc(cc: CurrencyCollection): [number, bigint][] {
5267
return r;
5368
}
5469

70+
export type PrettyTransaction = FlatTransaction & {
71+
failReason?: FailReason;
72+
}
73+
74+
export function prettifyTransaction(tx: Transaction): PrettyTransaction {
75+
const flatTx = flattenTransaction(tx);
76+
const failReason = extractFailReason(flatTx);
77+
return {
78+
...flatTx,
79+
failReason,
80+
}
81+
}
82+
5583
export function flattenTransaction(tx: Transaction): FlatTransaction {
5684
return {
5785
lt: tx.lt,
@@ -152,16 +180,15 @@ export function compareTransactionForTest(subject: any, cmp: FlatTransactionComp
152180
if (Array.isArray(subject)) {
153181
return {
154182
pass: subject.some(tx => compareTransaction(flattenTransaction(tx), cmp)),
155-
posMessage: ((subj: any[], cmp: FlatTransactionComparable) => `Expected ${inspect(subj.map(tx => flattenTransaction(tx)))} to contain a transaction that matches pattern ${inspect(cmp)}`).bind(undefined, subject, cmp),
156-
negMessage: ((subj: any[], cmp: FlatTransactionComparable) => `Expected ${inspect(subj.map(tx => flattenTransaction(tx)))} NOT to contain a transaction that matches pattern ${inspect(cmp)}, but it does`).bind(undefined, subject, cmp),
183+
posMessage: ((subj: any[], cmp: FlatTransactionComparable) => `Expected ${inspect(subj.map(tx => prettifyTransaction(tx)))} to contain a transaction that matches pattern ${inspect(cmp)}`).bind(undefined, subject, cmp),
184+
negMessage: ((subj: any[], cmp: FlatTransactionComparable) => `Expected ${inspect(subj.map(tx => prettifyTransaction(tx)))} NOT to contain a transaction that matches pattern ${inspect(cmp)}, but it does`).bind(undefined, subject, cmp),
157185
}
158186
} else {
159187
try {
160-
const flat = flattenTransaction(subject)
161188
return {
162-
pass: compareTransaction(flat, cmp),
163-
posMessage: ((flat: any, cmp: FlatTransactionComparable) => `Expected ${inspect(flat)} to match pattern ${inspect(cmp)}`).bind(undefined, flat, cmp),
164-
negMessage: ((flat: any, cmp: FlatTransactionComparable) => `Expected ${inspect(flat)} NOT to match pattern ${inspect(cmp)}, but it does`).bind(undefined, flat, cmp),
189+
pass: compareTransaction(flattenTransaction(subject), cmp),
190+
posMessage: ((subj: any, cmp: FlatTransactionComparable) => `Expected ${inspect(prettifyTransaction(subj))} to match pattern ${inspect(cmp)}`).bind(undefined, subject, cmp),
191+
negMessage: ((subj: any, cmp: FlatTransactionComparable) => `Expected ${inspect(prettifyTransaction(subj))} NOT to match pattern ${inspect(cmp)}, but it does`).bind(undefined, subject, cmp),
165192
}
166193
} catch (e) {
167194
if (subject.transactions !== undefined) {

tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"forceConsistentCasingInFileNames": true,
99
"strict": true,
1010
"skipLibCheck": true,
11+
"resolveJsonModule": true
1112
},
1213
"include": [
1314
"src/**/*"

0 commit comments

Comments
 (0)