Skip to content

Instantly share code, notes, and snippets.

@shopglobal
Created August 21, 2024 21:34
Show Gist options
  • Save shopglobal/e18d1eea6a6c7c56c3916d5cbf56f5ce to your computer and use it in GitHub Desktop.
Save shopglobal/e18d1eea6a6c7c56c3916d5cbf56f5ce to your computer and use it in GitHub Desktop.
Liquid restaking token

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;
    }
}

Key Features:

  • 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.

Deployment:

  • 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.

Important:

  • 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!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment