Here’s a basic implementation of a liquid restaking token in Solidity that takes a percentage of each transaction, swaps it for WETH, and adds liquidity to a Uniswap WETH pair:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
contract LiquidRestakingToken is ERC20, Ownable {
IUniswapV2Router02 public uniswapRouter;
address public uniswapPair;
address public WETH;
uint256 public transactionFee = 3; // 3% fee
uint256 public liquidityFee = 50; // 50% of fee goes to liquidity (1.5% of transaction)
uint256 public minTokensBeforeSwap = 100 * 10**18;
mapping(address => bool) private _isExcludedFromFee;
bool private inSwapAndLiquify;
event SwapAndLiquify(uint256 tokensSwapped, uint256 ethReceived, uint256 tokensIntoLiquidity);
constructor(address _router) ERC20("Liquid Restaking Token", "LRT") {
_mint(msg.sender, 1000000 * 10**18); // Mint 1,000,000 tokens
uniswapRouter = IUniswapV2Router02(_router);
WETH = uniswapRouter.WETH();
uniswapPair = IUniswapV2Factory(uniswapRouter.factory()).createPair(address(this), WETH);
_isExcludedFromFee[msg.sender] = true;
_isExcludedFromFee[address(this)] = true;
}
function _transfer(address sender, address recipient, uint256 amount) internal override {
if (_isExcludedFromFee[sender] || _isExcludedFromFee[recipient] || inSwapAndLiquify) {
super._transfer(sender, recipient, amount);
} else {
uint256 feeAmount = (amount * transactionFee) / 100;
uint256 transferAmount = amount - feeAmount;
super._transfer(sender, recipient, transferAmount);
uint256 liquidityTokens = (feeAmount * liquidityFee) / 100;
super._transfer(sender, address(this), liquidityTokens);
uint256 contractTokenBalance = balanceOf(address(this));
if (contractTokenBalance >= minTokensBeforeSwap) {
swapAndLiquify(contractTokenBalance);
}
}
}
function swapAndLiquify(uint256 contractTokenBalance) private {
inSwapAndLiquify = true;
// Split the contract token balance into halves
uint256 half = contractTokenBalance / 2;
uint256 otherHalf = contractTokenBalance - half;
// Capture the contract's current ETH balance
uint256 initialBalance = address(this).balance;
// Swap tokens for ETH
swapTokensForEth(half);
// How much ETH did we just swap into?
uint256 newBalance = address(this).balance - initialBalance;
// Add liquidity to Uniswap
addLiquidity(otherHalf, newBalance);
emit SwapAndLiquify(half, newBalance, otherHalf);
inSwapAndLiquify = false;
}
function swapTokensForEth(uint256 tokenAmount) private {
address;
path[0] = address(this);
path[1] = WETH;
_approve(address(this), address(uniswapRouter), tokenAmount);
uniswapRouter.swapExactTokensForETHSupportingFeeOnTransferTokens(
tokenAmount,
0, // accept any amount of ETH
path,
address(this),
block.timestamp
);
}
function addLiquidity(uint256 tokenAmount, uint256 ethAmount) private {
_approve(address(this), address(uniswapRouter), tokenAmount);
uniswapRouter.addLiquidityETH{value: ethAmount}(
address(this),
tokenAmount,
0, // slippage is okay
0, // slippage is okay
owner(),
block.timestamp
);
}
receive() external payable {}
function setTransactionFee(uint256 _fee) external onlyOwner {
require(_fee <= 10, "Fee can't exceed 10%");
transactionFee = _fee;
}
function setLiquidityFee(uint256 _fee) external onlyOwner {
require(_fee <= 100, "Liquidity fee must be between 0 and 100%");
liquidityFee = _fee;
}
function setMinTokensBeforeSwap(uint256 _minTokens) external onlyOwner {
minTokensBeforeSwap = _minTokens;
}
function excludeFromFee(address account, bool excluded) external onlyOwner {
_isExcludedFromFee[account] = excluded;
}
}
- Transaction Fee: A percentage of each transaction is taken as a fee.
- Liquidity Addition: A portion of the fee is swapped for WETH and added to a Uniswap liquidity pool.
- Auto Swap and Liquify: Once the contract’s token balance reaches a threshold, it swaps tokens for WETH and adds liquidity.
- This contract should be deployed on a network where Uniswap V2 is available.
- Replace
@openzeppelin/contracts
and@uniswap/v2-periphery
imports with your local paths if necessary.
- Adjust the fees and thresholds based on your requirements.
- Test thoroughly in a test environment before deploying to production.
Let me know if you need further customization!