Skip to content

Commit e6c3781

Browse files
committed
flags: frontend getTransactionFlags now mirrors backend (async + parser call)
The backend's Common.getTransactionFlags was refactored a while back to the clean functional contract: compute upstream flags, then `flags = await DigitalArtifactAnalyserService.analyseTransaction(tx, flags)`, return merged. The frontend equivalent in transaction.utils.ts was left behind on the old read-only side-channel pattern -- it only inspected tx._ordpoolFlags, never invoked the parser. On the tx-detail page the side-channel is never populated (the tx is loaded straight from electrs via /api/tx/<id>, bypassing any backend pre-enrichment), so every ordpool artifact bit was silently dropped. The Counterparty mpma tx 4a412b0a...4788e was the canonical regression case -- viewer fixed in 22d060a, chip rendering fixed here. Changes: - transaction.utils.ts::getTransactionFlags is now async; ends with the same `await analyseTransaction(tx, flags)` call as the backend; drops the tx._ordpoolFlags read entirely. - Three call sites (transaction.component.ts:setFeatures, transaction-raw.component.ts:processTransaction, tracker.component.ts:checkAccelerationEligibility) made async and awaited. - jest.config.js: added @noble/* to transformIgnorePatterns exceptions so @noble/secp256k1 (pure ESM) can be imported through the parser by any test that touches transaction.utils. - New regression test transaction.utils.spec.ts with 4 cases pinned to the real Counterparty fixture. The fourth case explicitly clears tx._ordpoolFlags before calling, so the old broken code would fail. - Updated stale docs: backend/.claude/CLAUDE.md and parser-side CLAUDE.md both described the old _ordpoolFlags-only pattern. Now describe the functional return-value pattern with the OTS pre- enrichment as the only remaining legitimate side-channel use.
1 parent 28347f5 commit e6c3781

8 files changed

Lines changed: 232 additions & 22 deletions

File tree

