# Introduction to Ambient

Ambient (formerly CrocSwap) is a decentralized exchange (DEX) protocol that allows for two-sided AMMs combining concentrated and ambient constant-product liquidity on any arbitrary pair of blockchain assets. &#x20;

Ambient runs the entire DEX inside a single smart contract, where individual AMM pools are lightweight data structures instead of separate smart contracts. This and other design decisions makes Ambient the most efficient Ethereum-based DEX in existence.

A hosted version of the Ambient frontend is [available here](https://ambient.finance)

## Advantages

Ambient is developed as an entirely new codebase with best engineering practices and innovative smart contract architecture choices in mind. This gives it a number of core advantages over competing DEXs:

* Substantial gas savings compared to other leading DEXs
* Combines concentrated (i.e. "UniV3 style"), ambient (i.e. "UniV2 style"), and knockout liquidity (behaves like limit orders which atomically fill and lock in a position in a single direction) within the same pool all on a single liquidity curve.
* Dynamically adjusted pool fees, maximizing LP returns relative to market conditions and demand for liquidity.
* Fees accumulated by concentrated LP positions auto re-invest back into the pool as ambient liquidity. Users earn compounded even without manually harvesting positions.
* JIT (just-in-time) liquidity attacks are prevented through the use of minimum TTL parameters on concentrated liquidity positions. Ordinary LP positions therefore earn higher fees.
* Ability for users to pre-fund tokens at the DEX in the form of "surplus collateral". Much higher efficiency for active traders by deferring token transfers to net settlement.
* "Gasless" transactions, where the user pays in the swapped token instead gas, through EIP-712 off-chain standard.
* Unique support for "permissioned pool" primitive where the ability to govern and restrict a pool can be offloaded to general purpose smart contract oracles running inside or outside the protocol.


# AMMs

Liquidity in Ambient is provided through an *automated market maker* (AMM) mechanism. Unlike traditional limit order books (LOBs) liquidity is not provided by individual orders, but an aggregate pool of liquidity with capital provided by liquidity providers (LPs).&#x20;

Each liquidity pool in Ambient represents a two-sided market between a pair of fungible assets or *token*s. At any given time, each pool has a single exchange rate determined by the ratio of *virtual reserves* committed to the pool. End users can *swap* one token in this pair for the other based on a deterministic formula.

Ambient pools use a *constant product market maker* (CPMM) algorithm. The product of the two sides of the virtual reserves will stay constant regardless of the size or direction of the swap. (Excepting fees and collected and concentrated liquidity bumps.) When a swapper sends a quantity of *base token* to the pool, the pool returns a quantity of *quote tokens* based on this constant product relationship. Hence the pool's exchange rate will move to raise the exchange rate of quote tokens relative to base tokens.&#x20;

This mechanism balances supply and demand by re-adjusting prices proportional to the size and direction of swaps. Because of this swappers do not receive the instantaneous exchange rate, but a slightly worse price depending how large their trade is relative to the liquidity in the pool. This difference is called *price impact*.&#x20;

In addition swappers pay a *liquidity fee* proportional to the amount of notional they traded. Liquidity fees are re-distributed to LPs based on their pro-rata contribution to active liquidity in the pool. The *fee rate* varies based on the specific pool and may be adjusted depending on market conditions, but is typically in the range of 0-1%.


# Concentrated Liquidity

Concentrated liquidity allows a user to provide liquidity within an arbitrary pre-defined price range on a single AMM curve. This contrasts with ambient liquidity, where the liquidity remains active across all possible prices from zero to infinity.&#x20;

The advantage of concentrated liquidity is primarily capital efficiency. The LP only has to commit collateral to support a restricted range of liquidity. For example a stablecoin pair that trades between $0.99-$1.01, it requires significantly less capital to provide concentrated liquidity in this range.

The downside of concentrated liquidity is that the position no longer accumulated fees if the curve price moves outside the range of the order. Therefore the concentrated LP either has to size their price range wisely, or periodically "rebalance" their order to move it back into range.

Ambient also supports native ambient liquidity, which is distinct from "full-range concentrated liquidity". The first advantage is that instead of accumulating fees in a separate side-pocket, fees accumulated by ambient LP positions auto-compound back into the original position without any manual management. The second advantage is that minting and burning ambient LP positions requires significantly lower gas costs. The third is that all ambient LP positions on a given curve are naturally fungible and can be easily wrapped into "LP tokens".


# Knockout Liquidity

Knockout liquidity behaves identical to range-based concentrated liquidity, except the liquidity is atomically and permanently removed from the AMM curve at any point the curve price moves past the edge of the range. Knockout liquidity can either be set to remove when curve price falls below the bottom of the range (*bids*) or when curve price rises above the top of the range (*asks*).

Knockout liquidity behaves somewhat similar to "non-reversible limit orders" found in traditional central limit order books. A user who wants to achieve directional execution at better than current market prices, can buy (sell) in the pool by placing a bid (ask) below (above) the current price. As long as the price at one point crosses this point the user's order will be filled. Unlike a vanilla concentrated liquidity range order, even if the price rises back over the fill price, the tokens the user bought will not convert back. Instead the user locks in the direction and price.&#x20;

For this reason, knockout liquidity is a useful tool for users who want directional execution, but want to receive more advantageous prices than traditional swaps. A knockout liquidity order offers better prices from 1) waiting for a cheaper price, 2) receiving instead of paying swap fees, 3) avoiding price slippage on the AMM curve. The downside though is that if the curve price never ends up reaching the knockout price, the order may never get filled, and the user will have to cancel and re-adjust.

In practice knockout liquidity is subject to a few restrictions the user should be aware. First, unlike typical concentrated liquidity, the width of the range order is fixed (at a typically narrow width) for all knockout orders in the entire pool. Second, knockout bids must always be placed below the current curve price and knockout asks above the current curve price. Third, in order to be fully knocked out, the price must move *fully* through the entire range. It's possible for the order to be "partially filled" if the curve moves into the middle of the range. And to convert back if the price moves back out of the range without ever reaching the knockout price.


# Governance & Policy

Ambient protocol uses a three-layer division of responsibility:

![](/files/smsyh165lG9a8UC8R4eD)

**Governance** fills the traditional role of a DAO. It includes full power over the protocol, and is controlled by M-of-N multisigs and timelock resolutions.

**Policy** is an intermediate layer that sits between the DAO governance and the DEX contract itself. It can ether directly relay resolutions from the governance layer, or delegate limited administrative control of the DEX to external smart contract *policy oracles* explicitly installed by DAO governance.

&#x20;**Mechanism** is the underlying DEX contract itself. `CrocSwapDex` (the core smart contract holding dex liquidity and positions) is built with tunable parameters that can be dynamically adjusted by external policy oracles. That allows the protocol to improve and experiment with new functionality in a way that's safer and more contained than directly upgrading the underlying DEX smart contract.&#x20;


# Surplus Collateral

Ambient provides the ability for users to directly deposit tokens in the DEX contract in the form of *surplus collateral*. Unlike collateral provided to support LP positions, surplus collateral sits idle and is not tied to any specific liquidity pool. At this point surplus collateral provides no yield, and will stay stable at its deposited amount without any rebalancing or loss.&#x20;

Surplus collateral can be thought of as a form of light-weight working capital, that's significantly more flexible and efficient than the traditional model of holding tokens outside the DEX. In *instant settlement mode,* a Ambient swap, mint or burn operation will directly pay or collect tokens from the user's wallet.&#x20;

Surplus collateral allows for debits or credits to be paid to the user's balance held inside the DEX contract. This avoids any token transfer associated with the swap, mint or burn, and instead involves a simple update of a single counter inside the DEX contract. An active trader can consolidate what would otherwise be a large number of token transfers across many trades, into a single net settlement at their convenience.


# Permissioned Pools

The default pool type in Ambient is a *permissionless pool*. Permissionless pools allow any user to mint, burn or swap without any restrictions. Permissionless pool types also allow any user to initialize a pool for any arbitrary pair of tokens. Pool types in Ambient are 256-bit values, allowing for an essentially unlimited number of pool types.&#x20;

*Permissioned pools* operate with the same underlying AMM mechanics, but every mint, burn, swap and initialize action is gated by an external *permission oracle*. A permission oracle can be any arbitrary smart contract that implements the interface. The address of a pool's permission oracle is determistically fixed based on the value of the pool index number. Therefore the permission oracle is fixed ahead of time.

Permission oracles may or may not also be set as policy oracles allowing them to dynamically and internally change the parameters (e.g. swap fee, concentrated liquidity tick spacing, etc.) in their own pool.

Currently permissioned pool types can only be created through protocol governance.


# Account Abstraction

All operations in the Ambient dex can be undertaken with *account abstraction*, where a user signs an operation off-chain and a third party relayer executes it on their behalf. Relayers can be made in any arbitrary token from a user's balance. There are multiple reasons why a user would want to do this:

* Avoid the inconvenience of managing transactions
* Pay an exact cost with easier to manage tokens (like USDC) instead of worrying about calculating gas fees.
* Save on execution costs by having the relayer bundle their actions with other similar actions to amortize the gas cost.
* Make a pre-signed transaction and save it for later&#x20;

There are two types of account abstraction, one is *immediate* and the other is *conditional*. Immediate account abstraction occurs when a user signs an intention and sends it to a relayer with the hope of imminent and unconditional execution.

Conditional account abstraction is when a user signs a transaction but prefers for it to be executed only after some condition is met, such as a fixed time delay or a certain market condition such as a stop loss. All intentions in Ambient can be gated with a check to an arbitrary oracle contract with arbitrary calldata. E.g. to implement a stop loss, the intention can be signed with a call to an oracle that checks the Chainlink price has exceeded a given threshold.

Intentions in Ambient use a multi-dimensional nonce system, so unlike Ethereum transactions they can be executed with arbitrary dependence and a user doesn't have to wait for one to finish before the next executes.&#x20;


# Swaps

The core functionality of Ambient is to allow users to exchange one type of token for another at fair market price&#x73;*.* If liquidity exists for a given pair in the DEX contract, then users will be able to swap between tokens. The frontend web app provides an interface for general-purpose swaps.

