
{"id":229239,"date":"2026-09-18T16:00:58","date_gmt":"2026-09-18T16:00:58","guid":{"rendered":"https:\/\/mycryptomania.com\/?p=229239"},"modified":"2026-09-18T16:00:58","modified_gmt":"2026-09-18T16:00:58","slug":"propamms-for-everyone-building-on-1inch-aqua-part-1","status":"publish","type":"post","link":"https:\/\/mycryptomania.com\/?p=229239","title":{"rendered":"PropAMMs for Everyone? Building on 1inch Aqua. Part 1."},"content":{"rendered":"<h4>How Aqua and SwapVM could open up the development of actively priced AMMs to independent developers.<\/h4>\n<p>1inch Aqua separates liquidity accounting from pricing and execution. Combined with <a href=\"https:\/\/business.1inch.com\/portal\/documentation\/aqua\/overview\">SwapVM<\/a>, that could make building actively priced AMMs aka propAMMs, much easier for independent developers to build and distribute.<\/p>\n<p>For the last three years I\u2019ve been the tech lead at Nabla Finance, one of the first propAMMs on EVM. To explore that design space, I deployed a separate WETH\/USDC propAMM on Optimism using two custom contracts on top of 1inch\u00a0Aqua.<\/p>\n<p><a href=\"https:\/\/optimistic.etherscan.io\/address\/0xfF61660F6fE99916b7D5ee5a6e0c9Ad4B8c03D9A\">ChainlinkPriceOracle <\/a><a href=\"https:\/\/optimistic.etherscan.io\/address\/0xfF61660F6fE99916b7D5ee5a6e0c9Ad4B8c03D9A\">0xfF61660F6fE99916b7D5ee5a6e0c9Ad4B8c03D9A<\/a><a href=\"https:\/\/optimistic.etherscan.io\/address\/0x146d7001bD33114DF0575CE6fF9Ed16b4fe93BD7\">OracleSwap <\/a><a href=\"https:\/\/optimistic.etherscan.io\/address\/0x146d7001bD33114DF0575CE6fF9Ed16b4fe93BD7\">0x146d7001bD33114DF0575CE6fF9Ed16b4fe93BD7<\/a><\/p>\n<p>Aqua offers a completely new approach to DEX LP with its unique set of properties and trade offs, which include many benefits for independent builders. It also means that theoretically anybody can supply capital to the propAMM described in this article (OracleSwap) with their own set of parameters via a so called <a href=\"https:\/\/business.1inch.com\/portal\/documentation\/aqua\/liquidity-layer\/strategy\">Aqua Strategy<\/a>. <a href=\"https:\/\/oracleswap.mieszkomanijak.dev\/\">You can try it out for yourself<\/a>.<\/p>\n<p>WARNING: OracleSwap is an unaudited educational implementation. It uses Chainlink push feeds with zero spread, zero fees and deliberately naive quoting. It is not intended to be profitable or to hold meaningful funds; as written, it is expected to lose money against informed\u00a0flow.<\/p>\n<p><strong>PropAMMs on Aqua\u00a0Series<\/strong><\/p>\n<p><strong>Part 1: Custom\u00a0<\/strong><a href=\"https:\/\/business.1inch.com\/portal\/documentation\/aqua\/getting-started\/build-an-aquaapp\"><strong>AquaApp<\/strong><\/a>Part 2: SwapVM + <a href=\"https:\/\/business.1inch.com\/portal\/documentation\/aqua\/swapvm\/aqua-router-opcodes\/extruction\">Extruction<\/a> (<em>Coming\u00a0soon<\/em>)Part 3: Pricing and oracles. (<em>Coming\u00a0soon<\/em>)<\/p>\n<h3>What is 1Inch Aqua and why would one build on\u00a0it?<\/h3>\n<p>Aqua is a very efficient way to manage, deploy and source liquidity. It is intended as 1inch\u2019s answer to the question of institutional grade trading onchain. It enables simultaneous deployment of the same liquidity into multiple strategies of various sorts (XYKs, CLMM ranges, RFQs and propAMMs) as well as rapid rebalancing and strategy parameters adjustment, all without moving the actual ERC20s from market maker\u2019s\u00a0wallet.<\/p>\n<p>Capital deployment and accounting are its only concerns. Pricing and execution live elsewhere: either inside SwapVM, 1inch\u2019s bespoke bytecode engine deployed as AquaSwapVMRouter, or inside a custom smart contract you write yourself aka\u00a0AquaApp.<\/p>\n<h4>It feels like\u00a0leverage<\/h4>\n<p>In contrast with earlier onchain solutions there are no <em>\u201cdeposits\u201d<\/em> to Aqua. It operates <em>solely on token approvals and virtual balances<\/em>. ERC20s leave and enter the maker\u2019s wallet exclusively during actual, atomic swaps. This leads to four profound consequences:<\/p>\n<p>Radical increase in capital efficiency by Liquidity Multiplexing. The same amount of tokens can be deployed into an arbitrary number of strategies. It\u2019s quantified as <a href=\"https:\/\/business.1inch.com\/portal\/documentation\/aqua\/overview#the-shared-liquidity-ratio\">Shared Liquidity Ratio<\/a>.High flexibility for Market Makers. Freedom to instantly modify strategy parameters independent of other LPs and experimentation without typical opportunity costs.Ease of development. Developers can rapidly build new swapping engines and strategy factories on top of maker supplied liquidity managed by Aqua; SwapVM based strategies additionally have a native path into 1inch\u2019s routing infrastructure.Tokens backing a strategy keep their other jobs: governance, airdrop allocations, staking to name a\u00a0few.<\/p>\n<p>It is worth stressing that Liquidity Multiplexing does <em>not<\/em> involve leverage of any sort. Aqua\u2019s Capital parallelisation simply allows the same capital to service multiple strategies at the same time. While it\u2019s radically novel for onchain swap liquidity, it is the ordinary way solvers and to an extent even traditional CLOB market makers\u00a0operate.<\/p>\n<h4>But what is the price of this efficiency?<\/h4>\n<p>Traditional non concentrated AMM pool swaps with <em>O(1)<\/em> computational complexity with respect to the number of liquidity providers <em>n<\/em> by forcing every LP into a single strategy: the same curve, and fee tier. For CLMM theoretical complexity depends on the number of liquidity ranges involved in a swap but practically remains O(1). It has been a conscious trade off of sacrificing efficiency and flexibility for simplicity, reliability and complete independence of execution.<\/p>\n<p>On the contrary, Aqua introduces <em>O(m)<\/em> complexity where <em>m<\/em> is the number of strategies and <em>m &gt;= n<\/em> (<em>n<\/em> is still the number of LPs) at all times. Each strategy belongs to a single LP and each LP can deploy multiple strategies in every \u201cpool\u201d and every strategy executes independently. In this case computational efficiency is sacrificed in favor of <em>high capital efficiency, ease of rebalancing, and engineering flexibility<\/em>. Yet, the onchain cost of this complexity is greatly reduced by moving it offchain, to solvers and DEX aggregators. Parties to which \u201cefficient search across many heterogeneous liquidity sources\u201d lies at the core of their operations.<\/p>\n<p>The original model was perfect for its time, CLMMs introduced more efficiency and more sophisticated market making strategies yet neither of them came close to what offchain and traditional Market Making enabled. Aqua attempts to bridge that gap in a novel and creative\u00a0way.<\/p>\n<h4>SwapVM and The Three Aqua App\u00a0Paths<\/h4>\n<p>In order to benefit from offchain aggregation, a strategy needs to be discoverable by aggregators and solver networks. SwapVM provides the native 1inch execution path: <a href=\"https:\/\/business.1inch.com\/portal\/documentation\/aqua\/liquidity-layer\/access-resolvers-and-pathfinder\">Pathfinder<\/a> discovers Aqua liquidity offchain and routes eligible Aqua strategies through 1inch\u2019s routing infrastructure, subject to its routing and access controls. SwapVM is an extensible onchain exchange framework currently exposing XYK, CLMM, stable swap and external pricing building blocks. Alternatively (and in parallel) 3rd party developers can take up integrations with other solver networks themselves.<\/p>\n<p>There are three paths by which Aqua apps can be\u00a0built:<\/p>\n<p>Path A: write a full application in Solidity without touching the SwapVM. Your contract calls pull() and push() on the Aqua contract directly to handle transfers and virtual accounting.Path B: compose a Program from SwapVM\u2019s built in instruction (opcode) set (XYCSwap, XYCConcentrate, PeggedSwap, Fee etc.) and ship it as data part of a Strategy.Path C: deploy a contract with your proprietary quoting logic and call it from inside a SwapVM Program via the Extruction instruction. SwapVM stays the executor delegating the custom quoting computation to your contract.<\/p>\n<p>Paths B and C use SwapVM and therefore have the native 1inch routing path; Path A is a bespoke AquaApp and requires separate discovery\/routing integration. The example in this article has been built with path A, though. A bespoke Solidity contract is best suited for showcasing propAMM architecture step by step. Besides, nobody would want this particular integration to be indexed, unless they are dedicated to losing money. Should there be enough interest, I will cover the other paths in the future articles of this\u00a0series.<\/p>\n<h3>What makes a\u00a0propAMM?<\/h3>\n<p>In a nutshell it\u2019s liquidity + <em>proprietary<\/em> quoting automated onchain. Quoting consists of <em>price source<\/em> and <em>quoting algorithm<\/em>. It also needs distribution i.e. access to onchain order flow. It can be divided into four broad components:<\/p>\n<p>Price source aka oracle. Latency and correctness are key properties to optimise.Quoting algorithm: the proprietary part. Should account at least for inventory skew, size, volatility and toxic flow suspicions. In extreme cases may prevent quoting completely.Liquidity. Usually internal inventory supplied by the propAMM creator (Nabla being a notable exception). Aqua provides a generalised accounting layer through which independent makers can allocate their own liquidity to strategies.Infrastructure and distribution. Onchain accounting, UIs, offchain services and aggregator\/solver integrations. Arguably the least glamorous and most tedious component, requiring a large amount of infrastructure and relationship building. Aqua provides liquidity and accounting, while SwapVM provides a native path into 1inch\u2019s routing\/distribution infrastructure, substantially reducing the amount of custom plumbing required.<\/p>\n<p>By providing for #3 and much of the execution\/routing for #4, Aqua+SwapVM lets a builder concentrate much more of their effort on the price source and quoting algorithm, particularly with Path\u00a0C.<\/p>\n<h3>The implementation<\/h3>\n<h4>TLDR<\/h4>\n<p>Contracts: <a href=\"https:\/\/github.com\/yellowBirdy\/aqua-propAMM\/tree\/part1\">github.com\/yellowBirdy\/aqua-propAMM<\/a>Frontend: <a href=\"https:\/\/oracleswap.mieszkomanijak.dev\/\">oracleswap.mieszkomanijak.dev<\/a>Example swap tx: <a href=\"https:\/\/optimistic.etherscan.io\/tx\/0x74f3f603461aff7362190f1b66584b532019e9f57c91b67d18084b4e55251ff6\">Optimism transaction<\/a><\/p>\n<h4>The price\u00a0source<\/h4>\n<p>ChainlinkPriceOracle (in <a href=\"https:\/\/github.com\/yellowBirdy\/aqua-propAMM\/blob\/part1\/src\/oracle\/ChainlinkPriceOracle.sol\">src\/oracle\/ChainlinkPriceOracle.sol<\/a>) wraps Chainlink price feeds, checks for staleness, and normalizes the values to 18 decimals before providing them to the main contract. In the deployment it\u2019s configured to ETH\/USD and USDC\/USD feeds.** (18 &#8211; oracle.decimals())); timestamp = updatedAt; }<\/p>\n<p>function getAssetPrice(address asset)<br \/>    external view<br \/>    returns (uint256 assetPrice)<br \/>{<br \/>    (uint256 price, uint256 timestamp) = _fetchAssetPrice(asset);<br \/>    if (timestamp + priceMaxAge &lt; block.timestamp) revert STALE_PRICE();<br \/>    return price;<br \/>}<\/p>\n<p>function _fetchAssetPrice(address asset)<br \/>    internal view<br \/>    returns (uint256 price, uint256 timestamp)<br \/>{<br \/>    AggregatorV3Interface oracle = oracleByAsset[asset];<br \/>    if (address(oracle) == address(0)) revert UNKNOWN_ASSET();<\/p>\n<p>    (, int256 signedPrice,, uint256 updatedAt,) = oracle.latestRoundData();<br \/>    \/\/\/ @notice casting a negative value to uint256 will yield a dangerous result<br \/>    if (signedPrice &lt;= 0) revert INVALID_PRICE();<br \/>    \/\/\/ @notice normalization to 18 decimals<br \/>    price = uint256(signedPrice) * (10 ** (18 &#8211; oracle.decimals()));<br \/>    timestamp = updatedAt;<br \/>}<\/p>\n<h4>The pricing algorithm (it\u2019s a\u00a0mulDiv)<\/h4>\n<p>uint8 decIn = IERC20Metadata(tokenIn).decimals();<br \/>uint8 decOut = IERC20Metadata(tokenOut).decimals();<\/p>\n<p>if (decIn &gt; decOut) {<br \/>    priceOut *= 10 ** (decIn &#8211; decOut);<br \/>} else {<br \/>    priceIn *= 10 ** (decOut &#8211; decIn);<br \/>}<\/p>\n<p>amountOut = Math.mulDiv(amountIn, priceIn, priceOut);<\/p>\n<p>Exact out mirrors the calculation but rounds the required input up, in favour of the\u00a0maker:<\/p>\n<p>uint8 decIn = IERC20Metadata(tokenIn).decimals();<br \/>uint8 decOut = IERC20Metadata(tokenOut).decimals();<\/p>\n<p>if (decOut &gt; decIn) {<br \/>    priceIn *= 10 ** (decOut &#8211; decIn);<br \/>} else {<br \/>    priceOut *= 10 ** (decIn &#8211; decOut);<br \/>}<\/p>\n<p>amountIn = Math.mulDiv(<br \/>    amountOut,<br \/>    priceOut,<br \/>    priceIn,<br \/>    Math.Rounding.Ceil<br \/>);<\/p>\n<p>Both quotes reduce to a decimal adjusted mulDiv; _quoteExactOut uses Math.Rounding.Ceil so rounding favours the\u00a0maker.<\/p>\n<p>This is the most naive implementation of a price source and quoting algorithm. <em>Do not use it<\/em> <em>in production.<\/em> Quoting is a complex and tightly guarded subject worth potentially hundreds of millions of USD. In the future installments of this series I intend to provide you with examples of better solutions and guidance on what to pay attention to when designing your\u00a0own.<\/p>\n<h4>The Aqua mechanics<\/h4>\n<p>As I mentioned earlier, Aqua limits its concerns to accounting (and permissions) only. The whole swapping logic aka application is represented as an address and an opaque bytes string called strategy.<\/p>\n<p>maker \u2192 application \u2192 strategyHash \u2192 token \u2192 balance<\/p>\n<p><em>Strategy<\/em> is an arbitrary struct defined by each app. In our case it\u00a0is:<\/p>\n<p>struct Strategy {<br \/>  address maker;<br \/>  address token0;<br \/>  address token1;<br \/>  bytes32 salt;<br \/>  uint256 nominalBalance0;<br \/>  uint256 nominalBalance1;<br \/>}<\/p>\n<p>Strategy memory strat = Strategy(<br \/>  makerAddress,<br \/>  USDC,<br \/>  WETH,<br \/>  bytes32(0),<br \/>  5_000_000,<br \/>  2_000_000_000_000_000<br \/>);<br \/>bytes32 stratHash = keccak256(abi.encode(strat));<\/p>\n<p>That\u2019s it. Aqua handles token movement and virtual accounting, while the app defines the swap flow. In this no callback implementation, OracleSwap first receives tokenIn from the taker and approves Aqua. It then credits the maker\u2019s strategy with push() and transfers tokenOut from the maker to the recipient with\u00a0pull():<\/p>\n<p>IERC20(tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);<br \/>IERC20(tokenIn).forceApprove(address(AQUA), amountIn);<\/p>\n<p>AQUA.push(<br \/>    strategy.maker,<br \/>    address(this),<br \/>    strategyHash,<br \/>    tokenIn,<br \/>    amountIn<br \/>);<\/p>\n<p>AQUA.pull(<br \/>    strategy.maker,<br \/>    strategyHash,<br \/>    tokenOut,<br \/>    amountOut,<br \/>    to<br \/>);<\/p>\n<p>The input tokens land in the maker\u2019s (aka Strategy deployer, not to be confused with the app contract deployer aka the Dev) account, output tokens are sent to the taker, then maker\u2019s virtual balances are updated accordingly.<\/p>\n<p>The rest of <a href=\"https:\/\/github.com\/yellowBirdy\/aqua-propAMM\/blob\/part1\/src\/OracleSwap.sol\">src\/OracleSwap.sol<\/a> is unremarkable in the way you want: swapExactIn, swapExactOut, quote functions for both directions mirroring the swaps deterministically so a simulation and a fill agree, and slippage bounds that revert with InsufficientOutputAmount or ExcessiveInputAmount.<\/p>\n<p>Tooling: Foundry for contracts and tests, Hardhat Ignition for deployment and CLI scripts for strategy lifecycle (ship and dock) and swap execution.<\/p>\n<h3>What I left out and what\u2019s\u00a0coming<\/h3>\n<p>The honest inventory. Almost every item here is a potential future installment.<\/p>\n<h4>Pricing<\/h4>\n<p>Zero spread and zero\u00a0feeNo inventory awareness. nominalBalance0 and nominalBalance1 are in the Strategy struct, but the quoting algo doesn\u2019t use them\u00a0yet.No depth curve and no size\u00a0capNo volatility adjustmentsNo toxic flow protectionInadequate price\u00a0source<\/p>\n<h4>Aqua mechanics<\/h4>\n<p>Quotes ignore safeBalances, so a quote can succeed while the swap\u00a0revertsMissing callback path prevents flash swaps and limits composabilityMost notably: SwapVM is not used, so this custom AquaApp does not get the native 1inch discovery\/routing path available to SwapVM based strategies.<\/p>\n<h4>What\u2019s next<\/h4>\n<p>I\u2019m planning this series for 3 to 5 articles going in two different yet complementary directions:<\/p>\n<p>Implementation as a SwapVM program with Extruction SwapVM opcode (external pricing contract). Path\u00a0C.Deeper dive into propAMM pricing. Both price source and the quoting algorithm.<\/p>\n<p>If you liked it please share and let me know if you would like to read the other\u00a0parts.<\/p>\n<p><em>Originally published at <\/em><a href=\"https:\/\/mieszkomanijak.dev\/writing\/propamms-for-everyone-building-on-1inch-aqua-part-1\/\"><em>https:\/\/mieszkomanijak.dev<\/em><\/a><em> on September 15,\u00a02026.<\/em><\/p>\n<p><a href=\"https:\/\/medium.com\/coinmonks\/propamms-for-everyone-building-on-1inch-aqua-part-1-5296ace3f76e\">PropAMMs for Everyone? Building on 1inch Aqua. Part 1.<\/a> was originally published in <a href=\"https:\/\/medium.com\/coinmonks\">Coinmonks<\/a> on Medium, where people are continuing the conversation by highlighting and responding to this story.<\/p>","protected":false},"excerpt":{"rendered":"<p>How Aqua and SwapVM could open up the development of actively priced AMMs to independent developers. 1inch Aqua separates liquidity accounting from pricing and execution. Combined with SwapVM, that could make building actively priced AMMs aka propAMMs, much easier for independent developers to build and distribute. For the last three years I\u2019ve been the tech [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":229240,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[],"class_list":["post-229239","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-interesting"],"_links":{"self":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts\/229239"}],"collection":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=229239"}],"version-history":[{"count":0,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/posts\/229239\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=\/wp\/v2\/media\/229240"}],"wp:attachment":[{"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=229239"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=229239"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mycryptomania.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=229239"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}