KyberSwap Docs
  • Introduction to KyberSwap
  • Getting Started
    • Quickstart
      • FAQ
    • Supported Exchanges And Networks
    • Foundational Topics
      • Decentralized Finance
        • Tokens
        • Stablecoins
        • DEX/DeFi Aggregator
        • Slippage
        • Price Impact
        • Zap
        • Maximal Extractable Value (MEV)
      • Decentralized Technologies
        • Wallets
        • Dapps
        • RPC
        • Oracles
        • On-Chain vs Off-Chain Data
      • Other Valuable Resources
  • KyberSwap Solutions
    • KyberSwap Interface
      • User Guides
        • Connect Your Wallet
        • Switching Networks
        • Instantly Swap At Superior Rates
        • Swap At Your Preferred Rates
        • Cross-chain Swap
        • Add Your Favourite Tokens
        • Get Crypto With Fiat
        • Bridge Your Assets Across Multiple Chains
      • Profiles
        • Profile Creation
        • Profile Customization
        • Sync Profile Across Devices
      • Notifications
        • Notification Center
    • KyberSwap Aggregator
      • Concepts
        • Dynamic Trade Routing
      • User Guides
        • Instantly Swap At Superior Rates
      • Developer Guides
        • Execute A Swap With The Aggregator API
        • Upgrading To APIv1
      • Aggregator API Specification
        • EVM Swaps
        • Permit
      • Contracts
        • Aggregator Contract Addresses
      • DEX IDs
      • Subgraphs
      • FAQ
    • KyberSwap Zap as a Service
      • KyberSwap Zap as a Service (ZaaS) API
        • ZaaS HTTP API
        • ZaaS GRPC API
      • KyberSwap Zap Liquidity Widget
      • Zap Fee Model
      • Zap's Supported Chains/Dexes
      • Zap's Deployed Contract Addresses
      • Zap's DEX IDs
    • KyberSwap Widget
      • Developer Guides
        • Integrating The KyberSwap Widget
        • Customizing The KyberSwap Widget
      • iFrame Alternative
      • Widget/iFrame Fee
    • KyberSwap Liquidity Widget
      • Integrating The KyberSwap Liquidity Widget
    • Limit Order
      • Concepts
        • Off-Chain Relay, On-Chain Settlement
        • Gasless Cancellation
      • User Guides
        • Swap At Your Preferred Rates
        • Update Limit Orders
        • Cancel Limit Orders
      • Developer Guides
        • Create Limit Order
        • Gasless Cancel
        • Hard Cancel
        • Fill Limit Order
      • Contracts
        • Limit Order Contract Addresses
      • Limit Order API Specification
        • General APIs
        • Maker APIs
        • Taker APIs
      • FAQ
    • KyberSwap OnChain Price Service
    • Fee Schedule
  • Governance
    • KyberDAO
      • User Guides
        • Participating in KyberDAO
        • Staking
        • Voting
        • Stake KNC And Enjoy Gas Savings
      • Fees to KyberDAO
      • KyberDAO Operator MultiSig
      • Contracts
        • KyberDAO Contract Repo
        • KyberDAO Contract Addresses
      • FAQ - Others
    • KNC Token
      • KNC Tokenomics & Utility
      • Gas Refund Program
      • KNC Contract Addresses
  • Security
    • Audits
  • Reference
    • Legacy
      • KyberSwap Classic
        • Concepts
          • Programmable Pricing Curves
          • Dynamic Auto-Adjusting Fees
          • Virtual Balances
          • Protocol Fees
        • Contracts
          • Classic Contract Repo
          • Classic Contract Addresses
          • Classic Contract Farming Addresses
      • KyberSwap Elastic
        • Concepts
          • Concentrated Liquidity
          • Reinvestment Curve
          • Tick-Range Mechanism
          • Pool Process Flows
          • Anti-Sniping Mechanism
          • Tick-Based Farming
          • Elastic Zap
          • TWAP Oracle
          • Elastic APR Calculations
        • Contracts
          • Elastic Contract Repo
          • Elastic Contract Addresses
          • Elastic Farming Contract Addresses
          • Elastic Zap Contract Addresses
          • Elastic Core Contracts
          • Elastic Core Libraries
          • Elastic Periphery Core Contracts
          • Elastic Peripheral Library Contracts
          • Elastic Peripheral Base Contracts
        • Subgraphs
      • Whitepapers
      • Audits
      • KyberAI
        • KyberScore
        • Concepts
        • On-Chain Indicators
          • Number Of Trades
          • Trading Volume
          • Netflow To Whale Wallets
          • Netflow To CEX
          • Number Of Transfers
          • Volume Of Transfers
          • Number Of Holders
          • Top Holders
        • Technical Indicators
          • Live Charts
          • Support & Resistance Levels
          • Live Trades
          • Funding Rate On CEX
          • Liquidations On CEX
        • Liquidity Analysis
      • Elastic Legacy
        • Elastic Legacy Contract Repo
        • Elastic Legacy Contract Addresses
        • Elastic Legacy Farming Contract Addresses
        • Remove Elastic Legacy Liquidity
      • Protocol
        • Overview
        • Smart Contract Architecture
        • Trust and Security Model
      • Integrations
        • Getting Started
        • Use Cases
        • Integration Types
        • Smart Contracts
        • Ethers JS
        • RESTful API
        • Slippage Rate Protection
        • Price Feed Security
        • Contract Events
        • Platform Fees
      • Reserves
        • Getting Started
          • Overview
          • Why Develop On Kyber
          • Create New Reserve
          • Existing Reserves
          • Customising Existing Reserves
        • Development Guides
          • Fed Price Reserve
          • Automated Price Reserve
          • Reserves with Ganache
          • Orderbook Reserve
        • Operations
          • Listing Policies
          • Reserve IDs
          • Reserve Rebates
          • Sanity Rates
      • Addresses
        • Introduction
        • Mainnet
        • Kovan
        • Rinkeby
        • Ropsten
      • API/ABI
        • Introduction
        • RESTful API
          • RESTful API Overview
          • RESTful API
        • Core Smart Contracts
          • IKyberNetworkProxy
          • KyberNetworkProxy
          • IKyberNetwork
          • ISimpleKyberProxy
          • IKyberMatchingEngine
          • KyberMatchingEngine
          • IKyberHint
          • KyberHintHandler
          • IKyberHintHandler
          • IKyberFeeHandler
          • IKyberStaking
          • KyberStaking
          • IKyberDao
          • KyberDao
          • IKyberStorage
          • KyberStorage
          • IKyberHistory
          • KyberHistory
          • IKyberReserve
          • KyberReserve
          • ConversionRates
          • LiquidityConversionRates
          • EpochUtils
          • IEpochUtils
          • KyberFeeHandler
        • Contract ABIs
          • ABIs
        • Code Snippets
          • Token Quantity Conversion
        • Misc Contracts
          • KyberNetwork
          • ConversionRatesInterface
          • PermissionGroups
          • SanityRates
          • Withdrawable
          • OrderbookReserveInterface
          • OrderbookReserveLister
    • KyberSwap Operator MultiSig
    • Permitable Tokens
    • Third-Party Integrations
    • KyberSwap Analytics
    • KyberSwap App
    • GitHub
    • KyberSwap Analytics
    • KyberSwap Blog
    • Kyber Network Press Kit
  • Socials
    • X
    • Discord
    • Telegram
    • LinkedIn
    • Reddit
    • Instagram
    • Tik Tok
  • Support
    • KyberSwap Help Center
    • Complaints Handling Process
Powered by GitBook
On this page
  • Introduction​
  • Risk Mitigation​
  • Overview​
  • Things to note​
  • Trading Tokens​
  • Reserve Routing​

Was this helpful?

  1. Reference
  2. Legacy
  3. Integrations

Ethers JS

PreviousSmart ContractsNextRESTful API

Last updated 2 years ago

Was this helpful?

You are referring to the Legacy version of KyberSwap docs.

For the most updated information, please refer to:

Introduction

This guide will walk you through on how you can interact with our protocol implementation using the library. You may also use the library, with some syntax changes. The most common group of users that can benefit from this guide are wallets or vendors who want to use their own UI.

Risk Mitigation

There are some risks when utilising Kyber. To safeguard users, we kindly ask that you refer to the and sections on what these risks are, and how to mitigate them.

Overview

We break this guide into 2 sections:

  1. - The section covers what contract interfaces to import, and functions to call to fetch rates and perform a simple trade.

  2. - This section covers the reserve routing feature to include / exclude reserves, or to split trades amongst multiple reserves.

