{
"error": "Unauthorized",
"message": "permit signature does not match the partner address"
}
{
"error": "Forbidden",
"message": "address is not a permitted partner"
}
{
"error": "Payload Too Large",
"message": "request entity too large"
}
{
"error": "Too Many Requests",
"message": "Rate limit exceeded. Try again in 7s."
}
{
"error": "Internal Server Error",
"message": "Internal Server Error"
}
Orders
One-time onboarding (gasless)
A one-time setup step, done gaslessly. Sign two EIP-712 messages offline, post the signatures here, and we relay them on chain and pay for it — the partner never sends a transaction or needs ETH.
Restricted to allowlisted partner addresses. Contact us before integrating.
Send whichever parts you still need; at least one. They are relayed as a single atomic batch, so you are never left with an allowance but no delegate. The response reads both values back from chain, so state.missing reflects what is actually set rather than what we submitted.
The two signatures
delegation lets the OstiumAtomicTrading wrapper place trades for you.
The verifying contract is Trading, but delegate is the wrapper — it is the contract that will call Trading for you, so it is what Trading checks. Sending anything else is rejected.
spender is TradingStorage, not Trading — different contracts.
Read name and version from the token itself; they differ between deployments and a wrong domain produces a signature that fails on chain.
Both nonces come from the chain at signing time — Trading.delegatableNonces(partner) and USDC.nonces(partner).
Addresses
Arbitrum (42161)
Arbitrum Sepolia (421614)
Trading
0x6D0bA1f9996DBD8885827e1b2e8f6593e7702411
0x2A9B9c988393f46a2537B0ff11E98c2C15a95afe
TradingStorage (permit spender)
0xccd5891083a8acd2074690f65d3024e7d13d66e7
0x0b9F5243B29938668c9Cfbd7557A389EC7Ef88b8
USDC
0xaf88d065e77c8cc2239327c5edb3a432268e5831
0xe73B11Fb1e3eeEe8AF2a23079A4410Fe1B370548
OstiumAtomicTrading (delegate)
0x5eB3960C3fd3274cD81fE5972e0de01084bDa325
0x32C06a3eC2A40DABf6A8f645f29321cB7236DAA3
Two cases where you send only one half
On Arbitrum Sepolia there is no permit. The test token is a plain ERC-20 with no permit or nonces, so no permit signature exists for it. Send approve(<TradingStorage>, <amount>) from the wallet, then post delegation alone. Mainnet USDC does implement ERC-2612.
A contract wallet can permit but not delegate.setDelegateWithSignature uses ecrecover, which no EIP-1271 signature satisfies. Call Trading.setDelegate(<OstiumAtomicTrading>) from the wallet, then post permit alone.
Notes
Keep expiry and deadline short — 30 minutes is plenty. Until one passes, anyone holding the signature can relay it. They cannot redirect it: delegate, spender and value are all inside the signed bytes.
Numbers are decimal strings, never JSON numbers. A uint256 nonce or a max allowance exceeds Number.MAX_SAFE_INTEGER, and a rounded value no longer matches what you signed.
Rate limit: 10 requests per 10 seconds per IP. Read x-ratelimit-* for the live budget rather than assuming this figure.
Your onboarding state, read back from chain rather than assumed from what was submitted. Absent if that read failed; the outcome above still stands.
Show child attributes
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Ostium","colors":{"primary":"#FF5A19","light":"#FF5A19","dark":"#FF5A19"},"logo":"https://mintcdn.com/ostium/rgKhfmKxgkO9O2aD/logo/ostium-logo.svg?fit=max\u0026auto=format\u0026n=rgKhfmKxgkO9O2aD\u0026q=85\u0026s=e2316b63df8fab1bbdb3c18daeff80e5","favicon":"https://mintcdn.com/ostium/PYRmOzvp-N2ht7oL/favicon.svg?fit=max\u0026auto=format\u0026n=PYRmOzvp-N2ht7oL\u0026q=85\u0026s=79bfadbe417502d9aec3c62a2a59d62e","navbar":{"links":[{"href":"https://discord.com/invite/ostiumlabs","label":"Support"}],"primary":{"type":"button","label":"Trade Now","href":"https://app.ostium.com"}},"footer":{"socials":{"x":"https://x.com/Ostium","github":"https://github.com/ostium-labs","linkedin":"https://linkedin.com/company/ostium"},"links":[{"header":"Legal","items":[{"label":"Terms of Use","href":"/legal/terms-of-use"},{"label":"Privacy Policy","href":"/legal/privacy-policy"},{"label":"Nasdaq Subscriber Agreement","href":"/legal/nasdaq-subscriber-agreement"},{"label":"OLP Recovery Plan Terms","href":"/legal/olp-recovery-plan-terms"}]}]},"contextual":{"options":["copy","view","chatgpt","claude","perplexity","mcp","cursor","vscode"],"display":"header"},"styling":{"latex":true},"navigation":{"global":{"anchors":[{"anchor":"Website","icon":"globe","href":"https://ostium.com"},{"anchor":"Developers","icon":"code","href":"/developer/sdk/overview"},{"anchor":"Blog","icon":"newspaper","href":"https://www.ostium.com/blog"},{"anchor":"X/Twitter","icon":"twitter","href":"https://www.x.com/Ostium"}]},"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"ostium","hasAudioNarration":false,"actualSubdomain":"ostium","customDomains":["docs.ostium.com"],"gitSource":{"type":"github","owner":"0xostium","repo":"new-docs","deployBranch":"master","contentDirectory":"","isPrivate":true,"mountPath":"","label":""},"chroma":{"liveDeploymentHistoryId":"6acc8e98e4af841eed2f1868","collectionId":"2ac89999-3290-4b13-920e-1ca75a2be88f","schemaVersion":"v3","latestBuiltDeploymentHistoryId":"6acc8e98e4af841eed2f1868"},"entitlements":{"AI_CHAT":{"status":"DISABLED"}},"buildId":"6acc8e98e4af841eed2f1868","clientVersion":"0.0.3966","searchFilterCounts":[{"language":"default","tag":"Developers","count":63},{"language":"default","tag":"Read Methods","count":17},{"language":"default","tag":"API Reference","count":16},{"language":"default","tag":"Traders","count":13},{"language":"default","tag":"Helpers","count":10},{"language":"default","tag":"Client Modes","count":8},{"language":"default","tag":"Builder API","count":7},{"language":"default","tag":"Trading","count":6},{"language":"default","tag":"Write Methods","count":5},{"language":"default","tag":"Previews \u0026 Limits","count":5}]},"assistantConfig":{},"hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-v7-file-tree","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"pause-universal-search-reconcile","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"per-site-search-delta","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-private-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-nav-generations","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-agent-model-picker","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-updated-9-15","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"isAtlas":false,"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"api-reference/orders/one-time-onboarding-gasless","slug":"/api-reference/orders/one-time-onboarding-gasless","pageMetadata":{"title":"One-time onboarding (gasless)","description":"A one-time setup step, done gaslessly. Sign two EIP-712 messages offline, post the signatures here, and we relay them on chain and pay for it — the partner never sends a transaction or needs ETH.\n\nRestricted to allowlisted partner addresses. Contact us before integrating.\n\nSend whichever parts you still need; at least one. They are relayed as a single atomic batch, so you are never left with an allowance but no delegate. The response reads both values back from chain, so `state.missing` reflects what is actually set rather than what we submitted.\n\n### The two signatures\n\n**`delegation`** lets the OstiumAtomicTrading wrapper place trades for you.\n\n- Domain: `{ name: \"Ostium\", version: \"1\", chainId, verifyingContract: \u003cTrading> }`\n- Type: `OstiumDelegation(address delegator, address delegate, uint256 nonce, uint256 expiry)`\n- The verifying contract is **Trading**, but `delegate` is the **wrapper** — it is the contract that will call Trading for you, so it is what Trading checks. Sending anything else is rejected.\n\n**`permit`** grants the USDC allowance (ERC-2612).\n\n- Domain: `{ name, version, chainId, verifyingContract: \u003cUSDC> }`\n- Type: `Permit(address owner, address spender, uint256 value, uint256 nonce, uint256 deadline)`\n- `spender` is **TradingStorage**, not Trading — different contracts.\n- Read `name` and `version` from the token itself; they differ between deployments and a wrong domain produces a signature that fails on chain.\n\nBoth nonces come from the chain at signing time — `Trading.delegatableNonces(partner)` and `USDC.nonces(partner)`.\n\n### Addresses\n\n| | Arbitrum (42161) | Arbitrum Sepolia (421614) |\n| --- | --- | --- |\n| Trading | `0x6D0bA1f9996DBD8885827e1b2e8f6593e7702411` | `0x2A9B9c988393f46a2537B0ff11E98c2C15a95afe` |\n| TradingStorage (permit spender) | `0xccd5891083a8acd2074690f65d3024e7d13d66e7` | `0x0b9F5243B29938668c9Cfbd7557A389EC7Ef88b8` |\n| USDC | `0xaf88d065e77c8cc2239327c5edb3a432268e5831` | `0xe73B11Fb1e3eeEe8AF2a23079A4410Fe1B370548` |\n| OstiumAtomicTrading (delegate) | `0x5eB3960C3fd3274cD81fE5972e0de01084bDa325` | `0x32C06a3eC2A40DABf6A8f645f29321cB7236DAA3` |\n\n### Two cases where you send only one half\n\n**On Arbitrum Sepolia there is no permit.** The test token is a plain ERC-20 with no `permit` or `nonces`, so no permit signature exists for it. Send `approve(\u003cTradingStorage>, \u003camount>)` from the wallet, then post `delegation` alone. Mainnet USDC does implement ERC-2612.\n\n**A contract wallet can permit but not delegate.** `setDelegateWithSignature` uses `ecrecover`, which no EIP-1271 signature satisfies. Call `Trading.setDelegate(\u003cOstiumAtomicTrading>)` from the wallet, then post `permit` alone.\n\n### Notes\n\n- **Keep `expiry` and `deadline` short** — 30 minutes is plenty. Until one passes, anyone holding the signature can relay it. They cannot redirect it: `delegate`, `spender` and `value` are all inside the signed bytes.\n- **Numbers are decimal strings**, never JSON numbers. A uint256 nonce or a max allowance exceeds `Number.MAX_SAFE_INTEGER`, and a rounded value no longer matches what you signed.\n\nRate limit: 10 requests per 10 seconds per IP. Read `x-ratelimit-*` for the live budget rather than assuming this figure.","deprecated":null,"version":null,"openapi":"/api-reference/openapi.json post /v1/onboard","href":"/api-reference/orders/one-time-onboarding-gasless","autogeneratedByOpenApi":true},"description":{"compiledSource":"\"use strict\";\nconst {Fragment: _Fragment, jsx: _jsx, jsxs: _jsxs} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n code: \"code\",\n h3: \"h3\",\n li: \"li\",\n p: \"p\",\n strong: \"strong\",\n table: \"table\",\n tbody: \"tbody\",\n td: \"td\",\n th: \"th\",\n thead: \"thead\",\n tr: \"tr\",\n ul: \"ul\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsxs(_Fragment, {\n children: [_jsx(_components.p, {\n children: \"A one-time setup step, done gaslessly. Sign two EIP-712 messages offline, post the signatures here, and we relay them on chain and pay for it — the partner never sends a transaction or needs ETH.\"\n }), \"\\n\", _jsx(_components.p, {\n children: \"Restricted to allowlisted partner addresses. Contact us before integrating.\"\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"Send whichever parts you still need; at least one. They are relayed as a single atomic batch, so you are never left with an allowance but no delegate. The response reads both values back from chain, so \", _jsx(_components.code, {\n children: \"state.missing\"\n }), \" reflects what is actually set rather than what we submitted.\"]\n }), \"\\n\", _jsx(_components.h3, {\n children: \"The two signatures\"\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.strong, {\n children: _jsx(_components.code, {\n children: \"delegation\"\n })\n }), \" lets the OstiumAtomicTrading wrapper place trades for you.\"]\n }), \"\\n\", _jsxs(_components.ul, {\n children: [\"\\n\", _jsxs(_components.li, {\n children: [\"Domain: \", _jsx(_components.code, {\n children: \"{ name: \\\"Ostium\\\", version: \\\"1\\\", chainId, verifyingContract: \u003cTrading> }\"\n })]\n }), \"\\n\", _jsxs(_components.li, {\n children: [\"Type: \", _jsx(_components.code, {\n children: \"OstiumDelegation(address delegator, address delegate, uint256 nonce, uint256 expiry)\"\n })]\n }), \"\\n\", _jsxs(_components.li, {\n children: [\"The verifying contract is \", _jsx(_components.strong, {\n children: \"Trading\"\n }), \", but \", _jsx(_components.code, {\n children: \"delegate\"\n }), \" is the \", _jsx(_components.strong, {\n children: \"wrapper\"\n }), \" — it is the contract that will call Trading for you, so it is what Trading checks. Sending anything else is rejected.\"]\n }), \"\\n\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.strong, {\n children: _jsx(_components.code, {\n children: \"permit\"\n })\n }), \" grants the USDC allowance (ERC-2612).\"]\n }), \"\\n\", _jsxs(_components.ul, {\n children: [\"\\n\", _jsxs(_components.li, {\n children: [\"Domain: \", _jsx(_components.code, {\n children: \"{ name, version, chainId, verifyingContract: \u003cUSDC> }\"\n })]\n }), \"\\n\", _jsxs(_components.li, {\n children: [\"Type: \", _jsx(_components.code, {\n children: \"Permit(address owner, address spender, uint256 value, uint256 nonce, uint256 deadline)\"\n })]\n }), \"\\n\", _jsxs(_components.li, {\n children: [_jsx(_components.code, {\n children: \"spender\"\n }), \" is \", _jsx(_components.strong, {\n children: \"TradingStorage\"\n }), \", not Trading — different contracts.\"]\n }), \"\\n\", _jsxs(_components.li, {\n children: [\"Read \", _jsx(_components.code, {\n children: \"name\"\n }), \" and \", _jsx(_components.code, {\n children: \"version\"\n }), \" from the token itself; they differ between deployments and a wrong domain produces a signature that fails on chain.\"]\n }), \"\\n\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"Both nonces come from the chain at signing time — \", _jsx(_components.code, {\n children: \"Trading.delegatableNonces(partner)\"\n }), \" and \", _jsx(_components.code, {\n children: \"USDC.nonces(partner)\"\n }), \".\"]\n }), \"\\n\", _jsx(_components.h3, {\n children: \"Addresses\"\n }), \"\\n\", _jsxs(_components.table, {\n children: [_jsx(_components.thead, {\n children: _jsxs(_components.tr, {\n children: [_jsx(_components.th, {}), _jsx(_components.th, {\n children: \"Arbitrum (42161)\"\n }), _jsx(_components.th, {\n children: \"Arbitrum Sepolia (421614)\"\n })]\n })\n }), _jsxs(_components.tbody, {\n children: [_jsxs(_components.tr, {\n children: [_jsx(_components.td, {\n children: \"Trading\"\n }), _jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"0x6D0bA1f9996DBD8885827e1b2e8f6593e7702411\"\n })\n }), _jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"0x2A9B9c988393f46a2537B0ff11E98c2C15a95afe\"\n })\n })]\n }), _jsxs(_components.tr, {\n children: [_jsx(_components.td, {\n children: \"TradingStorage (permit spender)\"\n }), _jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"0xccd5891083a8acd2074690f65d3024e7d13d66e7\"\n })\n }), _jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"0x0b9F5243B29938668c9Cfbd7557A389EC7Ef88b8\"\n })\n })]\n }), _jsxs(_components.tr, {\n children: [_jsx(_components.td, {\n children: \"USDC\"\n }), _jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"0xaf88d065e77c8cc2239327c5edb3a432268e5831\"\n })\n }), _jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"0xe73B11Fb1e3eeEe8AF2a23079A4410Fe1B370548\"\n })\n })]\n }), _jsxs(_components.tr, {\n children: [_jsx(_components.td, {\n children: \"OstiumAtomicTrading (delegate)\"\n }), _jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"0x5eB3960C3fd3274cD81fE5972e0de01084bDa325\"\n })\n }), _jsx(_components.td, {\n children: _jsx(_components.code, {\n children: \"0x32C06a3eC2A40DABf6A8f645f29321cB7236DAA3\"\n })\n })]\n })]\n })]\n }), \"\\n\", _jsx(_components.h3, {\n children: \"Two cases where you send only one half\"\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.strong, {\n children: \"On Arbitrum Sepolia there is no permit.\"\n }), \" The test token is a plain ERC-20 with no \", _jsx(_components.code, {\n children: \"permit\"\n }), \" or \", _jsx(_components.code, {\n children: \"nonces\"\n }), \", so no permit signature exists for it. Send \", _jsx(_components.code, {\n children: \"approve(\u003cTradingStorage>, \u003camount>)\"\n }), \" from the wallet, then post \", _jsx(_components.code, {\n children: \"delegation\"\n }), \" alone. Mainnet USDC does implement ERC-2612.\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [_jsx(_components.strong, {\n children: \"A contract wallet can permit but not delegate.\"\n }), \" \", _jsx(_components.code, {\n children: \"setDelegateWithSignature\"\n }), \" uses \", _jsx(_components.code, {\n children: \"ecrecover\"\n }), \", which no EIP-1271 signature satisfies. Call \", _jsx(_components.code, {\n children: \"Trading.setDelegate(\u003cOstiumAtomicTrading>)\"\n }), \" from the wallet, then post \", _jsx(_components.code, {\n children: \"permit\"\n }), \" alone.\"]\n }), \"\\n\", _jsx(_components.h3, {\n children: \"Notes\"\n }), \"\\n\", _jsxs(_components.ul, {\n children: [\"\\n\", _jsxs(_components.li, {\n children: [_jsxs(_components.strong, {\n children: [\"Keep \", _jsx(_components.code, {\n children: \"expiry\"\n }), \" and \", _jsx(_components.code, {\n children: \"deadline\"\n }), \" short\"]\n }), \" — 30 minutes is plenty. Until one passes, anyone holding the signature can relay it. They cannot redirect it: \", _jsx(_components.code, {\n children: \"delegate\"\n }), \", \", _jsx(_components.code, {\n children: \"spender\"\n }), \" and \", _jsx(_components.code, {\n children: \"value\"\n }), \" are all inside the signed bytes.\"]\n }), \"\\n\", _jsxs(_components.li, {\n children: [_jsx(_components.strong, {\n children: \"Numbers are decimal strings\"\n }), \", never JSON numbers. A uint256 nonce or a max allowance exceeds \", _jsx(_components.code, {\n children: \"Number.MAX_SAFE_INTEGER\"\n }), \", and a rounded value no longer matches what you signed.\"]\n }), \"\\n\"]\n }), \"\\n\", _jsxs(_components.p, {\n children: [\"Rate limit: 10 requests per 10 seconds per IP. Read \", _jsx(_components.code, {\n children: \"x-ratelimit-*\"\n }), \" for the live budget rather than assuming this figure.\"]\n })]\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){return _jsx(_Fragment,{})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"One-time onboarding (gasless)","description":"A one-time setup step, done gaslessly. Sign two EIP-712 messages offline, post the signatures here, and we relay them on chain and pay for it — the partner never sends a transaction or needs ETH.\n\nRestricted to allowlisted partner addresses. Contact us before integrating.\n\nSend whichever parts you still need; at least one. They are relayed as a single atomic batch, so you are never left with an allowance but no delegate. The response reads both values back from chain, so `state.missing` reflects what is actually set rather than what we submitted.\n\n### The two signatures\n\n**`delegation`** lets the OstiumAtomicTrading wrapper place trades for you.\n\n- Domain: `{ name: \"Ostium\", version: \"1\", chainId, verifyingContract: \u003cTrading> }`\n- Type: `OstiumDelegation(address delegator, address delegate, uint256 nonce, uint256 expiry)`\n- The verifying contract is **Trading**, but `delegate` is the **wrapper** — it is the contract that will call Trading for you, so it is what Trading checks. Sending anything else is rejected.\n\n**`permit`** grants the USDC allowance (ERC-2612).\n\n- Domain: `{ name, version, chainId, verifyingContract: \u003cUSDC> }`\n- Type: `Permit(address owner, address spender, uint256 value, uint256 nonce, uint256 deadline)`\n- `spender` is **TradingStorage**, not Trading — different contracts.\n- Read `name` and `version` from the token itself; they differ between deployments and a wrong domain produces a signature that fails on chain.\n\nBoth nonces come from the chain at signing time — `Trading.delegatableNonces(partner)` and `USDC.nonces(partner)`.\n\n### Addresses\n\n| | Arbitrum (42161) | Arbitrum Sepolia (421614) |\n| --- | --- | --- |\n| Trading | `0x6D0bA1f9996DBD8885827e1b2e8f6593e7702411` | `0x2A9B9c988393f46a2537B0ff11E98c2C15a95afe` |\n| TradingStorage (permit spender) | `0xccd5891083a8acd2074690f65d3024e7d13d66e7` | `0x0b9F5243B29938668c9Cfbd7557A389EC7Ef88b8` |\n| USDC | `0xaf88d065e77c8cc2239327c5edb3a432268e5831` | `0xe73B11Fb1e3eeEe8AF2a23079A4410Fe1B370548` |\n| OstiumAtomicTrading (delegate) | `0x5eB3960C3fd3274cD81fE5972e0de01084bDa325` | `0x32C06a3eC2A40DABf6A8f645f29321cB7236DAA3` |\n\n### Two cases where you send only one half\n\n**On Arbitrum Sepolia there is no permit.** The test token is a plain ERC-20 with no `permit` or `nonces`, so no permit signature exists for it. Send `approve(\u003cTradingStorage>, \u003camount>)` from the wallet, then post `delegation` alone. Mainnet USDC does implement ERC-2612.\n\n**A contract wallet can permit but not delegate.** `setDelegateWithSignature` uses `ecrecover`, which no EIP-1271 signature satisfies. Call `Trading.setDelegate(\u003cOstiumAtomicTrading>)` from the wallet, then post `permit` alone.\n\n### Notes\n\n- **Keep `expiry` and `deadline` short** — 30 minutes is plenty. Until one passes, anyone holding the signature can relay it. They cannot redirect it: `delegate`, `spender` and `value` are all inside the signed bytes.\n- **Numbers are decimal strings**, never JSON numbers. A uint256 nonce or a max allowance exceeds `Number.MAX_SAFE_INTEGER`, and a rounded value no longer matches what you signed.\n\nRate limit: 10 requests per 10 seconds per IP. Read `x-ratelimit-*` for the live budget rather than assuming this figure.","deprecated":null,"version":null,"openapi":"/api-reference/openapi.json post /v1/onboard","href":"/api-reference/orders/one-time-onboarding-gasless","autogeneratedByOpenApi":true}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[],"codeExamples":{}},"jsLinks":[],"openApiReferenceData":{"operation":{"summary":"One-time onboarding (gasless)","description":"A one-time setup step, done gaslessly. Sign two EIP-712 messages offline, post the signatures here, and we relay them on chain and pay for it — the partner never sends a transaction or needs ETH.\n\nRestricted to allowlisted partner addresses. Contact us before integrating.\n\nSend whichever parts you still need; at least one. They are relayed as a single atomic batch, so you are never left with an allowance but no delegate. The response reads both values back from chain, so `state.missing` reflects what is actually set rather than what we submitted.\n\n### The two signatures\n\n**`delegation`** lets the OstiumAtomicTrading wrapper place trades for you.\n\n- Domain: `{ name: \"Ostium\", version: \"1\", chainId, verifyingContract: \u003cTrading> }`\n- Type: `OstiumDelegation(address delegator, address delegate, uint256 nonce, uint256 expiry)`\n- The verifying contract is **Trading**, but `delegate` is the **wrapper** — it is the contract that will call Trading for you, so it is what Trading checks. Sending anything else is rejected.\n\n**`permit`** grants the USDC allowance (ERC-2612).\n\n- Domain: `{ name, version, chainId, verifyingContract: \u003cUSDC> }`\n- Type: `Permit(address owner, address spender, uint256 value, uint256 nonce, uint256 deadline)`\n- `spender` is **TradingStorage**, not Trading — different contracts.\n- Read `name` and `version` from the token itself; they differ between deployments and a wrong domain produces a signature that fails on chain.\n\nBoth nonces come from the chain at signing time — `Trading.delegatableNonces(partner)` and `USDC.nonces(partner)`.\n\n### Addresses\n\n| | Arbitrum (42161) | Arbitrum Sepolia (421614) |\n| --- | --- | --- |\n| Trading | `0x6D0bA1f9996DBD8885827e1b2e8f6593e7702411` | `0x2A9B9c988393f46a2537B0ff11E98c2C15a95afe` |\n| TradingStorage (permit spender) | `0xccd5891083a8acd2074690f65d3024e7d13d66e7` | `0x0b9F5243B29938668c9Cfbd7557A389EC7Ef88b8` |\n| USDC | `0xaf88d065e77c8cc2239327c5edb3a432268e5831` | `0xe73B11Fb1e3eeEe8AF2a23079A4410Fe1B370548` |\n| OstiumAtomicTrading (delegate) | `0x5eB3960C3fd3274cD81fE5972e0de01084bDa325` | `0x32C06a3eC2A40DABf6A8f645f29321cB7236DAA3` |\n\n### Two cases where you send only one half\n\n**On Arbitrum Sepolia there is no permit.** The test token is a plain ERC-20 with no `permit` or `nonces`, so no permit signature exists for it. Send `approve(\u003cTradingStorage>, \u003camount>)` from the wallet, then post `delegation` alone. Mainnet USDC does implement ERC-2612.\n\n**A contract wallet can permit but not delegate.** `setDelegateWithSignature` uses `ecrecover`, which no EIP-1271 signature satisfies. Call `Trading.setDelegate(\u003cOstiumAtomicTrading>)` from the wallet, then post `permit` alone.\n\n### Notes\n\n- **Keep `expiry` and `deadline` short** — 30 minutes is plenty. Until one passes, anyone holding the signature can relay it. They cannot redirect it: `delegate`, `spender` and `value` are all inside the signed bytes.\n- **Numbers are decimal strings**, never JSON numbers. A uint256 nonce or a max allowance exceeds `Number.MAX_SAFE_INTEGER`, and a rounded value no longer matches what you signed.\n\nRate limit: 10 requests per 10 seconds per IP. Read `x-ratelimit-*` for the live budget rather than assuming this figure.","tags":["Orders"],"requestBody":"9cb9807d-cef3-4f93-b30e-557d6666de07","responses":{"200":"91eb922f-1774-4fdc-abbe-6432a6a0e1a3","400":"b98a2982-c0c6-4c20-b3e1-92752b8e385a","401":"35013513-128f-4e59-8d12-4053ab4d18fd","403":"96567843-60c4-4a02-b0cd-8b7bd4e0ab7e","413":"3b04b831-4390-4ac3-878d-432d7e49ff58","429":"ea3ac76e-a662-4296-87e7-ccff23371282","500":"d128dcb6-7da1-4d39-87a3-cf789a00e2f7"},"method":"post","type":"path","servers":["35ccf0da-4e4c-4ce1-a160-3dfb930fa193","14a6e080-29f2-4577-ab29-a205a0e38f12"],"security":[],"path":"/v1/onboard","baseUrlOptions":["https://builder.prod.bedrock.ostium.io","https://builder.stage.bedrock.ostium.io"],"title":"One-time onboarding (gasless)","codeSamples":[],"requiredOnlyExamples":false,"requestExampleType":"generated","responseExampleType":"generated"},"dependencies":{"parameters":{},"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"chainId":{"oneOf":[{"type":"number","description":"42161 (Arbitrum One) or 421614 (Arbitrum Sepolia)","enum":[42161],"isRequired":true,"uniqueKey":"chainId.oneOf.0","typeLabel":"enum\u003cnumber>"},{"type":"number","description":"42161 (Arbitrum One) or 421614 (Arbitrum Sepolia)","enum":[421614],"isRequired":true,"uniqueKey":"chainId.oneOf.1","typeLabel":"enum\u003cnumber>"}],"isRequired":true,"uniqueKey":"chainId","typeLabel":"enum\u003cnumber>"},"partner":{"type":"string","pattern":"^0x[a-fA-F0-9]{40}$","description":"The address being onboarded, and the one that signed below.","isRequired":true,"uniqueKey":"partner","typeLabel":"string"},"delegation":{"type":"object","properties":{"delegate":{"type":"string","pattern":"^0x[a-fA-F0-9]{40}$","description":"Must be the OstiumAtomicTrading address for this chain — see the table above. It is the contract that will place your trades.","isRequired":true,"uniqueKey":"delegation.delegate","typeLabel":"string"},"nonce":{"type":"string","pattern":"^\\d+$","description":"Read `Trading.delegatableNonces(partner)` at signing time and send that value.","isRequired":true,"uniqueKey":"delegation.nonce","typeLabel":"string"},"expiry":{"type":"string","pattern":"^\\d+$","description":"Unix seconds after which the signature stops working. 30 minutes ahead is plenty.","isRequired":true,"uniqueKey":"delegation.expiry","typeLabel":"string"},"signature":{"type":"string","pattern":"^0x[a-fA-F0-9]{130}$","description":"The `OstiumDelegation` signature — 65 bytes. Must be an EOA signature: the contract recovers it with `ecrecover`, which no contract wallet can satisfy.","isRequired":true,"uniqueKey":"delegation.signature","typeLabel":"string"}},"required":["delegate","nonce","expiry","signature"],"description":"Lets the wrapper trade for you. Omit if you have already delegated.","uniqueKey":"delegation","typeLabel":"object"},"permit":{"type":"object","properties":{"value":{"type":"string","pattern":"^\\d+$","description":"USDC allowance granted to TradingStorage, scaled by 1e6 — 100 USDC is `\"100000000\"`. It is consumed as you trade, so most integrations sign max uint256 and never come back.","isRequired":true,"uniqueKey":"permit.value","typeLabel":"string"},"nonce":{"type":"string","pattern":"^\\d+$","description":"Read `USDC.nonces(partner)` at signing time and send that value.","isRequired":true,"uniqueKey":"permit.nonce","typeLabel":"string"},"deadline":{"type":"string","pattern":"^\\d+$","description":"Unix seconds after which the signature stops working. 30 minutes ahead is plenty.","isRequired":true,"uniqueKey":"permit.deadline","typeLabel":"string"},"signature":{"type":"string","pattern":"^0x([a-fA-F0-9]{2})+$","description":"The ERC-2612 `Permit` signature. 65 bytes from an EOA, or opaque bytes from a contract wallet — the token verifies it either way.","isRequired":true,"uniqueKey":"permit.signature","typeLabel":"string"}},"required":["value","nonce","deadline","signature"],"description":"Grants the USDC allowance. Omit if you have already approved, and on Arbitrum Sepolia, where the test token has no `permit`.","uniqueKey":"permit","typeLabel":"object"}},"required":["chainId","partner"],"uniqueKey":"","typeLabel":"object"},"example":{"chainId":42161,"partner":"0x1111111111111111111111111111111111111111","delegation":{"delegate":"0x2222222222222222222222222222222222222222","nonce":"0","expiry":"1800000000","signature":"0xabcd"},"permit":{"value":"1000000000000","nonce":"0","deadline":"1800000000","signature":"0xabcd"}},"examples":{"Example":{"title":"Example","value":{"chainId":42161,"partner":"0x1111111111111111111111111111111111111111","delegation":{"delegate":"0x2222222222222222222222222222222222222222","nonce":"0","expiry":"1800000000","signature":"0xabcd"},"permit":{"value":"1000000000000","nonce":"0","deadline":"1800000000","signature":"0xabcd"}}}},"exampleType":"examples"}},"exampleType":"examples"},"responses":{"91eb922f-1774-4fdc-abbe-6432a6a0e1a3":{"description":"Relayed. `status` is the terminal on-chain outcome, not an acknowledgement.","headers":{"x-request-id":{"schema":{"type":"string"},"description":"Always present, and always generated by the service — a value sent on the request is ignored. Use it to correlate a response with your own logs, and quote it when reporting a problem."},"x-ratelimit-limit":{"schema":{"type":"string"},"description":"Requests allowed per window."},"x-ratelimit-remaining":{"schema":{"type":"string"},"description":"Requests left in the current window."},"x-ratelimit-reset":{"schema":{"type":"string"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["confirmed","reverted"],"description":"`confirmed` means both signatures applied on chain. `reverted` means neither did — the batch is atomic.","isRequired":true,"uniqueKey":"status","typeLabel":"enum\u003cstring>"},"userOpHash":{"type":"string","description":"Identifies the ERC-4337 user operation we submitted for you. Quote it if you need to ask us about a specific call.","example":"0x8c8b184c3208dd11c0275a0cd5a62d0ef0fb4578d1f9b36569b2d346048af4f7","isRequired":true,"uniqueKey":"userOpHash","typeLabel":"string"},"transactionHash":{"type":"string","description":"The transaction it landed in — look it up on Arbiscan.","example":"0x9cae473bf1c8d54da1d32138e7bbe1d2ca05213c98fe70945b402634aac13279","isRequired":true,"uniqueKey":"transactionHash","typeLabel":"string"},"reason":{"type":"string","description":"Why it reverted. Present only when `status` is `reverted`.","uniqueKey":"reason","typeLabel":"string"},"state":{"type":"object","properties":{"onboarded":{"type":"boolean","description":"True when nothing is left to do — you can start trading.","isRequired":true,"uniqueKey":"state.onboarded","typeLabel":"boolean"},"missing":{"type":"array","items":{"type":"string","enum":["delegation","allowance"],"uniqueKey":"state.missing.items.[INDEX]","typeLabel":"enum\u003cstring>"},"description":"What is still outstanding. Empty when `onboarded` is true.","example":[],"isRequired":true,"uniqueKey":"state.missing","typeLabel":"enum\u003cstring>[]"},"delegate":{"type":"string","description":"The address currently allowed to trade for you. Should be OstiumAtomicTrading; the zero address means no delegation is set.","example":"0x32C06a3eC2A40DABf6A8f645f29321cB7236DAA3","isRequired":true,"uniqueKey":"state.delegate","typeLabel":"string"},"allowance":{"type":"string","description":"USDC that TradingStorage may spend, scaled by 1e6. Only checked against zero here — whether it is enough depends on the sizes you intend to trade.","isRequired":true,"uniqueKey":"state.allowance","typeLabel":"string"}},"required":["onboarded","missing","delegate","allowance"],"description":"Your onboarding state, read back from chain rather than assumed from what was submitted. Absent if that read failed; the outcome above still stands.","uniqueKey":"state","typeLabel":"object"}},"required":["status","userOpHash","transactionHash"],"uniqueKey":"","typeLabel":"object"},"example":{"status":"confirmed","userOpHash":"0xaaaa","transactionHash":"0xbbbb","state":{"onboarded":true,"missing":[],"delegate":"0x2222222222222222222222222222222222222222","allowance":"115792089237316195423570985008687907853269984665640564039457584007913129639935"}},"examples":{"Example":{"title":"Example","value":{"status":"confirmed","userOpHash":"0xaaaa","transactionHash":"0xbbbb","state":{"onboarded":true,"missing":[],"delegate":"0x2222222222222222222222222222222222222222","allowance":"115792089237316195423570985008687907853269984665640564039457584007913129639935"}}}},"exampleType":"examples"}},"exampleType":"examples"},"b98a2982-c0c6-4c20-b3e1-92752b8e385a":{"description":"Validation failed","headers":{"x-request-id":{"schema":{"type":"string"},"description":"Always present, and always generated by the service — a value sent on the request is ignored. Use it to correlate a response with your own logs, and quote it when reporting a problem."},"x-ratelimit-limit":{"schema":{"type":"string"},"description":"Requests allowed per window."},"x-ratelimit-remaining":{"schema":{"type":"string"},"description":"Requests left in the current window."},"x-ratelimit-reset":{"schema":{"type":"string"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"HTTP status name, e.g. \"Bad Request\"","example":"Bad Request","isRequired":true,"uniqueKey":"error","typeLabel":"string"},"message":{"type":"string","description":"Human-readable detail","example":"Validation failed","isRequired":true,"uniqueKey":"message","typeLabel":"string"},"issues":{"type":"array","items":{"oneOf":[{}],"uniqueKey":"issues.items.[INDEX]","typeLabel":"any"},"description":"Raw zod issue array — present on validation failures only","uniqueKey":"issues","typeLabel":"any[]"},"details":{"description":"Optional structured detail from the thrower","uniqueKey":"details","typeLabel":"any"}},"required":["error","message"],"uniqueKey":"","typeLabel":"object"},"example":{"error":"Bad Request","message":"Validation failed","issues":[]},"examples":{"Example":{"title":"Example","value":{"error":"Bad Request","message":"Validation failed","issues":[]}}},"exampleType":"examples"}},"exampleType":"examples"},"35013513-128f-4e59-8d12-4053ab4d18fd":{"description":"A signature does not match the partner address","headers":{"x-request-id":{"schema":{"type":"string"},"description":"Always present, and always generated by the service — a value sent on the request is ignored. Use it to correlate a response with your own logs, and quote it when reporting a problem."},"x-ratelimit-limit":{"schema":{"type":"string"},"description":"Requests allowed per window."},"x-ratelimit-remaining":{"schema":{"type":"string"},"description":"Requests left in the current window."},"x-ratelimit-reset":{"schema":{"type":"string"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"HTTP status name, e.g. \"Bad Request\"","example":"Bad Request","isRequired":true,"uniqueKey":"error","typeLabel":"string"},"message":{"type":"string","description":"Human-readable detail","example":"Validation failed","isRequired":true,"uniqueKey":"message","typeLabel":"string"},"issues":{"type":"array","items":{"oneOf":[{}],"uniqueKey":"issues.items.[INDEX]","typeLabel":"any"},"description":"Raw zod issue array — present on validation failures only","uniqueKey":"issues","typeLabel":"any[]"},"details":{"description":"Optional structured detail from the thrower","uniqueKey":"details","typeLabel":"any"}},"required":["error","message"],"uniqueKey":"","typeLabel":"object"},"example":{"error":"Unauthorized","message":"permit signature does not match the partner address"},"examples":{"Example":{"title":"Example","value":{"error":"Unauthorized","message":"permit signature does not match the partner address"}}},"exampleType":"examples"}},"exampleType":"examples"},"96567843-60c4-4a02-b0cd-8b7bd4e0ab7e":{"description":"The address is not an allowlisted partner","headers":{"x-request-id":{"schema":{"type":"string"},"description":"Always present, and always generated by the service — a value sent on the request is ignored. Use it to correlate a response with your own logs, and quote it when reporting a problem."},"x-ratelimit-limit":{"schema":{"type":"string"},"description":"Requests allowed per window."},"x-ratelimit-remaining":{"schema":{"type":"string"},"description":"Requests left in the current window."},"x-ratelimit-reset":{"schema":{"type":"string"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"HTTP status name, e.g. \"Bad Request\"","example":"Bad Request","isRequired":true,"uniqueKey":"error","typeLabel":"string"},"message":{"type":"string","description":"Human-readable detail","example":"Validation failed","isRequired":true,"uniqueKey":"message","typeLabel":"string"},"issues":{"type":"array","items":{"oneOf":[{}],"uniqueKey":"issues.items.[INDEX]","typeLabel":"any"},"description":"Raw zod issue array — present on validation failures only","uniqueKey":"issues","typeLabel":"any[]"},"details":{"description":"Optional structured detail from the thrower","uniqueKey":"details","typeLabel":"any"}},"required":["error","message"],"uniqueKey":"","typeLabel":"object"},"example":{"error":"Forbidden","message":"address is not a permitted partner"},"examples":{"Example":{"title":"Example","value":{"error":"Forbidden","message":"address is not a permitted partner"}}},"exampleType":"examples"}},"exampleType":"examples"},"3b04b831-4390-4ac3-878d-432d7e49ff58":{"description":"Request body is larger than the parser limit","headers":{"x-request-id":{"schema":{"type":"string"},"description":"Always present, and always generated by the service — a value sent on the request is ignored. Use it to correlate a response with your own logs, and quote it when reporting a problem."},"x-ratelimit-limit":{"schema":{"type":"string"},"description":"Requests allowed per window."},"x-ratelimit-remaining":{"schema":{"type":"string"},"description":"Requests left in the current window."},"x-ratelimit-reset":{"schema":{"type":"string"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"HTTP status name, e.g. \"Bad Request\"","example":"Bad Request","isRequired":true,"uniqueKey":"error","typeLabel":"string"},"message":{"type":"string","description":"Human-readable detail","example":"Validation failed","isRequired":true,"uniqueKey":"message","typeLabel":"string"},"issues":{"type":"array","items":{"oneOf":[{}],"uniqueKey":"issues.items.[INDEX]","typeLabel":"any"},"description":"Raw zod issue array — present on validation failures only","uniqueKey":"issues","typeLabel":"any[]"},"details":{"description":"Optional structured detail from the thrower","uniqueKey":"details","typeLabel":"any"}},"required":["error","message"],"uniqueKey":"","typeLabel":"object"},"example":{"error":"Payload Too Large","message":"request entity too large"},"examples":{"Example":{"title":"Example","value":{"error":"Payload Too Large","message":"request entity too large"}}},"exampleType":"examples"}},"exampleType":"examples"},"ea3ac76e-a662-4296-87e7-ccff23371282":{"description":"Rate limit exceeded","headers":{"x-request-id":{"schema":{"type":"string"},"description":"Always present, and always generated by the service — a value sent on the request is ignored. Use it to correlate a response with your own logs, and quote it when reporting a problem."},"x-ratelimit-limit":{"schema":{"type":"string"},"description":"Requests allowed per window."},"x-ratelimit-remaining":{"schema":{"type":"string"},"description":"Requests left in the current window."},"x-ratelimit-reset":{"schema":{"type":"string"},"description":"Seconds until the window resets."},"retry-after":{"schema":{"type":"string"},"description":"Seconds to wait before retrying."}},"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"HTTP status name, e.g. \"Bad Request\"","example":"Bad Request","isRequired":true,"uniqueKey":"error","typeLabel":"string"},"message":{"type":"string","description":"Human-readable detail","example":"Validation failed","isRequired":true,"uniqueKey":"message","typeLabel":"string"},"issues":{"type":"array","items":{"oneOf":[{}],"uniqueKey":"issues.items.[INDEX]","typeLabel":"any"},"description":"Raw zod issue array — present on validation failures only","uniqueKey":"issues","typeLabel":"any[]"},"details":{"description":"Optional structured detail from the thrower","uniqueKey":"details","typeLabel":"any"}},"required":["error","message"],"uniqueKey":"","typeLabel":"object"},"example":{"error":"Too Many Requests","message":"Rate limit exceeded. Try again in 7s."},"examples":{"Example":{"title":"Example","value":{"error":"Too Many Requests","message":"Rate limit exceeded. Try again in 7s."}}},"exampleType":"examples"}},"exampleType":"examples"},"d128dcb6-7da1-4d39-87a3-cf789a00e2f7":{"description":"Unexpected error","headers":{"x-request-id":{"schema":{"type":"string"},"description":"Always present, and always generated by the service — a value sent on the request is ignored. Use it to correlate a response with your own logs, and quote it when reporting a problem."},"x-ratelimit-limit":{"schema":{"type":"string"},"description":"Requests allowed per window."},"x-ratelimit-remaining":{"schema":{"type":"string"},"description":"Requests left in the current window."},"x-ratelimit-reset":{"schema":{"type":"string"},"description":"Seconds until the window resets."}},"content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string","description":"HTTP status name, e.g. \"Bad Request\"","example":"Bad Request","isRequired":true,"uniqueKey":"error","typeLabel":"string"},"message":{"type":"string","description":"Human-readable detail","example":"Validation failed","isRequired":true,"uniqueKey":"message","typeLabel":"string"},"issues":{"type":"array","items":{"oneOf":[{}],"uniqueKey":"issues.items.[INDEX]","typeLabel":"any"},"description":"Raw zod issue array — present on validation failures only","uniqueKey":"issues","typeLabel":"any[]"},"details":{"description":"Optional structured detail from the thrower","uniqueKey":"details","typeLabel":"any"}},"required":["error","message"],"uniqueKey":"","typeLabel":"object"},"example":{"error":"Internal Server Error","message":"Internal Server Error"},"examples":{"Example":{"title":"Example","value":{"error":"Internal Server Error","message":"Internal Server Error"}}},"exampleType":"examples"}},"exampleType":"examples"}},"security":{},"servers":{"35ccf0da-4e4c-4ce1-a160-3dfb930fa193":{"url":"https://builder.prod.bedrock.ostium.io","description":"Production (Arbitrum One)"},"14a6e080-29f2-4577-ab29-a205a0e38f12":{"url":"https://builder.stage.bedrock.ostium.io","description":"Staging (Arbitrum Sepolia)"}}},"originalFileLocation":"api-reference/openapi.json"},"subdomain":"ostium","siteName":"Ostium","title":"One-time onboarding (gasless) - Ostium","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-09-17T13:14:46.794Z","originalFileLocation":"api-reference/orders/one-time-onboarding-gasless.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"A one-time setup step, done gaslessly. Sign two EIP-712 messages offline, post the signatures here, and we relay them on chain and pay for it — the partner never sends a transaction or needs ETH. Restricted to allowlisted partner addresses. Contact us before integrating. Send whichever parts you still need; at least one. They are relayed as a single atomic batch, so you are never left with an allowance but no delegate. The response reads both values back from chain, so state.missing reflects what is actually set rather than what we submitted. ### The two signatures delegation lets the OstiumAtomicTrading wrapper place trades for you. - Domain: { name: \u0026quot;Ostium\u0026quot;, version: \u0026quot;1\u0026quot;, chainId, verifyingContract: } - Type: OstiumDelegation(address delegator, address delegate, uint256 nonce, uint256 expiry) - The verifying contract is Trading, but delegate is the wrapper — it is the contract that will call Trading for you, so it is what Trading checks. Sending anything else is rejected. permit grants the USDC allowance (ERC-2612). - Domain: { name, version, chainId, verifyingContract: } - Type: Permit(address owner, address spender, uint256 value, uint256 nonce, uint256 deadline) - spender is TradingStorage, not Trading — different contracts. - Read name and version from the token itself; they differ between deployments and a wrong domain produces a signature that fails on chain. Both nonces come from the chain at signing time — Trading.delegatableNonces(partner) and USDC.nonces(partner). ### Addresses | | Arbitrum (42161) | Arbitrum Sepolia (421614) | | --- | --- | --- | | Trading | 0x6D0bA1f9996DBD8885827e1b2e8f6593e7702411 | 0x2A9B9c988393f46a2537B0ff11E98c2C15a95afe | | TradingStorage (permit spender) | 0xccd5891083a8acd2074690f65d3024e7d13d66e7 | 0x0b9F5243B29938668c9Cfbd7557A389EC7Ef88b8 | | USDC | 0xaf88d065e77c8cc2239327c5edb3a432268e5831 | 0xe73B11Fb1e3eeEe8AF2a23079A4410Fe1B370548 | | OstiumAtomicTrading (delegate) | 0x5eB3960C3fd3274cD81fE5972e0de01084bDa325 | 0x32C06a3eC2A40DABf6A8f645f29321cB7236DAA3 | ### Two cases where you send only one half On Arbitrum Sepolia there is no permit. The test token is a plain ERC-20 with no permit or nonces, so no permit signature exists for it. Send approve(, ) from the wallet, then post delegation alone. Mainnet USDC does implement ERC-2612. A contract wallet can permit but not delegate. setDelegateWithSignature uses ecrecover, which no EIP-1271 signature satisfies. Call Trading.setDelegate() from the wallet, then post permit alone. ### Notes - Keep expiry and deadline short — 30 minutes is plenty. Until one passes, anyone holding the signature can relay it. They cannot redirect it: delegate, spender and value are all inside the signed bytes. - Numbers are decimal strings, never JSON numbers. A uint256 nonce or a max allowance exceeds Number.MAX_SAFE_INTEGER, and a rounded value no longer matches what you signed. Rate limit: 10 requests per 10 seconds per IP. Read x-ratelimit-* for the live budget rather than assuming this figure.\">\u003cmeta data-mint-head name=\"application-name\" content=\"Ostium\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/ostium/YrHQiGdYpiT2HO80/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/ostium/YrHQiGdYpiT2HO80/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/ostium/YrHQiGdYpiT2HO80/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/ostium/YrHQiGdYpiT2HO80/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/ostium/YrHQiGdYpiT2HO80/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/ostium/YrHQiGdYpiT2HO80/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/ostium/YrHQiGdYpiT2HO80/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/ostium/YrHQiGdYpiT2HO80/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/ostium/YrHQiGdYpiT2HO80/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"One-time onboarding (gasless) - Ostium\">\u003cmeta data-mint-head property=\"og:description\" content=\"A one-time setup step, done gaslessly. Sign two EIP-712 messages offline, post the signatures here, and we relay them on chain and pay for it — the partner never sends a transaction or needs ETH. Restricted to allowlisted partner addresses. Contact us before integrating. Send whichever parts you still need; at least one. They are relayed as a single atomic batch, so you are never left with an allowance but no delegate. The response reads both values back from chain, so state.missing reflects what is actually set rather than what we submitted. ### The two signatures delegation lets the OstiumAtomicTrading wrapper place trades for you. - Domain: { name: \u0026quot;Ostium\u0026quot;, version: \u0026quot;1\u0026quot;, chainId, verifyingContract: } - Type: OstiumDelegation(address delegator, address delegate, uint256 nonce, uint256 expiry) - The verifying contract is Trading, but delegate is the wrapper — it is the contract that will call Trading for you, so it is what Trading checks. Sending anything else is rejected. permit grants the USDC allowance (ERC-2612). - Domain: { name, version, chainId, verifyingContract: } - Type: Permit(address owner, address spender, uint256 value, uint256 nonce, uint256 deadline) - spender is TradingStorage, not Trading — different contracts. - Read name and version from the token itself; they differ between deployments and a wrong domain produces a signature that fails on chain. Both nonces come from the chain at signing time — Trading.delegatableNonces(partner) and USDC.nonces(partner). ### Addresses | | Arbitrum (42161) | Arbitrum Sepolia (421614) | | --- | --- | --- | | Trading | 0x6D0bA1f9996DBD8885827e1b2e8f6593e7702411 | 0x2A9B9c988393f46a2537B0ff11E98c2C15a95afe | | TradingStorage (permit spender) | 0xccd5891083a8acd2074690f65d3024e7d13d66e7 | 0x0b9F5243B29938668c9Cfbd7557A389EC7Ef88b8 | | USDC | 0xaf88d065e77c8cc2239327c5edb3a432268e5831 | 0xe73B11Fb1e3eeEe8AF2a23079A4410Fe1B370548 | | OstiumAtomicTrading (delegate) | 0x5eB3960C3fd3274cD81fE5972e0de01084bDa325 | 0x32C06a3eC2A40DABf6A8f645f29321cB7236DAA3 | ### Two cases where you send only one half On Arbitrum Sepolia there is no permit. The test token is a plain ERC-20 with no permit or nonces, so no permit signature exists for it. Send approve(, ) from the wallet, then post delegation alone. Mainnet USDC does implement ERC-2612. A contract wallet can permit but not delegate. setDelegateWithSignature uses ecrecover, which no EIP-1271 signature satisfies. Call Trading.setDelegate() from the wallet, then post permit alone. ### Notes - Keep expiry and deadline short — 30 minutes is plenty. Until one passes, anyone holding the signature can relay it. They cannot redirect it: delegate, spender and value are all inside the signed bytes. - Numbers are decimal strings, never JSON numbers. A uint256 nonce or a max allowance exceeds Number.MAX_SAFE_INTEGER, and a rounded value no longer matches what you signed. Rate limit: 10 requests per 10 seconds per IP. Read x-ratelimit-* for the live budget rather than assuming this figure.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Ostium\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.ostium.com/api-reference/orders/one-time-onboarding-gasless\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://ostium.mintlify.app/_mintlify/api/og/v2?division=Orders\u0026amp;title=One-time+onboarding+%28gasless%29\u0026amp;description=A+one-time+setup+step%2C+done+gaslessly.+Sign+two+EIP-712+messages+offline%2C+post+the+signatures+here%2C+and+we+relay+them+on+chain+and+pay+for+it+%E2%80%94+the+partner+neve\u0026amp;theme=4c59edf2484b017f3ed2d875\u0026amp;signature=8f7553e99c3e898fb503dbf518e7ceb4e30256343d1fdf63dfa78380bde60ec1\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"One-time onboarding (gasless) - Ostium\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"A one-time setup step, done gaslessly. Sign two EIP-712 messages offline, post the signatures here, and we relay them on chain and pay for it — the partner never sends a transaction or needs ETH. Restricted to allowlisted partner addresses. Contact us before integrating. Send whichever parts you still need; at least one. They are relayed as a single atomic batch, so you are never left with an allowance but no delegate. The response reads both values back from chain, so state.missing reflects what is actually set rather than what we submitted. ### The two signatures delegation lets the OstiumAtomicTrading wrapper place trades for you. - Domain: { name: \u0026quot;Ostium\u0026quot;, version: \u0026quot;1\u0026quot;, chainId, verifyingContract: } - Type: OstiumDelegation(address delegator, address delegate, uint256 nonce, uint256 expiry) - The verifying contract is Trading, but delegate is the wrapper — it is the contract that will call Trading for you, so it is what Trading checks. Sending anything else is rejected. permit grants the USDC allowance (ERC-2612). - Domain: { name, version, chainId, verifyingContract: } - Type: Permit(address owner, address spender, uint256 value, uint256 nonce, uint256 deadline) - spender is TradingStorage, not Trading — different contracts. - Read name and version from the token itself; they differ between deployments and a wrong domain produces a signature that fails on chain. Both nonces come from the chain at signing time — Trading.delegatableNonces(partner) and USDC.nonces(partner). ### Addresses | | Arbitrum (42161) | Arbitrum Sepolia (421614) | | --- | --- | --- | | Trading | 0x6D0bA1f9996DBD8885827e1b2e8f6593e7702411 | 0x2A9B9c988393f46a2537B0ff11E98c2C15a95afe | | TradingStorage (permit spender) | 0xccd5891083a8acd2074690f65d3024e7d13d66e7 | 0x0b9F5243B29938668c9Cfbd7557A389EC7Ef88b8 | | USDC | 0xaf88d065e77c8cc2239327c5edb3a432268e5831 | 0xe73B11Fb1e3eeEe8AF2a23079A4410Fe1B370548 | | OstiumAtomicTrading (delegate) | 0x5eB3960C3fd3274cD81fE5972e0de01084bDa325 | 0x32C06a3eC2A40DABf6A8f645f29321cB7236DAA3 | ### Two cases where you send only one half On Arbitrum Sepolia there is no permit. The test token is a plain ERC-20 with no permit or nonces, so no permit signature exists for it. Send approve(, ) from the wallet, then post delegation alone. Mainnet USDC does implement ERC-2612. A contract wallet can permit but not delegate. setDelegateWithSignature uses ecrecover, which no EIP-1271 signature satisfies. Call Trading.setDelegate() from the wallet, then post permit alone. ### Notes - Keep expiry and deadline short — 30 minutes is plenty. Until one passes, anyone holding the signature can relay it. They cannot redirect it: delegate, spender and value are all inside the signed bytes. - Numbers are decimal strings, never JSON numbers. A uint256 nonce or a max allowance exceeds Number.MAX_SAFE_INTEGER, and a rounded value no longer matches what you signed. Rate limit: 10 requests per 10 seconds per IP. Read x-ratelimit-* for the live budget rather than assuming this figure.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://ostium.mintlify.app/_mintlify/api/og/v2?division=Orders\u0026amp;title=One-time+onboarding+%28gasless%29\u0026amp;description=A+one-time+setup+step%2C+done+gaslessly.+Sign+two+EIP-712+messages+offline%2C+post+the+signatures+here%2C+and+we+relay+them+on+chain+and+pay+for+it+%E2%80%94+the+partner+neve\u0026amp;theme=4c59edf2484b017f3ed2d875\u0026amp;signature=8f7553e99c3e898fb503dbf518e7ceb4e30256343d1fdf63dfa78380bde60ec1\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/ostium/YrHQiGdYpiT2HO80/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Ostium\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#FF5A19\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.ostium.com/api-reference/orders/one-time-onboarding-gasless\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/api-reference/orders/one-time-onboarding-gasless.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.ostium.com/#organization\",\"name\":\"Ostium\",\"url\":\"https://docs.ostium.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/ostium/rgKhfmKxgkO9O2aD/logo/ostium-logo.svg?fit=max\u0026auto=format\u0026n=rgKhfmKxgkO9O2aD\u0026q=85\u0026s=e2316b63df8fab1bbdb3c18daeff80e5\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.ostium.com#website\",\"name\":\"Ostium\",\"url\":\"https://docs.ostium.com\",\"publisher\":{\"@id\":\"https://docs.ostium.com/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.ostium.com/api-reference/orders/one-time-onboarding-gasless#webpage\",\"url\":\"https://docs.ostium.com/api-reference/orders/one-time-onboarding-gasless\",\"name\":\"One-time onboarding (gasless)\",\"description\":\"A one-time setup step, done gaslessly. Sign two EIP-712 messages offline, post the signatures here, and we relay them on chain and pay for it — the partner never sends a transaction or needs ETH. Restricted to allowlisted partner addresses. Contact us before integrating. Send whichever parts you still need; at least one. They are relayed as a single atomic batch, so you are never left with an allowance but no delegate. The response reads both values back from chain, so state.missing reflects what is actually set rather than what we submitted. ### The two signatures delegation lets the OstiumAtomicTrading wrapper place trades for you. - Domain: { name: \\\"Ostium\\\", version: \\\"1\\\", chainId, verifyingContract: } - Type: OstiumDelegation(address delegator, address delegate, uint256 nonce, uint256 expiry) - The verifying contract is Trading, but delegate is the wrapper — it is the contract that will call Trading for you, so it is what Trading checks. Sending anything else is rejected. permit grants the USDC allowance (ERC-2612). - Domain: { name, version, chainId, verifyingContract: } - Type: Permit(address owner, address spender, uint256 value, uint256 nonce, uint256 deadline) - spender is TradingStorage, not Trading — different contracts. - Read name and version from the token itself; they differ between deployments and a wrong domain produces a signature that fails on chain. Both nonces come from the chain at signing time — Trading.delegatableNonces(partner) and USDC.nonces(partner). ### Addresses | | Arbitrum (42161) | Arbitrum Sepolia (421614) | | --- | --- | --- | | Trading | 0x6D0bA1f9996DBD8885827e1b2e8f6593e7702411 | 0x2A9B9c988393f46a2537B0ff11E98c2C15a95afe | | TradingStorage (permit spender) | 0xccd5891083a8acd2074690f65d3024e7d13d66e7 | 0x0b9F5243B29938668c9Cfbd7557A389EC7Ef88b8 | | USDC | 0xaf88d065e77c8cc2239327c5edb3a432268e5831 | 0xe73B11Fb1e3eeEe8AF2a23079A4410Fe1B370548 | | OstiumAtomicTrading (delegate) | 0x5eB3960C3fd3274cD81fE5972e0de01084bDa325 | 0x32C06a3eC2A40DABf6A8f645f29321cB7236DAA3 | ### Two cases where you send only one half On Arbitrum Sepolia there is no permit. The test token is a plain ERC-20 with no permit or nonces, so no permit signature exists for it. Send approve(, ) from the wallet, then post delegation alone. Mainnet USDC does implement ERC-2612. A contract wallet can permit but not delegate. setDelegateWithSignature uses ecrecover, which no EIP-1271 signature satisfies. Call Trading.setDelegate() from the wallet, then post permit alone. ### Notes - Keep expiry and deadline short — 30 minutes is plenty. Until one passes, anyone holding the signature can relay it. They cannot redirect it: delegate, spender and value are all inside the signed bytes. - Numbers are decimal strings, never JSON numbers. A uint256 nonce or a max allowance exceeds Number.MAX_SAFE_INTEGER, and a rounded value no longer matches what you signed. Rate limit: 10 requests per 10 seconds per IP. Read x-ratelimit-* for the live budget rather than assuming this figure.\",\"dateModified\":\"2026-09-17T13:14:46.794Z\",\"isPartOf\":{\"@id\":\"https://docs.ostium.com#website\"},\"breadcrumb\":{\"@id\":\"https://docs.ostium.com/api-reference/orders/one-time-onboarding-gasless#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.ostium.com/api-reference/orders/one-time-onboarding-gasless#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Ostium\",\"item\":\"https://docs.ostium.com\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"One-time onboarding (gasless)\",\"item\":\"https://docs.ostium.com/api-reference/orders/one-time-onboarding-gasless\"}]},{\"@type\":[\"TechArticle\",\"APIReference\"],\"@id\":\"https://docs.ostium.com/api-reference/orders/one-time-onboarding-gasless#apireference\",\"headline\":\"One-time onboarding (gasless)\",\"name\":\"One-time onboarding (gasless)\",\"description\":\"A one-time setup step, done gaslessly. Sign two EIP-712 messages offline, post the signatures here, and we relay them on chain and pay for it — the partner never sends a transaction or needs ETH. Restricted to allowlisted partner addresses. Contact us before integrating. Send whichever parts you still need; at least one. They are relayed as a single atomic batch, so you are never left with an allowance but no delegate. The response reads both values back from chain, so state.missing reflects what is actually set rather than what we submitted. ### The two signatures delegation lets the OstiumAtomicTrading wrapper place trades for you. - Domain: { name: \\\"Ostium\\\", version: \\\"1\\\", chainId, verifyingContract: } - Type: OstiumDelegation(address delegator, address delegate, uint256 nonce, uint256 expiry) - The verifying contract is Trading, but delegate is the wrapper — it is the contract that will call Trading for you, so it is what Trading checks. Sending anything else is rejected. permit grants the USDC allowance (ERC-2612). - Domain: { name, version, chainId, verifyingContract: } - Type: Permit(address owner, address spender, uint256 value, uint256 nonce, uint256 deadline) - spender is TradingStorage, not Trading — different contracts. - Read name and version from the token itself; they differ between deployments and a wrong domain produces a signature that fails on chain. Both nonces come from the chain at signing time — Trading.delegatableNonces(partner) and USDC.nonces(partner). ### Addresses | | Arbitrum (42161) | Arbitrum Sepolia (421614) | | --- | --- | --- | | Trading | 0x6D0bA1f9996DBD8885827e1b2e8f6593e7702411 | 0x2A9B9c988393f46a2537B0ff11E98c2C15a95afe | | TradingStorage (permit spender) | 0xccd5891083a8acd2074690f65d3024e7d13d66e7 | 0x0b9F5243B29938668c9Cfbd7557A389EC7Ef88b8 | | USDC | 0xaf88d065e77c8cc2239327c5edb3a432268e5831 | 0xe73B11Fb1e3eeEe8AF2a23079A4410Fe1B370548 | | OstiumAtomicTrading (delegate) | 0x5eB3960C3fd3274cD81fE5972e0de01084bDa325 | 0x32C06a3eC2A40DABf6A8f645f29321cB7236DAA3 | ### Two cases where you send only one half On Arbitrum Sepolia there is no permit. The test token is a plain ERC-20 with no permit or nonces, so no permit signature exists for it. Send approve(, ) from the wallet, then post delegation alone. Mainnet USDC does implement ERC-2612. A contract wallet can permit but not delegate. setDelegateWithSignature uses ecrecover, which no EIP-1271 signature satisfies. Call Trading.setDelegate() from the wallet, then post permit alone. ### Notes - Keep expiry and deadline short — 30 minutes is plenty. Until one passes, anyone holding the signature can relay it. They cannot redirect it: delegate, spender and value are all inside the signed bytes. - Numbers are decimal strings, never JSON numbers. A uint256 nonce or a max allowance exceeds Number.MAX_SAFE_INTEGER, and a rounded value no longer matches what you signed. Rate limit: 10 requests per 10 seconds per IP. Read x-ratelimit-* for the live budget rather than assuming this figure.\",\"url\":\"https://docs.ostium.com/api-reference/orders/one-time-onboarding-gasless\",\"mainEntityOfPage\":{\"@id\":\"https://docs.ostium.com/api-reference/orders/one-time-onboarding-gasless#webpage\"},\"dateModified\":\"2026-09-17T13:14:46.794Z\",\"publisher\":{\"@id\":\"https://docs.ostium.com/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.ostium.com#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Ostium\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Ostium","nav":{"global":{"anchors":[{"anchor":"Website","icon":"globe","href":"https://ostium.com"},{"anchor":"Developers","icon":"code","href":"/developer/sdk/overview"},{"anchor":"Blog","icon":"newspaper","href":"https://www.ostium.com/blog"},{"anchor":"X/Twitter","icon":"twitter","href":"https://www.x.com/Ostium"}]},"tabs":[{"tab":"Traders","pages":[{"title":"Traders","href":"/traders/welcome"}]},{"tab":"Vault","pages":[{"title":"Vault","href":"/vault/overview"}]},{"tab":"Protocol","pages":[{"title":"Protocol","href":"/protocol/how-ostium-works"}]},{"tab":"Developers","pages":[{"title":"Developers","href":"/developer/sdk/overview"}]},{"tab":"API Reference","groups":[{"group":"Overview","pages":[{"title":"Overview","href":"/api-reference/overview"}]},{"group":"Prices","pages":[{"title":"Latest price for every pair","deprecated":null,"version":null,"openapi":"/api-reference/openapi.json get /v1/prices","href":"/api-reference/prices/latest-price-for-every-pair"},{"title":"Latest price for one pair","deprecated":null,"version":null,"openapi":"/api-reference/openapi.json get /v1/prices/{pair}","href":"/api-reference/prices/latest-price-for-one-pair"},{"title":"OHLC candles","deprecated":null,"version":null,"openapi":"/api-reference/openapi.json post /v1/ohlc","href":"/api-reference/prices/ohlc-candles"},{"title":"Price Stream","href":"/api-reference/price-stream"}]},{"group":"Liquidity","pages":[{"title":"Price for a given trade size","deprecated":null,"version":null,"openapi":"/api-reference/openapi.json get /v1/depth/{pair}/quote","href":"/api-reference/liquidity/price-for-a-given-trade-size"},{"title":"Price across a range of trade sizes","deprecated":null,"version":null,"openapi":"/api-reference/openapi.json get /v1/depth/{pair}","href":"/api-reference/liquidity/price-across-a-range-of-trade-sizes"}]},{"group":"Markets","pages":[{"title":"Markets","href":"/api-reference/markets"},{"title":"Every listed pair and its live state","deprecated":null,"version":null,"openapi":"/api-reference/openapi.json get /v1/pairs","href":"/api-reference/markets/every-listed-pair-and-its-live-state"},{"title":"When each market is open","deprecated":null,"version":null,"openapi":"/api-reference/openapi.json get /v1/market-hours","href":"/api-reference/markets/when-each-market-is-open"}]},{"group":"Portfolio","pages":[{"title":"Open positions for a wallet","deprecated":null,"version":null,"openapi":"/api-reference/openapi.json get /v1/trades","href":"/api-reference/portfolio/open-positions-for-a-wallet"},{"title":"Limit/stop orders for a wallet","deprecated":null,"version":null,"openapi":"/api-reference/openapi.json get /v1/limits","href":"/api-reference/portfolio/limitstop-orders-for-a-wallet"},{"title":"Order history for a wallet","description":"Everything that has already happened, newest first: fills of every kind and the orders that were cancelled. Pending orders are excluded — they have not happened yet.\n\n### Narrowing the log\n\n| Want | Send |\n| --- | --- |\n| Fills only | `isCancelled=false` |\n| Cancellations only | `isCancelled=true` |\n| Both | omit `isCancelled` |\n| One kind of event | `orderAction=Open` |\n| The last 7 days | `since=\u003cunix seconds>` |\n\n`orderAction` is one of `Open`, `Close`, `TakeProfit`, `StopLoss`, `Liquidation`, `RemoveCollateral`, `CloseDayTrade`. Bounding by `since` rather than paging by `skip` is the better way through a long history — it is indexed on execution time.\n\n### Reading a fill\n\n```json\n{\n \"orderAction\": \"Liquidation\",\n \"price\": 4511.41,\n \"collateral\": 9.4,\n \"leverage\": 50,\n \"closePercent\": 100,\n \"profitPercent\": -73.93,\n \"totalProfitPercent\": -100,\n \"amountSentToTrader\": 0,\n \"liquidationFee\": 2.327023,\n \"pair\": { \"id\": \"5\", \"from\": \"XAU\", \"to\": \"USD\" }\n}\n```\n\nA liquidated XAU-USD position: the whole position closed, the trader got nothing back, and 2.33 USDC went to the liquidator.\n\n| Field | Means |\n| --- | --- |\n| `closePercent` | How much of the position this fill closed — `50` is half, `100` is all |\n| `profitPercent` | PnL on this fill, as a percentage |\n| `totalProfitPercent` | PnL on the position as a whole; a liquidation reads `-100` |\n| `amountSentToTrader` | USDC actually returned to the wallet |\n| `priceImpactP` | How far the fill moved from mid, as a percentage |\n\nThose four are populated on closing fills; on an `Open` they are `0`. Fee fields (`fundingFee`, `rolloverFee`, `closeFee`, `liquidationFee`, `devFee`, `vaultFee`, `oracleFee`) are USDC amounts settled by this fill.\n\nRate limit: 60 requests per 10 seconds per IP. This budget is **shared across `/v1/pairs`, `/v1/trades`, `/v1/limits` and `GET /v1/orders`**, not counted per path. Read `x-ratelimit-*` for the live budget rather than assuming this figure.","deprecated":null,"version":null,"openapi":"/api-reference/openapi.json get /v1/orders","href":"/api-reference/portfolio/order-history-for-a-wallet"}]},{"group":"Orders","pages":[{"title":"One-time onboarding (gasless)","deprecated":null,"version":null,"openapi":"/api-reference/openapi.json post /v1/onboard","href":"/api-reference/orders/one-time-onboarding-gasless"},{"title":"Submit an order","description":"One endpoint, three actions. `type` picks between opening a position, closing one (fully or partially), and withdrawing collateral from one. Pick the variant in the request body below to see the fields each needs.\n\n### Reading the response\n\n**`status` is the answer, not the HTTP code.** A `200` means the order was submitted and settled; `status` says how it settled:\n\n| `status` | What happened |\n| --- | --- |\n| `filled` | It executed. `execution` carries the `tradeId` and the resulting amounts. |\n| `reverted` | It reached the chain and changed nothing. `reason` says why. |\n\n### Signing\n\nEvery intent is an EIP-712 signature under this domain:\n\n```json\n{\n \"name\": \"OstiumAtomicTrading\",\n \"version\": \"1\",\n \"chainId\": 42161,\n \"verifyingContract\": \"0x5eB3960C3fd3274cD81fE5972e0de01084bDa325\"\n}\n```\n\nOn Arbitrum Sepolia (`421614`) it is a different contract, so the domain differs too:\n\n```json\n{\n \"name\": \"OstiumAtomicTrading\",\n \"version\": \"1\",\n \"chainId\": 421614,\n \"verifyingContract\": \"0x32C06a3eC2A40DABf6A8f645f29321cB7236DAA3\"\n}\n```\n\nThe struct you sign is the `intent` object itself, and its `primaryType` is named after the `type` you send:\n\n| `type` | `primaryType` |\n| --- | --- |\n| `open` | `OpenIntent` |\n| `close` | `CloseIntent` |\n| `removeCollateral` | `RemoveCollateralIntent` |\n\nDeclare the fields in the order the request body lists them, **and with exactly these solidity widths**. EIP-712 hashes the field names AND types into a single typehash, so a different order — or `uint256` where the contract says `uint192` — is a different type: the digest changes and the contract cannot match your signature. The JSON schema below shows `string` and `integer`, which cannot express the widths, so take them from here:\n\n```\nOpenIntent(uint256 collateral,uint192 openPrice,uint192 tp,uint192 sl,address trader,\n uint32 leverage,uint16 pairIndex,bool buy,bool isDayTrade,address builder,\n uint32 builderFee,uint256 slippageP,uint256 nonce,uint256 deadline)\n\nCloseIntent(address trader,uint16 pairIndex,uint8 index,uint256 tradeId,\n uint16 closePercentage,uint192 marketPrice,uint32 slippageP,uint256 nonce,\n uint256 deadline)\n\nRemoveCollateralIntent(address trader,uint16 pairIndex,uint8 index,uint256 tradeId,\n uint256 removeAmount,uint256 nonce,uint256 deadline)\n```\n\n(Line breaks above are for reading only — the canonical type string has none.)\n\nAs a viem `signTypedData` call:\n\n```ts\nawait account.signTypedData({\n domain, // the OstiumAtomicTrading domain above\n primaryType: 'OpenIntent',\n types: {\n OpenIntent: [\n { name: 'collateral', type: 'uint256' },\n { name: 'openPrice', type: 'uint192' },\n { name: 'tp', type: 'uint192' },\n { name: 'sl', type: 'uint192' },\n { name: 'trader', type: 'address' },\n { name: 'leverage', type: 'uint32' },\n { name: 'pairIndex', type: 'uint16' },\n { name: 'buy', type: 'bool' },\n { name: 'isDayTrade', type: 'bool' },\n { name: 'builder', type: 'address' },\n { name: 'builderFee', type: 'uint32' },\n { name: 'slippageP', type: 'uint256' },\n { name: 'nonce', type: 'uint256' },\n { name: 'deadline', type: 'uint256' },\n ],\n },\n message: intent,\n});\n```\n\nTo check your work without spending anything, the wrapper exposes `hashOpenIntent`, `hashCloseIntent` and `hashRemoveCollateralIntent` as view functions — a local digest that matches those is a signature the contract will accept.\n\nSign as `trader`: an EOA, or a contract wallet that answers EIP-1271.\n\n**One trap the schema cannot warn you about.** `slippageP` is a `uint256` on `OpenIntent` but a `uint32` on `CloseIntent`. The contract types them differently, and signing the wrong width silently changes the digest.\n\n### Limits\n\nChecked before anything is submitted, and tighter than the contract itself — an intent the contract would accept can still be refused here:\n\n| Field | Allowed | Meaning |\n| --- | --- | --- |\n| `slippageP` | `0 \u003c slippageP ≤ 100` | scaled by 10000, so `100` is 1% — the most you can tolerate |\n| `closePercentage` | `0 \u003c closePercentage ≤ 10000` | scaled by 10000, so `10000` is 100% and `5000` is half |\n| `deadline` | `now \u003c deadline ≤ now + 60s` | unix seconds |\n\nBoth percentages use the same base of **10000**. Neither may be `0`: a zero `slippageP` cannot fill, and the protocol reads a zero `closePercentage` as a *full* close.\n\n### Before your first order\n\nThe trader must be an allowlisted partner, and onboarded once per chain through `POST /v1/onboard`. Without the delegation an order reaches the chain and reverts as `NotDelegate`.\n\n### Field notes\n\n- **Send amounts as strings of plain digits** — `\"50000000\"`, not `5e7` or `50000000`. They are too large for a JSON number, and rounding one changes the digest.\n- **Addresses can be any casing.** Checksummed, lowercase or uppercase all work.\n- **Every field is signed**, `builder` and `builderFee` included. Nothing can be added or altered between you and the contract.\n\nRate limit: 30 requests per 10 seconds per IP. Read `x-ratelimit-*` for the live budget rather than assuming this figure.","deprecated":null,"version":null,"openapi":"/api-reference/openapi.json post /v1/orders","href":"/api-reference/orders/submit-an-order"}]},{"group":"Status","pages":[{"title":"Service status","deprecated":null,"version":null,"openapi":"/api-reference/openapi.json get /v1/status","href":"/api-reference/status/service-status"}]}]}]},"title":"One-time onboarding (gasless) - Ostium","buildId":"dpl_cadrRc5H9XQiJQbDFN5mcYRs2PAj","deploymentId":"dpl_cadrRc5H9XQiJQbDFN5mcYRs2PAj","dynamics":["c2aa737661","2b790eda92","b0f84f53ff","a9bb46feb3"],"chunks":["OpenApiReferenceProvider-DKJwRUt2.js","utils-CWD_VS3u.js","cloneDeep-izs3Hzxb.js","_Uint8Array-BVIqJ90l.js","isObjectLike-C8vmzPEN.js","isArray-Dcu1dQ5t.js","utils-oFjciODD.js","toString-BX-iyh8G.js","isSymbol-CSH9U2Gm.js","addUniqueKeysToSchema-C76WsxC3.js","snippetPresets-DvPMxpX_.js","style-to-object-BY3JEAdk.js","v4-DIgz8zy6.js","ApiExamples-CuNQfndH.js","isEqual-BfaiBbcl.js","usePlaygroundInputsStore-BVFpVUoF.js","shallow-t59Jg5Kr.js","getApiPlaygroundDisplay-C_JZ4amX.js","CodeSnippet-CLRDnx_h.js","CodeSnippets-C2myU4kc.js","Page-7TwLLapg.js","getPastedJson-B-JaVXw7.js","icon-BA-iDhH3.js","MethodPill-D4qZABFp.js","DuplicateIcon-THIDKO1O.js","createIcon-CYYJwgFd.js","DynamicMarkdown-UGQuxDKp.js","SectionHeading-jYELb23h.js","axios-vUo_bRtp.js","Markdown-DKsLa3wy.js","clsx-CefdjKz2.js","lib--scc4DXi.js","lib-B8HbPgcC.js","html-void-elements-mCSPHezU.js","dist-BaREnp3o.js","katex-YMM-FMeD.js","marked.esm-CZWythHk.js","AssistantPageLink-sgx_0XzF.js"]}