Let's go over an example of how to perform a token swap using the **1inch Classic Swap API**.

---

## Before You Start

- You must have a valid **1inch API Key**. You can get one from the [1inch Business](https://business.1inch.com/portal).
- Your wallet must have:
  - USDC (or the token you're swapping) on Base
  - ETH on Base to pay gas fees

## Typescript Program

1. Loads configuration variables (private key, wallet address, RPC URL, and 1inch Business Portal API key) from the environment or a local `.env` file
2. Connects to the Base chain using viem
3. Checks the allowance of the token being swapped and increases the approval if necessary
4. Uses the `/swap` endpoint to construct the swap transaction
5. It signs and broadcasts the transaction

### Dependencies

- node v18.17.1
- npm v10.9.2

```bash
npm install dotenv ethers
```

### Example

```typescript
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);
});
```

## Go Program

Uses the [official 1inch Go SDK](https://github.com/1inch/1inch-sdk-go/) to perform a token swap on the Base chain:

1. Loads configuration variables (private key, RPC URL, and 1inch Business Portal API key) from the environment
2. Connects to the Base chain using the SDK
3. Uses the `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 router
4. If the allowance is **insufficient**, it uses the `GetApproveTransaction` function in the SDK to call the `/approve/transaction` REST endpoint to construct and send an approval transaction
5. Then, it uses the `GetSwap` function in the SDK to call the `/swap` REST endpoint to construct the swap transaction
6. It signs and broadcasts these transactions using the SDK client

### Dependencies

- go v1.24.5

```bash
go get github.com/1inch/1inch-sdk-go/sdk-clients/aggregation
go get github.com/ethereum/go-ethereum
```

### Code

```go
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)
	}
}
```
