Example: Execute ETH to USDT Swap
Submitting a Swap transaction on the Ethereum Network
Overview
Native infrastructure supports both on-chain and off-chain pricing for swaps. The signer generates a transaction data signature to execute the swap on-chain securely on Native smart contracts.
Only two steps are required to start using Native:
Call firm-quote to obtain the transaction data.
Submit the transaction data to the Native Router to execute the swap. The security of the swap is ensured by the transaction data signature.
The Native Router address and the transaction calldata can be found in the firm-quote
response.
Using calldata in the response directly
Typescript
import axios from 'axios';
import {ethers} from "ethers";
const apiKey = '' // Contact Native to get your API key;
const baseUrl = 'https://newapi.native.org/v1/';
const provider = 'https://eth.llamarpc.com';
const walletAddress = ''; // Your address
const privateKey = ''; // Your private key for the address above
// Swap input
const chain = 'ethereum'; // 1, 56
const tokenIn = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; // ETH
const tokenOut = '0xdAC17F958D2ee523a2206206994597C13D831ec7'; // USDT
const amount = 1; // in ether, not in wei
async function callFirmQuote() {
const endpoint = 'firm-quote?';
const headers: any = {
api_key: apiKey,
};
const response = await axios
.get(
`${baseUrl}${endpoint}chain=${chain}&token_in=${tokenIn}&token_out=${tokenOut}&amount=${amount}&from_address=${walletAddress}`,
{
headers,
}
)
console.log('Firm quote result', response.data)
return response.data;
}
async function callRouterToSwap(firmQuoteResult) {
const jsonRpcProvider = new ethers.JsonRpcProvider(provider);
const signer = new ethers.Wallet(privateKey, jsonRpcProvider);
const tx = signer.sendTransaction({
to: firmQuoteResult.txRequest.target,
data: firmQuoteResult.txRequest.calldata,
value: firmQuoteResult.txRequest.value
})
await tx.wait()
}
async function main() {
const firmQuoteResult = await callFirmQuote();
await callRouterToSwap(firmQuoteResult)
}
main();
Python
import os
import json
import requests
import asyncio
import urllib.parse
from decimal import Decimal
from web3 import AsyncHTTPProvider, Web3
from web3.eth import AsyncEth
from web3.net import AsyncNet
from web3.middleware import async_geth_poa_middleware
from async_web3_helper import constructAsyncSignAndSendRawMiddleware
apiKey = '' # Contact Native to get your API key;
baseUrl = 'https://newapi.native.org/v1/';
provider = 'https://eth.llamarpc.com';
walletAddress = ''; # Your address
privateKey = ''; # Your private key for the address above
# Swap input
chain = 'ethereum';
tokenIn = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; # ETH
tokenOut = '0xdAC17F958D2ee523a2206206994597C13D831ec7'; # USDT
amount = 1; # in ether, not in wei
# firm quote
def call_firm_quote():
endpoint = "firm-quote?"
params = {
"chain": chain,
"token_in": tokenIn,
"token_out": tokenOut,
"amount": amount,
"from_address": walletAddress,
}
response = requests.request(
"GET",
baseUrl + endpoint + urllib.parse.urlencode(params),
data="",
headers={
"apiKey": apiKey,
},
params=params,
timeout=3,
)
print("Get firm quote")
print(response.status_code)
print(response.json())
return response.json()
async def callRouterToSwap(firmQuoteResult):
web3 = Web3(
AsyncHTTPProvider(provider),
modules={"eth": AsyncEth, "net": AsyncNet},
middlewares=[],
)
web3.middleware_onion.inject(async_geth_poa_middleware, layer=0)
web3.middleware_onion.add(constructAsyncSignAndSendRawMiddleware(privateKey))
tx = {
"chainId": await web3.eth.chain_id,
"from": web3.to_checksum_address(walletAddress),
"nonce": await web3.eth.get_transaction_count(walletAddress),
"gasPrice": await web3.eth.gas_price,
"to": firmQuoteResult["txRequest"]["target"],
"data": firmQuoteResult["txRequest"]["calldata"],
"value": firmQuoteResult["txRequest"]["value"]
}
sendTx = web3.eth.sendTransaction(tx)
txReceipt = await web3.eth.wait_for_transaction_receipt(sendTx)
return txReceipt
def main():
firmQuoteRes = call_firm_quote()
loop = asyncio.get_event_loop()
tasks = [callRouterToSwap(firmQuoteRes)]
caroutines = asyncio.gather(*tasks)
txReceipt = loop.run_until_complete(caroutines)
loop.close()
main()
Constructing calldata using params
Typescript
import axios from "axios";
import { ethers } from "ethers";
import routerAbi from "./nativeRouter.json";
const apiKey = ""; // Contact Native to get your API key;
const baseUrl = "https://newapi.native.org/v1/";
const provider = "https://eth.llamarpc.com";
const routerAddress = "0xEAd050515E10fDB3540ccD6f8236C46790508A76"; // Refer to Contract Address section -> NativeRouter (Proxy) for the address
const walletAddress = ""; // Your address
const privateKey = ""; // Your private key for the address above
const NATIVE_ETH_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
// Swap input
const chain = "ethereum"; // 1, 56
const tokenIn = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // ETH
const tokenOut = "0xdAC17F958D2ee523a2206206994597C13D831ec7"; // USDT
const amount = 1; // in ether, not in wei
type FirmQuoteResult = {
orders: any[];
widgetFee: {
signer: string;
feeRecipient: string;
feeRate: number;
};
widgetFeeSignature: string;
calldata: string;
amountIn: string;
amountOut: string;
fallbackSwapDataArray: string[];
};
async function callFirmQuote(): Promise<FirmQuoteResult> {
const endpoint = "firm-quote?";
const headers: any = {
api_key: apiKey,
};
const response = await axios.get(
`${baseUrl}${endpoint}chain=${chain}&token_in=${tokenIn}&token_out=${tokenOut}&amount=${amount}&address=${walletAddress}`,
{
headers,
}
);
console.log("Firm quote result", response.data);
return response.data;
}
function getRouterContractAndSigner(): {
router: ethers.Contract;
signer: ethers.Signer;
} {
const jsonRpcProvider = new ethers.JsonRpcProvider(provider);
const signer = new ethers.Wallet(privateKey, jsonRpcProvider);
const routerContract = new ethers.Contract(routerAddress, routerAbi, signer);
return { router: routerContract, signer };
}
async function callRouterToSwap(firmQuoteResult: FirmQuoteResult) {
const {router: routerContract, signer} = getRouterContract();
let args = {
orders: firmQuoteResult.calldata, // this encodes the order information and signatures from PMMs
recipient: walletAddress,
amountIn: firmQuoteResult.amountIn,
amountOutMinimum: 0, // use this to specify your slippage tolerance
widgetFee: {
signer: firmQuoteResult.widgetFee.signer,
feeRecipient: firmQuoteResult.widgetFee.feeRecipient,
feeRate: firmQuoteResult.widgetFee.feeRate,
},
widgetFeeSignature: firmQuoteResult.widgetFeeSignature,
fallbackSwapDataArray: firmQuoteResult.fallbackSwapDataArray,
};
// only for output token as native ETH
if (tokenOut === NATIVE_ETH_ADDRESS) {
const routerIface = new ethers.Interface(routerAbi);
const encodedArgs = routerIface.encodeFunctionData(
firmQuote.orders.length === 1
? routerIface.getFunction("exactInputSingle")
: routerIface.getFunction("exactInput"),
[
{
orders: firmQuote.calldata,
recipient: firmQuote.recipient,
amountIn: firmQuote.amountIn,
amountOutMinimum,
widgetFee: firmQuote.widgetFee,
widgetFeeSignature: firmQuote.widgetFeeSignature,
fallbackSwapDataArray: firmQuote.fallbackSwapDataArray,
},
]
);
const data = [
encodedArgs,
routerIface.encodeFunctionData("unwrapWETH9(uint256,address)", [
amountOutMinimum,
query.to_address,
]),
];
const calldata = routerIface.encodeFunctionData("multicall(bytes[])", [
data,
]);
const tx = await signer.sendTransaction{
to: routerAddress,
data: calldata,
value: ethers.utils.parseEther(amount.toString())
}
await tx.wait();
} else {
const functionName =
firmQuoteResult.orders.length > 1 ? "exactInput" : "exactInputSingle";
const tx = await routerContract[functionName](args, {value: ethers.utils.parseEther(amount.toString())});
await tx.wait();
}
}
async function main() {
const firmQuoteResult = await callFirmQuote();
await callRouterToSwap(firmQuoteResult);
}
main();
Python
import os
import json
import requests
import asyncio
import urllib.parse
from decimal import Decimal
from web3 import AsyncHTTPProvider, Web3
from web3.eth import AsyncEth
from web3.net import AsyncNet
from web3.middleware import async_geth_poa_middleware
from async_web3_helper import constructAsyncSignAndSendRawMiddleware
apiKey = '' # Contact Native to get your API key;
baseUrl = 'https://newapi.native.org/v1/';
provider = 'https://eth.llamarpc.com';
routerAddress = '0xEAd050515E10fDB3540ccD6f8236C46790508A76'; # Refer to Contract Address section -> NativeRouter (Proxy) for the address
walletAddress = ''; # Your address
privateKey = ''; # Your private key for the address above
path_to_native_router_abi_json = ""
# Swap input
chainId = 'ethereum'; # 1, 56
tokenIn = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; # ETH
tokenOut = '0xdAC17F958D2ee523a2206206994597C13D831ec7'; # USDT
amount = 1; # in ether, not in wei
# firm quote
def call_firm_quote():
endpoint = "firm-quote?"
params = {
"chain": chain,
"token_in": tokenIn,
"token_out": tokenOut,
"amount": amount,
"address": walletAddress,
}
response = requests.request(
"GET",
baseUrl + endpoint + urllib.parse.urlencode(params),
data="",
headers={
"apiKey": apiKey,
},
params=params,
timeout=3,
)
print("Get firm quote")
print(response.status_code)
print(response.json())
return response.json()
# call router to swap
def loadAbi(name: str):
with open(path_to_native_router_abi_json) as json_file:
json_data = json.load(json_file)
return json_data
async def loadContract(web3: Web3, contractAddress: str, abiName: str):
address = web3.to_checksum_address(contractAddress)
abi = loadAbi(abiName)
return web3.eth.contract(address=address, abi=abi)
async def getRouterContract(web3: Web3):
return await loadContract(web3, routerAddress, "Router")
async def callRouterToSwap(calldata, fallbackData, orders, widgetFee, widgetFeeSignature):
web3 = Web3(
AsyncHTTPProvider(provider),
modules={"eth": AsyncEth, "net": AsyncNet},
middlewares=[],
)
web3.middleware_onion.inject(async_geth_poa_middleware, layer=0)
web3.middleware_onion.add(constructAsyncSignAndSendRawMiddleware(privateKey))
routerContract = await getRouterContract(web3)
tx = {
"chainId": await web3.eth.chain_id,
"from": web3.to_checksum_address(walletAddress),
"nonce": await web3.eth.get_transaction_count(walletAddress),
"gasPrice": await web3.eth.gas_price,
}
widgetFeeTuple = (
widgetFee["signer"],
widgetFee["feeRecipient"],
int(widgetFee["feeRate"]),
)
sendTx = None
useExactInputSingle = len(orders) == 1
args = (
calldata,
web3.to_checksum_address(walletAddress),
int(orders[0]["sellerTokenAmount"]),
0, #use this to specify your slippage tolerance
widgetFeeTuple,
widgetFeeSignature,
fallbackData
)
if useExactInputSingle:
sendTx = await routerContract.functions.exactInput(args).transact(tx)
else:
sendTx = await routerContract.functions.exactInputSingle(args).transact(tx)
txReceipt = await web3.eth.wait_for_transaction_receipt(sendTx)
return txReceipt
def main():
firmQuoteRes = call_firm_quote()
loop = asyncio.get_event_loop()
calldata = firmQuoteRes["calldata"]
orders = firmQuoteRes["orders"]
widgetFee = firmQuoteRes["widgetFee"]
widgetFeeSignature = firmQuoteRes["widgetFeeSignature"]
fallbackData = firmQuoteRes["fallbackData"]
tasks = [callRouterToSwap(calldata, fallbackData, orders, widgetFee, widgetFeeSignature)]
caroutines = asyncio.gather(*tasks)
txReceipt = loop.run_until_complete(caroutines)
loop.close()
main()
This code snippet does not handle native ETH as output. For such use cases, please refer to the TypeScript example for encoding a multicall to unwrap WETH to ETH.
Last updated