# Create Account Source: https://docs.otim.com/api-reference/create-account POST /accounts Creates a new Account under an existing Entity. The Account is automatically provisioned on your Platform's default chain. If auto-earn is enabled in your Platform Settings, the Account will be configured to earn yield on idle balances. # Create Counterparty Source: https://docs.otim.com/api-reference/create-counterparty POST /counterparties Creates a new Counterparty under an existing Entity. A Counterparty represents an external party that funds can be sent to or received from. Counterparties can be either wallet addresses or bank accounts. # Create Entity Source: https://docs.otim.com/api-reference/create-entity POST /entities Creates a new Entity and initiates the verification flow. The response includes links that the Entity can use to complete identity verification (KYC for individuals, KYB for businesses) and accept the terms of service. # Create IBAN Source: https://docs.otim.com/api-reference/create-iban POST /ibans Issues a new IBAN for a verified Entity. The IBAN is linked to an Account, and any fiat deposited to it will be automatically converted and routed into that Account. The Entity must have completed verification (KYC/KYB) before an IBAN can be issued. # Create Transfer Source: https://docs.otim.com/api-reference/create-transfer POST /transfers Creates a transfer from an Account. You can send funds to another Account on your Platform (book transfer) or to an external Counterparty (on-chain or wire transfer). Provide either `destinationAccountId` or `counterpartyId`, but not both. # Delete Counterparty Source: https://docs.otim.com/api-reference/delete-counterparty DELETE /counterparties/{id} Permanently deletes a Counterparty. This cannot be undone. Any pending transfers to this Counterparty will fail. # Delete IBAN Source: https://docs.otim.com/api-reference/delete-iban DELETE /ibans/{id} Deactivates and deletes an IBAN. Any future deposits to this IBAN will be rejected. This cannot be undone. # Get Account Source: https://docs.otim.com/api-reference/get-account GET /accounts/{id} Returns the details of a single Account by ID, including current balances and auto-earn status. # Get Counterparty Source: https://docs.otim.com/api-reference/get-counterparty GET /counterparties/{id} Returns the details of a single Counterparty by ID. # Get Entity Source: https://docs.otim.com/api-reference/get-entity GET /entities/{id} Returns the details of a single Entity, including their current verification status. Use this endpoint to check whether an Entity has completed KYC/KYB and is eligible for fiat capabilities like IBANs. # Get IBAN Source: https://docs.otim.com/api-reference/get-iban GET /ibans/{id} Returns the details of a single IBAN by ID. # Get Platform Settings Source: https://docs.otim.com/api-reference/get-platform-settings GET /platform/settings Returns the current settings for your Platform, including auto-earn configuration, default chain, and webhook URL. # Get Transfer Source: https://docs.otim.com/api-reference/get-transfer GET /transfers/{id} Returns the details of a single transfer by ID. # List Accounts Source: https://docs.otim.com/api-reference/list-accounts GET /accounts Returns a paginated list of Accounts on your Platform. # List Counterparties Source: https://docs.otim.com/api-reference/list-counterparties GET /counterparties Returns a paginated list of Counterparties on your Platform. # List Entities Source: https://docs.otim.com/api-reference/list-entities GET /entities Returns a paginated list of Entities on your Platform. # List IBANs Source: https://docs.otim.com/api-reference/list-ibans GET /ibans Returns a paginated list of IBANs on your Platform. # List Transfers Source: https://docs.otim.com/api-reference/list-transfers GET /transfers Returns a paginated list of transfers on your Platform. # Update Account Source: https://docs.otim.com/api-reference/update-account PATCH /accounts/{id} Updates an Account's display name or metadata. Only the fields included in the request body will be changed. # Update Counterparty Source: https://docs.otim.com/api-reference/update-counterparty PATCH /counterparties/{id} Updates a Counterparty's display name. # Update Platform Settings Source: https://docs.otim.com/api-reference/update-platform-settings PATCH /platform/settings Updates your Platform settings. Any fields not included in the request body will remain unchanged. # Account Source: https://docs.otim.com/core-objects/account Holds, routes, and earns yield on funds. An Account is the object on Otim that holds, routes, and earns yield on funds. Every Account belongs to a Platform and has a wallet address on a specific blockchain network. Accounts are the core primitive you interact with when moving money through Otim. Accounts serve three main purposes: 1. **Holding funds.** Each Account maintains real-time balances (available, pending, locked, and holding) so you always know exactly where your money is. 2. **Routing funds.** Accounts are the source and destination for transfers, orchestrations, and automated instructions. When you set up an Otim Instruction to transfer ETH every week, that instruction runs against an Account. When an Orchestrator settles a cross-chain payment, the funds land in an Account. 3. **Earning yield.** Accounts can be configured to automatically earn yield on idle funds, so capital that would otherwise sit dormant is put to work. ## Linking to an Entity Accounts can optionally be linked to an Entity. This is not required for basic functionality. An Account without an Entity can still hold funds, execute instructions, and participate in orchestrations. However, linking an Entity unlocks additional capabilities like fiat deposit rails (via IBANs) and external transfers (via Counterparties). A single Entity can own multiple Accounts. This is useful when you want to separate funds by purpose (e.g., a treasury Account and an operations Account) while keeping them under the same verified identity. # Counterparty Source: https://docs.otim.com/core-objects/counterparty An external wallet or bank account for outbound transfers. A Counterparty represents an external destination that funds can be sent to or from Otim. This can be an external wallet address on any supported chain or a traditional bank account. Counterparties are how you model the "other side" of an outbound transfer. Every Counterparty belongs to an Entity. This creates a clear ownership chain: your Platform owns the Entity, the Entity has been verified through KYC/KYB, and the Counterparty is a known destination tied to that verified identity. This structure ensures that outbound transfers always have a traceable path from source to destination. ## Types of Counterparties Counterparties come in two forms: * **Wallet**: An external blockchain address. Use this when you need to send tokens to an address outside of your Platform's Accounts. * **Bank account**: A traditional bank account with routing and account details. Use this when you need to send fiat to an external bank. ## When to use Counterparties Counterparties are required whenever you want to move funds outside of your Platform. Transfers between your own Accounts do not need a Counterparty since both sides are already within your Platform. But as soon as the destination is external, you need a Counterparty to represent it. A single Entity can have multiple Counterparties. For example, a verified business Entity might have one Counterparty for a partner's wallet address and another for their operating bank account. # Entity Source: https://docs.otim.com/core-objects/entity A verified person or business. An Entity represents a verified person or business on Otim. Entities are created through a KYC (Know Your Customer) or KYB (Know Your Business) verification process and are scoped to the Platform that created them. Entities exist because certain financial operations require a verified identity behind them. While Accounts can function on their own for on-chain operations, an Entity is what bridges the gap between on-chain and off-chain finance. ## What verification unlocks Before an Entity is verified, it is essentially a placeholder with a pending status. Once verification is complete, the Entity unlocks three key capabilities: 1. **Fiat deposit rails.** A verified Entity can be issued an IBAN, which allows it to receive fiat deposits that automatically route into a linked Account. 2. **External transfer destinations.** A verified Entity can create Counterparties, which are external wallet addresses or bank accounts that funds can be sent to. 3. **KYC-gated flows.** Some operations on Otim require a verified identity by design. Having a verified Entity attached to an Account ensures those flows are available. ## Entity types An Entity can represent either an individual person or a business. The verification process differs depending on the type: * **Individual**: Goes through KYC, which typically requires personal identification documents. * **Business**: Goes through KYB, which typically requires business registration documents and beneficial ownership information. ## Relationship to Accounts An Entity can own multiple Accounts. This lets you model real-world structures where a single person or business manages several financial accounts for different purposes. Linking an Entity to an Account is optional, but once linked, that Account inherits access to the Entity's fiat rails and counterparties. # IBAN Source: https://docs.otim.com/core-objects/iban A fiat deposit rail issued to a verified Entity. An IBAN is a fiat deposit rail that allows a verified Entity to receive traditional bank transfers into Otim. When someone sends fiat to the IBAN, the funds are automatically converted and routed into an Account linked to that Entity. IBANs are the on-ramp from traditional banking into the Otim platform. They bridge the gap between fiat and on-chain finance by giving each verified Entity a dedicated bank account number that external parties can send money to. Otim supports every G7 currency and many more. ## How it works 1. A verified Entity is issued an IBAN. 2. The IBAN is linked to one of the Entity's Accounts. 3. When fiat arrives at the IBAN, Otim automatically routes the funds into that Account. From there, the funds are available for any operation you would normally perform with an Account: transfers, orchestrations, instructions, or simply holding and earning yield. ## Requirements Only verified Entities can receive IBANs. This means the Entity must have completed KYC (for individuals) or KYB (for businesses) before an IBAN can be issued. This requirement exists because fiat deposit rails are subject to regulatory compliance, and a verified identity is necessary to operate them. ## When to use IBANs IBANs are useful whenever you need to accept fiat payments from external parties who use traditional banking. Common scenarios include: * Accepting customer payments via bank transfer * Receiving payroll or vendor payments into an Otim Account * On-ramping fiat into Otim for cross-chain settlement via Orchestrators # Platform Source: https://docs.otim.com/core-objects/platform Top-level container for all objects on Otim. A Platform represents your company on Otim. It is the top-level container that scopes every other object in the system. Depending on your use case, you will create accounts, entities, and other resources under your Platform to model your financial operations. # Authentication Source: https://docs.otim.com/docs/api/authentication All API requests must include a valid API key in the `Authorization` header using the Bearer scheme. ```bash theme={null} curl https://api.otim.com/v0/accounts \ -H "Authorization: Bearer sk_..." ``` ## API keys API keys are created in the Developer section of your dashboard. Each key is scoped to your Platform and can be independently labeled and revoked. Keys are prefixed with `sk_` and should be treated as secrets. Do not expose them in client-side code, public repositories, or logs. ## Environments | Environment | Base URL | Description | | ----------- | --------------------------------- | ----------------------------------------------------- | | Sandbox | `https://sandbox.api.otim.com/v0` | For development and testing. No real funds are moved. | | Production | `https://api.otim.com/v0` | For live operations with real funds. | Sandbox and production use separate API keys. You can create and manage keys for each environment independently from your dashboard. ## Error responses Requests with a missing or invalid API key return a `401 Unauthorized` response: ```json theme={null} { "error": "Invalid API key.", "code": "unauthorized" } ``` # Idempotency Source: https://docs.otim.com/docs/api/idempotency To safely retry requests without creating duplicate resources, you can include an `Idempotency-Key` header on any POST request. ```bash theme={null} curl -X POST https://api.otim.com/v0/transfers \ -H "Authorization: Bearer sk_..." \ -H "Content-Type: application/json" \ -H "Idempotency-Key: your-unique-key-here" \ -d '{ "sourceAccountId": "acc_01JA1B2C3D4E5F6G7H8J9K0L", "destinationAccountId": "acc_02XY9Z8W7V6U5T4S3R2Q1P0N", "amount": "100.00" }' ``` ## How it works * If a request with the same `Idempotency-Key` has already been processed, the API returns the original response without executing the operation again. * Idempotency keys are scoped to your Platform and expire after 24 hours. * Keys can be any unique string up to 256 characters. UUIDs are a good choice. ## When to use idempotency keys Use idempotency keys on any request where a retry could cause unintended side effects, especially: * **Creating transfers** to prevent duplicate payments. * **Creating Accounts or Entities** to prevent duplicate resources if a network timeout occurs. GET, PATCH, and DELETE requests are naturally idempotent and do not require an idempotency key. # Pagination Source: https://docs.otim.com/docs/api/pagination All list endpoints return paginated results using cursor-based pagination. This ensures consistent results even when new items are created between requests. ## Response format Every list endpoint returns the same pagination structure: ```json theme={null} { "data": [], "hasMore": true, "nextCursor": "abc123" } ``` | Field | Type | Description | | ------------ | -------------- | ------------------------------------------------------------------------- | | `data` | array | The list of items for the current page. | | `hasMore` | boolean | Whether there are more items after this page. | | `nextCursor` | string or null | The cursor to use for the next page. `null` when there are no more items. | ## Fetching the next page To fetch the next page, pass the `nextCursor` value as the `cursor` query parameter: ```bash theme={null} # First page curl https://api.otim.com/v0/accounts?limit=10 \ -H "Authorization: Bearer sk_..." # Next page curl https://api.otim.com/v0/accounts?limit=10&cursor=abc123 \ -H "Authorization: Bearer sk_..." ``` Continue paginating until `hasMore` is `false`. ## Parameters | Parameter | Default | Max | Description | | --------- | ------- | --- | ----------------------------------------------------- | | `limit` | 50 | 100 | Number of items to return per page. | | `cursor` | - | - | Cursor from a previous response's `nextCursor` field. | # Sandbox & Testing Source: https://docs.otim.com/docs/api/sandbox The sandbox environment lets you build and test your integration without moving real funds. It mirrors the production API with a few differences designed to make testing easier. ## Base URL ``` https://sandbox.api.otim.com/v0 ``` Use sandbox API keys (created separately in your dashboard) to authenticate requests to this environment. ## What works in sandbox The sandbox supports the full API surface. You can create Entities, Accounts, Counterparties, IBANs, and Transfers just like in production. Key differences: * **No real funds are moved.** Transfers are simulated and complete instantly. * **Verification is auto-approved.** Entities created in sandbox are automatically approved for KYC and ToS, so you can test the full flow without waiting for manual review. * **IBANs accept simulated deposits.** You can trigger test deposits to sandbox IBANs from your dashboard. * **Webhooks fire normally.** If you have a webhook URL configured, sandbox events will be delivered to it. Use this to test your webhook handler. ## Test data Sandbox Accounts start with a zero balance. You can fund them by triggering a simulated deposit from your dashboard or by creating a test IBAN and sending a simulated fiat deposit. ## Moving to production When you are ready to go live: 1. Create production API keys in your dashboard. 2. Switch your base URL to `https://api.otim.com/v0`. 3. Ensure your Entities complete real KYC/KYB verification before issuing IBANs or processing live transfers. # Transfer Types Source: https://docs.otim.com/docs/api/transfer-types When you create a transfer, Otim automatically determines the transfer type based on the destination you provide. ## Book transfer A book transfer moves funds between two Accounts on the same Platform. These settle instantly with no fees. ```json theme={null} { "sourceAccountId": "acc_01...", "destinationAccountId": "acc_02...", "amount": "100.00" } ``` Provide `destinationAccountId` to create a book transfer. ## Wire transfer A wire transfer sends funds to an external bank account via a Counterparty. These are subject to standard processing times for the destination banking network. ```json theme={null} { "sourceAccountId": "acc_01...", "counterpartyId": "cpty_01...", "amount": "500.00", "currency": "USD" } ``` Provide a `counterpartyId` that has type `bank_account` to create a wire transfer. ## On-chain transfer An on-chain transfer sends funds to an external wallet via a Counterparty. These settle once the transaction is confirmed on the destination chain. ```json theme={null} { "sourceAccountId": "acc_01...", "counterpartyId": "cpty_02...", "amount": "250.00" } ``` Provide a `counterpartyId` that has type `wallet` to create an on-chain transfer. ## Transfer statuses | Status | Description | | ------------ | --------------------------------------------------------------------- | | `pending` | The transfer has been created and is queued for processing. | | `processing` | The transfer is actively being processed. | | `completed` | The transfer has settled successfully. | | `failed` | The transfer failed. Check the transfer details for more information. | | `cancelled` | The transfer was cancelled before it was processed. | # Webhooks Source: https://docs.otim.com/docs/api/webhooks Webhooks allow you to receive real-time notifications when events happen on your Platform. Instead of polling the API, you can configure a URL that Otim will send POST requests to whenever a relevant event occurs. ## Setup Configure your webhook URL via the [Update Platform Settings](/api-reference/update-platform-settings) endpoint: ```bash theme={null} curl -X PATCH https://api.otim.com/v0/platform/settings \ -H "Authorization: Bearer sk_..." \ -H "Content-Type: application/json" \ -d '{ "webhookUrl": "https://yourapp.com/webhooks/otim" }' ``` Set `webhookUrl` to `null` to disable webhooks. ## Payload format All webhook payloads follow the same structure: ```json theme={null} { "id": "evt_01JA1B2C3D4E5F6G7H8J9K0L", "type": "transfer.completed", "createdAt": "2025-01-15T12:00:00Z", "data": { } } ``` | Field | Type | Description | | ----------- | ------ | ---------------------------------------------- | | `id` | string | Unique identifier for the event. | | `type` | string | The event type. | | `createdAt` | string | ISO 8601 timestamp of when the event occurred. | | `data` | object | The full object associated with the event. | ## Event types ### Transfers | Event | Description | | --------------------- | ---------------------------------------- | | `transfer.created` | A new transfer has been initiated. | | `transfer.processing` | The transfer is being processed. | | `transfer.completed` | The transfer has completed successfully. | | `transfer.failed` | The transfer has failed. | ### Entities | Event | Description | | --------------------- | ----------------------------------------------- | | `entity.kyc.approved` | An Entity has passed identity verification. | | `entity.kyc.rejected` | An Entity's identity verification was rejected. | | `entity.tos.approved` | An Entity has accepted the terms of service. | ### Accounts | Event | Description | | ----------------- | ----------------------------------- | | `account.created` | A new Account has been provisioned. | ### IBANs | Event | Description | | ----------------------- | ----------------------------------------------------------------------------- | | `iban.deposit.received` | A fiat deposit has been received at an IBAN and routed to the linked Account. | ## Responding to webhooks Your endpoint should return a `200` status code within 10 seconds. If Otim does not receive a `200` response, the webhook will be retried up to 5 times with exponential backoff. ## Verifying signatures Each webhook request includes a `X-Otim-Signature` header containing an HMAC-SHA256 signature of the request body. Verify this signature to ensure the request came from Otim. ```javascript theme={null} const crypto = require('crypto'); function verifyWebhook(body, signature, secret) { const expected = crypto .createHmac('sha256', secret) .update(body) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expected) ); } ``` Your webhook signing secret is available in the Developer section of your dashboard. # Actions & Instructions Source: https://docs.otim.com/docs/concepts/actions-instructions Actions and Instructions work together as the building blocks of Otim's execution engine. Actions are smart contracts that define what can be executed, while Instructions specify how and when these Actions should be executed. Think of Actions as the "what" and Instructions as the "when" and "how" of each automated operation. To give a simple example, you might create an Instruction to transfer 0.1 ETH to your friend Alice every week. Let's deconstruct this sentence: * "transfer" here refers to executing the `Transfer` Action contract * "0.1 ETH" and "to Alice" refer to specific `Transfer` Action arguments * "every week" refers to specific Instruction scheduling parameters # Delegation & EIP-7702 Source: https://docs.otim.com/docs/concepts/delegation EIP-7702 "delegation" is the process that allows users to enable Otim's smart features for their Externally Owned Accounts (EOAs). Users can easily delegate to Otim by following the onboarding flow in the App. During this process, users can decide to upgrade their EOA on a specific blockchain or for all EVM blockchains that implement EIP-7702. ## What does this mean? In this context, to "delegate" means to authorize a smart contract to act on your behalf. After you delegate to Otim, your EOA can be called as if it were the Otim core smart contract. This enables all sorts of automation functionality since Otim can now interact with your EOA programmatically. ## How does it work? At a high level, you can delegate to Otim by signing a specific delegation message and sending this signed message to Otim. Once Otim receives and parses the signed message, it will send a transaction on-chain to perform the delegation. The Otim App's onboarding flow is designed to walk you through this process and explain more details along the way. ## EIP-7702 [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) is a new transaction type that allows EOAs to set persistent runtime bytecode in their account. This enables Ethereum accounts to delegate execution authority to a trusted smart contract while maintaining full control of their assets. At its core, EIP-7702 acts as a secure connector that allows EOAs to tap into smart contract functionality without changing their fundamental security model. This creates a foundation for building sophisticated account features while preserving the simplicity and security of traditional Ethereum accounts. During an EIP-7702 transaction, a "delegation designator" of the form `0xef0100 || delegate_address` is set in the `code` field of the signing EOA. Subsequently, all code-executing operations called on the EOA will load the runtime bytecode of the contract deployed at `delegate_address` and execute in the context of the EOA. The delegation process itself is straightforward: users first authorize a specific smart contract by signing an EIP-7702 authorization. This authorization is then submitted to the network through an EIP-7702 transaction, which establishes the delegation relationship. Once delegated, the account can utilize all the functionality of the authorized smart contract while maintaining its sovereignty. Users can revoke this delegation at any time by submitting a transaction that clears the delegation, effectively removing the smart contract's ability to act on behalf of the account. # Deployed Addresses Source: https://docs.otim.com/docs/developers/deployed-addresses ## Core Contracts | Name | Address | | :------------------- | :------------------------------------------------------------------------------------------------------------------------ | | `ActionManager` | **[0xe41Dfd2706e3b35c230696F3107eda85eE73cE3e](https://etherscan.io/address/0xe41Dfd2706e3b35c230696F3107eda85eE73cE3e)** | | `FeeTokenRegistry` | **[0xa972A2C093456c7298C3527fc87DE41F89A2eBaE](https://etherscan.io/address/0xa972A2C093456c7298C3527fc87DE41F89A2eBaE)** | | `Gateway` | **[0x30891995b7aD3d4d38D16E14EE91B57372C091d2](https://etherscan.io/address/0x30891995b7aD3d4d38D16E14EE91B57372C091d2)** | | `InstructionStorage` | **[0xBeb6f683a7a247De61B9D56885f65331D7F55030](https://etherscan.io/address/0xBeb6f683a7a247De61B9D56885f65331D7F55030)** | | `OtimDelegate` | **[0x23E5F9C457A69Ce776d20A8fe812A6701D66fcE8](https://etherscan.io/address/0x23E5F9C457A69Ce776d20A8fe812A6701D66fcE8)** | | `Treasury` | **[0x1FDdC4EEEc10E3317B27563Ad0126285A329350d](https://etherscan.io/address/0x1FDdC4EEEc10E3317B27563Ad0126285A329350d)** | ## Action Contracts | Name | Address | | :---------------------------- | :------------------------------------------------------------------------------------------------------------------------ | | `DeactivateInstructionAction` | **[0x0B393a117eE8e2FAF4FB13fd181201fDD924Dd83](https://etherscan.io/address/0x0B393a117eE8e2FAF4FB13fd181201fDD924Dd83)** | | `RefuelAction` | **[0x8FD4DA969D38e512da77cA2f7C8C6D504A855543](https://etherscan.io/address/0x8FD4DA969D38e512da77cA2f7C8C6D504A855543)** | | `RefuelERC20Action` | **[0x3Ad8f77fE11Ff9EB5c5776Eb87f605e889779Aac](https://etherscan.io/address/0x3Ad8f77fE11Ff9EB5c5776Eb87f605e889779Aac)** | | `TransferAction` | **[0xE598ce7062A835A908C1e49D8d67E927E3c83F23](https://etherscan.io/address/0xE598ce7062A835A908C1e49D8d67E927E3c83F23)** | | `TransferERC20Action` | **[0x695175ba4F2594E0A42cF89790913dCc1CcCeD72](https://etherscan.io/address/0x695175ba4F2594E0A42cF89790913dCc1CcCeD72)** | | `UniswapV3ExactInputAction` | **[0xd8BE0f31BbF564C584599069ED3cb93b9b2264c7](https://etherscan.io/address/0xd8BE0f31BbF564C584599069ED3cb93b9b2264c7)** | ## Core Contracts | Name | Address | | :------------------- | :------------------------------------------------------------------------------------------------------------------------ | | `ActionManager` | **[0xe41Dfd2706e3b35c230696F3107eda85eE73cE3e](https://basescan.org/address/0xe41Dfd2706e3b35c230696F3107eda85eE73cE3e)** | | `FeeTokenRegistry` | **[0xa972A2C093456c7298C3527fc87DE41F89A2eBaE](https://basescan.org/address/0xa972A2C093456c7298C3527fc87DE41F89A2eBaE)** | | `Gateway` | **[0x30891995b7aD3d4d38D16E14EE91B57372C091d2](https://basescan.org/address/0x30891995b7aD3d4d38D16E14EE91B57372C091d2)** | | `InstructionStorage` | **[0xBeb6f683a7a247De61B9D56885f65331D7F55030](https://basescan.org/address/0xBeb6f683a7a247De61B9D56885f65331D7F55030)** | | `OtimDelegate` | **[0x23E5F9C457A69Ce776d20A8fe812A6701D66fcE8](https://basescan.org/address/0x23E5F9C457A69Ce776d20A8fe812A6701D66fcE8)** | | `Treasury` | **[0x1FDdC4EEEc10E3317B27563Ad0126285A329350d](https://basescan.org/address/0x1FDdC4EEEc10E3317B27563Ad0126285A329350d)** | ## Action Contracts | Name | Address | | :---------------------------- | :------------------------------------------------------------------------------------------------------------------------ | | `DeactivateInstructionAction` | **[0x0B393a117eE8e2FAF4FB13fd181201fDD924Dd83](https://basescan.org/address/0x0B393a117eE8e2FAF4FB13fd181201fDD924Dd83)** | | `RefuelAction` | **[0x8FD4DA969D38e512da77cA2f7C8C6D504A855543](https://basescan.org/address/0x8FD4DA969D38e512da77cA2f7C8C6D504A855543)** | | `RefuelERC20Action` | **[0x3Ad8f77fE11Ff9EB5c5776Eb87f605e889779Aac](https://basescan.org/address/0x3Ad8f77fE11Ff9EB5c5776Eb87f605e889779Aac)** | | `TransferAction` | **[0xE598ce7062A835A908C1e49D8d67E927E3c83F23](https://basescan.org/address/0xE598ce7062A835A908C1e49D8d67E927E3c83F23)** | | `TransferERC20Action` | **[0x695175ba4F2594E0A42cF89790913dCc1CcCeD72](https://basescan.org/address/0x695175ba4F2594E0A42cF89790913dCc1CcCeD72)** | | `UniswapV3ExactInputAction` | **[0x8a3E70F61288047d016d9975975dFEfB187Ad6cB](https://basescan.org/address/0x8a3E70F61288047d016d9975975dFEfB187Ad6cB)** | ## Core Contracts | Name | Address | | :------------------- | :----------------------------------------------------------------------------------------------------------------------- | | `ActionManager` | **[0xe41Dfd2706e3b35c230696F3107eda85eE73cE3e](https://arbiscan.io/address/0xe41Dfd2706e3b35c230696F3107eda85eE73cE3e)** | | `FeeTokenRegistry` | **[0xa972A2C093456c7298C3527fc87DE41F89A2eBaE](https://arbiscan.io/address/0xa972A2C093456c7298C3527fc87DE41F89A2eBaE)** | | `Gateway` | **[0x30891995b7aD3d4d38D16E14EE91B57372C091d2](https://arbiscan.io/address/0x30891995b7aD3d4d38D16E14EE91B57372C091d2)** | | `InstructionStorage` | **[0xBeb6f683a7a247De61B9D56885f65331D7F55030](https://arbiscan.io/address/0xBeb6f683a7a247De61B9D56885f65331D7F55030)** | | `OtimDelegate` | **[0x23E5F9C457A69Ce776d20A8fe812A6701D66fcE8](https://arbiscan.io/address/0x23E5F9C457A69Ce776d20A8fe812A6701D66fcE8)** | | `Treasury` | **[0x1FDdC4EEEc10E3317B27563Ad0126285A329350d](https://arbiscan.io/address/0x1FDdC4EEEc10E3317B27563Ad0126285A329350d)** | ## Action Contracts | Name | Address | | :---------------------------- | :----------------------------------------------------------------------------------------------------------------------- | | `DeactivateInstructionAction` | **[0x0B393a117eE8e2FAF4FB13fd181201fDD924Dd83](https://arbiscan.io/address/0x0B393a117eE8e2FAF4FB13fd181201fDD924Dd83)** | | `RefuelAction` | **[0x8FD4DA969D38e512da77cA2f7C8C6D504A855543](https://arbiscan.io/address/0x8FD4DA969D38e512da77cA2f7C8C6D504A855543)** | | `RefuelERC20Action` | **[0x3Ad8f77fE11Ff9EB5c5776Eb87f605e889779Aac](https://arbiscan.io/address/0x3Ad8f77fE11Ff9EB5c5776Eb87f605e889779Aac)** | | `TransferAction` | **[0xE598ce7062A835A908C1e49D8d67E927E3c83F23](https://arbiscan.io/address/0xE598ce7062A835A908C1e49D8d67E927E3c83F23)** | | `TransferERC20Action` | **[0x695175ba4F2594E0A42cF89790913dCc1CcCeD72](https://arbiscan.io/address/0x695175ba4F2594E0A42cF89790913dCc1CcCeD72)** | | `UniswapV3ExactInputAction` | **[0x6b2Cd8195204d43D7113fE23e3dC5f1a30862Ff3](https://arbiscan.io/address/0x6b2Cd8195204d43D7113fE23e3dC5f1a30862Ff3)** | ## Core Contracts | Name | Address | | :------------------- | :----------------------------------------------------------------------------------------------------------------------------------- | | `ActionManager` | **[0xe41Dfd2706e3b35c230696F3107eda85eE73cE3e](https://optimistic.etherscan.io/address/0xe41Dfd2706e3b35c230696F3107eda85eE73cE3e)** | | `FeeTokenRegistry` | **[0xa972A2C093456c7298C3527fc87DE41F89A2eBaE](https://optimistic.etherscan.io/address/0xa972A2C093456c7298C3527fc87DE41F89A2eBaE)** | | `Gateway` | **[0x30891995b7aD3d4d38D16E14EE91B57372C091d2](https://optimistic.etherscan.io/address/0x30891995b7aD3d4d38D16E14EE91B57372C091d2)** | | `InstructionStorage` | **[0xBeb6f683a7a247De61B9D56885f65331D7F55030](https://optimistic.etherscan.io/address/0xBeb6f683a7a247De61B9D56885f65331D7F55030)** | | `OtimDelegate` | **[0x23E5F9C457A69Ce776d20A8fe812A6701D66fcE8](https://optimistic.etherscan.io/address/0x23E5F9C457A69Ce776d20A8fe812A6701D66fcE8)** | | `Treasury` | **[0x1FDdC4EEEc10E3317B27563Ad0126285A329350d](https://optimistic.etherscan.io/address/0x1FDdC4EEEc10E3317B27563Ad0126285A329350d)** | ## Action Contracts | Name | Address | | :---------------------------- | :----------------------------------------------------------------------------------------------------------------------------------- | | `DeactivateInstructionAction` | **[0x0B393a117eE8e2FAF4FB13fd181201fDD924Dd83](https://optimistic.etherscan.io/address/0x0B393a117eE8e2FAF4FB13fd181201fDD924Dd83)** | | `RefuelAction` | **[0x8FD4DA969D38e512da77cA2f7C8C6D504A855543](https://optimistic.etherscan.io/address/0x8FD4DA969D38e512da77cA2f7C8C6D504A855543)** | | `RefuelERC20Action` | **[0x3Ad8f77fE11Ff9EB5c5776Eb87f605e889779Aac](https://optimistic.etherscan.io/address/0x3Ad8f77fE11Ff9EB5c5776Eb87f605e889779Aac)** | | `TransferAction` | **[0xE598ce7062A835A908C1e49D8d67E927E3c83F23](https://optimistic.etherscan.io/address/0xE598ce7062A835A908C1e49D8d67E927E3c83F23)** | | `TransferERC20Action` | **[0x695175ba4F2594E0A42cF89790913dCc1CcCeD72](https://optimistic.etherscan.io/address/0x695175ba4F2594E0A42cF89790913dCc1CcCeD72)** | | `UniswapV3ExactInputAction` | **[0xDeFAa4B69440dd569d295E60241667af8da5C777](https://optimistic.etherscan.io/address/0xDeFAa4B69440dd569d295E60241667af8da5C777)** | ## Core Contracts | Name | Address | | :------------------- | :-------------------------------------------------------------------------------------------------------------------------------- | | `ActionManager` | **[0x5C19E012Ba6473714C1887b211b55C768809f124](https://sepolia.etherscan.io/address/0x5C19E012Ba6473714C1887b211b55C768809f124)** | | `FeeTokenRegistry` | **[0x19c370A2e300429F98c17803984DC5413faAc02E](https://sepolia.etherscan.io/address/0x19c370A2e300429F98c17803984DC5413faAc02E)** | | `Gateway` | **[0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225](https://sepolia.etherscan.io/address/0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225)** | | `InstructionStorage` | **[0x8A26dA63B7300f93c18A472cE440508C284C77d6](https://sepolia.etherscan.io/address/0x8A26dA63B7300f93c18A472cE440508C284C77d6)** | | `OtimDelegate` | **[0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c](https://sepolia.etherscan.io/address/0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c)** | | `Treasury` | **[0xc7536a1e8205C139d25165F6305e20299b792511](https://sepolia.etherscan.io/address/0xc7536a1e8205C139d25165F6305e20299b792511)** | ## Action Contracts | Name | Address | | :---------------------------- | :-------------------------------------------------------------------------------------------------------------------------------- | | `DeactivateInstructionAction` | **[0x1f719754FC21E58b628E02659896309354888058](https://sepolia.etherscan.io/address/0x1f719754FC21E58b628E02659896309354888058)** | | `RefuelAction` | **[0x355A1dA513d010fdD404E138807ea75731d08133](https://sepolia.etherscan.io/address/0x355A1dA513d010fdD404E138807ea75731d08133)** | | `RefuelERC20Action` | **[0xf8a22699d6a8Ff6284898288573B6d5a8893728B](https://sepolia.etherscan.io/address/0xf8a22699d6a8Ff6284898288573B6d5a8893728B)** | | `SweepAction` | **[0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb](https://sepolia.etherscan.io/address/0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb)** | | `SweepERC20Action` | **[0x006BDC1dD878831590851f48A1D82c0E14914125](https://sepolia.etherscan.io/address/0x006BDC1dD878831590851f48A1D82c0E14914125)** | | `SweepCCTPAction` | **[0xa4B51D22e75783a0e52036d574b53A2101Ef8652](https://sepolia.etherscan.io/address/0xa4B51D22e75783a0e52036d574b53A2101Ef8652)** | | `SweepUniswapV3Action` | **[0xE68Cae39ce20B1670dc2Ca17FcA63D0EafB27701](https://sepolia.etherscan.io/address/0xE68Cae39ce20B1670dc2Ca17FcA63D0EafB27701)** | | `TransferAction` | **[0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F](https://sepolia.etherscan.io/address/0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F)** | | `TransferCCTPAction` | **[0x2CA6cAA0B4F5653b1Aa4B4922A3fF34f868112F3](https://sepolia.etherscan.io/address/0x2CA6cAA0B4F5653b1Aa4B4922A3fF34f868112F3)** | | `TransferERC20Action` | **[0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4](https://sepolia.etherscan.io/address/0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4)** | | `UniswapV3ExactInputAction` | **[0x38C4623A081586d873137995a77440B801F5CD7c](https://sepolia.etherscan.io/address/0x38C4623A081586d873137995a77440B801F5CD7c)** | ## Core Contracts | Name | Address | | :------------------- | :-------------------------------------------------------------------------------------------------------------------------------- | | `ActionManager` | **[0x5C19E012Ba6473714C1887b211b55C768809f124](https://sepolia.basescan.org/address/0x5C19E012Ba6473714C1887b211b55C768809f124)** | | `FeeTokenRegistry` | **[0x19c370A2e300429F98c17803984DC5413faAc02E](https://sepolia.basescan.org/address/0x19c370A2e300429F98c17803984DC5413faAc02E)** | | `Gateway` | **[0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225](https://sepolia.basescan.org/address/0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225)** | | `InstructionStorage` | **[0x8A26dA63B7300f93c18A472cE440508C284C77d6](https://sepolia.basescan.org/address/0x8A26dA63B7300f93c18A472cE440508C284C77d6)** | | `OtimDelegate` | **[0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c](https://sepolia.basescan.org/address/0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c)** | | `Treasury` | **[0xc7536a1e8205C139d25165F6305e20299b792511](https://sepolia.basescan.org/address/0xc7536a1e8205C139d25165F6305e20299b792511)** | ## Action Contracts | Name | Address | | :---------------------------- | :-------------------------------------------------------------------------------------------------------------------------------- | | `DeactivateInstructionAction` | **[0x1f719754FC21E58b628E02659896309354888058](https://sepolia.basescan.org/address/0x1f719754FC21E58b628E02659896309354888058)** | | `RefuelAction` | **[0x355A1dA513d010fdD404E138807ea75731d08133](https://sepolia.basescan.org/address/0x355A1dA513d010fdD404E138807ea75731d08133)** | | `RefuelERC20Action` | **[0xf8a22699d6a8Ff6284898288573B6d5a8893728B](https://sepolia.basescan.org/address/0xf8a22699d6a8Ff6284898288573B6d5a8893728B)** | | `SweepAction` | **[0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb](https://sepolia.basescan.org/address/0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb)** | | `SweepERC20Action` | **[0x006BDC1dD878831590851f48A1D82c0E14914125](https://sepolia.basescan.org/address/0x006BDC1dD878831590851f48A1D82c0E14914125)** | | `SweepCCTPAction` | **[0x89972bD81F8081b793D3E4Ce2D092495415016D8](https://sepolia.basescan.org/address/0x89972bD81F8081b793D3E4Ce2D092495415016D8)** | | `SweepUniswapV3Action` | **[0x1E1A662d44d605e524797f2cD155320a0a41051b](https://sepolia.basescan.org/address/0x1E1A662d44d605e524797f2cD155320a0a41051b)** | | `TransferAction` | **[0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F](https://sepolia.basescan.org/address/0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F)** | | `TransferCCTPAction` | **[0x60960EE9957f4280F3A4e5997AfdAf59AEDF94Ec](https://sepolia.basescan.org/address/0x60960EE9957f4280F3A4e5997AfdAf59AEDF94Ec)** | | `TransferERC20Action` | **[0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4](https://sepolia.basescan.org/address/0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4)** | | `UniswapV3ExactInputAction` | **[0x701e90a513c19bb70e9b1bc885Fe65675d057921](https://sepolia.basescan.org/address/0x701e90a513c19bb70e9b1bc885Fe65675d057921)** | ## Core Contracts | Name | Address | | :------------------- | :------------------------------------------------------------------------------------------------------------------------------- | | `ActionManager` | **[0x5C19E012Ba6473714C1887b211b55C768809f124](https://sepolia.arbiscan.io/address/0x5C19E012Ba6473714C1887b211b55C768809f124)** | | `FeeTokenRegistry` | **[0x19c370A2e300429F98c17803984DC5413faAc02E](https://sepolia.arbiscan.io/address/0x19c370A2e300429F98c17803984DC5413faAc02E)** | | `Gateway` | **[0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225](https://sepolia.arbiscan.io/address/0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225)** | | `InstructionStorage` | **[0x8A26dA63B7300f93c18A472cE440508C284C77d6](https://sepolia.arbiscan.io/address/0x8A26dA63B7300f93c18A472cE440508C284C77d6)** | | `OtimDelegate` | **[0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c](https://sepolia.arbiscan.io/address/0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c)** | | `Treasury` | **[0xc7536a1e8205C139d25165F6305e20299b792511](https://sepolia.arbiscan.io/address/0xc7536a1e8205C139d25165F6305e20299b792511)** | ## Action Contracts | Name | Address | | :---------------------------- | :------------------------------------------------------------------------------------------------------------------------------- | | `DeactivateInstructionAction` | **[0x1f719754FC21E58b628E02659896309354888058](https://sepolia.arbiscan.io/address/0x1f719754FC21E58b628E02659896309354888058)** | | `RefuelAction` | **[0x355A1dA513d010fdD404E138807ea75731d08133](https://sepolia.arbiscan.io/address/0x355A1dA513d010fdD404E138807ea75731d08133)** | | `RefuelERC20Action` | **[0xf8a22699d6a8Ff6284898288573B6d5a8893728B](https://sepolia.arbiscan.io/address/0xf8a22699d6a8Ff6284898288573B6d5a8893728B)** | | `SweepAction` | **[0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb](https://sepolia.arbiscan.io/address/0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb)** | | `SweepERC20Action` | **[0x006BDC1dD878831590851f48A1D82c0E14914125](https://sepolia.arbiscan.io/address/0x006BDC1dD878831590851f48A1D82c0E14914125)** | | `TransferAction` | **[0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F](https://sepolia.arbiscan.io/address/0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F)** | | `TransferERC20Action` | **[0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4](https://sepolia.arbiscan.io/address/0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4)** | | `UniswapV3ExactInputAction` | **[0x701e90a513c19bb70e9b1bc885Fe65675d057921](https://sepolia.arbiscan.io/address/0x701e90a513c19bb70e9b1bc885Fe65675d057921)** | ## Core Contracts | Name | Address | | :------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- | | `ActionManager` | **[0x5C19E012Ba6473714C1887b211b55C768809f124](https://sepolia-optimism.etherscan.io/address/0x5C19E012Ba6473714C1887b211b55C768809f124)** | | `FeeTokenRegistry` | **[0x19c370A2e300429F98c17803984DC5413faAc02E](https://sepolia-optimism.etherscan.io/address/0x19c370A2e300429F98c17803984DC5413faAc02E)** | | `Gateway` | **[0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225](https://sepolia-optimism.etherscan.io/address/0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225)** | | `InstructionStorage` | **[0x8A26dA63B7300f93c18A472cE440508C284C77d6](https://sepolia-optimism.etherscan.io/address/0x8A26dA63B7300f93c18A472cE440508C284C77d6)** | | `OtimDelegate` | **[0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c](https://sepolia-optimism.etherscan.io/address/0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c)** | | `Treasury` | **[0xc7536a1e8205C139d25165F6305e20299b792511](https://sepolia-optimism.etherscan.io/address/0xc7536a1e8205C139d25165F6305e20299b792511)** | ## Action Contracts | Name | Address | | :---------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- | | `DeactivateInstructionAction` | **[0x1f719754FC21E58b628E02659896309354888058](https://sepolia-optimism.etherscan.io/address/0x1f719754FC21E58b628E02659896309354888058)** | | `RefuelAction` | **[0x355A1dA513d010fdD404E138807ea75731d08133](https://sepolia-optimism.etherscan.io/address/0x355A1dA513d010fdD404E138807ea75731d08133)** | | `RefuelERC20Action` | **[0xf8a22699d6a8Ff6284898288573B6d5a8893728B](https://sepolia-optimism.etherscan.io/address/0xf8a22699d6a8Ff6284898288573B6d5a8893728B)** | | `SweepAction` | **[0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb](https://sepolia-optimism.etherscan.io/address/0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb)** | | `SweepERC20Action` | **[0x006BDC1dD878831590851f48A1D82c0E14914125](https://sepolia-optimism.etherscan.io/address/0x006BDC1dD878831590851f48A1D82c0E14914125)** | | `TransferAction` | **[0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F](https://sepolia-optimism.etherscan.io/address/0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F)** | | `TransferERC20Action` | **[0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4](https://sepolia-optimism.etherscan.io/address/0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4)** | | `UniswapV3ExactInputAction` | **[0x701e90a513c19bb70e9b1bc885Fe65675d057921](https://sepolia-optimism.etherscan.io/address/0x701e90a513c19bb70e9b1bc885Fe65675d057921)** | ## Core Contracts | Name | Address | | :------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | | `ActionManager` | **[0x5C19E012Ba6473714C1887b211b55C768809f124](https://explorer-host.pecorino.signet.sh/address/0x5C19E012Ba6473714C1887b211b55C768809f124)** | | `FeeTokenRegistry` | **[0x19c370A2e300429F98c17803984DC5413faAc02E](https://explorer-host.pecorino.signet.sh/address/0x19c370A2e300429F98c17803984DC5413faAc02E)** | | `Gateway` | **[0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225](https://explorer-host.pecorino.signet.sh/address/0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225)** | | `InstructionStorage` | **[0x8A26dA63B7300f93c18A472cE440508C284C77d6](https://explorer-host.pecorino.signet.sh/address/0x8A26dA63B7300f93c18A472cE440508C284C77d6)** | | `OtimDelegate` | **[0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c](https://explorer-host.pecorino.signet.sh/address/0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c)** | | `Treasury` | **[0xc7536a1e8205C139d25165F6305e20299b792511](https://explorer-host.pecorino.signet.sh/address/0xc7536a1e8205C139d25165F6305e20299b792511)** | ## Action Contracts | Name | Address | | :---------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | | `DeactivateInstructionAction` | **[0x1f719754FC21E58b628E02659896309354888058](https://explorer-host.pecorino.signet.sh/address/0x1f719754FC21E58b628E02659896309354888058)** | | `RefuelAction` | **[0x355A1dA513d010fdD404E138807ea75731d08133](https://explorer-host.pecorino.signet.sh/address/0x355A1dA513d010fdD404E138807ea75731d08133)** | | `RefuelERC20Action` | **[0xf8a22699d6a8Ff6284898288573B6d5a8893728B](https://explorer-host.pecorino.signet.sh/address/0xf8a22699d6a8Ff6284898288573B6d5a8893728B)** | | `SweepAction` | **[0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb](https://explorer-host.pecorino.signet.sh/address/0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb)** | | `SweepERC20Action` | **[0x006BDC1dD878831590851f48A1D82c0E14914125](https://explorer-host.pecorino.signet.sh/address/0x006BDC1dD878831590851f48A1D82c0E14914125)** | | `TransferAction` | **[0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F](https://explorer-host.pecorino.signet.sh/address/0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F)** | | `TransferERC20Action` | **[0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4](https://explorer-host.pecorino.signet.sh/address/0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4)** | ## Core Contracts | Name | Address | | :------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | | `ActionManager` | **[0x5C19E012Ba6473714C1887b211b55C768809f124](https://explorer.pecorino.signet.sh/address/0x5C19E012Ba6473714C1887b211b55C768809f124)** | | `FeeTokenRegistry` | **[0x19c370A2e300429F98c17803984DC5413faAc02E](https://explorer.pecorino.signet.sh/address/0x19c370A2e300429F98c17803984DC5413faAc02E)** | | `Gateway` | **[0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225](https://explorer.pecorino.signet.sh/address/0x3ac1d56A5d8c6A95773E312Dd5fe3A2B2E0de225)** | | `InstructionStorage` | **[0x8A26dA63B7300f93c18A472cE440508C284C77d6](https://explorer.pecorino.signet.sh/address/0x8A26dA63B7300f93c18A472cE440508C284C77d6)** | | `OtimDelegate` | **[0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c](https://explorer.pecorino.signet.sh/address/0xE1B38C99ab2727BcbD75149f25a0BD7c8a93CB1c)** | | `Treasury` | **[0xc7536a1e8205C139d25165F6305e20299b792511](https://explorer.pecorino.signet.sh/address/0xc7536a1e8205C139d25165F6305e20299b792511)** | ## Action Contracts | Name | Address | | :---------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | | `DeactivateInstructionAction` | **[0x1f719754FC21E58b628E02659896309354888058](https://explorer.pecorino.signet.sh/address/0x1f719754FC21E58b628E02659896309354888058)** | | `RefuelAction` | **[0x355A1dA513d010fdD404E138807ea75731d08133](https://explorer.pecorino.signet.sh/address/0x355A1dA513d010fdD404E138807ea75731d08133)** | | `RefuelERC20Action` | **[0xf8a22699d6a8Ff6284898288573B6d5a8893728B](https://explorer.pecorino.signet.sh/address/0xf8a22699d6a8Ff6284898288573B6d5a8893728B)** | | `SweepAction` | **[0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb](https://explorer.pecorino.signet.sh/address/0xDbA56dbdB3a34470F663C3De9d27D19989Ac3eAb)** | | `SweepERC20Action` | **[0x006BDC1dD878831590851f48A1D82c0E14914125](https://explorer.pecorino.signet.sh/address/0x006BDC1dD878831590851f48A1D82c0E14914125)** | | `TransferAction` | **[0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F](https://explorer.pecorino.signet.sh/address/0xaf8a6c7D2e256DF87D08bC644Dc19A3B5a49Cf5F)** | | `TransferERC20Action` | **[0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4](https://explorer.pecorino.signet.sh/address/0xF1dd53104b5844BCD17f7F1A5B9ab16EAC0FD0c4)** | # Orchestrators Source: https://docs.otim.com/docs/developers/orchestrators/overview Otim Orchestrators are atomic routers that turn complex, multi-step stablecoin routing into a single transfer. No more juggling swaps, transfers, or async calls across chains and tokens. Simply send any asset on any chain to an Orchestrator address, and always get the result you expect. Think of them like **webhooks for money**: they use the Otim OS under the hood to automatically route and transform any incoming asset based on the desired settlement state. ### Why Use Orchestrators? * **Single API Call**: Define your desired money flow and generate an Orchestrator address via a single API call * **Automatic Routing**: Send any digital asset to the Orchestrator and watch it route and transform **on autopilot** * **Flexible Configuration**: Configure Orchestrators for one-time or persistent usage * **Trustless & Non-Custodial**: Every Orchestrator is **non-custodial** with **trustless execution** and **built-in compliance** Orchestrators Diagram ## Use Cases ### Cross-Chain Payment Settlement Accept payments in any asset on any chain, and automatically: * Bridge assets across chains * Convert to your preferred settlement currency * Route to any financial account ### Yield Vault Deposits Send any asset to deposit into a yield vault that only accepts USDC. The Orchestrator automatically: * Accepts multiple asset types (ETH, USDT, DAI, etc.) * Swaps them to USDC if needed * Deposits the USDC into the yield vault ### Bank Conversions & Fiat Orchestration Orchestrate complex fiat settlement flows across multiple local currencies, tokens, and banking partners. The Orchestrator handles: * Converting any digital asset to fiat across all kinds of local currencies (USD, EUR, etc.) and acceptable token formats * Accepting assets from any chain and automatically convert them to bank-acceptable formats * Settling to traditional banking partners * Orchestrating assets immediately when onramped assets land on an Orchestrator *** Want to get started? Head over to the [SDK](/sdk/overview) section to get going! # Off-chain Source: https://docs.otim.com/docs/developers/system-architecture/off-chain/off-chain Otim's off-chain infrastructure consists of two main components that work alongside the on-chain protocol. Off-chain Diagram ## API The web API acts as an interface between the Otim App and the rest of the system. While users can view account activity, craft new Instructions, and sign messages entirely in the App, some operations require access to Otim's backend system. The API allows the App to read user data from the database and request the Executor to interact with the Otim protocol. Check out all API endpoints in the [API Reference](/api-reference/create-account). ## Executor The Executor is an off-chain agent designed to execute Instructions as soon as their conditions are valid on-chain. All conditions that need to be met for an Instruction to successfully execute on-chain are strictly defined in the Otim protocol. This means that if the Executor submits an Instruction too early, the transaction will simply revert. The Executor's incentives are clear: 1. **Don't** waste gas trying to submit invalid Instructions 2. **Do** execute Instructions when they become valid in order to collect a fee # Current Actions Source: https://docs.otim.com/docs/developers/system-architecture/protocol/action-contracts/current-actions Below is the first batch of Actions currently available to users, but many more Actions are in development! | Action | Description | | :------------------------ | :----------------------------------------------------------------------------- | | **DeactivateInstruction** | Deactivate an existing instruction to stop its execution. | | **Refuel** | Refuel a target account when its native token balance falls below a threshold. | | **RefuelERC20** | Refuel a target account when its ERC20 token balance falls below a threshold. | | **Sweep** | Move native tokens from a target account when balance exceeds a threshold. | | **SweepCCTP** | Move CCTP tokens from a target account when balance exceeds a threshold. | | **SweepERC20** | Move ERC20 tokens from a target account when balance exceeds a threshold. | | **SweepUniswapV3** | Move tokens via Uniswap V3 swap when balance exceeds a threshold. | | **Transfer** | Send native tokens to a target account at scheduled intervals. | | **TransferCCTP** | Send CCTP tokens to a destination domain at scheduled intervals. | | **TransferERC20** | Send ERC20 tokens to a target account at scheduled intervals. | | **UniswapV3ExactInput** | Swap tokens using Uniswap V3 pools at scheduled intervals. | Find all of the above Actions' deployed contract addresses [here](/docs/developers/deployed-addresses). Reference the 712 hash ABI's for each Action [here](/docs/resources/appendix/action-hash-abis). # Structure Source: https://docs.otim.com/docs/developers/system-architecture/protocol/action-contracts/structure Each Action contract must implement the `IAction` interface. This interface defines two external functions: `argumentsHash` and `execute` ### `argumentsHash` This function returns the information necessary to construct the signing hash for the Instruction depending on the Action arguments. ### `execute` The `execute` function contains the actual feature-specific logic and is called during each execution. This function is designed to be lightweight and reusable considering Instructions are designed to be executed many times. # ActionManager Source: https://docs.otim.com/docs/developers/system-architecture/protocol/core-contracts/action-manager This is an Otim-operated contract for managing which Actions can be executed through the protocol. Only Actions that are added to this contract can be executed by Otim users. This layer of security is meant to prevent users from accidentally executing buggy or malicious Action contracts. If a bug or exploit is found in one of the active Action contracts, Otim can easily remove the contract from `ActionManager` and deploy a new one. `ActionManager` also includes a kill-switch for the entire protocol in case a bug or exploit is found in Otim's core contracts. The source code is available on [GitHub](https://github.com/otimlabs/otim-protocol/blob/main/src/core/ActionManager.sol). # InstructionStorage Source: https://docs.otim.com/docs/developers/system-architecture/protocol/core-contracts/instruction-storage This helper contract holds the execution state of each Instruction for each user. This contract accurately maintains execution state for each Instruction while enforcing strict access control for storage writes. Only EOAs delegated to our specific instance of `OtimDelegate` can write to the storage of this contract and cannot call it directly. Also, data is "siloed" by the user's address, meaning each user can only alter their own data. This strict access control strategy ensures that all user data is secure and accurate on-chain. The source code is available on [GitHub](https://github.com/otimlabs/otim-protocol/blob/main/src/core/InstructionStorage.sol). # OtimDelegate Source: https://docs.otim.com/docs/developers/system-architecture/protocol/core-contracts/otim-delegate This is the main `otim-protocol` smart contract. This is the contract that users will delegate their EOAs to in order to enable Otim's features (also known as the "delegate contract"). It is this contract that actually executes within the context of the user's account. While the user is delegated to this contract, the runtime bytecode *is* the user's account. `OtimDelegate` contains general Instruction validation and execution logic, as well as deactivation logic. Once a user is delegated to this contract, they can construct and sign Instructions to be executed on their behalf. The source code is available on [GitHub](https://github.com/otimlabs/otim-protocol/blob/main/src/OtimDelegate.sol). # Treasury Source: https://docs.otim.com/docs/developers/system-architecture/protocol/core-contracts/treasury This contract holds all fees collected from Instruction executions. Each time an Instruction is executed on behalf of a user, a fee is collected and sent to this `Treasury` contract. To read more on Otim's fee structure, check out the [Fee Structure](/docs/concepts/fee-structure) page. The source code is available on [GitHub](https://github.com/otimlabs/otim-protocol/blob/main/src/infrastructure/Treasury.sol). # Instruction Lifecycle Source: https://docs.otim.com/docs/developers/system-architecture/protocol/instruction-flows/instruction-lifecycle Instructions are processed through Otim's core contracts at different points in their lifecycle. Everything interacting with Instructions uses EIP-712 hashes for execution validation. See [How to Construct Instruction EIP-712 Hashes](/docs/resources/appendix/instruction-712-hashes) for more detail. ## Activation An Instruction's first execution (a.k.a. activation) is special because certain validity conditions are checked at this time that don't need to be checked for subsequent executions. Since Instructions are immutable and designed to be executed more than once, we can get a lot out of the way during activation. For example, the user's signature is only validated during activation. The scheduling and validity of subsequent executions depend entirely on the Instruction scheduling parameters, the application logic of the Action contract, and the Action arguments. Because of this, users get recurring executions with just one signature. Core Contracts Activation flow diagram ## Execution After activation, an Instruction can only be executed when the scheduling parameters and Action application logic allow. The user's signature does not need to be checked for subsequent executions because they have already committed to the execution dynamics defined in the Instruction. This means subsequent executions can be submitted by the Otim Executor in a strictly defined way without the user's ongoing attention and intervention. Core Contracts Execution flow diagram ## Deactivation While each Instruction's execution dynamics are strictly defined on-chain, a user may decide to deactivate an Instruction before its natural completion. For example, say Alice wants to automate her rent payments so she creates a simple `Transfer` Instruction that automatically sends \$1000 USDC to her landlord every month for the remainder of her lease. A few months down the line, Alice decides to break her lease and move into a new apartment. In this case, Alice should deactivate her `Transfer` Instruction before its natural end to stop paying her former landlord. Core Contracts Deactivation flow diagram # Getting Started Source: https://docs.otim.com/docs/getting-started Create your Platform and make your first API call. ## 1. Request access Sign up at [otim.com](https://otim.com) and request Platform access. Once approved, you will receive access to your dashboard and sandbox environment. ## 2. Get your API key Navigate to the Developer section of your dashboard to create an API key. Include it in the `Authorization` header of every request: ```bash theme={null} curl https://api.otim.com/v0/platform/settings \ -H "Authorization: Bearer sk_..." ``` ## 3. Configure your Platform Set up your Platform settings to configure defaults for your Accounts, like auto-earn and your preferred chain. ```bash theme={null} curl -X PATCH https://api.otim.com/v0/platform/settings \ -H "Authorization: Bearer sk_..." \ -H "Content-Type: application/json" \ -d '{ "autoEarnEnabled": true, "autoEarnVault": "aave_usdc", "defaultChain": "base" }' ``` ## 4. Create an Entity An [Entity](/core-objects/entity) represents a person or business on your Platform. Creating an Entity kicks off the verification flow. ```bash theme={null} curl -X POST https://api.otim.com/v0/entities \ -H "Authorization: Bearer sk_..." \ -H "Content-Type: application/json" \ -d '{ "email": "alice@example.com", "type": "individual", "fullName": "Alice Smith" }' ``` The response includes `kycLink` and `tosLink`. Share these with the Entity so they can complete verification. ## 5. Create an Account Once the Entity exists, create an [Account](/core-objects/account) to hold and move funds. ```bash theme={null} curl -X POST https://api.otim.com/v0/accounts \ -H "Authorization: Bearer sk_..." \ -H "Content-Type: application/json" \ -d '{ "entityId": "ent_01JA1B2C3D4E5F6G7H8J9K0L", "name": "Main Account" }' ``` The Account is automatically provisioned on your default chain. If auto-earn is enabled, idle balances will start earning yield immediately. ## 6. Send a transfer Move funds from an Account to another Account or to an external [Counterparty](/core-objects/counterparty). ```bash theme={null} curl -X POST https://api.otim.com/v0/transfers \ -H "Authorization: Bearer sk_..." \ -H "Content-Type: application/json" \ -d '{ "sourceAccountId": "acc_01JA1B2C3D4E5F6G7H8J9K0L", "destinationAccountId": "acc_02XY9Z8W7V6U5T4S3R2Q1P0N", "amount": "100.00", "description": "Payment to vendor" }' ``` ## Next steps * Set up [Counterparties](/core-objects/counterparty) to send funds to external wallets or bank accounts * Issue [IBANs](/core-objects/iban) to accept fiat deposits (requires a verified Entity) * Browse the full [API reference](/api-reference/get-platform-settings) # Documentation Source: https://docs.otim.com/docs/introduction Otim enables any platform to launch their own financial products without partnering with a bank. Our mission is to supercharge how money earns, moves, and spends in a unified, developer-first way. We took a pragmatic approach to what modern banking infrastructure should look like if it were architected from scratch today. Get access to the bank's business model and brand new revenue mechanics that weren't possible before, built for developers in the simplest form possible. ## Feedback We're building Otim in the open and your feedback is welcome. Tell us what's working, what's broken, and what you want to see next. For support or feedback, reach out at [julian@otim.com](mailto:julian@otim.com). # Action ABI Reference Source: https://docs.otim.com/docs/resources/appendix/action-hash-abis This document contains the ABI structures for all Actions, taken from their respective `hash` functions. Use these to properly encode Action arguments when forming EIP-712 hashes. ## DeactivateInstruction ```typescript theme={null} const deactivateInstructionAbi = [ { name: 'instructionId', type: 'bytes32' }, { name: 'fee', type: 'tuple', components: [ { name: 'token', type: 'address' }, { name: 'maxBaseFeePerGas', type: 'uint256' }, { name: 'maxPriorityFeePerGas', type: 'uint256' }, { name: 'executionFee', type: 'uint256' } ] } ] as const ``` ## Refuel ```typescript theme={null} const refuelAbi = [ { name: 'target', type: 'address' }, { name: 'threshold', type: 'uint256' }, { name: 'endBalance', type: 'uint256' }, { name: 'gasLimit', type: 'uint256' }, { name: 'fee', type: 'tuple', components: [ { name: 'token', type: 'address' }, { name: 'maxBaseFeePerGas', type: 'uint256' }, { name: 'maxPriorityFeePerGas', type: 'uint256' }, { name: 'executionFee', type: 'uint256' } ] } ] as const ``` ## RefuelERC20 ```typescript theme={null} const refuelERC20Abi = [ { name: 'token', type: 'address' }, { name: 'target', type: 'address' }, { name: 'threshold', type: 'uint256' }, { name: 'endBalance', type: 'uint256' }, { name: 'fee', type: 'tuple', components: [ { name: 'token', type: 'address' }, { name: 'maxBaseFeePerGas', type: 'uint256' }, { name: 'maxPriorityFeePerGas', type: 'uint256' }, { name: 'executionFee', type: 'uint256' } ] } ] as const ``` ## Sweep ```typescript theme={null} const sweepAbi = [ { name: 'target', type: 'address' }, { name: 'threshold', type: 'uint256' }, { name: 'endBalance', type: 'uint256' }, { name: 'gasLimit', type: 'uint256' }, { name: 'fee', type: 'tuple', components: [ { name: 'token', type: 'address' }, { name: 'maxBaseFeePerGas', type: 'uint256' }, { name: 'maxPriorityFeePerGas', type: 'uint256' }, { name: 'executionFee', type: 'uint256' } ] } ] as const ``` ## SweepCCTP ```typescript theme={null} const sweepCCTPAbi = [ { name: 'token', type: 'address' }, { name: 'destinationDomain', type: 'uint32' }, { name: 'destinationMintRecipient', type: 'bytes32' }, { name: 'threshold', type: 'uint256' }, { name: 'endBalance', type: 'uint256' }, { name: 'fee', type: 'tuple', components: [ { name: 'token', type: 'address' }, { name: 'maxBaseFeePerGas', type: 'uint256' }, { name: 'maxPriorityFeePerGas', type: 'uint256' }, { name: 'executionFee', type: 'uint256' } ] } ] as const ``` ## SweepERC20 ```typescript theme={null} const sweepERC20Abi = [ { name: 'target', type: 'address' }, { name: 'token', type: 'address' }, { name: 'threshold', type: 'uint256' }, { name: 'endBalance', type: 'uint256' }, { name: 'fee', type: 'tuple', components: [ { name: 'token', type: 'address' }, { name: 'maxBaseFeePerGas', type: 'uint256' }, { name: 'maxPriorityFeePerGas', type: 'uint256' }, { name: 'executionFee', type: 'uint256' } ] } ] as const ``` ## SweepUniswapV3 ```typescript theme={null} const sweepUniswapV3Abi = [ { name: 'recipient', type: 'address' }, { name: 'tokenIn', type: 'address' }, { name: 'tokenOut', type: 'address' }, { name: 'feeTier', type: 'uint24' }, { name: 'threshold', type: 'uint256' }, { name: 'endBalance', type: 'uint256' }, { name: 'floorAmountOut', type: 'uint256' }, { name: 'meanPriceLookBack', type: 'uint32' }, { name: 'maxPriceDeviationBPS', type: 'uint32' }, { name: 'fee', type: 'tuple', components: [ { name: 'token', type: 'address' }, { name: 'maxBaseFeePerGas', type: 'uint256' }, { name: 'maxPriorityFeePerGas', type: 'uint256' }, { name: 'executionFee', type: 'uint256' } ] } ] as const ``` ## Transfer ```typescript theme={null} const transferAbi = [ { name: 'target', type: 'address' }, { name: 'value', type: 'uint256' }, { name: 'gasLimit', type: 'uint256' }, { name: 'schedule', type: 'tuple', components: [ { name: 'startAt', type: 'uint256' }, { name: 'startBy', type: 'uint256' }, { name: 'interval', type: 'uint256' }, { name: 'timeout', type: 'uint256' } ] }, { name: 'fee', type: 'tuple', components: [ { name: 'token', type: 'address' }, { name: 'maxBaseFeePerGas', type: 'uint256' }, { name: 'maxPriorityFeePerGas', type: 'uint256' }, { name: 'executionFee', type: 'uint256' } ] } ] as const ``` ## TransferCCTP ```typescript theme={null} const transferCCTPAbi = [ { name: 'token', type: 'address' }, { name: 'amount', type: 'uint256' }, { name: 'destinationDomain', type: 'uint32' }, { name: 'destinationMintRecipient', type: 'bytes32' }, { name: 'schedule', type: 'tuple', components: [ { name: 'startAt', type: 'uint256' }, { name: 'startBy', type: 'uint256' }, { name: 'interval', type: 'uint256' }, { name: 'timeout', type: 'uint256' } ] }, { name: 'fee', type: 'tuple', components: [ { name: 'token', type: 'address' }, { name: 'maxBaseFeePerGas', type: 'uint256' }, { name: 'maxPriorityFeePerGas', type: 'uint256' }, { name: 'executionFee', type: 'uint256' } ] } ] as const ``` ## TransferERC20 ```typescript theme={null} const transferERC20Abi = [ { name: 'token', type: 'address' }, { name: 'target', type: 'address' }, { name: 'value', type: 'uint256' }, { name: 'schedule', type: 'tuple', components: [ { name: 'startAt', type: 'uint256' }, { name: 'startBy', type: 'uint256' }, { name: 'interval', type: 'uint256' }, { name: 'timeout', type: 'uint256' } ] }, { name: 'fee', type: 'tuple', components: [ { name: 'token', type: 'address' }, { name: 'maxBaseFeePerGas', type: 'uint256' }, { name: 'maxPriorityFeePerGas', type: 'uint256' }, { name: 'executionFee', type: 'uint256' } ] } ] as const ``` ## UniswapV3ExactInput ```typescript theme={null} const uniswapV3ExactInputAbi = [ { name: 'recipient', type: 'address' }, { name: 'tokenIn', type: 'address' }, { name: 'tokenOut', type: 'address' }, { name: 'feeTier', type: 'uint24' }, { name: 'amountIn', type: 'uint256' }, { name: 'floorAmountOut', type: 'uint256' }, { name: 'meanPriceLookBack', type: 'uint32' }, { name: 'maxPriceDeviationBPS', type: 'uint32' }, { name: 'schedule', type: 'tuple', components: [ { name: 'startAt', type: 'uint256' }, { name: 'startBy', type: 'uint256' }, { name: 'interval', type: 'uint256' }, { name: 'timeout', type: 'uint256' } ] }, { name: 'fee', type: 'tuple', components: [ { name: 'token', type: 'address' }, { name: 'maxBaseFeePerGas', type: 'uint256' }, { name: 'maxPriorityFeePerGas', type: 'uint256' }, { name: 'executionFee', type: 'uint256' } ] } ] as const ``` These ABIs are taken directly from the `hash` function in each Action's contract found on [GitHub](https://github.com/otimlabs/otim-protocol/tree/main/src/actions). Use them to properly encode Action arguments when forming EIP-712 hashes. See [How to Construct Instruction Activation and Deactivation EIP-712 Hashes](/docs/resources/appendix/instruction-712-hashes) for details on how to use these ABIs. # API Errors Source: https://docs.otim.com/docs/resources/appendix/api-errors All API errors return a JSON body with an `error` message and a machine-readable `code`. ```json theme={null} { "error": "Entity not found.", "code": "not_found" } ``` ## HTTP status codes | Status | Description | | ------ | ------------------------------------------------------------------------------------------------------------------------ | | `400` | Bad request. The request body is invalid or missing required fields. | | `401` | Unauthorized. The API key is missing or invalid. | | `403` | Forbidden. The API key does not have permission for this operation. | | `404` | Not found. The requested resource does not exist. | | `409` | Conflict. The resource already exists or the idempotency key was already used with different parameters. | | `422` | Unprocessable. The request was well-formed but could not be completed (e.g., insufficient balance, Entity not verified). | | `429` | Rate limited. Too many requests. Retry after the duration specified in the `Retry-After` header. | | `500` | Internal server error. Something went wrong on our end. | ## Common error codes | Code | Status | Description | | ---------------------- | ------ | ------------------------------------------------------------------------------------- | | `unauthorized` | 401 | Missing or invalid API key. | | `forbidden` | 403 | API key does not have access to this resource. | | `not_found` | 404 | The requested resource was not found. | | `validation_error` | 400 | One or more fields in the request body are invalid. | | `insufficient_balance` | 422 | The source Account does not have enough funds. | | `entity_not_verified` | 422 | The Entity has not completed KYC/KYB. Required for IBANs and wire transfers. | | `duplicate_resource` | 409 | A resource with these attributes already exists. | | `idempotency_conflict` | 409 | The idempotency key was already used with different request parameters. | | `rate_limited` | 429 | Too many requests. Back off and retry. | | `transfer_failed` | 422 | The transfer could not be completed. Check the transfer details for more information. | ## Rate limits The API allows up to 100 requests per second per API key. If you exceed this limit, you will receive a `429` response with a `Retry-After` header indicating how many seconds to wait before retrying. ## Protocol error codes The following error codes apply to on-chain protocol operations. These are returned when an Instruction execution fails at the contract level. | Code | Type | Description | | :--- | :-------- | :---------------------------------------------------------------------- | | 1201 | Terminal | User account is not delegated to the OtimDelegate contract | | 1202 | Terminal | Signature verification failed for the provided instruction | | 1203 | Terminal | Action cannot be executed due to contract-level restrictions | | 1204 | Retryable | Action execution failed on-chain, may succeed with retry | | 1205 | Terminal | Instruction has already been deactivated and cannot be executed | | 1206 | Terminal | Instruction has already been deactivated in DeactivateInstructionAction | | Code | Type | Description | | :--- | :-------- | :--------------------------------------------------------------- | | 1301 | Terminal | Invalid or malformed arguments provided for transfer action | | 1302 | Terminal | SafeERC20 transfer operation failed due to token contract issues | | 1303 | Retryable | Insufficient fee token balance to cover execution costs | | 1304 | Retryable | Insufficient native token balance for the transfer amount | | Code | Type | Description | | :--- | :-------- | :---------------------------------------------------------------- | | 1305 | Terminal | Invalid or malformed arguments provided for ERC20 transfer action | | 1306 | Terminal | SafeERC20 transfer operation failed due to token contract issues | | 1307 | Retryable | Insufficient fee token balance to cover execution costs | | 1308 | Retryable | Insufficient ERC20 token balance for the transfer amount | | Code | Type | Description | | :--- | :-------- | :--------------------------------------------------------------- | | 1309 | Terminal | Invalid or malformed arguments provided for refuel action | | 1310 | Terminal | SafeERC20 transfer operation failed due to token contract issues | | 1311 | Retryable | Insufficient fee token balance to cover execution costs | | 1313 | Retryable | Insufficient native token balance for the refuel amount | | Code | Type | Description | | :--- | :-------- | :--------------------------------------------------------------- | | 1314 | Terminal | Invalid or malformed arguments provided for ERC20 refuel action | | 1315 | Terminal | SafeERC20 transfer operation failed due to token contract issues | | 1316 | Retryable | Insufficient fee token balance to cover execution costs | | 1318 | Retryable | Insufficient ERC20 token balance for the refuel amount | | Code | Type | Description | | :--- | :-------- | :---------------------------------------------------------------------- | | 1319 | Terminal | Invalid or malformed arguments provided for UniswapV3 swap action | | 1320 | Terminal | UniswapV3 pool does not exist for the specified token pair and fee tier | | 1321 | Terminal | Unexpected invalid tick data encountered during pool validation | | 1322 | Terminal | SafeERC20 transfer operation failed due to token contract issues | | 1323 | Retryable | Insufficient fee token balance to cover execution costs | | 1324 | Retryable | Insufficient input token balance for the swap amount | | 1328 | Retryable | UniswapV3 swap received too little output tokens | | Code | Type | Description | | :--- | :-------- | :------------------------------------------------------------------------ | | 1340 | Retryable | Insufficient fee token balance to cover execution costs | | 1341 | Terminal | Invalid or malformed arguments provided for deactivate instruction action | | 1342 | Terminal | SafeERC20 transfer operation failed due to token contract issues | | Code | Type | Description | | :--- | :-------- | :--------------------------------------------------------------- | | 1343 | Terminal | Invalid or malformed arguments provided for sweep CCTP action | | 1344 | Terminal | CCTP token is not supported for the specified operation | | 1345 | Retryable | Insufficient fee token balance to cover execution costs | | 1346 | Terminal | SafeERC20 transfer operation failed due to token contract issues | | Code | Type | Description | | :--- | :-------- | :--------------------------------------------------------------- | | 1347 | Terminal | Invalid or malformed arguments provided for transfer CCTP action | | 1348 | Retryable | Insufficient token balance for the CCTP transfer amount | | 1349 | Terminal | CCTP token is not supported for the specified operation | | 1350 | Retryable | Insufficient fee token balance to cover execution costs | | 1351 | Terminal | SafeERC20 transfer operation failed due to token contract issues | | Code | Type | Description | | :--- | :-------- | :--------------------------------------------------------------- | | 1352 | Terminal | Invalid or malformed arguments provided for sweep action | | 1353 | Retryable | Insufficient fee token balance to cover execution costs | | 1354 | Terminal | SafeERC20 transfer operation failed due to token contract issues | | Code | Type | Description | | :--- | :-------- | :--------------------------------------------------------------- | | 1355 | Terminal | Invalid or malformed arguments provided for sweep ERC20 action | | 1356 | Retryable | Insufficient fee token balance to cover execution costs | | 1357 | Terminal | SafeERC20 transfer operation failed due to token contract issues | | Code | Type | Description | | :--- | :-------- | :---------------------------------------------------------------------- | | 1358 | Terminal | Invalid or malformed arguments provided for sweep UniswapV3 action | | 1359 | Terminal | UniswapV3 pool does not exist for the specified token pair and fee tier | | 1360 | Terminal | Unexpected invalid tick data encountered during pool validation | | 1361 | Terminal | SafeERC20 transfer operation failed due to token contract issues | | 1362 | Retryable | Insufficient fee token balance to cover execution costs | # How to Construct Instruction Activation and Deactivation EIP-712 Hashes Source: https://docs.otim.com/docs/resources/appendix/instruction-712-hashes This guide shows how to form EIP-712 hashes for instruction activation and deactivation in TypeScript. As long as you have the Action ABI, you can form the activation hash like this: ```typescript theme={null} import { keccak256, encodeAbiParameters, encodePacked } from 'viem' // 1. Define the Instruction ABI (standardized for all Instructions) const instructionAbi = [ { name: 'salt', type: 'uint256' }, { name: 'maxExecutions', type: 'uint256' }, { name: 'action', type: 'address' }, { name: 'arguments', type: 'bytes' } ] as const // 2. Form the domain separator (standardized for all Instructions) // This matches exactly what's in OtimDelegate.sol const domainSeparator = keccak256(encodeAbiParameters( [ { name: 'name', type: 'string' }, { name: 'version', type: 'string' }, { name: 'chainId', type: 'uint256' }, { name: 'verifyingContract', type: 'address' }, { name: 'salt', type: 'bytes32' } ], [ 'OtimDelegate', // OtimDelegate '1', // Version chainId, // Current chain ID delegateAddress, // OtimDelegate contract address keccak256('ON_TIME_INSTRUCTED_MONEY') // Salt from contract ] )) // 3. Encode the Action arguments // Example for `sweepDepositAccountERC20` action // Get the ABI from action-hash-abis.mdx const actionAbi = [ { name: 'token', type: 'address' }, { name: 'depositor', type: 'address' }, { name: 'recipient', type: 'address' }, { name: 'threshold', type: 'uint256' }, { name: 'fee', type: 'tuple', components: [ { name: 'token', type: 'address' }, { name: 'maxBaseFeePerGas', type: 'uint256' }, { name: 'maxPriorityFeePerGas', type: 'uint256' }, { name: 'executionFee', type: 'uint256' } ] } ] as const const actionArguments = encodeAbiParameters( actionAbi, [ tokenAddress, depositorAddress, recipientAddress, threshold, { token: feeTokenAddress, maxBaseFeePerGas: 0n, maxPriorityFeePerGas: 1200000n, executionFee: 0n } ] ) // 4. Form the instruction hash const instructionHash = keccak256(encodeAbiParameters( instructionAbi, [ salt, // Random salt maxExecutions, // Number of times this can be executed actionAddress, // Address of the action contract actionArguments // ABI-encoded action arguments ] )) // 5. Form the final EIP-712 hash const finalHash = keccak256(encodePacked( ['bytes', 'bytes32', 'bytes32'], ['\x19\x01', domainSeparator, instructionHash] )) ``` For deactivation, it's even simpler since we just need the instruction ID: ```typescript theme={null} // Form the deactivation hash const instructionId = "..." // The Instruction ID of the Instruction you want to deactivate const deactivationHash = keccak256(encodePacked( ['bytes', 'bytes32', 'bytes32'], [ '\x19\x01', domainSeparator, keccak256(encodeAbiParameters( [{ name: 'instructionId', type: 'bytes32' }], [instructionId] )) ] )) ``` That's it! The key is having the correct ABI structure, which matches what's defined in the [Instruction.sol contract](https://github.com/otimlabs/otim-protocol/blob/main/src/libraries/Instruction.sol). In order to swap out the `actionAbi` for the Action you want to activate, look at the `hash` function in the Action's contract ABI. For example, from the RefuelERC20 Action's ABI: ```typescript theme={null} // From the hash function in RefuelERC20 Action's ABI // We only need the input type of the hash function const refuelERC20Abi = [ { name: 'token', type: 'address' }, { name: 'target', type: 'address' }, { name: 'threshold', type: 'uint256' }, { name: 'endBalance', type: 'uint256' }, { name: 'fee', type: 'tuple', components: [ { name: 'token', type: 'address' }, { name: 'maxBaseFeePerGas', type: 'uint256' }, { name: 'maxPriorityFeePerGas', type: 'uint256' }, { name: 'executionFee', type: 'uint256' } ] } ] as const ``` This shows us exactly how to structure the arguments for that Action. The rest of the ABI (constructor, other functions, errors) isn't needed for forming the EIP-712 hash. For a complete reference of all Action ABIs, see [Action Hash ABIs](/docs/resources/appendix/action-hash-abis). # Changelog Source: https://docs.otim.com/docs/resources/changelog See what's new and upcoming with all of Otim's products and platform. ✨ ## Entities, platform updates, and vault improvements * Added entities feature with account linking * Onchain transfers, onchain balance reading, and balance formatting * Added Polygon and BNB chains * Added export private keys functionality for users * Vault migration support * Minimum total shares for vaults * Removed vault underlying token from settlement requests * Various UI and performance fixes ## Vault support across SDKs ### TS SDK v0.0.18 * Vault migrate support in sdk-core and sdk-server * Vault minimum total shares * Underlying token handling for settlement requests ### Go SDK v0.2.0 * Minimum total shares auto detection * Updated API surface * Vault migrate support ## Auto-earn, settlement improvements, and orchestration support ### App v0.6.0 * Auto-earn functionality with optimistic UI, historical data, and APY display * Orchestration settlement with withdraw and settle API integration * Conditional payment request orchestration form * Settle and deposit flow * Rescue funds feature for recovering stuck assets * Recurring trades and transfers * Upgraded developer page ### TS SDK v0.0.14 * Settle+deposit and settle+withdraw flows * New settlement shape and type definitions * Settlement type and source fields ### Go SDK v0.0.7 * Settlement vault deposit * Updated orchestration build request struct * Auto-pull OtimDelegate address from API ## Otim Go SDK initial release Build and launch Otim orchestrators directly within your Go applications * EIP-7702 delegation signing * Settlement building and signing * Signature parsing and ABI encoding ## Mobile support, virtual accounts, and token upgrades ### New features * Mobile responsive design * Virtual accounts * ERC-4626 vault deposit and withdraw actions * Token list upgrade and trades revamp * Multiple passkeys support * EUR bank connection ### Improvements * Portfolio chart visualization improvements * Recurring trade support with proper floor amount handling * Renamed payment requests to orchestrations * Session expiration and authentication fixes ## Orchestration support and settlements rename * Auto build and raw transaction support * Renamed payment requests to settlements * Orchestration support with max runs configuration ## TypeScript SDK restructure * Refactored SDK codebase with new sdk-core and sdk-server packages * Added activity SDK client * Switched utils and turnkey packages to production mode ## Passkey authentication, developer tools, and multi-chain ### Authentication * Passkey-only login with persistent sessions * Legacy passkey migration from app.otim.com to otim.com ### New features * Developer tab for API exploration * QuickBooks invoice search integration * Multi-chain balances display * Wallet balance in accounts tab * Instruction batch signer * Cancel payment requests * Chain network type toggle ### Chains * Added BNB, Unichain, and Polygon mainnet and testnets ## Payment requests, bank connections, and new chains ### Payment requests * Multi-tenant pay page * Sign payment requests with passkey * Payment request form and settlement flow * QuickBooks integration for payment settings ### Banking * Bank connection integration * Link multiple banks * Bank-to-wallet transfers ### Chains * Added Base, Arbitrum, and Optimism to the production chain selector ### Other * Contacts management * Token balances display * Various authentication and delegation fixes ## Improved table views, separate mainnet from testnet * Added chain column to the Overview and Instruction tables * Separated out testnet chains and made available at [app-testnet.otim.com](https://app-testnet.otim.com) * Other app performance enhancements and minor bug fixes ## Error and RLP authorization management improvements * Simple updates around how errors are bubbled up from calls * Fixed RLP preprocessing for 7702 authlist building ## Otim TypeScript SDK v0.1 release Build trustless automated executions into your apps and wallets with less than 10 lines of code * Install simply by running `pnpm add @otim/sdk`, `npm install @otim/sdk`, or `yarn add @otim/sdk` * Provides simple flows for: * Setting up the client * Authenticating users * Delegating accounts via EIP-7702 * Activating automated instructions * Compatible with embedded wallet providers like Turnkey, Privy, DFNS, Dynamic, Para, Openfort, etc. * Seamless integration into existing wallet infrastructure to enable flows like subscriptions, DCA, and topping up wallet balances ## Error handling, seamless authentication ### Dashboard * Added error message tooltips that show you exactly why an execution didn't work * Added error toasts for API calls so you'll always know when a request doesn’t go through * Added pagination to both Activity and Instructions tabs * Pay for instruction deactivation using any token ### Authentication * Improved authentication UX to sign in only once and access all supported chains seamlessly Incorporated several performance improvements to both authentication and dashboard loading ## App improvements, a new fee token ### Instructions * Added USDT as a fee token option * Fixed keeping embedded wallet login session live * Updated default and minimum values for schedule and advanced parameters in the instruction builder * Several other minor performance and UX flow improvements ### Onboarding * Added OS-dependent delegation command * Turned delegation tutorial video into popup ## Ethereum Mainnet deployment Here are some of the things you can do today on the Otim app: * Delegate your account to Otim on Ethereum Mainnet * Create an Otim-delegated account using just your email * Perform swap, transfer, and refuel actions on a recurring or conditional basis * Track and manage your instructions in the app dashboard # Security & Audits Source: https://docs.otim.com/docs/resources/security-and-audits ## Otim Protocol Security Audits November 14, 2025 September 11, 2025 May 7, 2025 March 25, 2025 ## Bug Bounty Program soon™️ # Getting Your Developer Keys Source: https://docs.otim.com/sdk/getting-your-developer-keys ## Step 1: Onboard Sign up for an Otim account at [app.otim.com](https://app.otim.com). ## Step 2: Request Developer Access After getting through the onboarding, find your wallet address by clicking the bottom left and notify the Otim team to activate your developer account. ## Step 3: Navigate to the Developer tab Once the Otim team has notified you that your developer access is ready, you should see the Developer tab appear in your Otim App sidebar. ## Step 4: Create a Developer Key Firstly, create a Developer Key and make sure to save the returned private key because it won't be shown again. ## Step 5: Create an API Key Then, create an API Key and make sure to save that too because it will only show once. *** And that's it! Now you're ready to start orchestrating away with any of our SDKs. You are allowed to rotate your Developer Key or API Key at any time. # Create a Basic Settlement Orchestration Source: https://docs.otim.com/sdk/guides/create-basic-settlement This guide shows you how to create a settlement orchestration that accepts tokens across multiple chains and settles to USDC on Base. Make sure you've completed the [Installation & Setup](/sdk/installation-and-setup) before continuing. ## Define the Settlement A settlement orchestration specifies: * **Settlement token and chain**: What the recipient receives and where * **Accepted tokens**: Which tokens on which chains can be used as payment * **Recipient address**: Who receives the final settlement ```go main.go theme={null} const ( // Chain IDs ethereumChainID = 1 baseChainID = 8453 // Token addresses usdcEth = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" usdcBase = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" ) // Settlement amount: 1 USDC (6 decimals) amount := new(big.Int) amount.SetString("1000000", 10) // Build settlement request buildRequest := &client.BuildSettlementOrchestrationRequest{ Params: &client.SettlementParams{ AcceptedTokens: map[client.ChainID][]common.Address{ ethereumChainID: {common.HexToAddress(usdcEth)}, baseChainID: {common.HexToAddress(usdcBase)}, }, SettlementChainId: baseChainID, SettlementToken: common.HexToAddress(usdcBase), SettlementAmount: hexutil.Big(*amount), RecipientAddress: common.HexToAddress(""), }, } // Build, sign, and submit buildResponse, _ := otimClient.BuildSettlementOrchestration(ctx, buildRequest) newRequest, _ := otimClient.NewOrchestrationFromBuild(ctx, buildResponse) otimClient.NewOrchestration(ctx, newRequest) fmt.Println("Request ID:", buildResponse.RequestID) fmt.Println("Payment Address:", buildResponse.EphemeralWalletAddress.Hex()) ``` ```typescript index.ts theme={null} // Define settlement const settlement = prepareSettlement({ amount: BigInt(1000000), // 1 USDC (6 decimals) chainId: chains.base.id, token: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base recipient: '', acceptedTokens: { [chains.mainnet.id]: ['0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'], // USDC on Ethereum [chains.base.id]: ['0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'], // USDC on Base }, maxRuns: 1 }); // Create orchestration const result = await client.orchestration.create(settlement); console.log("Request ID:", result.requestId); console.log("Payment Address:", result.ephemeralWalletAddress); ``` ## Understanding the Response When created successfully, you'll receive: * **Request ID**: Unique identifier for tracking this orchestration * **Payment Address**: The ephemeral wallet address to share with whoever is sending payment The sender transfers their tokens to the payment address, and Otim handles the cross-chain settlement to deliver the specified amount to your recipient. ## Run It ```go Command Line theme={null} go run main.go ``` ```typescript Command Line theme={null} npx tsx index.ts ``` ## Next Steps * [Track your orchestration](/sdk/guides/track-orchestration) to monitor its status * Check out the [Otim Playground](https://github.com/otimlabs/otim-playground) for complete working examples # Track an Orchestration Source: https://docs.otim.com/sdk/guides/track-orchestration After creating an orchestration, you can track its status using the request ID returned from the creation response. ## Get Orchestration Details ```go main.go theme={null} details, err := otimClient.GetOrchestrationDetails(ctx, &client.GetOrchestrationDetailsRequest{ RequestID: requestID, }) if err != nil { log.Fatal("Failed to get details:", err) } fmt.Printf("Status: %+v\n", details) ``` ```typescript index.ts theme={null} const details = await client.orchestration.getDetails({ requestId: requestId, }); console.log("Status:", details); ``` ## Understanding the Response The orchestration details include: * **Status**: Current state of the orchestration (pending, inProgress, paid, completed, failed) * **Request ID**: The unique identifier for this orchestration * **Payment Address**: The ephemeral wallet address for receiving payments * **Settlement Details**: Information about the target chain, token, and amount ## Polling for Updates For long-running orchestrations, you can poll periodically to check for status changes: ```go main.go theme={null} for { details, _ := otimClient.GetOrchestrationDetails(ctx, &client.GetOrchestrationDetailsRequest{ RequestID: requestID, }) fmt.Println("Current status:", details.Status) if details.Status == "paid" || details.Status == "completed" || details.Status == "failed" { break } time.Sleep(5 * time.Second) } ``` ```typescript index.ts theme={null} const pollInterval = 5000; // 5 seconds const checkStatus = async () => { const details = await client.orchestration.getDetails({ requestId: requestId, }); console.log("Current status:", details.status); if (details.status === "paid" || details.status === "completed" || details.status === "failed") { return details; } await new Promise(resolve => setTimeout(resolve, pollInterval)); return checkStatus(); }; const finalDetails = await checkStatus(); ``` ## Next Steps For complete working examples, check out the [Otim Playground](https://github.com/otimlabs/otim-playground). # Installation & Setup Source: https://docs.otim.com/sdk/installation-and-setup Learn how to install and use the Otim server-side SDKs. SDKs are available for [Go](https://github.com/otimlabs/otim-go-sdk) and [TypeScript](https://www.npmjs.com/package/@otim/sdk-server). ## Installation ```go Command Line theme={null} # Make sure your project is using Go Modules go mod init # Install otim-go-sdk go get github.com/otimlabs/otim-go-sdk ``` ```typescript Command Line theme={null} # Initialize your project pnpm init # Install otim-sdk-server pnpm add @otim/sdk-server ``` ## Initialize the Client ```go main.go theme={null} package main import ( "context" "fmt" "log" "math/big" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/otimlabs/otim-go-sdk/client" "github.com/otimlabs/otim-go-sdk/signer" ) func main() { ctx := context.Background() // Initialize the signer with your private key ethSigner, err := signer.NewEthSigner("") if err != nil { log.Fatal("Failed to create signer:", err) } // Create the client otimClient, err := client.NewClient( ethSigner, "https://api.otim.com", "", ) if err != nil { log.Fatal("Failed to create client:", err) } fmt.Println("Otim client initialized successfully") } ``` ```typescript index.ts theme={null} import { chains, createOtimServerClient, prepareSettlement } from '@otim/sdk-server'; async function main() { const client = createOtimServerClient({ appId: "", privateKey: "", publicKey: "", apiKey: "", environment: "production", // or "sandbox" for testnets }); await client.init(); console.log('Otim client initialized successfully'); } main().catch(console.error); ``` # Overview Source: https://docs.otim.com/sdk/overview The Otim SDK provides a complete toolkit for integrating Otim’s smart orchestration capabilities directly into your app or workfow. ## Start Building with Otim! Get developer keys in order to authorized performing actions through the SDK. ### Server-Side Want to just jump right in with some quickstart sandboxes? Check out the [Otim Playground](https://github.com/otimlabs/otim-playground) GitHub repository to find every developer example you may need.