Rupees and the INR gateway
The vault, the loans and every split are in USDC. Rupees exist only at the edges, where the INR gateway converts them. There is no rupee balance on-chain, so who actually holds the rupees?
The gateway is an exchange counter with two drawers
| Drawer | Where | What happens to it |
|---|---|---|
| USDC float | On-chain, in inr.runway-rama.testnet. Anyone can see it. | Goes down when someone pays in rupees; goes up when someone is paid out in rupees. |
| INR treasury | Off-chain: Runway's Razorpay / RazorpayX / bank balance. | Goes up when someone pays in rupees; goes down when someone is paid out in rupees. |
So rupees in and rupees out balance each other over time. If one drawer runs low, Runway's operations team rebalances off-chain (for example, sells rupees for USDC on an Indian exchange and adds it to the float with FundFloat). On testnet the float was seeded with 5,000 mock USDC and the mock provider moves no real rupees; every on-chain step is still real.
Who converts, step by step
- The customer pays ₹ by UPI to a QR the gateway created. The rupees land in Runway's provider account.
- The provider notifies the gateway agent (it runs in a secure enclave). The agent checks the webhook's signature and fetches the payment again from the provider's API.
- It calls
record_payinwith the live rate. The contract checks: the agent's role, the rate band (₹70–₹130), the per-payment cap (₹1,00,000), the daily cap (₹10,00,000), the float, and that this payment was never recorded before. - The contract sends USDC from the float to the target and stores a receipt on-chain.
The exact formulas
Rates are in paise per 1 USDC (₹100.00 = 10000). USDC is counted in millionths. Results are rounded down.
The gateway keeps a 0.5% spread on each conversion; that is its income and covers FX risk. A full rupee round trip (₹ in, ₹ out) costs about 1%. Try your own amounts at the live rate in the rupee converter.
The six rupee flows
| Flow | Path | Float | Treasury |
|---|---|---|---|
| UPI sale | customer ₹ → USDC to the merchant's settlement account → split as usual | − | + |
| Merchant paid in ₹ | merchant's share → gateway → ₹ to their UPI ID | + | − |
| Loan in ₹ | vault → gateway → ₹ to the merchant (for merchants in ₹ mode) | + | − |
| Lender deposits ₹ | investor ₹ → USDC into the vault for them → shares minted | − | + |
| Lender withdraws to ₹ | shares' USDC value → gateway → ₹ to their UPI ID | + | − |
| Repay in ₹ | merchant ₹ → USDC straight to their loan | − | + |
Example: a merchant who sells in ₹ and is paid in ₹
| Step | Float | Treasury | Merchant's bank |
|---|---|---|---|
| Start | 5,000.00 | ₹5,00,000 | ₹0 |
| Customer pays ₹10,000 by UPI → 99.50 USDC to the settlement account | 4,900.50 | ₹5,10,000 | ₹0 |
| Settled, no loan: 99.50 USDC → gateway → payout | 5,000.00 | ₹5,00,099.75 | ₹9,900.25 |
The float is back where it started, the merchant received ₹9,900.25 for a ₹10,000 sale, and the gateway kept ₹99.75 (two spreads).
What is recorded on-chain
- Every pay-in receipt: ₹ amount, rate, USDC delivered, a hash of the payer (no UPI ID), status.
- Every payout: USDC, ₹ amount, rate, a hash of the bank reference (UTR), status.
- Payout details: only as ciphertext that the gateway enclave can decrypt. The site shows you a masked label like
n***@okaxis.
See them for any merchant under “INR rail” on the Audit page.
When something goes wrong
- The float is too low for a payment: new UPI QRs are refused. Crypto keeps working.
- A payout can't be sent (wrong or missing UPI ID): it fails and the USDC is refunded to the person's NEAR account. If even that fails, it becomes a refund they can claim from the gateway.
- The price feeds disagree: the oracle needs at least two exchange prices close together, so pay-ins and payouts pause instead of using a bad rate.