Getting Started
This doc will walk you through the required steps to add Hashflow as a liquidity source for your Taker endpoint. This could be an aggregator, an algorithmic trading bot, or any software intended to run trades against Hashflow liquidity.

1. Set up authentication

Hashflow authenticates all incoming requests based on the source field (passed in the requests below). This helps us track usage, as well as prevent DDoS attacks.
The first step in setting up your taker is reaching out to the Hashflow team, in order to have a new source created, together with associated credentials. The team will provide you with the authentication key once credentials have been generated.
To authenticate your requests, you will need to submit a header with each request: <YOUR_SOURCE>_AUTH_KEY: <generated credential key>
For example, if your source is my_aggregator, the header name will be MY_AGGREGATOR_AUTH_KEY.

2. Query /marketMakers

Next, you will need to query which market makers are currently available on Hashflow for a given network. You can do this by sending the following HTTP REST request:
GET https://api.hashflow.com/taker/v1/marketMakers
?source=<source>
&networkId=<chainId>
  • For <source> use your identifier (e.g. 1inch, zerion, ...)
  • For <chainId> use your network ID (e.g. 1 for mainnet, 137 for polygon).
  • Make sure you use the header that authenticates your source
The response to this request includes all available market makers on that chain and has format
{
marketMakers: string[]
}
For example: {marketMakers: ['mm1', 'mmXYZ']}.
You'll want to re-run this request periodically for each chain so that you can tell which market makers are available.

3. Query Price Levels (for price discovery)

In order to understand indicative order flow, we expose price levels for the different market makers. These levels tell you, at any given time:
  • what pairs are available for a given network (chain)
  • how much liquidity is available for each particular pair
  • what the rough prices will be once you query signed quotes for those pairs
In general, this step will allow you to do price discovery (which is inexpensive), before requesting signed quotes (which is more expensive).
The price levels APIs benefit from caching and can be queried frequently (e.g. every second).
You can get those by querying:
GET https://api.hashflow.com/taker/v2/price-levels
?source=<source>
&networkId=<chain-id>
&marketMakers[]=<mm1>
&marketMakers[]=<mm2>
This endpoint allows you query levels for an arbitrary number of market makers.
You will then get a response of the following format:
{
status: "success" | "fail",
networkId: number,
levels: Record<
string, // They key is the market maker name
Array<{
pair: {
baseToken: string, // The address of the base token
quoteToken: string, // The address of the quote token
baseTokenName: string, // The name of the base token (e.g. ETH)
quoteTokenName: string, // The name of the quote token (e.g. USDC)
},
// string representation of the level e.g. (2.5 for 2.5 ETH)
levels: Array<{
// string representation of the level (e.g. for 2.5 ETH, this will be "2.5")
level: string,
// string representation of the price per unit at that level
// (e.g. 3500 for "up to 2.5 ETH at 3500 USDT per ETH")
// this price is not in decimals -- it's the actual exchange rate,
// in floating point notation
price: string
}>
}>
>
}
Things to note:
  • for each market maker, there will be one entry in the top level array for each supported pair
  • levels and prices are not in decimals (e.g. 1 means 1 ETH and not 1 WEI)
  • for native tokens (e.g. AVAX on avalanche, ETH on ethereum) Hashflow uses the 0 address (0x00..00)
Each level represents up to how much liquidity can be accessed at a given price. Also, the first level represents the minimum amount that the market maker is willing to take a quote for.
For example, suppose our levels for ETH-USDC are
  • { level: "0.5", "price": "3000" }
  • { level: "1.5", price: "3000"}
  • { level: "5", price: "2999"}
This tells us the following:
  • the trader needs to sell at least 0.5 ETH
  • the trader can sell up to 5 ETH
  • the first 1.5 ETH will be sold for 3000 USDC
  • the next 3.5 ETH will be sold for 2999 USDC
Note that, in general, as liquidity goes up, rates go down. This is expected, as market maker prices generally mirror Centralized Exchange order books.

4. Query /rfq

By this point, you should have an index of what market makers are available on each chain and which trading pairs they offer, as well as prices they are offering. The next step is to request a signed quote that is ready for execution.
This can target specific market makers, or be sent to all available market makers.
This request will look like:
POST https://api.hashflow.com/taker/v2/rfq
// JSON body
{
networkId: number, // 1 for mainnet
source: string, // Your identifier (e.g. "1inch", "zerion")
rfqType: number, // RFQ type (e.g. who has the last look). 0: RFQ-t(aker) and 1: RFQ-m(aker)
// Base token (the token the trader sells).
baseToken: string, // contract address (e.g. "0x123a...789")
baseTokenAmount: ?string, // decimal amount (e.g. "1000000" for 1 USDT)
// Quote token (the token the trader buys).
quoteToken: string, // contract address (e.g. "0x123a...789")
quoteTokenAmount: ?string, // decimal amount (e.g. "1000000" for 1 USDT)
/* NOTE: Exactly one of base/quote tokenAmount must be present. */
// The trader wallet address that will swap with our contract. This can be a proxy
trader: string,
// The wallet address of the actual trader (e.g. end user wallet).
// If effectiveTrader is not present, we assume trader == effectiveTrader.
effectiveTrader: ?string,
// The market maker to request. If missing, we query all MMs and pick the best quote
marketMakers: ?string[], // e.g. ["mm1"]
}
You will then get a response of the following format:
{
status: 'success' | 'fail',
error?: string,
rfqId: string, // Unique RFQ identifier
signature?: string,
quoteData?: {
rfqType: number,
txid: string, // Unique quote identifier. Different from RFQ ID.
pool: string,
eoa: ?string, // EOA address if market maker uses an EOA
// Quote
baseToken: string,
baseTokenAmount: string,
quoteToken: string,
quoteTokenAmount: string,
fees: string,
// UNIX timestamp when quote expires
quoteExpiry: number,
trader: string,
effectiveTrader: ?string,
// The following parameter is used by Hashflow contracts to prevent replay.
nonce: number
},
gasEstimate?: number, // Estimated number of gas units
nativeTokenPriceUsd?: number, // USD price of gas currency (ETH, MATIC, etc)
}

