Let's go over an example of how to perform a token swap using the 1inch Classic Swap API.
.env file/swap endpoint to construct the swap transaction
Bash
1
npm install dotenv ethers
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
import dotenv from "dotenv";
import { Wallet, JsonRpcProvider, Contract } from "ethers";
dotenv.config();
const requiredEnvVars = ["PRIVATE_KEY", "API_KEY", "RPC_URL"];
for (const key of requiredEnvVars) {
if (!process.env[key]) {
console.error(`Missing required environment variable: ${key}`);
process.exit(1);
}
}
const AGGREGATION_ROUTER_V6 = "0x111111125421ca6dc452d289314280a0f8842a65";
const usdcBase = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
const wethBase = "0x4200000000000000000000000000000000000006";
const BASE_CHAIN_ID = 8453;
const baseUrl = `https://api.1inch.com/swap/v6.1/${BASE_CHAIN_ID}`;
const config = {
aggregationRouter: AGGREGATION_ROUTER_V6,
privateKey: process.env.PRIVATE_KEY!,
apiKey: process.env.API_KEY!,
rpcUrl: process.env.RPC_URL!,
srcTokenAddress: usdcBase,
dstTokenAddress: wethBase,
amountToSwap: "100000",
slippage: 1 // 1%
};
const erc20ABI = [
{
constant: true,
inputs: [
{ name: "owner", type: "address" },
{ name: "spender", type: "address" }
],
name: "allowance",
outputs: [{ name: "", type: "uint256" }],
payable: false,
stateMutability: "view",
type: "function"
},
{
constant: false,
inputs: [
{ name: "spender", type: "address" },
{ name: "amount", type: "uint256" }
],
name: "approve",
outputs: [{ name: "", type: "bool" }],
payable: false,
stateMutability: "nonpayable",
type: "function"
}
];
type TransactionPayload = {
to: string;
data: string;
value: bigint;
};
type TxResponse = {
tx: TransactionPayload;
};
const provider = new JsonRpcProvider(config.rpcUrl);
const wallet = new Wallet(config.privateKey, provider);
function buildQueryURL(path: string, params: Record<string, string>): string {
const url = new URL(baseUrl + path);
url.search = new URLSearchParams(params).toString();
return url.toString();
}
async function call1inchAPI<T>(endpointPath: string, queryParams: Record<string, string>): Promise<T> {
const url = buildQueryURL(endpointPath, queryParams);
const response = await fetch(url, {
method: "GET",
headers: {
Accept: "application/json",
Authorization: `Bearer ${config.apiKey}`
}
});
if (!response.ok) {
const body = await response.text();
throw new Error(`1inch API returned status ${response.status}: ${body}`);
}
return (await response.json()) as T;
}
async function signAndSendTransaction(tx: TransactionPayload): Promise<string> {
try {
const txResponse = await wallet.sendTransaction({
to: tx.to,
data: tx.data,
value: tx.value
});
return txResponse.hash;
} catch (err) {
console.error("Transaction signing or broadcasting failed");
console.error("Transaction data:", tx);
throw err;
}
}
async function checkAllowance(tokenAddress: string, spenderAddress: string): Promise<bigint> {
console.log("Checking token allowance...");
const tokenContract = new Contract(tokenAddress, erc20ABI, provider);
const allowance = await tokenContract.allowance(wallet.address, spenderAddress);
console.log("Allowance:", allowance.toString());
return allowance;
}
async function approveIfNeeded(tokenAddress: string, spenderAddress: string, requiredAmount: bigint): Promise<void> {
const allowance = await checkAllowance(tokenAddress, spenderAddress);
if (allowance >= requiredAmount) {
console.log("Allowance is sufficient for the swap.");
return;
}
console.log("Insufficient allowance. Approving exact amount needed...");
const tokenContract = new Contract(tokenAddress, erc20ABI, wallet);
const tx = await tokenContract.approve(spenderAddress, requiredAmount);
console.log("Approval transaction sent. Hash:", tx.hash);
console.log("Waiting for confirmation...");
await tx.wait();
console.log("Approval confirmed!");
}
async function performSwap(): Promise<void> {
const swapParams = {
src: config.srcTokenAddress,
dst: config.dstTokenAddress,
amount: config.amountToSwap,
from: wallet.address.toLowerCase(),
slippage: config.slippage.toString(),
disableEstimate: "false",
allowPartialFill: "false"
};
console.log("Fetching swap transaction...");
const swapTx = await call1inchAPI<TxResponse>("/swap", swapParams);
console.log("Swap transaction:", swapTx.tx);
const txHash = await signAndSendTransaction(swapTx.tx);
console.log("Swap transaction sent. Hash:", txHash);
}
async function main() {
try {
await approveIfNeeded(config.srcTokenAddress, config.aggregationRouter, BigInt(config.amountToSwap));
await performSwap();
} catch (err) {
console.error("Error:", (err as any)?.response?.data || err);
}
}
main().catch((err) => {
console.error("Unhandled error in main:", err);
process.exit(1);
});
Uses the official 1inch Go SDK to perform a token swap on the Base chain:
GetApproveAllowance function in the SDK to call the /approve/allowance REST endpoint to check if your wallet has granted the proper USDC allowance to the 1inch routerGetApproveTransaction function in the SDK to call the /approve/transaction REST endpoint to construct and send an approval transactionGetSwap function in the SDK to call the /swap REST endpoint to construct the swap transaction
Bash
12
go get github.com/1inch/1inch-sdk-go/sdk-clients/aggregation
go get github.com/ethereum/go-ethereum
GO
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
package main
import (
"context"
"fmt"
"log"
"math/big"
"os"
"time"
"github.com/1inch/1inch-sdk-go/constants"
"github.com/1inch/1inch-sdk-go/sdk-clients/aggregation"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
)
var (
privateKey = os.Getenv("PRIVATE_KEY")
nodeUrl = os.Getenv("RPC_URL")
devPortalToken = os.Getenv("DEV_PORTAL_API_KEY")
)
const (
UsdcBase = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
WethBase = "0x4200000000000000000000000000000000000006"
amountUsdc = "100000" // 0.1 USDC (6 decimals)
)
func main() {
config, err := aggregation.NewConfiguration(aggregation.ConfigurationParams{
NodeUrl: nodeUrl,
PrivateKey: privateKey,
ChainId: constants.BaseChainId,
ApiUrl: "https://api.1inch.com",
ApiKey: devPortalToken,
})
if err != nil {
log.Fatalf("Failed to create configuration: %v\n", err)
}
client, err := aggregation.NewClient(config)
if err != nil {
log.Fatalf("Failed to create client: %v\n", err)
}
ctx := context.Background()
walletAddr := client.Wallet.Address().Hex()
// Step 1: Check Allowance
allowanceData, err := client.GetApproveAllowance(ctx, aggregation.GetAllowanceParams{
TokenAddress: UsdcBase,
WalletAddress: walletAddr,
})
if err != nil {
log.Fatalf("Failed to get allowance: %v\n", err)
}
allowance := new(big.Int)
allowance.SetString(allowanceData.Allowance, 10)
amountToSwap := new(big.Int)
amountToSwap.SetString(amountUsdc, 10)
// Step 2: Approve if needed
if allowance.Cmp(amountToSwap) < 0 {
fmt.Println("Insufficient allowance. Approving...")
approveData, err := client.GetApproveTransaction(ctx, aggregation.GetApproveParams{
TokenAddress: UsdcBase,
Amount: amountUsdc,
})
if err != nil {
log.Fatalf("Failed to get approve data: %v\n", err)
}
data, err := hexutil.Decode(approveData.Data)
if err != nil {
log.Fatalf("Failed to decode approve data: %v\n", err)
}
to := common.HexToAddress(approveData.To)
tx, err := client.TxBuilder.New().SetData(data).SetTo(&to).Build(ctx)
if err != nil {
log.Fatalf("Failed to build approve transaction: %v\n", err)
}
signedTx, err := client.Wallet.Sign(tx)
if err != nil {
log.Fatalf("Failed to sign approve transaction: %v\n", err)
}
err = client.Wallet.BroadcastTransaction(ctx, signedTx)
if err != nil {
log.Fatalf("Failed to broadcast approve transaction: %v\n", err)
}
fmt.Printf("Approve transaction sent: https://basescan.org/tx/%s\n", signedTx.Hash().Hex())
// Wait for approval to be mined
for {
receipt, _ := client.Wallet.TransactionReceipt(ctx, signedTx.Hash())
if receipt != nil {
fmt.Println("Approve transaction confirmed.")
break
}
time.Sleep(2 * time.Second)
}
} else {
fmt.Println("Sufficient allowance already present.")
}
// Step 3: Perform Swap
swapData, err := client.GetSwap(ctx, aggregation.GetSwapParams{
Src: UsdcBase,
Dst: WethBase,
Amount: amountUsdc,
From: walletAddr,
Slippage: 1, // 1% slippage
})
if err != nil {
log.Fatalf("Failed to get swap data: %v\n", err)
}
tx, err := client.TxBuilder.New().
SetData(swapData.TxNormalized.Data).
SetTo(&swapData.TxNormalized.To).
SetGas(swapData.TxNormalized.Gas).
SetValue(swapData.TxNormalized.Value).
Build(ctx)
if err != nil {
log.Fatalf("Failed to build transaction: %v\n", err)
}
signedTx, err := client.Wallet.Sign(tx)
if err != nil {
log.Fatalf("Failed to sign transaction: %v\n", err)
}
err = client.Wallet.BroadcastTransaction(ctx, signedTx)
if err != nil {
log.Fatalf("Failed to broadcast transaction: %v\n", err)
}
fmt.Printf("Swap transaction sent: https://basescan.org/tx/%s\n", signedTx.Hash().Hex())
// Wait for swap transaction to be mined
for {
receipt, _ := client.Wallet.TransactionReceipt(ctx, signedTx.Hash())
if receipt != nil {
fmt.Println("Swap transaction confirmed!")
break
}
time.Sleep(2 * time.Second)
}
}