FOR

ONCHAIN BUILDERS

Build onchain products with more control over how money moves.

Build onchain products with more control over how money moves.

Build onchain products with more control over how money moves.

Eco enables programmable swaps, powerful transaction automation, stablecoin-focused execution, and seamless cross-chain UX.

Eco enables programmable swaps, powerful transaction automation, stablecoin-focused execution, and seamless cross-chain UX.

Fast transfers and swaps, optimized for stablecoins

Fast transfers and swaps, optimized for stablecoins

Fast transfers and swaps, optimized for stablecoins

Eco Routes is a protocol for programmable swaps and fast cross-chain stablecoin transfers. Developers use Routes API and CLI to set conditions for money movement, for seamless stablecoin UX with predictable outcomes.

Eco Routes is a protocol for programmable swaps and fast cross-chain stablecoin transfers. Developers use Routes API and CLI to set conditions for money movement, for seamless stablecoin UX with predictable outcomes.

Maximum developer control

Maximum developer control

Routes API and CLI give you control over speed, verification, price quote format, and more. CLI lets you preview and publish a real transfer before writing any integration code.

Signed, verifiable quotes, checked before funds move

Signed, verifiable quotes, checked before funds move

Every quote is signed by a solver or the Eco router. Verify the signer client-side before any funds move.

Noncustodial end-to-end

Noncustodial end-to-end

Each intent is escrowed in its own vault. Once an intent expires unfilled, it becomes eligible for a refund: a permissionless transaction that doesn’t depend on Eco to validate.

Seamless multi-chain actions

Seamless multi-chain actions

Bridge and act in one step: deposit into a protocol, mint, or settle a position the moment funds land.

Gasless user experience

Gasless user experience

Fund intents with a Permit3, Permit2, or ERC-3009 signature, so users never need native gas to start a transfer.

quote.ts
1const quote = await routes.quote({
2 from: "base:USDC",
3 to: "arbitrum:USDC",
4 amount: "2500",
5 speed: "fast",
6 verification: "strict",
7 quoteFormat: "exactIn",
8})
verify.ts
1const quote = await routes.quote(request)
2
3const signer = recoverSigner(quote)
4if (!isTrustedSigner(signer)) {
5 throw new Error("untrusted quote")
6}
7
8await routes.publish(quote)
refund.ts
1const intent = await routes.publish(quote)
2// funds wait in this intent's own vault
3
4if (await intent.isExpired()) {
5 // anyone can trigger the refund
6 await intent.refund()
7}
destination.ts
1await routes.publish(quote, {
2 onArrival: [
3 approve(USDC, vault),
4 deposit(vault, quote.amountOut),
5 ],
6})
gasless.ts
1const permit = await wallet.signPermit3({
2 token: "USDC",
3 amount: "2500",
4 chains: ["base", "arbitrum"],
5})
6
7await routes.publish(quote, { funding: permit })

Turn onchain transactions into programs

Turn onchain transactions into programs

Turn onchain transactions into programs

Sauce is Eco’s runtime for the EVM and Solana. Sauce SDK lets you write transaction logic in TypeScript subset, compile it locally, and run it atomically against live onchain state. No contract to deploy, no backend to provision.

Sauce is Eco’s runtime for the EVM and Solana. Sauce SDK lets you write transaction logic in TypeScript subset, compile it locally, and run it atomically against live onchain state. No contract to deploy, no backend to provision.

Programmable, real-time routing

Programmable, real-time routing

Read multiple contracts, compare the results, and execute the best path in a single transaction.

Atomic, all-or-nothing execution

Atomic, all-or-nothing execution

An unhandled failure reverts that execution, so approvals change and money moves only if all conditions are satisfied. Fallbacks and retries are steps you program, and network gas can still be spent.

Composable with Routes

Composable with Routes

Make a program the destination of an intent, so a cross-chain transfer lands and runs your logic in the same fulfillment.

TypeScript in, bytecode out

TypeScript in, bytecode out