5. Execute best quote

Once you have obtained a signed quote and decided to execute it, you can call the Hashflow smart contract to swap the funds between trader and market maker.
The tradeSingleHop call is meant to be composable. However, since it is most often composed with AMMs that have slippage, the Hashflow contracts allow you to tune the token amounts in order to account for slippage (see the description of maxBaseTokenAmount below).
The contract has the following addresses:
"HashflowRouter": {
"1": {
"address": "0xE2e3441004E7D377A2D97142e75d465e0dD36aF9",
"transactionHash": "0x13d8707b6ae677776e2bbb180b9077f8ab20919b28775f5d171be39ebb409fb2"
},
"4": {
"address": "0x2414DC4831DdB8146949C755Da47934b57dB372C",
"transactionHash": "0xda0991536411255dd475ccab2742bae1a8b11d2b423484b02c806e22a567d189"
},
"10": {
"address": "0x54a06197130E02AA0244C4a413F70C52348C3610",
"transactionHash": "0x569a1c5b0a9f409d3c773e1ea0e1d0e5aa512d53b5d20f90720938df52ba29bf"
},
"42": {
"address": "0x46Bf7446748814E9A74CaE1284f80B8469388EB7",
"transactionHash": "0xd27584c94e9407e6e83c2b24d955654de554b3c5f4bfd3f8b023abb27c54bdf8"
},
"56": {
"address": "0x5E8297eFe1a5D9064F5DD3BB525d84807440a90D",
"transactionHash": "0x0dd1b4374ac91857ae13bef8cea85a31b0e7ecfec84051024e508de4034f50dc"
},
"97": {
"address": "0x45A9DFAaF2551c669a847192A606c8B38ED260a4",
"transactionHash": "0xc385560fb2de045c14439d976f18ef07abd4986aa25995ffae78b80ca60b82e3"
},
"137": {
"address": "0xeDc827442114F038D009417a88942a619b8cfe17",
"transactionHash": "0x9cc0e8f33a99d53a0ff96f5ad9a37b64d1877c9368c6a81c45555531282be0dc"
},
"42161": {
"address": "0x32c85e56A82d66Fa3C13E7dF900682D63fcBAf89",
"transactionHash": "0xc11ce0250887e01e2981e9d61ad2d70213050fa89fc55735ffb12c470deaeee6"
},
"43114": {
"address": "0x39b558bfC3C65230A5Fa5170dbF7A44158A340f6",
"transactionHash": "0xcfb7f4a3e4e95933d910c42eab8cc2d8614e3034f1f44645f27baa088e189cf9"
},
"80001": {
"address": "0x0cC0906C87b8E2A80b3F1D3BDeEF0B2E4fEC3EBF",
"transactionHash": "0x8e45ae17594518938453a0a4fe5c4e996bedbe210fcd39eeb5fea97c4243e873"
}
},
and the following ABI:
{
"inputs": [
{
"components": [
{
"internalType": "enum IQuote.RFQType",
"name": "rfqType",
"type": "uint8"
},
{
"internalType": "address",
"name": "pool",
"type": "address"
},
{
"internalType": "address",
"name": "eoa",
"type": "address"
},
{
"internalType": "address",
"name": "trader",
"type": "address"
},
{
"internalType": "address",
"name": "effectiveTrader",
"type": "address"
},
{
"internalType": "address",
"name": "baseToken",
"type": "address"
},
{
"internalType": "address",
"name": "quoteToken",
"type": "address"
},
{
"internalType": "uint256",
"name": "effectiveBaseTokenAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "maxBaseTokenAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "maxQuoteTokenAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "fees",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "quoteExpiry",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "nonce",
"type": "uint256"
},
{
"internalType": "bytes32",
"name": "txid",
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "signedQuote",
"type": "bytes"
}
],
"internalType": "struct IQuote.Quote",
"name": "quote",
"type": "tuple"
}
],
"name": "tradeSingleHop",
"outputs": [],
"stateMutability": "payable",
"type": "function"
}
Some clarification for the ABI fields:I.
  • eoa. EOA address. Set to eoa, if set in your signed quote. Otherwise, use 0x0000000000000000000000000000000000000000 address.
  • maxBaseTokenAmount / maxQuoteTokenAmount. These are what you receive in the API as baseTokenAmount / quoteTokenAmount. Sometimes you can receive a quote for higher than what you requested. It is essential that you use the requested amount in the effectiveBaseTokenAmount field.
  • effectiveBaseTokenAmount . The actual swapped amount. This has to be less than or equal to maxBaseTokenAmount. We suggest to keep them equal unless there's a discrepancy with the requested amount.
You can use Etherscan to confirm the trade went through.

6. Success!

Congrats! You've successfully integrated with Hashflow as a taker 🥳