What you will build.
Model DEMO-FX-01, an agreed exchange of fictional USD and EUR amounts. The first currency leg has completed but the second has not. Build a reconciliation view that cannot describe the whole exchange as settled merely because one provider reported success.
Your working record
Create a private worksheet with columns for the source reference, observed state, responsible system, observation time, expected result and unresolved issue. Use the same fictional case throughout the steps; do not paste live personal or financial records into this exercise.
Run the example before changing it.
Inspect the baseline record below. Download and read the complete JavaScript file before running it. It uses only Node’s built-in test and assertion modules; there is no package installation, account connection or secret to configure.
{
"quoteValid": true,
"legA": "final",
"legB": "final"
}Save it as on-chain-fx-settlement.mjs. Open a terminal in that folder and run:
node --version
node --test on-chain-fx-settlement.mjsExpected result: four passing tests and zero failures. These tests verify the teaching classifier only. They do not verify a production integration, security boundary or financial outcome.
File or test runner will not start?
Check that Node.js 22 or later is installed, the filename ends in .mjs rather than .mjs.txt, and your terminal is in the download folder. If a managed browser blocks scripts, open “Read the full exercise” and save that text locally under the same filename. Do not disable organizational security controls.
Read the full exercise
// Hybrid-Chain offline teaching model: on-chain-fx-settlement
// Fictional fields/states; NOT an API client, authorization system or production implementation.
// No network calls, credentials, payments or file writes. Review before running.
// Requires Node.js 22+. Run: node --test on-chain-fx-settlement.mjs
import test from 'node:test';
import assert from 'node:assert/strict';
export function inspect(record) {
if (!record.quoteValid) return 'REQUOTE_REQUIRED';
if (record.legA !== 'final') return 'LEG_A_UNRESOLVED';
if (record.legB !== 'final') return 'LEG_B_UNRESOLVED';
return 'BOTH_LEGS_RECORDED';
}
const baseline = {
"quoteValid": true,
"legA": "final",
"legB": "final"
};
const cases = [
{
"name": "both leg records present",
"patch": {},
"expected": "BOTH_LEGS_RECORDED"
},
{
"name": "quote invalid for release",
"patch": {
"quoteValid": false
},
"expected": "REQUOTE_REQUIRED"
},
{
"name": "first leg pending",
"patch": {
"legA": "pending"
},
"expected": "LEG_A_UNRESOLVED"
},
{
"name": "second leg pending",
"patch": {
"legB": "pending"
},
"expected": "LEG_B_UNRESOLVED"
}
];
for (const example of cases) {
test(example.name, () => {
assert.equal(inspect({...baseline, ...example.patch}), example.expected);
});
}
Know what the tests demonstrate
- both leg records present
BOTH_LEGS_RECORDED- quote invalid for release
REQUOTE_REQUIRED- first leg pending
LEG_A_UNRESOLVED- second leg pending
LEG_B_UNRESOLVED
Now change one fixture at a time. If you change a case’s input without changing its expected result, the test should fail. Read the actual-versus-expected difference and use the walkthrough to decide whether your rule or your expectation needs correcting. Keep an unchanged copy for comparison.
Work through the case, step by step.
- 01
Create one row per currency leg
Do this
- Label the exchange DEMO-FX-01 and its legs A and B; do not use a single amount field for both currencies.
- Record agreed amounts, quote reference, expiry and each leg's independent provider reference.
- Choose a display rule: the whole exchange is complete only when both authoritative leg outcomes are final.
Expected resultThe worksheet makes asymmetric outcomes visible and does not claim atomic delivery between unrelated systems.
If your result is different
If you cannot identify the second leg's authority, mark the exchange incomplete rather than inferring it from the first.
- 02
Check the quote before release
Do this
- Run the baseline local exercise with quoteValid=true and both leg states final.
- Change quoteValid to false; expect REQUOTE_REQUIRED even though the synthetic leg fields remain final.
- Explain in the worksheet that this is a pre-release acceptance rule; a real expired quote does not undo transfers already completed.
Expected resultReaders can distinguish an invalid authorization basis from the historical fact that a payment may already have happened.
If your result is different
If funds have actually moved, reconcile that fact separately. Never use a quote-expiry flag as a refund instruction.
- 03
Inspect a one-sided completion
Do this
- Set legB to pending while legA stays final and run the test.
- Record LEG_B_UNRESOLVED together with the B-side provider and escalation owner.
- Repeat with legA pending. Keep the two provider references available to support rather than hiding them behind a spinner.
Expected resultThe view names the unresolved leg, which is more useful than a generic failed-exchange label.
If your result is different
If a notification disagrees with the provider's authoritative status, preserve both observations and reconcile before changing the conclusion.
- 04
Define reconciliation and closure
Do this
- Restore both final outcomes and record the local BOTH_LEGS_RECORDED result.
- Document who can investigate, re-quote, refund or release a leg; those actions are not implemented by this exercise.
- Use the funding guide to map actual readiness and settlement evidence onto the worksheet.
Expected resultYou have a practical status model with explicit partner dependencies and a repeatable incomplete-case test.
If your result is different
Do not label BOTH_LEGS_RECORDED as proof of a banking partnership, guaranteed FX execution or cross-chain atomicity.
Review what you produced.
Save the worksheet, unmodified test output, one deliberately failing case and the explanation of its fix. A useful result includes an unresolved item with an owner; it is not just a screenshot of four green tests.
Map the model to actual systems.
Banks, issuers, liquidity providers, and your application must agree FX execution, custody, currency availability, redemption, and exception handling. Token creation does not create a backed currency or banking permission.
For each worksheet field, name the owning system, exact current operation, permission and error behavior. Put “not available” next to any missing operation and assign a dependency owner. Do not substitute the teaching classifier for that missing integration.
Open the current API reference and inspect each operation’s request, response, scope and implementation status. Code examples on this page are local models only; they do not call those operations.
Take the next technical step.
Use your worksheet and failing-case notes in the next walkthrough. Keep the same reference trail so you can see which assumptions become actual observations—and which remain dependencies.
Trace funding and settlement records