Coder Social home page Coder Social logo

uniswapx's Introduction

UniswapX

Integration Tests Unit Tests

UniswapX is an ERC20 swap settlement protocol that provides swappers with a gasless experience, MEV protection, and access to arbitrary liquidity sources. Swappers generate signed orders which specify the specification of their swap, and fillers compete using arbitrary fill strategies to satisfy these orders.

UniswapX Protocol Architecture

Architecture

Reactors

Order Reactors settle UniswapX orders. They are responsible for validating orders of a specific type, resolving them into inputs and outputs, and executing them against the filler's strategy, and verifying that the order was successfully fulfilled.

Reactors process orders using the following steps:

  • Validate the order
  • Resolve the order into inputs and outputs
  • Pull input tokens from the swapper to the fillContract using permit2 permitWitnessTransferFrom with the order as witness
  • Call reactorCallback on the fillContract
  • Transfer output tokens from the fillContract to the output recipients

Reactors implement the IReactor interface which abstracts the specifics of the order specification. This allows for different reactor implementations with different order formats to be used with the same interface, allowing for shared infrastructure and easy extension by fillers.

Current reactor implementations:

Fill Contracts

Order fillContracts fill UniswapX orders. They specify the filler's strategy for fulfilling orders and are called by the reactor with reactorCallback when using executeWithCallback or executeBatchWithCallback.

Some sample fillContract implementations are provided in this repository:

  • SwapRouter02Executor: A fillContract that uses UniswapV2 and UniswapV3 via the SwapRouter02 router

Direct Fill

If a filler wants to simply fill orders using funds held by an address rather than using a fillContract strategy, they can do so gas efficiently by using execute or executeBatch. These functions cause the reactor to skip the reactorCallback and simply pull tokens from the filler using msg.sender.

Integrating with UniswapX

Jump to the docs for Creating a Filler Integration.

Deployment Addresses

Contract Address Source
Exclusive Dutch Order Reactor 0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4 ExclusiveDutchOrderReactor
OrderQuoter 0x54539967a06Fc0E3C3ED0ee320Eb67362D13C5fF OrderQuoter
Permit2 0x000000000022D473030F116dDEE9F6B43aC78BA3 Permit2

Usage

# install dependencies
forge install

# compile contracts
forge build

# run unit tests
forge test

# run integration tests
FOUNDRY_PROFILE=integration forge test

Fee-on-Transfer Disclaimer

Note that UniswapX handles fee-on-transfer tokens by transferring the amount specified to the recipient. This means that the actual amount received by the recipient will be after fees.

Version Log

Version Number Commit Contract Address
1.0 597cf617dd6d32b3f181edbc37aed11bc5648d93 Contract no longer in use. Read more about the bug here.
1.1 cf53fc7dd48029a9189d26812d676a4ea9d08d6c 0x6000da47483062A0D734Ba3dc7576Ce6A0B645C4

Audit

V1

V1.1

V2

Bug Bounty

This repository is subject to the Uniswap Labs Bug Bounty program, per the terms defined here.

uniswapx's People

Contributors

akarys92 avatar azflin avatar dannydaniil avatar eltociear avatar ewilz avatar hensha256 avatar marktoda avatar mr-uniswap avatar rileydcampbell avatar snreynolds avatar willpote avatar zhongeric avatar zumzoom avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

uniswapx's Issues

Add order type generic test suite

We have several reactors already and more will come in the future. While the order structure and resolution semantics differ, there is a lot of behavior that should be constant between them.

We should have a generic extensible test suite that covers general cases for all reactor types. Some general case examples:

  • same signature can't be used twice
  • all resolved outputs must be fulfilled else fails
  • no more than max resolved input is taken
  • multiple orders can be filled in batch

Can do this by writing a base test suite with a virtual setUp function and virtual buildOrders function

UniswapX orders API contains mixed second/millisecond timestamps

Referring to the UniswapX swagger docs to query orders...

  • When sorted with sortKey=createdAt&desc=false, it returns orders starting with "createdAt": 1677266409.
  • When sorted with sortKey=createdAt&desc=true, it returns orders starting with "createdAt": 1677247163306.