Things to note

  1. If the source token is not ETH (ie. an ERC20 token), the user is required to first call the ERC20 approve function to give an allowance to the kyber proxy contract.

  2. To prevent front running, the contract limits the gas price trade transactions can have. The transaction will be reverted if the limit is exceeded. To query for the maximum gas limit, check the public variable maxGasPrice.

// DISCLAIMER: Code snippets in this guide are just examples and you
// should always do your own testing. If you have questions, visit our
// https://t.me/KyberDeveloper.

let maxGasPrice = await KyberNetworkProxyContract.maxGasPrice();

Suppose we want to convert 100 KNC to DAI tokens on Ropsten, which is a token to token conversion. In addition, we want to charge a platform fee of 0.25%. Note that ETH is used as the base pair i.e. KNC -> ETH -> DAI.

The code example will also work for token -> ether and ether -> token conversions.

  • We use ethers for connecting to the Ethereum blockchain

  • ethers includes a BN utils library for BigNumber variables, which we shall also instantiate for convenience

  • The node-fetch module is used for making API queries

// Importing the relevant packages
const ethers = require('ethers');
const BN = ethers.BigNumber;
const fetch = require('node-fetch');

ethers provides a very simple method getDefaultProvider to easily connect to the Ethereum blockchain. While not necessary, it is recommended to provide an API key for the various providers offered (Eg. Alchemy, Infura and Etherscan).

// Connecting to a provider
const NETWORK = 'ropsten';
const PROJECT_ID = 'INFURA_PROJECT_ID'; // Replace this with your own Project ID
const provider = new ethers.getDefaultProvider(NETWORK, { infura: PROJECT_ID });

Next, we will define the constants that we will be using for this scenario.

  • ETH_ADDRESS used by Kyber to represent Ether

  • ZERO_BN: BigNumber instance of 0

  • MAX_UINT256: BigNumber instance of 2**256 - 1

  • EMPTY_HINT: The hint parameter is used for [reserve routing]. In this case, an empty hint would be 0x.

const ETH_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
const ZERO_BN = ethers.constants.Zero;
const MAX_UINT256 = ethers.constants.MaxUint256;
const EMPTY_HINT = '0x';

We define the source and destination tokens, as well as the source quantity to be used for the trade.

// Tokens and srcQty
const SRC_TOKEN_ADDRESS = '0x7b2810576aa1cce68f2b118cef1f36467c648f92'; // Ropsten KNC address
const DEST_TOKEN_ADDRESS = '0xad6d458402f60fd3bd25163575031acdce07538d'; // Ropsten DAI address
const SRC_DECIMALS = new BN.from(18);
const SRC_QTY = BN.from(100).mul(BN.from(10).pow(SRC_DECIMALS)); // 100 KNC

The following ABIs are imported for these functionalities:

// Contract ABIs and proxy address
const IERC20_ABI = [
  {
    anonymous: false,
    inputs: [
      {
        indexed: true,
        internalType: 'address',
        name: '_owner',
        type: 'address',
      },
      {
        indexed: true,
        internalType: 'address',
        name: '_spender',
        type: 'address',
      },
      {
        indexed: false,
        internalType: 'uint256',
        name: '_value',
        type: 'uint256',
      },
    ],
    name: 'Approval',
    type: 'event',
  },
  {
    inputs: [
      { internalType: 'address', name: '_owner', type: 'address' },
      { internalType: 'address', name: '_spender', type: 'address' },
    ],
    name: 'allowance',
    outputs: [{ internalType: 'uint256', name: 'remaining', type: 'uint256' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'address', name: '_spender', type: 'address' },
      { internalType: 'uint256', name: '_value', type: 'uint256' },
    ],
    name: 'approve',
    outputs: [{ internalType: 'bool', name: 'success', type: 'bool' }],
    stateMutability: 'nonpayable',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'address', name: '_owner', type: 'address' }],
    name: 'balanceOf',
    outputs: [{ internalType: 'uint256', name: 'balance', type: 'uint256' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [],
    name: 'decimals',
    outputs: [{ internalType: 'uint8', name: 'digits', type: 'uint8' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [],
    name: 'totalSupply',
    outputs: [{ internalType: 'uint256', name: 'supply', type: 'uint256' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'address', name: '_to', type: 'address' },
      { internalType: 'uint256', name: '_value', type: 'uint256' },
    ],
    name: 'transfer',
    outputs: [{ internalType: 'bool', name: 'success', type: 'bool' }],
    stateMutability: 'nonpayable',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'address', name: '_from', type: 'address' },
      { internalType: 'address', name: '_to', type: 'address' },
      { internalType: 'uint256', name: '_value', type: 'uint256' },
    ],
    name: 'transferFrom',
    outputs: [{ internalType: 'bool', name: 'success', type: 'bool' }],
    stateMutability: 'nonpayable',
    type: 'function',
  },
];
const IKyberNetworkProxy_ABI = [
  {
    anonymous: false,
    inputs: [
      {
        indexed: true,
        internalType: 'address',
        name: 'trader',
        type: 'address',
      },
      {
        indexed: false,
        internalType: 'contract IERC20',
        name: 'src',
        type: 'address',
      },
      {
        indexed: false,
        internalType: 'contract IERC20',
        name: 'dest',
        type: 'address',
      },
      {
        indexed: false,
        internalType: 'address',
        name: 'destAddress',
        type: 'address',
      },
      {
        indexed: false,
        internalType: 'uint256',
        name: 'actualSrcAmount',
        type: 'uint256',
      },
      {
        indexed: false,
        internalType: 'uint256',
        name: 'actualDestAmount',
        type: 'uint256',
      },
      {
        indexed: false,
        internalType: 'address',
        name: 'platformWallet',
        type: 'address',
      },
      {
        indexed: false,
        internalType: 'uint256',
        name: 'platformFeeBps',
        type: 'uint256',
      },
    ],
    name: 'ExecuteTrade',
    type: 'event',
  },
  {
    inputs: [],
    name: 'enabled',
    outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract ERC20', name: 'src', type: 'address' },
      { internalType: 'contract ERC20', name: 'dest', type: 'address' },
      { internalType: 'uint256', name: 'srcQty', type: 'uint256' },
    ],
    name: 'getExpectedRate',
    outputs: [
      { internalType: 'uint256', name: 'expectedRate', type: 'uint256' },
      { internalType: 'uint256', name: 'worstRate', type: 'uint256' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'src', type: 'address' },
      { internalType: 'contract IERC20', name: 'dest', type: 'address' },
      { internalType: 'uint256', name: 'srcQty', type: 'uint256' },
      { internalType: 'uint256', name: 'platformFeeBps', type: 'uint256' },
      { internalType: 'bytes', name: 'hint', type: 'bytes' },
    ],
    name: 'getExpectedRateAfterFee',
    outputs: [
      { internalType: 'uint256', name: 'expectedRate', type: 'uint256' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [],
    name: 'maxGasPrice',
    outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'src', type: 'address' },
      { internalType: 'uint256', name: 'srcAmount', type: 'uint256' },
      { internalType: 'contract IERC20', name: 'dest', type: 'address' },
      { internalType: 'address payable', name: 'destAddress', type: 'address' },
      { internalType: 'uint256', name: 'maxDestAmount', type: 'uint256' },
      { internalType: 'uint256', name: 'minConversionRate', type: 'uint256' },
      {
        internalType: 'address payable',
        name: 'platformWallet',
        type: 'address',
      },
    ],
    name: 'trade',
    outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
    stateMutability: 'payable',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract ERC20', name: 'src', type: 'address' },
      { internalType: 'uint256', name: 'srcAmount', type: 'uint256' },
      { internalType: 'contract ERC20', name: 'dest', type: 'address' },
      { internalType: 'address payable', name: 'destAddress', type: 'address' },
      { internalType: 'uint256', name: 'maxDestAmount', type: 'uint256' },
      { internalType: 'uint256', name: 'minConversionRate', type: 'uint256' },
      { internalType: 'address payable', name: 'walletId', type: 'address' },
      { internalType: 'bytes', name: 'hint', type: 'bytes' },
    ],
    name: 'tradeWithHint',
    outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
    stateMutability: 'payable',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'src', type: 'address' },
      { internalType: 'uint256', name: 'srcAmount', type: 'uint256' },
      { internalType: 'contract IERC20', name: 'dest', type: 'address' },
      { internalType: 'address payable', name: 'destAddress', type: 'address' },
      { internalType: 'uint256', name: 'maxDestAmount', type: 'uint256' },
      { internalType: 'uint256', name: 'minConversionRate', type: 'uint256' },
      {
        internalType: 'address payable',
        name: 'platformWallet',
        type: 'address',
      },
      { internalType: 'uint256', name: 'platformFeeBps', type: 'uint256' },
      { internalType: 'bytes', name: 'hint', type: 'bytes' },
    ],
    name: 'tradeWithHintAndFee',
    outputs: [{ internalType: 'uint256', name: 'destAmount', type: 'uint256' }],
    stateMutability: 'payable',
    type: 'function',
  },
];

