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 pendingInbound = inventory.pending_inbound || {};
|
||||
const pendingOutbound = inventory.pending_outbound || {};
|
||||
const balanceAssets = config.trackedAssets?.length
|
||||
? config.trackedAssets
|
||||
: [...config.assetRegistry.values()];
|
||||
|
||||
return {
|
||||
synced_at: inventory.synced_at || inventorySnapshot?.ingested_at || 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 pendingInboundUnits = String(pendingInbound[asset.assetId] || '0');
|
||||
const pendingOutboundUnits = String(pendingOutbound[asset.assetId] || '0');
|
||||
|
|
@ -1540,8 +1543,8 @@ function buildTradeFunnelSummary(lifecycleRows = []) {
|
|||
unresolved_submission_count: unresolvedSubmissions.length,
|
||||
no_trade_count: noTradeRows.length,
|
||||
successful_trades: successfulTrades,
|
||||
unresolved_submissions: unresolvedSubmissions,
|
||||
no_trade_rows: noTradeRows,
|
||||
unresolved_submissions: unresolvedSubmissions.slice(0, 20),
|
||||
no_trade_rows: [],
|
||||
counts,
|
||||
caveat:
|
||||
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');
|
||||
});
|
||||
|
||||
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', () => {
|
||||
const config = buildConfig();
|
||||
const bootstrap = buildDashboardBootstrap({
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue