# Money and a rate from a snapshot ## Intention I want to convert a foreign-currency amount at a rate for an explicitly supplied date. An external indicator requires a snapshot — see [Read an external limit from a snapshot](/recipes/n-package/external-snapshot/). This scene applies the same contract to a Money rate on a date. ## Incorrect form and why it stays silent ```law title="Incorrect form" external snapshot function Rate(on: Date) -> Money { capability "recipe.fx/1"; } ``` A function without snapshot_from does not pin a rate source. Money / Money of one currency yields a share, multiplied by the price of one USD in KZT; adding currencies directly is forbidden. ## Correct form ```law language "law.core" version "0.2"; package recipes.l.r04 version "0.1.0"; namespace "urn:recipe:l-calc:04"; external snapshot function Rate(on: Date) -> Money { capability "recipe.fx/1"; snapshot_from context.fx; } relation dollars(p: Text, amount: Money, on: Date); relation tenge(p: Text, amount: Money); rule Exchange strict { for p: Text; for amount: Money; for on: Date; when dollars(p, amount, on); then tenge(p, (amount / 1 USD) * Rate(on)); } ``` ## Frozen execution scene | Facts and choice | Question | Answer | |---|---|---| | 1. 10 USD at 490 KZT | `truth(tenge("invoice", 4900 KZT))` | `TRUE_ONLY` / `COMPUTED` | | 2. 10 USD at the next day's rate 495 KZT | `truth(tenge("invoice", 4950 KZT))` | `TRUE_ONLY` / `COMPUTED` | | 3. snapshot not presented | `truth(tenge("invoice", 4950 KZT))` | `NEITHER` / `EXTERNAL_UNAVAILABLE` | ```law test "ten dollars at the dated rate" { given { context { legal_time @2026-09-13; decision_time @2026-09-13T09:00:00+05:00; knowledge_time @2026-09-13T09:00:00+05:00; timezone "Asia/Almaty"; } assert dollars("invoice", 10 USD, @2026-09-12); } evaluate truth(tenge("invoice", 4900 KZT)); expect truth_status == TRUE_ONLY; expect evaluation_status == COMPUTED; } ``` ```law test "ten dollars at next day rate" { given { context { legal_time @2026-09-13; decision_time @2026-09-13T09:00:00+05:00; knowledge_time @2026-09-13T09:00:00+05:00; timezone "Asia/Almaty"; } assert dollars("invoice", 10 USD, @2026-09-13); } evaluate truth(tenge("invoice", 4950 KZT)); expect truth_status == TRUE_ONLY; expect evaluation_status == COMPUTED; } ``` ```law test "snapshot not presented" { given { context { legal_time @2026-09-13; decision_time @2026-09-13T09:00:00+05:00; knowledge_time @2026-09-13T09:00:00+05:00; timezone "Asia/Almaty"; } assert dollars("invoice", 10 USD, @2026-09-13); } evaluate truth(tenge("invoice", 4950 KZT)); expect truth_status == NEITHER; expect evaluation_status == EXTERNAL_UNAVAILABLE; expect issue(EXTERNAL_SNAPSHOT_MISSING); } ``` The teaching snapshot is pinned by a semantic hash; [resource bytes](https://github.com/arxohq/law/blob/master/docs/recipes/l-calc/resources/04-fx.json): ```json { "schemaVersion": "law.core.external-snapshot/0.1", "id": "urn:recipe:l-calc:04:fx", "capability": "recipe.fx/1", "snapshotBinding": "context.fx", "calls": [ { "symbol": "urn:recipe:l-calc:04#Rate", "args": [ { "kind": "value", "type": { "name": "urn:law:std#Date" }, "value": "2026-09-12" } ], "result": { "kind": "value", "type": { "name": "urn:law:std#Money" }, "value": "490", "currency": "KZT" } }, { "symbol": "urn:recipe:l-calc:04#Rate", "args": [ { "kind": "value", "type": { "name": "urn:law:std#Date" }, "value": "2026-09-13" } ], "result": { "kind": "value", "type": { "name": "urn:law:std#Money" }, "value": "495", "currency": "KZT" } } ], "contentHash": "sha256:61f90adf1bb21fea0eec3bd638ed536cac7b31db8916e13f890b97b728137704" } ``` ## Counterfactual Mutation: ` snapshot_from context.fx;` → ``; expected `LDC-E1305`. Additional counterfactuals are shown as separate table rows. ## Boundary The snapshot is instructional, not an NBK rate. The input currency tag, the date and the rate source belong to the rule. A missing rate is not a zero price; HTTP is not called at evaluation time. The snapshot envelope carries a schema version: without it the request is refused before evaluation. ## Pitfall Without a presented snapshot there is no rate and no zero price: the question stays Not established, not refuted, with EXTERNAL_SNAPSHOT_MISSING.