// Kyber Network Proxy Contract Address
const IKyberNetworkProxy_ADDRESS = '0xa16Fc6e9b5D359797999adA576F7f4a4d57E8F75';

Replace the PRIVATE_KEY with a private key (including the 0x prefix) of the sender of the transaction. There are other import methods that ethers.js support, such as reading from Metamask, and mnemonic phrases.

// User Details
const PRIVATE_KEY = 'PRIVATE_KEY'; // Eg. 0x40ddbce3c7df9ab8d507d6b4af3861d224711b35299470ab7a217f780fe696cd
const USER_WALLET = new ethers.Wallet(PRIVATE_KEY, provider);
// Platform fees
const PLATFORM_WALLET = 'PLATFORM_WALLET'; // Eg. 0x483C5100C3E544Aef546f72dF4022c8934a6945E
const PLATFORM_FEE = 25; // 0.25%

Note that we fix USER_WALLET as the sender of any transactions made here to these contracts.

// Instantiate contracts, using USER_WALLET as sender of txns
const KyberNetworkProxyContract = new ethers.Contract(
  IKyberNetworkProxy_ADDRESS,
  IKyberNetworkProxy_ABI,
  USER_WALLET,
);
const srcTokenContract = new ethers.Contract(
  SRC_TOKEN_ADDRESS,
  IERC20_ABI,
  USER_WALLET,
);

Now that we have defined the trade details, we break down the process into a number of steps.

We first check if there is sufficient allowance given to the proxy contract for the trade. Should it be insufficient, we will call the approve function of the source token contract.

async function checkAndApproveTokenForTrade(
  srcTokenContract,
  userAddress,
  srcQty,
) {
  if (srcTokenContract.address == ETH_ADDRESS) {
    return;
  }

  // check existing allowance given to proxy contract
  let existingAllowance = await srcTokenContract.allowance(
    userAddress,
    IKyberNetworkProxy_ADDRESS,
  );

  // if zero allowance, just set to MAX_UINT256
  if (existingAllowance.eq(ZERO_BN)) {
    console.log('Approving KNP contract to max allowance');
    await srcTokenContract.approve(IKyberNetworkProxy_ADDRESS, MAX_UINT256);
  } else if (existingAllowance.lt(srcQty)) {
    // if existing allowance is insufficient, reset to zero, then set to MAX_UINT256
    console.log('Approving KNP contract to zero, then max allowance');
    await srcTokenContract.approve(IKyberNetworkProxy_ADDRESS, ZERO_BN);
    await srcTokenContract.approve(IKyberNetworkProxy_ADDRESS, MAX_UINT256);
  }
  return;
}
let hint = EMPTY_HINT;

Next, we fetch the expected rate for the trade, which we can set as the minimum conversion rate. Should the actual rate fall below this, the trade will revert. You may choose to add a buffer (reduce the fetched expected rate by some percentage).

let minConversionRate = await KyberNetworkProxyContract.getExpectedRateAfterFee(
  SRC_TOKEN_ADDRESS,
  DEST_TOKEN_ADDRESS,
  SRC_QTY,
  PLATFORM_FEE,
  hint,
);

Divide the rate by 10**18 to get a 'readable' rate. For example, if the rate returned from a query of 1 WBTC -> KNC is 7980824281140923034320, then 1 WBTC can be exchanged for 7980824281140923034320 / 1e18 ~= 7980.824 KNC.

We next define the gas limit and price to be used for the trade. There are a number of ways to go about this. We give 2 possible methods to determine each parameter, but this is definitely customisable to suit your needs.

async function getGasConfig(
  KyberNetworkProxyContract,
  provider,
  srcTokenAddress,
  destTokenAddress,
  srcQty,
  destAddress,
  maxDestAmount,
  minConversionRate,
  platformWallet,
  platformFee,
  hint,
) {
  let gasConfig = { gasLimit: ZERO_BN, gasPrice: ZERO_BN };

  // Configure gas limit
  // Method 1: Use estimateGas function, add buffer
  let gasLimit =
    await KyberNetworkProxyContract.estimateGas.tradeWithHintAndFee(
      srcTokenAddress,
      srcQty,
      destTokenAddress,
      destAddress,
      maxDestAmount,
      minConversionRate,
      platformWallet,
      platformFee,
      hint,
    );

  gasConfig.gasLimit = gasLimit.mul(BN.from(110)).div(BN.from(100));

  // Method 2: Use /gasLimit API (only Ropsten and mainnet)
  // let gasLimitRequest = await fetch(
  //   `https://${NETWORK == "mainnet" ? "" : NETWORK + "-"}api.kyber.network/gas_limit?` +
  //   `source=${srcTokenAddress}&dest=${destTokenAddress}&amount=${srcQty}`
  //   );

  // let gasLimit = await gasLimitRequest.json();
  // if (gasLimit.error) {
  //   console.log(gasLimit);
  //   process.exit(0);
  // } else {
  //   gasConfig.gasLimit = BN.from(gasLimit.data);
  // }

  // Configure gas price
  let maxGasPrice = await KyberNetworkProxyContract.maxGasPrice();
  // Method 1: Fetch gasPrice
  let gasPrice = await provider.getGasPrice();

  //Method 2: Manual gasPrice input
  // let gasPrice = BN.from(30).mul((BN.from(10).mul(BN.from(9))));

  // Check against maxGasPrice
  gasConfig.gasPrice = gasPrice.gt(maxGasPrice) ? maxGasPrice : gasPrice;
  return gasConfig;
}

We can finally make a call to execute the trade.

let ethValue = SRC_TOKEN_ADDRESS == ETH_ADDRESS ? SRC_QTY : ZERO_BN;

await KyberNetworkProxyContract.tradeWithHintAndFee(
  SRC_TOKEN_ADDRESS,
  SRC_QTY,
  DEST_TOKEN_ADDRESS,
  USER_WALLET.address, // destAddress
  MAX_UINT256, // maxDestAmount: set to be arbitrarily large
  minConversionRate,
  PLATFORM_WALLET,
  PLATFORM_FEE,
  hint,
  {
    value: ethValue,
    gasLimit: gasConfig.gasLimit,
    gasPrice: gasConfig.gasPrice,
  },
);

The main function will combine the different functions together to obtain the conversion rate and execute the trade.

async function main() {
  // Step 1: Check and approve allowance if needed
  await checkAndApproveTokenForTrade(
    srcTokenContract,
    USER_WALLET.address,
    SRC_QTY,
  );

  let hint = EMPTY_HINT; // build hint here (see section on reserve routing)
  // Step 2: Get rate for trade
  let minConversionRate =
    await KyberNetworkProxyContract.getExpectedRateAfterFee(
      SRC_TOKEN_ADDRESS,
      DEST_TOKEN_ADDRESS,
      SRC_QTY,
      PLATFORM_FEE,
      hint,
    );

  // Step 3: Get gas limit estimates and price
  let gasConfig = await getGasConfig(
    KyberNetworkProxyContract,
    provider,
    SRC_TOKEN_ADDRESS,
    DEST_TOKEN_ADDRESS,
    SRC_QTY,
    USER_WALLET.address,
    MAX_UINT256,
    minConversionRate,
    PLATFORM_WALLET,
    PLATFORM_FEE,
    hint,
  );

  // Step 4: Execute trade
  let ethValue = SRC_TOKEN_ADDRESS == ETH_ADDRESS ? SRC_QTY : ZERO_BN;

  console.log('Executing Trade...');
  await KyberNetworkProxyContract.tradeWithHintAndFee(
    SRC_TOKEN_ADDRESS,
    SRC_QTY,
    DEST_TOKEN_ADDRESS,
    USER_WALLET.address, // destAddress
    MAX_UINT256, // maxDestAmount: set to be arbitrarily large
    minConversionRate,
    PLATFORM_WALLET,
    PLATFORM_FEE,
    hint,
    {
      value: ethValue,
      gasLimit: gasConfig.gasLimit,
      gasPrice: gasConfig.gasPrice,
    },
  );

  // Quit the program
  process.exit(0);
}

