Clipper
Search…
Integrating with Clipper
Guidance for how developers can build Clipper transactions into their own DeFi application.

Ethereum Mainnet

Clipper implements the PLP API from 0x, making it easy for developers to integrate Clipper into their own DeFi applications. Importantly, Clipper works off of a "Send, then Swap" modality. That means in the same transaction assets must be sent into our Pool, then the appropriate swap function must be called. If the assets are sent in a transaction prior to the call to swap user funds may be stolen. Developers are encouraged to reference our ClipperRouter contract to see an example of how to transact with Clipper, and to join our Discord for assistance.
From the PLP API, the functions getSellQuote, sellTokenForToken, and sellTokenForETH should be called on our Exchange Interface contract. To minimize gas, please call sellETHForToken on the Pool contract, attaching the ETH as value to the function call. In getSellQuote, use the addresses 0x0000000000000000000000000000000000000000 or 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE to represent ETH. It is suggested that developers identify the transaction source for attribution by setting the auxiliaryBytes in the contract call to a unique string since these bytes are emitted by the Clipper exchange in the swap event. For instance, our Clipper Router contract uses the encoded version of ClipperRouter.
To learn the addresses of the tokens in the Pool, developers can call nTokens (which returns the number of tokens in the Pool) and tokenAt (which takes as input an integer from 0 to nTokens()-1, and returns the address of the ERC20 token contract) on the Pool contract. Note that ETH is always available in the Pool and is not considered to be a "token".

Polygon (RFQ Architecture)

Clipper is implemented using an RFQ architecture on other chains that have faster block times than Ethereum Mainnet, starting with Polygon. In an RFQ architecture, you first ask our offchain server to quote a transaction price, specifying the buy and sell tokens and either a target input or output amount. You then have a short amount of time to accept that quote, and receive back a signed certificate from our offchain server. Then, you must pass that transaction and signed certificate to our onchain smart contracts to execute the swap. Much like the Mainnet implementation of Clipper, this infrastructure implements the "send, then swap" modality and is designed to be as simple as possible to chain within a larger set of transactions.

Getting the Exchange Address and Asset Information

GET to https://api.clipper.exchange/rfq/pool?chain_id=.... with the chain_id set to the appropriate (integer) chain ID. Polygon is 137. The JSON response will include the pool's address and list of contract addresses on that chain. Example response:
1
{
2
"pool": {
3
"chain_id": 137,
4
"address": "0xD01e3549160c62Acabc4D0EB89F67aAFA3de8EEd",
5
"num_assets": 7,
6
"pool_tokens": 1744171922596734892507136,
7
"value_in_usd": 1955850.4306469245
8
},
9
"assets": [{
10
"name": "USDC",
11
"address": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
12
"balance": "287945821795",
13
"price_in_usd": 0.9997835373,
14
"value_in_usd": 287883.49226496054,
15
"target_value_in_usd": 288882.55208657164
16
}, {
17
"name": "ETH",
18
"address": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
19
"balance": "91082274931865214483",
20
"price_in_usd": 4559.0177414797,
21
"value_in_usd": 415245.7073487052,
22
"target_value_in_usd": 411368.75417127804
23
}, {
24
"name": "USDT",
25
"address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
26
"balance": "204708040486",
27
"price_in_usd": 1.0008107141,
28
"value_in_usd": 204874.0001808054,
29
"target_value_in_usd": 205684.37708563902
30
}, {
31
"name": "MATIC",
32
"address": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270",
33
"balance": "255804849489186177066089",
34
"price_in_usd": 2.0681423682,
35
"value_in_usd": 529040.8472196101,
36
"target_value_in_usd": 514210.9427140975
37
}, {
38
"name": "DAI",
39
"address": "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063",
40
"balance": "202296673572783491062883",
41
"price_in_usd": 1.0024245729,
42
"value_in_usd": 202787.15660528824,
43
"target_value_in_usd": 205684.37708563902
44
}, {
45
"name": "WBTC",
46
"address": "0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6",
47
"balance": "450316030",
48
"price_in_usd": 62901.8231205992,
49
"value_in_usd": 283256.9926743044,
50
"target_value_in_usd": 288882.55208657164
51
}, {
52
"name": "GYEN",
53
"address": "0x482bc619eE7662759CDc0685B4E78f464Da39C73",
54
"balance": "3735473968223",
55
"price_in_usd": 0.00877056958018,
56
"value_in_usd": 32762.234353250915,
57
"target_value_in_usd": 41136.875417127805
58
}]
59
}
Copied!
  • num_assets will be an integer value with the number of assets in the pool.
  • The assets key at the top level is a list of length num_assets describing the pool assets. The name and address of each asset are the most important fields.

Requesting a Quote

POST to the https://api.clipper.exchange/rfq/quote endpoint a JSON body with the following keys.
1
{
2
"output_asset_symbol": "ETH",
3
"input_asset_symbol": "USDT",
4
"chain_id": 137,
5
"output_amount": "1000000000000000000", // XOR: "input_amount": "1000000000000000000",
6
"time_in_seconds": 30
7
}
Copied!
  • chain_id should be set to the appropriate (integer) chain ID. Polygon is 137.
  • Exactly one of input_amount or output_amount should be specified, and that value should be a string.
  • time_in_seconds refers to the amount of time between accepting a quote and receiving the quote on the blockchain. A good suggestion for Polygon is 30 seconds. As this value increases, the quote will get worse for the user.
  • input_asset_symbol and output_asset_symbol should correspond to the asset name returned from the /rfq/pool call above. Observe that "ETH" and "MATIC" are both assets, but that the Clipper exchange uses the wrapped version of these assets, a distinction made clear and unambiguous by the inclusion of the contract addresses in the response.
