DACTreasury

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/DACTreasury.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity ^0.8.24;

// Compatible with OpenZeppelin Contracts ^5.0.0
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {VestingWallet} from "@openzeppelin/contracts/finance/VestingWallet.sol";

// Uniswap V2
import {IUniswapV2Router02} from "@uniswap/contracts/interfaces/IUniswapV2Router02.sol";
import {IUniswapV2Factory} from "@uniswapcore/contracts/interfaces/IUniswapV2Factory.sol";
import {IUniswapV2Pair} from "@uniswapcore/contracts/interfaces/IUniswapV2Pair.sol";
import {IWETH} from "@uniswap/contracts/interfaces/IWETH.sol";

// Interfaces for the DAC ecosystem
import {IDACSwapper} from "./IDACSwapper.sol";
import {IDACToken} from "./IDACToken.sol";
import {IDAChronicle} from "./IDAChronicle.sol";
import {IDACAuthority} from "./IDACAuthority.sol";
import {DACAccessManaged} from "./DACAccessManaged.sol";
import {DACRewardSplitter} from "./DACRewardSplitter.sol";
import {
    IDACTreasury,
    DACTreasury__InvalidDacToken,
    DACTreasury__InvalidDaChronicle,
    DACTreasury__InvalidUniswapRouter,
    DACTreasury__InvalidUniswapFactory,
    DACTreasury__InvalidUniswapPair,
    DACTreasury__InvalidUniswapPairTokensMismatch,
    DACTreasury__InvalidProjectOpsWallet,
    DACTreasury__InvalidFounderWallet,
    DACTreasury__InvalidExchangeAddress,
    DACTreasury__InvalidAirdropAddress,
    DACTreasury__AirdropCapExceeded,
    DACTreasury__ProjectOpsCapExceeded,
    DACTreasury__FounderCapExceeded,
    DACTreasury__ExchangeCapExceeded,
    DACTreasury__InsufficientEthAmount,
    DACTreasury__DACTransferFailed,
    DACTreasury__SlippageToleranceInvalid,
    DACTreasury__RewardSplitInvalid,
    DACTreasury__DiscountPriceInvalid,
    DACTreasury__CallerDoesNotOwnNFT,
    DACTreasury__DACApprovalFailed,
    DACTreasury__LiquidityApprovalFailed,
    DACTreasury__InvalidFromTokenAddress,
    DACTreasury__InvalidToTokenAddress,
    DACTreasury__InvalidSwapperAddress,
    DACTreasury__DeadlineExpired,
    DACTreasury__TokenSwapBelowMinAmount,
    DACTreasury__InsufficientTokenBalance
} from "./IDACTreasury.sol";