Write programs yourself or use the SDK’s builders for advanced flows, across EVM chains and Solana. One interface for onchain logic, without smart contract overhead.

Skills for coding agents

Skills for coding agents

Install Eco skills into Claude Code, Cursor, Codex, and more, or connect your agent to the Eco docs MCP server.

best-swap.ts
1export default program(({ pools, minOut }) => {
2 const best = max(pools.quote(10_000))
3 require(best.out >= minOut, "slippage")
4 best.pool.swap(10_000)
5})
all-or-nothing.ts
1export default program(({ amountIn, minOut }) => {
2 USDC.approve(router, amountIn)
3 const out = router.swap(USDC, WETH, amountIn)
4 require(out >= minOut, "min output")
5 // a failed check reverts the approve and the swap
6})
on-arrival.ts
1await routes.publish(quote, {
2 onArrival: program(({ received }) => {
3 const best = max(pools.quote(received))
4 best.pool.swap(received)
5 }),
6})
strategy.ts
1// one file, compiled per chain
2export default program(({ amountIn }) => {
3 const out = router.swap(USDC, WETH, amountIn)
4 lending.supply(WETH, out)
5})
6
7// sauce build → bytecode for EVM chains and Solana
skills
1// loaded by Claude Code, Cursor, Codex, and more
2skills: ["sauce-sdk", "sauce-programs"]
3
4// or point your agent at the docs MCP server
5mcp: "eco-docs"

One signature across supported chains.

One signature across supported chains.

One signature across supported chains.

Permit3 is an open-source token-permission protocol that extends Permit2 across chains. Users sign once to authorize token operations on each supported chain a flow touches.

Permit3 is an open-source token-permission protocol that extends Permit2 across chains. Users sign once to authorize token operations on each supported chain a flow touches.

Multichain approvals in one signature

Multichain approvals in one signature

Replace a stack of per-chain approvals and chain switches with a single prompt. Each chain verifies its own slice of that signature with a Merkle proof, so no cross-chain messaging is needed

Scoped and time-bound

Scoped and time-bound

Limit each permission to the exact tokens, amounts, contracts, chains, and deadline the action needs.

Permit2 compatible

Permit2 compatible

Permit3 also answers Permit2’s approve, allowance, transferFrom and lockdown, so existing Permit2 approval flows keep working alongside it.

Every token standard supported

Every token standard supported

One interface for ERC-20, ERC-721, and ERC-1155.

Witness data

Witness data

Attach order details to a permit’s signature. The signature commits to them; your contract decodes and checks what the user agreed to.

