RootLend Documentation Web App / Demo Video
RootLend is a next-generation DeFi protocol transforming peer-to-peer (P2P) lending and borrowing through a secure, collateral-backed dApp. Designed to overcome the inefficiencies of traditional finance, RootLend offers users a faster, more transparent, and more accessible lending experience. By integrating AI-driven loan evaluation and automated funding mechanisms, the platform intelligently matches eligible borrowers with available capital—streamlining decision-making, reducing wait times, and maximizing efficiency for both lenders and borrowers.
Powered by the Rootstock test network, RootLend brings together robust smart contracts, seamless integrations, and a highly intuitive front-end interface. Users enjoy complete control over their assets and data, secured through wallet or email login — ensuring privacy and security at every step.
With the RootLend dashboard, accessing the platform's full suite of features is effortless. Unlike traditional financial systems that involve lengthy approvals, high fees, and intermediaries, RootLend provides:
- Instant access to lending and borrowing markets without the need for banks or credit checks
- Greater transparency through blockchain technology, ensuring fair and tamper-proof transactions
- Lower fees and better rates thanks to the low gas fees on Rootstock network and the elimination of middlemen
- 24/7 global accessibility — no waiting for business hours or paperwork
- Enhanced security and user ownership of funds
Lenders earn interest on the funds they lend, based on the agreed loan terms — offering potentially higher yields compared to traditional savings accounts or investments.
Every loan is backed by collateral. If a borrower fails to repay, the lender can claim the collateral, reducing the risk of loss.
Lenders do not need to trust the borrower personally — smart contracts automatically enforce the loan terms and collateral handling.
Lenders choose which loans to fund based on the borrower's terms, amount, interest rate, and duration. They stay fully in control of where and how they allocate their capital.
All loan details, collateral, and borrower activities are recorded transparently on-chain, giving lenders a clear view before making decisions.
Lenders can communicate directly with borrowers through RootLend’s decentralized chat while maintaining full privacy.
As RootLend grows and adds more supported tokens and features, lenders benefit from a wider range of investment opportunities.
Borrowers can quickly access loans by providing collateral, without needing to go through lengthy credit checks like traditional banks require.
Anyone can borrow without middlemen, paperwork, or restrictions, as long as they meet the collateral requirements.
Borrowers can receive funding almost instantly once a lender accepts their loan request, speeding up financial access.
Borrowers maintain control over their wallets and assets, with no need to expose personal information.
Loan amount, interest rate, duration, and collateral are all transparent and set before the loan starts — no hidden fees or surprises.
If no lender funds the loan before expiry, the borrower can reclaim their collateral easily and without penalties.
Borrowers can chat securely and privately with potential lenders to discuss loan terms if needed.
Frequent successful repayments can help borrowers build a reputation within the RootLend ecosystem, potentially making it easier to get better loan terms in the future. RootLend delivers a lending experience that not only matches but exceeds the capabilities of traditional finance, empowering users with true financial freedom.
The platform features a powerful combination of smart contracts and front-end technologies, delivering a full suite of tools including a P2P lending and borrowing dApp, a Swap, an integrated dashboard, and secure wallet or email login (powered by Web3Modal and Ethers.js). It also offers decentralized chat within the lending dApp, comprehensive documentation, and reliable file storage through Filebase. Additional features like search functionalities, pagination, a wide library of Flaticons, and an intuitive, responsive design ensure a slick and seamless user experience across devices.
Solidity, Foundry, OpenZeppelin
Web3Modal, Ethers.js, Rootstock Testnet Token Tracker
Next.js, Tailwind CSS, JavaScript
Node.js, PostgreSQL
Chainlink oracles
TradingView Live API, Filebase Storage
To get started, visit our Web App.
Click the "Connect Wallet" button at the top right corner of the screen. You can log in either using your wallet or your email. If you don't have a wallet installed yet, the WalletConnect window provides direct links to download a wallet compatible with your browser (Brave, Google Chrome, Opera, or Yandex browsers are supported).
Download an EVM-compatible wallet (such as MetaMask or Trust Wallet) and use its built-in browser to access RootLend. Then, click "Connect Wallet" at the top right.
After initiating the connection, ensure you switch the network to the Rootstock Test Network via the wallet connect interface. Once connected, your dashboard display will update — typically showing your Rootstock RBTC token balance along with a portion of your wallet address.
Since RootLend is currently deployed on the Rootstock network's testnet, you will need testnet RBTC to cover gas fees during transactions.
You can easily obtain testnet RBTC through the Rootstock testnet faucet.
The homepage of the RootLend web app is the first page you’ll see when you visit the platform. It serves as the gateway to the RootLend dashboard and features the Connect Wallet button, along with important resources such as platform information and documentation.
This is the core functional area of RootLend, bringing together the platform’s most essential features — including user balances, key metrics, the P2P lending and borrowing dApp and Swap.
This section of the dashboard shows all the balances of a user after logging in. It displays the following info for a user: RTL balance, RTL price, RBTC balance, number of loans you created, number of loans you funded.
The metrics section shows live updates to RootLend including the following: RTL Total Supply, RTL Price, Protocol Blockchain, Total number of Loans, Max Loan Amount, Min Loan Amount, contract addresses of all contracts used by the protocol and Live RBTC market chart.
RootLend enables users to participate in unique P2P lending and borrowing activities involving supported tokens. To initiate the process, a borrower creates a loan request using the "Approve/Create Loan" buttons in the Borrow section. A lender can then fund the loan by using the "Fund Loan" button in the Lend section. Each loan on RootLend is defined by a loan amount, an interest rate, an expiry date, and collateral. The borrower's collateral is securely locked by the system until the loan's expiry. If no lender funds the loan before the expiry, the borrower can reclaim their collateral using the "Withdraw Collateral" button in the Lend section. Borrowers who successfully receive funding can withdraw the borrowed tokens using the "Withdraw Loan" button. If a borrower fails to repay the loan by the expiry date, the lender has the right to claim the posted collateral through the "Claim Collateral" button. Lenders are rewarded generously based on the agreed terms of the loan, with additional security provided by RootLend’s advanced security algorithms. When borrowing through RootLend, users must present sufficient collateral and repay the borrowed amount with interest using the "Repay Loan" button to fulfill their obligations. For a better user experience, the dApp integrates features like a "View All Available Loans" section and a "View Your Loans" section, along with search functionality and pagination to easily navigate between loans. A decentralized chat is also available, allowing lenders and borrowers to communicate privately and efficiently. New supported tokens and collateral options will continue to be added to the platform over time, ensuring RootLend remains dynamic and versatile.
To borrow, follow the steps below (Your wallet will popup to sign the transactions):
- Fill the details above setting the duration, amount of RBTC to borrow, collateral, collateral amount, "yes if collateral is ERC20 or no if collateral is ERC721 (an NFT)", and interest rate (which is the amount of interest you will pay to any user that funds your loan).
- Click on the "Approve" button to approve the loan transaction.
- Proceed to create the loan by clicking on the "Create Loan Request" button. After creating the loan, you will be automatically navigated to the Lending section where you can monitor all your created/funded loans.
Lending on RootLend is simpler than you might expect. Thanks to a smooth and intuitive user experience, users can easily carry out all lending activities. The platform features dedicated sections like "View All Loans", "Loans You Funded", and "Loans You Created" to help users effortlessly track their loan activities.
Search and pagination functionalities further enhance the usability of this powerful lending dApp.
To start lending, users can easily navigate and interact with the available action buttons provided within the dApp.
- Fund loan: This button allows users to fund loans created by other borrowers. To ensure fairness, the system prevents users from funding their own loans. In addition to manual funding, RootLend integrates an AI-powered funding system that automates loan evaluation and fulfillment. This system uses a Vercel Cron job to trigger a Next.js API route once every day. The API fetches all active loan requests, and the AI logic checks each one against predefined criteria—such as minimum interest rate and maximum loan amount.
The API can be found in the "/api/ai_fund_bot.js/" directory. And the cron job can be found in the "vercel.json" file in the root folder.
If a loan meets the criteria, the AI automatically initiates funding using a secure smart contract function, ensuring efficient loan matching and reducing wait times for borrowers—without requiring manual interaction.
- Claim collateral: The claim collateral button is typically available for the lender to use in case a borrower fails to repay a loan during the specified duration in the loan. You can only claim collateral of a loan you funded.
- Withdraw loan: This button can be used by the borrower of a loan to withdraw the loan after it has been funded by a lender.
- Repay loan: This button is available to only the borrower of a loan such that before the end of the loan duration, the borrower uses it to repay the borrowed amount with interest.
- Withdraw collateral: In the event that there is no lender of a loan you created as a borrower, you can use this button to withdraw your collateral back. You can use this button only when your loan has not been funded.
For effective communication between borrowers and lenders, RootLend provides a built-in decentralized chat system. Only the borrower and lender associated with a specific loan can communicate through this chat.
Please note: do not send funds directly to the lender or borrower. All transactions are securely managed by the dApp itself.
The RootLend Token (RTL) is the native token of the RootLend platform and is primarily used as collateral for lending activities. In addition to RTL, users can also use other supported tokens like USDT and DAI as collateral.
RootLend offers a seamless and secure token swap feature, integrated with ChainLink Oracles to ensure accurate and reliable price feeds. This allows users to efficiently swap a variety of tokens directly within the platform.
Currently, users can swap the following tokens:
-
RBTC (Rootstock BTC)
-
RTL (RootLend Token)
-
USDT (Tether)
-
DAI (Dai Stablecoin)
With the integration of ChainLink Oracles, the platform ensures that the swap rates reflect the most up-to-date market prices, providing users with fair and transparent exchange rates every time. This integration guarantees accuracy and trustworthiness in token conversions, removing the need for manual price checks or reliance on third-party services. Whether you're a lender, borrower, or general user on RootLend, this swap feature provides flexibility and convenience, making it easy to exchange tokens directly within the platform. The process is fast, secure, and powered by decentralized oracles, ensuring that you get the best rates available at the time of your transaction.
In upcoming updates, RootLend will introduce a host of new features and improvements to enhance the user experience and expand the platform's capabilities:
We remain committed to continuously improving the user experience, making the platform even more intuitive, responsive, and accessible.
Notifications will be integrated into the Lending dApp, allowing users to stay updated on loan status, payments, and other relevant activities in real time.
For better communication and collaboration, file sharing through IPFS and the Filebase SDK will be integrated into the decentralized chat system, allowing users to exchange documents and data securely.
We plan to expand the list of supported tokens and collateral options, providing more flexibility for both borrowers and lenders.
Additional security measures will be implemented to further protect user assets and transactions, building on RootLend’s robust security architecture.
RootLend aims to introduce staking and other yield farming features, allowing users to earn passive income by staking their tokens or providing liquidity to the platform.
We’re exploring the integration of cross-chain capabilities, allowing users to interact with RootLend using assets from other blockchain networks, enhancing accessibility and liquidity.
In the future, we plan to launch a mobile app, providing users with easy access to RootLend's features and functionality on the go.
Lenders and borrowers will have access to detailed analytics and reporting tools, helping them track their activities, earnings, and overall performance on the platform.
Liquidity providers in the RootLend Swap will be rewarded with incentives. Users who supply liquidity to the swap pools (RBTC, RTL, USDT, DAI) will earn a share of the trading fees and possibly additional rewards in RTL tokens, encouraging a healthy liquidity environment.
- Patrick Ominisan - Blockchain developer
- Jeremiah Oyeniran - Smart contract developer
Contract Addresses with Rootstock Testnet Links Rootstock Testnet Explorer
Note that all contracts are verified on the Rootstock testnet explorer.
✅ P2PLending 🟢
- Address:
0x91f3ff344623adc499ec6a34fc6311e8abbf7880
✅ RootLend Token 🟢
- Address:
0x326e042259c7cf517ca303f6cbe732d33331645e
✅ Swap 🟢
- Address:
0x4df364ca65d0ae96cde5acf255ed5bd873af224b
- USDT: 0xa4a6888685faeca07b5e42e00a05f92ad3b921ca
- DAI: 0xe2608d2d22e59ebf715114e976bdc8aa85891c1b
The P2PLending contract enables peer-to-peer lending with ERC20 tokens and ERC721 NFTs as collateral. It manages loan creation, funding, repayment, and collateral claiming.
Initializes the contract by setting the owner, treasury address, and accepted collateral tokens.
constructor(
address _initialowner,
address _treasuryaddr,
address RTL,
address usdt,
address dai
) Ownable(_initialowner) {
treasuryAddress = _treasuryaddr;
_addCollateral(RTL);
_addCollateral(usdt);
_addCollateral(dai);
}- Sets Initial Owner: Calls
Ownableconstructor with_initialowner. - Sets Treasury Address: Sets
treasuryAddressto_treasuryaddr. - Adds Initial Collaterals: Adds
RTL,usdt, anddaias accepted collaterals.
Sets the DAO address which has certain administrative privileges.
function setadaoaddress(address _dao) public onlyOwner {
dao = _dao;
}- Ownership Restriction: Can only be called by the contract owner (
onlyOwner). - Sets DAO Address: Updates the
daostate variable.
Adds a new collateral type to the list of accepted collaterals.
function addCollateral(address _collateral) public onlyOwner {
_addCollateral(_collateral);
emit CollateralAdded(_collateral);
}- Ownership Restriction: Can only be called by the contract owner.
- Internal Logic: Calls
_addCollateralto handle the actual logic. - Event Emission: Emits
CollateralAddedevent.
Handles the logic of adding a new collateral.
function _addCollateral(address _collateral) internal {
accepteddCollaterals[_collateral] = true;
accepted_collaterals.push(_collateral);
}- Updates Mapping: Marks the token as accepted in
accepteddCollaterals. - Updates Array: Adds the token to
accepted_collaterals.
Allows a borrower to create a new loan request.
function createLoan(
uint _amount,
uint _interest,
uint _duration,
uint _collateralamount,
address _collateral,
bool _isERC20,
uint _fundingDeadline
) external payable isCollateral(_collateral) {
require(_amount >= MIN_LOAN_AMOUNT && _amount <= MAX_LOAN_AMOUNT, "Loan amount must be between MIN_LOAN_AMOUNT and MAX_LOAN_AMOUNT");
require(_interest >= MIN_INTEREST_RATE && _interest <= MAX_INTEREST_RATE, "Interest rate must be between MIN_INTEREST_RATE and MAX_INTEREST_RATE");
require(_duration > 0, "Loan duration must be greater than 0");
require(outstanding[msg.sender] == false, "settle outstanding loan");
uint loanId = loanCount++;
uint _repaymentAmount = _amount + (_amount * _interest) / 100;
loans[loanId] = Loan({
loan_id: loanId,
amount: _amount,
interest: _interest,
duration: _duration + block.timestamp,
repaymentAmount: _repaymentAmount,
fundingDeadline: _fundingDeadline + block.timestamp,
collateralAmount: _collateralamount,
borrower: msg.sender,
lender: payable(address(0)),
collateral: _collateral,
isCollateralErc20: _isERC20,
active: true,
repaid: false
});
if (_isERC20) {
require(IERC20(_collateral).transferFrom(msg.sender, address(this), _collateralamount), "ERC20 transfer failed");
} else {
IERC721(_collateral).transferFrom(msg.sender, address(this), _collateralamount);
}
emit LoanCreated(loanId, _amount, _interest, _duration, _fundingDeadline, msg.sender, address(0));
}- Parameter Validation: Ensures loan amount and interest rate are within allowed ranges. Checks loan duration is positive and borrower has no outstanding loans.
- Repayment Calculation: Calculates the total repayment amount including interest.
- Loan Creation: Creates and stores a new loan record in the
loansmapping. - Collateral Transfer: Transfers the specified collateral amount from borrower to contract.
- Uses
IERC20.transferFromfor ERC20 tokens. - Uses
IERC721.transferFromfor ERC721 tokens.
- Uses
- Event Emission: Emits
LoanCreatedevent.
Allows a lender to fund an active loan.
function fundLoan(uint _loanId) external payable onlyActiveLoan(_loanId) {
Loan storage loan = loans[_loanId];
require(msg.sender != loan.borrower, "Borrower cannot fund their own loan");
if (block.timestamp > loan.fundingDeadline) {
loan.active = false;
revert("deadline passed");
}
payable(loan.borrower).transfer(msg.value);
loan.lender = payable(msg.sender);
outstanding[loan.borrower] = true;
emit LoanFunded(_loanId, msg.sender, msg.value);
}- Active Loan Check: Ensures the loan is active.
- Self-Funding Restriction: Prevents borrower from funding their own loan.
- Deadline Check: Verifies the current time is within the funding deadline.
- Funds Transfer: Transfers the loan amount to the borrower's address.
- Loan Update: Updates the loan's lender and marks it as funded.
- Event Emission: Emits
LoanFundedevent.
Automatically funds eligible loans using an AI-driven backend process.
function aiFundLoan(uint _loanId, address _funder) external payable onlyAiOperator onlyActiveLoan(_loanId) {
Loan storage loan = loans[_loanId];
require(_funder != loan.borrower, "Borrower cannot fund");
require(block.timestamp <= loan.fundingDeadline, "Funding deadline passed");
payable(_funder).transfer(loan.amount);
loan.lender = payable(_funder);
outstanding[loan.borrower] = true;
emit LoanFunded(_loanId, _funder, loan.amount);
}Allows the borrower to repay the loan.
function repayLoan(uint _loanId) external payable onlyActiveLoan(_loanId) onlyBorrower(_loanId) {
Loan storage loan = loans[_loanId];
require(!loan.repaid, "Loan has already been repaid");
uint interestAmount = (loan.amount * loan.interest) / 100;
uint repaymentAmount = loan.amount + interestAmount;
uint serviceFee = (repaymentAmount * SERVICE_FEE_PERCENTAGE) / 100;
uint amountAfterFee = repaymentAmount - serviceFee;
loan.lender.transfer(amountAfterFee);
payable(treasuryAddress).transfer(serviceFee);
if (loan.isCollateralErc20) {
require(IERC20(loan.collateral).transfer(msg.sender, loan.collateralAmount), "Failed to transfer ERC20 collateral");
} else {
IERC721(loan.collateral).transferFrom(address(this), msg.sender, loan.collateralAmount);
}
totalServiceCharges += serviceFee;
emit LoanRepaid(_loanId, repaymentAmount);
emit ServiceFeeDeducted(_loanId, serviceFee);
loan.repaid = true;
outstanding[loan.borrower] = false;
loan.active = false;
}- Active Loan and Borrower Check: Ensures the loan is active and the caller is the borrower.
- Repayment Validation: Ensures the loan is not already repaid.
- Repayment Calculation: Calculates total repayment amount including interest.
- Deadline Check: Verifies that the current block timestamp is within the loan's funding deadline.
- Self-Funding Restriction: Prevents borrowers from funding their own loans.
- Service Fee Deduction: Calculates and deducts the service fee from the repayment amount.
- Funds Transfer: Transfers the remaining amount to the lender and the service fee to the treasury.
- AI-Powered Trigger: This function is typically triggered by a backend AI system (via a scheduled API route using Vercel Cron) that periodically evaluates and selects high-potential loans based on parameters such as interest rate and amount.
- Collateral Return: Returns the collateral to the borrower.
- Uses
IERC20.transferfor ERC20 tokens. - Uses
IERC721.transferFromfor ERC721 tokens.
- Uses
- Service Charges Update: Accumulates the service fee.
- Event Emission: Emits
LoanRepaidandServiceFeeDeductedevents. - Loan Update: Marks the loan as repaid and inactive.
Allows the lender to claim the collateral if the loan defaults.
function claimCollateral(uint _loanId) external onlyActiveLoan(_loanId) {
Loan storage loan = loans[_loanId];
require(block.timestamp > loan.fundingDeadline && !loan.repaid, "Loan is still active or already repaid");
require(msg.sender == loan.lender, "Only the lender can claim collateral");
if (loan.isCollateralErc20) {
require(IERC20(loan.collateral).transfer(msg.sender, loan.collateralAmount), "Failed to transfer ERC20 collateral");
} else {
IERC721(loan.collateral).transferFrom(address(this), msg.sender, loan.collateralAmount);
}
loan.active = false;
defaulters[loan.borrower] += 1;
outstanding[loan.borrower] = false;
emit CollateralClaimed(_loanId, msg.sender);
}- Active Loan Check: Ensures the loan is active.
- Default Check: Verifies the loan is past its funding deadline and not repaid.
- Lender Check: Confirms that the caller is the lender.
- Collateral Transfer: Transfers the collateral to the lender.
- Uses
IERC20.transferfor ERC20 tokens. - Uses
IERC721.transferFromfor ERC721 tokens.
- Uses
- Loan Update: Marks the loan as inactive and the borrower as a
defaulter.
- Event Emission: Emits
CollateralClaimedevent.
Allows the borrower to withdraw their collateral if the loan is not funded.
function withdrawFunds(uint _loanId) external {
Loan storage loan = loans[_loanId];
require(loan.collateralAmount > 0, "loan does not exist");
require(block.timestamp > loan.fundingDeadline, "funding deadline not reached");
if (loan.isCollateralErc20) {
require(IERC20(loan.collateral).transfer(msg.sender, loan.collateralAmount), "ERC20 transfer failed");
} else {
IERC721(loan.collateral).transferFrom(address(this), msg.sender, loan.collateralAmount);
}
loan.active = false;
loan.collateralAmount = 0;
loan.borrower = address(0);
loan.collateral = address(0);
}- Collateral Existence Check: Ensures the loan has collateral.
- Funding Deadline Check: Verifies the funding deadline has passed.
- Collateral Transfer: Transfers the collateral back to the borrower.
- Uses
IERC20.transferfor ERC20 tokens. - Uses
IERC721.transferFromfor ERC721 tokens.
- Uses
- Loan Update: Marks the loan as inactive and clears the collateral details.
Fetches all loan records.
function getAllLoans() public view returns (Loan[] memory) {
Loan[] memory result = new Loan[](loanCount);
for (uint i = 0; i < loanCount; i++) {
result[i] = loans[i];
}
return result;
}- Loan Array Creation: Creates an array of all loans.
- Loan Retrieval: Iterates through the
loansmapping and populates the array. - Return: Returns the array containing all loans.
Fetches information about a specific loan.
function getLoanInfo(uint _loanId) public view returns (Loan memory) {
return loans[_loanId];
}- Return Loan: Returns the loan struct corresponding to
_loanId.
Events are emitted to allow external systems to react to contract state changes.
LoanCreated: Emitted when a loan is created.LoanFunded: Emitted when a loan is funded.LoanRepaid: Emitted when a loan is repaid.ServiceFeeDeducted: Emitted when the service fee is deducted.ServiceChargesWithdrawn: Emitted when service charges are withdrawn.CollateralClaimed: Emitted when collateral is claimed by the lender.CollateralAdded: Emitted when a new collateral is added.
event LoanCreated(uint loanId, uint amount, uint interest, uint duration, uint fundingDeadline, address borrower, address lender);
event LoanFunded(uint loanId, address lender, uint amount);
event LoanRepaid(uint loanId, uint repaymentAmount);
event ServiceFeeDeducted(uint loanId, uint serviceFee);
event ServiceChargesWithdrawn(uint amount);
event CollateralClaimed(uint loanId, address lender);
event CollateralAdded(address collateral);RootLend's token swap feature enables users to exchange tokens seamlessly within the platform, leveraging Chainlink Oracles for real-time, accurate price feeds. This ensures fair and transparent swap rates for supported tokens: RBTC, RTL (RootLend Token), USDT, and DAI. The feature is powered by a Solidity smart contract (Swapper.sol) that integrates liquidity pools, Chainlink price feeds, and secure token swapping mechanisms.
- Supported Tokens: Swap between RBTC, RTL, USDT, and DAI.
- Chainlink Oracles: Real-time price feeds ensure accurate market rates.
- Liquidity Pools: Decentralized pools maintain token balances for swaps.
- Slippage Protection: A 98% slippage tolerance prevents unfavorable trades.
- Security: Reentrancy protection and Ownable access control.
- Flexibility: Supports RBTC-to-token, token-to-RBTC, and token-to-token swaps.
- Liquidity Management: Users can add or remove liquidity to/from pools.
- Extensibility: Admins can add new tokens with custom price feeds.
The Swapper.sol contract is built with the following key components:
- Token Struct: Stores token metadata (address, decimals, name, Chainlink price feed).
- LiquidityPool Struct: Manages balances for token pairs.
- token Prices: Fallback prices for tokens (e.g., RBTC at $1755, RTL at 0.0003 RBTC).
swapTokensForTokens: Swap between ERC20 tokens.swapEthForTokens: Swap RBTC for ERC20 tokens.swapTokensForEth: Swap ERC20 tokens for RBTC.
addLiquidity: Add tokens or RBTC to a pool.removeLiquidity: Withdraw liquidity from a pool.
addToken: Add new tokens with price feeds (owner-only).
The contract uses OpenZeppelin's IERC20, Ownable, and ReentrancyGuard for secure token interactions and access control, and Chainlink's AggregatorV3Interface for price feeds.
Below is a detailed breakdown of the key components and functions in Swap.sol.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "../lib/chainlink-brownie-contracts/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "../lib/openzeppelin-contracts/contracts/access/Ownable.sol";
import "../lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol";
contract Swapper is Ownable, ReentrancyGuard {
// ...
}- Dependencies: Uses OpenZeppelin for ERC20 token interactions, ownership, and reentrancy protection; Chainlink for price feeds.
- Inheritance:
Ownablerestricts sensitive functions to the contract owner;ReentrancyGuardprevents reentrancy attacks.
struct Token {
AggregatorV3Interface tokenpricefeed;
address tokenAddress; // Address of the token (0x0 for RBTC)
uint256 decimal; // Number of decimals (18 for RBTC, USDT, DAI, RTL)
string name; // Token name (e.g., "USDT")
}
struct LiquidityPool {
uint256 token1Id; // First token ID
uint256 token2Id; // Second token ID
uint256 token1Balance; // Balance of token1
uint256 token2Balance; // Balance of token2
}- Token: Stores token details, including its Chainlink price feed and ERC20 address (0x0 for RBTC).
- LiquidityPool: Tracks token pair balances for swaps, identified by a bytes32 pair ID.
constructor() Ownable(msg.sender) {
tokens[0] = Token({
tokenpricefeed: AggregatorV3Interface(0x91f3Ff344623aDC499eC6A34fC6311e8Abbf7880),
tokenAddress: address(0),
decimal: 18,
name: "RBTC"
});
tokenPrice[0] = RBTC_PRICE_USD;
// Similar initialization for USDT, DAI, RTL
tokenCount = 4;
}- Initializes four tokens (RBTC, USDT, DAI, RTL) with their Chainlink price feeds and token prices.
- Sets
tokenCountto track the number of supported tokens.
function _calculateSwapAmount(
uint256 _fromTokenId,
uint256 _toTokenId,
uint256 _amountToSwap,
uint256 _fromDecimal,
uint256 _toDecimal
) internal view returns (uint256) {
uint256 fromPrice = tokenPrice[_fromTokenId];
uint256 toPrice = tokenPrice[_toTokenId];
require(fromPrice > 0 && toPrice > 0, "Invalid price data");
uint256 amountToReceive;
if (_fromTokenId == 3) { // RTL as fromToken
if (_toTokenId == 0) { // RTL to RBTC
amountToReceive = (_amountToSwap * fromPrice) / 10**18;
} else { // RTL to USDT/DAI
uint256 ethValue = (_amountToSwap * fromPrice) / 10**18; // RTL in RBTC
amountToReceive = (ethValue * RBTC_PRICE_USD) / toPrice; // RBTC to USD
}
} else if (_toTokenId == 3) { // USDT/DAI/RBTC to RTL
// Similar logic for RBTC to RTL and USD to RTL
} else { // RBTC/USDT/DAI to RBTC/USDT/DAI
amountToReceive = (_amountToSwap * fromPrice) / toPrice;
}
return (amountToReceive * 10**_toDecimal) / 10**_fromDecimal;
}- Calculates the output amount for a swap based on token prices.
- Handles special cases for RTL (priced in RBTC) versus other tokens (priced in USD).
- Adjusts for token decimals to ensure accurate amounts.
function swapTokensForTokens(
uint256 _fromTokenId,
uint256 _toTokenId,
uint256 _amountToSwap
) external nonReentrant returns (bool) {
require(_fromTokenId < tokenCount && _toTokenId < tokenCount, "Invalid token ID");
require(_fromTokenId != 0 && _toTokenId != 0, "Use RBTC swap functions for RBTC");
require(_amountToSwap > 0, "Invalid amount");
uint256 amountToReceive = _calculateSwapAmount(...);
uint256 minAmountOut = (amountToReceive * SLIPPAGE_TOLERANCE) / 100;
require(amountToReceive >= minAmountOut, "Slippage too high");
require(IERC20(fromToken.tokenAddress).transferFrom(...));
require(IERC20(toToken.tokenAddress).transfer(...));
emit SwapExecuted(...);
return true;
}- Validates token IDs, amounts, and liquidity.
- Applies slippage tolerance (98% of expected output).
- Transfers tokens using
transferFromand updates liquidity pool balances. - Emits a
SwapExecutedevent.
function swapEthForTokens(uint256 _tokenId) external payable nonReentrant returns (bool) {
require(_tokenId < tokenCount && _tokenId != 0, "Invalid token ID");
require(msg.value > 0, "Invalid RBTC amount");
uint256 amountToReceive = _calculateSwapAmount(...);
require(IERC20(token.tokenAddress).transfer(...));
emit SwapExecuted(...);
return true;
}- Handles RBTC-to-token swaps by accepting
msg.valueand transferring tokens. - Similar logic applies to
swapTokensForEth.
function addLiquidity(
uint256 _token1Id,
uint256 _token2Id,
uint256 _token1Amount,
uint256 _token2Amount
) external payable {
if (_token1Id != 0) {
require(IERC20(tokens[_token1Id].tokenAddress).transferFrom(...));
} else {
require(msg.value == _token1Amount, "Incorrect RBTC amount");
}
emit LiquidityAdded(...);
}- Allows users to add liquidity by transferring tokens or RBTC.
- Initializes new pools or updates existing ones.
- Similar logic for
removeLiquidity.
function addToken(
address _tokenAddress,
uint256 _decimal,
string memory _name,
AggregatorV3Interface _priceFeed
) external onlyOwner {
tokens[tokenCount] = Token({...});
tokenPrice[tokenCount] = gettokenPrice(_priceFeed);
tokenCount++;
}- Allows the owner to add new tokens with custom price feeds.
- Updates
tokenPricefor the new token.
- Wallet: A Web3 wallet (e.g., MetaMask) with RBTC and supported tokens.
- Network: Deployed on an Ethereum-compatible network (e.g., Sepolia testnet).
- Approvals: Approve the Swapper contract to spend ERC20 tokens.
- Connect Wallet: Connect your wallet to the RootLend platform.
- Select Tokens: Choose the
fromTokenandtoToken(e.g., RBTC to USDT). - Enter Amount: Specify the amount to swap.
- Approve Tokens: For token-to-token swaps, approve the contract to spend your tokens.
- Execute Swap:
- For RBTC-to-token: Call
swapEthForTokenswith RBTC. - For token-to-token: Call
swapTokensForTokens. - For token-to-RBTC: Call
swapTokensForEth.
- For RBTC-to-token: Call
- Confirm Transaction: Sign the transaction in your wallet.
- Select a token pair (e.g., RBTC/USDT).
- Specify amounts for both tokens.
- Approve tokens (if not RBTC).
- Call
addLiquiditywith the appropriate amounts.
- Select the token pair.
- Call
removeLiquidityto withdraw all liquidity.
- Reentrancy Protection:
nonReentrantmodifier prevents reentrancy attacks. - Slippage Tolerance: Ensures users receive at least 98% of the expected amount.
- Chainlink Oracles: Provides reliable price feeds (though token prices are used in this version).
- Ownable: Restricts token addition to the contract owner.
- Liquidity Checks: Prevents swaps with insufficient liquidity.