contract DACTreasury is IDACTreasury, DACAccessManaged, ReentrancyGuard {
    using SafeERC20 for IERC20; // It should be for WETH and other tokens other than DAC...

    // ================================================================
    // │                           CONSTANTS                          │
    // ================================================================

    // Allocation Caps (Percentages) in basis points (1% = 100 basis points)
    uint16 public constant AIRDROP_CAP_PERCENT = 500;
    uint16 public constant PROJECT_OPS_CAP_PERCENT = 500;
    uint16 public constant EXCHANGE_LIQUIDITY_CAP_PERCENT = 500;
    uint16 public constant FOUNDER_CAP_PERCENT = 500;

    // Allocation Vesting Periods
    uint64 public constant PROJECT_OPS_VESTING_PERIOD = 365 days * 10; // 10 years
    uint64 public constant FOUNDER_VESTING_PERIOD = 365 days * 3; // 3 years
    uint64 public constant FOUNDER_CLIFF = 180 days; // 6 months

    // Total basis points used for calculations (100% = 10,000 basis points)
    uint256 public constant TOTAL_BASIS_POINTS = 10000;
    // Maximum slippage tolerance expressed in basis points (10% = 1000 basis points)
    uint256 public constant MAX_SLIPPAGE_BASIS_POINTS = 1000;

    // The highest token ID eligible to receive early adopter royalty rewards
    uint256 public constant EARLY_REWARD_TOKEN_ID_CAP = 3650;

    // ================================================================
    // │                      State variables                         │
    // ================================================================

    // ERC20 DAC Token
    IDACToken public immutable i_dacToken;
    address public immutable i_dacTokenAddress;

    // DAChronicle NFT Contract
    IDAChronicle public immutable i_daChronicle;

    // Uniswap V2 and WETH
    IUniswapV2Router02 public immutable i_uniswapRouter;
    address public immutable i_uniswapRouterAddress;
    IWETH public immutable i_weth;
    address public immutable i_wethAddress;
    IUniswapV2Pair public immutable i_uniPair;
    // Immutable flag to indicate if DAC is token0 in the pair
    bool public immutable i_isDACToken0;

    // Vesting Wallets for Project Operations and Founder
    VestingWallet public immutable i_projectOpsVestingWallet;
    VestingWallet public immutable i_founderVestingWallet;

    // Reward Splitter Contract
    DACRewardSplitter public immutable i_rewardSplitter;

    // Allocation Tracking
    uint256 public s_airdropMinted;
    uint256 public s_projectOpsMinted;
    uint256 public s_exchangeLiquidityMinted;
    uint256 public s_founderMinted;

    // Reward Splitting the split percentage that goes to the users from the NFT perspective change (initialized 20% = 2000 basis points)
    uint256 public s_rewardSplitPercentage = 2000;
    bool public s_rewardSplitEnabled = false;

    // Slippage tolerance expressed in basis points (initialized 1% = 100 basis points)
    uint256 public s_slippageToleranceBP = 100;

    // NFT Sale Prices (Can be updated by Admin)
    uint256 public s_nftSalePriceWEI;
    uint256 public s_nftPerspectivePriceDAC;
    uint256 public s_nftPerspectiveDiscountPriceDAC;

    // ================================================================
    // │                        Constructor                           │
    // ================================================================

    /**
     * @dev Constructor to initialize the DACTreasury contract.
     * @param initialAuthority The address of the initial authority managing access controls.
     * @param dacToken The address of the DAC ERC-20 token contract.
     * @param daChronicle The address of the DAChronicle ERC-721 NFT contract.
     * @param uniswapRouter The address of the Uniswap V2 Router.
     * @param nftSalePriceWEI The sale price of DAChronicle NFT in WEI.
     * @param nftPerspectivePriceDAC The price of changing DAChronicle NFT perspective in DAC tokens.
     * @param nftPerspectiveDiscountPriceDAC The discounted price of changing DAChronicle NFT perspective in DAC tokens.
     * @param projectOpsWallet The address designated for project operations funds.
     * @param founderWallet The address designated for founder allocations.
     */
    constructor(
        address initialAuthority,
        address dacToken,
        address daChronicle,
        address uniswapRouter,
        uint256 nftSalePriceWEI,
        uint256 nftPerspectivePriceDAC,
        uint256 nftPerspectiveDiscountPriceDAC,
        address projectOpsWallet,
        address founderWallet
    ) DACAccessManaged(IDACAuthority(initialAuthority)) {
        if (dacToken == address(0)) {
            revert DACTreasury__InvalidDacToken(address(0));
        }
        if (daChronicle == address(0)) {
            revert DACTreasury__InvalidDaChronicle(address(0));
        }
        if (uniswapRouter == address(0)) {
            revert DACTreasury__InvalidUniswapRouter(address(0));
        }
        if (projectOpsWallet == address(0)) {
            revert DACTreasury__InvalidProjectOpsWallet(address(0));
        }
        if (founderWallet == address(0)) {
            revert DACTreasury__InvalidFounderWallet(address(0));
        }

        i_dacToken = IDACToken(dacToken);
        i_dacTokenAddress = dacToken;
        i_daChronicle = IDAChronicle(daChronicle);
        i_uniswapRouter = IUniswapV2Router02(uniswapRouter);
        i_uniswapRouterAddress = uniswapRouter;
        i_weth = IWETH(i_uniswapRouter.WETH());
        i_wethAddress = i_uniswapRouter.WETH();

        // Get the pair address from the Uniswap Factory
        address factory = i_uniswapRouter.factory();
        if (factory == address(0)) {
            revert DACTreasury__InvalidUniswapFactory(address(0));
        }

        address uniPair = IUniswapV2Factory(factory).getPair(dacToken, i_wethAddress);
        if (uniPair == address(0)) {
            revert DACTreasury__InvalidUniswapPair(address(0));
        }

        i_uniPair = IUniswapV2Pair(uniPair);

        // Determine the order of tokens in the pair
        address token0 = i_uniPair.token0();
        if (token0 != dacToken && token0 != i_wethAddress) {
            revert DACTreasury__InvalidUniswapPairTokensMismatch(token0);
        }

        i_isDACToken0 = (token0 == dacToken);

        s_nftSalePriceWEI = nftSalePriceWEI;
        s_nftPerspectivePriceDAC = nftPerspectivePriceDAC;
        s_nftPerspectiveDiscountPriceDAC = nftPerspectiveDiscountPriceDAC;

        // Deploy Vesting Wallets
        // Project Operations: Vesting over 10 years
        i_projectOpsVestingWallet =
            new VestingWallet(projectOpsWallet, uint64(block.timestamp), PROJECT_OPS_VESTING_PERIOD);

        // Founder: Cliff of 6 months, vesting over 3 years
        i_founderVestingWallet =
            new VestingWallet(founderWallet, uint64(block.timestamp) + FOUNDER_CLIFF, FOUNDER_VESTING_PERIOD);

        // Reward Splitter
        i_rewardSplitter = new DACRewardSplitter(initialAuthority, dacToken);

        // Initialize Allocation Tracking
        s_airdropMinted = 0;
        s_projectOpsMinted = 0;
        s_exchangeLiquidityMinted = 0;
        s_founderMinted = 0;
    }

    // ================================================================
    // │                         Functions                            │
    // ================================================================

    // ================================
    // ===== Allocation Functions =====
    // ================================

    /**
     * @inheritdoc IDACTreasury
     */
    function mintAirdrop(address to, uint256 amount) external override onlyAdmin {
        if (to == address(0)) {
            revert DACTreasury__InvalidAirdropAddress(address(0));
        }
        uint256 totalSupply = getTotalSupply();
        uint256 cap = (totalSupply * AIRDROP_CAP_PERCENT) / TOTAL_BASIS_POINTS;
        if (s_airdropMinted + amount > cap) {
            revert DACTreasury__AirdropCapExceeded(s_airdropMinted, amount, cap);
        }
        s_airdropMinted += amount;
        emit AirdropMinted(to, amount);
        i_dacToken.mint(to, amount);
    }

    /**
     * @inheritdoc IDACTreasury
     */
    function mintProjectOps(uint256 amount) external override onlyAdmin {
        uint256 totalSupply = getTotalSupply();
        uint256 cap = (totalSupply * PROJECT_OPS_CAP_PERCENT) / TOTAL_BASIS_POINTS;
        if (s_projectOpsMinted + amount > cap) {
            revert DACTreasury__ProjectOpsCapExceeded(s_projectOpsMinted, amount, cap);
        }
        s_projectOpsMinted += amount;
        emit ProjectOpsMinted(address(i_projectOpsVestingWallet), amount);
        i_dacToken.mint(address(i_projectOpsVestingWallet), amount);
    }

    /**
     * @inheritdoc IDACTreasury
     */
    function mintFounder(uint256 amount) external override onlyAdmin {
        uint256 totalSupply = getTotalSupply();
        uint256 cap = (totalSupply * FOUNDER_CAP_PERCENT) / TOTAL_BASIS_POINTS;
        if (s_founderMinted + amount > cap) {
            revert DACTreasury__FounderCapExceeded(s_founderMinted, amount, cap);
        }
        s_founderMinted += amount;
        emit FounderMinted(address(i_founderVestingWallet), amount);
        i_dacToken.mint(address(i_founderVestingWallet), amount);
    }

    /**
     * @inheritdoc IDACTreasury
     */
    function mintExchange(address to, uint256 amount) external override onlyAdmin {
        if (to == address(0)) {
            revert DACTreasury__InvalidExchangeAddress(address(0));
        }
        uint256 totalSupply = getTotalSupply();
        uint256 cap = (totalSupply * EXCHANGE_LIQUIDITY_CAP_PERCENT) / TOTAL_BASIS_POINTS;
        if (s_exchangeLiquidityMinted + amount > cap) {
            revert DACTreasury__ExchangeCapExceeded(s_exchangeLiquidityMinted, amount, cap);
        }
        s_exchangeLiquidityMinted += amount;
        emit ExchangeLiquidityMinted(to, amount);
        i_dacToken.mint(to, amount);
    }

    // ================================
    // ========= NFT Functions ========
    // ================================

    /**
     * @inheritdoc IDACTreasury
     */
    function mintNFT(
        bool isSale,
        address to,
        uint256 tokenId,
        string calldata uri,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external payable override nonReentrant {
        if (isSale && msg.value < s_nftSalePriceWEI) {
            revert DACTreasury__InsufficientEthAmount(msg.sender, msg.value, s_nftSalePriceWEI);
        }

        // Verify that the tokenId is within the early royalty reward range
        address beneficiary = tokenId <= EARLY_REWARD_TOKEN_ID_CAP
            ? to
            : i_founderVestingWallet.owner();

        // Call DAChronicle's delegateSafeMint
        i_daChronicle.delegateSafeMint(isSale, to, tokenId, uri, deadline, v, r, s, beneficiary);
        emit NFTMinted(msg.sender, to, tokenId, isSale, msg.value);

        if (isSale) {
            // Wrap ETH to WETH
            i_weth.deposit{value: address(this).balance}();
        }
    }

    /**
     * @inheritdoc IDACTreasury
     */
    function updateNFTPerspectiveWithDAC(
        bool applyDiscount,
        uint256 tokenId,
        string calldata newUri,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external override nonReentrant {
        // Verify ownership of the NFT
        if (i_daChronicle.ownerOf(tokenId) != msg.sender) {
            revert DACTreasury__CallerDoesNotOwnNFT(msg.sender, tokenId);
        }

        uint256 nftSalePriceDAC = applyDiscount ? s_nftPerspectiveDiscountPriceDAC : s_nftPerspectivePriceDAC;

        // Transfer DAC tokens from user to Treasury
        bool success = i_dacToken.transferFrom(msg.sender, address(this), nftSalePriceDAC);
        if (!success) {
            revert DACTreasury__DACTransferFailed(msg.sender, address(this), nftSalePriceDAC);
        }

        // Call DAChronicle's delegateSetTokenURI
        i_daChronicle.delegateSetTokenURI(applyDiscount, tokenId, newUri, deadline, v, r, s);
        emit PerspectiveUpdatedWithDAC(msg.sender, tokenId, nftSalePriceDAC, newUri);

        // calculate the reward split and burn the remaining amount
        uint256 rewardSplitAmount =
            s_rewardSplitEnabled ? (nftSalePriceDAC * s_rewardSplitPercentage) / TOTAL_BASIS_POINTS : 0;
        uint256 burnAmount = nftSalePriceDAC - rewardSplitAmount;

        if (rewardSplitAmount > 0) {
            success = i_dacToken.transfer(address(i_rewardSplitter), rewardSplitAmount);
            if (!success) {
                revert DACTreasury__DACTransferFailed(address(this), address(i_rewardSplitter), rewardSplitAmount);
            }
            i_rewardSplitter.addToOpenReward(rewardSplitAmount);
            emit AddedToRewardSplit(rewardSplitAmount);
        }

        if (burnAmount > 0) {
            emit DACBurned(burnAmount);
            i_dacToken.burn(burnAmount);
        }
    }

    // ================================
    // ======= Agent Functions ========
    // ================================

    /**
     * @inheritdoc IDACTreasury
     */
    function provideLiquidity(uint256 amountDAC, uint256 amountOutMin, uint256 deadline)
        external
        override
        onlyLiquidityAgent
        nonReentrant
    {
        _swapDACForWETH(amountDAC, amountOutMin, deadline);
        _addLiquidity(deadline);
    }

    /**
     * @inheritdoc IDACTreasury
     */
    function buyBackAndBurn(uint256 liquidityAmount, uint256 minAmountDAC, uint256 minAmountWETH, uint256 deadline)
        external
        override
        onlyLiquidityAgent
        nonReentrant
    {
        // Remove liquidity from Uniswap
        (, uint256 amountWETHReceived) = _removeLiquidity(liquidityAmount, minAmountDAC, minAmountWETH, deadline);

        // Swap WETH back to DAC
        _swapWETHForDAC(amountWETHReceived, deadline);

        // Burn the DAC tokens
        uint256 dacTreasuryBalance = i_dacToken.balanceOf(address(this));
        emit DACBurned(dacTreasuryBalance);
        i_dacToken.burn(dacTreasuryBalance);
    }

    /**
     * @inheritdoc IDACTreasury
     */
    function swapTreasuryTokens(
        address fromToken,
        address toToken,
        uint256 amountIn,
        uint256 amountOutMin,
        uint256 deadline,
        address swapper
    ) external override onlyTreasurerAgent nonReentrant onlySwapper(swapper) {
        if (fromToken == address(0)) {
            revert DACTreasury__InvalidFromTokenAddress(address(0));
        }
        if (toToken == address(0)) {
            revert DACTreasury__InvalidToTokenAddress(address(0));
        }
        if (swapper == address(0)) {
            revert DACTreasury__InvalidSwapperAddress(address(0));
        }
        if (block.timestamp > deadline) {
            revert DACTreasury__DeadlineExpired(deadline, block.timestamp);
        }

        // Ensure treasury has enough balance of the fromToken
        uint256 treasuryBalance = IERC20(fromToken).balanceOf(address(this));
        if (treasuryBalance < amountIn) {
            revert DACTreasury__InsufficientTokenBalance(fromToken, treasuryBalance, amountIn);
        }

        // Approve the swapper contract to spend treasury's fromToken
        IERC20(fromToken).safeIncreaseAllowance(address(swapper), amountIn);

        // Swap tokens via the swapper contract
        uint256 amountOut = IDACSwapper(swapper).swapTokens(fromToken, toToken, amountIn, amountOutMin);

        // Validate the swap and ensure the target tokens are received
        if (amountOut < amountOutMin) {
            revert DACTreasury__TokenSwapBelowMinAmount(amountOut, amountOutMin);
        }

        emit TokensSwapped(swapper, fromToken, toToken, amountIn, amountOut, amountOutMin);

        // Transfer the swapped target tokens back to the treasury
        // slither-disable-next-line arbitrary-send-erc20
        IERC20(toToken).safeTransferFrom(address(swapper), address(this), amountOut);

        // Decrease the allowance of the swapper contract
        uint256 currentAllownce = IERC20(fromToken).allowance(address(this), address(swapper));
        if (currentAllownce > 0) {
            IERC20(fromToken).safeDecreaseAllowance(address(swapper), currentAllownce);
        }
    }

    // ================================
    // ======== Admin Functions =======
    // ================================

    /**
     * @inheritdoc IDACTreasury
     */
    function updateSlippageTolerance(uint256 newSlippageToleranceBP) external override onlyAdmin {
        if (newSlippageToleranceBP > MAX_SLIPPAGE_BASIS_POINTS) {
            revert DACTreasury__SlippageToleranceInvalid(newSlippageToleranceBP);
        }
        s_slippageToleranceBP = newSlippageToleranceBP;
        emit SlippageToleranceUpdated(newSlippageToleranceBP);
    }

    /**
     * @inheritdoc IDACTreasury
     */
    function updateNFTSalePriceETH(uint256 newPrice) external override onlyAdmin {
        s_nftSalePriceWEI = newPrice;
        emit NFTSalePriceWEIUpdated(newPrice);
    }

    /**
     * @inheritdoc IDACTreasury
     */
    function updateNFTSalePriceDAC(uint256 newPrice) external override onlyAdmin {
        if (newPrice < s_nftPerspectiveDiscountPriceDAC) {
            revert DACTreasury__DiscountPriceInvalid(newPrice, s_nftPerspectiveDiscountPriceDAC);
        }
        s_nftPerspectivePriceDAC = newPrice;
        emit NFTPerspectivePriceDACUpdated(newPrice);
    }

    /**
     * @inheritdoc IDACTreasury
     */
    function updateNFTSaleDiscountPriceDAC(uint256 newPrice) external override onlyAdmin {
        if (newPrice > s_nftPerspectivePriceDAC) {
            revert DACTreasury__DiscountPriceInvalid(s_nftPerspectivePriceDAC, newPrice);
        }
        s_nftPerspectiveDiscountPriceDAC = newPrice;
        emit NFTPerspectiveDiscountPriceDACUpdated(newPrice);
    }

    /**
     * @inheritdoc IDACTreasury
     */
    function updateRewardSplitPercentage(uint256 newRewardSplitPercentage) external override onlyAdmin {
        if (newRewardSplitPercentage > TOTAL_BASIS_POINTS) {
            revert DACTreasury__RewardSplitInvalid(newRewardSplitPercentage);
        }
        s_rewardSplitPercentage = newRewardSplitPercentage;
        emit RewardSplitPercentageUpdated(newRewardSplitPercentage);
    }

    /**
     * @inheritdoc IDACTreasury
     */
    function enableRewardSplit(bool enabled) external override onlyAdmin {
        s_rewardSplitEnabled = enabled;
        emit RewardSplitEnabled(enabled);
    }

    // ================================
    // ======= Helper Functions =======
    // ================================

    /**
     * @inheritdoc IDACTreasury
     */
    function getTotalSupply() public view override returns (uint256) {
        return i_dacToken.totalSupply();
    }

    /**
     * @inheritdoc IDACTreasury
     */
    function calculateSlippageTolerance(uint256 amount) public view override returns (uint256) {
        return (amount * (TOTAL_BASIS_POINTS - s_slippageToleranceBP)) / TOTAL_BASIS_POINTS;
    }

    /**
     * @notice Swaps DAC tokens for WETH using Uniswap.
     * @param amountDAC The amount of DAC tokens to mint and to swap.
     * @param amountOutMin The minimum amount of WETH to receive.
     * @param deadline The deadline timestamp for the swap.
     *
     * Emits a {SwappedDACForWETH} event.
     */
    function _swapDACForWETH(uint256 amountDAC, uint256 amountOutMin, uint256 deadline) private {
        // Initialize the liquidity path as [DAC, WETH]
        address[] memory path = new address[](2);
        path[0] = i_dacTokenAddress;
        path[1] = i_wethAddress;
        uint256 dacTreasuryBalance = i_dacToken.balanceOf(address(this));
        uint256 amountToMint = dacTreasuryBalance < amountDAC ? amountDAC - dacTreasuryBalance : 0;

        if (amountToMint > 0) {
            emit DACMinted(amountToMint);
            i_dacToken.mint(address(this), amountToMint);
        }

        // Approve Uniswap Router to spend DAC
        if (!i_dacToken.approve(i_uniswapRouterAddress, amountDAC)) {
            revert DACTreasury__DACApprovalFailed(i_uniswapRouterAddress, amountDAC);
        }

        // Swap DAC to WETH
        uint256[] memory amountsOut = i_uniswapRouter.swapExactTokensForTokens({
            amountIn: amountDAC,
            amountOutMin: amountOutMin,
            path: path,
            to: address(this),
            deadline: deadline
        });

        emit SwappedDACForWETH(amountDAC, amountOutMin, amountsOut[1]);
    }

    /**
     * @notice Swaps WETH tokens for DAC using Uniswap.
     * @param amountWETH The amount of WETH tokens to swap.
     * @param deadline The deadline timestamp for the swap.
     *
     * Emits a {SwappedWETHForDAC} event.
     */
    function _swapWETHForDAC(uint256 amountWETH, uint256 deadline) private {
        // Initialize the liquidity path as [DAC, WETH]
        address[] memory path = new address[](2);
        path[0] = i_wethAddress;
        path[1] = i_dacTokenAddress;

        // Get the estimated amount of DAC tokens for the input WETH
        uint256[] memory estimatedAmountsOut = i_uniswapRouter.getAmountsOut(amountWETH, path);
        uint256 estimatedDAC = estimatedAmountsOut[1];

        // Safely increase WETH allowance using SafeERC20
        IERC20(address(i_weth)).safeIncreaseAllowance(i_uniswapRouterAddress, amountWETH);

        // Swap WETH to DAC
        uint256[] memory amountsOut = i_uniswapRouter.swapExactTokensForTokens({
            amountIn: amountWETH,
            amountOutMin: calculateSlippageTolerance(estimatedDAC),
            path: path,
            to: address(this),
            deadline: deadline
        });

        emit SwappedWETHForDAC(amountWETH, estimatedDAC, amountsOut[1]);
    }

    /**
     * @notice Gets the required DAC amount to maintain the reserve ratio in the Uniswap pair.
     * @param availableWETH The amount of WETH tokens available to provide liquidity.
     */
    function _getRequiredDAC(uint256 availableWETH) private view returns (uint256 requiredDAC) {
        // Fetch reserves from the pair contract
        (uint112 reserve0, uint112 reserve1,) = i_uniPair.getReserves();

        uint256 reserveDAC;
        uint256 reserveWETH;

        if (i_isDACToken0) {
            reserveDAC = uint256(reserve0);
            reserveWETH = uint256(reserve1);
        } else {
            reserveDAC = uint256(reserve1);
            reserveWETH = uint256(reserve0);
        }

        // Calculate the required DAC amount to maintain the reserve ratio
        requiredDAC = (availableWETH * reserveDAC) / reserveWETH;
    }

    /**
     * @notice Adds liquidity to the Uniswap DAC/WETH pair.
     * @param deadline The deadline timestamp for adding liquidity.
     *
     * Emits a {LiquidityProvided} event.
     */
    function _addLiquidity(uint256 deadline) private {
        uint256 availableWETH = IERC20(i_wethAddress).balanceOf(address(this));
        uint256 requiredDAC = _getRequiredDAC(availableWETH);

        // Ensure the contract has enough DAC to provide liquidity
        emit DACMinted(requiredDAC);
        i_dacToken.mint(address(this), requiredDAC);

        // Approve Uniswap Router to spend DAC
        if (!i_dacToken.approve(i_uniswapRouterAddress, requiredDAC)) {
            revert DACTreasury__DACApprovalFailed(i_uniswapRouterAddress, requiredDAC);
        }

        // Safely increase WETH allowance using SafeERC20
        IERC20(i_wethAddress).safeIncreaseAllowance(i_uniswapRouterAddress, availableWETH);

        (uint256 amountDACAdded, uint256 amountETHAdded, uint256 liquidityReceived) = i_uniswapRouter.addLiquidity({
            tokenA: i_dacTokenAddress,
            tokenB: i_wethAddress,
            amountADesired: requiredDAC,
            amountBDesired: availableWETH,
            amountAMin: calculateSlippageTolerance(requiredDAC),
            amountBMin: calculateSlippageTolerance(availableWETH),
            to: address(this),
            deadline: deadline
        });

        emit LiquidityProvided(amountDACAdded, amountETHAdded, liquidityReceived);

        // Decrease the allowance for WETH allowance of the uniswap contract, if there is any...
        uint256 currentWethAllownce = IERC20(i_wethAddress).allowance(address(this), i_uniswapRouterAddress);
        if (currentWethAllownce > 0) {
            IERC20(i_wethAddress).safeDecreaseAllowance(i_uniswapRouterAddress, currentWethAllownce);
        }
    }

    /**
     * @notice Removes liquidity from the Uniswap DAC/WETH pair.
     * @param liquidityAmount The amount of liquidity tokens to remove.
     * @param minAmountDAC The minimum amount of DAC to receive from removing liquidity.
     * @param minAmountWETH The minimum amount of WETH to receive from removing liquidity.
     * @param deadline The deadline timestamp for the Uniswap transaction.
     *
     * Emits a {LiquidityRemoved} event.
     */
    function _removeLiquidity(uint256 liquidityAmount, uint256 minAmountDAC, uint256 minAmountWETH, uint256 deadline)
        private
        returns (uint256 amountDACReceived, uint256 amountWETHReceived)
    {
        // Approve Uniswap Router to spend liquidity tokens
        if (!i_uniPair.approve(i_uniswapRouterAddress, liquidityAmount)) {
            revert DACTreasury__LiquidityApprovalFailed(i_uniswapRouterAddress, liquidityAmount);
        }

        // Remove liquidity from Uniswap
        (amountDACReceived, amountWETHReceived) = i_uniswapRouter.removeLiquidity({
            tokenA: i_dacTokenAddress,
            tokenB: i_wethAddress,
            liquidity: liquidityAmount,
            amountAMin: minAmountDAC,
            amountBMin: minAmountWETH,
            to: address(this),
            deadline: deadline
        });

        emit LiquidityRemoved(amountDACReceived, amountWETHReceived, liquidityAmount);
    }

    // ================================
    // ====== Receive Functions =======
    // ================================
    // Note: We accept donations to the Treasury contract.
    // Any ETH sent to the contract will be used for liquidity operations.

    /**
     * @dev Allows the contract to receive ETH.
     */
    receive() external payable {}

    /**
     * @dev Fallback function to receive ETH.
     */
    fallback() external payable {}
}
"
    },
    "lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/finance/VestingWallet.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (finance/VestingWallet.sol)
pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";
import {SafeERC20} from "../token/ERC20/utils/SafeERC20.sol";
import {Address} from "../utils/Address.sol";
import {Context} from "../utils/Context.sol";
import {Ownable} from "../access/Ownable.sol";

/**
 * @dev A vesting wallet is an ownable contract that can receive native currency and ERC-20 tokens, and release these
 * assets to the wallet owner, also referred to as "beneficiary", according to a vesting schedule.
 *
 * Any assets transferred to this contract will follow the vesting schedule as if they were locked from the beginning.
 * Consequently, if the vesting has already started, any amount of tokens sent to this contract will (at least partly)
 * be immediately releasable.
 *
 * By setting the duration to 0, one can configure this contract to behave like an asset timelock that hold tokens for
 * a beneficiary until a specified time.
 *
 * NOTE: Since the wallet is {Ownable}, and ownership can be transferred, it is possible to sell unvested tokens.
 * Preventing this in a smart contract is difficult, considering that: 1) a beneficiary address could be a
 * counterfactually deployed contract, 2) there is likely to be a migration path for EOAs to become contracts in the
 * near future.
 *
 * NOTE: When using this contract with any token whose balance is adjusted automatically (i.e. a rebase token), make
 * sure to account the supply/balance adjustment in the vesting schedule to ensure the vested amount is as intended.
 */
contract VestingWallet is Context, Ownable {
    event EtherReleased(uint256 amount);
    event ERC20Released(address indexed token, uint256 amount);

    uint256 private _released;
    mapping(address token => uint256) private _erc20Released;
    uint64 private immutable _start;
    uint64 private immutable _duration;

    /**
     * @dev Sets the beneficiary (owner), the start timestamp and the vesting duration (in seconds) of the vesting
     * wallet.
     */
    constructor(address beneficiary, uint64 startTimestamp, uint64 durationSeconds) payable Ownable(beneficiary) {
        _start = startTimestamp;
        _duration = durationSeconds;
    }

    /**
     * @dev The contract should be able to receive Eth.
     */
    receive() external payable virtual {}

    /**
     * @dev Getter for the start timestamp.
     */
    function start() public view virtual returns (uint256) {
        return _start;
    }

    /**
     * @dev Getter for the vesting duration.
     */
    function duration() public view virtual returns (uint256) {
        return _duration;
    }

    /**
     * @dev Getter for the end timestamp.
     */
    function end() public view virtual returns (uint256) {
        return start() + duration();
    }

    /**
     * @dev Amount of eth already released
     */
    function released() public view virtual returns (uint256) {
        return _released;
    }

    /**
     * @dev Amount of token already released
     */
    function released(address token) public view virtual returns (uint256) {
        return _erc20Released[token];
    }

    /**
     * @dev Getter for the amount of releasable eth.
     */
    function releasable() public view virtual returns (uint256) {
        return vestedAmount(uint64(block.timestamp)) - released();
    }

    /**
     * @dev Getter for the amount of releasable `token` tokens. `token` should be the address of an
     * {IERC20} contract.
     */
    function releasable(address token) public view virtual returns (uint256) {
        return vestedAmount(token, uint64(block.timestamp)) - released(token);
    }

    /**
     * @dev Release the native token (ether) that have already vested.
     *
     * Emits a {EtherReleased} event.
     */
    function release() public virtual {
        uint256 amount = releasable();
        _released += amount;
        emit EtherReleased(amount);
        Address.sendValue(payable(owner()), amount);
    }

    /**
     * @dev Release the tokens that have already vested.
     *
     * Emits a {ERC20Released} event.
     */
    function release(address token) public virtual {
        uint256 amount = releasable(token);
        _erc20Released[token] += amount;
        emit ERC20Released(token, amount);
        SafeERC20.safeTransfer(IERC20(token), owner(), amount);
    }

    /**
     * @dev Calculates the amount of ether that has already vested. Default implementation is a linear vesting curve.
     */
    function vestedAmount(uint64 timestamp) public view virtual returns (uint256) {
        return _vestingSchedule(address(this).balance + released(), timestamp);
    }

    /**
     * @dev Calculates the amount of tokens that has already vested. Default implementation is a linear vesting curve.
     */
    function vestedAmount(address token, uint64 timestamp) public view virtual returns (uint256) {
        return _vestingSchedule(IERC20(token).balanceOf(address(this)) + released(token), timestamp);
    }

    /**
     * @dev Virtual implementation of the vesting formula. This returns the amount vested, as a function of time, for
     * an asset given its total historical allocation.
     */
    function _vestingSchedule(uint256 totalAllocation, uint64 timestamp) internal view virtual returns (uint256) {
        if (timestamp < start()) {
            return 0;
        } else if (timestamp >= end()) {
            return totalAllocation;
        } else {
            return (totalAllocation * (timestamp - start())) / duration();
        }
    }
}
"
    },
    "lib/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol": {
      "content": "pragma solidity >=0.6.2;

import './IUniswapV2Router01.sol';

interface IUniswapV2Router02 is IUniswapV2Router01 {
    function removeLiquidityETHSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline
    ) external returns (uint amountETH);
    function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
        address token,
        uint liquidity,
        uint amountTokenMin,
        uint amountETHMin,
        address to,
        uint deadline,
        bool approveMax, uint8 v, bytes32 r, bytes32 s
    ) external returns (uint amountETH);

    function swapExactTokensForTokensSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
    function swapExactETHForTokensSupportingFeeOnTransferTokens(
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external payable;
    function swapExactTokensForETHSupportingFeeOnTransferTokens(
        uint amountIn,
        uint amountOutMin,
        address[] calldata path,
        address to,
        uint deadline
    ) external;
}
"
    },
    "lib/v2-core/contracts/interfaces/IUniswapV2Factory.sol": {
      "content": "pragma solidity >=0.5.0;

interface IUniswapV2Factory {
    event PairCreated(address indexed token0, address indexed token1, address pair, uint);

    function feeTo() external view returns (address);
    function feeToSetter() external view returns (address);

    function getPair(address tokenA, address tokenB) external view returns (address pair);
    function allPairs(uint) external view returns (address pair);
    function allPairsLength() external view returns (uint);

    function createPair(address tokenA, address tokenB) external returns (address pair);

    function setFeeTo(address) external;
    function setFeeToSetter(address) external;
}
"
    },
    "lib/v2-core/contracts/interfaces/IUniswapV2Pair.sol": {
      "content": "pragma solidity >=0.5.0;

interface IUniswapV2Pair {
    event Approval(address indexed owner, address indexed spender, uint value);
    event Transfer(address indexed from, address indexed to, uint value);

    function name() external pure returns (string memory);
    function symbol() external pure returns (string memory);
    function decimals() external pure returns (uint8);
    function totalSupply() external view returns (uint);
    function balanceOf(address owner) external view returns (uint);
    function allowance(address owner, address spender) external view returns (uint);

    function approve(address spender, uint value) external returns (bool);
    function transfer(address to, uint value) external returns (bool);
    function transferFrom(address from, address to, uint value) external returns (bool);

    function DOMAIN_SEPARATOR() external view returns (bytes32);
    function PERMIT_TYPEHASH() external pure returns (bytes32);
    function nonces(address owner) external view returns (uint);

    function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;

    event Mint(address indexed sender, uint amount0, uint amount1);
    event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
    event Swap(
        address indexed sender,
        uint amount0In,
        uint amount1In,
        uint amount0Out,
        uint amount1Out,
        address indexed to
    );
    event Sync(uint112 reserve0, uint112 reserve1);

    function MINIMUM_LIQUIDITY() external pure returns (uint);
    function factory() external view returns (address);
    function token0() external view returns (address);
    function token1() external view returns (address);
    function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
    function price0CumulativeLast() external view returns (uint);
    function price1CumulativeLast() external view returns (uint);
    function kLast() external view returns (uint);

    function mint(address to) external returns (uint liquidity);
    function burn(address to) external returns (uint amount0, uint amount1);
    function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
    function skim(address to) external;
    function sync() external;

    function initialize(address, address) external;
}
"
    },
    "lib/v2-periphery/contracts/interfaces/IWETH.sol": {
      "content": "pragma solidity >=0.5.0;

interface IWETH {
    function deposit() external payable;
    function transfer(address to, uint value) external returns (bool);
    function withdraw(uint) external;
}
"
    },
    "src/IDACSwapper.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity ^0.8.24;