multichain.ts
1const signature = await permit3.sign({
2 chains: [base, arbitrum, optimism],
3 permits: [usdcOnBase, usdcOnArbitrum, usdcOnOptimism],
4})
5
6// each chain verifies its slice with a Merkle proof
scoped.ts
1await permit3.sign({
2 token: USDC,
3 amount: 500n,
4 spender: vault,
5 chain: base,
6 deadline: in30m,
7})
permit2.ts
1// existing Permit2 flows keep working
2await permit2.permitTransferFrom(transfer, owner, signature)
3
4// add Permit3 alongside for multichain approvals
5await permit3.permit(multichainPermit, signature)
standards.ts
1permit3.permit({ token: USDC, standard: "ERC-20", amount: 500n })
2permit3.permit({ token: pass, standard: "ERC-721", id: 42n })
3permit3.permit({ token: item, standard: "ERC-1155", id: 7n, amount: 3n })
witness.ts
1const signature = await permit3.sign(permit, {
2 witness: { order },
3 witnessType: "Order(address taker,uint256 minOut)",
4})
5
6// your contract checks exactly what the user agreed to
quote.ts
1const quote = await fetch(`${API}/v1/quotes`, {
2 method: "POST", headers, // x-api-key
3 body: JSON.stringify({
4 type: "exact-in",
5 source: { chainId: 8453, token: BASE_USDC,
6 amount: "2500000000", funder }, // base units
7 destination: { chainId: 42161,
8 token: ARB_USDC, recipient },
9 slippage: 0.005,
10 dappId: "your-app",
11 }),
12}).then((r) => r.json());
verify.ts
1// expiry, EIP-712 signature, intent hashes
2// (Verify quotes quickstart; signer from
3// GET /v1/chains)
4await verifyQuoteSignature(quote, quoteSigner);
5
6// the signature doesn't cover the JSON or tx,
7// so also check before funding:
8// · execution.transaction → same intent hashes
9// · destination token, amount and recipient
refund.ts
1// funds wait in the intent's own vault.
2// expiry alone moves nothing: once the reward
3// deadline passes unproven, anyone can send
4// the refund on the source chain's Portal
5const hash = await wallet.writeContract({
6 address: PORTAL, abi: portalAbi,
7 functionName: "refund", args: refundArgs });
8await client.waitForTransactionReceipt({ hash });
9// then confirm the creator's balance
destination.ts
1const quote = await getQuote({ // see quote.ts
2 type: "custom",
3 source: { chainId: 8453, token: BASE_USDC,
4 amount: "2500000000", funder },
5 destination: { chainId: 42161,
6 token: ARB_USDC, recipient,
7 calls: [
8 { target: ARB_USDC, data: approve,
9 value: "0" },
10 { target: VAULT, data: deposit, value: "0" },
11 ] },
12 dappId: "your-app",
13});
14// calls are quoted before funding; size them
15// from quote.destination.amountOut
gasless.ts
1// prerequisite: the user approved Permit3
2// on the token; the signed transfer is
3// bound to this quote's vault
4const permit3 = { owner, permitContract,
5 salt, deadline, timestamp, merkleRoot,
6 permits: [{ chainId: 8453, token: BASE_USDC,
7 amount: "2500000000", modeOrExpiration: 0,
8 account: quote.execution.vault }] };
9await fetch(`${API}/v1/intents/submit/permit3`, {
10 method: "POST", headers,
11 body: JSON.stringify({ permit3, signature,
12 target: { quoteId: quote.id } }) });
best-swap.ts
1import { uniswapV3 } from "@sauce/router";
2
3function main(amt: Uint256, min: Uint256) {
4 const q = UniswapV3.QuoterV2;
5 const a = q.quoteExactInputSingle({
6 tokenIn: WETH, tokenOut: USDC, amountIn: amt,
7 fee: 500, sqrtPriceLimitX96: 0 }).amountOut;
8 const b = q.quoteExactInputSingle({
9 tokenIn: WETH, tokenOut: USDC, amountIn: amt,
10 fee: 3000, sqrtPriceLimitX96: 0 }).amountOut;
11 require((a >= b ? a : b) >= min, "below min");
12 const pool = UniswapV3.Factory
13 .getPool(WETH, USDC, a >= b ? 500 : 3000);
14 uniswapV3(pool, WETH, USDC, amt, 0, min,
15 ctx.self());
16}
all-or-nothing.ts
1import { uniswapV3 } from "@sauce/router";
2
3function main(pool: Address, amt: Uint256,
4 min: Uint256) {
5 require(pot.WETH >= amt, "not enough WETH");
6 uniswapV3(pool, WETH, USDC, amt, 0, min,
7 ctx.self());
8}
9// an unhandled failure reverts this execution;
10// network gas is still spent
on-arrival.ts
1// run a compiled program when funds land
2const cook = routes.buildSauceEvmCall({
3 pot, engine, program });
4const quote = await getQuote({ // see quote.ts
5 type: "custom",
6 source: { chainId: 8453, token: BASE_USDC,
7 amount: "2500000000", funder },
8 destination: { chainId: 42161,
9 token: ARB_USDC, recipient: pot,
10 calls: [{ target: cook.target,
11 data: cook.data, value: "0" }] },
12 dappId: "your-app",
13});
strategy.ts
1import { uniswapV3 } from "@sauce/router";
2
3function main(pool: Address, amt: Uint256,
4 min: Uint256) {
5 const before = pot.USDC;
6 uniswapV3(pool, WETH, USDC, amt, 0, min,
7 ctx.self());
8 const got = pot.USDC - before;
9 USDC.approve(AaveV3.Pool, got);
10 AaveV3.Pool.supply(USDC, got, ctx.self(), 0);
11}
12// sauce compile strategy.sauce.ts --chain base
13// names resolve per chain; venues must exist
14// on each target
skills
1# give your coding agent Sauce's skills
2# (Claude Code, Codex, Cursor, Copilot, Gemini)
3sauce skills install --dir .
4
5# and its commands as an MCP server
6sauce mcp setup --agent claude,cursor
multichain.ts
1// one signature over a Merkle root of
2// per-chain permits; each chain runs its own
3// transaction with its proof (not atomic
4// across chains)
5await permit3.write.permit([
6 owner, salt, deadline, timestamp,
7 { chainId: 42161n, permits }, // ChainPermits
8 proof, // bytes32[] proof for this chain
9 signature,
10]);
scoped.ts
1// one spender, amount and expiry, one chain
2const permits = [{
3 modeOrExpiration: in30m, // >3: expiring
4 tokenKey: pad(USDC, { size: 32 }),
5 account: vault, // spender
6 amountDelta: 500_000000n, // 500 USDC
7}];
8await permit3.write.permit([owner, salt,
9 deadline, timestamp, permits, signature]);
permit2.ts
1// existing Permit2 transfers: four arguments
2await permit2.write.permitTransferFrom([
3 permit, transferDetails, owner, signature]);
4
5// Permit3 also answers Permit2's approve,
6// allowance, transferFrom and lockdown
7await permit3.write.transferFrom([
8 owner, to, 500_000000n, USDC]);
standards.ts
1// one operation array; each entry names
2// its token by a 32-byte tokenKey
3const permits = [
4 { modeOrExpiration: 0, account: to,
5 tokenKey: pad(USDC, { size: 32 }),
6 amountDelta: 500_000000n },
7 { modeOrExpiration: 0, account: to,
8 tokenKey: nftKey, // see IPermit3
9 amountDelta: 1n },
10];
witness.ts
1// the signature also commits to your order;
2// your contract still has to check it
3await permit3.write.permitWitness([
4 owner, salt, deadline, timestamp, permits,
5 orderHash, // bytes32: your hash of the order
6 "Order(address taker,uint256 minOut)",
7 signature,
8]);

