Some checks failed
deploy / deploy (push) Failing after 40s
Proof: targeted pair-native strategy, preflight, outcome, dashboard, and ops tests pass; full npm test passes 237/237; operator dashboard production bundle builds; ops watcher Python test passes. Assumptions: DB asset, pair, strategy config, and price route rows remain canonical; legacy EURe fields stay only for old-row/API compatibility; local shell has no Kubernetes context for direct live namespace recheck. Still fake: venue-native terminal fill ids and realized fee/PnL attribution remain unavailable; live deployment verification must happen through the repo workflow because manual cluster repair is out of scope.
624 lines
20 KiB
JavaScript
624 lines
20 KiB
JavaScript
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,
|
|
};
|
|
const USDC = {
|
|
assetId: 'nep141:usdc.omft.near',
|
|
symbol: 'USDC',
|
|
decimals: 6,
|
|
};
|
|
|
|
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,
|
|
withMakerSuppressed = async (operation) => operation(),
|
|
getTradingConfig = null,
|
|
} = {}) {
|
|
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,
|
|
getTradingConfig,
|
|
withMakerSuppressed,
|
|
now: () => Date.parse('2026-04-12T10:00:00.000Z'),
|
|
uuid: (() => {
|
|
let next = 1;
|
|
return () => `id-${next++}`;
|
|
})(),
|
|
}),
|
|
};
|
|
}
|
|
|
|
function buildPair({
|
|
sourceAsset,
|
|
destinationAsset,
|
|
source,
|
|
routeId,
|
|
canTrade = true,
|
|
blockReason = null,
|
|
}) {
|
|
const pairId = `${sourceAsset.assetId}->${destinationAsset.assetId}`;
|
|
return {
|
|
key: pairId,
|
|
pairId,
|
|
takerEnabled: true,
|
|
canTrade,
|
|
blockReason,
|
|
assetIn: sourceAsset,
|
|
assetOut: destinationAsset,
|
|
strategyConfig: {
|
|
configId: `${pairId}:v1`,
|
|
version: 1,
|
|
requestDefaultNotional: '5',
|
|
requestMaxNotional: null,
|
|
slippageBps: 200,
|
|
requestMaxSlippageBps: null,
|
|
minDeadlineMs: 60_000,
|
|
priceMaxAgeMs: 30_000,
|
|
inventoryMaxAgeMs: 30_000,
|
|
},
|
|
priceRoute: source ? {
|
|
routeId,
|
|
source,
|
|
baseAssetId: BTC.assetId,
|
|
quoteAssetId: sourceAsset.assetId === BTC.assetId ? destinationAsset.assetId : sourceAsset.assetId,
|
|
} : null,
|
|
};
|
|
}
|
|
|
|
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('DB null request limits allow operator-chosen amount and slippage', async () => {
|
|
const store = buildStore({ inventoryUnits: '6000000000000000000' });
|
|
const relay = buildRelay();
|
|
relay.quote = async function quote() {
|
|
this.quoteCalls += 1;
|
|
return [{
|
|
quote_hash: 'uncapped-quote-hash',
|
|
amount_out: '12000',
|
|
expiration_time: '2026-04-12T10:01:00.000Z',
|
|
}];
|
|
};
|
|
const pairId = `${EURE.assetId}->${BTC.assetId}`;
|
|
const pair = {
|
|
key: pairId,
|
|
pairId,
|
|
takerEnabled: true,
|
|
canTrade: true,
|
|
assetIn: EURE,
|
|
assetOut: BTC,
|
|
strategyConfig: {
|
|
requestDefaultNotional: '5',
|
|
requestMaxNotional: null,
|
|
slippageBps: 200,
|
|
requestMaxSlippageBps: null,
|
|
minDeadlineMs: 60_000,
|
|
priceMaxAgeMs: 30_000,
|
|
inventoryMaxAgeMs: 30_000,
|
|
},
|
|
priceRoute: {
|
|
source: 'btc_eur_reference',
|
|
},
|
|
};
|
|
const { controller } = buildController({
|
|
store,
|
|
relay,
|
|
getTradingConfig: async () => ({
|
|
ok: true,
|
|
tradingEure: EURE,
|
|
tradingBtc: BTC,
|
|
pairByKey: new Map([[pairId, pair]]),
|
|
defaultTakerPair: pair,
|
|
}),
|
|
});
|
|
|
|
const preflight = await controller.preflight({ amount_eure: '6', slippage_bps: 250 });
|
|
|
|
assert.equal(preflight.state, 'draft');
|
|
assert.equal(preflight.reason_code, 'quote_available');
|
|
assert.equal(preflight.request_max_notional, null);
|
|
assert.equal(preflight.request_max_slippage_bps, null);
|
|
assert.equal(preflight.slippage_bps, 250);
|
|
assert.equal(preflight.live_submit_capable, true);
|
|
assert.equal(relay.quoteCalls, 1);
|
|
});
|
|
|
|
test('nBTC/USDC preflight accepts pair_id and source_amount without EURe price fields', async () => {
|
|
const nowIso = '2026-04-12T10:00:00.000Z';
|
|
const store = buildStore({ nowIso });
|
|
store.loadLatestInventorySnapshot = async () => ({
|
|
ingested_at: nowIso,
|
|
payload: {
|
|
synced_at: nowIso,
|
|
spendable: {
|
|
[USDC.assetId]: '10000000',
|
|
[BTC.assetId]: '0',
|
|
},
|
|
pending_inbound: {},
|
|
},
|
|
});
|
|
store.loadLatestMarketPrice = async ({ priceRouteId } = {}) => ({
|
|
ingested_at: nowIso,
|
|
payload: {
|
|
observed_at: nowIso,
|
|
price_id: 'price-usdc-1',
|
|
price_route_id: priceRouteId,
|
|
usdc_per_btc: '50000',
|
|
},
|
|
});
|
|
const relay = buildRelay();
|
|
relay.quote = async function quote(request) {
|
|
this.quoteCalls += 1;
|
|
assert.equal(request.defuse_asset_identifier_in, USDC.assetId);
|
|
assert.equal(request.defuse_asset_identifier_out, BTC.assetId);
|
|
assert.equal(request.exact_amount_in, '10000000');
|
|
return [{
|
|
quote_hash: 'usdc-quote-hash',
|
|
amount_out: '20000',
|
|
expiration_time: '2026-04-12T10:01:00.000Z',
|
|
}];
|
|
};
|
|
const pair = buildPair({
|
|
sourceAsset: USDC,
|
|
destinationAsset: BTC,
|
|
source: 'btc_usdc_reference',
|
|
routeId: 'btc-usdc-route',
|
|
});
|
|
const { controller } = buildController({
|
|
store,
|
|
relay,
|
|
getTradingConfig: async () => ({
|
|
ok: true,
|
|
tradingEure: EURE,
|
|
tradingBtc: BTC,
|
|
pairByKey: new Map([[pair.key, pair]]),
|
|
pairById: new Map([[pair.pairId, pair]]),
|
|
defaultTakerPair: pair,
|
|
}),
|
|
});
|
|
|
|
const preflight = await controller.preflight({
|
|
pair_id: pair.pairId,
|
|
source_amount: '10',
|
|
slippage_bps: 200,
|
|
});
|
|
|
|
assert.equal(preflight.state, 'draft');
|
|
assert.equal(preflight.reason_code, 'quote_available');
|
|
assert.equal(preflight.amount_eure, null);
|
|
assert.equal(preflight.source_asset_id, USDC.assetId);
|
|
assert.equal(preflight.destination_asset_id, BTC.assetId);
|
|
assert.equal(preflight.expected_destination_amount_units, '20000');
|
|
assert.equal(preflight.min_destination_amount_units, '19600');
|
|
assert.equal(preflight.notional, '10');
|
|
assert.equal(preflight.notional_symbol, 'USDC');
|
|
assert.equal(preflight.reference_price_id, 'price-usdc-1');
|
|
assert.equal(relay.quoteCalls, 1);
|
|
});
|
|
|
|
test('nBTC/USDC preflight blocks missing route before solver quote', async () => {
|
|
const relay = buildRelay();
|
|
const pair = buildPair({
|
|
sourceAsset: USDC,
|
|
destinationAsset: BTC,
|
|
source: null,
|
|
routeId: null,
|
|
canTrade: false,
|
|
blockReason: 'price_route_missing',
|
|
});
|
|
const { controller } = buildController({
|
|
relay,
|
|
getTradingConfig: async () => ({
|
|
ok: true,
|
|
tradingEure: EURE,
|
|
tradingBtc: BTC,
|
|
pairByKey: new Map([[pair.key, pair]]),
|
|
pairById: new Map([[pair.pairId, pair]]),
|
|
defaultTakerPair: pair,
|
|
}),
|
|
});
|
|
|
|
const preflight = await controller.preflight({ pair_id: pair.pairId, source_amount: '10' });
|
|
|
|
assert.equal(preflight.state, 'blocked');
|
|
assert.equal(preflight.reason_code, 'price_route_missing');
|
|
assert.equal(relay.quoteCalls, 0);
|
|
});
|
|
|
|
test('nBTC/USDC preflight blocks insufficient source inventory with source-specific reason', async () => {
|
|
const nowIso = '2026-04-12T10:00:00.000Z';
|
|
const store = buildStore({ nowIso });
|
|
store.loadLatestInventorySnapshot = async () => ({
|
|
ingested_at: nowIso,
|
|
payload: {
|
|
synced_at: nowIso,
|
|
spendable: {
|
|
[USDC.assetId]: '1',
|
|
},
|
|
pending_inbound: {},
|
|
},
|
|
});
|
|
store.loadLatestMarketPrice = async ({ priceRouteId } = {}) => ({
|
|
ingested_at: nowIso,
|
|
payload: {
|
|
observed_at: nowIso,
|
|
price_id: 'price-usdc-2',
|
|
price_route_id: priceRouteId,
|
|
usdc_per_btc: '50000',
|
|
},
|
|
});
|
|
const relay = buildRelay();
|
|
const pair = buildPair({
|
|
sourceAsset: USDC,
|
|
destinationAsset: BTC,
|
|
source: 'btc_usdc_reference',
|
|
routeId: 'btc-usdc-route',
|
|
});
|
|
const { controller } = buildController({
|
|
store,
|
|
relay,
|
|
getTradingConfig: async () => ({
|
|
ok: true,
|
|
tradingEure: EURE,
|
|
tradingBtc: BTC,
|
|
pairByKey: new Map([[pair.key, pair]]),
|
|
pairById: new Map([[pair.pairId, pair]]),
|
|
defaultTakerPair: pair,
|
|
}),
|
|
});
|
|
|
|
const preflight = await controller.preflight({ pair_id: pair.pairId, source_amount: '10' });
|
|
|
|
assert.equal(preflight.state, 'blocked');
|
|
assert.equal(preflight.reason_code, 'insufficient_spendable_usdc');
|
|
assert.equal(relay.quoteCalls, 0);
|
|
});
|
|
|
|
test('pair-native preflight treats explicit zero source_amount as invalid instead of using default', async () => {
|
|
const relay = buildRelay();
|
|
const pair = buildPair({
|
|
sourceAsset: USDC,
|
|
destinationAsset: BTC,
|
|
source: 'btc_usdc_reference',
|
|
routeId: 'btc-usdc-route',
|
|
});
|
|
const { controller } = buildController({
|
|
relay,
|
|
getTradingConfig: async () => ({
|
|
ok: true,
|
|
tradingEure: EURE,
|
|
tradingBtc: BTC,
|
|
pairByKey: new Map([[pair.key, pair]]),
|
|
pairById: new Map([[pair.pairId, pair]]),
|
|
defaultTakerPair: pair,
|
|
}),
|
|
});
|
|
|
|
const preflight = await controller.preflight({ pair_id: pair.pairId, source_amount: 0 });
|
|
|
|
assert.equal(preflight.state, 'blocked');
|
|
assert.equal(preflight.reason_code, 'invalid_source_amount');
|
|
assert.equal(preflight.source_amount, '0');
|
|
assert.equal(relay.quoteCalls, 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');
|
|
});
|
|
|
|
|
|
test('preflight suppresses maker responses while collecting solver quotes', async () => {
|
|
let suppressed = false;
|
|
let wrapperCalls = 0;
|
|
const relay = buildRelay();
|
|
relay.quote = async function quote() {
|
|
this.quoteCalls += 1;
|
|
assert.equal(suppressed, true);
|
|
return [{
|
|
quote_hash: 'external-quote-hash',
|
|
amount_out: '10000',
|
|
expiration_time: '2026-04-12T10:01:00.000Z',
|
|
}];
|
|
};
|
|
|
|
const { controller } = buildController({
|
|
relay,
|
|
withMakerSuppressed: async (operation, context) => {
|
|
wrapperCalls += 1;
|
|
assert.match(context.requestId, /^id-/);
|
|
suppressed = true;
|
|
try {
|
|
return await operation();
|
|
} finally {
|
|
suppressed = false;
|
|
}
|
|
},
|
|
});
|
|
|
|
const preflight = await controller.preflight({ amount_eure: '5', slippage_bps: 200 });
|
|
|
|
assert.equal(preflight.state, 'draft');
|
|
assert.equal(preflight.selected_quote.quote_hash, 'external-quote-hash');
|
|
assert.equal(wrapperCalls, 1);
|
|
assert.equal(suppressed, false);
|
|
});
|
|
|
|
|
|
test('refreshOutcomes refreshes relay status before recomputing settlement truth', async () => {
|
|
const { controller, store, relay } = buildController();
|
|
const preflight = await controller.preflight({ amount_eure: '5', slippage_bps: 200 });
|
|
await controller.submit({ request_id: preflight.request_id });
|
|
const accepted = store.submissions.find((entry) => entry.status === 'accepted_by_relay');
|
|
store.loadSubmissionsForStatusRefresh = async () => [accepted];
|
|
store.refreshOutcomes = async () => [{ request_id: preflight.request_id, outcome_status: 'failed' }];
|
|
relay.getStatus = async () => ({
|
|
intent_hash: accepted.intent_hash,
|
|
status: 'SETTLED',
|
|
data: { hash: 'settlement-tx-hash' },
|
|
});
|
|
|
|
const refreshed = await controller.refreshOutcomes();
|
|
const statusRefresh = store.submissions.at(-1);
|
|
|
|
assert.equal(refreshed.refreshed_statuses.length, 1);
|
|
assert.equal(refreshed.outcomes[0].outcome_status, 'failed');
|
|
assert.equal(statusRefresh.status, 'accepted_by_relay');
|
|
assert.equal(statusRefresh.result_code, 'relay_status_refreshed');
|
|
assert.equal(statusRefresh.relay_status, 'SETTLED');
|
|
assert.equal(statusRefresh.relay_status_response.data.hash, 'settlement-tx-hash');
|
|
assert.equal(statusRefresh.submitted_at, accepted.submitted_at);
|
|
assert.ok(statusRefresh.status_checked_at);
|
|
});
|