/**
 * @title IDACSwapper
 * @notice Interface for swapping tokens within the DAC Treasury, facilitating asset backing swaps for the DAC ecosystem.
 * @dev Defines the required functions for DAC Swapper contracts.
 */
interface IDACSwapper {
    /**
     * @notice Performs a token swap from `fromToken` to `toToken` and transfers the resulting tokens back to the caller (the DAC Treasury).
     * @dev The caller must have approved the contract to spend `amountIn` of `fromToken`.
     * @dev Only callable by the DAC Treasury.
     * @param fromToken The address of the ERC-20 token to swap (e.g., the treasury's token).
     * @param toToken The address of the ERC-20 token to receive after the swap.
     * @param amountIn The amount of `fromToken` to swap.
     * @param amountOutMin The minimum acceptable amount of `toToken` to receive.
     * @return amountOut The actual amount of `toToken` received from the swap.
     */
    function swapTokens(address fromToken, address toToken, uint256 amountIn, uint256 amountOutMin)
        external
        returns (uint256 amountOut);
}
"
    },
    "src/IDACToken.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity ^0.8.24;

// Compatible with OpenZeppelin Contracts ^5.0.0
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
 * @title IDACToken
 * @notice Interface for interacting with the DACToken contract, the central ERC-20 token of the DAC Ecosystem.
 * @dev Provides a function for minting new tokens, intended to be used by the contract owner, typically representing the ecosystem's treasury.
 */
