Repository Public
1# Maya Protocol (MAYAChain) — execution dossier23Status in this phase: **price discovery only** (`executability: quote_only`, `atomic: false`). Adapter:4`packages/swap/src/providers/maya.ts` (configuration) on the engine of `thorchain.ts`. MAYAChain is a fork of THORChain:5same quote endpoint shape, same 1e8 fixed point, same memo grammar, same vault model. **Read `thorchain.md` first**; this6file states what was verified for Maya on **2026-11-10 (2026-11-11 UTC)**, MAYANode `1.135.4`, and what differs.78## 1. Sources910| What | URL |11|---|---|12| Endpoints | https://docs.mayaprotocol.com/mayachain-dev-docs/concepts/connecting-to-mayachain |13| Swap quickstart | https://docs.mayaprotocol.com/mayachain-dev-docs/introduction/swapping-guide/quickstart-guide |14| Streaming swaps | https://docs.mayaprotocol.com/mayachain-dev-docs/introduction/swapping-guide/streaming-swaps |15| Sending transactions | https://docs.mayaprotocol.com/mayachain-dev-docs/concepts/sending-transactions |16| Memos | https://docs.mayaprotocol.com/mayachain-dev-docs/concepts/transaction-memos |17| Quote implementation | https://gitlab.com/mayachain/mayanode/-/blob/develop/x/mayachain/querier_quotes.go |18| Limit enforcement | https://gitlab.com/mayachain/mayanode/-/blob/develop/x/mayachain/swap_current.go |19| Short codes | https://gitlab.com/mayachain/mayanode/-/blob/develop/common/asset.go |20| EVM router source | https://gitlab.com/mayachain/ethereum/eth-router/-/blob/master/contracts/MAYAChain_Router.sol |2122Endpoints: the docs list `https://mayanode.mayachain.info/mayachain` (pinned by the adapter), Midgard23`https://midgard.mayachain.info/v2`, Tendermint `https://tendermint.mayachain.info`. A second, independently hosted MAYANode24answers at `https://api-maya.liquify.com/mayachain` (same version, same routers; not in the docs). No key. No published rate25limit, only "Careful querying this too much - you could overload the public nodes". No terms-of-use page found in the docs.26Measured: 150–620 ms per quote, no failure in about 60 calls.2728## 2. What is served2930Chain → wallet network: `BTC` bitcoin, `ETH` ethereum, `ARB` arbitrum. (Maya's other chains — `DASH`, `ZEC`, `ADA`, `THOR`31(RUNE), `MAYA` (CACAO) — have no wallet network. Mimir still carries keys for chains it does not run pools for.)3233`GET /mayachain/pools`: 25 pools, 18 `Available`. In the wallet registry by chain + contract: **BTC, ETH, Arbitrum ETH,34Ethereum USDC and USDT, Arbitrum USDC** (6 assets). `ARB.USDT` and `ARB.WBTC` are Available but not in the registry;35`ETH.PEPE`, `ARB.ARB`, `ARB.DAI` are `Staged` (refused: `pool(ETH.PEPE-…) is not available`).36No DOGE, LTC, BCH, SOL, XRP, TRX, ATOM: for those pairs the adapter says "unsupported" without any network call.3738Halts: `GET /mayachain/inbound_addresses` has `halted` per chain only (no trading-pause flags). Same handling as THORChain:39read with each quote, 15 s cache, fail closed. Nothing was halted on the day.4041## 3. The quote — differences from THORChain4243`GET /mayachain/quote/swap?from_asset&to_asset&amount&destination&liquidity_tolerance_bps=<slippageBps>&streaming_interval=3&streaming_quantity=0`44451. **Errors are HTTP 200** with `{"error": "…"}` (THORNode: HTTP 400 `{code, message}`). Handled; e.g. `amount less than46 dust threshold`, `fail to add outbound tx … not enough asset to pay for fees` → no route.472. **Streaming must be asked for.** Maya has no advanced swap queue: without `streaming_interval` a large swap is quoted as48 a single swap. Measured for 2 BTC → ETH: about 39 ETH single (`slippage_bps` ≈ 2800) against about 71.6 ETH streamed (29 bps). The49 adapter therefore always sends `streaming_interval=3&streaming_quantity=0` (the network picks the count, minimum 5 bps of50 slip per sub-swap). For small amounts `max_streaming_quantity` is 1 and the swap is single; the memo still ends in `/3/0`.51 The interval is a free choice (3 blocks ≈ 18 s between sub-swaps); the docs give no recommendation. Streaming is reported52 in `steps[0].via[2]` and `raw.streaming`; 2 BTC → ETH: 193 sub-swaps, 58 min of streaming, 68 min in total.533. **The limit is enforced before the outbound fee.** The quote computes `LIM = floor(expected_amount_out × (10000 − bps) /54 10000)` from the amount *net* of the outbound fee (`querier_quotes.go`), but execution compares the *gross* swap output55 with it (`swap_current.go`: `if !swapTarget.IsZero() && assetAmount.LT(swapTarget)`), and the outbound fee is deducted56 afterwards. So what the recipient is guaranteed is `LIM − outbound fee`, not `LIM`. The adapter reports57 **`minAmountOut = LIM − fees.outbound`** (both from the quote; the outbound fee is dynamic, so this floor is itself an58 estimate) and no route when that is not positive. On a 40 USD BTC → ETH quote: expected 0.01971008 ETH, limit59 0.01961152, real floor 0.01956260. A signer that wants an exact floor must write its own, higher `LIM` in the memo.604. `expiry` = node wall clock + 15 min. `total_swap_seconds` is omitted when zero (Arbitrum → BTC): `estimatedSeconds: null`.615. Short codes in memos: `b` = BTC.BTC, `e` = ETH.ETH, `a` = ARB.ETH; tokens as `ETH.USDC`, `ARB.USDC`.6263Everything else (units, fee mapping, minimums, refusals, validation of memo recipient / asset / limit / router) is as in64`thorchain.md` §3.6566## 4. Transaction shape6768| Source | Shape | Memo carrier |69|---|---|---|70| BTC | vault output first, `OP_RETURN` with the memo, change back to the address of input 0. Maya's docs page lists the outputs as vault / memo / change, the quote `notes` say vault, change, then `OP_RETURN`: follow the quote `notes`. **"BTC Taproot is not supported. Funds sent to or from a Taproot address will be lost."** Segwit bech32 preferred. | `OP_RETURN`, 80 bytes |71| ETH, Arbitrum ETH | `router.depositWithExpiry(vault, 0x0000000000000000000000000000000000000000, amount, memo, expiry)` with `value = amount` (or, per the quote `notes`, a plain transfer with the memo hex-encoded in `data`) | string argument |72| ERC-20 (USDC, USDT) | `token.approve(router, amount)`, then `router.depositWithExpiry(vault, token, amount, memo, expiry)`; `expiry` = now + 60 min recommended. USDT on Ethereum needs its allowance reset to 0 before a new non-zero approval. | string argument |7374Routers named by the network (`router` in `/mayachain/inbound_addresses`, identical on both hosts): Ethereum75`0xe3985E6b61b814F7Cdb188766562ba71b446B46d`, Arbitrum `0x700E97ef07219440487840Dc472E7120A7FF11F4`. Pinned by the adapter,76which rejects any other router (including THORChain's). Not yet checked against verified explorer source.7778Dust thresholds on the day (1e8 units): BTC 10 000; ETH and ARB 0. An `=:e:0x…:1961152/3/0` memo is 58 bytes.7980## 5. Refunds8182Same rules as THORChain (`thorchain.md` §5): refund to the sender less the source-chain outbound fee; nothing back when the83amount is at or below dust, cannot pay the refund's outbound fee, is sent to a vault that is no longer active, or (BTC)84involves Taproot or a change output away from input 0. Streaming: "If the first attempt fails, the entire streaming swap is85refunded"; otherwise the swapped part is paid out and the rest of the input refunded (partial fill). `refundable: true` for86every quote returned (all are at or above `recommended_min_amount_in`).8788## 6. What the isolated signer could and could not verify8990Identical to `thorchain.md` §6, with two Maya specifics:9192- the minimum-output check must use `LIM − outbound fee` (point 3 above), or the signer must set `LIM` itself;93- two independently hosted public nodes exist, so a two-source check of the vault is possible today — as **set membership**94 in `GET /mayachain/vaults/asgard`, not equality: the two hosts named different active vaults at the same moment95 (`0xa2b7…ed2c` and `0x7df8…4f04`; BTC `bc1qhrha…` and `bc1q0ys0…`). Two hosts are still not a trust-minimised proof.9697The vault address remains unverifiable by the signer on its own; Maya's node set is also much smaller than THORChain's98(`DESIREDVALIDATORSET` 55, shallow pools), which weighs on the same risk.99100## 7. Tracking101102`GET /mayachain/tx/status/{hash}` (checked live), `GET /mayachain/tx/{hash}`,103`GET https://midgard.mayachain.info/v2/actions?txid={hash}`.104105## 8. Execution: not enabled (v6, 2026-11-11)106107THORChain swaps are now executed by the wallet (`thorchain.md` §9–§13). The same code would cover Maya — same memo grammar,108same vault model — but the defence it rests on does not exist for Maya today: the signer signs only when **at least three109independently operated nodes** confirm the vault, and on 2026-11-11 only **two** keyless public MAYANode operators answered110(`mayanode.mayachain.info`, also the only entry of the Cosmos chain registry, and `api-maya.liquify.com`;111`midgard.mayachain.info` proxies the first one and is not independent; `gateway.liquify.com/chain/maya_api` answers112"Chain not publicly accessible"; no THORWallet, Rorcual, IBS or Keplr node for Maya was found). With two operators a single113compromise plus one outage, or one actor behind both, is enough to name a false vault. Lowering the quorum for Maya would be114a weaker design than the one THORChain swaps ship with, so Maya stays `quote_only`.115116What enabling it would take: a third and preferably fourth independent MAYANode (or a self-hosted one), then a Maya table117in `packages/core/src/thorchain.ts` (pools, short codes, routers with bytecode hashes after reading their sources, the118`mayapub` key prefix), and two differences handled explicitly: the memo limit is enforced **before** the outbound fee on119Maya (§3 above), so the approved minimum must be `limit − outbound fee`; and Maya streams only when asked.120121