Before running this code example, the following fields need to be modified:

  1. Change INFURA_PROJECT_ID to your Infura Project ID.

  2. Change PRIVATE_KEY to the private key (with 0x prefix) of the Ethereum wallet holding Ether.

  3. Change PLATFORM_WALLET to a wallet address for platform fees.

// DISCLAIMER: Code snippets in this guide are just examples and you
// should always do your own testing. If you have questions, visit our
// https://t.me/KyberDeveloper.

const ethers = require('ethers');
const BN = ethers.BigNumber;
const fetch = require('node-fetch');

const NETWORK = 'ropsten';
const PROJECT_ID = 'INFURA_PROJECT_ID'; // Replace this with your own Project ID
const provider = new ethers.getDefaultProvider(NETWORK, { infura: PROJECT_ID });

// Universal Constants
const ETH_ADDRESS = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
const ZERO_BN = ethers.constants.Zero;
const MAX_UINT256 = ethers.constants.MaxUint256;
const EMPTY_HINT = '0x';

// Tokens and srcQty
const SRC_TOKEN_ADDRESS = '0x7b2810576aa1cce68f2b118cef1f36467c648f92'; // Ropsten KNC address
const DEST_TOKEN_ADDRESS = '0xad6d458402f60fd3bd25163575031acdce07538d'; // Ropsten DAI address
const SRC_DECIMALS = new BN.from(18);
const SRC_QTY = BN.from(100).mul(BN.from(10).pow(SRC_DECIMALS)); // 100 KNC

// Contract ABIs and proxy address
const IERC20_ABI = [
  {
    anonymous: false,
    inputs: [
      {
        indexed: true,
        internalType: 'address',
        name: '_owner',
        type: 'address',
      },
      {
        indexed: true,
        internalType: 'address',
        name: '_spender',
        type: 'address',
      },
      {
        indexed: false,
        internalType: 'uint256',
        name: '_value',
        type: 'uint256',
      },
    ],
    name: 'Approval',
    type: 'event',
  },
  {
    inputs: [
      { internalType: 'address', name: '_owner', type: 'address' },
      { internalType: 'address', name: '_spender', type: 'address' },
    ],
    name: 'allowance',
    outputs: [{ internalType: 'uint256', name: 'remaining', type: 'uint256' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'address', name: '_spender', type: 'address' },
      { internalType: 'uint256', name: '_value', type: 'uint256' },
    ],
    name: 'approve',
    outputs: [{ internalType: 'bool', name: 'success', type: 'bool' }],
    stateMutability: 'nonpayable',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'address', name: '_owner', type: 'address' }],
    name: 'balanceOf',
    outputs: [{ internalType: 'uint256', name: 'balance', type: 'uint256' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [],
    name: 'decimals',
    outputs: [{ internalType: 'uint8', name: 'digits', type: 'uint8' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [],
    name: 'totalSupply',
    outputs: [{ internalType: 'uint256', name: 'supply', type: 'uint256' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'address', name: '_to', type: 'address' },
      { internalType: 'uint256', name: '_value', type: 'uint256' },
    ],
    name: 'transfer',
    outputs: [{ internalType: 'bool', name: 'success', type: 'bool' }],
    stateMutability: 'nonpayable',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'address', name: '_from', type: 'address' },
      { internalType: 'address', name: '_to', type: 'address' },
      { internalType: 'uint256', name: '_value', type: 'uint256' },
    ],
    name: 'transferFrom',
    outputs: [{ internalType: 'bool', name: 'success', type: 'bool' }],
    stateMutability: 'nonpayable',
    type: 'function',
  },
];
const IKyberNetworkProxy_ABI = [
  {
    anonymous: false,
    inputs: [
      {
        indexed: true,
        internalType: 'address',
        name: 'trader',
        type: 'address',
      },
      {
        indexed: false,
        internalType: 'contract IERC20',
        name: 'src',
        type: 'address',
      },
      {
        indexed: false,
        internalType: 'contract IERC20',
        name: 'dest',
        type: 'address',
      },
      {
        indexed: false,
        internalType: 'address',
        name: 'destAddress',
        type: 'address',
      },
      {
        indexed: false,
        internalType: 'uint256',
        name: 'actualSrcAmount',
        type: 'uint256',
      },
      {
        indexed: false,
        internalType: 'uint256',
        name: 'actualDestAmount',
        type: 'uint256',
      },
      {
        indexed: false,
        internalType: 'address',
        name: 'platformWallet',
        type: 'address',
      },
      {
        indexed: false,
        internalType: 'uint256',
        name: 'platformFeeBps',
        type: 'uint256',
      },
    ],
    name: 'ExecuteTrade',
    type: 'event',
  },
  {
    inputs: [],
    name: 'enabled',
    outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract ERC20', name: 'src', type: 'address' },
      { internalType: 'contract ERC20', name: 'dest', type: 'address' },
      { internalType: 'uint256', name: 'srcQty', type: 'uint256' },
    ],
    name: 'getExpectedRate',
    outputs: [
      { internalType: 'uint256', name: 'expectedRate', type: 'uint256' },
      { internalType: 'uint256', name: 'worstRate', type: 'uint256' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'src', type: 'address' },
      { internalType: 'contract IERC20', name: 'dest', type: 'address' },
      { internalType: 'uint256', name: 'srcQty', type: 'uint256' },
      { internalType: 'uint256', name: 'platformFeeBps', type: 'uint256' },
      { internalType: 'bytes', name: 'hint', type: 'bytes' },
    ],
    name: 'getExpectedRateAfterFee',
    outputs: [
      { internalType: 'uint256', name: 'expectedRate', type: 'uint256' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [],
    name: 'maxGasPrice',
    outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'src', type: 'address' },
      { internalType: 'uint256', name: 'srcAmount', type: 'uint256' },
      { internalType: 'contract IERC20', name: 'dest', type: 'address' },
      { internalType: 'address payable', name: 'destAddress', type: 'address' },
      { internalType: 'uint256', name: 'maxDestAmount', type: 'uint256' },
      { internalType: 'uint256', name: 'minConversionRate', type: 'uint256' },
      {
        internalType: 'address payable',
        name: 'platformWallet',
        type: 'address',
      },
    ],
    name: 'trade',
    outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
    stateMutability: 'payable',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract ERC20', name: 'src', type: 'address' },
      { internalType: 'uint256', name: 'srcAmount', type: 'uint256' },
      { internalType: 'contract ERC20', name: 'dest', type: 'address' },
      { internalType: 'address payable', name: 'destAddress', type: 'address' },
      { internalType: 'uint256', name: 'maxDestAmount', type: 'uint256' },
      { internalType: 'uint256', name: 'minConversionRate', type: 'uint256' },
      { internalType: 'address payable', name: 'walletId', type: 'address' },
      { internalType: 'bytes', name: 'hint', type: 'bytes' },
    ],
    name: 'tradeWithHint',
    outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
    stateMutability: 'payable',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'src', type: 'address' },
      { internalType: 'uint256', name: 'srcAmount', type: 'uint256' },
      { internalType: 'contract IERC20', name: 'dest', type: 'address' },
      { internalType: 'address payable', name: 'destAddress', type: 'address' },
      { internalType: 'uint256', name: 'maxDestAmount', type: 'uint256' },
      { internalType: 'uint256', name: 'minConversionRate', type: 'uint256' },
      {
        internalType: 'address payable',
        name: 'platformWallet',
        type: 'address',
      },
      { internalType: 'uint256', name: 'platformFeeBps', type: 'uint256' },
      { internalType: 'bytes', name: 'hint', type: 'bytes' },
    ],
    name: 'tradeWithHintAndFee',
    outputs: [{ internalType: 'uint256', name: 'destAmount', type: 'uint256' }],
    stateMutability: 'payable',
    type: 'function',
  },
];

// Kyber Network Proxy Contract Address
const IKyberNetworkProxy_ADDRESS = '0xa16Fc6e9b5D359797999adA576F7f4a4d57E8F75';

// User Details
const PRIVATE_KEY = 'PRIVATE_KEY'; // Eg. 0x40ddbce3c7df9ab8d507d6b4af3861d224711b35299470ab7a217f780fe696cd
const USER_WALLET = new ethers.Wallet(PRIVATE_KEY, provider);

// Platform fees
const PLATFORM_WALLET = 'PLATFORM_WALLET'; // Eg. 0x483C5100C3E544Aef546f72dF4022c8934a6945E
const PLATFORM_FEE = 25; // 0.25%

