Getting Started
So you want to integrate with Hashflow to offer zero slippage trading with tight spreads? Awesome! This doc will walk you through the necessary steps.
NOTE: We authenticate all incoming requests based on the 'source' field (passed in the requests below) to prevent DDoS attacks. If you're adding a new taker, please reach out on Telegram or Discord and we can set up authentication. Otherwise, your requests will get rejected.

1. Query /marketMakers

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

2. Query /pairs

Now that you have a list of available market makers for each chain, you'll want to query the trading-pairs for each marketMaker (on each chain) so that you know which trades to request from them.
You can do this by sending an HTTP REST request to
1
GET https://api-staging.hashflow.com/taker/v1/quote/pairs
2
?source=<source>
3
&networkId=<chainId>
4
&marketMaker=<marketMaker>
Copied!
The response will be of format
1
{
2
status: 'success' | 'fail',
3
pairs?: {
4
baseTokenName: string;
5
quoteTokenName: string
6
}[],
7
error?: string,
8
}
Copied!
where baseTokenName and quoteTokenName are the short names for each token (e.g. ETH, USDT).
This will tell you which trading pairs this market maker is currently offering on the given chain. Re-run these requests every 5-10 minutes to get the latest state. Market makers frequently drop and re-add token pairs based on market conditions.

3. (Optional) Query Price Levels

In order to understand indicative order flow, we expose price levels for the different MMs. You can get those by querying:
1
POST https://api-staging.hashflow.com/taker/v1/quote/priceLevels
2
{
3
"source": string, // Your identifier (e.g. "1inch")
4
"marketMaker": string, // MM identifier
5
"networkId": number, // Network
6
}
Copied!
You will then get a response of format
1
{
2
"status": "success" | "fail",
3
"networkId": number,
4
"levels": Array<{
5
// string representation of the level e.g. (2.5 for 2.5 ETH)
6
level: string,
7
8
// string representation of the price per unit at that level
9
// (e.g. 3500 for "up to 2.5 ETH at 3500 USDT per ETH")
10
// this price is not in decimals -- it's the actual exchange rate,
11
// in floating point notation
12
price: string
13
}>
14
}
Copied!
These price levels indicate general quotes the MM will issue at different volumes (levels).

4. Query /signedRfq