Start building with Eco.

Preview a transfer with the CLI in minutes, integrate the Routes API into your backend, or talk to us about programmable execution with Sauce.

Start building with Eco.

Preview a transfer with the CLI in minutes, integrate the Routes API into your backend, or talk to us about programmable execution with Sauce.

Start building with Eco.

Preview a transfer with the CLI in minutes, integrate the Routes API into your backend, or talk to us about programmable execution with Sauce.

Frequently asked questions

Frequently asked questions

Frequently asked questions

Eco’s developer products are designed to let you program and deploy send or swap flows optimized for your use case: cross-chain deposits and payouts, programmable swaps, advanced trading and liquidity strategies, gasless single-click actions, and more.

No, all Eco products are extensible across onchain assets and markets. We optimize Routes liquidity for stablecoins, because stablecoins are the asset most users use to access or go between onchain markets.

Use Routes destination calls for a fixed sequence of actions after a transfer lands, like depositing into a vault. Use Sauce when later steps depend on state read during the same transaction, such as comparing pools and swapping through the best one.

The CLI and the open quoting service work without a key. Production Routes API calls need an API key, which you can request from the Eco team. Permit3 is open source, and Sauce v1 is available to select partners on request.

Get email updates on Eco and

everything stablecoins

THANKS FOR SUBSCRIBING

© 2026 Eco. All Rights Reserved.

Get email updates on Eco and

everything stablecoins

THANKS FOR SUBSCRIBING

© 2026 Eco. All Rights Reserved.

Get email updates on Eco and

everything stablecoins

THANKS FOR SUBSCRIBING

© 2026 Eco. All Rights Reserved.