// Instantiate contracts, using USER_WALLET as sender of txns
const KyberNetworkProxyContract = new ethers.Contract(
  IKyberNetworkProxy_ADDRESS,
  IKyberNetworkProxy_ABI,
  USER_WALLET,
);
const srcTokenContract = new ethers.Contract(
  SRC_TOKEN_ADDRESS,
  IERC20_ABI,
  USER_WALLET,
);

async function main() {
  // Step 1: Check and approve allowance if needed
  await checkAndApproveTokenForTrade(
    srcTokenContract,
    USER_WALLET.address,
    SRC_QTY,
  );

  let hint = EMPTY_HINT; // build hint here (see section on reserve routing)
  // Step 2: Get rate for trade
  let minConversionRate =
    await KyberNetworkProxyContract.getExpectedRateAfterFee(
      SRC_TOKEN_ADDRESS,
      DEST_TOKEN_ADDRESS,
      SRC_QTY,
      PLATFORM_FEE,
      hint,
    );

  // Step 3: Get gas limit estimates and price
  let gasConfig = await getGasConfig(
    KyberNetworkProxyContract,
    provider,
    SRC_TOKEN_ADDRESS,
    DEST_TOKEN_ADDRESS,
    SRC_QTY,
    USER_WALLET.address,
    MAX_UINT256,
    minConversionRate,
    PLATFORM_WALLET,
    PLATFORM_FEE,
    hint,
  );

  // Step 4: Execute trade
  let ethValue = SRC_TOKEN_ADDRESS == ETH_ADDRESS ? SRC_QTY : ZERO_BN;

  console.log('Executing Trade...');
  await KyberNetworkProxyContract.tradeWithHintAndFee(
    SRC_TOKEN_ADDRESS,
    SRC_QTY,
    DEST_TOKEN_ADDRESS,
    USER_WALLET.address, // destAddress
    MAX_UINT256, // maxDestAmount: set to be arbitrarily large
    minConversionRate,
    PLATFORM_WALLET,
    PLATFORM_FEE,
    hint,
    {
      value: ethValue,
      gasLimit: gasConfig.gasLimit,
      gasPrice: gasConfig.gasPrice,
    },
  );

  // Quit the program
  process.exit(0);
}

async function checkAndApproveTokenForTrade(
  srcTokenContract,
  userAddress,
  srcQty,
) {
  if (srcTokenContract.address == ETH_ADDRESS) {
    return;
  }

  // check existing allowance given to proxy contract
  let existingAllowance = await srcTokenContract.allowance(
    userAddress,
    IKyberNetworkProxy_ADDRESS,
  );

  // if zero allowance, just set to MAX_UINT256
  if (existingAllowance.eq(ZERO_BN)) {
    console.log('Approving KNP contract to max allowance');
    await srcTokenContract.approve(IKyberNetworkProxy_ADDRESS, MAX_UINT256);
  } else if (existingAllowance.lt(srcQty)) {
    // if existing allowance is insufficient, reset to zero, then set to MAX_UINT256
    console.log('Approving KNP contract to zero, then max allowance');
    await srcTokenContract.approve(IKyberNetworkProxy_ADDRESS, ZERO_BN);
    await srcTokenContract.approve(IKyberNetworkProxy_ADDRESS, MAX_UINT256);
  }
  return;
}

async function getGasConfig(
  KyberNetworkProxyContract,
  provider,
  srcTokenAddress,
  destTokenAddress,
  srcQty,
  destAddress,
  maxDestAmount,
  minConversionRate,
  platformWallet,
  platformFee,
  hint,
) {
  let gasConfig = { gasLimit: ZERO_BN, gasPrice: ZERO_BN };

  // Configure gas limit
  // Method 1: Use estimateGas function, add buffer
  let gasLimit =
    await KyberNetworkProxyContract.estimateGas.tradeWithHintAndFee(
      srcTokenAddress,
      srcQty,
      destTokenAddress,
      destAddress,
      maxDestAmount,
      minConversionRate,
      platformWallet,
      platformFee,
      hint,
    );

  gasConfig.gasLimit = gasLimit.mul(BN.from(110)).div(BN.from(100));

  // Method 2: Use /gasLimit API (only Ropsten and mainnet)
  // let gasLimitRequest = await fetch(
  //   `https://${NETWORK == "mainnet" ? "" : NETWORK + "-"}api.kyber.network/gas_limit?` +
  //   `source=${srcTokenAddress}&dest=${destTokenAddress}&amount=${srcQty}`
  //   );

  // let gasLimit = await gasLimitRequest.json();
  // if (gasLimit.error) {
  //   console.log(gasLimit);
  //   process.exit(0);
  // } else {
  //   gasConfig.gasLimit = BN.from(gasLimit.data);
  // }

  // Configure gas price
  let maxGasPrice = await KyberNetworkProxyContract.maxGasPrice();
  // Method 1: Fetch gasPrice
  let gasPrice = await provider.getGasPrice();

  //Method 2: Manual gasPrice input
  // let gasPrice = BN.from(30).mul((BN.from(10).mul(BN.from(9))));

  // Check against maxGasPrice
  gasConfig.gasPrice = gasPrice.gt(maxGasPrice) ? maxGasPrice : gasPrice;
  return gasConfig;
}

main();

In previous network versions, the hint parameter was used to filter permissionless reserves. With Katalyst, we utilise this parameter for routing trades to specific reserves.

There are 4 optional routing rules:

  1. BestOfAll - This is the default routing rule when no hint is provided, and is the classic reserve matching algorithm used by the Kyber smart contracts since the beginning.

  2. MaskIn (Whitelist) - Specify a list of reserves to be included and perform the BestOfAll routing on them

  3. MaskOut (Blacklist) - Specify a list of reserves to be excluded and perform the BestOfAll routing on the remaining reserves

  4. Split - Specify a list of reserves and their respective percentages of the total srcQty that will be routed to each reserve.

For token -> token trades, you can specify a routing rule for each half. For example, a MaskIn route can be used for the token -> ether side, while a Split route can be used for the ether -> token side.

The contract to interact with for this functionality here is the KyberStorage contract.

const IKyberStorage_ABI = [
  {
    inputs: [
      { internalType: 'address', name: 'kyberProxy', type: 'address' },
      { internalType: 'uint256', name: 'maxApprovedProxies', type: 'uint256' },
    ],
    name: 'addKyberProxy',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'bytes32[]', name: 'reserveIds', type: 'bytes32[]' },
    ],
    name: 'getEntitledRebateData',
    outputs: [
      { internalType: 'bool[]', name: 'entitledRebateArr', type: 'bool[]' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'bytes32[]', name: 'reserveIds', type: 'bytes32[]' },
    ],
    name: 'getFeeAccountedData',
    outputs: [
      { internalType: 'bool[]', name: 'feeAccountedArr', type: 'bool[]' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [],
    name: 'getKyberProxies',
    outputs: [
      {
        internalType: 'contract IKyberNetworkProxy[]',
        name: '',
        type: 'address[]',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'bytes32[]', name: 'reserveIds', type: 'bytes32[]' },
    ],
    name: 'getRebateWalletsFromIds',
    outputs: [
      { internalType: 'address[]', name: 'rebateWallets', type: 'address[]' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'bytes32', name: 'reserveId', type: 'bytes32' }],
    name: 'getReserveAddressesByReserveId',
    outputs: [
      {
        internalType: 'address[]',
        name: 'reserveAddresses',
        type: 'address[]',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'bytes32[]', name: 'reserveIds', type: 'bytes32[]' },
    ],
    name: 'getReserveAddressesFromIds',
    outputs: [
      {
        internalType: 'address[]',
        name: 'reserveAddresses',
        type: 'address[]',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'token', type: 'address' },
      { internalType: 'uint256', name: 'startIndex', type: 'uint256' },
      { internalType: 'uint256', name: 'endIndex', type: 'uint256' },
    ],
    name: 'getReserveAddressesPerTokenSrc',
    outputs: [
      {
        internalType: 'address[]',
        name: 'reserveAddresses',
        type: 'address[]',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'address', name: 'reserve', type: 'address' }],
    name: 'getReserveDetailsByAddress',
    outputs: [
      { internalType: 'bytes32', name: 'reserveId', type: 'bytes32' },
      { internalType: 'address', name: 'rebateWallet', type: 'address' },
      {
        internalType: 'enum IKyberStorage.ReserveType',
        name: 'resType',
        type: 'uint8',
      },
      { internalType: 'bool', name: 'isFeeAccountedFlag', type: 'bool' },
      { internalType: 'bool', name: 'isEntitledRebateFlag', type: 'bool' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'bytes32', name: 'reserveId', type: 'bytes32' }],
    name: 'getReserveDetailsById',
    outputs: [
      { internalType: 'address', name: 'reserveAddress', type: 'address' },
      { internalType: 'address', name: 'rebateWallet', type: 'address' },
      {
        internalType: 'enum IKyberStorage.ReserveType',
        name: 'resType',
        type: 'uint8',
      },
      { internalType: 'bool', name: 'isFeeAccountedFlag', type: 'bool' },
      { internalType: 'bool', name: 'isEntitledRebateFlag', type: 'bool' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'address', name: 'reserve', type: 'address' }],
    name: 'getReserveId',
    outputs: [{ internalType: 'bytes32', name: 'reserveId', type: 'bytes32' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      {
        internalType: 'address[]',
        name: 'reserveAddresses',
        type: 'address[]',
      },
    ],
    name: 'getReserveIdsFromAddresses',
    outputs: [
      { internalType: 'bytes32[]', name: 'reserveIds', type: 'bytes32[]' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'token', type: 'address' },
    ],
    name: 'getReserveIdsPerTokenDest',
    outputs: [
      { internalType: 'bytes32[]', name: 'reserveIds', type: 'bytes32[]' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'token', type: 'address' },
    ],
    name: 'getReserveIdsPerTokenSrc',
    outputs: [
      { internalType: 'bytes32[]', name: 'reserveIds', type: 'bytes32[]' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'bytes32[]', name: 'reserveIds', type: 'bytes32[]' },
      { internalType: 'contract IERC20', name: 'src', type: 'address' },
      { internalType: 'contract IERC20', name: 'dest', type: 'address' },
    ],
    name: 'getReservesData',
    outputs: [
      { internalType: 'bool', name: 'areAllReservesListed', type: 'bool' },
      { internalType: 'bool[]', name: 'feeAccountedArr', type: 'bool[]' },
      { internalType: 'bool[]', name: 'entitledRebateArr', type: 'bool[]' },
      {
        internalType: 'contract IKyberReserve[]',
        name: 'reserveAddresses',
        type: 'address[]',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [],
    name: 'isKyberProxyAdded',
    outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'address', name: 'kyberProxy', type: 'address' }],
    name: 'removeKyberProxy',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'address', name: '_kyberFeeHandler', type: 'address' },
      {
        internalType: 'address',
        name: '_kyberMatchingEngine',
        type: 'address',
      },
    ],
    name: 'setContracts',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'address', name: '_kyberDao', type: 'address' }],
    name: 'setKyberDaoContract',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
];
const IKyberStorage_ADDRESS = '0xa4eaD31a6c8e047E01cE1128E268c101AD391959';

