OpenAPI Specification
openapi: 3.0.1
info:
title: liquidity-api Order Permit API
version: ''
description: This API provides quotes for moving and converting between M extensions and other stablecoins
servers:
- url: https://gateway.m0.xyz/v1/orchestration
description: mainnet
- url: https://gateway.stage.m0.xyz/v1/orchestration
description: stage
- url: https://gateway.dev.m0.xyz/v1/orchestration
description: devnet
- url: http://localhost:5600
description: localnet
tags:
- name: Permit
paths:
/permit/quote:
post:
description: 'Get quotes for the gasless-approval (EIP-2612 permit) flow.
Like `/quote`, but every returned quote''s approvals are satisfied by
signing an off-chain EIP-2612 permit instead of broadcasting a separate
`approve` transaction — collapsing approve + action into a single
on-chain transaction. **EVM only.**
Each `PermitQuote` carries one `PermitRecipe` per leg (in execution
order); sign each recipe''s `typedData`, then either POST the signed
recipes to `/permit/build` to receive executable payloads, or encode
the `*WithPermit` call locally from the recipe.
Only routes whose every leg is permit-capable are returned.
**Error responses**
All errors return `commons.ErrorBody` (`{ code, message, requestId }`).
Branch on `code`, not the HTTP status.
| Status | `code` | When |
| ------ | ------------------- | ----------------------------------------------------------------------- |
| 400 | `BadPermitRequest` | Missing/invalid fields, `source` == `destination`, or an unknown asset. |
| 404 | `PermitNotSupported`| Structurally valid, but no permit-capable path exists for this route. |
| 500 | `PermitQuoteError` | Unexpected internal failure while computing permit quotes. |'
operationId: permit_permitQuote
tags:
- Permit
parameters: []
responses:
'200':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/PermitQuoteResponse'
'400':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorBody'
'404':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorBody'
'500':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorBody'
security:
- ApiKeyAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
route:
$ref: '#/components/schemas/Route'
amountIn:
type: string
sender:
type: string
funder:
type: string
nullable: true
description: 'The account that holds the input tokens and signs the permit.
Defaults to `sender` when omitted.'
recipient:
type: string
nullable: true
maxNumQuotes:
type: integer
nullable: true
providers:
$ref: '#/components/schemas/ProvidersParams'
nullable: true
required:
- route
- amountIn
- sender
/permit/build:
post:
description: 'Exchange signed permit recipes for executable transaction payloads.
Echo back the `PermitRecipe`s from `/permit/quote`, each paired with
the user''s EIP-2612 signature over its `typedData`. Returns one
`Payload` per recipe, in order — the encoded `*WithPermit` call.
The server re-validates each recipe before encoding (permit `value`
matches `amountIn`, `spender` matches the operation target, `deadline`
not expired, and the recovered signer equals the permit `owner`).
**Error responses**
All errors return `commons.ErrorBody` (`{ code, message, requestId }`).
Branch on `code`, not the HTTP status.
| Status | `code` | When |
| ------ | ------------------------ | ------------------------------------------------------------- |
| 400 | `BadPermitRequest` | Malformed body, or a recipe that fails internal consistency. |
| 410 | `PermitExpired` | A recipe''s permit `deadline` has already passed. |
| 422 | `InvalidPermitSignature` | Signature does not recover to the permit `owner`. |
| 500 | `PermitBuildError` | Unexpected internal failure while encoding the payloads. |'
operationId: permit_permitBuild
tags:
- Permit
parameters: []
responses:
'200':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/PermitBuildResponse'
'400':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorBody'
'410':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorBody'
'422':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorBody'
'500':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorBody'
security:
- ApiKeyAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
signedRecipes:
type: array
items:
$ref: '#/components/schemas/SignedPermitRecipe'
required:
- signedRecipes
components:
schemas:
Chain:
title: Chain
type: string
enum:
- Ethereum
- Solana
- Arbitrum
- Base
- Linea
- Sepolia
- BaseSepolia
- ArbitrumSepolia
- HyperEVM
- BinanceSmartChain
- Mantra
- Plasma
- SolanaDevnet
- Soneium
- Citrea
- Fluent
- Moca
- Rise
SwapPermitOp:
title: SwapPermitOp
type: object
description: M Swap Facility `swap*WithPermit`.
properties:
method:
$ref: '#/components/schemas/SwapMethod'
extensionIn:
type: string
nullable: true
extensionOut:
type: string
nullable: true
amount:
type: string
recipient:
type: string
deadline:
type: string
required:
- method
- amount
- recipient
- deadline
PermitIssues:
title: PermitIssues
type: object
description: Soft warnings; their presence does not fail the request.
properties:
balance:
$ref: '#/components/schemas/BalanceIssue'
nullable: true
description: Present when `sender` does not hold `amountIn` of the input token.
SvmPayload:
title: SvmPayload
type: object
properties:
chain:
$ref: '#/components/schemas/Chain'
transaction:
type: string
format: byte
required:
- chain
- transaction
ProvidersParams:
title: ProvidersParams
type: object
properties:
include:
type: array
items:
$ref: '#/components/schemas/Providers'
nullable: true
exclude:
type: array
items:
$ref: '#/components/schemas/Providers'
nullable: true
BalanceIssue:
title: BalanceIssue
type: object
properties:
token:
type: string
actual:
type: string
expected:
type: string
required:
- token
- actual
- expected
Payload:
title: Payload
type: object
properties:
provider:
type: string
annotation:
type: string
nullable: true
data:
$ref: '#/components/schemas/TransactionPayload'
solver:
$ref: '#/components/schemas/PayloadSolver'
nullable: true
description: 'Present only on limit-order legs. Identifies the solver that produced
this leg, as reported to the server over gRPC.'
required:
- provider
- data
EvmPayload:
title: EvmPayload
type: object
properties:
chain:
$ref: '#/components/schemas/Chain'
chainId:
type: integer
to:
type: string
data:
type: string
value:
type: string
required:
- chain
- chainId
- to
- data
- value
Eip2612Message:
title: Eip2612Message
type: object
properties:
owner:
type: string
spender:
type: string
value:
type: string
nonce:
type: string
deadline:
type: string
required:
- owner
- spender
- value
- nonce
- deadline
Route:
title: Route
type: object
properties:
source:
$ref: '#/components/schemas/AssetAddress'
destination:
$ref: '#/components/schemas/AssetAddress'
required:
- source
- destination
Eip712Domain:
title: Eip712Domain
type: object
properties:
name:
type: string
version:
type: string
chainId:
type: integer
verifyingContract:
type: string
description: The input token's address.
required:
- name
- version
- chainId
- verifyingContract
ErrorBody:
title: ErrorBody
type: object
description: 'Standard error response body. Every error response from this API
returns this shape.'
properties:
code:
$ref: '#/components/schemas/ErrorCode'
description: Stable machine-readable identifier for the failure mode.
message:
type: string
description: Human-readable description of what went wrong. Safe to surface in UI.
requestId:
type: string
nullable: true
description: 'Per-request correlation id (matches the `requestId` field in
server logs). Include this when filing support tickets.'
required:
- code
- message
TransactionPayload:
title: TransactionPayload
oneOf:
- type: object
allOf:
- type: object
properties:
type:
type: string
enum:
- evm
- $ref: '#/components/schemas/EvmPayload'
required:
- type
- type: object
allOf:
- type: object
properties:
type:
type: string
enum:
- svm
- $ref: '#/components/schemas/SvmPayload'
required:
- type
PayloadSolver:
title: PayloadSolver
type: object
description: 'The solver behind a limit-order leg. `exclusive` means the leg is bound to
this solver on-chain (it becomes the order''s `designatedSolver`); when
false, the quote reflects this solver''s price but any solver may fill.'
properties:
address:
type: string
name:
type: string
nullable: true
exclusive:
type: boolean
feeBps:
type: integer
nullable: true
required:
- address
- exclusive
SignedPermitRecipe:
title: SignedPermitRecipe
type: object
properties:
recipe:
$ref: '#/components/schemas/PermitRecipe'
description: Echoed verbatim from `/permit/quote`.
signature:
type: string
description: 65-byte hex EIP-2612 permit signature. Build splits to v,r,s where the contract needs it.
required:
- recipe
- signature
LimitOrderPermitOp:
title: LimitOrderPermitOp
type: object
description: OrderBook `openOrderWithPermit`.
properties:
destChainId:
type: integer
fillDeadline:
type: integer
tokenIn:
type: string
tokenOut:
type: string
description: bytes32-encoded destination token.
amountIn:
type: string
amountOut:
type: string
recipient:
type: string
description: bytes32-encoded recipient.
solver:
type: string
description: bytes32-encoded designated solver (zero for an open fill).
sender:
type: string
deadline:
type: string
required:
- destChainId
- fillDeadline
- tokenIn
- tokenOut
- amountIn
- amountOut
- recipient
- solver
- sender
- deadline
PermitQuoteResponse:
title: PermitQuoteResponse
type: object
properties:
requestId:
type: string
description: Per-request correlation id.
quotes:
type: array
items:
$ref: '#/components/schemas/PermitQuote'
issues:
$ref: '#/components/schemas/PermitIssues'
nullable: true
description: Non-fatal findings shared across all quotes (e.g. insufficient balance).
required:
- requestId
- quotes
AmmSwapPermitOp:
title: AmmSwapPermitOp
type: object
description: Uniswap V3 `multicall([selfPermit, exactInputSingle])`. Signature split to v,r,s.
properties:
tokenIn:
type: string
tokenOut:
type: string
fee:
type: integer
recipient:
type: string
amountIn:
type: string
amountOutMinimum:
type: string
deadline:
type: string
required:
- tokenIn
- tokenOut
- fee
- recipient
- amountIn
- amountOutMinimum
- deadline
PermitRecipe:
title: PermitRecipe
type: object
description: 'Everything needed to sign one leg''s permit and produce its executable
transaction — either by POSTing to `/permit/build` or by encoding the
`*WithPermit` call locally (self-encoding).'
properties:
provider:
$ref: '#/components/schemas/Providers'
annotation:
type: string
description: 'Human-readable label for this leg, e.g. `M0 Swap Facility wM → USD+`.
For display in clients.'
chain:
$ref: '#/components/schemas/Chain'
chainId:
type: integer
spender:
type: string
description: Contract granted the permit and called in `/permit/build` (the tx `to`).
value:
type: string
description: Native value for the final transaction. "0" for the permit-capable providers.
typedData:
$ref: '#/components/schemas/Eip2612TypedData'
solver:
$ref: '#/components/schemas/PayloadSolver'
nullable: true
description: 'Present only on limit-order legs. Identifies the solver that produced
this leg, mirroring `Payload.solver` on `/quote`. For display.'
operation:
$ref: '#/components/schemas/PermitOperation'
description: Static args the build step encodes into the `*WithPermit` call.
functionSignature:
type: string
description: 'Solidity signature of the `*WithPermit` call this recipe encodes to,
e.g. `swapWithPermit(address,address,uint256,address,uint256,bytes)`.
Lets a client encode the transaction locally — from `operation` plus
the signature — instead of calling `/permit/build`.'
required:
- provider
- annotation
- chain
- chainId
- spender
- value
- typedData
- operation
- functionSignature
Providers:
title: Providers
type: string
enum:
- m-swap-facility
- m-wormhole-portal
- m-hyperlane-portal
- m-portal
- wormhole-cctp
- jupiter
- raydium
- mayan-swift
- kyber-swap
- limit-order
- uniswap-v3
PermitOperation:
title: PermitOperation
oneOf:
- type: object
allOf:
- type: object
properties:
type:
type: string
enum:
- swap
- $ref: '#/components/schemas/SwapPermitOp'
required:
- type
- type: object
allOf:
- type: object
properties:
type:
type: string
enum:
- limitOrder
- $ref: '#/components/schemas/LimitOrderPermitOp'
required:
- type
- type: object
allOf:
- type: object
properties:
type:
type: string
enum:
- ammSwap
- $ref: '#/components/schemas/AmmSwapPermitOp'
required:
- type
description: 'Provider-specific static args for the `*WithPermit` call, discriminated
so the build step (or a self-encoding client) knows exactly what to build.'
PermitBuildResponse:
title: PermitBuildResponse
type: object
properties:
requestId:
type: string
payloads:
type: array
items:
$ref: '#/components/schemas/Payload'
issues:
$ref: '#/components/schemas/PermitIssues'
nullable: true
required:
- requestId
- payloads
SwapMethod:
title: SwapMethod
type: string
enum:
- swap
- swapInM
- swapOutM
Eip2612TypedData:
title: Eip2612TypedData
type: object
description: 'Directly signable EIP-712 payload. `types`/`primaryType` are the standard
EIP-2612 `Permit` definition and are omitted; clients use the canonical set.'
properties:
domain:
$ref: '#/components/schemas/Eip712Domain'
message:
$ref: '#/components/schemas/Eip2612Message'
required:
- domain
- message
PermitQuote:
title: PermitQuote
type: object
description: A quote whose approvals are satisfied by signing permits, not `approve`.
properties:
route:
$ref: '#/components/schemas/Route'
recipient:
type: string
amountIn:
type: string
amountOut:
type: string
minAmountOut:
type: string
description: 'Floor that must be met or the transaction reverts. Equals
`amountOut` for exact-output providers; below it for AMM legs
per `slippageBps`.'
estFillTime:
type: integer
expiresAt:
type: integer
description: 'Unix seconds after which this quote is no longer buildable — the
earliest permit `deadline` across its recipes. `/permit/build`
rejects with `PermitExpired` past this.'
recipes:
type: array
items:
$ref: '#/components/schemas/PermitRecipe'
description: One per leg, in execution order.
required:
- route
- recipient
- amountIn
- amountOut
- minAmountOut
- estFillTime
- expiresAt
- recipes
AssetAddress:
title: AssetAddress
type: object
properties:
chain:
$ref: '#/components/schemas/Chain'
address:
type: string
required:
- chain
- address
ErrorCode:
title: ErrorCode
type: string
enum:
- QuoteError
- BadQuoteRequest
- NoQuotesAvailable
- BadOrderRequest
- OrderNotFound
- OrderNotCancellable
- CancelOrderError
- BadTopologyRequest
- BadPermitRequest
- PermitNotSupported
- PermitExpired
- InvalidPermitSignature
- PermitQuoteError
- PermitBuildError
description: 'Stable machine-readable identifier for an API error. Each value
corresponds 1:1 to an error type (and to one `responses` entry
in the OpenAPI spec). Branch on this rather than on the HTTP status
code — multiple errors may share a status as the API evolves.'
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: x-api-key