Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SwapFeeManager contract for managing Dex and Burn Fees #8

Open
wants to merge 6 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions contracts/SwapFeeManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract SwapFeeManager is Ownable {
using SafeERC20 for IERC20;

address public immutable dexFeeWallet;
address public immutable burnFeeWallet;

uint256 private constant BURN_FEE_PERCENT = 25;
uint256 private constant TOTAL_PERCENT = 100;

event FeesSplit(uint256 dexFeeAmount, uint256 burnFeeAmount);

constructor(
address _dexFeeWallet,
address _burnFeeWallet
) Ownable(_msgSender()) {
require(
_dexFeeWallet != address(0),
"dexFeeWallet must not be zero address"
);
require(
_burnFeeWallet != address(0),
"burnFeeWallet must not be zero address"
);

dexFeeWallet = _dexFeeWallet;
burnFeeWallet = _burnFeeWallet;
}

// Function to receive Ether
receive() external payable {}

/**
* @dev Splits the Ether balance in the contract into 75% for dexFeeWallet and 25% for burnFeeWallet.
*/
function splitAndWithdraw() external onlyOwner {
uint256 totalBalance = address(this).balance;
require(totalBalance > 0, "No fees to split");

uint256 burnFeeAmount = (totalBalance * BURN_FEE_PERCENT) /
TOTAL_PERCENT;
uint256 dexFeeAmount = totalBalance - burnFeeAmount;

emit FeesSplit(dexFeeAmount, burnFeeAmount);

payable(dexFeeWallet).transfer(dexFeeAmount);
payable(burnFeeWallet).transfer(burnFeeAmount);
}

/**
* @dev Splits and withdraws ERC20 token balance.
* @param tokenAddress Address of the ERC20 token.
*/
function splitAndWithdrawToken(address tokenAddress) external onlyOwner {
require(tokenAddress != address(0), "Token address must not be zero");
IERC20 token = IERC20(tokenAddress);

uint256 totalBalance = token.balanceOf(address(this));
require(totalBalance > 0, "No token fees to split");

uint256 burnFeeAmount = (totalBalance * BURN_FEE_PERCENT) /
TOTAL_PERCENT;
uint256 dexFeeAmount = totalBalance - burnFeeAmount;

emit FeesSplit(dexFeeAmount, burnFeeAmount);

token.safeTransfer(dexFeeWallet, dexFeeAmount);
token.safeTransfer(burnFeeWallet, burnFeeAmount);
}
}
90 changes: 90 additions & 0 deletions test/SwapFeeManagerTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
const { expect } = require("chai");
const { ethers } = require("hardhat");
require('chai').use(require('chai-as-promised')).should();

describe("SwapFeeManager", function () {
beforeEach(async function () {
// Resets the Hardhat Network to its initial state
await network.provider.send("hardhat_reset");
accounts = await ethers.getSigners();

// Set balances for dexFeeWallet and burnFeeWallet to 0
await network.provider.send("hardhat_setBalance", [accounts[2].address, "0x0"]);
await network.provider.send("hardhat_setBalance", [accounts[3].address, "0x0"]);

SwapFeeManager = await ethers.getContractFactory("SwapFeeManager");
swapFeeManager = await SwapFeeManager.deploy(
accounts[2].address, // dexFeeWallet
accounts[3].address // burnFeeWallet
);
await swapFeeManager.waitForDeployment();

Token = await ethers.getContractFactory("Token");
token = await Token.deploy();
await token.waitForDeployment();

await token.transfer(accounts[1].address, ethers.parseEther("100"));
});

it("should correctly split and withdraw Ether fees", async function () {
// Send Ether to the SwapFeeManager contract
await accounts[1].sendTransaction({
to: swapFeeManager.target,
value: ethers.parseEther("1"),
});

const managerBalance = await ethers.provider.getBalance(swapFeeManager.target);
expect(managerBalance).to.equal(ethers.parseEther("1"));

await swapFeeManager.connect(accounts[0]).splitAndWithdraw().should.be.fulfilled;

const dexFeeBalance = await ethers.provider.getBalance(accounts[2].address);
const burnFeeBalance = await ethers.provider.getBalance(accounts[3].address);

expect(dexFeeBalance).to.equal(ethers.parseEther("0.75"));
expect(burnFeeBalance).to.equal(ethers.parseEther("0.25"));

// Ensure the fee manager contract's Ether balance is now zero
const managerBalanceAfter = await ethers.provider.getBalance(swapFeeManager.target);
expect(managerBalanceAfter).to.equal(ethers.parseEther("0"));
});

it("should correctly split and withdraw ERC20 token fees", async function () {
// Approve and transfer tokens to the SwapFeeManager contract
await token.connect(accounts[1]).approve(swapFeeManager.target, ethers.parseEther("1"));
await token.connect(accounts[1]).transfer(swapFeeManager.target, ethers.parseEther("1"));

const managerTokenBalance = await token.balanceOf(swapFeeManager.target);
expect(managerTokenBalance).to.equal(ethers.parseEther("1"));

await swapFeeManager.connect(accounts[0]).splitAndWithdrawToken(token.target).should.be.fulfilled;

const dexFeeTokenBalance = await token.balanceOf(accounts[2].address);
const burnFeeTokenBalance = await token.balanceOf(accounts[3].address);

expect(dexFeeTokenBalance).to.equal(ethers.parseEther("0.75"));
expect(burnFeeTokenBalance).to.equal(ethers.parseEther("0.25"));

// Ensure the fee manager contract's token balance is now zero
const managerTokenBalanceAfter = await token.balanceOf(swapFeeManager.target);
expect(managerTokenBalanceAfter).to.equal(ethers.parseEther("0"));
});

it("should not allow non-owner to split and withdraw Ether fees", async function () {
await accounts[1].sendTransaction({
to: swapFeeManager.target,
value: ethers.parseEther("1"),
});

// Attempt to call splitAndWithdraw as a non-owner
await swapFeeManager.connect(accounts[1]).splitAndWithdraw().should.be.rejectedWith("OwnableUnauthorizedAccount");
});

it("should not allow non-owner to split and withdraw ERC20 token fees", async function () {
await token.connect(accounts[1]).approve(swapFeeManager.target, ethers.parseEther("1"));
await token.connect(accounts[1]).transfer(swapFeeManager.target, ethers.parseEther("1"));

// Attempt to call splitAndWithdrawToken as a non-owner
await swapFeeManager.connect(accounts[1]).splitAndWithdrawToken(token.target).should.be.rejectedWith("OwnableUnauthorizedAccount");
});
});