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

auto-swap, added fee for token creation #22

Merged
merged 7 commits into from
Jul 17, 2024
Merged
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
13 changes: 12 additions & 1 deletion contracts/BNBPartyFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,25 @@ contract BNBPartyFactory is BNBPartyInternal, ReentrancyGuard {
string calldata name,
string calldata symbol
) external payable override nonReentrant returns (IERC20 newToken) {
require(
msg.value >= party.createTokenFee,
"BNBPartyFactory: insufficient BNB"
);
require(
address(BNBPositionManager) != address(0),
"BNBPartyFactory: BNBPositionManager not set"
);
// create new token
newToken = new ERC20Token(name, symbol, party.initialTokenAmount);
// create First Liquidity Pool
address liquidityPool = _createFLP(address(newToken));
if (msg.value > party.createTokenFee) {
_executeSwap(address(newToken));
}
emit StartParty(address(newToken), msg.sender, liquidityPool);
}

function handleSwap(address recipient) external override nonReentrant {
function handleSwap(address recipient) external override {
require(isParty[msg.sender], "LP is not at the party");

uint256 WBNBBalance = WBNB.balanceOf(msg.sender);
Expand Down
23 changes: 23 additions & 0 deletions contracts/BNBPartyInternal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,27 @@ abstract contract BNBPartyInternal is BNBPartyState {
// create new Liquidity Pool
_createLP(positionManager, token0, token1, amount0, amount1, party.lpFee);
}

function _executeSwap(address tokenOut) internal {
require(address(swapRouter) != address(0), "BNBPartyFactory: swapRouter not set");
uint256 amountIn = msg.value - party.createTokenFee;
// Approve the swap router to spend WBNB
WBNB.approve(address(swapRouter), amountIn);

// Prepare swap parameters
ISwapRouter.ExactInputParams memory params = ISwapRouter
.ExactInputParams({
path: abi.encodePacked(
address(WBNB),
party.partyLpFee,
tokenOut
),
recipient: msg.sender,
deadline: block.timestamp,
amountIn: amountIn,
amountOutMinimum: 0
});

swapRouter.exactInput{value: amountIn }(params);
}
}
19 changes: 15 additions & 4 deletions contracts/BNBPartyState.sol
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/access/Ownable.sol";
import "@bnb-party/v3-periphery/contracts/interfaces/ISwapRouter.sol";

import "./interfaces/INonfungiblePositionManager.sol";
import "./interfaces/IBNBParty.sol";
import "./interfaces/IBNBPartyFactory.sol";
import "./interfaces/IWBNB.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

abstract contract BNBPartyState is IBNBParty, Ownable {
abstract contract BNBPartyState is IBNBPartyFactory, Ownable {
INonfungiblePositionManager public BNBPositionManager; // BNB Party position manager
INonfungiblePositionManager public positionManager; // Default Pancakeswap V3 position manager
ISwapRouter public swapRouter; // V3 swap router
mapping(address => bool) public isParty; // LiquidityPool => isParty
mapping(address => uint256) public lpToTokenId; // LiquidityPool => nft tokenId

Expand All @@ -35,9 +38,17 @@ abstract contract BNBPartyState is IBNBParty, Ownable {
require(
_BNBPositionManager != BNBPositionManager &&
_positionManager != positionManager,
"BNBPartyState: already set"
"BNBPartyFactory: positionManager already set"
);
positionManager = _positionManager;
BNBPositionManager = _BNBPositionManager;
}

function setSwapRouter(ISwapRouter _swapRouter) external onlyOwner {
require(
_swapRouter != swapRouter,
"BNBPartyFactory: swapRouter already set"
);
swapRouter = _swapRouter;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IBNBParty {
interface IBNBPartyFactory {
/// @notice create token and initial LP
/// @param name token name
/// @param symbol token symbol
Expand Down
13 changes: 0 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

63 changes: 60 additions & 3 deletions test/BNBPartyFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ describe("BNBPartyFactory", function () {
let swapRouter: SwapRouter
let weth9: IWBNB
const partyTarget = ethers.parseEther("100")
const tokenCreationFee = ethers.parseUnits("1", 17)
const tokenCreationFee = ethers.parseUnits("1", 16)
const returnFeeAmount = ethers.parseUnits("1", 17)
const bonusFee = ethers.parseUnits("1", 16)
const initialTokenAmount = "10000000000000000000000000"
Expand Down Expand Up @@ -91,6 +91,8 @@ describe("BNBPartyFactory", function () {
await positionManager.getAddress(),
await positionManager.getAddress()
)
// Set Swap Router in BNBPartyFactory
await bnbPartyFactory.setSwapRouter(await swapRouter.getAddress())
})

beforeEach(async () => {})
Expand All @@ -110,17 +112,43 @@ describe("BNBPartyFactory", function () {
})

it("should create party LP", async function () {
await bnbPartyFactory.createParty(name, symbol)
await bnbPartyFactory.createParty(name, symbol, { value: tokenCreationFee })
expect(await positionManager.totalSupply()).to.equal(1)
})

it("bnb factory is owner of the party LP", async () => {
await bnbPartyFactory.createParty(name, symbol)
await bnbPartyFactory.createParty(name, symbol, { value: tokenCreationFee })
const tokenId = (await positionManager.totalSupply()) - 1n
const owner = await positionManager.ownerOf(tokenId)
expect(owner).to.equal(await bnbPartyFactory.getAddress())
})

it("should revert if not enough BNB is sent", async function () {
await expect(bnbPartyFactory.createParty(name, symbol, { value: tokenCreationFee - 1n })).to.be.revertedWith(
"BNBPartyFactory: insufficient BNB"
)
})

it("should revert to Create Party if position manager is not set", async function () {
await bnbPartyFactory.setNonfungiblePositionManager(ethers.ZeroAddress, ethers.ZeroAddress)
await expect(bnbPartyFactory.createParty(name, symbol, { value: tokenCreationFee })).to.be.revertedWith(
"BNBPartyFactory: BNBPositionManager not set"
)
await bnbPartyFactory.setNonfungiblePositionManager(
await positionManager.getAddress(),
await positionManager.getAddress()
)
})

it("should revert if swap router is not set", async function () {
const amountIn = ethers.parseUnits("1", 18)
await bnbPartyFactory.setSwapRouter(ethers.ZeroAddress)
await expect(bnbPartyFactory.createParty(name, symbol, { value: amountIn })).to.be.revertedWith(
"BNBPartyFactory: swapRouter not set"
)
await bnbPartyFactory.setSwapRouter(await swapRouter.getAddress())
})

describe("Smart Router", function () {
let tokenId: string
let deadline: number
Expand Down Expand Up @@ -209,6 +237,35 @@ describe("BNBPartyFactory", function () {
expect(balanceAfter).to.be.gt(balanceBefore)
})

it("execute auto-swap", async () => {
const amountIn = ethers.parseUnits("1", 17)
const tx = await bnbPartyFactory.createParty(name, symbol, { value: amountIn })
await tx.wait()
const events = await bnbPartyFactory.queryFilter(
bnbPartyFactory.filters["StartParty(address,address,address)"]
)
const tokenAddress = events[events.length - 1].args.tokenAddress
// check liquidity pool balance
const liquidityPoolBalance = await weth9.balanceOf(
await v3Factory.getPool(await weth9.getAddress(), tokenAddress, FeeAmount.HIGH)
)
expect(liquidityPoolBalance).to.be.equal(amountIn - tokenCreationFee)
})

it("Should increase user tokens with excess party fee", async () => {
const amountIn = ethers.parseUnits("1", 17)
const tx = await bnbPartyFactory.createParty(name, symbol, { value: amountIn })
await tx.wait()
const events = await bnbPartyFactory.queryFilter(
bnbPartyFactory.filters["StartParty(address,address,address)"]
)
const tokenAddress = events[events.length - 1].args.tokenAddress
const token = await ethers.getContractAt("ERC20Token", tokenAddress)

const balance = await token.balanceOf(await signers[0].getAddress())
expect(balance).to.be.gt(0)
})

function getDataHexString(token0: string, token1: string) {
return ethers.concat([
ethers.zeroPadValue(token0, 20),
Expand Down