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
Submitted on: 2025-11-05 17:28:37
Comments
Log in to comment.
No comments yet.