How to Create an Intent Adapter using Router's CCIF
This guide will walk you through the steps for creating an intent adapter using Router's Intent library. We will cover the steps from setting up dependencies, creating the contract, defining functions, and invoking them.
Prerequisites
Before proceeding further, make sure, you have the following:
Node.js and npm installed in your system
Hardhat
A basic understanding of Solidity
Step 1: Set Up the Project
Initialize the project directory:
Open your terminal and create a project directory:
mkdirmy-intent-adaptercdmy-intent-adapter
Initialize a Node.js project:
npminit-y
Install Hardhat:
npminstall--save-devhardhat
Create a new Hardhat project:
npxhardhat
Step 2: Install Required Dependencies
Install Router Protocol Intents packages:
npminstall@routerprotocol/intents-core
or
yarnadd@routerprotocol/intents-core
Install other dependencies: Install the dependencies needed for SafeERC20.
npminstall@openzeppelin/contracts
or
yarnadd@openzeppelin/contracts
Step 3: Define the Adapter Contract
Create a Solidity file for your adapter in the contracts folder.
Example: StaderStakeEth Adapter: This adapter uses the Router Intents framework to enable users that have funds on any compatible chain to stake their funds on Stader on Ethereum. (Modify as per the needs of your own protocol)
RouterIntentEoaAdapterWithoutDataProvider and EoaExecutorWithoutDataProvider from the Router Protocol Intents package.
IERC20 and SafeERC20 for token transfers.
Constructor
The constructor accepts addresses for native token, wnative token and protocol-specific contracts or the contracts containing entry points for the protocol. (e.g., ethx, staderPool).
Functions
execute: This function has to be present in every adapter contract and is expected to handle the data received from the multi caller contract (BatchTransaction contract). It parses the input, ensures the correct value is passed, and executes the _stake function.
_stake: This is an internal function that interacts with the protocol (in this case, the StaderPool) to deposit funds.
Step 4: Customize for Your Protocol
To adapt the contract to your own protocol, follow these steps:
Replace Protocol-Specific Logic
Change the external contract interfaces (e.g., IStaderPool) to match your protocol's interface.
Change the constructor arguments according to the protocol. (the address of native and wnative tokens at first two places remains fixed).
Adjust the parsing according to the needed arguments in the execute function and parseInputs function.
Modify the name of the internal function and logic in _stake to interact with protocol’s staking, lending, or swapping functions.
Adjust the ERC20 token interactions as necessary (use SafeERC20 for safety).
In this step, we explain how to customize the PancakeswapMint adapter for PancakeSwap or any other protocol. The goal is to replace protocol-specific logic, imports, and interfaces with the equivalent components of the new protocol you're targeting.
Example: Adapting for a new Protocol (PancakeSwap Mint)
In PancakeswapMint adapter, the contract interacts with the IPancakeswapNonfungiblePositionManager interface for minting positions.
The core function is _mint, which handles the logic for minting a new position on PancakeSwap. This function interacts with the PancakeSwap contract to mint a position by calling nonFungiblePositionManager.mint(mintParams).
The parseInputs function decodes the MintParams for the PancakeSwap position manager.
functionparseInputs(bytesmemory data ) publicpurereturns (IPancakeswapNonfungiblePositionManager.MintParamsmemory) {return abi.decode(data, (IPancakeswapNonfungiblePositionManager.MintParams)); }
Depending on the protocol, you need to replace this with the appropriate input structure that matches the action being performed.
Ensure that the constructor is updated to reflect the new protocol's requirements. In the provided PancakeswapMint contract, the constructor accepts the address for nonFungiblePositionManager:
consthre=require("hardhat");asyncfunctionmain() {constStaderStakeEth=awaithre.ethers.getContractFactory("StaderStakeEth");conststaderStakeEth=awaitStaderStakeEth.deploy("0xNativeTokenAddress",// FIXED: Replace with actual native token address"0xWNativeTokenAddress",// FIXED: Replace with actual wrapped native token address"0xEthxTokenAddress",// PROTOCOL SPECIFIC NEED 1: Replace with the token address or pool address"0xStaderPoolAddress"// PROTOCOL SPECIFIC NEED 2: if any );awaitstaderStakeEth.deployed();console.log("StaderStakeEth deployed to:",staderStakeEth.address);}main().catch((error) => {console.error(error);process.exitCode =1;});
Once deployed, you can interact with the adapter using the following steps:
Contact us for getting the adapter whitelisted on our BatchTransaction Contract in order to use the intents framework.
Any one can interact with the whitelisted intent adapters via Router's BatchTransaction. Deployed addressed can be found here.
/** * @dev function to execute batch calls on the same chain * @param appId Application Id * @param tokens Addresses of the tokens to fetch from the user * @param amounts amounts of the tokens to fetch from the user * @param feeData data for fee deduction * @param target Addresses of the contracts to call * @param value Amounts of native tokens to send along with the transactions * @param callType Type of call. 1: call, 2: delegatecall * @param data Data of the transactions */functionexecuteBatchCallsSameChain(uint256 appId,address[] calldata tokens,uint256[] calldata amounts,bytescalldata feeData,address[] calldata target,uint256[] calldata value,uint256[] calldata callType,bytes[] calldata data ) externalpayable {}
The data can be created in the following way in order to call any adapter. Note that the protocol data is subject to change according to the needs of the action, here we are just giving an example for how to create data for Stader. The fee data contains following:
This guide demonstrates how to create a generic intent adapter using Router Protocol's Intents package. Adapt the contract logic to the protocol by modifying the constructor, external interfaces, and the core functionality (e.g., staking, swapping, adding liquidity, lending). Happy building!
Notes:
You can modify the contract to suit different protocols by adjusting the external contract interface, parsing in execute function and logic inside the internal function.
Make sure to update the deployment addresses (like native, wnative, and protocol-specific contract addresses) in the deployment script.
Contact us for getting the adapter whitelisted on our BatchTransaction Contract.