![](/files/sE2ZzjaPbzGFjk0V3IcS)

If adequate liquidity exists in a given for the pair, a swap will most likely be executed as a *single hop*. Single hops incur lower liquidity and gas fees, so are preferred. A *multihop swap* is when swaps across multiple pools are chained, usually in the case when there's little to no liquidity in any pair between the input and output token. The on-chain contracts take a pre-determined path. Finding a cost efficient price multihop path, or *routing*. Ambient provides an off-chain API for approximately optimal routing, but alternative clients are free to implement their own routing algorithms based on publicly available on-chain data.&#x20;

The *intermediate tokens* in a multihop swap are those tokens in the middle of the path that are only temporarily held when swapping through a sequence of pairs. Unlike in other AMMs, intermediate tokens in Ambient are never transferred because settlement occurs based on the net debit agains the entire DEX protocol. This reduces both the amount of gas from unnecessary token transfers, as well as taxable/capital events on the intermediate tokens.

In addition to the input and output tokens, the user defines the following parameters on a per swap basis (with sensible defaults chosen by the front-end app):

* Quantity to swap. This either be a fixed input quantity or fixed output quantity.
* Slippage. The minimum possible output tokens or maximum possible input tokens the user is willing to accept. Past this point the swap transaction will revert.
* Settlement. Swaps can either be settled directly with ERC20 tokens or to/from the user's surplus collateral balance at the DEX. A user can mix-and-match, i.e. pay input as ERC20 token transfer and receive output as surplus collateral.&#x20;


# LP Positions

Users can provide liquidity to Ambient by committing tokens to a specific pool. *Liquidity providers* (LPs) earn a yield on their capital generated by the swap fees paid into the pool. For receiving these fees, LPs must deal with the fact that the collateral they deposit is not fixed. LP positions continuously rebalance between the two tokens in the pair as swaps arrive in the pool.&#x20;

This rebalance imposes a statistical cost on LPs in the form of *impermanent loss* (IL), which is the difference between the value of the rebalanced position vs. an equivalent static portfolio of the two assets. The larger the price ratio of the assets in the pool moves from the starting price, the larger IL cost the LP experiences.

There are two types of LP positions supported in Ambient: *ambient liquidity* and *concentrated liquidity*. Ambient liquidity is always active at every price point in the pool. This is recommended for users who want easy to manage positions that don't need to be manually managed. It's also recommended for users with smaller amounts of capital, since ambient liquidity has lower gas costs.&#x20;

Concentrated liquidity is created in a user-determined price range. Concentrated liquidity requires substantially more sophistication and manual management. Concentrated LPs are more capital efficient, generating higher fees for a fixed amount of capital. But this is only true when the current pool price is inside the LP's price range. *Out-of-range* LPs do not contributed to the *active liquidity* in the pool, and therefore do not earn any fees for the time they're out-of-range. However even out-of-range LPs suffer from a form of statistical IL. For this reason it is imperative for yield-seeking concentrated LPs to minimize the time their positions spend out of range.

### Minting

*Minting* is the process of creating a new LP position or committing additional capital to a pre-existing LP position. Ordinary users can use the frontend web app to mint both concentrated and ambient liquidity

![](/files/O1VjWkt9llYYoUyU0s2G)

Users who want to mint an LP position have the following parameters to choose from:

* The token pair to provide liquidity on.
* Concentrated or ambient liquidity type
* Lower and upper price range that the LP is active across (concentrated liquidity only)
* Deposit quantity. Can be fixed in terms of either side of the pair.
* Maximum slippage.
* Settlement type. Deposits can be made directly from ERC20 token or from the user's surplus collateral held at the DEX.

Ambient LP position can be minted in the form of ERC20 LP tokens or as native lightweight entries inside the DEX contract. We recommend only using LP tokens when there's a specific need, as native LP positions incur a lower gas cost.

### Managing LP Positions

Users can view their existing LP positions through the web frontend

<figure><img src="/files/atvSBqFNKf7slmTmr6pG" alt=""><figcaption></figcaption></figure>