By this point, you should have an index of what market makers are available on each chain and which trading pairs they offer. If you used /priceLevels, you'll also know their indicative order flow. Now, whenever you want to get a quote for a specific trade, send a request for each market maker which offers the requested pair on the requested chain.
This request will look like:
1
POST https://api-staging.hashflow.com/taker/v1/quote/signedRfq
2
3
// JSON body
4
{
5
networkId: number; // 1 for mainnet
6
source: string, // Your identifier (e.g. "1inch", "slingshot")
7
8
// Base token (the token the trader sells).
9
baseToken: string, // contract address (e.g. "0x123a...789")
10
baseTokenAmount: ?string, // decimal amount (e.g. "1000000" for 1 USDT)
11
12
// Quote token (the token the trader buys).
13
quoteToken: string, // contract address (e.g. "0x123a...789")
14
quoteTokenAmount: ?string, // decimal amount (e.g. "1000000" for 1 USDT)
15
16
/* NOTE: Exactly one of base/quote tokenAmount must be present. */
17
18
// The trader wallet address that will swap with our contract. This can be a proxy
19
trader: string,
20
21
// The wallet address of the actual trader (e.g. end user wallet).
22
// If effectiveTrader is not present, we assume trader == effectiveTrader.
23
effectiveTrader: ?string,
24
25
// The market maker to request. If missing, we query all MMs and pick the best quote
26
marketMaker: ?string, // e.g. "mm1"
27
}
Copied!
You will then get a response of the following format:
1
{
2
status: 'success' | 'fail',
3
error?: string,
4
rfqId: string, // Unique RFQ identifier
5
signature?: string,
6
7
quoteData?: {
8
rfqType: number,
9
txid: string, // Unique quote identifier. Different from RFQ ID.
10
pool: string,
11
eoa: ?string, // EOA address if market maker uses an EOA
12
13
// Quote
14
baseToken: string,
15
baseTokenAmount: string,
16
quoteToken: string,
17
quoteTokenAmount: string,
18
fees: string,
19
20
// UNIX timestamp when quote expires
21
quoteExpiry: number,
22
23
trader: string,
24
effectiveTrader: ?string,
25
26
// The following parameter is used by Hashflow contracts to prevent replay.
27
nonce: number
28
},
29
30
gasEstimate?: number, // Estimated number of gas units
31
nativeTokenPriceUsd?: number, // USD price of gas currency (ETH, MATIC, etc)
32
}
Copied!

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 contract has the following addresses:
1
{
2
"contractName": "HashflowRouter",
3
"networks": {
4
"1": {
5
"address": "0x79cdFd7Bc46D577b95ed92bcdc8abAba1844Af0c",
6
"transactionHash": "0xc9abc910fdcb11c42a0eacad49bbf9b7101c943ce38a66e8cf8c1e6bf74377e2"
7
},
8
"4": {
9
"address": "0x2414DC4831DdB8146949C755Da47934b57dB372C",
10
"transactionHash": "0xda0991536411255dd475ccab2742bae1a8b11d2b423484b02c806e22a567d189"
11
},
12
"42": {
13
"address": "0x46Bf7446748814E9A74CaE1284f80B8469388EB7",
14
"transactionHash": "0xd27584c94e9407e6e83c2b24d955654de554b3c5f4bfd3f8b023abb27c54bdf8"
15
},
16
"56": {
17
"address": "0x375E05F6e12028e933ce598Ad1BEd7f1194aB071",
18
"transactionHash": "0x0e9c2dff3309ed642a596febca55bd786b21fba26804b16c13633bf511583a15"
19
},
20
"97": {
21
"address": "0x45A9DFAaF2551c669a847192A606c8B38ED260a4",
22
"transactionHash": "0xc385560fb2de045c14439d976f18ef07abd4986aa25995ffae78b80ca60b82e3"
23
},
24
"137": {
25
"address": "0xACfaAa9Da11e66a8Cc8AF8E3D844673968FFf63f",
26
"transactionHash": "0xda678c491cf351bf044f906e8073760667e2d2e6e8b9c4087674f9c705bf5d70"
27
},
28
"31337": {
29
"address": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0",
30
"transactionHash": "0xfa77418cabc0b2575ee3c18670e466f6ca410e80290d6dbd905d24b14538f03f"
31
},
32
"42161": {
33
"address": "0x36c543B8bb76b330ecB66A13c1C1377f889f1919",
34
"transactionHash": "0x048757d3a007b3ab0524ee9e36c5162bfc7bced5a2e53aa56cc66cf3607f68f4"
35
},
36
"43114": {
37
"address": "0x43B4bf8758CAE65E6b8242D2669E0E5E20Ff693A",
38
"transactionHash": "0x11f86f4432cc4adce39d1b28932edf8ccd7dfdf95597a16fe1cf1aca8be750e6"
39
},
40
"80001": {
41
"address": "0x0cC0906C87b8E2A80b3F1D3BDeEF0B2E4fEC3EBF",
42
"transactionHash": "0x8e45ae17594518938453a0a4fe5c4e996bedbe210fcd39eeb5fea97c4243e873"
43
}
44
}
45
}
Copied!
and the following ABI:
1
{
2
"inputs": [
3
{
4
"components": [
5
{
6
"internalType": "enum IQuote.RFQType",
7
"name": "rfqType",
8
"type": "uint8"
9
},
10
{
11
"internalType": "address",
12
"name": "pool",
13
"type": "address"
14
},
15
{
16
"internalType": "address",
17
"name": "eoa",
18
"type": "address"
19
},
20
{
21
"internalType": "address",
22
"name": "trader",
23
"type": "address"
24
},
25
{
26
"internalType": "address",
27
"name": "effectiveTrader",
28
"type": "address"
29
},
30
{
31
"internalType": "address",
32
"name": "baseToken",
33
"type": "address"
34
},
35
{
36
"internalType": "address",
37
"name": "quoteToken",
38
"type": "address"
39
},
40
{
41
"internalType": "uint256",
42
"name": "effectiveBaseTokenAmount",
43
"type": "uint256"
44
},
45
{
46
"internalType": "uint256",
47
"name": "maxBaseTokenAmount",
48
"type": "uint256"
49
},
50
{
51
"internalType": "uint256",
52
"name": "maxQuoteTokenAmount",
53
"type": "uint256"
54
},
55
{
56
"internalType": "uint256",
57
"name": "fees",
58
"type": "uint256"
59
},
60
{
61
"internalType": "uint256",
62
"name": "quoteExpiry",
63
"type": "uint256"
64
},
65
{
66
"internalType": "uint256",
67
"name": "nonce",
68
"type": "uint256"
69
},
70
{
71
"internalType": "bytes32",
72
"name": "txid",
73
"type": "bytes32"
74
},
75
{
76
"internalType": "bytes",
77
"name": "signedQuote",
78
"type": "bytes"
79
}
80
],
81
"internalType": "struct IQuote.Quote",
82
"name": "quote",
83
"type": "tuple"
84
}
85
],
86
"name": "tradeSingleHop",
87
"outputs": [],
88
"stateMutability": "payable",
89
"type": "function"
90
}
Copied!
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. Switch to production

After some more testing, you have confirmed that the whole taker flow works end-to-end. Now it's time to go live! Instead of using the staging version of Hashflow (which you have been calling all along), you can now connect to prod.
To do this, instead of calling https://api-staging.hashflow.com in your request, you'll want to call https://api.hashflow.com . That's all!

7. Success!

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