Across Docs

Minimum-input quotes

Run your own swap or other origin-chain action first, then bridge whatever it produced.

Use a minimum-input quote when your own contract does something on the origin chain before the funds reach Across: a swap, a vault withdrawal, an unwrap, or any other call. You rarely know the exact amount that action will produce, so instead of a fixed origin.amount you quote on a floor, origin.minInputAmount. Your contract then deposits whatever its action produced, as long as it meets the floor, and Across bridges all of it.

Activated on request. Minimum-input quotes need the min-input-quote permission. Contact Across to activate it for your integration, then select Minimum-input quote when you create a key in the integrator portal. Without it, the quote returns 403 permission.not_entitled.

How it works

  1. You request a quote with origin.minInputAmount. It is priced on that floor.
  2. In one transaction, your contract runs its own actions, approves Across to take the funds, and sends the deposit transaction from the quote.
  3. Across bridges the whole deposit. The guaranteed output, the fees and your app fee scale with the amount actually deposited.
  4. You track the transfer like any other.

Quote on the floor

Send origin.minInputAmount in place of origin.amount. Set depositor to the address that will send the deposit, usually your contract. A refund on the origin chain is paid to it.

1-quote.ts
const BASE_URL = "https://api.across.to";
const API_KEY = process.env.ACROSS_API_KEY;

const res = await fetch(`${BASE_URL}/v1/quotes`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    origin: {
      token: { chainId: 42161, address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831" },
      minInputAmount: "100000000", // at least 100 USDC will come out of your swap
      depositor: "0xYourContractAddress",
    },
    destination: {
      token: { chainId: 8453, address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" },
      recipient: "0xUserWalletAddress",
    },
    amountType: "exact_input",
  }),
});

const quote = await res.json();

The response echoes the floor as origin.minAmount. Every amount and fee on the quote, including destination.minAmount, is for a deposit of exactly the floor.

Read the two actions

nextActions holds two steps, and no submit_execution:

ActionWhat it is
approve_token (id: "approval")The ERC-20 approval for spender
send_transaction (id: "prefunded-store")The deposit: send data to to on the origin chain

The deposit transaction completes the order on chain. Across picks it up from there, so there is nothing to submit to the executions endpoint.

For a native-token origin, send the deposit as the transaction value, at least minAmount.

Deposit from your contract

Run your action, approve what it produced, and send the deposit transaction, all in the same transaction:

YourContract.sol
uint256 produced = _yourOriginAction();              // swap, withdraw, unwrap, …
IERC20(token).approve(spender, produced);            // `spender` from the approval action
(bool ok, ) = depositTo.call(depositData);           // `to` and `data` from the deposit action
require(ok, "Across deposit failed");

The deposit must land before the quote's originValidUntil.

The deposit takes everything it is allowed to. It pulls the caller's whole balance of the token, up to the allowance. The quote's approval action asks for an unlimited allowance, so a contract that holds other funds, or a wallet that follows the action as-is, would deposit all of it. Approve only the amount you mean to deposit, from a contract that holds only this user's funds. Don't hand this flow to end users' wallets.

For the same reason, authorizationPolicy.approvalAmount: "exact" is rejected on these quotes: an exact approval of the floor could not cover a larger deposit. Set the allowance from your contract instead.

Track it

Use the quote's orderId with GET /v1/transfers/{orderId}, or receive updates through webhooks, exactly as in the Quickstart.

Depositing more than the floor

  • The whole deposit is bridged. Relayers compete on the amount actually deposited, not the floor.
  • The guaranteed output scales with it. Depositing 2% more than the floor raises the guaranteed minimum output by 2%.
  • Your app fee is sized on the actual deposit too.
  • With destinationActions, steps that set populateDynamically: true use everything that arrived. On steps with a fixed amount, the surplus goes to destination.refundAddress, or to the recipient if you did not set one.

A deposit below the floor reverts, so nothing moves. There is no cap above the floor. A deposit far above it is auctioned at its real size, and if no relayer fills it, it is refunded like any other transfer.

Limits

  • Gasful, exact_input only. Your contract sends the deposit and pays its gas, through the approval path. If you set authorizationPolicy.allowedTypes, it must include approval.
  • EVM origins, crosschain only. Not for same-chain swaps, and not from Solana, Tron or HyperCore.
  • Not combinable with origin.amount, originAction, attestation or authorizationPolicy.approvalAmount: "exact".

On this page