Users can remove or *burn* liquidity in an specific position. This removes the liquidity from the pool and pays back the underlying collateral (based on the current pool's price) to the user. The token collateral can either be paid back to the user's wallet or deposited into the user's surplus collateral balance at the DEX.&#x20;

Out of range positions can be re-positioned back into range with a single transaction. Under the hood this combines a burn, swap, and mint operation into a single transaction.&#x20;

![](/files/WWdYBFxT1djzQdTtXUdC)


# Surplus Collateral

Users can directly deposit a static balance of tokens at the DEX contract. *Surplus collateral* can be used as a form of lightweight working capital with lower overhead and transaction costs than dealing with ERC20 token transfers on every swap, mint and burn operation.

Surplus collateral can either be directly deposited by a user by sending tokens directly to the DEX contract. Or it can be received as the output of a swap, burn or harvest operation. Surplus collateral can either be directly withdrawn, in which case the user receives the underlying tokens back to their wallet. Or it can be paid as the input into a swap or mint operation.&#x20;

Surplus collateral can also be directly transferred between users.&#x20;


# Dynamic Fees

Unlike traditional AMMs, Ambient allows for *dynamic liquidity fees*. That involves periodically adjusting the fees in a pool over time to reflect changing market conditions.&#x20;

Some AMMs support multiple fee tiers across the same pair. Initially Ambient will run with a single pool type with a dynamically adjusted liquidity fee. That assures that all LPs receive the market-optimal fee tier without having to manually move positions between pools.

Pool fee tier is titrated by an external policy oracle, which can be continuously update over time without any changes to the underlying DEX smart contract. The general idea is that lower fee tiers tend to out-perform during periods of market calm, as most swappers patiently route to the lowest cost pools. In contrast during periods of high volatility, demand for liquidity runs hot. Swappers will pay high cost to access immediate liquidity when.&#x20;

The current fee tier model utilizes the fixed tier Uniswap V3 pools as a guineau pig. The policy oracle snapshots the accumulated returns to in-range liquidity every 60 minutes. The Ambient fee tier for that pair is dynamically toggled to whichever liquidity UniV3 pool is performing best over recent history.&#x20;


# Knockout Positions

Knockout LP positions provides an analogous facility for AMMs to reversible limit orders in CLOBs.  At a high level, users can think of knockout LP positions as a way for patient traders to get cheap directional execution. Users can either place *bids* below the current price, which commit base tokens that convert to quote tokens. Or they can place *asks* above the current price, which commit ask tokens that convert to base tokens.&#x20;

Knockout LP positions behave different than traditional concentrated LP positions, once they're fully filled the position has locked in the position at a fixed price. Traditional LP positions are not practical for directional execution, because if/when the price moves back up the tokens the user bought get sold back to the pool.&#x20;

### Versus Limit Orders

From a high level perspective, users familiar with limit orders can mostly think of knockout LP positions as equivalent. First, unlike limit orders which occupy a single price, knockout LP positions occupy a small range. This range is typically on the order of 0.64% for volatile pairs and 0.01% for stable pairs. Traders with very precise execution targets should be aware. &#x20;

Second a knockout order only locks in the position if/when the curve price moves *fully* through the range order. Once the price moves through the end of the range, the order will be filled and all the base tokens posted to the knockout bid will have fully traded into quote tokens. And vice versa for knockout asks. That makes it possible for a knockout order to be "partially filled" if the price is currently in the middle of the range. If the user removes the knockout LP position, they'll receive back both a proportional mixture of the converted tokens and and the remaining unconverted tokens.

The user should be aware that unlike a true limit order, partial fills in a knockout LP position are reversible. The converted tokens only become non reversible at the point when the order has fully filled, i.e. when the curve price touches the end of the range.&#x20;

Third, unlike most limit order books, knockout LP positions are not based on a first-in-first-out (FIFO) queue. All in-range liquidity, both knockout and classical, participates equally relative to its pro-rata contribution to aggregate active liquidity. Therefore there is no advantage to "low-latency" or layering the book to try to gain preferential execution relative to other.

Fourth,  while in range knockout LP positions earn the liquidity fee paid by swappers. This can be thought of as analogous to "maker rebates" in a LOB. Like a "maker rebate", this earned fee is *at least* proportional to the notional size of the order times the liquidity fee of the pool. Unlike limit orders, knockout LP positions can earn multiples more than this, depending how lone the curve price stays in range.&#x20;

### Post-Fill

After a knockout position has been filled, the converted tokens are locked in and the liquidity is no longer active in the curve. To retrieve the token the user must claim the position, which returns the converted tokens and the accumulated liquidity fees. In the protocol this is done by posting a Merkle proof related to the underlying transaction, but the Ambient frontend handles that complexity under the hood.


# Initializing Pools

Any user can initialize a pool for an arbitrary pair of tokens. The user who initializes the pool selects the starting price.

To prevent spam for pools on non-existent tokens or pools with drastically skewed prices, the user must permanently burn a small but economically meaningless quantity of both tokens in the pool. &#x20;


# Gasless Transactions

All operations inside Ambient can be executed in the form of "gasless" transactions. This facility allows the user to sign an EIP-712 message off-chain. The message includes an optional tip for the relayer who actually sends the Ethereum transaction and pays the gas. The action is evaluated as if the user directly called the DEX contract from their own wallet.&#x20;

This option allows users to pay for Ambient trades with arbitrary tokens in their wallet or from their DEX positions. The relayer pays for the gas, avoiding the need for the user to keep any Ethereum in their wallet. Both swaps and LP positions can be executed with gasless transactions.

Relayers tips are generally less expensive and easier to pay when paid directly from a user's pre-existing surplus collateral balance. As such we suggest users that heavily utilize this feature to maintain at least a small balance of working collateral at the dex.


# External Routers

Generally most operations can be carried out by directly interacting with the single DEX contract, obviating the need for the typical router contracts seen in most AMM designs.&#x20;

However Ambient does support the ability for users to approve external contracts to call operations on their behalf. This is useful for cases where a user wants bespoke logic, or wants to use Ambient through another protocol.&#x20;

Users can approve an external contract address to call Ambient on their behalf for a fixed or unlimited number of times. Users should be very careful before approving any router contract, as it can execute arbitrary logic, including taking their positions on its own behalf.


# Ops & Treasury Multisigs

Governance of the Ambient protocol is ultimately controlled by the DAO multisigs. There are two primary multisig authorities, *ops multisig* and *treasury multisig*.

Ops multisig is meant for the day-to-day administration of the protocol. Ops has a restricted set of power, but more flexibility and speed. Ops multisig combines a Gnosis Safe behind a 12 hour timelock. Abilities of the ops multisig include:

* Set and revise pool parameters (e.g. liquidity fees, tick spacing, JIT thresholds, etc.)
* Initialize new pool type templates
* Set, turn on or turn off the protocol fee
* Set the sub-tick size price improvement token size threshold
* Set the minimum liquidity commitment for newly initialized pool types.
* Install policy oracle conduits with any power the ops multisig has.

Treasury multisig is reserved for more serious administrative actions. However it requires a higher degree of commitment and longer delay. Treasury multisig combines a Gnosis Safe behind a 5 day timelock. In addition to the standard ops multisig powers, the treasury multisig can also:

* Upgrade the code in the DEX contract
* Transfer authority of the DEX to a new policy controller contract
* Collect the accumulated protocol fees in the DEX
* Forcibly uninstall a policy oracle before its expiry time
* Forcibly convert a permissioned pool to a permissionless pool (in case of misbehaving permission oracle)

### Treasury Multisig

Treasury multisig is currently implemented as a 2-of-3 Gnosis Safe behind an OpenZeppelin timelock. The signers on the safe are three independent members of the project

* @0xdoug&#x20;
* @miyuki\_eto&#x20;
* @benwolski

The timelock is currently set to a delay of 2 days

The contracts are available here:

* Gnosis Safe - 0xDBD8D583a18C99e7f5191351E6E739AF8e62DaC3
* Timelock - 0x7237C120FCA2081f1A36AB933B065389174962B7

### Ops Multisig

Ops multisig is currently implemented as a 2-of-3 Gnosis Safe behind an OpenZeppelin timelock. The signers on the safe are three independent members of the project

* @0xdoug&#x20;
* @miyuki\_eto&#x20;
* @benwolski

The timelock is currently set to a delay of 2 days

The contracts are available here:

* Gnosis Safe - 0x9fACdcfb3b58D85d0440aF292D64480Ad2503A6e
* Timelock - 0x41114A13230625A2735FaA7183e528Ed2538cB7b


# Policy

*The policy controller* is an intermediate layer that sits between DAO governance and the DEX contract itself. Policy is coordinated by the `CrocPolicy` smart contract which has direct administrative authority over the DEX contract. Governance decisions are called as *resolutions* in the policy contract, which directly pass through an arbitrary *protocol commands* to the DEX contract interface.&#x20;

Protocol commands can be invoked with or without escalated privilege (*sudo mode*). Sudo mode is required for all major operations in the `CrocSwapDex`contract, including upgrading code, transferring authority to a new controller or disbursing accumulated protocol fees. Only the treasury and emergency multisigs can call protocol commands in sudo mode.

The policy layer can also delegate a restricted set of administrative controls on the `CrocSwapDex` contract to external *policy oracles*. Governance can install a *policy conduit* which defines a narrow mapping between a fixed address and a narrow subset of protocol commands. The policy oracle can then independently and directly invoke these protocol commands.

The purpose of a policy oracle is to dynamically extend the base capabilities of the protocol in a modular, safely contained and pre-defined avenue without having to directly upgrade the underlying DEX contract code. An example of a policy oracle is a smart contract that dynamically adjusts the liquidity fee in a pool based on market conditions. This allows governance to offload that responsibility to a deterministic oracle.


# Upgrading Code

The base layer `CrocSwapDex` contract is built with an upgradeable architecture. New functionality can be added by installing additional proxy contracts. Broken or sub-optimal functionality can be fixed by re-installing existing proxy contracts. With 65,000 slots for unique proxy contracts, upgradeability is practically unlimited.&#x20;

However since all proxy contracts have full access to the DEX contract's state and funds, introducing new code upgrades is an extremely careful process. Only the fully privileged treasury multisig can upgrade code. And then only after an extensive timelock, which gives all users with funds at the DEX time to carefully evaluate their comfort with the newly introduced code.


# Emergency

In addition to the standard ops and treasury multisigs, Ambient governance includes an *emergency authority* reserved for unexpected cases when user funds are in immediate danger of loss.&#x20;

Emergency authority is delegated at the `CrocPolicy` contract and has the following powers:

* Force the DEX contract into *safe mode*, where all user operations (including swaps, mints, burns, deposits and withdraws) are frozen until treasury authority disables safe mode.
* Forcibly uninstall a policy oracle
* Standard ops multisig abilities

Emergency authority power is delegated to the treasury multisig and has the ability to execute with a 30 second timelock for the ability to execute near immediately.&#x20;


# Deployed Contracts

## Swell Chain

|                | Address                                    |
| -------------- | ------------------------------------------ |
| CrocSwapDex    | 0xaAAaAaaa82812F0a1f274016514ba2cA933bF24D |
| CrocPolicy     | 0x79Cf6E6aF136B04C145f330509AD547b0D7eF6e9 |
| CrocQuery      | 0xaab17419F062bB28CdBE82f9FC05E7C47C3F6194 |
| CrocImpact     | 0xd83eF4d0e968A96329aC297bBf049CDdaC7E0362 |
| CrocSwapRouter | 0x983a06261aE018FEFA16e1E95c7fda13a2eB1038 |

## Blast

|                       | Address                                    |
| --------------------- | ------------------------------------------ |
| CrocSwapDex           | 0xaAaaaAAAFfe404EE9433EEf0094b6382D81fb958 |
| CrocPolicy            | 0x503E3DfDdD22D1c4A9787D2DAc8643AFc58c162C |
| CrocQuery             | 0xA3BD3bE19012De72190c885FB270beb93e36a8A7 |
| CrocImpact            | 0x6A699AB45ADce02891E6115b81Dfb46CAa5efDb9 |
| CrocSwapRouter        | 0xaab17419F062bB28CdBE82f9FC05E7C47C3F6194 |
| CrocSwapRouterBypass  | 0xd83eF4d0e968A96329aC297bBf049CDdaC7E0362 |
| Blast Points Operator | 0xd45C74BFB150Bc2Cb94e471638283e99E4Da14EB |

#### Governance Contracts

<table><thead><tr><th width="173.76947040498442"></th><th>Address</th></tr></thead><tbody><tr><td>Treasury Multisig</td><td>0xc73C8C60ea7d7f4338F9A8542927F4F1471e36ed</td></tr><tr><td>Treasury Timelock</td><td>0x19D005f3d4A9C5B0211457339dDa9822Dc2dEE94</td></tr></tbody></table>

## Mainnet

<table><thead><tr><th width="173.76947040498442"></th><th>Address</th></tr></thead><tbody><tr><td>CrocSwapDex</td><td>0xAaAaAAAaA24eEeb8d57D431224f73832bC34f688</td></tr><tr><td>CrocPolicy</td><td>0x0b6CD0ECb176cb39Ad99B3A0E4294167a80B68a3</td></tr><tr><td>CrocQuery</td><td>0xCA00926b6190c2C59336E73F02569c356d7B6b56</td></tr><tr><td>CrocImpact</td><td>0x3e3EDd3eD7621891E574E5d7f47b1f30A994c0D0</td></tr><tr><td>CrocSwapRouter</td><td>0x533E164ded63f4c55E83E1f409BDf2BaC5278035</td></tr><tr><td>CriocSwapRouterBypass</td><td>0xa3e58B0cB05447398358B6C59E4B2465342EFEd2</td></tr></tbody></table>

#### Governance Contracts

<table><thead><tr><th width="173.76947040498442"></th><th>Address</th></tr></thead><tbody><tr><td>Ops Multisig</td><td>0x9fACdcfb3b58D85d0440aF292D64480Ad2503A6e</td></tr><tr><td>Ops Timelock</td><td>0x41114A13230625A2735FaA7183e528Ed2538cB7b</td></tr><tr><td>Treasury Multisig</td><td>0xDBD8D583a18C99e7f5191351E6E739AF8e62DaC3</td></tr><tr><td>Treasury Timelock</td><td>0x7237C120FCA2081f1A36AB933B065389174962B7</td></tr></tbody></table>

## Scroll

|                       | Address                                    |
| --------------------- | ------------------------------------------ |
| CrocSwapDex           | 0xaaaaAAAACB71BF2C8CaE522EA5fa455571A74106 |
| CrocPolicy            | 0x70b161F2f0A18Bd1865021F25f9e895021E9DC4f |
| CrocQuery             | 0x62223e90605845Cf5CC6DAE6E0de4CDA130d6DDf |
| CrocImpact            | 0xc2c301759B5e0C385a38e678014868A33E2F3ae3 |
| CrocSwapRouter        | 0xfB5f26851E03449A0403Ca945eBB4201415fd1fc |
| CriocSwapRouterBypass | 0xED5535C6237f72BD9b4fDEAa3b6D8d9998b4C4e4 |

#### Governance contracts

|                   | Address                                    |
| ----------------- | ------------------------------------------ |
| Ops Multisig      | 0x1E0cc2fbEb09e320223A380357978d651ed652bC |
| Ops Timelock      | 0xDb0eE1193C4D05eb644efb2a1db13275b8F5994f |
| Treasury Multisig | 0x81956099675d25363d17B983125dD99269A9f26F |
| Treasury Timelock | 0x51D3BA9CA9a120dA0BCf8b487Bd42878758f7916 |


# Token Transfers

### ERC20 Tokens

Standard ERC20 token transfers to and from the DEX contract are paid to users with `transfer()` calls and collected from users with `transferFrom()`calls. Users need to `approve()` or `permit()` against the `CrocSwapDex` contract for any tokens they're planning to deposit or sell in a swap.&#x20;

Tokens being received from the DEX do not require any approval calls. Tokens that are traded through the surplus collateral facility do not recall any approval. For example a short-term speculator could buy a token, have it settle to their surplus collateral deposit, and sell it back for USDC without ever needing to approve the token.

### &#x20;Native Ethereum

Ambient supports the use of the chain's native token (e.g. ETH on Ethereum) without resorting to ERC20 wrappers.

Native ETH paid by the DEX to the user is sent directly to the user's wallet address.

Native ETH deposited or swapped into the DEX must be sent in the `msg.value` of the contract call or transaction. If the user knows the exact quantity of ETH required by the transaction they can set `msg.value` exactly. Otherwise the user should attach the upper-bound of the ETH debit they expect the contract to incur. All DEX transactions involving ETH will refund any overpaid amount in `msg.value` to the end-user.

**WARNING:** ETH refunds for `msg.value` will not be paid for any transaction not involving native ETH. (E.g. a swap between USDC and DAI.) `msg.value` should only be attached on transactions involving ETH deposits or pools.


# Type Conventions

Regardless of command type of callpath Ambient employs a series of standard conventions when representing various parameters.

### Tokens

Tokens are represented as 160-bit Ethereum addresses. For standard ERC20 token types the address of the token tracker.&#x20;

For the native currency of the chain (e.g. ETH for Ethereum, MATIC for Polygon, AVAX for Polygon), this is always represented as the `0x0` address. Ambient does ***not*** use WETH or wrappers on chain native tokens. Users should always use the zero address for ETH of the chain's native token.

Virtual tokens are represented as the trailing 160 bits of the keccak256 hash of the virtual token tracker address and the virtual token salt. Hence virtual tokens are represented in address space, but the address will always be empty.

Pairs are represented as the joint combination of a base token and a quote token. For a given pair, the lower address must always be used as the base token of the pair. (Hence chain native ETH will always be the base token).&#x20;

### Pool Type Index

An essentially unlimited number of pools can be supported for any given pairs. Pool types map to specific properties of a pool, such as fee rate, tick size and whether the pool is permissioned. Pool type indexes apply consistently across any possible pair.&#x20;

Pool index is a 256-bit number chosen arbitrarily by protocol governance. Pool type index is fixed for the lifetime of any given pool. For permissioned pools the leading 160-bits of the pool index is the address of the permission oracle.

Currently Ambient only has one pool type index initialized and it is `420`

### Token Quantities

Token quantities are represented as either signed or unsigned 128-bit integers. Note that token quantities are raw values, and do ***not*** normalize for the ERC20 token decimal value.

Signed token quantities indicate the direction of the flow relative to the pool. Negative token quantities indicate a credit received by the user from the pool. Positive token quantities indicate a debit paid to the pool by the user.

### Prices

All curve prices are represented on chain as fixed-point Q64.64 representations of the *square root.* Curve prices always represent the instantaneous exchange rate between the raw tokens and do ***not*** normalize for the ERC20 token decimals.&#x20;

The SDK contains helper functions for correctly converting to/from floating point prices to Croc native Q64.64 price root representations.&#x20;

### Ticks

Ticks are represented as signed 24-bit integers that deterministically map to/from curve prices. The tick value is represented as a function of price (P):

$$log\_{1.0001}P$$

### Settlement Flags

Settlement flags are a light-weight way to indicate trade two-sided trade settlement. Settlement flags are a single 8-bit integer. Flag values are mapped to the following:

* `0x1` Indicates that the base side of the pair should be settled with surplus collateral
* `0x2` Indicates that the quote side of the pair should be settled with surplus collateral

All remaining bits are currently unused, but may be in the future. As such the client should set all other bits to 0 for future backwards compatibility.

### Pool Liquidity Fees

Pool liquidity fees (or "swap fees") are represented as unsigned 16-bit integers, and represent units of 0.0001%.&#x20;


# DEX Contract Interface

The `CrocSwapDex` contract is a thin low-level interface. It's designed with the principal of maximizing flexibility, efficiency, and future upgradeability on-chain, by delegating more complexity to off-chain clients.&#x20;

The DEX contract ABI exposes five Solidity methods:

* `swap()` - The sole "vanilla" Solidity method that accepts standard argument types. Because swaps are the most common operation this method runs purely in the "hot-path" without any calls to proxy sidecar contracts.
* `userCmd()` - Single point of entry for all possible user commands. Accepts a *callpath index* which maps to the specific proxy sidecar contract and an arbitrary byte string which is specifically structured for that specific sidecar.
* `userCmdRouter()` - Same as `userCmd()` but called by an external router contract on behalf of an end user. The user must have previosuly approved the router contract.
* `userCmdRelayer()` - Same as `userCmd()` but executes a command signed by a user off-chain with the EIP-712 standard.
* `protocolCmd()` - Executes a protocol administrative command. This call can only be made the protocol authority contract.
* `readSlot()` - Low-level view method for reading an arbitrary slot location in DEX contract storage. This only accepts the raw slot location, in most cases ordinary users are better off using the higher level `CrocQuery` contract to query information.


# userCmd Callpaths

The standard call to `CrocSwapDex` is run through `userCmd()`&#x20;

&#x20;The method call follows the below generalized format:

```
function userCmd (
  uint16 callpath, 
  bytes calldata cmd)
  public payable returns (bytes memory)
```

`callpath` is a 16-bit index that identifies the specific proxy contract the command call is forwarded to through DELEGATECALL.&#x20;

`cmd` is the raw bytestring that's passed unmodified to the specific callpath. The format of this bytestring is dependent on the specific callpath.

Every call returns the raw bytestring returned by the raw callpath, which may or may not be empty.


# Swaps

### Hot Path

The most common operation on a DEX is a vanilla swap. Therefore `CrocSwapDex` contains the entire logic for a full swap inside the core contract. This avoids the gas overhead of DELEGATECALL to another proxy contract.

{% hint style="info" %}
Note: Hot path swaps are not guaranteed to be available. Any integrations using Hot path swap should include a fallback to a cold path swap call. (Or just use cold path swap exclusively).

Hot path swaps are currently closed on the Ethereum and Scroll deployment. See section[#cold-path](#cold-path "mention") for how to format using this callpath.
{% endhint %}

The hotpath swap method is

```
 function swap (
   address base, 
   address quote,
   uint256 poolIdx, 
   bool isBuy, 
   bool inBaseQty, 
   uint128 qty, 
   uint16 tip,
   uint128 limitPrice, 
   uint128 minOut,
   uint8 settleFlags) 
   public payable 
   returns (int128 baseFlow,
            int128 quoteFlow)
```

The parameter arguments are as follows:

* base - The address of the base token or virtual token
* quote - The address of the quote token or virtual token
* poolIdx - The arbitrary index of the pool type the user is swapping on.
* isBuy - True if the user wants to pay base token and receive quote token. False if the user wants to receive base token and pay quote token
* inBaseQty - True if the quantity field is fixed in base token, false in quote token. (Regardless of whether they're being paid or receive)
* qty - The quantity of the fixed side of the swap.&#x20;
* tip - If zero the user accepts the standard swap fee rate in the pool. If non-zero the user agrees  to pay up to this swap fee rate to the pool's LPs. In standard cases this should be 0.
* limitPrice - Represents the worse possible price the user is willing to accept. If buying this represents an upper bound. For information on how to set this value or encode prices see [#note-on-limitprice](#note-on-limitprice "mention")
* minOut - Minimum (maximum) expected output (input) of the token being bought (sold). Exceeding this value will revert the transaction.
* settleFlags - Flag indicating how the user wants to settle the traded tokens for this swap. For standard direct transfer of tokens to/from the caller's wallet, this should be set with a value of 0. (see[Type Conventions](/developers/type-conventions))&#x20;

The return value is the token flows associated with the swap for the base and quote token side of the pool. Negative indicates a credit that the caller received, positive indicates a debit that the caller paid.&#x20;

#### Note on limitPrice

Limit price is based on the price of the curve, not the fill price of the swap. If the swap pushes the price of the curve to the limit price, then the swap will stop and leave the remaining quantity unfilled (rather than reverting the transaction). Traditional slippage limits should be set with the `minOut` parameter.

Price value is based on a specific encoding of the *square root* of the pool price in Q64.64 fixed point format. For more information on encoding see [Type Conventions](/developers/type-conventions)

Using a meaningful value here is not necessary if the caller is uninterested in partial fills and slippage is set with `minOut` parameter value. In this case this value can be set to "max values" below based on the direction of the swap:

* `isBuy=false` : `65538`
* `isBuy=true` : `21267430153580247136652501917186561137`

### Cold Path

The same swap functionality is available using the standard `userCmd()` method call rather than `swap()` call. The behavior is identical, except the swap code is called on a proxy contract which imposes an overhead of approximately 5000 gas.&#x20;

The swap path is called with callpath index 1. The bytestring command is just the standard ABI packing of the above swap parameters:

```
userCmd(1, abi.encode(
    base, 
    quote, 
    poolIdx,
    isBuy, 
    inBaseQty,
    qty,
    tip,
    limitPrice,
    minOut,
    settleFlags))
```

The parameter arguments are as follows:

* base - The address of the base token or virtual token
* quote - The address of the quote token or virtual token
* poolIdx - The arbitrary index of the pool type the user is swapping on.
* isBuy - True if the user wants to pay base token and receive quote token. False if the user wants to receive base token and pay quote token
* inBaseQty - True if the quantity field is fixed in base token, false in quote token. (Regardless of whether they're being paid or receive)
* qty - The quantity of the fixed side of the swap.&#x20;
* tip - If zero the user accepts the standard swap fee rate in the pool. If non-zero the user agrees  to pay up to this swap fee rate to the pool's LPs. In standard cases this should be 0.
* limitPrice - Represents the worse possible price the user is willing to accept. If buying this represents an upper bound. For information on how to set this value or encode prices see [#note-on-limitprice](#note-on-limitprice "mention")
* minOut - Minimum (maximum) expected output (input) of the token being bought (sold). Exceeding this value will revert the transaction.
* settleFlags - Flag indicating how the user wants to settle the traded tokens for this swap. For standard direct transfer of tokens to/from the caller's wallet, this should be set with a value of 0. (see[Type Conventions](/developers/type-conventions))&#x20;


# Hot Path Swap Migration

As of February 26 2024, integrations previously utilizing the `swap()` method on Ethereum and Scroll network will find that callpath reverts on all transactions.&#x20;

The core swap callpath has been upgraded from the base deployment on Ethereum and Scroll. Therefore calls made directly to `swap()` will fail, since that that callpath uses deprecated swap code. Swap calls made to `swap()` instead of `userCmd()` will now universally revert.

Any external integrators or bots will need to migrate how their swap calls are made. Any external smart contracts routing swaps will need to migrate the swap call.

Users of the Ambient frontend or the latest version Crocswap SDK do not need to take any action. Swap logic in the frontend and SDK will automatically use the updated callpath. No other transaction types are affected.

Transitioning existing integrations should be simple and straightforward. The below options describe three possible options, and users are free to use whichever they prefer.

## Option 1: Direct call to CrocSwapDex

The most gas efficient way to swap is to directly call the main dex contract with `userCmd()`, but the upgraded swap path requires a different slightly different format to encode arguments. The Cold Path section of the [Swaps](/users/swaps) documents how to call swaps using this logic. Migration specific example is provided in the below page:

[Directly Calling CrocSwapDex](/developers/dex-contract-interface/swaps/hot-path-swap-migration/directly-calling-crocswapdex)

## Option 2: External Router

This is the simplest and easiest migration option. Callers will simply need to call the same ABI compatible `swap()`function on a new contract address. However it is the highest gas cost. More details are provided in the below page:

[External Swap Router](/developers/dex-contract-interface/swaps/hot-path-swap-migration/external-swap-router)

## Future Deployments and Upgrades

This migration requirement will not affect future deployments, and `swap()` callpath will still be available on future networks such as Blast mainnet.

Any future upgrades to swap callpath will use the same proxy callpath, so any of the above migration options will remain available and open in perpetuity.


# Directly Calling CrocSwapDex

The most gas efficient approach is for users to directly call the `CrocSwapDex` contract. However with the swap path upgrade, swappers must use the `userCmd()`function.

This method takes the same arguments as `swap()`but requires pre-formatting the arguments to the call into an ABI byte array. `userCmd()` should be called with a proxy index of 1 (the swap callapth) and the standard swap arguments ABI encoded.

### Solidity

```jsx
// Ethereum
address dex = 0xAaAaAAAaA24eEeb8d57D431224f73832bC34f688

uint16 SWAP_PROXY = 1

CrocSwapDex(dex).userCmd(SWAP_PROXY, abi.encode(
	base, 
  quote,
  poolIdx, 
  isBuy, 
  inBaseQty, 
  qty, 
  tip,
  limitPrice, 
  minOut,
  settleFlags));

------------------------------------------------------------
// Scroll
address dex = 0xAaAaAAAaA24eEeb8d57D431224f73832bC34f688

uint16 SWAP_PROXY = 1

CrocSwapDex(dex).userCmd(SWAP_PROXY, abi.encode(
	base, 
  quote,
  poolIdx, 
  isBuy, 
  inBaseQty, 
  qty, 
  tip,
  limitPrice, 
  minOut,
  settleFlags));
```

### Javascript

```jsx
const { ethers } = require('ethers');
import { AbiCoder } from "ethers/lib/utils";

const provider = new ethers.providers.JsonRpcProvider(...);

const routerAbi = ... // See below

const etherDexAddr = "0xAaAaAAAaA24eEeb8d57D431224f73832bC34f688"
const scrollDexAddr = "0xAaAaAAAaA24eEeb8d57D431224f73832bC34f688"

const contract = new ethers.Contract(etherRouterAddr, routerAbi, provider);

const abi = new ethers.utils.AbiCoder()
const cmd = abi.encode([
	"address", 
	"address", 
	"uint256", 
	"bool", 
	"bool", 
	"uint128", 
	"uint16", 
	"uint128", 
	"uint128", 
	"uint8"], 
[
	base, 
  quote,
  420, 
  isBuy, 
  inBaseQty, 
  qty, 
  0,
  limitPrice, 
  minOut,
  settleFlags])

contract.userCmd(1, cmd)
```

#### ABI

```jsx
[{
      "inputs": [
        {
          "internalType": "uint16",
          "name": "callpath",
          "type": "uint16"
        },
        {
          "internalType": "bytes",
          "name": "cmd",
          "type": "bytes"
        }
      ],
      "name": "userCmd",
      "outputs": [
        {
          "internalType": "bytes",
          "name": "",
          "type": "bytes"
        }
      ],
      "stateMutability": "payable",
      "type": "function"
    }]
```


# External Swap Router

## External Router Migration

The `CrocSwapRouter` contract exposes an ABI compatible `swap()` function that works with the upgraded swap callpath. This option will add approximately 60,000 gas to swap calls compared to direct swap calls.

The contract is deployed at

Ethereum: `0x533E164ded63f4c55E83E1f409BDf2BaC5278035`

Scroll: `0xfB5f26851E03449A0403Ca945eBB4201415fd1fc`

### Solidity Example

```solidity
// Ethereum
address router = 0x533E164ded63f4c55E83E1f409BDf2BaC5278035;
CrocSwapRouter(router).swap(
	base, 
  quote,
  poolIdx, 
  isBuy, 
  inBaseQty, 
  qty, 
  tip,
  limitPrice, 
  minOut,
  settleFlags);

//Scroll
address router = 0xfB5f26851E03449A0403Ca945eBB4201415fd1fc;
CrocSwapRouter(router).swap(
	base, 
  quote,
  poolIdx, 
  isBuy, 
  inBaseQty, 
  qty, 
  tip,
  limitPrice, 
  minOut,
  settleFlags);
```

### Javascript Example

The below example uses an Ethers provider to send a swap to the router contract:

```jsx
const { ethers } = require('ethers');

const provider = new ethers.providers.JsonRpcProvider(...);

const routerAbi = ... // See below

const etherRouterAddr = "0x533E164ded63f4c55E83E1f409BDf2BaC5278035"
const scrollRouterAddr = "0xfB5f26851E03449A0403Ca945eBB4201415fd1fc"

const contract = new ethers.Contract(etherRouterAddr, routerAbi, provider);

contract.swap(
	baseAddress, 
  quoteAddress,
  420, 
  isBuy, 
  inBaseQty, 
  qty, 
  0,
  limitPrice, 
  minOut,
  0);
```

#### ABI

For an ABI to call swap on the contract, you can use the below JSON

```jsx
[
    {
      "inputs": [
        {
          "internalType": "address",
          "name": "base",
          "type": "address"
        },
        {
          "internalType": "address",
          "name": "quote",
          "type": "address"
        },
        {
          "internalType": "uint256",
          "name": "poolIdx",
          "type": "uint256"
        },
        {
          "internalType": "bool",
          "name": "isBuy",
          "type": "bool"
        },
        {
          "internalType": "bool",
          "name": "inBaseQty",
          "type": "bool"
        },
        {
          "internalType": "uint128",
          "name": "qty",
          "type": "uint128"
        },
        {
          "internalType": "uint16",
          "name": "tip",
          "type": "uint16"
        },
        {
          "internalType": "uint128",
          "name": "limitPrice",
          "type": "uint128"
        },
        {
          "internalType": "uint128",
          "name": "minOut",
          "type": "uint128"
        },
        {
          "internalType": "uint8",
          "name": "reserveFlags",
          "type": "uint8"
        }
      ],
      "name": "swap",
      "outputs": [
        {
          "internalType": "int128",
          "name": "baseFlow",
          "type": "int128"
        },
        {
          "internalType": "int128",
          "name": "quoteFlow",
          "type": "int128"
        }
      ],
      "stateMutability": "payable",
      "type": "function"
    },
  ]
```

### Notes

Users will need to approve any tokens being sent to the `CrocSwapRouter` contract address

Swapping to/from dex balance surplus collateral is not available through this contract.


# Flat LP Calls

Simple LP operations should use *flat calls* for gas efficiency. Supported flat operations are:

* Mint ambient liquidity
* Mint concentrated liquidity
* Burn ambient liquidity
* Burn concentrated liquidity
* Harvest accumulated fees in concentrated liquidity

Flat LP calls use callpath index 2:

```
userCmd(2, abi.encode(
  code,         // uint8
  base,         // address
  quote,        // address
  poolIdx,      // uint256
  bidTick,      // int24
  askTick,      // int24
  qty,          // uint128
  limitLower,   // uint128
  limitHigher,  // uint128
  settleFlags,  // uint8
  lpConduit     // address
))
```

The call code specifies the type of LP action according to the following codes:

* Mint concentrated liquidity:
  * 1 - Fixed in liquidity units
  * 11 - Fixed in base tokens
  * 12 - Fixed in quote tokens
* Burn concentrated liquidity:
  * 2 - Fixed in liquidity units
  * 21 - Fixed in base tokens
  * 22 - Fixed in quote tokens
* Mint ambient liquidity
  * 3 - Fixed in liquidity units
  * 31 - Fixed in base tokens
  * 32 - Fixed in quote tokens
* Burn ambient liquidity
  * 4 - Fixed in liquidity units
  * 41 - Fixed in base tokens
  * 42 - Fixed in quote tokens
* 5 - Harvest accumulated fees (concentrated liquidity)

The remaining parameters are

* base - The address of the base token or virtual token
* quote - The address of the quote token or virtual token
* poolIdx - The arbitrary index of the pool type the user is swapping on.
* bidTick - The tick index of the lower range of the LP position (ignored for ambient liquidity operations)
* askTick - The tick index of the lower range of the LP position (ignored for ambient liquidity operations)
* qty - The size of the liquidity being added or removed. Fixed in terms of liquidity units, base token deposit or quote token deposit. Ignored for harvest calls.
* limitLower - The minimum acceptable curve price. If the price of the pool is below this threshold the transaction will revert.
* limitUpper - The maximum acceptable curve price. If the price of the pool is above this threshold the transaction will revert.
* settleFlags - Flag indicating how the user wants to settle the traded tokens for this swap (see[Type Conventions](/developers/type-conventions))&#x20;
* lpConduit - The address of the tracker the liquidity is deposited at. E.g. the LP token tracken if the user is creating ERC20 LP tokens. If LP position is stored natively at the exhange this should be to `address(0)`


# Long Form Orders

Long form orders allow callers to execute an arbitrary sequence of individual swaps, mints and burns within and across pools in a single contract. It also allows for callers to net out any token flows between multiple pools, avoiding gas overhead and increasing capital efficiency.

Long form orders are called with callpath index 4:

```
userCmd(4, order)
```

To support arbitrarily complex sequence of calls the encoding format of the long form order uses a custom bytestring format.&#x20;

### Encoding Format

The input argument for this method is a binary encoding, but with several nested variable length array fields. Each array field is preceded by a count field that must allign with the number of elements in the array. The nested structure is visualized below.

(In the below and following diagrams all primitive type fields are marked with their Solidity type and byte size.)

&#x20;

![](/files/G3z9b8Rx65uhDDY4eieg)

Visualized in the above diagram are the following nested layers.

The base layer order directive is composed of the following sub-fields:

* Settlement directive (opening): The user directive for settling collateral related to flows in the first token of the first pair.
* Hop directives array: An array of directives, constituting a chain of hops, each corresponding to a sequence of overlapping pairs.

Each hop directive (second layer) is composed of the following sub-fields:

* Pool directives array: An array of one or more directives, each corresponding to a set of tradable actions to take place on a single liquidity pool type within the pair.
* Settlement directive: The user directive for settling related to flows in the second token of the pair. Along with the previous settlement directive (either the previous hop in the chain or the top-layer opening settlement directive), this defines the token pair over which this hop takes place on.
* Price improve flags: Flags indicating if the user is requesting off-grid price improvement and on which side.

Pool directives (third layer) are arranged as a compose of the following:

* Pool type index: Index of the pool type that the trading actions should be applied to. This index is set by the protocol and corresponds to market parameters such as fee rate, grid size, etc.
* Ambient liquidity directive: A directive defining any net mint or burn actions (if any) to take on ambient liquidity in the pool
* Range liquidity directives: An array (possibly empty) of directives related to minting or burning concentrated liquidity range orders
* Swap directive: A directive specifying the net swap action (if any) to take on the pool
* Chaining flags: A set of flags related to how the pool chains rolling flow between pairs.

Range order directives (fourth layer) are a composite of the following sub-fields:

* Open tick: The price tick index on one side of the range order
* Close bookend array: The reason this is an array is because it allows us to economically encode multiple range orders sharing a single boundary on one side.

The remaining sections decompose the composite fields not broken down by the original visualization.

#### Settlement Directive

![](/files/GKsNi278CXqWJ40RHzD0)

Describes the settlement directive from both the opening of the top-layer order directive as well as at each hop in the chain.

* Token: The address of the token for this leg of the chain. (Or `0x0` for pairs with native Ethereum legs)
* Limit Qty: The user's minimal-acceptable quantity for net flows on this leg. Worse than this threshold will revert the entire transaction
* Dust threshold: The quantity threshold below which the user requests to skip the token transfer (usually to save gas on economically meaningless flows)
* Surplus collateral flag: If true, the user requests to first settle any flows using their surplus collateral balance at the exchange.

#### Ambient Liquidity Directive

![](/files/uLHokKKZqigoWDghGeum)

* Is Add: If true indicates that this action is to mint liquidity. If false, burns liquidity.
* Liquidity: The total amount of liquidity to mint or burn. (Or zero if no action)

#### Swap Directive

![](/files/Fnr6MBBmKLx1jJmFg0xb)

* Mask: Unusued. Always set to zero.
* Flags: Bit flag field with two flags:
  * Is Buy: Indicates swap will convert base-side token to quote-side token. (By convention CrocSwap internally always defines the base side as the token with the lexically smaller address in the pair.)
  * In Base Qty: The quantity field of the swa is denominated in the pair's base-side token.
* Qty: The quantity to swap (final result could be smaller if swap hits the limit price).
* Limit Price: The worse price up to which the user is willing to trade. Note that this represents the price on the margin, for this reason the average fill price of the swap will always be better than this limit price.

#### Range Bookend Directive

![](/files/5KmmPqi8IPuOUvsOlZKK)

Describes the range directive bookend that, when attached to an open tick index defines a single liquidity range order.

* Close tick: The price tick index on the opposite side of the range order.
* Is Add: If true indicates that the order is minting liquidity. If false, burning.
* Liquidity: The amount of liquidity to mint/burn.

#### Price Improve Flags

![](/files/xbQzVccQx4D9iTWjvsv9)

* Is Enabled: If true the user is requesting off-grid price improvement. (Normally disabled unless used to save on gas.)
* Use base side: If enabled the user is requesting that price improve collateral threshold is based on the base-side token in the pair. (Be aware by convention CrocSwap internally always defines the base side as the token with the lexically smaller address in the pair.)

#### Chaining Flag

![](/files/rNkpHQ36XEKL7OTlUZ2r)

* Exit side: If true, indicates whether any gap back fills should use the second token in the pair
* Swap Defer: If true, the swap directive is executed *after* the liquidity directives. Default is to execute before.
* Offset surplus: If true, any gap backfills should be offset by the client's pre-existing surplus collateral held at the exchange.

#### Field Encoding

The long-form order directive contains four distinct types of fields:

* Composite fields: Composed of multiple sub-fields in a pre-determined arrangement. Composite fields can be nested.
* Arrays: Contain 0, 1 or more (up to 256) elements of the same type.
* Primitives: A single address or value representing a specific Solidity type that can be encoded in 32 bytes or less
* Flags: One or more boolean flags that are packed bitwise into a single byte.

All array fields conform to the following structure:

![](/files/8oOXkIf5F2mSwSazQn8c)

Starts with a length premable: a single `uint8` byte that encodes the length of the array. If the length is zero, that's the entire field. Otherwise there is N sequential elements of the sub-type of the array.

Valid primitive types are the following, along with the number of bytes they're encoded over. All primitives are big-Endian, and any bytes in excess of the size type should be padded with zero. Unlike Solidity's `abi.encode`, smaller types do not use a full 32 bytes to avoid needlessly bloating the transaction data on large order directives.

* `uint8`: 1 byte
* `uint3`: 3 bytes
* `int3`: 3 bytes
* `address`: 32 bytes
* `int128`: 32 bytes
* `uint128`: 32 bytes
* `uint256`: 32 bytes
* `int256`: 32 bytes

Flags are always encoded big-endian and padded with zeros for any unused bits in the field.


# Knockout LP Calls

Knockout liquidity positions behave analogously to limit orders in traditional limit order books. At this point the sole route for users to interact with knockout liquidity is through the knockout liquidity call path. This path supports the following transaction types:

* Mint an open knockout liquidity position
* Burn an unfilled or partially filled knockout liquidity position
* Claim a fully filled knockout liquidity position&#x20;
* Recover a fully filled knockout liquidity position. (Forfeits accumulated fees but requires no Merkle proof)

Knockout LP calls use callpath index 7:

```
userCmd(7, abi.encode(
  code,         // uint8
  base,         // address
  quote,        // address
  poolIdx,      // uint256
  lowTick,      // int24
  highTick,     // int24
  isBid,        // bool
  settleFlags,  // uint8
  args          // bytes
))
```

The call code specifies the type of LP action according to the following codes:

* 91 - Mint knockout liquidity
* 92 - Burn open knockout liquidity
* 93 - Claim filled knockout liquidity
* 94 - Recover filled knockout liquidity

The remaining parameters are

* base - The address of the base token or virtual token
* quote - The address of the quote token or virtual token
* poolIdx - The arbitrary index of the pool type the user is swapping on.
* lowTick - The tick index of the lower range of the LP position
* highTick - The tick index of the upper range of the LP position
* qty - The size of the liquidity being added or removed. Fixed in terms of liquidity units, base token deposit or quote token deposit.
* settleFlags - Flag indicating how the user wants to settle the traded tokens for this swap (see[Type Conventions](/developers/type-conventions))&#x20;
* args - Additional ABI encoded args specific to the particular operation being called

### Mint Calls

Mint calls create a new open knockout liquidity position. The process is similar to the classical concentrated range LP mint. Knockout LP positions impose additional restrictions compared to classical concentrated LP positions:

* The pool must explicitly enable knockout liquidity
* The width of the position (tick difference between ask and bid tick) must exactly match the pool's knockout width parameter.
* Knockout bids must be placed below the pool's current price. Knockout asks must be placed above the pool's current price.

The specific mint call is structured as follows:

```
userCmd(7, abi.encode(
  91,  
  base,         // address
  quote,        // address
  poolIdx,      // uint256
  lowTick,      // int24
  highTick,     // int24
  isBid,        // bool
  abi.encode(
    qty,        // uint128       
    insideMid   // bool
  )
))
```

The additional args include:

* qty - The size of the LP positions in terms of tokens. Base tokens for bid orders (knockout liquidity below the curve price) and quote tokens for ask orders (knockout liquidity above the curve price)
* insideMid - If false, the curve's current price must be outside the order range. If true, the mint can occur with the curve price inside the range. (This should almost always be set to false.)

### Burn Calls

Burn calls will remove the liquidity associated with an open knockout LP position. It can only be called for liquidity that hasn't previously been fully filled. It behaves similar to burning a classical concentrated LP position.

The specific burn call is structured as follows:

```
userCmd(7, abi.encode(
  92,
  base,         // address
  quote,        // address
  poolIdx,      // uint256
  lowTick,      // int24
  highTick,     // int24
  isBid,        // bool
  abi.encode(
    qty,        // uint128     
    inLiqQty,   // bool  
    insideMid   // bool
  )
))
```

The additional args include:

* qty - The amount of the LP position to burn in terms of tokens. Base tokens for bid orders (knockout liquidity below the curve price) and quote tokens for ask orders (knockout liquidity above the curve price)
* inLiqQty - If true qty is argument denominated in the form of sqrt(X\*Y) liquidity instead of token amount.&#x20;
* insideMid - If false, the curve's current price must be outside the order range. If true, the burn can occur with the curve price inside the range. True allows for burns when the order has been partially filled.

### Recover Call

Recover calls will reclaim the converted tokens on a knockout LP position that has been fully filled and de-activated. A recover call *will not* reclaim the liquidity fees accumulated by the position, however all of the underlying position will still be returned. The primary purpose to use recover instead of claim is to avoid posting the Merkle proof when accumulated fees are small relative to the cost of the calldata.

The specific recover call is structured as follows:

```
userCmd(7, abi.encode(
  94,
  base,         // address
  quote,        // address
  poolIdx,      // uint256
  lowTick,      // int24
  highTick,     // int24
  isBid,        // bool
  abi.encode(
    pivotTime  // uint32
  )
))
```

The additional args include:

* pivotTime - The block time that either the knockout LP tranche was minted at. (The SDK provides a function to easily query this value.)

### Claim Call

Claim calls will reclaim the converted tokens on a knockout LP position that has been fully filled and de-activated. A claim call will reclaim the underlying filled liquidity position as well as any accumulated rewards that the position collected while in range.

The specific recover call is structured as follows:

```
userCmd(7, abi.encode(
  93,
  base,         // address
  quote,        // address
  poolIdx,      // uint256
  lowTick,      // int24
  highTick,     // int24
  isBid,        // bool
  abi.encode(
    merkleRoot  // uint160
    merkleProof // uint96[]
  )
))
```

The additional args include:

* merkleRoot - The Merkle root of the knockout tick at the time the position was minted
* merkleProof - A Merkle proof starting at the posted root that must hash to the current Merkle state of the knockout tick

#### Merkle Proof

When a given tick with active knockout LP positions is crossed, the protocol snapshots the accumulated in-range fees associated with the position. To reduce gas costs and on-chain storage requirements, each snapshot is appended as an hashed entry to a Merkle chain rooted at the specific tick location. Successive knockout events occuring at the same tick will hash onto the previous chain's entry.&#x20;

Therefore to prove the fee snapshot, the user must supply a valid Merkle proof at the time of the claim call, since the snapshot is not directly accessible on-chain. Each knockout event emits an EVM log event (`CrocKnockoutCross`). The event logs can be indexed off-chain to construct the Merkle proof. The SDK and Subgraph manifest provides functionality for doing this.


# Pool Initialization

Pools can be initialized for a given pair by using callpath index 0:

```
userCmd(1, abi.encode(
  71,        // Fixed initPool subcode  
  base,      // address
  quote,     // address
  poolIdx,   // uint256
  price      // uint128
)
```

In addition to the fixed initPool command sub-code (71), the following parameters are passed to initialize a pool:

* base - The address of the base token or virtual token
* quote - The address of the quote token or virtual token
* poolIdx - The arbitrary index of the pool type the user is swapping on.
* price - The initial price in the pool. Represented as Q64.64 square root (see [Type Conventions](/developers/type-conventions) for description of how to represent prices)

Initializing a pool will require a permanent commitment of a small amount of base and quote tokens (based on the set `newPoolLiquidity` in the `CrocSwapDex` contract). Pool initialization will always try funding based on the user's surplus collateral. If insufficient will try to collect tokens directly.&#x20;

Therefore user must either fund their surplus collateral, approve ERC20 tokens, or send sufficient quantity in `msg.value` of the call. (See [Token Transfers](/developers/token-transfers) for more information on these mechanics.)


# Surplus Collateral

In addition to paying and receiving surplus collateral in the input/output of swap, mint and burn calls user can directly deposit, withdraw or transfer their surplus collateral balance.&#x20;

All of these surplus collateral use callpath 0 with specific&#x20;

### Deposits

To deposit surplus collateral, the user should call the following user command:

```
userCmd(1, abi.encode(
    73,     // Fixed sub-command code
    recv,   // address
    value,  // uint128
    token   // address
))
```

* recv - The address the surplus collateral will be credited
* value - The total amount deposited
* token - The address of the ERC20 token deposited or `address(0)` for native Ethereum)

Note that deposit will use `transferFrom()` in to deposit the surplus collateral, so the user must approve or permit at least `value` amount of the token. In the case of native Ethereum, the deposit is collected from `msg.value` so the user must attach at least the amount matching the value parameter to the Ethereum transaction.

For EIP-2612 compliant tokens, users can deposit using an off-chain permit signature avoiding the need to approve the DEX contract:

```
userCmd(0, abi.encode(
    83,     // Fixed sub-command code
    recv,       // address
    value,      // uint128
    token,      // address
    deadline,   // uint256
    v,          // uint8
    r,          // bytes32
    s           // bytes32
))
```

* deadline - The deadline for the permit call
* v, r, s - The EIP-712 signature to be passed directly to the token contract

### Withdraw

To withdraw surplus collateral, the user should call the following user command:

```
userCmd(0, abi.encode(
    74,     // Fixed sub-command code
    recv,   // address
    value,  // uint128
    token   // address
))
```

* recv - The address the surplus collateral will be sent to
* value - The total amount to withdraw from surplus collateral. 0 is treated as a magic value that will withdraw the entire balance.
* token - The address of the ERC20 token or `address(0)` for native Ethereum)

### Transfer

Users can also bilaterally transfer surplus liquidity between owner addresses:

```
userCmd(0, abi.encode(
    75,     // Fixed sub-command code
    recv,   // address
    value,  // uint128
    token   // address
))
```

* recv - The address the surplus collateral will be transferred to
* value - The total amount to transfer from surplus collateral. 0 is treated as a magic value that will withdraw the entire balance.
* token - The address of the ERC20 token or `address(0)` for native Ethereum


# Router Calls

The base `userCmd()` treates the calling address (`msg.sender`) as the owner of the call for crediting/debiting the output/input of transactions and assigning ownership of LP positions and surplus collateral. However Ambient supports allowing for a third-party smart contract (*router*) to execute arbitrary commands on the user's behalf.&#x20;

### Invocation

In most cases whatever the user wants to do can be accomplished by directly calling the `CrocSwapDex` contract. But Ambient also allows users to authorize an intermediate smart contract to call Ambient on their behalf. Router calls on behalf of users will execute as if a user had directly called `userCmd` on the Ambient contract. The method to do this is:

```
function userCmdRouter (
  uint16 callpathIdx, 
  bytes calldata cmd, 
  address client)
  returns (bytes memory)
```

`callpathIdx` and `cmd` are the same parameters that would be passed to the standard `userCmd` and will behave identically.

`client` is the address of the end user the router is calling on behalf.&#x20;

### Approval

Before a router call is invoked, the user ***must*** previously approve the specific smart contract address making the call. Otherwise the contract call will revert. The user must either directly call the approve command.

```
userCmd(0, abi.encode(
  72,         // Fixed initPool subcode  
  router,     // address
  nCalls,     // uint32
  callpaths,  // uint16[]
)
```

`nCalls` is the number of calls the router is approved to make on a user's behalf. Setting this value to the 32-bit maximum will approve the router for an unlimited number of calls (which reduces the gas overhead from decrementing a router's call count). To revoke approval, this value can be set to 0.

`callpaths` are the proxy index calls the router contract is approved for. For example to only approve the router contract to perform swaps this value would be set to `[1]` (the swap proxy contract index)


# Relayer Calls

Relayer calls allow an end-user to sign a Ambient command using an off-chain EIP-712 signature. This signed command can then be executed inside an Ethereum transaction sent by a third-party relayer. The user can optionally "tip" the relayer to compensate for the gas cost.

```
function userCmdRelayer (
  uint16 callpathIdx, 
  bytes cmd, 
  bytes conds,
  bytes tip,
  bytes signature)
  returns (bytes memory)
```

`callpathIdx` and `cmd` are the same parameters that would be passed to the standard `userCmd` and will behave identically.

#### Conds

`conds` is a bytestring encoded in a fixed format that governs the conditions of the relayer call. If any of the set conditions are not met at execution call, the transaction will fail. The bytestring is encoded as follows

```
conds = abi.encode(
  deadline,    // uint48 
  alive,       // uint48
  salt,        // bytes32
  nonce,       // uint32
  relayer)     // address 
```

`block.timestamp` must occur before `deadline` and after `alive`&#x20;

`nonce` defines a user-specific relayer nonce that increments by one on every relayer call. The user's current on-chain nonce must match the condition or the transaction will falt. This is to prevent replay attacks. Every unique value of `salt` defines a unique nonce track (that starts at 0), this allows for multidimensional nonces.

`relayer` restricts the address of the relayer that can execute the command. Either `msg.sender` or `tx.origin` must match this address or the contract call will revert. If this value is set to `address(0)` the condition is unenforced.

#### tip

The bytestring of `tip` can either be empty (in which case no tip is paid to the relayer) or can be set using the following fixed encoding scheme:

```
tip = abi.encode(
  token,     // address
  amount,    // uint128
  recv)      // address 
```

`token` is the token the tip is being paid in. `amount` is the total amount of the token being paid as a tip to relayer.

`recv` is the recipient of the relayer tip. It can either be set to a specific address (either EOA or smart contract), or can be generically paid based on the following magic values:

* `address(256)` - Tip is paid to the `msg.sender` of the contract caller
* `address(512)` - Tip is paid to the `tx.origin` of the Ethereum transaction.

Tips are always paid from the surplus collateral balance of the end-user. If the end-user has insufficient surplus collateral the contract call will revert.

#### signature

This is the EIP-712 signature of the command and conditions. It is constructed from the standard v, r, and s recovery signature as follows:

```
signature = abi.encode(
  v,       // uint8
  r,       // uint256
  s)       // uint256  
```

The signature is constructed using the EIP-712 standard. The EIP-712 domain hash is constructed as:

```
const domain = {
  name: "CrocSwap",
  chainId: [chain ID],
  verifyingContract: [CrocSwapDex contract address],
  version: "1.0"
}
```

The typed content hash is constructed as

```
CrocRelayerCall: [
  { name: "callpath", type: "uint8"},
  { name: "cmd", type: "bytes" },
  { name: "conds", type: "bytes" },
  { name: "tip", type: "bytes" }
]
```


# Query Contracts

Ambient protocol has additional stateless query contracts to provide easy and convenient access to contract data in a simple way. Because all methods in these contracts are view type, they can be called in either an on-chain or off-chain contract.&#x20;

Additional or future versions of these contracts may be deployed over time, but existing query contracts are immutable and will always be available on-chain at current address with full backwards compatibility.

The two available query contracts are

* CrocSwapQuery - Suite of methods to query liquidity positions, curve liquidity and prices, and user dex balances
* CrocImpact - Calculates price impact of a given swap operation


# CrocQuery Contract

The `CrocQuery` contract provides a set of convenient view functions for querying the state of pool parameters, liquidity curves, tick boundaries, user LP positions, and user surplus collateral positions within the core `CrocSwapDex` contract.

The `CrocQuery` contract can be found at&#x20;

Mainnet: `0xCA00926b6190c2C59336E73F02569c356d7B6b56`

Scroll: `0x62223e90605845Cf5CC6DAE6E0de4CDA130d6DDf`

### Pool Price

The below functions are used to query the pool's instantenous price and prick tick respectively.&#x20;

```
function queryPrice (address base, 
                     address quote, 
                     uint256 poolIdx) 
   public view returns (uint128)
```

The parameter types are as follows:

* `base` - The address of the base-side token in the pool's pair (always the smaller address)
* `quote` - The address of the quote-side token in the pool's pair
* `poolIdx` - The pool type index. (Defaults to 420 for standard pools)

Return value is the current price of the pool, where price is represented as a Q64.64 fixed point representation of the *square-root* of the current base to quote exchange rate in the pool.

```
function queryCurveTick (address base, 
                         address quote, 
                         uint256 poolIdx) 
   public view returns (int24)
```

The parameter types are as follows:

* `base` - The address of the base-side token in the pool's pair (always the smaller address)
* `quote` - The address of the quote-side token in the pool's pair
* `poolIdx` - The pool type index. (Defaults to 420 for standard pools)

Return value is the current price tick of the pool, where tick is defined as the floor of the base 1.0001 logarithm of the pool price.

### Pool Liquidity

The `queryLiquidity` function returns the liquidity in a pool at its current tick. This can be used to estimate a price impact for small swaps, assuming they don't cross a tick boundary.

```
function queryLiquidity (address base, 
                         address quote, 
                         uint256 poolIdx) 
   public view returns (uint128)
```

The parameter types are as follows:

* `base` - The address of the base-side token in the pool's pair (always the smaller address)
* `quote` - The address of the quote-side token in the pool's pair
* `poolIdx` - The pool type index. (Defaults to 420 for standard pools)

Return value is the current liquidity in the pool, where liquidity is represented as the *square-root* of the full-range equivalent of the product of the base and quote virtual liquidity in the pool.

### Ambient Liquidity Positions

The below function returns information associated with a full range ambient liquidity position

```
function queryAmbientTokens (address owner, 
                             address base, 
                             address quote,
                             uint256 poolIdx)
        public view returns (uint128 liq, 
                             uint128 baseQty, 
                             uint128 quoteQty) 
```

The parameter types are as follows:

* `owner` - The address of the position's owner
* `base` - The address of the base-side token in the pool's pair (always the smaller address)
* `quote` - The address of the quote-side token in the pool's pair
* `poolIdx` - The pool type index. (Defaults to 420 for standard pools)

Return values are as follows:

* `liq` - The full range liquidity contribution of this position as the square root of the base and quote tokens
* `baseQty` - The total amount of base side tokens currently owned by this position
* `quoteQty` The total amount of quote side tokens currently owned by this position

### Range Liquidity Positions

The below function returns information associated with a concrentrated range liquidity position

```
function queryAmbientTokens (address owner, 
                             address base, 
                             address quote,
                             uint256 poolIdx,
                             int24 lowerTick,
                             int24 uppeTick)
        public view returns (uint128 liq, 
                             uint128 baseQty, 
                             uint128 quoteQty) 
```

The parameter types are as follows:

* `owner` - The address of the position's owner
* `base` - The address of the base-side token in the pool's pair (always the smaller address)
* `quote` - The address of the quote-side token in the pool's pair
* `poolIdx` - The pool type index. (Defaults to 420 for standard pools)
* `lowerTick` - The tick index of the lower boundary of the range position
* `upperTick` - The tick index of the upper boundary of the range position

Return values are as follows:

* `liq` - The in-range liquidity contribution of this position as the square root of the virtual base and quote tokens&#x20;
* `baseQty` - The total amount of base side tokens currently owned by this position
* `quoteQty` The total amount of quote side tokens currently owned by this position

### Range Liquidity Positions

The below function returns information associated with a knockout liquidity or limit order position

```
function queryKnockoutTokens (address owner, 
                              address base, 
                              address quote,
                              uint256 poolIdx, 
                              uint32 pivot, 
                              bool isBid,
                              int24 lowerTick, 
                              int24 upperTick)
        public view returns (uint128 liq, 
                             uint128 baseQty, 
                             uint128 quoteQty, 
                             bool knockedOut) 
```

The parameter types are as follows:

* `owner` - The address of the position's owner
* `base` - The address of the base-side token in the pool's pair (always the smaller address)
* `quote` - The address of the quote-side token in the pool's pair
* `poolIdx` - The pool type index. (Defaults to 420 for standard pools)
* `pivot` - The EVM tick of when the limit order tick was created (used to distinguish knockout orders created at different times)
* `isBid` - If true, indicates the this was a limit order swapping base tokens for quote tokens. Vice versa if otherwise
* `lowerTick` - The tick index of the lower boundary of the range position
* `upperTick` - The tick index of the upper boundary of the range position

Return values are as follows:

* `liq` - The in-range liquidity contribution of this position as the square root of the virtual base and quote tokens&#x20;
* `baseQty` - The total amount of base side tokens currently owned by this position
* `quoteQty` The total amount of quote side tokens currently owned by this position
* `knockedOut` - If true, indicates that the order has been atomically knocked out and the position is locked and no longer active on the liquidity curve.


# CrocImpact Contract

The `CrocImpact` contract provides a simple way to calculate the price impact, and resulting token flows associated with a hypothetical swap.

The CrocImpact is deployed at the following address:

Mainnet: `0x3e3EDd3eD7621891E574E5d7f47b1f30A994c0D0`

Scroll: `0xc2c301759B5e0C385a38e678014868A33E2F3ae3`

Note that calling this contract does *not* execute a swap. It merely returns the token quantities that would be paid/received *if* a swap was to be executed at the current chain state. If being calculated off-chain, note that the it's possible that the state of the curve could change between the time the function is called and the time the actual swap transaction arrives on-chain.&#x20;

The contract has a single public method:

```
 function calcImpact (
   address base, 
   address quote,
   uint256 poolIdx, 
   bool isBuy, 
   bool inBaseQty, 
   uint128 qty, 
   uint16 tip,
   uint128 limitPrice, 
   public view 
   returns (int128 baseFlow,
            int128 quoteFlow,
            uint128 finalPrice)
```

The parameter arguments are as follows:

* base - The address of the base token or virtual token
* quote - The address of the quote token or virtual token
* poolIdx - The arbitrary index of the pool type the user is swapping on. The primary pool index on mainnet deployment is 420.&#x20;
* isBuy - True if the user wants to pay base token and receive quote token. False if the user wants to receive base token and pay quote token
* inBaseQty - True if the quantity field is fixed in base token, false in quote token. (Regardless of whether they're being paid or receive)
* qty - The quantity of the fixed side of the swap.&#x20;
* tip - If zero the user accepts the standard swap fee rate in the pool. If non-zero the user agrees  to pay up to this swap fee rate to the pool's LPs. In standard cases this should be 0.
* limitPrice - Represents the worse possible price the user is willing to accept. If buying this represents an upper bound.

The return value is the following:

* baseFlow - The base side token flows the swap would pay/receive to the caller. Negative quantity indicate a credit received by the user from the pool. Positive quantity indicates a debit paid to the pool by the user.
* quoteFlow - The quote side token flows the swap would pay/receive to the caller. Same negative/positive convention as baseFlow return value
* finalPrice - The final price the pool would arrive at after the swap was executed. Note this is the post-swap pool price, *not* the realized price the swapper pays. The latter can be calculated by dividing base and quote token flows from above.

More information on type conventions for these arguments can be found on [Type Conventions](/developers/type-conventions) page


# Logs and Queries

As a design principal, Ambient generally runs with minimal to no log events for normal operations.  This is to economize on the gas costs of common operations.&#x20;

Instead, because of its single contract design and simplified storage layout, tracing is easily done by tracing for state differences at the `CrocSwapDex` contract address. Ambient provides an off-chain API for history and position monitoring. &#x20;


# Libraries and APIs

Ambient has developed a suite of develop tooling around the CrocSwap protocol. Available resources include:

#### SDK

SDK libraries for interacting with the on-chain contracts can be found at Github here:\
<https://github.com/CrocSwap/sdk>

#### Indexer API

A backend API&#x20;


# SDK

Typescript SDK library for interacting with the on-chain contracts is available in the NPM package `@crocswap/sdk` and can be found on [Github](https://github.com/CrocSwap/sdk)<br>

Currently the SDK is only available in Typescript/Javascript.


# Indexer API

A convenient API for retrieving open positions, transaction history, price history, pool statistics, and other on-chain information in convenient form is both available for easy and convenient self-hosting, as well as being centrally hosted by the Ambient project.

More information about the available endpoints can be found in the `graphcache-go` project repo here:\
\
<https://github.com/CrocSwap/graphcache-go>\
\
A centrally hosted version of the API is available from:\
\
Mainnet: `https://ambindexer.net/gcgo/`

Scroll: `https://ambindexer.net/scroll-gcgo`

Running the indexer is light weight and can be done on most desktop machines. More instructions for running the indexer locally are in the linked Github repo.<br>


# Subgraph

A subgraph covering a comprehensive suite of dex activity exists at \
\
<https://thegraph.com/hosted-service/subgraph/crocswap/croc-mainnet> \
\
The repository for the Ambient subgraph code is available at\
\
<https://github.com/CrocSwap/croc-subgraph>\
\
In many cases users may want to consider using the [Indexer API](/developers/libraries-and-apis/indexer-api) as it has the same data but in a more normalized format.&#x20;