For the token -> ether side of the trade, call getReserveIdsPerTokenSrc of the kyberStorage contract. For the ether -> token side of the trade, call getReserveIdsPerTokenDest of the kyberStorage contract.

Get reserve IDs for WBTC -> ETH.

let srcTokenReserveIds = await KyberStorageContract.getReserveIdsPerTokenSrc(
  WBTC_ADDRESS,
);

Get reserve IDs for ETH -> KNC.

let destTokenReserveIds = await KyberStorageContract.getReserveIdsPerTokenDest(
  KNC_ADDRESS,
);

The contract to interact with for this functionality here is the KyberHintHandler contract, which is inherited by the KyberMatchingEngine contract.

  • For token -> ether trades, call the buildTokenToEthHint function

  • For ether -> token trades, call the buildTokenToEthHint function

  • For token -> token trades, call the buildTokenToTokenHint function

Their input parameters are explained below:

Parameter
Type
Description

tokenSrc

IERC20

source ERC20 token contract address

tokenToEthType

uint256

0 = BestOfAll, 1 = MaskIn, 2 = MaskOut, 3 = Split

tokenToEthReserveIds

bytes32[]

list of reserve IDs for token -> ether trade

tokenToEthSplits

uint256[]

percentages (in basis points) for Split trade

tokenDest

IERC20

source ERC20 token contract address

ethToTokenType

uint256

0 = BestOfAll, 1 = MaskIn, 2 = MaskOut, 3 = Split

ethToTokenReserveIds

bytes32[]

list of reserve IDs for token -> ether trade

ethToTokenSplits

uint256[]

percentages (in basis points) for Split trade

  • The correct builder hint function must be used for the correct trade type. Otherwise, the hint will not be built correctly, and will result in transaction failure.

  • For token -> token trades, a combination of TradeTypes are allowed. For example, the token -> eth trade can be BestOfAll, while the eth -> token trade can be Split.

