import test from 'node:test'; import assert from 'node:assert/strict'; import { KeyPair } from 'near-api-js'; import { createIntentRequestController } from '../src/core/intent-request-controller.mjs'; import { applySlippageBps, buildSolverQuoteRequest, computeBtcReceiveUnitsFromEure, normalizeSolverQuotes, parseDecimalToUnits, selectBestSolverQuote, } from '../src/core/intent-requests.mjs'; const BTC = { assetId: 'nep141:btc.omft.near', symbol: 'BTC', decimals: 8, }; const EURE = { assetId: 'nep141:eure.omft.near', symbol: 'EURe', decimals: 18, }; function buildConfig() { return { tradingBtc: BTC, tradingEure: EURE, nearIntentsAccountId: 'unrip.test.near', nearVerifierContract: 'intents.near', intentRequestDefaultAmountEure: 5, intentRequestMaxAmountEure: 5, intentRequestDefaultSlippageBps: 200, intentRequestMaxSlippageBps: 200, intentRequestMinDeadlineMs: 60_000, intentRequestQuoteTimeoutMs: 10_000, intentRequestPublishTimeoutMs: 10_000, intentRequestStatusTimeoutMs: 10_000, intentRequestInventoryMaxAgeMs: 30_000, intentRequestPriceMaxAgeMs: 30_000, executorResponseTimeoutMs: 10_000, }; } function buildStore({ inventoryUnits = '5000000000000000000', nowIso = '2026-04-12T10:00:00.000Z', inventorySyncedAt = nowIso, priceObservedAt = nowIso, } = {}) { const preflights = []; const submissions = []; return { preflights, submissions, async loadLatestInventorySnapshot() { return { ingested_at: nowIso, payload: { synced_at: inventorySyncedAt, spendable: { [EURE.assetId]: inventoryUnits, [BTC.assetId]: '1000', }, pending_inbound: { [EURE.assetId]: '100000000000000000000', }, }, }; }, async loadLatestMarketPrice() { return { ingested_at: nowIso, payload: { observed_at: priceObservedAt, eure_per_btc: '50000', }, }; }, async insertPreflight(payload) { preflights.push(payload); }, async findPreflight({ requestId, idempotencyKey }) { return preflights.find((entry) => ( (requestId && entry.request_id === requestId) || (idempotencyKey && entry.idempotency_key === idempotencyKey) )) || null; }, async findSubmissionByRequest({ requestId }) { return [...submissions].reverse().find((entry) => entry.request_id === requestId) || null; }, async insertSubmissionResult(payload) { submissions.push(payload); }, async refreshOutcomes() { return []; }, }; } function buildRelay() { return { quoteCalls: 0, publishCalls: 0, async quote() { this.quoteCalls += 1; return [{ quote_hash: 'quote-hash-1', amount_out: '10000', expiration_time: '2026-04-12T10:01:00.000Z', }]; }, async publishIntent() { this.publishCalls += 1; return { status: 'OK', intent_hash: 'intent-hash-1' }; }, async getStatus() { return { status: 'PENDING' }; }, }; } function buildController({ store = buildStore(), relay = buildRelay(), armed = true, verifierRegistered = true } = {}) { return { store, relay, controller: createIntentRequestController({ config: buildConfig(), store, relayRpcClient: relay, verifierClient: { async isPublicKeyRegistered() { return verifierRegistered; }, async currentSalt() { return '252812b3'; }, }, signer: KeyPair.fromRandom('ed25519'), isArmed: () => armed, isPaused: () => false, now: () => Date.parse('2026-04-12T10:00:00.000Z'), uuid: (() => { let next = 1; return () => `id-${next++}`; })(), }), }; } test('EURe decimal parsing, BTC expected receive, and slippage math are exact enough for request limits', () => { const sourceUnits = parseDecimalToUnits('5', EURE.decimals, { field: 'amount_eure' }); const expectedBtc = computeBtcReceiveUnitsFromEure({ eureUnits: sourceUnits, eurPerBtc: '50000', eureDecimals: EURE.decimals, btcDecimals: BTC.decimals, }); assert.equal(sourceUnits, '5000000000000000000'); assert.equal(expectedBtc, '10000'); assert.equal(applySlippageBps(expectedBtc, 200), '9800'); }); test('solver quote normalization selects the best quote above explicit min receive', () => { const quotes = normalizeSolverQuotes({ quotes: [ { quote_hash: 'low', amount_out: '9700' }, { quote_hash: 'best', amount_out: '10050' }, { quote_hash: 'ok', amount_out: '9900' }, ], }); const selected = selectBestSolverQuote(quotes, { minDestinationAmountUnits: '9800' }); assert.equal(selected.quote_hash, 'best'); assert.deepEqual(buildSolverQuoteRequest({ sourceAssetId: EURE.assetId, destinationAssetId: BTC.assetId, sourceAmountUnits: '5000000000000000000', minDeadlineMs: 60_000, }), { defuse_asset_identifier_in: EURE.assetId, defuse_asset_identifier_out: BTC.assetId, exact_amount_in: '5000000000000000000', min_deadline_ms: 60000, }); }); test('preflight is side-effect-free and does not publish a live intent', async () => { const { controller, relay } = buildController(); const preflight = await controller.preflight({ amount_eure: '5', slippage_bps: 200 }); assert.equal(preflight.state, 'draft'); assert.equal(preflight.live_submit_capable, true); assert.equal(preflight.reason_code, 'quote_available'); assert.equal(relay.quoteCalls, 1); assert.equal(relay.publishCalls, 0); }); test('insufficient spendable EURe blocks before solver quote or signing', async () => { const store = buildStore({ inventoryUnits: '0' }); const relay = buildRelay(); const { controller } = buildController({ store, relay }); const preflight = await controller.preflight({ amount_eure: '5', slippage_bps: 200 }); assert.equal(preflight.state, 'blocked'); assert.equal(preflight.reason_code, 'insufficient_spendable_eure'); assert.equal(preflight.inventory_snapshot.pending_inbound[EURE.assetId], '100000000000000000000'); assert.equal(relay.quoteCalls, 0); assert.equal(relay.publishCalls, 0); }); test('duplicate request submit returns stored result and never publishes twice', async () => { const { controller, store, relay } = buildController(); const preflight = await controller.preflight({ amount_eure: '5', slippage_bps: 200 }); const first = await controller.submit({ request_id: preflight.request_id }); const second = await controller.submit({ request_id: preflight.request_id }); assert.equal(first.submission_result.status, 'accepted_by_relay'); assert.equal(second.duplicate, true); assert.equal(relay.publishCalls, 1); assert.equal(store.submissions.filter((entry) => entry.status === 'submit_requested').length, 1); assert.equal(store.submissions.filter((entry) => entry.status === 'accepted_by_relay').length, 1); }); test('executor disarmed blocks request submission without calling relay publish', async () => { const { controller, relay } = buildController({ armed: false }); const preflight = await controller.preflight({ amount_eure: '5', slippage_bps: 200 }); const result = await controller.submit({ request_id: preflight.request_id }); assert.equal(result.submission_result.status, 'blocked'); assert.equal(result.submission_result.result_code, 'executor_disarmed'); assert.equal(relay.publishCalls, 0); }); test('stale reference price blocks request preflight before solver quote', async () => { const store = buildStore({ priceObservedAt: '2026-04-12T09:59:00.000Z' }); const relay = buildRelay(); const { controller } = buildController({ store, relay }); const preflight = await controller.preflight({ amount_eure: '5', slippage_bps: 200 }); assert.equal(preflight.state, 'blocked'); assert.equal(preflight.reason_code, 'stale_reference_price'); assert.equal(relay.quoteCalls, 0); assert.equal(relay.publishCalls, 0); }); test('unregistered signer blocks request preflight before solver quote or signing', async () => { const relay = buildRelay(); const { controller } = buildController({ relay, verifierRegistered: false }); const preflight = await controller.preflight({ amount_eure: '5', slippage_bps: 200 }); assert.equal(preflight.state, 'blocked'); assert.equal(preflight.reason_code, 'signer_not_registered'); assert.equal(relay.quoteCalls, 0); assert.equal(relay.publishCalls, 0); }); test('relay publish failure records submit_requested first and never reports completion', async () => { const relay = buildRelay(); relay.publishIntent = async function publishIntent() { this.publishCalls += 1; throw new Error('relay publish unavailable'); }; const { controller, store } = buildController({ relay }); const preflight = await controller.preflight({ amount_eure: '5', slippage_bps: 200 }); const result = await controller.submit({ request_id: preflight.request_id }); assert.equal(result.submission_result.status, 'failed'); assert.equal(result.submission_result.result_code, 'publish_intent_failed'); assert.equal(result.submission_result.result_text, 'relay publish unavailable'); assert.equal(relay.publishCalls, 1); assert.equal(store.submissions[0].status, 'submit_requested'); assert.equal(store.submissions[1].status, 'failed'); assert.notEqual(result.submission_result.status, 'completed'); });