Notice the first query returns createdAt with 10 digits, but the second query returns createdAt with 13 digits. This means the orders API contain createdAt values with both second and millisecond units, making the sort unusable. Orders in milliseconds from 9 months ago are interpreted as newer than orders today in seconds.

Reproducible steps

curl -X 'GET' \
  'https://api.uniswap.org/v2/orders?limit=3&sortKey=createdAt&desc=false&chainId=1' \
  -H 'accept: application/json'
{
  "orders": [
    {
      "createdAt": 1677266409,
      "orderHash": "0xe632accbc66d256b06a1dc086674c3e1ad35389f4a5092844514297fd2696fc9",
    },
    {
      "createdAt": 1677480309,
      "orderHash": "0x65d4f575a1a108d72268bc8ef68c5ff137d3ea42b0620feebb15c9dca4a85711",
    },
    {
      "createdAt": 1677482239,
      "orderHash": "0x21e918919abf507cdaa53024da41c26a22ed088876c059abda8599a6884b9e38",
    }
  ]
}
curl -X 'GET' \
  'https://api.uniswap.org/v2/orders?limit=3&sortKey=createdAt&desc=true&chainId=1' \
  -H 'accept: application/json'
{
  "orders": [
    {
      "createdAt": 1677247163306,
      "orderHash": "0xa733542f84862db1f545d72914b7dcda5da1886549dcee99758cfc7dc644c88b",
    },
    {
      "createdAt": 1677243562051,
      "orderHash": "0xd6c796e22c2d6daac545067503618fb17338c5480146a7c68406035b4d239de5",
    },
    {
      "createdAt": 1677239962653,
      "orderHash": "0x474fe0fd8ca33303eb66a4983571cd1ce8f446e40ea4f3e7a7fdfba119188581",
    }
  ]
}

Fix DirectTakerExecutor ownership issue

Right now the DirectTakerExecutor has an issue where anyone can call execute to take earned funds. I think easy solution is to just have configured whitelisted reactor address

Deploy all Reactor Implementations

I already asked in Discord, but I didn't get an answer.

Why is only the ExclusiveDutchOrderReactor deployed? Will the other implementations, LimitOrderReactor/DutchOrderReactor also be deployed?
If yes, when? If no, why not?

Consider re-instating nested orderinfo

We flattened orderinfo to save gas on encoding and hashing. One problem here is some fields / info is duplicated for each order type, opening new ways to easily brick sig verification. Ie adding new field to orderinfo without updating the respective witness generation can cause unprotected order fields

Consider FoT token case

Currently gouda reactors transferFrom output tokens from the filler to the recipient. For FoT tokens, this can result in the user getting less than intended as the actually transferred amount would not include the fee

https://api.mainnet.minepi.com/operations/57720890124673025

{
"_links": {
"self": {
"href": "https://api.mainnet.minepi.com/operations/57720890124673025"
},
"transaction": {
"href": "https://api.mainnet.minepi.com/transactions/dd5121380a88ba1269eb427347187e3aa050b832f08322381b453cd7a9205632"
},
"effects": {
"href": "https://api.mainnet.minepi.com/operations/57720890124673025/effects"
},
"succeeds": {
"href": "https://api.mainnet.minepi.com/effects?order=desc\u0026cursor=57720890124673025"
},
"precedes": {
"href": "https://api.mainnet.minepi.com/effects?order=asc\u0026cursor=57720890124673025"
}
},
"id": "57720890124673025",
"paging_token": "57720890124673025",
"transaction_successful": true,
"source_account": "GDZAIZDOJ3RB5Q6VB2HRCOGTJJKHNNSTAV7SLFB67Y5ZF72DX2XE74UW",
"type": "claim_claimable_balance",
"type_i": 15,
"created_at": "2024-02-28T07:32:33Z",
"transaction_hash": "dd5121380a88ba1269eb427347187e3aa050b832f08322381b453cd7a9205632",
"balance_id": "0000000009a0c52572f2e9a37b5277aabd07ec5442d41b4f771948a681a9723d950b940a",
"claimant": "GDZAIZDOJ3RB5Q6VB2HRCOGTJJKHNNSTAV7SLFB67Y5ZF72DX2XE74UW"
}

