Proof: npm test passed 161/161; npm run operator-dashboard:build passed; local dev dashboard API returned HTTP 200 after cleaning stale port-forward processes. Assumptions: Forgejo main push remains the deployment path; imported 1Click assets should remain catalog-visible but not enter Funds balances unless inventory-enabled. Still fake: no live funds movement; local dashboard access still depends on a healthy kubectl port-forward because no public dashboard ingress is part of this turn.
This commit is contained in:
parent
af87a08a19
commit
339a1d8c43
2 changed files with 92 additions and 3 deletions
|
|
@ -755,11 +755,14 @@ function buildBalanceSummary({ inventorySnapshot, marketPrice, config }) {
|
||||||
const spendable = inventory.spendable || {};
|
const spendable = inventory.spendable || {};
|
||||||
const pendingInbound = inventory.pending_inbound || {};
|
const pendingInbound = inventory.pending_inbound || {};
|
||||||
const pendingOutbound = inventory.pending_outbound || {};
|
const pendingOutbound = inventory.pending_outbound || {};
|
||||||
|
const balanceAssets = config.trackedAssets?.length
|
||||||
|
? config.trackedAssets
|
||||||
|
: [...config.assetRegistry.values()];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
synced_at: inventory.synced_at || inventorySnapshot?.ingested_at || null,
|
synced_at: inventory.synced_at || inventorySnapshot?.ingested_at || null,
|
||||||
reconciliation_status: inventory.reconciliation_status || null,
|
reconciliation_status: inventory.reconciliation_status || null,
|
||||||
items: [...config.assetRegistry.values()].map((asset) => {
|
items: balanceAssets.map((asset) => {
|
||||||
const spendableUnits = String(spendable[asset.assetId] || '0');
|
const spendableUnits = String(spendable[asset.assetId] || '0');
|
||||||
const pendingInboundUnits = String(pendingInbound[asset.assetId] || '0');
|
const pendingInboundUnits = String(pendingInbound[asset.assetId] || '0');
|
||||||
const pendingOutboundUnits = String(pendingOutbound[asset.assetId] || '0');
|
const pendingOutboundUnits = String(pendingOutbound[asset.assetId] || '0');
|
||||||
|
|
@ -1540,8 +1543,8 @@ function buildTradeFunnelSummary(lifecycleRows = []) {
|
||||||
unresolved_submission_count: unresolvedSubmissions.length,
|
unresolved_submission_count: unresolvedSubmissions.length,
|
||||||
no_trade_count: noTradeRows.length,
|
no_trade_count: noTradeRows.length,
|
||||||
successful_trades: successfulTrades,
|
successful_trades: successfulTrades,
|
||||||
unresolved_submissions: unresolvedSubmissions,
|
unresolved_submissions: unresolvedSubmissions.slice(0, 20),
|
||||||
no_trade_rows: noTradeRows,
|
no_trade_rows: [],
|
||||||
counts,
|
counts,
|
||||||
caveat:
|
caveat:
|
||||||
successfulTrades.length > 0
|
successfulTrades.length > 0
|
||||||
|
|
|
||||||
|
|
@ -847,6 +847,92 @@ test('bootstrap balances and funding handles distinguish nBTC reserve from legac
|
||||||
assert.equal(bootstrap.funds.funding.handles[0].label, 'btc:mainnet funding handle');
|
assert.equal(bootstrap.funds.funding.handles[0].label, 'btc:mainnet funding handle');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('bootstrap balances exclude imported catalog assets that are not inventory-enabled', () => {
|
||||||
|
const config = buildDualBtcConfig();
|
||||||
|
const importedOnlyAsset = {
|
||||||
|
assetId: 'nep141:sol-imported-token.near',
|
||||||
|
symbol: 'CATALOG',
|
||||||
|
label: 'Catalog-only token',
|
||||||
|
decimals: 6,
|
||||||
|
chain: 'sol',
|
||||||
|
supported: true,
|
||||||
|
enabledForInventory: false,
|
||||||
|
};
|
||||||
|
config.assetRegistry = new Map([
|
||||||
|
...config.assetRegistry,
|
||||||
|
[importedOnlyAsset.assetId, importedOnlyAsset],
|
||||||
|
]);
|
||||||
|
|
||||||
|
const bootstrap = buildDashboardBootstrap({
|
||||||
|
config,
|
||||||
|
inventorySnapshot: {
|
||||||
|
ingested_at: '2026-05-12T19:45:00.000Z',
|
||||||
|
payload: {
|
||||||
|
synced_at: '2026-05-12T19:45:00.000Z',
|
||||||
|
reconciliation_status: 'ok',
|
||||||
|
spendable: {
|
||||||
|
[config.tradingBtc.assetId]: '100000',
|
||||||
|
[importedOnlyAsset.assetId]: '999999',
|
||||||
|
},
|
||||||
|
pending_inbound: {},
|
||||||
|
pending_outbound: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
marketPrice: null,
|
||||||
|
recentQuotes: [],
|
||||||
|
submissionPage: { page: 1, page_size: 20, total: 0, total_pages: 1, items: [] },
|
||||||
|
submissionSummary: { total: 0 },
|
||||||
|
fundingObservations: [],
|
||||||
|
recentTradeDecisions: [],
|
||||||
|
recentExecuteTradeCommands: [],
|
||||||
|
recentExecutionResults: [],
|
||||||
|
recentAlertTransitions: [],
|
||||||
|
serviceSnapshots: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(bootstrap.funds.balances.items.map((item) => item.asset_id), [
|
||||||
|
'nep141:nbtc.bridge.near',
|
||||||
|
'nep141:btc.omft.near',
|
||||||
|
'nep141:eure.omft.near',
|
||||||
|
]);
|
||||||
|
assert.equal(
|
||||||
|
bootstrap.strategy.asset_catalog.items.some((item) => item.asset_id === importedOnlyAsset.assetId),
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('bootstrap keeps no-trade counts without shipping non-rendered row payloads', () => {
|
||||||
|
const config = buildConfig();
|
||||||
|
const bootstrap = buildDashboardBootstrap({
|
||||||
|
config,
|
||||||
|
inventorySnapshot: null,
|
||||||
|
marketPrice: null,
|
||||||
|
recentQuotes: [],
|
||||||
|
submissionPage: { page: 1, page_size: 20, total: 0, total_pages: 1, items: [] },
|
||||||
|
submissionSummary: { total: 0 },
|
||||||
|
fundingObservations: [],
|
||||||
|
recentTradeDecisions: [{
|
||||||
|
observed_at: '2026-05-12T19:40:00.000Z',
|
||||||
|
payload: {
|
||||||
|
decision_id: 'decision-rejected',
|
||||||
|
quote_id: 'quote-rejected',
|
||||||
|
pair: config.activePair,
|
||||||
|
decision: 'rejected',
|
||||||
|
decision_reason: 'below_threshold',
|
||||||
|
raw_solver_payload: Object.fromEntries(Array.from({ length: 100 }, (_, index) => [`field_${index}`, index])),
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
recentExecuteTradeCommands: [],
|
||||||
|
recentExecutionResults: [],
|
||||||
|
recentAlertTransitions: [],
|
||||||
|
serviceSnapshots: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const funnel = bootstrap.strategy.strategy_state.trade_funnel;
|
||||||
|
assert.equal(funnel.no_trade_count, 1);
|
||||||
|
assert.deepEqual(funnel.no_trade_rows, []);
|
||||||
|
});
|
||||||
|
|
||||||
test('bootstrap normalizes actionable decision vocabulary before exposing it to the dashboard', () => {
|
test('bootstrap normalizes actionable decision vocabulary before exposing it to the dashboard', () => {
|
||||||
const config = buildConfig();
|
const config = buildConfig();
|
||||||
const bootstrap = buildDashboardBootstrap({
|
const bootstrap = buildDashboardBootstrap({
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue