@anywalletkit/protocol-swap2-thorchain 0.3.5 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -28,12 +28,96 @@ nodes of different operators (Liquify's gateway and the IBS team's node, both ch
28
28
  operator; a local mock needs a second mock there). Node URLs must be `https:` (`allowInsecureNodeUrl` for local
29
29
  development).
30
30
 
31
+ ### How a second node confirms the vault
32
+
33
+ THORChain keeps several asgard vaults active and its inbound address moves among them every few minutes, so a node a
34
+ few blocks behind the quoting node often still names the previous one. A confirming node agrees when the quoted
35
+ vault is:
36
+
37
+ - its current inbound address for the source chain (`/thorchain/inbound_addresses`), or
38
+ - that chain's address of a vault it lists with status `ActiveVault` (`/thorchain/vaults/asgard`, chain matched
39
+ exactly; EVM addresses compare case-insensitively, others exactly) that its mimir (`/thorchain/mimir`) does not
40
+ flag `CompromisedVault-<pubkey>` or `SolvencyHaltVault-<pubkey>-<chain>`. THORNode leaves such vaults out of inbound
41
+ election and does not credit an inbound to a compromised vault.
42
+
43
+ A `RetiringVault` (or any other status) never confirms. The rest stays as strict: on EVM the router in the node's
44
+ `inbound_addresses` must be the quote's; a halt or trading pause on the source or the destination chain refuses; the
45
+ calldata must still pay exactly the quoted vault.
46
+
47
+ A confirming node is skipped for the next one when it trails the quoting node's THORChain height
48
+ (`/thorchain/lastblock`, the `thorchain` field) by more than `confirmMaxLagBlocks` (default 50 ≈ 5 minutes), does not
49
+ answer within `confirmTimeoutMs` (default 2500 ms), redirects (also when the runtime followed the redirect itself, as
50
+ React Native's fetch does: `res.url` must be the URL asked), or answers in another shape. When every node trails, the
51
+ refusal says so ("no confirming node is within 50 blocks …"); when none answers, it is "no second node answered to
52
+ confirm the inbound vault".
53
+
54
+ **Another shape** includes a field of another type. The type of every field read is checked (THORNode's own types):
55
+ in `inbound_addresses` each entry is an object with a string `chain`, `address`, `router` and `dust_threshold` are
56
+ strings and `halted`, `global_trading_paused` and `chain_trading_paused` booleans when present; in `vaults/asgard`
57
+ `status` is a string, `pub_key` a `thorpub1…` string, `addresses` a list of objects with string `chain` and `address`;
58
+ every `mimir` value is a number or a string (int64), and a vault flag an int64; `lastblock`'s `thorchain` is a
59
+ non-negative int64. One such field anywhere in an answer (another chain's entry or another vault included) skips
60
+ the confirming node, and `verifyBuild` skips it the same way. The quoting node's `inbound_addresses` and `lastblock`
61
+ are read the same way: another shape makes that node unavailable and the next `nodeUrl` host is asked, with the
62
+ existing refusals when none is left ("inbound addresses unavailable from every node", "THORChain height
63
+ (/thorchain/lastblock) unavailable from every node that answered the inbound addresses"; before broadcast "cannot
64
+ refresh inbound addresses before broadcast").
65
+
66
+ **Time.** The vault check has its own 8 s budget. It starts when the quoting node's quote is in hand (before
67
+ broadcast: when a node is asked to refresh the inbound snapshot) and covers that node's `inbound_addresses` and
68
+ `lastblock` reads and every confirming node, each getting `confirmTimeoutMs` or what is left; time a failed host took
69
+ before does not count, and no confirming node is asked with less than 100 ms left. When the budget runs out before
70
+ any confirming node was asked, the refusal is "the 8 s confirmation budget ran out before a second node could be asked to confirm the
71
+ inbound vault — retry". `confirmTimeoutMs` bounds confirming nodes only: each read of the quoting node (the quote,
72
+ `inbound_addresses`, `lastblock`) gets 8 s whatever it is. When a node answers `inbound_addresses` but not
73
+ `lastblock`, the refusal names it: "THORChain height (/thorchain/lastblock) unavailable from every node that answered
74
+ the inbound addresses" (before broadcast: "cannot read THORChain height (/thorchain/lastblock) before broadcast from
75
+ any node that answered the inbound addresses"). Worst case without a caller signal: each `nodeUrl` host takes at most
76
+ 8 s for the quote (16 s when a quote without a slippage is retried with a wider tolerance) plus 8 s for its snapshot,
77
+ and the host that quotes is confirmed within the 8 s that started when its quote was in hand: hosts × 16 s, or hosts ×
78
+ 24 s with the retry (32 s / 48 s with the two default hosts); `preflightDeposit` hosts × 8 s. A caller's AbortSignal
79
+ (the router passes one: 8 s per provider, 25 s when it is the only one) replaces the 8 s bound of the quote request;
80
+ when it aborts, the quote request, the quoting node's `lastblock` read and the confirming reads stop at once, and its
81
+ `inbound_addresses` read keeps its own 8 s.
82
+
83
+ **Requests a confirming node gets** (`THORNODE_CONFIRM_PATHS`), all `GET`, no query string, no body, headers
84
+ `accept` and `x-client-id` only. A proxy in front of a confirming node can pass exactly these four and refuse
85
+ anything else:
86
+
87
+ | Path | When |
88
+ | --- | --- |
89
+ | `/thorchain/lastblock` | every confirmation |
90
+ | `/thorchain/inbound_addresses` | every confirmation |
91
+ | `/thorchain/vaults/asgard` | when the node's current inbound is not the quoted vault |
92
+ | `/thorchain/mimir` | when the node's current inbound is not the quoted vault |
93
+
94
+ The quoting node (`nodeUrl`) gets `/thorchain/quote/swap?from_asset=…&to_asset=…&amount=…&destination=…` with
95
+ `tolerance_bps` or `liquidity_tolerance_bps`, `streaming_quantity=1` and, when set, `refund_address`, `affiliate`,
96
+ `affiliate_bps`; then `/thorchain/inbound_addresses` and `GET /thorchain/lastblock` (no query string, so a proxy in
97
+ front of a `nodeUrl` must pass it too); `trackIntent` reads `/thorchain/tx/status/<HASH>` and `/thorchain/tx/<HASH>`.
98
+ The quote's `inbound_addresses` read and the quote itself ask for `cache: "no-store"`, which React Native's fetch
99
+ sends as an extra `?_=<time>` query; `lastblock` and the confirming reads set no cache option.
100
+
31
101
  Behind `remoteProvider`, the app checks the backend's build itself: `remoteProvider({name: "thorchain", baseUrl,
32
102
  ...thorchainPins()})` pins the routers and runs `verifyBuild` on the client — the vault in the router call confirmed
33
103
  by two nodes, the source asset and exact input, and the memo's destination, refund and limit against the app's own
34
104
  request (pass the backend's `affiliate` / `affiliateBps`). The pins open the stub to the Tron deposit route
35
105
  (`depositAddressRoutes`): its vault is the one `verifyBuild` confirms.
36
106
 
107
+ `verifyBuild` confirms the vault by the same rule as the provider's confirming nodes, so an inbound rotation between
108
+ the backend's quote and the device's check does not refuse: current inbound or a non-flagged `ActiveVault` (never
109
+ `RetiringVault` or another status), both chains open, on EVM the node's router equal to the call's. It asks every
110
+ `nodeUrl` node (default: the two public nodes) in parallel on the four `THORNODE_CONFIRM_PATHS` (the same GETs, so the
111
+ same proxy rules), each within `confirmTimeoutMs` (default 2500 ms, so the check takes at most that); a node that
112
+ fails, redirects, answers another shape (a field of another type included, as above), is slower, or trails the
113
+ highest THORChain height the nodes report by more than `confirmMaxLagBlocks` (default 50) is skipped. In `nodeUrl`
114
+ order a node's refusal refuses and two confirming nodes pass. Refusals: "a THORNode reports another inbound vault
115
+ than `<vault>`", "a THORNode reports another router",
116
+ "`<CHAIN>` is halted / paused" (now also the destination chain), "a THORNode lists the inbound vault as `<Status>`, not
117
+ ActiveVault", "a THORNode's mimir flags the inbound vault as compromised" (or `insolvent`), "two THORNodes within 50
118
+ blocks of the highest THORChain height they report could not confirm the inbound vault (behind or unreachable)", "two
119
+ THORNodes could not confirm the inbound vault".
120
+
37
121
  ## Activity
38
122
 
39
123
  Routers and Asgard vaults rotate, so `describeActivity(tx, ctx)` matches no