The response is a JSON object:
1
{
2
"id": "60aa826e-b4ee-4660-8b28-4ffb77e936f5",
3
"must_accept_by": "2021-10-25 22:39:45.408794+00:00",
4
"good_until": 1635201615,
5
"chain_id": 137,
6
"input_asset_address": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
7
"input_amount": "3442034079",
8
"input_value_in_usd": 1.01,
9
"output_asset_address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
10
"output_amount": "1000000000000000000",
11
"output_value_in_usd": 1.00
12
}
Copied!
must_accept_by is a human-readable UTC timestamp by which you must accept the quote and should be expected to be a short duration. If this time has passed already, request a new quote - the server will not sign a quote after must_accept_by has passed.

Accepting a Quote

POST to https://api.clipper.exchange/rfq/sign endpoint with the following JSON body:
1
{
2
"quote_id": "60aa826e-b4ee-4660-8b28-4ffb77e936f5",
3
"destination_address": "0x5901920A7b8cb1Bba39220FAC138Ffb3800dD212"
4
}
Copied!
  • quote_id should be set to the ID field of the quote requested above
  • destination_address will receive the output token.
The response is a JSON object that adds signature information to the quote:
1
{
2
"chain_id": 137,
3
"input_asset_address": "0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619",
4
"output_asset_address": "0xc2132D05D31c914a87C6611C10748AEb04B58e8F",
5
"input_amount": "3442034079",
6
"output_amount": "1000000000000000000",
7
"good_until": 1635201615,
8
"destination_address": "0x5901920A7b8cb1Bba39220FAC138Ffb3800dD212",
9
"signature": {
10
"v": 28,
11
"r": "0x19e2400ad86ee2f353f7f7d7a859339e3ba9d056691872385ada9ddc1d104ea6",
12
"s": "0x5d8de6fe4e25b9c26817b0bc3c33a78ce1e24799aae3b89886f64ac6a98a3c37"
13
},
14
"clipper_exchange_address": "0xD01e3549160c62Acabc4D0EB89F67aAFA3de8EEd"
15
}
Copied!

Interacting with the Clipper Exchange

There are several different external functions for interacting with the Clipper exchange onchain, depending on your use case. In all of these functions, Signature is a struct defined by:
1
struct Signature {
2
uint8 v;
3
bytes32 r;
4
bytes32 s;
5
}
Copied!

Swapping Tokens for Tokens

1
function swap(address inputToken, address outputToken, uint256 inputAmount, uint256 outputAmount, uint256 goodUntil, address destinationAddress, Signature calldata theSignature, bytes calldata auxiliaryData) external;
2
function transmitAndSwap(address inputToken, address outputToken, uint256 inputAmount, uint256 outputAmount, uint256 goodUntil, address destinationAddress, Signature calldata theSignature, bytes calldata auxiliaryData) external;
Copied!
Observe that the signed quote has all of these values to use, except for auxiliaryData which can be set to any string and is used for identification purposes in the event logs.
The difference between swap and transmitAndSwap is that transmitAndSwap starts by transferring inputAmount of the inputToken from msg.sender to the exchange, while swap assumes that the appropriate amount of the input token has already been transmitted. Which function you should use depends on if and how you are connecting the Clipper swap to other operations.

Handling Native Currency

On each chain a native currency has special privileges and abilities and is often not ERC20 compatible. For instance, MATIC is the native currency on Polygon. Unlike the Clipper implementation on Ethereum mainnet, Clipper's RFQ architecture only uses ERC20 tokens, meaning that these native currencies must be wrapped before exchange with Clipper, and that Clipper can only return the wrapped version of these assets from the exchange.
However, for convenience, we provide two sets of functions for handling native currency in a direct fashion. For these swaps, you must get a signature for a transaction to or from the wrapped version of the native currency, as this is what will actually be exchanged.
For swaps of tokens to native currency (example: DAI to MATIC on Polygon), use:
1
function sellTokenForEth(address inputToken, uint256 inputAmount, uint256 outputAmount, uint256 goodUntil, address destinationAddress, Signature calldata theSignature, bytes calldata auxiliaryData) external;
2
function transmitAndSellTokenForEth(address inputToken, uint256 inputAmount, uint256 outputAmount, uint256 goodUntil, address destinationAddress, Signature calldata theSignature, bytes calldata auxiliaryData) external;
Copied!
As with token-to-token swaps, the difference between these functions is that the first assumes that at least inputAmount of inputToken has already been sent, while the second pulls that input token directly from msg.sender. Observe further that:
  • The outputToken argument is not present (since it will be native currency)
  • These functions refer to the native currency as Eth regardless of chain
For swaps of native currency to tokens (example: MATIC to USDC on Polygon), use:
1
function sellEthForToken(address outputToken, uint256 inputAmount, uint256 outputAmount, uint256 goodUntil, address destinationAddress, Signature calldata theSignature, bytes calldata auxiliaryData) external payable;
Copied!
This function can operate either by attaching the native currency to the call as msg.value or by transferring that native currency to the exchange contract prior to the function call.
Last modified 26d ago