interface IDACToken is IERC20 {
    /**
     * @notice Mints new tokens to a specified address.
     * @dev
     * - This function can only be called by the contract owner.
     * - The owner is expected to be the DAC Ecosystem treasury, ensuring responsible issuance of tokens.
     * - Refer to {DACAuthority} for details on ownership and access control.
     * @param to The address that will receive the newly minted tokens.
     * @param amount The amount of tokens to mint, denominated in the smallest unit of the token.
     */
    function mint(address to, uint256 amount) external;

    /**
     * @dev Destroys a `value` amount of tokens from the caller.
     *
     * See {ERC20-_burn}.
     */
    function burn(uint256 amount) external;
}
"
    },
    "src/IDAChronicle.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity ^0.8.24;

// Compatible with OpenZeppelin Contracts ^5.0.0
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";

/**
 * @title IDAChronicle
 * @dev Interface for the DAChronicle NFT contract, enabling token minting and metadata updates
 * via signatures, as defined in EIP-2612. This interface allows delegated actions on behalf of
 * the Chronicles Agent without requiring direct transactions from the Chronicles Agent account.
 */
interface IDAChronicle is IERC721 {
    /**
     * @notice Mints an ERC721 token with the specified `tokenId` and `uri` on behalf of the Chronicles Agent.
     * @dev Requires a valid signature from the Chronicles Agent for authorization.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     * - `to` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - The provided signature must be valid and signed by the Chronicles Agent.
     *
     * @param isSale Whether the mint is part of a sale.
     * @param to The address to mint the token to.
     * @param tokenId The ID of the token to mint.
     * @param uri The metadata URI for the token.
     * @param deadline The expiration time for the signature.
     * @param v Signature recovery ID.
     * @param r Signature parameter.
     * @param s Signature parameter.
     * @param beneficiary The address to receive royalties from the token.
     */
    function delegateSafeMint(
        bool isSale,
        address to,
        uint256 tokenId,
        string calldata uri,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s,
        address beneficiary
    ) external;

