Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/core/counterparty/pack/__tests__/coreOracle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

import { describe, expect, it } from 'vitest';
import { asBaseUnits } from '@/core/numeric';
import { type MpmaTableFormat, mpmaTableFormatAt } from '../../mpmaTableFormat';
import { unpackCounterpartyMessage } from '../../unpack';
import { bytesToHex } from '../../unpack/binary';
import { packComposeMessage } from '../messages';
Expand Down Expand Up @@ -406,6 +407,16 @@ async function composeDataFromCore(testCase: OracleCase): Promise<string> {

describe.skipIf(!API_URL)('local packing matches counterparty-core', () => {
it.each(CASES)('$label', async (testCase) => {
// Like the wallet, establish the consensus table layout independently of compose bytes.
let mpmaTableFormat: MpmaTableFormat | undefined;
if (testCase.composeType === 'mpma') {
const response = await fetchOracle(`${API_URL}/v2/`);
if (!response.ok) throw new Error(`Core status returned ${response.status}`);
const { result } = await response.json() as { result: { network: string; counterparty_height: number } };
const format = mpmaTableFormatAt(result.network, result.counterparty_height + 1);
if (!format) throw new Error('Cannot establish the MPMA table format for the oracle');
mpmaTableFormat = format;
}
const fromCore = await composeDataFromCore(testCase);

let observed: Record<string, unknown> | undefined;
Expand All @@ -415,7 +426,7 @@ describe.skipIf(!API_URL)('local packing matches counterparty-core', () => {
observed = unpacked.data as Record<string, unknown>;
}

const packed = packComposeMessage(testCase.composeType, testCase.params, observed);
const packed = packComposeMessage(testCase.composeType, testCase.params, observed, { mpmaTableFormat });
expect(packed, 'this case should be packable locally').not.toBeNull();
expect(bytesToHex(packed!.bytes).toLowerCase()).toBe(fromCore);
}, 60_000);
Expand Down
22 changes: 21 additions & 1 deletion src/core/counterparty/pack/__tests__/onchainRoundTrip.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,11 @@ const PARAMS_FROM_DECODED: Record<string, (data: Record<string, any>) => Record<
// wallet, not an indexer, and reproducing every historical encoding is not its job. A struct
// send is therefore one it can read and deliberately cannot rebuild, which counts as declined
// rather than as a defect.
// The text-only composer cannot rebuild a binary memo from its display hex string.
// Compare raw bytes, so text which merely looks hexadecimal remains eligible.
enhanced_send: (data) =>
data.layout === 'legacy'
data.layout === 'legacy' || (data.memoBytes instanceof Uint8Array
&& bytesToHex(new TextEncoder().encode(data.memo ?? '')) !== bytesToHex(data.memoBytes))
? null
: {
asset: data.asset,
Expand Down Expand Up @@ -424,6 +427,23 @@ describe.skipIf(!API_URL)('rebuilding real on-chain messages', () => {
}, 60_000);
});

describe('on-chain enhanced-send memo classification', () => {
it('declines the binary memo from the failing weekly sample', () => {
const original = '434e54525052545902841b0000001c61620c4b1a000f42405501c4c987c235584a64990c94bb49cf6b87c778df7a5820ff397c86e327b1623cdc0b6f0875d3ab1a4e21ab8a00441d937a8cb0d2bb641c';
const decoded = unpackCounterpartyMessage(original);
expect(decoded.success).toBe(true);
expect(PARAMS_FROM_DECODED.enhanced_send!(decoded.data as Record<string, unknown>)).toBeNull();
});
it('still rebuilds hexadecimal-looking text exactly', () => {
const packed = packComposeMessage('send', { asset: 'XCP', quantity: 1, destination: '1BoatSLRHtKNngkdXEeobR76b53LETtpyT', memo: 'deadbeef' })!;
const decoded = unpackCounterpartyMessage(packed.bytes);
expect(decoded.success).toBe(true);
const params = PARAMS_FROM_DECODED.enhanced_send!(decoded.data as Record<string, unknown>);
expect(params).not.toBeNull();
expect(bytesToHex(packComposeMessage('send', params!)!.bytes)).toBe(bytesToHex(packed.bytes));
});
});

describe('on-chain fairminter sample classification', () => {
// The real shape, shortened: CNTRPRTY, type 0x5a, then an array head and a body. Only the head
// and the trailing bytes matter to the rule under test.
Expand Down
5 changes: 5 additions & 0 deletions src/pages/market/orders/[baseAsset]/[quoteAsset].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,11 @@ function AssetOrdersView({ baseAsset, quoteAsset, tab, setTab, onAutoSelectTab,
const sell: Order[] = [];

orders.forEach(order => {
const amount = getOrderBaseAmount(order, baseAsset);
const quote = getOrderQuoteAmount(order, baseAsset);
const price = getOrderPricePerUnit(order, baseAsset);
// Open orders can have no remaining liquidity while matches await settlement.
if (![amount, quote, price].every(value => Number.isFinite(value) && value > 0)) return;
if (order.give_asset === baseAsset) {
sell.push(order);
} else if (order.give_asset === quoteAsset) {
Expand Down
12 changes: 12 additions & 0 deletions src/pages/market/orders/__tests__/pair-pagination.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,18 @@ beforeEach(() => {
afterEach(cleanup);

describe('Order pair pagination ownership', () => {
it('excludes exhausted open buy and sell orders from price levels', async () => {
const exhaustedSell = { ...order('empty-sell', 'OLD', '2'), give_remaining_normalized: asDisplayUnits('0') };
const exhaustedBuy = { ...order('empty-buy', 'XCP', '3'), get_asset: 'OLD', get_remaining_normalized: asDisplayUnits('0') };
vi.mocked(fetchOrdersByPair).mockResolvedValue(page([exhaustedSell, exhaustedBuy, order('available')]));
render(<AssetOrdersPage />);
await flush();
expect(screen.getAllByTestId('level')).toHaveLength(1);
expect(screen.getByTestId('level')).toHaveTextContent('1: 1');
await act(async () => { fireEvent.click(screen.getByRole('tab', { name: 'Buy' })); });
expect(screen.queryByTestId('level')).not.toBeInTheDocument();
});

it('publishes the complete order book atomically after all pages finish', async () => {
const last = deferred<PaginatedResponse<Order>>();
vi.mocked(fetchOrdersByPair)
Expand Down
Loading