backend/.claude/CLAUDE.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -111,14 +111,18 @@ Key imports: `DigitalArtifactAnalyserService`, `InscriptionParserService`, `Insc
111111

112112
### HARD RULE: Ordpool Flags Must Be Applied Everywhere
113113

114-
Ordpool transaction flags (`ordpool_inscription`, `ordpool_rune`, `ordpool_cat21`, `ordpool_atomical`, `ordpool_src20`, `ordpool_labitbu`, plus the type flags `ordpool_counterparty`, `ordpool_stamp`, `ordpool_src721`, `ordpool_src101`, plus the sub-op flags) MUST be applied to every transaction, everywhere -- mempool, confirmed blocks, individual lookups, WebSocket, frontend. They must be computed together with the upstream flags in `getTransactionFlags`, not as a post-processing step.
114+
Ordpool transaction flags (`ordpool_inscription`, `ordpool_rune`, `ordpool_cat21`, `ordpool_atomical`, `ordpool_src20`, `ordpool_labitbu`, plus the type flags `ordpool_counterparty`, `ordpool_stamp`, `ordpool_src721`, `ordpool_src101`, `ordpool_ots`, plus the sub-op flags) MUST be applied to every transaction, everywhere -- mempool, confirmed blocks, individual lookups, WebSocket, frontend. They must be computed together with the upstream flags in `getTransactionFlags`, not as a post-processing step.
115115

116-
**To avoid cascading async changes to upstream code**, the parser sets `tx._ordpoolFlags` (Number) as a side effect from `analyseTransaction()` (async, per-tx) or `analyseTransactions()` (async, per-block, also computes `ordpoolStats`). Upstream's `Common.getTransactionFlags()` stays sync and reads `tx._ordpoolFlags` via a 3-line HACK in `src/api/common.ts`. This keeps all upstream function signatures untouched (no async/await changes in `Common.getTransactionFlags`, `classifyTransaction`, `classifyTransactions`, `summarizeBlockTransactions`, `processBlockTemplates`, `dataToMempoolBlocks`, etc.).
116+
**The pattern: functional return value, not side-channel mutation.** `Common.getTransactionFlags()` (`src/api/common.ts`) is `async` and at the very end of upstream flag computation calls `flags = await DigitalArtifactAnalyserService.analyseTransaction(tx, flags)` -- the parser takes the upstream-flag bigint in, ORs in the ordpool artifact bits, and returns the merged bigint. The frontend's `getTransactionFlags` (`src/app/shared/transaction.utils.ts`) follows the same shape: also async, also ends with the same call, also returns the merged bigint. **No `_ordpoolFlags` side-channel reads in either place.**
117117

118118
The flow:
119-
1. Mempool tx arrival → `await DigitalArtifactAnalyserService.analyseTransaction(tx, 0n)` in `mempool.ts` sets `tx._ordpoolFlags`.
120-
2. Block extension → `await DigitalArtifactAnalyserService.analyseTransactions(txs)` in `$getBlockExtended` sets `tx._ordpoolFlags` on every tx AND returns `ordpoolStats` for the block.
121-
3. Sync classification anywhere → `Common.getTransactionFlags(tx)` reads `tx._ordpoolFlags` and ORs it into the returned Number.
119+
1. Mempool tx arrival → `await Common.getTransactionFlags(tx)` (now async) computes upstream + ordpool flags in one call and returns the merged number.
120+
2. Block extension → `await DigitalArtifactAnalyserService.analyseTransactions(txs)` computes the per-block `ordpoolStats` AND, for backend-only consumers like the OTS pre-enrichment helper, sets `tx._ordpoolFlags` as a side effect on each tx. Subsequent `Common.getTransactionFlags(tx)` calls do NOT depend on this side-effect -- they re-derive the ordpool bits from the witness themselves and OR them into the upstream flags.
121+
3. Frontend mirror → `await getTransactionFlags(tx)` in `transaction.utils.ts` does the same: upstream static-flag computation + `await DigitalArtifactAnalyserService.analyseTransaction(tx, flags)` at the end.
122+
123+
The `tx._ordpoolFlags` field still exists. Its only remaining role is the **OTS pre-enrichment side-channel** (`src/api/ordpool-ots-flag.ts`): the indexer-derived `ordpool_ots` bit is not parser-derived (the parser cannot tell from a witness whether a hash matches a published OTS calendar commit), so it's injected via mutation just before `Common.getTransactionFlags` reads it at the end of `common.ts`. This is the ONE legitimate use of the side-channel; do not add new readers or writers.
124+
125+
A regression spec lives at `frontend/src/app/shared/transaction.utils.spec.ts` -- it asserts that a real Counterparty mpma tx (txid `4a412b0a...4788e`) gets `ordpool_counterparty` (bit 55) OR'd into the returned flags, including explicit verification that the side-channel is NOT being read. Keep the test green.
122126

123127
### Code Marking Convention (for merge-friendly changes)
124128

frontend/jest.config.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,13 @@ module.exports = {
1212
'^@environments/(.*)$': '<rootDir>/src/environments/$1',
1313
'^@interfaces/(.*)$': '<rootDir>/src/app/interfaces/$1',
1414
},
15+
// @noble/secp256k1 ships as pure ESM ("type":"module"). Jest skips
16+
// transforming node_modules by default, so its import lands at runtime
17+
// as raw ESM and Node throws SyntaxError on the export statement. The
18+
// pattern below extends jest-preset-angular's default (which keeps
19+
// *.mjs and @angular/common/locales transforming) with an exception
20+
// for @noble packages so their .js files get transformed too.
21+
transformIgnorePatterns: [
22+
'node_modules/(?!(.*\\.mjs$|@angular/common/locales/.*\\.js$|@noble/.*))',
23+
],
1524
};

frontend/src/app/components/tracker/tracker.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -785,10 +785,10 @@ export class TrackerComponent implements OnInit, OnDestroy {
785785
return this.isLoadingTx || this.loadingCachedTx || this.loadingPosition;
786786
}
787787

788-
checkAccelerationEligibility() {
788+
async checkAccelerationEligibility(): Promise<void> {
789789
if (this.tx) {
790790
const txHeight = this.tx.status?.block_height || (this.stateService.latestBlockHeight >= 0 ? this.stateService.latestBlockHeight + 1 : null);
791-
this.tx.flags = getTransactionFlags(this.tx, null, null, txHeight, this.stateService.network);
791+
this.tx.flags = await getTransactionFlags(this.tx, null, null, txHeight, this.stateService.network);
792792
const replaceableInputs = (this.tx.flags & (TransactionFlags.sighash_none | TransactionFlags.sighash_acp)) > 0n;
793793
const highSigop = (this.tx.sigops * 20) > this.tx.weight;
794794
this.eligibleForAcceleration = !replaceableInputs && !highSigop;

frontend/src/app/components/transaction/transaction-raw.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
262262
}
263263
}
264264

265-
processTransaction(tx: Transaction, hex: string, psbt: string): void {
265+
async processTransaction(tx: Transaction, hex: string, psbt: string): Promise<void> {
266266
this.transaction = tx;
267267
this.rawHexTransaction = hex;
268268
this.psbt = psbt;
@@ -276,7 +276,7 @@ export class TransactionRawComponent implements OnInit, OnDestroy {
276276
});
277277

278278
const txHeight = this.transaction.status?.block_height || (this.stateService.latestBlockHeight >= 0 ? this.stateService.latestBlockHeight + 1 : null);
279-
this.transaction.flags = getTransactionFlags(this.transaction, this.cpfpInfo, null, txHeight, this.stateService.network);
279+
this.transaction.flags = await getTransactionFlags(this.transaction, this.cpfpInfo, null, txHeight, this.stateService.network);
280280
this.filters = this.transaction.flags ? toFilters(this.transaction.flags).filter(f => f.txPage) : [];
281281

282282
this.setupGraph();

frontend/src/app/components/transaction/transaction.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,13 +1007,13 @@ export class TransactionComponent implements OnInit, AfterViewInit, OnDestroy {
10071007
this.isAccelerated$.next(this.isAcceleration);
10081008
}
10091009

1010-
setFeatures(): void {
1010+
async setFeatures(): Promise<void> {
10111011
if (this.tx) {
10121012
this.segwitEnabled = !this.tx.status.confirmed || isFeatureActive(this.stateService.network, this.tx.status.block_height, 'segwit');
10131013
this.taprootEnabled = !this.tx.status.confirmed || isFeatureActive(this.stateService.network, this.tx.status.block_height, 'taproot');
10141014
this.rbfEnabled = !this.tx.status.confirmed || isFeatureActive(this.stateService.network, this.tx.status.block_height, 'rbf');
10151015
const txHeight = this.tx.status?.block_height || (this.stateService.latestBlockHeight >= 0 ? this.stateService.latestBlockHeight + 1 : null);
1016-
this.tx.flags = getTransactionFlags(this.tx, null, null, txHeight, this.stateService.network);
1016+
this.tx.flags = await getTransactionFlags(this.tx, null, null, txHeight, this.stateService.network);
10171017
// HACK: always show all flags, because why not?
10181018
// this.filters = this.tx.flags ? toFilters(this.tx.flags).filter(f => f.txPage) : [];
10191019
this.filters = this.tx.flags ? toFilters(this.tx.flags) : [];
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/* eslint-disable */
2+
// Real-mainnet Counterparty tx (mpma -- multi-party multi-asset send).
3+
// Block 948,817, txid 4a412b0a71439ad5eaf5f8a91878f8cf7c895037bc6b59ba93fd3d954eb4788e.
4+
// Three 1-of-3 bare-multisig outputs encoding the encrypted CNTRPRTY message,
5+
// plus a p2pkh change output. The parser must classify this as a Counterparty
6+
// artifact and OR the ordpool_counterparty flag (bit 55) into the result.
7+
//
8+
// This fixture is the regression-trigger for "frontend getTransactionFlags
9+
// doesn't run the parser". The bug surfaced when the
10+
// digital-artifact-viewer was missing a Counterparty case AND the chip was
11+
// missing because the analyser was never invoked on the tx-detail page.
12+
// See https://ordpool.space/tx/4a412b0a...4788e for the live render.
13+
export const COUNTERPARTY_MPMA_TX: any = {
14+
txid: '4a412b0a71439ad5eaf5f8a91878f8cf7c895037bc6b59ba93fd3d954eb4788e',
15+
version: 1,
16+
locktime: 0,
17+
vin: [{
18+
txid: '8b9c8f1b6fcbdb103255314957f3744f9ff3ebc759fc3d4139beb278c35c9f1c',
19+
vout: 1,
20+
prevout: {
21+
scriptpubkey: '76a91483b6241a78354ae6ad241a9695e3888191347c3f88ac',
22+
scriptpubkey_asm: 'OP_DUP OP_HASH160 OP_PUSHBYTES_20 83b6241a78354ae6ad241a9695e3888191347c3f OP_EQUALVERIFY OP_CHECKSIG',
23+
scriptpubkey_type: 'p2pkh',
24+
scriptpubkey_address: '1D1RiWbmikELv1P2hfWKguWuQMZ1Siws5g',
25+
value: 20486,
26+
},
27+
scriptsig: '4830450221009f03b2512d65922dad70ea38b0ecf4b9795d86731c9593fbef094c49d9ab894902200e2eed1d4065c304d838b09ebd6d3f1979db1509f824f5b2a87d351a46737e3901210378b853908eb411fb14c8374a38bbfffd81643d63b12f284ad4a04c7c0f3db0fb',
28+
scriptsig_asm: 'OP_PUSHBYTES_72 30450221009f03b2512d65922dad70ea38b0ecf4b9795d86731c9593fbef094c49d9ab894902200e2eed1d4065c304d838b09ebd6d3f1979db1509f824f5b2a87d351a46737e3901 OP_PUSHBYTES_33 0378b853908eb411fb14c8374a38bbfffd81643d63b12f284ad4a04c7c0f3db0fb',
29+
is_coinbase: false,
30+
sequence: 4294967295,
31+
}],
32+
vout: [
33+
{
34+
scriptpubkey: '512102aeae1912b08f011a062af2a1b5246aa8cd22ed2c5c04ac5c5f61d2714f354eb52103ef95c3bca32b96ba26776fba1a184ef26c9709ddbea15e044a03e4abe4dbcae1210378b853908eb411fb14c8374a38bbfffd81643d63b12f284ad4a04c7c0f3db0fb53ae',
35+
scriptpubkey_asm: 'OP_PUSHNUM_1 OP_PUSHBYTES_33 02aeae1912b08f011a062af2a1b5246aa8cd22ed2c5c04ac5c5f61d2714f354eb5 OP_PUSHBYTES_33 03ef95c3bca32b96ba26776fba1a184ef26c9709ddbea15e044a03e4abe4dbcae1 OP_PUSHBYTES_33 0378b853908eb411fb14c8374a38bbfffd81643d63b12f284ad4a04c7c0f3db0fb OP_PUSHNUM_3 OP_CHECKMULTISIG',
36+
scriptpubkey_type: 'multisig',
37+
value: 1000,
38+
},
39+
{
40+
scriptpubkey: '512102aeae1912b08f011a0629f2a0a1784c4b6813a6585e1cbe12ef22d496a37c03d42103cf27e2ddbc089a80f2abefba1a190ef26c9d360946e63f3861e094abe4db9a4d210378b853908eb411fb14c8374a38bbfffd81643d63b12f284ad4a04c7c0f3db0fb53ae',
41+
scriptpubkey_asm: 'OP_PUSHNUM_1 OP_PUSHBYTES_33 02aeae1912b08f011a0629f2a0a1784c4b6813a6585e1cbe12ef22d496a37c03d4 OP_PUSHBYTES_33 03cf27e2ddbc089a80f2abefba1a190ef26c9d360946e63f3861e094abe4db9a4d OP_PUSHBYTES_33 0378b853908eb411fb14c8374a38bbfffd81643d63b12f284ad4a04c7c0f3db0fb OP_PUSHNUM_3 OP_CHECKMULTISIG',
42+
scriptpubkey_type: 'multisig',
43+
value: 1000,
44+
},
45+
{
46+
scriptpubkey: '5121028bae1912b08f011a0629f2e368ab7f1e8ca10a768a1cbe12ff22d496a67c03c72103cf3b83bba8789a80f2abefba1a184ef26c9759ddc766bf3861e094abe4dbca62210378b853908eb411fb14c8374a38bbfffd81643d63b12f284ad4a04c7c0f3db0fb53ae',
47+
scriptpubkey_asm: 'OP_PUSHNUM_1 OP_PUSHBYTES_33 028bae1912b08f011a0629f2e368ab7f1e8ca10a768a1cbe12ff22d496a67c03c7 OP_PUSHBYTES_33 03cf3b83bba8789a80f2abefba1a184ef26c9759ddc766bf3861e094abe4dbca62 OP_PUSHBYTES_33 0378b853908eb411fb14c8374a38bbfffd81643d63b12f284ad4a04c7c0f3db0fb OP_PUSHNUM_3 OP_CHECKMULTISIG',
48+
scriptpubkey_type: 'multisig',
49+
value: 1000,
50+
},
51+
{
52+
scriptpubkey: '76a91483b6241a78354ae6ad241a9695e3888191347c3f88ac',
53+
scriptpubkey_asm: 'OP_DUP OP_HASH160 OP_PUSHBYTES_20 83b6241a78354ae6ad241a9695e3888191347c3f OP_EQUALVERIFY OP_CHECKSIG',
54+
scriptpubkey_type: 'p2pkh',
55+
scriptpubkey_address: '1D1RiWbmikELv1P2hfWKguWuQMZ1Siws5g',
56+
value: 16286,
57+
},
58+
],
59+
size: 534,
60+
weight: 2136,
61+
sigops: 244,
62+
fee: 1200,
63+
status: {
64+
confirmed: true,
65+
block_height: 948817,
66+
block_hash: '000000000000000000015161015df87a22f9240e9ab392b2e2dce9b0acb05556',
67+
block_time: 1778438338,
68+
},
69+
};
70+
71+
// Minimal plain p2pkh tx with no ordpool data. Used as the negative case --
72+
// after getTransactionFlags returns, none of the ordpool_* bits (range 48-81)
73+
// should be set.
74+
export const PLAIN_P2PKH_TX: any = {
75+
txid: '0000000000000000000000000000000000000000000000000000000000000001',
76+
version: 2,
77+
locktime: 0,
78+
vin: [{
79+
txid: '0000000000000000000000000000000000000000000000000000000000000000',
80+
vout: 0,
81+
prevout: {
82+
scriptpubkey: '76a91400112233445566778899aabbccddeeff0011223388ac',
83+
scriptpubkey_asm: 'OP_DUP OP_HASH160 OP_PUSHBYTES_20 00112233445566778899aabbccddeeff00112233 OP_EQUALVERIFY OP_CHECKSIG',
84+
scriptpubkey_type: 'p2pkh',
85+
scriptpubkey_address: '1111111111111111111114oLvT2',
86+
value: 100000,
87+
},
88+
scriptsig: '',
89+
scriptsig_asm: '',
90+
is_coinbase: false,
91+
sequence: 4294967295,
92+
}],
93+
vout: [{
94+
scriptpubkey: '76a91400112233445566778899aabbccddeeff0011223388ac',
95+
scriptpubkey_asm: 'OP_DUP OP_HASH160 OP_PUSHBYTES_20 00112233445566778899aabbccddeeff00112233 OP_EQUALVERIFY OP_CHECKSIG',
96+
scriptpubkey_type: 'p2pkh',
97+
scriptpubkey_address: '1111111111111111111114oLvT2',
98+
value: 99000,
99+
}],
100+
size: 200,
101+
weight: 800,
102+
sigops: 4,
103+
fee: 1000,
104+
status: { confirmed: true, block_height: 948000, block_hash: 'aa'.repeat(32), block_time: 1778000000 },
105+
};
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { OrdpoolTransactionFlags } from 'ordpool-parser';
2+
3+
import { getTransactionFlags } from './transaction.utils';
4+
import { COUNTERPARTY_MPMA_TX, PLAIN_P2PKH_TX } from './transaction.utils.fixtures';
5+
6+
/**
7+
* Regression test for the "frontend tx-detail page never invoked the
8+
* ordpool parser" bug. The contract: getTransactionFlags MUST OR ordpool
9+
* artifact bits into the returned flags when given a tx that the parser
10+
* recognises -- mirroring the backend's common.ts::Common.getTransactionFlags
11+
* which calls `analyseTransaction(tx, flags)` at the end of its computation.
12+
*
13+
* Background (so this test isn't deleted later "as redundant"):
14+
*
15+
* The first implementation of frontend/backend integration used a side-channel
16+
* pattern: the parser mutated tx._ordpoolFlags as a side effect, and
17+
* getTransactionFlags read that field back. The architecture was later
18+
* refactored to a clean functional contract -- analyseTransaction(tx, flags)
19+
* takes input flags, returns merged flags, no side-channel needed. The
20+
* backend was updated. The frontend's getTransactionFlags was NOT updated
21+
* for ~a month, so any tx that arrived without server-pre-classified flags
22+
* (i.e. anything fetched via the tx-detail Esplora proxy at /api/tx/<id>)
23+
* had its ordpool artifact bits silently dropped. The Counterparty mpma
24+
* tx 4a412b0a...4788e is the canonical regression case.
25+
*/
26+
// jsdom test environment doesn't expose structuredClone; this works fine
27+
// for plain-data fixtures (no Date / Map / Set / Uint8Array inside).
28+
function deepClone<T>(v: T): T {
29+
return JSON.parse(JSON.stringify(v));
30+
}
31+
32+
describe('getTransactionFlags (ordpool integration)', () => {
33+
34+
it('ORs ordpool_counterparty into flags for a real Counterparty mpma tx', async () => {
35+
// Clone the fixture so we don't accidentally mutate it across test
36+
// re-runs (the parser internally sets tx._ordpoolFlags as a side effect
37+
// for the OTS pre-enrichment path, and we want each test to start fresh).
38+
const tx = deepClone(COUNTERPARTY_MPMA_TX);
39+
40+
const flags = await getTransactionFlags(tx, null, null, tx.status.block_height, 'mainnet');
41+
42+
expect(flags & OrdpoolTransactionFlags.ordpool_counterparty).toBe(OrdpoolTransactionFlags.ordpool_counterparty);
43+
});
44+
45+
it('returns no ordpool bits for a plain p2pkh tx', async () => {
46+
const tx = deepClone(PLAIN_P2PKH_TX);
47+
48+
const flags = await getTransactionFlags(tx, null, null, tx.status.block_height, 'mainnet');
49+
50+
// Mask: every ordpool bit lives at position 48 or above. A plain tx must
51+
// not light any of them up.
52+
const ORDPOOL_BIT_MASK = ((1n << 32n) - 1n) << 48n;
53+
expect(flags & ORDPOOL_BIT_MASK).toBe(0n);
54+
});
55+
56+
it('returns the same value across two calls (no parser-mutation leaking across calls)', async () => {
57+
// The parser DOES still mutate tx._ordpoolFlags internally (used by the
58+
// OTS pre-enrichment path). Calling getTransactionFlags twice on the
59+
// SAME tx object must still produce the same flags both times -- not
60+
// double-OR something, not pick up stale leftovers from the previous run.
61+
const tx = deepClone(COUNTERPARTY_MPMA_TX);
62+
63+
const first = await getTransactionFlags(tx, null, null, tx.status.block_height, 'mainnet');
64+
const second = await getTransactionFlags(tx, null, null, tx.status.block_height, 'mainnet');
65+
66+
expect(second).toBe(first);
67+
});
68+
69+
it('does not read or rely on tx._ordpoolFlags side-channel', async () => {
70+
// The OLD bug was: getTransactionFlags only inspected tx._ordpoolFlags,
71+
// which was populated by the backend's side-effect pattern. On the
72+
// tx-detail page the field was never set, so the chip never fired.
73+
//
74+
// Guard: explicitly clear _ordpoolFlags BEFORE calling the function.
75+
// If the function only reads the side-channel (the old broken code),
76+
// this test would return 0 for the counterparty bit. The new code calls
77+
// the parser directly and computes the flag from the witness.
78+
const tx = deepClone(COUNTERPARTY_MPMA_TX);
79+
(tx as any)._ordpoolFlags = undefined;
80+
81+
const flags = await getTransactionFlags(tx, null, null, tx.status.block_height, 'mainnet');
82+
83+
expect(flags & OrdpoolTransactionFlags.ordpool_counterparty).toBe(OrdpoolTransactionFlags.ordpool_counterparty);
84+
});
85+
});

0 commit comments

Comments
 (0)