Actor based testing (w foundry invariants)

Create some scenarios to test using the foundry actor-based invariant testing. This can cover fuzzed transaction ordering as well as parameters and callers.

This suite could cover testing cases such as:

  • offerer makes multiple orders, they can be filled in any order
  • multiple offerers make orders, they can be filled together or separate
  • no transaction ordering allows an order to be canceled (nonce used) without also being filled
    Etc

Optimize Operational Costs for Cross-Chain Swap Fillers via Reactor Contract Asset Management

💡 Optimize Operational Costs for Cross-Chain Swap Fillers via Reactor Contract Asset Management

Description

📃 While reviewing the white paper's diagram related to cross-chain swaps, I noticed a potential improvement in the operational cost structure for fillers. Currently, fillers transfer bond assets each time they claim a new order and get it back after oracle message, incurring significant token transfer gas costs.

🚀 Proposal: I propose a modification where fillers maintain their assets within the Reactor contract and deposit bond to this contract or another contract to use it as bon credit. This approach allows fillers to utilize their credit in the Reactor contract for claiming new orders, eliminating the need for repetitive asset transfers and thus reducing the associated gas costs.

⚠️ Note: This suggestion is based on my understanding of the system as derived from the white paper's diagrams, and I haven't delved into the actual implementation details. This idea aims to streamline the process and decrease operational expenses for fillers involved in cross-chain swaps.

Screenshot 2024-01-02 at 10 25 41 AM

https://uniswap.org/whitepaper-uniswapx.pdf

Consider batch execution error handling

When filler calls executeBatch(), we do not necessarily want the txn to revert if one or more orders fail, we may want to handle partial execution.

I did some quick thinking and this is one possible solution:

In IReactorCallback.reactorCallback(), have it return an array of booleans (corresponding to whether a resolvedOrder succeeded or not).

  • if true, proceed to transferring outputs from fillContract to recipient(s)
  • if false, transfer input tokens back to maker

I told Mark about this, and he expressed objection to implementing this altogether. Here is his rationale:

  • "i think its doable but i still much prefer having it be all-or-none. Having to transfer the input tokens back to the maker adds a lot of complexity / moving parts.
    as a concrete example, if a filler is batching a bunch of directional AMM swaps (like a bunch of WETH->USDC orders) they would group all the WETH together and do one big swap. But then if they decide some of them are failures, what? they have to swap back some of the stuff otherwise we wouldn't have the input tokens to send back and the whole thing would revert anyways
    another issue is that if we take an order, and then it fails and we return the input tokens -- the nonce was still used on permit2 so the order is now invalid. User just got their order canceled for nothing
    it seems to me that if a partial fail were to happen, the filler should be able to catch it beforehand -- and since they're likely using flashbots protect anyways they're not wasting any gas on full failures"

  • "also with this model its now griefable; anyone can cancel anyone else's orders for basically free (well they have to pay gas)"

Allen's rebuttal to Mark's point about "it seems to me that if a partial fail were to happen, the filler should be able to catch it beforehand -- and since they're likely using flashbots protect anyways they're not wasting any gas on full failures":

  • consider price movements inbetween filler submitting txn and the txn being mined. flashbots can prevent them from executing this txn yes, but perhaps filler may want it to partiall execute still

Cross-Chain Dutch Limit Order Implementation

Cross-Chain gouda orders will need new code for limit orders and dutch limit orders. Should work exactly like DutchLimitReactor, but taking into account the async cross-chain logic like escrow.

Automated Testing

As discussed with @willpote , I do not think unit tests are sufficient and some form of holistic testing for a wide variety of cases will help us build confidence in the product. Such cases include:

  1. Testing the quoter works for various orders
  2. Testing txns with fees
  3. Testing RFQ exclusive filler validation works

I originally suggested manually testing on goerli or tenderly but Pote suggested "automated testing" (like we do in Routing API) instead. I myself am unclear what "automated testing" entails.

Consider not using permit2 for direct taker

Makes it a little harder to setup for contracts/contract wallets that want to fill

Alternatively, maybe somehow allow taker permit sig to be injected and processed at the same time?