    /**
     * @notice Updates the metadata URI of a token on behalf of the Chronicles Agent.
     * @dev Requires a valid signature from the Chronicles Agent for authorization.
     *
     * Emits a {TokenURIUpdated} event.
     *
     * Requirements:
     * - The caller must provide a valid signature.
     * - `tokenId` must exist.
     * - `deadline` must be a timestamp in the future.
     *
     * @param applyDiscount Whether to apply a discount to the token URI update.
     * @param tokenId The ID of the token to update.
     * @param uri The new metadata URI for the token.
     * @param deadline The expiration time for the signature.
     * @param v Signature recovery ID.
     * @param r Signature parameter.
     * @param s Signature parameter.
     */
    function delegateSetTokenURI(
        bool applyDiscount,
        uint256 tokenId,
        string calldata uri,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;
}
"
    },
    "src/IDACAuthority.sol": {
      "content": "// SPDX-License-Identifier: MIT

pragma solidity ^0.8.24;

/**
 * @title IDACAuthority
 * @dev Interface for the DACAuthority contract, which defines events and functions
 * that other contracts in the DAC (Decentralized AI Chronicles) ecosystem can interact with.
 */
interface IDACAuthority {
    // ================================================================
    // │                           Events                             │
    // ================================================================

    /**
     * @dev Emitted when the admin account is updated.
     * @param previousAdmin The address of the previous admin.
     * @param newAdmin The address of the new admin.
     */
    event AdminSet(address indexed previousAdmin, address indexed newAdmin);

    /**
     * @dev Emitted when the chronicles agent account is updated.
     * @param previousChroniclesAgent The address of the previous chronicles agent.
     * @param newChroniclesAgent The address of the new chronicles agent.
     */
    event ChroniclesAgentSet(address indexed previousChroniclesAgent, address indexed newChroniclesAgent);

    /**
     * @dev Emitted when the liquidity manager agent account is updated.
     * @param previousLiquidityAgent The address of the previous liquidity manager agent.
     * @param liquidityAgent The address of the new liquidity manager agent.
     */
    event LiquidityAgentSet(address indexed previousLiquidityAgent, address indexed liquidityAgent);

    /**
     * @dev Emitted when the treasurer agent account is updated.
     * @param previousTreasurerAgent The address of the previous treasurer agent.
     * @param treasurerAgent The address of the new treasurer agent.
     */
    event TreasurerAgentSet(address indexed previousTreasurerAgent, address indexed treasurerAgent);

    /**
     * @dev Emitted when the treasury contract is updated.
     * @param previousTreasury The address of the previous treasury contract.
     * @param newTreasury The address of the new treasury contract.
     */
    event TreasurySet(address indexed previousTreasury, address indexed newTreasury);

    /**
     * @dev Emitted when a new swapper is proposed to be whitelisted.
     * @param swapper The address of the swapper proposed to be whitelisted.
     * @param activationTime The timestamp when the swapper becomes active.
     */
    event SwapperWhitelisted(address indexed swapper, uint256 activationTime);

    /**
     * @dev Emitted when a swapper is disabled.
     * @param swapper The address of the swapper that has been disabled.
     */
    event SwapperDisabled(address indexed swapper);

    // ================================================================
    // │                         Functions                            │
    // ================================================================

    /**
     * @notice Returns the address of the admin account.
     * @return The current admin address.
     */
    function admin() external view returns (address);

    /**
     * @notice Returns the address of the chronicles agent account.
     * @return The current chronicles agent address.
     */
    funct

Tags:
ERC20, ERC721, ERC165, Multisig, Mintable, Burnable, Non-Fungible, Swap, Liquidity, Voting, Timelock, Upgradeable, Multi-Signature, Factory|addr:0x1ea55022826e48c465b02ec8c4ca522b5e0aee82|verified:true|block:23733983|tx:0x67ae9ddea0f23e140c2bee2fe69cf283df1a16dbc00879e266b0c0860c3dbf9a|first_check:1762360115

Submitted on: 2025-11-05 17:28:37

Comments

Log in to comment.

No comments yet.