const IKyberHint_ABI = [
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'tokenDest', type: 'address' },
      {
        internalType: 'enum IKyberHint.TradeType',
        name: 'ethToTokenType',
        type: 'uint8',
      },
      {
        internalType: 'bytes32[]',
        name: 'ethToTokenReserveIds',
        type: 'bytes32[]',
      },
      {
        internalType: 'uint256[]',
        name: 'ethToTokenSplits',
        type: 'uint256[]',
      },
    ],
    name: 'buildEthToTokenHint',
    outputs: [{ internalType: 'bytes', name: 'hint', type: 'bytes' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'tokenSrc', type: 'address' },
      {
        internalType: 'enum IKyberHint.TradeType',
        name: 'tokenToEthType',
        type: 'uint8',
      },
      {
        internalType: 'bytes32[]',
        name: 'tokenToEthReserveIds',
        type: 'bytes32[]',
      },
      {
        internalType: 'uint256[]',
        name: 'tokenToEthSplits',
        type: 'uint256[]',
      },
    ],
    name: 'buildTokenToEthHint',
    outputs: [{ internalType: 'bytes', name: 'hint', type: 'bytes' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'tokenSrc', type: 'address' },
      {
        internalType: 'enum IKyberHint.TradeType',
        name: 'tokenToEthType',
        type: 'uint8',
      },
      {
        internalType: 'bytes32[]',
        name: 'tokenToEthReserveIds',
        type: 'bytes32[]',
      },
      {
        internalType: 'uint256[]',
        name: 'tokenToEthSplits',
        type: 'uint256[]',
      },
      { internalType: 'contract IERC20', name: 'tokenDest', type: 'address' },
      {
        internalType: 'enum IKyberHint.TradeType',
        name: 'ethToTokenType',
        type: 'uint8',
      },
      {
        internalType: 'bytes32[]',
        name: 'ethToTokenReserveIds',
        type: 'bytes32[]',
      },
      {
        internalType: 'uint256[]',
        name: 'ethToTokenSplits',
        type: 'uint256[]',
      },
    ],
    name: 'buildTokenToTokenHint',
    outputs: [{ internalType: 'bytes', name: 'hint', type: 'bytes' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'tokenDest', type: 'address' },
      { internalType: 'bytes', name: 'hint', type: 'bytes' },
    ],
    name: 'parseEthToTokenHint',
    outputs: [
      {
        internalType: 'enum IKyberHint.TradeType',
        name: 'ethToTokenType',
        type: 'uint8',
      },
      {
        internalType: 'bytes32[]',
        name: 'ethToTokenReserveIds',
        type: 'bytes32[]',
      },
      {
        internalType: 'contract IKyberReserve[]',
        name: 'ethToTokenAddresses',
        type: 'address[]',
      },
      {
        internalType: 'uint256[]',
        name: 'ethToTokenSplits',
        type: 'uint256[]',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'tokenSrc', type: 'address' },
      { internalType: 'bytes', name: 'hint', type: 'bytes' },
    ],
    name: 'parseTokenToEthHint',
    outputs: [
      {
        internalType: 'enum IKyberHint.TradeType',
        name: 'tokenToEthType',
        type: 'uint8',
      },
      {
        internalType: 'bytes32[]',
        name: 'tokenToEthReserveIds',
        type: 'bytes32[]',
      },
      {
        internalType: 'contract IKyberReserve[]',
        name: 'tokenToEthAddresses',
        type: 'address[]',
      },
      {
        internalType: 'uint256[]',
        name: 'tokenToEthSplits',
        type: 'uint256[]',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'tokenSrc', type: 'address' },
      { internalType: 'contract IERC20', name: 'tokenDest', type: 'address' },
      { internalType: 'bytes', name: 'hint', type: 'bytes' },
    ],
    name: 'parseTokenToTokenHint',
    outputs: [
      {
        internalType: 'enum IKyberHint.TradeType',
        name: 'tokenToEthType',
        type: 'uint8',
      },
      {
        internalType: 'bytes32[]',
        name: 'tokenToEthReserveIds',
        type: 'bytes32[]',
      },
      {
        internalType: 'contract IKyberReserve[]',
        name: 'tokenToEthAddresses',
        type: 'address[]',
      },
      {
        internalType: 'uint256[]',
        name: 'tokenToEthSplits',
        type: 'uint256[]',
      },
      {
        internalType: 'enum IKyberHint.TradeType',
        name: 'ethToTokenType',
        type: 'uint8',
      },
      {
        internalType: 'bytes32[]',
        name: 'ethToTokenReserveIds',
        type: 'bytes32[]',
      },
      {
        internalType: 'contract IKyberReserve[]',
        name: 'ethToTokenAddresses',
        type: 'address[]',
      },
      {
        internalType: 'uint256[]',
        name: 'ethToTokenSplits',
        type: 'uint256[]',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
];
const IKyberHintHandler_ADDRESS = '0xeB4DBDEC268bC9818669E9926e62004317d84b54';

Note that the splits parameter must be empty, ie. [].

Select the first reserve for a WBTC -> ETH trade.

let reserveIds = await KyberStorageContract.getReserveIdsPerTokenSrc(
  WBTC_ADDRESS,
);
let hint = await KyberHintHandlerContract.buildTokenToEthHint(
  WBTC_ADDRESS,
  BN.from(1), // MaskIn
  reserveIds.slice(0, 1), // only first reserve
  [],
);

Note that the splits parameter must be empty, ie. [].

Exclude the first reserve from a WBTC -> ETH trade.

let reserveIds = await KyberStorageContract.getReserveIdsPerTokenSrc(
  WBTC_ADDRESS,
);
let hint = await KyberHintHandlerContract.buildTokenToEthHint(
  WBTC_ADDRESS,
  BN.from(2), // MaskOut
  reserveIds.slice(0, 1), // exclude only first reserve
  [],
);

Note that the splits values must add up to 10000 (100%).

Split evenly among 2 reserves for a ETH -> KNC trade.

let reserveIds = await KyberStorageContract.getReserveIdsPerTokenDest(
  KNC_ADDRESS,
);
let hint = await KyberHintHandlerContract.buildEthToTokenHint(
  KNC_ADDRESS,
  BN.from(3), // Split
  reserveIds.slice(0, 2), // select first 2 reserves
  [BN.from(5000), BN.from(5000)],
);
  • The BestOfAll TradeType is primarily for specifying the BestOfAll behaviour for one side of token -> token trades. It is not needed for token -> ether and ether -> token trades.

  • The reserveIds and splits parameters must be empty.

For a WBTC -> KNC trade, do a MaskIn route for WBTC -> ETH, and BestOfAll route for ETH -> KNC.

let reserveIds = await KyberStorageContract.getReserveIdsPerTokenSrc(
  WBTC_ADDRESS,
);
let hint = await KyberHintHandlerContract.buildTokenToTokenHint(
  WBTC_ADDRESS,
  1, // MaskIn
  reserveIds.slice(0, 1), // select only 1 reserve
  [],
  KNC_ADDRESS,
  0, // BestOfAll
  [],
  [],
);
// DISCLAIMER: Code snippets in this guide are just examples and you
// should always do your own testing. If you have questions, visit our
// https://t.me/KyberDeveloper.

const ethers = require('ethers');
const BN = ethers.BigNumber;
const NETWORK = 'ropsten';
const provider = new ethers.getDefaultProvider(NETWORK);

const IKyberStorage_ABI = [
  {
    inputs: [
      { internalType: 'address', name: 'kyberProxy', type: 'address' },
      { internalType: 'uint256', name: 'maxApprovedProxies', type: 'uint256' },
    ],
    name: 'addKyberProxy',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'bytes32[]', name: 'reserveIds', type: 'bytes32[]' },
    ],
    name: 'getEntitledRebateData',
    outputs: [
      { internalType: 'bool[]', name: 'entitledRebateArr', type: 'bool[]' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'bytes32[]', name: 'reserveIds', type: 'bytes32[]' },
    ],
    name: 'getFeeAccountedData',
    outputs: [
      { internalType: 'bool[]', name: 'feeAccountedArr', type: 'bool[]' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [],
    name: 'getKyberProxies',
    outputs: [
      {
        internalType: 'contract IKyberNetworkProxy[]',
        name: '',
        type: 'address[]',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'bytes32[]', name: 'reserveIds', type: 'bytes32[]' },
    ],
    name: 'getRebateWalletsFromIds',
    outputs: [
      { internalType: 'address[]', name: 'rebateWallets', type: 'address[]' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'bytes32', name: 'reserveId', type: 'bytes32' }],
    name: 'getReserveAddressesByReserveId',
    outputs: [
      {
        internalType: 'address[]',
        name: 'reserveAddresses',
        type: 'address[]',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'bytes32[]', name: 'reserveIds', type: 'bytes32[]' },
    ],
    name: 'getReserveAddressesFromIds',
    outputs: [
      {
        internalType: 'address[]',
        name: 'reserveAddresses',
        type: 'address[]',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'token', type: 'address' },
      { internalType: 'uint256', name: 'startIndex', type: 'uint256' },
      { internalType: 'uint256', name: 'endIndex', type: 'uint256' },
    ],
    name: 'getReserveAddressesPerTokenSrc',
    outputs: [
      {
        internalType: 'address[]',
        name: 'reserveAddresses',
        type: 'address[]',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'address', name: 'reserve', type: 'address' }],
    name: 'getReserveDetailsByAddress',
    outputs: [
      { internalType: 'bytes32', name: 'reserveId', type: 'bytes32' },
      { internalType: 'address', name: 'rebateWallet', type: 'address' },
      {
        internalType: 'enum IKyberStorage.ReserveType',
        name: 'resType',
        type: 'uint8',
      },
      { internalType: 'bool', name: 'isFeeAccountedFlag', type: 'bool' },
      { internalType: 'bool', name: 'isEntitledRebateFlag', type: 'bool' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'bytes32', name: 'reserveId', type: 'bytes32' }],
    name: 'getReserveDetailsById',
    outputs: [
      { internalType: 'address', name: 'reserveAddress', type: 'address' },
      { internalType: 'address', name: 'rebateWallet', type: 'address' },
      {
        internalType: 'enum IKyberStorage.ReserveType',
        name: 'resType',
        type: 'uint8',
      },
      { internalType: 'bool', name: 'isFeeAccountedFlag', type: 'bool' },
      { internalType: 'bool', name: 'isEntitledRebateFlag', type: 'bool' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'address', name: 'reserve', type: 'address' }],
    name: 'getReserveId',
    outputs: [{ internalType: 'bytes32', name: 'reserveId', type: 'bytes32' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      {
        internalType: 'address[]',
        name: 'reserveAddresses',
        type: 'address[]',
      },
    ],
    name: 'getReserveIdsFromAddresses',
    outputs: [
      { internalType: 'bytes32[]', name: 'reserveIds', type: 'bytes32[]' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'token', type: 'address' },
    ],
    name: 'getReserveIdsPerTokenDest',
    outputs: [
      { internalType: 'bytes32[]', name: 'reserveIds', type: 'bytes32[]' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'token', type: 'address' },
    ],
    name: 'getReserveIdsPerTokenSrc',
    outputs: [
      { internalType: 'bytes32[]', name: 'reserveIds', type: 'bytes32[]' },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'bytes32[]', name: 'reserveIds', type: 'bytes32[]' },
      { internalType: 'contract IERC20', name: 'src', type: 'address' },
      { internalType: 'contract IERC20', name: 'dest', type: 'address' },
    ],
    name: 'getReservesData',
    outputs: [
      { internalType: 'bool', name: 'areAllReservesListed', type: 'bool' },
      { internalType: 'bool[]', name: 'feeAccountedArr', type: 'bool[]' },
      { internalType: 'bool[]', name: 'entitledRebateArr', type: 'bool[]' },
      {
        internalType: 'contract IKyberReserve[]',
        name: 'reserveAddresses',
        type: 'address[]',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [],
    name: 'isKyberProxyAdded',
    outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'address', name: 'kyberProxy', type: 'address' }],
    name: 'removeKyberProxy',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'address', name: '_kyberFeeHandler', type: 'address' },
      {
        internalType: 'address',
        name: '_kyberMatchingEngine',
        type: 'address',
      },
    ],
    name: 'setContracts',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
  {
    inputs: [{ internalType: 'address', name: '_kyberDao', type: 'address' }],
    name: 'setKyberDaoContract',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function',
  },
];
const IKyberHint_ABI = [
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'tokenDest', type: 'address' },
      {
        internalType: 'enum IKyberHint.TradeType',
        name: 'ethToTokenType',
        type: 'uint8',
      },
      {
        internalType: 'bytes32[]',
        name: 'ethToTokenReserveIds',
        type: 'bytes32[]',
      },
      {
        internalType: 'uint256[]',
        name: 'ethToTokenSplits',
        type: 'uint256[]',
      },
    ],
    name: 'buildEthToTokenHint',
    outputs: [{ internalType: 'bytes', name: 'hint', type: 'bytes' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'tokenSrc', type: 'address' },
      {
        internalType: 'enum IKyberHint.TradeType',
        name: 'tokenToEthType',
        type: 'uint8',
      },
      {
        internalType: 'bytes32[]',
        name: 'tokenToEthReserveIds',
        type: 'bytes32[]',
      },
      {
        internalType: 'uint256[]',
        name: 'tokenToEthSplits',
        type: 'uint256[]',
      },
    ],
    name: 'buildTokenToEthHint',
    outputs: [{ internalType: 'bytes', name: 'hint', type: 'bytes' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'tokenSrc', type: 'address' },
      {
        internalType: 'enum IKyberHint.TradeType',
        name: 'tokenToEthType',
        type: 'uint8',
      },
      {
        internalType: 'bytes32[]',
        name: 'tokenToEthReserveIds',
        type: 'bytes32[]',
      },
      {
        internalType: 'uint256[]',
        name: 'tokenToEthSplits',
        type: 'uint256[]',
      },
      { internalType: 'contract IERC20', name: 'tokenDest', type: 'address' },
      {
        internalType: 'enum IKyberHint.TradeType',
        name: 'ethToTokenType',
        type: 'uint8',
      },
      {
        internalType: 'bytes32[]',
        name: 'ethToTokenReserveIds',
        type: 'bytes32[]',
      },
      {
        internalType: 'uint256[]',
        name: 'ethToTokenSplits',
        type: 'uint256[]',
      },
    ],
    name: 'buildTokenToTokenHint',
    outputs: [{ internalType: 'bytes', name: 'hint', type: 'bytes' }],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'tokenDest', type: 'address' },
      { internalType: 'bytes', name: 'hint', type: 'bytes' },
    ],
    name: 'parseEthToTokenHint',
    outputs: [
      {
        internalType: 'enum IKyberHint.TradeType',
        name: 'ethToTokenType',
        type: 'uint8',
      },
      {
        internalType: 'bytes32[]',
        name: 'ethToTokenReserveIds',
        type: 'bytes32[]',
      },
      {
        internalType: 'contract IKyberReserve[]',
        name: 'ethToTokenAddresses',
        type: 'address[]',
      },
      {
        internalType: 'uint256[]',
        name: 'ethToTokenSplits',
        type: 'uint256[]',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'tokenSrc', type: 'address' },
      { internalType: 'bytes', name: 'hint', type: 'bytes' },
    ],
    name: 'parseTokenToEthHint',
    outputs: [
      {
        internalType: 'enum IKyberHint.TradeType',
        name: 'tokenToEthType',
        type: 'uint8',
      },
      {
        internalType: 'bytes32[]',
        name: 'tokenToEthReserveIds',
        type: 'bytes32[]',
      },
      {
        internalType: 'contract IKyberReserve[]',
        name: 'tokenToEthAddresses',
        type: 'address[]',
      },
      {
        internalType: 'uint256[]',
        name: 'tokenToEthSplits',
        type: 'uint256[]',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
  {
    inputs: [
      { internalType: 'contract IERC20', name: 'tokenSrc', type: 'address' },
      { internalType: 'contract IERC20', name: 'tokenDest', type: 'address' },
      { internalType: 'bytes', name: 'hint', type: 'bytes' },
    ],
    name: 'parseTokenToTokenHint',
    outputs: [
      {
        internalType: 'enum IKyberHint.TradeType',
        name: 'tokenToEthType',
        type: 'uint8',
      },
      {
        internalType: 'bytes32[]',
        name: 'tokenToEthReserveIds',
        type: 'bytes32[]',
      },
      {
        internalType: 'contract IKyberReserve[]',
        name: 'tokenToEthAddresses',
        type: 'address[]',
      },
      {
        internalType: 'uint256[]',
        name: 'tokenToEthSplits',
        type: 'uint256[]',
      },
      {
        internalType: 'enum IKyberHint.TradeType',
        name: 'ethToTokenType',
        type: 'uint8',
      },
      {
        internalType: 'bytes32[]',
        name: 'ethToTokenReserveIds',
        type: 'bytes32[]',
      },
      {
        internalType: 'contract IKyberReserve[]',
        name: 'ethToTokenAddresses',
        type: 'address[]',
      },
      {
        internalType: 'uint256[]',
        name: 'ethToTokenSplits',
        type: 'uint256[]',
      },
    ],
    stateMutability: 'view',
    type: 'function',
  },
];

const IKyberStorage_ADDRESS = '0xa4eaD31a6c8e047E01cE1128E268c101AD391959';
const IKyberHintHandler_ADDRESS = '0xeB4DBDEC268bC9818669E9926e62004317d84b54';

const KyberStorageContract = new ethers.Contract(
  IKyberStorage_ADDRESS,
  IKyberStorage_ABI,
  provider,
);

const KyberHintHandlerContract = new ethers.Contract(
  IKyberHintHandler_ADDRESS,
  IKyberHint_ABI,
  provider,
);

const WBTC_ADDRESS = '0x3dff0dce5fc4b367ec91d31de3837cf3840c8284'; // Ropsten WBTC address
const KNC_ADDRESS = '0x7b2810576aa1cce68f2b118cef1f36467c648f92'; // Ropsten KNC address

async function main() {
  let srcTokenReserveIds = await KyberStorageContract.getReserveIdsPerTokenSrc(
    WBTC_ADDRESS,
  );
  let destTokenReserveIds =
    await KyberStorageContract.getReserveIdsPerTokenDest(KNC_ADDRESS);

  let hint;

  // MaskIn Hint
  hint = await KyberHintHandlerContract.buildTokenToEthHint(
    WBTC_ADDRESS,
    BN.from(1), // MaskIn
    srcTokenReserveIds.slice(0, 1), // only first reserve
    [],
  );

  // MaskOut Hint
  hint = await KyberHintHandlerContract.buildEthToTokenHint(
    KNC_ADDRESS,
    BN.from(2), // MaskOut
    srcTokenReserveIds.slice(0, 1), // exclude only first reserve
    [],
  );

  // Split + BestOfAll Hint
  hint = await KyberHintHandlerContract.buildTokenToTokenHint(
    WBTC_ADDRESS,
    BN.from(0), // BestOfAll
    [],
    [],
    KNC_ADDRESS,
    BN.from(3), // Split
    destTokenReserveIds.slice(0, 3), // 3 reserves
    [BN.from(4000), BN.from(3000), BN.from(3000)], // 1st reserve 40%, 2nd and 3rd reserves 30% each
  );
}

main();

Trading Tokens

Import Relevant Packages

Connect to an Ethereum Node

Define Constants and Trade Details

Universal Constants

Tokens and Source Quantity

Contract ABIs and Proxy Address

: Token approvals and transfers

: Fetch rates and execute trades

Sender

Platform Wallet and Fees

Find out more about platform fees .

Instantiate Contracts

Core Steps For Trade Execution

Step 1: Checking Token Allowance

Step 2: Hint

For simple trades, we can simply set the hint as the EMPTY_HINT. Otherwise, we can build hints to specify .

Step 3: Fetching Rates

Understanding the rate

Step 3: Gas Configurations

Step 4: Executing Trade

Tying Everything Together

Full code example

Reserve Routing

Overview

Fetching Reserve IDs

Define Contract ABI and Address

We import the to fetch reserve IDs, and define the kyberStorage address.

Methods

Examples

Building Hints

Notes

Define Contract ABI and Address

We import the to fetch reserve IDs, and define the kyberHintHandler address.

MaskIn TradeType

Example

MaskOut TradeType

Example

Split TradeType

Example

BestOfAll TradeType

Example

Full Code Example

Classic
Elastic
Limit Order
Aggregator
​
ethers.js
Web3
​
Slippage Rates Protection
Price Feed Security
​
Trading Tokens
Reserve Routing
​
​
​
​
​
​
​
​
IERC20_ABI
IKyberNetworkProxy_ABI
​
​
here
​
​
​
​
reserve routes
​
​
​
​
​
​
​
​
​
​
IKyberStorage_ABI
​
​
​
​
​
IKyberHint_ABI
​
​
​
​
​
​
​
​
​