Excessive data passed to `reactorCallback`

The following information is not useful to an IReactorCallback implementation, and can be passed as fillData if actually necessary:

  • ResolvedOrder.OrderInfo (all fields)
  • ResolvedOrder.sig
  • ResolvedOrder.hash
  • ResolvedOrder.InputToken.maxAmount

Reducing the data passed to the function will reduce the memory footprint of the execution scope and therefore the gas.

[edit: added to list of unnecessary fields]

Store collected fees in escrow or send directly

With balance checks there's much less benefit to bringing the fee assets into the reactor itself. we can either have a separate escrow contract that stores them and manages claims, or just have executors send them directly to the fee recipients. This would simplify direct taker logic as we wouldn't have to worry about standing balances of uncollected fees

Permit 2 replay revert?

I had a look at the permit2 contracts, considering hosting a marketplace for orders, but I don’t think I can reveal the permit2 signatures? Because if an attacker were to use up the permit without going through the reactor then the reactor will fail as the permit has been used up. Can someone clarify this?

Consider checking balances instead of transferring output tokens

Currently the flow of output tokens is as follows:

  • fillContract approves them to the reactor
  • reactor does transferFrom from fillContract to recipient

We could save some gas by skipping the approval step and having the fillContract directly transfer them to the recipient. This would require the reactor to check recipient token balances before and after reactorCallback. Since orders are an array, and each order has an array of outputs we would likely need a 2d array of prev balances, something like:

uint256[][] preBalances = []
for (order i of orders) {
   for (output j of order.outputs) {
    preBalances[i][j] = output.token.balanceOf(output.recipient)
  }
}

reactorCallback()

for (i of preBalances) {
  for (j of preBalances[i]) {
    output = orders[i].outptus[j]
    require(output.token.balanceOf(output.recipient) > preBalances[i][j] + output.amount)
  }
}

Which is pretttty ugly imo, so worth considering if there are cleaner ways to do this

Consider adding some simple fill macros for gas optimization

for ex:
if fillContract == address(1) -- directFill: permit2.permitTransferFrom(msg.sender, fillData)
if fillContract == address(2) -- matchOrders as in seaport: for (fromOrderIndex, toOrderIndex, outputIndex in abi.decode(fillData)) match(from, to, outputIndex)

...

cant decode order

albeit this may be user error, I cant decode this order i got from your endpoint using the sdk, or solidity
hash: 0x4568c1ed3ce7d3135c6f424fd9a7ad772e4d8a7eba67ca90265f6e2177dd7311

import { parseOrder, Order, OrderValidation } from "@uniswap/uniswapx-sdk";

let order =
  "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000064b51b750000000000000000000000000000000000000000000000000000000064b51b75000000000000000000000000b507d4ef5ed7a01e37cb578f497329cdb3c273a50000000000000000000000000000000000000000000000000000000000002710000000000000000000000000111111111117dc0aa78b770fa6a738034120c3020000000000000000000000000000000000000000000000056bc75e2d631000000000000000000000000000000000000000000000000000056bc75e2d631000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000e80bf394d190851e215d5f67b67f8f5a52783f1e000000000000000000000000b8bff65b2eeb60d6b37312ca0740a742d5e7f95500000000000000000000000000000000000000000000000000000189635c5eac0000000000000000000000000000000000000000000000000000000064b51b75000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000000000000000000000000000000039a65e493e91140000000000000000000000000000000000000000000000000039a65e493e9114000000000000000000000000b8bff65b2eeb60d6b37312ca0740a742d5e7f955";

let parsedOrder: Order = parseOrder(order);
       throw new errors_1.MissingConfiguration("reactor", reactor);
        ^

MissingConfiguration [Error]: Missing configuration for reactor: 0x0000000000000000000000000000000000000120
    at parseOrder (/Users/nuhhtyy/Work/test/node_modules/@uniswap/uniswapx-sdk/dist/src/order/index.js:25:15)
    at Object.<anonymous> (/Users/nuhhtyy/Work/test/index.js:5:49)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:12)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:75:12)
    at internal/main/run_main_module.js:17:47
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

is it just the case that their might be malformed orders on the endpoint? hence the validation? thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.