1
0
forked from mico/idle_moloch

can create a pool

This commit is contained in:
yellow 2024-10-31 16:59:36 +01:00
parent f6f3e5cadf
commit 2b6beb4b68
7 changed files with 236 additions and 10 deletions

View File

@ -4,3 +4,8 @@ out = "out"
libs = ["lib"]
# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
remappings = [
"@uniswap/v3-core/=lib/v3-core/",
"@uniswap/v3-periphery=lib/v3-periphery/",
]

View File

@ -12,7 +12,7 @@ contract RaidGeldScript is Script, Constants {
function run() public {
vm.startBroadcast();
raidgeld = new RaidGeld(DAO_TOKEN, POOL, BAAL);
raidgeld = new RaidGeld(DAO_TOKEN, POOL, BAAL, NFT_POSITION_MANAGER);
vm.stopBroadcast();
}
}

View File

@ -8,4 +8,5 @@ contract Constants {
address public constant BAAL = 0x4d5A5B4a679b10038e1677C84Cb675d10d29fFFD;
address public constant WETH = 0x4200000000000000000000000000000000000006;
address public constant SWAP_ROUTER = 0x2626664c2603336E57B271c5C0b26F421741e481;
address public constant NFT_POSITION_MANAGER = 0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1;
}

View File

@ -1,13 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IBaal} from "lib/Baal/contracts/interfaces/IBaal.sol";
import {RaidGeldUtils} from "../src/RaidGeldUtils.sol";
import {Army, Player, Raider, Boss} from "../src/RaidGeldStructs.sol";
import "../src/Constants.sol";
import { IUniswapV3Factory } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
import { IERC20, TransferHelper } from "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol";
import { INonfungiblePositionManager } from "./lib/INonfungiblePositionManager.sol";
import { CustomMath} from "./lib/CustomMath.sol";
import {RaidGeldUtils } from "../src/RaidGeldUtils.sol";
import { Army, Player, Raider, Boss } from "../src/RaidGeldStructs.sol";
import { Constants} from "../src/Constants.sol";
contract RaidGeld is ERC20, Ownable, Constants {
uint256 public constant MANTISSA = 1e4;
@ -24,6 +29,12 @@ contract RaidGeld is ERC20, Ownable, Constants {
// RGCVII token
ERC20 public daoToken;
IBaal public baal;
address public DAO;
bool poolDeployed = false;
int24 internal maxTick;
int24 internal minTick;
uint24 internal poolFee = 10000;
INonfungiblePositionManager public nonfungiblePositionManager;
// RGCVII pool
address public pool;
// Uniswap
@ -45,13 +56,27 @@ contract RaidGeld is ERC20, Ownable, Constants {
uint16 championLevel
);
event DaoTokenBuyInAmountSet(address indexed owner, uint256 oldAmount, uint256 newAmount);
event UniswapPositionCreated(
address indexed pool,
uint256 indexed positionId,
uint160 sqrtPriceX96,
uint128 liquidity,
uint256 amount0,
uint256 amount1
);
// Modifier for functions that should only be available to registered players
modifier onlyPlayer() {
require(players[msg.sender].created_at != 0, "Not an initiated player");
_;
}
// Modifier for dao to deploy the swap pool
modifier onlyDaoOnlyOnce() {
require(msg.sender == DAO && poolDeployed == false, "Not DAO");
_;
poolDeployed = true;
}
modifier onlyActiveSession() {
require(players[msg.sender].has_active_session, "Session is not active, you need to buy into the game first");
_;
@ -65,11 +90,19 @@ contract RaidGeld is ERC20, Ownable, Constants {
_;
}
constructor(address _daoToken, address _pool, address _baal) ERC20("Raid Geld", "GELD") Ownable(msg.sender) {
constructor(address _daoToken, address _pool, address _baal, address _nftPositionManager) ERC20("Raid Geld", "GELD") Ownable(msg.sender) {
daoToken = ERC20(_daoToken);
pool = _pool;
baal = IBaal(_baal);
BUY_IN_DAO_TOKEN_AMOUNT = 500 * 10 ** daoToken.decimals();
nonfungiblePositionManager = INonfungiblePositionManager(_nftPositionManager);
IUniswapV3Factory factory = IUniswapV3Factory(nonfungiblePositionManager.factory());
int24 tickSpacing = factory.feeAmountTickSpacing(poolFee);
require(tickSpacing != 0, "InvalidPoolFee");
maxTick = (887272 / tickSpacing) * tickSpacing;
minTick = -maxTick;
}
function start_game(address player) private {
@ -143,7 +176,7 @@ contract RaidGeld is ERC20, Ownable, Constants {
function performSacrifice(uint256 _baseAmount) private {
uint256 amount = _baseAmount * SACRIFICE_SHARE / MANTISSA;
address[] memory tokens = new address[](0);
baal.ragequit(address(this), amount, 0, tokens);
_ragequit(amount);
}
// Override for default number of decimals
@ -292,6 +325,80 @@ contract RaidGeld is ERC20, Ownable, Constants {
BUY_IN_DAO_TOKEN_AMOUNT = newAmount;
}
function deploySwapPool(uint256 _geldAmount, uint256 _daoTokenAmount) external onlyDaoOnlyOnce {
uint256 daoBalanceBefore = daoToken.balanceOf(address(this));
uint256 geldBalanceBefore = balanceOf(address(this));
_mint(address(this), _geldAmount);
bool isGeldFirst = address(this) < address(daoToken);
// Ensure correct order of tokens based on their addresses
(address token0, address token1, uint256 liquidityAmount0, uint256 liquidityAmount1) = isGeldFirst
? (address(this), address(daoToken), _geldAmount, _daoTokenAmount)
: (address(daoToken), address(this), _daoTokenAmount, _geldAmount);
// calculate the sqrtPriceX96
uint160 sqrtPriceX96 = CustomMath.calculateSqrtPriceX96(liquidityAmount0, liquidityAmount1);
// Create and initialize the pool if necessary
pool = nonfungiblePositionManager.createAndInitializePoolIfNecessary(token0, token1, poolFee, sqrtPriceX96);
// console.log("pool", pool);
// approve weth and shares with NonfungiblePositionManager (taken from univ3 docs)
TransferHelper.safeApprove(token0, address(nonfungiblePositionManager), liquidityAmount0);
TransferHelper.safeApprove(token1, address(nonfungiblePositionManager), liquidityAmount1);
// console.log("approve OK");
// Set up mintParams with full range for volatile token
// tick upper and lower need to be a valid tick per fee (divisible by 200 for 1%)
// position receipt NFT goes to the vault
INonfungiblePositionManager.MintParams memory mintParams = INonfungiblePositionManager.MintParams({
token0: token0,
token1: token1,
fee: poolFee,
tickLower: minTick,
tickUpper: maxTick,
amount0Desired: liquidityAmount0,
amount1Desired: liquidityAmount1,
amount0Min: 0,
amount1Min: 0,
recipient: _msgSender(), // baalVaultOnly ensures vault is the caller
deadline: block.timestamp + 15 minutes // Ensure a reasonable deadline
});
// Mint the position
(uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1) = nonfungiblePositionManager.mint(
mintParams
);
// Remove allowance and refund in both assets.
uint256 daoRefund = daoBalanceBefore - daoToken.balanceOf(address(this));
if (daoRefund > 0) {
TransferHelper.safeApprove(address(daoToken), address(nonfungiblePositionManager), 0);
_ragequit(daoRefund);
}
uint256 geldRefund = geldBalanceBefore - balanceOf(address(this));
if (geldRefund > 0) {
TransferHelper.safeApprove(address(this), address(nonfungiblePositionManager), 0);
_burn(address(this), geldRefund);
}
emit UniswapPositionCreated(pool, tokenId, sqrtPriceX96, liquidity, amount0, amount1);
}
function _ragequit(uint256 _amount) private {
address[] memory tokens = new address[](0);
baal.ragequit(address(this), _amount, 0, tokens);
}
function setDaoAddress(address _dao) external onlyOwner {
DAO = _dao;
}
receive() external payable {
revert("No plain Ether accepted, use register() function to check in :)");
}

69
src/lib/CustomMath.sol Normal file
View File

@ -0,0 +1,69 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7 <0.9.0;
import { Math } from "@openzeppelin/contracts/utils/math/Math.sol";
/**
* @title Math utility functions
* @author DAOHaus
* @notice Includes math functions to calculate prices on Uniswap V3
*/
library CustomMath {
/// @dev Scale used by Uniswap for working with Q64.96 (binary fixed-point) numbers
uint256 constant Q96 = 2 ** 96;
/**
* @notice Calculates the squeare root of provided value
* @param x value
* @return SQRT(`value`)
*/
function sqrt(uint256 x) internal pure returns (uint256) {
if (x == 0) return 0;
uint256 z = (x + 1) / 2;
uint256 y = x;
while (z < y) {
y = z;
z = (x / z + z) / 2;
}
return y;
}
/**
* @dev Calculates the sqrtPriceX96 value for Uniswap V3 pools.
*
* This function computes the square root of the price ratio between two tokens
* and adjusts it to the Uniswap V3 format, which requires the square root price
* to be scaled by 2^96. This format is used by Uniswap V3 to facilitate high-precision
* and low-cost arithmetic operations within the protocol.
*
* @param amount0 The amount of token0, where token0 is the token with a numerically lower address.
* @param amount1 The amount of token1, where token1 is the token with a numerically higher address.
*
* The price ratio is calculated as the number of units of token1 equivalent to one unit of token0,
* scaled up by 1e18 to maintain precision during the division operation.
*
* @return The square root of the price ratio, adjusted to the Uniswap V3 fixed-point format (sqrtPriceX96).
*
* Requirements:
* - Both `amount0` and `amount1` must be greater than zero to avoid division by zero errors
* and ensure meaningful price calculations.
*
*/
function calculateSqrtPriceX96(uint256 amount0, uint256 amount1) internal pure returns (uint160) {
require(amount0 > 0 && amount1 > 0, "Token amounts cannot be zero");
// Calculate the price ratio as amount1 / amount0
// Here, `amount1` is multiplied by 1e18 to retain precision after dividing by `amount0`.
uint256 priceRatio = (amount1 * 1e18) / amount0;
// Compute the square root of the price ratio.
uint256 sqrtPrice = sqrt(priceRatio);
// Adjust the square root price to the Uniswap V3 fixed-point format by scaling up by 2^96,
// then dividing by 1e9 to correct for the initial scaling by 1e18.
uint256 sqrtPriceX96 = (sqrtPrice * Q96) / 1e9;
// Return the result as a uint160, conforming to the Uniswap V3 type requirement for sqrtPriceX96.
return uint160(sqrtPriceX96);
}
}

View File

@ -0,0 +1,44 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.7 <0.9.0;
// @notice taken from RaidGuild s7 Yeet24ShamanModule
// @notice https://basescan.org/address/0x22952b522e72015B671B4715599F7522073E37c1#code
interface INonfungiblePositionManager {
struct MintParams {
address token0;
address token1;
uint24 fee;
int24 tickLower;
int24 tickUpper;
uint256 amount0Desired;
uint256 amount1Desired;
uint256 amount0Min;
uint256 amount1Min;
address recipient;
uint256 deadline;
}
function factory() external view returns (address);
function ownerOf(uint256 tokenId) external view returns (address);
/// @notice Creates a new position wrapped in a NFT
/// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized
/// a method does not exist, i.e. the pool is assumed to be initialized.
/// @param params The params necessary to mint a position, encoded as `MintParams` in calldata
/// @return tokenId The ID of the token that represents the minted position
/// @return liquidity The amount of liquidity for this position
/// @return amount0 The amount of token0
/// @return amount1 The amount of token1
function mint(
MintParams calldata params
) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
function createAndInitializePoolIfNecessary(
address token0,
address token1,
uint24 fee,
uint160 sqrtPriceX96
) external payable returns (address pool);
}

View File

@ -36,7 +36,7 @@ contract raid_geldTest is Test, Constants {
vm.deal(owner, 10 ether);
fundAccount(player1);
vm.prank(owner);
raid_geld = new RaidGeld(DAO_TOKEN, POOL, BAAL);
raid_geld = new RaidGeld(DAO_TOKEN, POOL, BAAL, NFT_POSITION_MANAGER);
raid_geld.weth().deposit{value: 5 ether}();
}