Our Swap, Intent, and Price APIwill be discontinued permanently on 31.10.24
The Intent API lets you trade gasless, MEV-protected and at optimal rates.
Overview
This guide provides an overview of how to interact with the Intent API: Query for swap rates, construct a swap order, and submit an order for execution.
Prerequisites
Before you start, ensure you have:
Familiarity with HTTP requests and responses.
An Ethereum Mainnet wallet address with enough balance for the swap.
Ensure that the tokens you intend to swap are approved for use by the Permit2 contract with at least the amount the user intends to swap.
Uniswap Labs developed the Permit2 contract and acts as an intermediary, allowing each swap operation to be approved via signatures, saving the user gas cost and increasing security. We recommend that the token is approved with the max uint256 amount, to avoid having to approve multiple times. -> Read more about what is Permit2.
Example
// Assuming you have a connected wallet and the token's contract instanceconsttokenContract=newethers.Contract(tokenAddress, tokenABI, wallet);constamountToApprove=ethers.constants.MaxUint256; // Approve maximum for convenienceconstspenderAddress='0x000000000022d473030f116ddee9f6b43ac78ba3'; // Permit2 contract addressawaittokenContract.approve(spenderAddress, amountToApprove);
2. Get a swap quote
To initiate a swap, you first need to query the current swap rate between two tokens. This involves sending a request to our solver API with the details of your intended swap, including the input token, output token, and the amount you wish to swap. Check the solver API reference guide for in-depth details.
Understanding Gas Fees in Transactions
When using intents, it's important to note that a transaction's executor (in this case, the filler) pays for the gas fees. As a result, the gas cost is deducted from the calculated output amount of your transaction. This ensures the network fees are covered, allowing your transaction to be processed smoothly on the blockchain. We will automatically convert and deduct the gas cost from the output quote amount when the venue is set to helix.
To ensure the uniqueness and sequence of your transactions, you must first fetch a nonce for your swap order. The nonce is a number that represents the current transaction count for your wallet address, ensuring that each transaction is processed exactly once. The nonce is saved in the execution contract, so it's different than the wallet's nonce.
This is the nonce value you should use in your order.
4. Construct the Order
After obtaining the nonce and the price, construct your swap order. This payload outlines the specifics of the swap, including the tokens involved, the amounts, and the timing parameters.
To maintain consistency with other Intent APIs, the order follows the same interface as Uniswap X's Signed Order. To build the order, you can either use UniswapX's SDK - if you're using JavaScript / Typescript or encode the ABI in any other way you'd like.
We are developing our own SDK to allow for quick order creation in several languages.
// ABI["tuple(address,address,uint256,uint256,address,bytes)","uint256","uint256","address","uint256","tuple(address,uint256,uint256)","tuple(address,uint256,uint256,address)[]",]// Schema{"reactor": "Address of the reactor","swapper": "Your wallet address","nonce": "Incremented nonce value","deadline": "UNIX timestamp of the deadline","additionalValidationContract": "Address for additional validation, if any","additionalValidationData": "Data for additional validation, if any","decayStartTime": "UNIX timestamp for decay start","decayEndTime": "UNIX timestamp for decay end","exclusiveFiller": "Address of the exclusive filler, if any","exclusivityOverrideBps": "Basis points for exclusivity override","input": {"token":"Contract address of the input token","startAmount":"Initial amount of the input token","endAmount":"Final amount of the input token" },"outputs": [ {"token":"Contract address of the output token","startAmount":"Initial amount of the output token","endAmount":"Final amount of the output token","recipient":"Recipient's address" } ]}// Example code:constorderData= {// Fill in with the structured order data as per the JSON structure above};constserializedOrder=serialize(orderData);functionserialize(orderData) {constabiCoder=newethers.utils.AbiCoder();returnabiCoder.encode( ["tuple(address,address,uint256,uint256,address,bytes)","uint256","uint256","address","uint256","tuple(address,uint256,uint256)","tuple(address,uint256,uint256,address)[]" ], [ [orderData.reactor,orderData.swapper,orderData.nonce,orderData.deadline,orderData.additionalValidationContract,orderData.additionalValidationData ],orderData.decayStartTime,orderData.decayEndTime,orderData.exclusiveFiller,orderData.exclusivityOverrideBps, [orderData.input.token,orderData.input.startAmount,orderData.input.endAmount ],orderData.outputs.map((output) => [output.token,output.startAmount,output.endAmount,output.recipient ]) ] );}
4.1 Fee Taking (Optional)
Fees can be automatically deducted from the transaction proceeds, ensuring that the user experiences no additional upfront costs while the provider receives compensation for their service.
Currently, we only support taking fees in the same token as the output token (outToken).
To implement fee-taking, follow these steps:
Calculate the fee amount you want to charge.
Subtract the fee amount from the original output amount.
Add a new output to the outputs array with the fee amount and the wallet address that should receive the fee.
Note: The sum of all output amounts (including the fee) should be less than or equal to the original expected output amount.
Here's how to modify the existing code to implement fee-taking:
Some code
Example 1: Using UniswapX SDK
import { DutchOrder, NonceManager } from'@uniswap/uniswapx-sdk';import { ethers } from'ethers';// ... (previous code remains the same)constfeeAmount=BigNumber.from('1000000000000000'); // Example fee of 0.001 constfeeRecipient='0x1234567890123456789012345678901234567890'; // Wallet to receive the feeconstorder= builder.deadline(deadline).decayEndTime(deadline).decayStartTime(deadline -100).nonce(nonce).input({ token:'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', amount:BigNumber.from('1000000'), }).output({ token:'0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', startAmount:BigNumber.from('999000000000000000'),// Reduced by fee amount endAmount:BigNumber.from('899000000000000000'),// Reduced by fee amount recipient:'0x0000000000000000000000000000000000000000', }).output({ token:'0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', startAmount: feeAmount, endAmount:, recipient: feeRecipient, }).build();// ... (rest of the code remains the same)// Some code
Example 2: Raw Code
// ... (previous code remains the same)
const feeAmount = ethers.utils.parseEther('0.001'); // Example fee of 0.001 ETH
const feeRecipient = '0x1234567890123456789012345678901234567890'; // Wallet to receive the fee
const orderData = {
// ... (other fields remain the same)
outputs: [
{
token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
startAmount: ethers.utils.parseEther('0.999'), // Reduced by fee amount
endAmount: ethers.utils.parseEther('0.899'), // Reduced by fee amount
recipient: "0x0000000000000000000000000000000000000000"
},
{
token: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
startAmount: feeAmount,
endAmount: feeAmount,
recipient: feeRecipient
}
]
};
const serializedOrder = serialize(orderData);
// ... (rest of the code remains the same)
5. Sign the order
All orders must be signed with your private key.
To sign the provided data for the transaction, users must prepare the data according to the Ethereum EIP-712 standard, which enables typed structured data signing. This process involves specifying a domain, types for the data being signed, and the values of the transaction.
Once the order is signed, submit it to the Propellerswap Helix API for execution. This involves sending a POST request to our order submission endpoint with the order details and signature.