commit 26b65e95e23d1ab3652154019ddc12c45a0b5684 Author: gozzy Date: Sat Mar 11 16:48:49 2023 +0000 initialise diff --git a/contracts/DelegatedVesting.sol b/contracts/DelegatedVesting.sol new file mode 100755 index 0000000..4d6e292 --- /dev/null +++ b/contracts/DelegatedVesting.sol @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: Unlicense + +pragma solidity 0.8.0; + +import "./interfaces/IERC20.sol"; +import "./interfaces/IGovernance.sol"; + +contract DelegatedInstance { + + address public spender; + address public sender; + uint256 public balance; + + IGovernance governance; + IERC20 token; + + constructor( + address stakeholderAddress, + address governanceAddress, + address tokenAddress, + uint256 stakeAmount + ) { + governance = IGovernance(governanceAddress); + token = IERC20(tokenAddress); + + spender = stakeholderAddress; + balance = stakeAmount; + sender = msg.sender; + } + + function delegate(address to) public { + require(msg.sender == spender, "Incorrect spender"); + + governance.delegate(to); + } + + function lockAndDelegate( + address to, + uint256 amount, uint256 deadline, + uint8 v, bytes32 r, bytes32 s + ) external { + require(msg.sender == sender); + + token.transferFrom(msg.sender, address(this), amount); + governance.lock( + address(this), amount, deadline, v, r, s + ); + governance.delegate(to); + } + + function unlockAndRedeem() public { + require(msg.sender == sender, "Incorrect sender"); + + uint256 stake = balance; + + delete balance; + governance.unlock(stake); + token.transfer(spender, stake); + } + +} + +contract DelegatedVesting { + + uint256 public vestingPeriod; + address public vestingGovernance; + IERC20 public vestingToken; + + mapping(address => uint256) balances; + mapping(address => address) delegations; + mapping(address => uint256) commitments; + + constructor( + uint256 vestingTimestamp, + address governanceAddress, + address tokenAddress + ) { + vestingPeriod = vestingTimestamp; + vestingToken = IERC20(tokenAddress); + vestingGovernance = governanceAddress; + } + + function isActiveCommitment(address stakeholderAddress) public view returns (bool) { + uint256 commitment = commitments[stakeholderAddress]; + uint256 stake = balances[stakeholderAddress]; + + return stake > 0 && commitment > now; + } + + function isDelegatedCommitment(address stakeholderAddress) public view returns (bool) { + uint256 delegated = delegations[stakeholderAddress]; + bool state = isActiveCommitment(stakeholderAddress); + + return state && delegated != address(0x0); + } + + function isFulfilledCommitment(address stakeholderAddress) public view returns (bool) { + uint256 commitment = commitments[stakeholderAddress]; + uint256 stake = balances[stakeholderAddress]; + + return stake > 0 && commitment < now; + } + + function makeCommitment( + address recipientAddress, + uint256 amount + ) public { + require(vestingToken.transferFrom(msg.sender, address(this), amount)); + + commitments[recipientAddress] = vestingPeriod; + + if(isActiveCommitment(recipientAddress)) { + balances[recipientAddress] = balances[recipientAddress] + amount; + } else { + balances[recipientAddress] = amount; + } + } + + function delegateCommitment( + address to, + uint256 deadline, + uint8 v, bytes32 r, bytes32 s + ) public { + require(isActiveCommitment(msg.sender), "Not an active commitment"); + + if(isDelegatedCommitment(msg.sender)) { + DelegatedInstance(delegations[msg.sender]).delegate(to); + } else { + DelegatedInstance e = new DelegatedInstance( + msg.sender, + vestingGovernance, + address(vestingToken), + balances[msg.sender], + deadline, + v, r, s + ); + vestingToken.approve(address(e), balances[msg.sender]); + e.lockAndDelegate(to, balances[msg.sender], deadline, v, r, s); + delegations[msg.sender] = address(e); + } + } + + function fulfilCommitment() public { + require(isFulfilledCommitment(msg.sender), "Commitment is not possible to fulfil"); + + uint256 stake = balances[msg.sender]; + uint256 delegated = delegations[msg.sender]; + + delete balances[msg.sender]; + + if(delegated != address(0x0)){ + delete delegations[msg.sender]; + DelegatedInstance(delegated).unlockAndRedeem(); + } else { + vestingToken.transfer(msg.sender, stake); + } + } + + } diff --git a/contracts/LiquidityManagement.sol b/contracts/LiquidityManagement.sol new file mode 100755 index 0000000..9aaac38 --- /dev/null +++ b/contracts/LiquidityManagement.sol @@ -0,0 +1,83 @@ +pragma solidity 0.8.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +// https://docs.uniswap.org/contracts/v2/reference/smart-contracts/router-02 +// https://github.com/Uniswap/v2-periphery/blob/master/contracts/interfaces/IUniswapV2Router02.sol +import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol"; + +contract LiquidityManagement { + + address constant DAI_ADDRESS = 0x6B175474E89094C44Da98b954EedeAC495271d0F; + address constant TORN_ADDRESS = 0x77777FeDdddFfC19Ff86DB637967013e6C6A116C; + address constant ETH_TORN_ADDRESS = 0x0C722a487876989Af8a05FFfB6e32e45cc23FB3A; // TORN/ETH ? + address constant DAI_TORN_ADDRESS = 0xb9C6f39dB4e81DB44Cf057C7D4d8e3193745101E; + address constant UNIV2_ROUTER02_ADDRESS = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D; + address constant TORN_TREASURY = 0x5efda50f22d34F262c29268506C5Fa42cB56A1Ce; // Governance + + IERC20 DAI; + IERC20 TORN; + IERC20 UNIV2_DAI_TORN; + IERC20 UNIV2_ETH_TORN; + IUniswapV2Router02 UNIV2_ROUTER; + + constructor() { + IERC20 DAI = IERC20(DAI_ADDRESS); + IERC20 TORN = IERC20(TORN_ADDRESS); + IERC20 UNIV2_DAI_TORN = IERC20(DAI_TORN_ADDRESS); + IERC20 UNIV2_ETH_TORN = IERC20(ETH_TORN_ADDRESS); + IUniswapV2Router02 UNIV2_ROUTER = IUniswapV2Router02(UNIV2_ROUTER02_ADDRESS); + } + + // Add liquidity to the DAI/TORN and ETH/TORN pools + function addLiquidityAndWithdrawToTreasury( + uint256 amountETH, + uint256 amountDAI, + uint256 amountTORN, + uint256 slippageETH, + uint256 slippageTORN, + uint256 slippageDAI + ) public returns (bool) { + // transfer tokens from the user to the contract + require(DAI.transferFrom(msg.sender, address(this), amountDAI)); + require(TORN.transferFrom(msg.sender, address(this), amountTORN)); + require(msg.value == amountETH); + + // deadline for the transaction to be mined (10 minutes) + uint256 deploymentDeadline = block.timestamp + 10 minutes; + // Split the TORN amount in half for the two liquidity pools + uint256 amountSeedTORN = amountTORN / 2; + // configure slippage + uint256 minimumAmountDAI = amountDAI - slippageDAI; + uint256 minimumAmountETH = amountETH - slippageETH; + uint256 minimumAmountTORN = amountSeedTORN - slippageTORN; + + + // DAI/TORN + DAI.approve(UNIV2_ROUTER02_ADDRESS, amountDAI); + UNIV2_ROUTER.addLiquidity( + DAI_ADDRESS, // tokenA address + TORN_ADDRESS, // tokenB address + amountDAI, // tokenA amount + amountSeedTORN, // tokenB amount + minimumAmountDAI, // minimum tokenA amount + minimumAmountTORN, // minimum tokenB amount + TORN_TREASURY, // to + deploymentDeadline, // deadline + ); + + // ETH/TORN + TORN.approve(UNIV2_ROUTER02_ADDRESS, amountTORN); + UNIV2_ROUTER.addLiquidityETH( + TORN_ADDRESS, // token address + amountSeedTORN, // token amount + minimumAmountTORN, // minimum token amount + minimumAmountETH, // minimum eth amount + TORN_TREASURY, // to + deploymentDeadline, // deadline + ); + + return true; + } + +} diff --git a/contracts/RollingDutchAuction.sol b/contracts/RollingDutchAuction.sol new file mode 100644 index 0000000..151f575 --- /dev/null +++ b/contracts/RollingDutchAuction.sol @@ -0,0 +1,408 @@ +pragma solidity 0.8.13; + +import { UD60x18 } from "@prb/math/UD60x18.sol"; +import { IERC20 } from "@root/interfaces/IERC20.sol"; + +import { inv, add, sub, mul, exp, ln, wrap, unwrap, gte, mod, div } from "@prb/math/UD60x18.sol"; + +/* + * @title Rolling Dutch Auction + * @author Samuel JJ Gosling + * @description A dutch auction derivative with composite logarithimic decay +*/ + +contract RollingDutchAuction { + + /* @dev Address mapping for an auction's redeemable balances */ + mapping(address => mapping(bytes => bytes)) public _claims; + + /* @dev Auction mapping translating to an indexed window */ + mapping(bytes => mapping(uint256 => Window)) public _window; + + /* @dev Auction mapping for associated parameters */ + mapping(bytes => Auction) public _auctions; + + /* @dev Auction mapping for the window index */ + mapping(bytes => uint256) public _windows; + + struct Auction { + uint256 windowDuration; /* @dev Unix time window duration */ + uint256 windowTimestamp; /* @dev Unix timestamp for window start */ + uint256 startTimestamp; /* @dev Unix auction start timestamp */ + uint256 endTimestamp; /* @dev Unix auction end timestamp */ + uint256 duration; /* @dev Unix time auction duration */ + uint256 proceeds; /* @dev Auction proceeds balance */ + uint256 reserves; /* @dev Auction reserves balance */ + uint256 price; /* @dev Auction origin price */ + } + + struct Window { + bytes bidId; /* @dev Bid identifier */ + uint256 expiry; /* @dev Unix timestamp window exipration */ + uint256 price; /* @dev Window price */ + uint256 volume; /* @dev Window volume */ + bool processed; /* @dev Window fuflfillment state */ + } + + /* + * @dev Conditioner to ensure an auction is active + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier + */ + modifier activeAuction(bytes memory auctionId) { + require(remainingWindowTime(auctionId) > 0 || remainingTime(auctionId) > 0); + _; + } + + /* + * @dev Conditioner to ensure an auction is inactive + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier + */ + modifier inactiveAuction(bytes memory auctionId) { + require(remainingWindowTime(auctionId) == 0 && remainingTime(auctionId) == 0); + _; + } + + /* + * @dev Helper to view an auction's operator address + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier + */ + function operatorAddress(bytes memory auctionId) public pure returns (address opAddress) { + (opAddress,,,,) = abi.decode(auctionId, (address, address, address, uint256, bytes)); + } + + /* + * @dev Helper to view an auction's purchase token address + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Ancoded auction parameter identifier + */ + function purchaseToken(bytes memory auctionId) public pure returns (address tokenAddress) { + (,, tokenAddress,,) = abi.decode(auctionId, (address, address, address, uint256, bytes)); + } + + /* + * @dev Helper to view an auction's reserve token address + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier + */ + function reserveToken(bytes memory auctionId) public pure returns (address tokenAddress) { + (, tokenAddress,,,) = abi.decode(auctionId, (address, address, address, uint256, bytes)); + } + + /* + * @dev Helper to decode claim hash balances + * @param c͟l͟a͟i͟m͟H͟a͟s͟h͟ Encoded (uint256, uint256) values + */ + function balancesOf(bytes memory claimHash) public pure returns (uint256, uint256) { + uint256 refundBalance; + uint256 claimBalance; + + if (keccak256(claimHash) != keccak256(bytes(""))) { + (refundBalance, claimBalance) = abi.decode(claimHash, (uint256, uint256)); + } + + return (refundBalance, claimBalance); + } + + /* + * @dev Auction deployment + * @param o͟p͟e͟r͟a͟t͟o͟r͟A͟d͟r͟e͟s͟s͟ Auction management address + * @param r͟e͟s͟e͟r͟v͟e͟T͟o͟k͟e͟n͟ Auctioning token address + * @param p͟u͟r͟c͟h͟a͟s͟e͟T͟o͟k͟e͟n͟ Currency token address + * @param r͟e͟s͟e͟r͟v͟e͟A͟m͟o͟u͟n͟t͟ Auctioning token amount + * @param m͟i͟n͟i͟m͟u͟m͟P͟u͟r͟c͟h͟a͟s͟e͟A͟m͟o͟u͟n͟t͟ Minimum currency purchase amount + * @param s͟t͟a͟r͟t͟i͟n͟g͟O͟r͟i͟g͟i͟n͟P͟r͟i͟c͟e͟ Auction starting price + * @param s͟t͟a͟r͟t͟T͟i͟m͟e͟s͟t͟a͟m͟p͟ Unix timestamp auction initiation + * @param e͟n͟d͟T͟i͟m͟e͟s͟t͟a͟m͟p͟ Unix timestamp auction expiration + * @param w͟i͟n͟d͟o͟w͟D͟u͟r͟a͟t͟i͟o͟n͟ Uinx time window duration + */ + function createAuction( + address operatorAddress, + address reserveToken, + address purchaseToken, + uint256 reserveAmount, + uint256 minimumPurchaseAmount, + uint256 startingOriginPrice, + uint256 startTimestamp, + uint256 endTimestamp, + uint256 windowDuration + ) public returns (bytes memory) { + bytes memory auctionId = abi.encode( + operatorAddress, + reserveToken, + purchaseToken, + minimumPurchaseAmount, + abi.encodePacked(reserveAmount, startingPrice, startTimestamp, endTimestamp, windowDuration) + ); + + Auction storage state = _auctions[auctionId]; + + require(state.price == 0, "AUCTION EXISTS"); + + IERC20(reserveToken).transferFrom(msg.sender, address(this), reserveAmount); + + state.duration = endTimestamp - startTimestamp; + state.windowDuration = windowDuration; + state.windowTimestamp = startTimestamp; + state.startTimestamp = startTimestamp; + state.endTimestamp = endTimestamp; + state.price = startingOriginPrice; + state.reserves = reserveAmount; + + emit NewAuction(auctionId, reserveToken, reserveAmount, startingPrice, endTimestamp); + + return auctionId; + } + + /* + * @dev Helper to view an auction's minimum purchase amount + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier + */ + function minimumPurchase(bytes memory auctionId) public pure returns (uint256 minimumAmount) { + (,,, minimumAmount,) = abi.decode(auctionId, (address, address, address, uint256, bytes)); + } + + /* + * @dev Helper to view an auction's maximum order reserve amount + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier + */ + function maximumPurchase(bytes memory auctionId) public view returns (uint256) { + return unwrap(inv(scalarPrice(auctionId))); + } + + /* + * @dev Helper to view an auction's active scalar price formatted to uint256 + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier + */ + function scalarPriceUint(bytes memory auctionId) public view returns (uint256) { + return unwrap(scalarPrice(auctionId)); + } + + /* + * @dev Active price decay following time delta (x) between the current + * timestamp and the window's start timestamp or if the window is expired; time + * delta between the window's expiration. Which is the applied as the exponent + * of Euler's number and subject to the natural logarithim. Finally applied as + * as a product to the origin price (y) and substracted from itself + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier + */ + function scalarPrice(bytes memory auctionId) public view returns (UD60x18) { + Auction storage state = _auctions[auctionId]; + Window storage w = _window[auctionId][_windows[auctionId]]; + + bool isInitialised = w.expiry != 0; + bool isExpired = w.expiry < block.timestamp && isInitialised; + + uint256 timestamp = isExpired ? w.expiry : state.windowTimestamp; + + UD60x18 t = wrap(block.timestamp - timestamp); + UD60x18 t_r = wrap(state.endTimestamp - timestamp); + + UD60x18 x = div(add(t, mod(t, sub(t_r, t))), t_r); + UD60x18 y = !isInitialised ? wrap(state.price) : wrap(w.price); + + return sub(y, mul(ln(exp(x)), y)); + } + + /* + * @dev Bid submission + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier + * @param p͟r͟i͟c͟e͟ Bid order price + * @param v͟o͟l͟u͟m͟e͟ Bid order volume + */ + function commitBid(bytes memory auctionId, uint256 price, uint256 volume) + activeAuction(auctionId) + public returns (bytes memory) { + Window storage w = _window[auctionId][_windows[auctionId]]; + + require(minimumPurchase(auctionId) <= volume, "INSUFFICIENT VOLUME"); + + bool hasExpired; + + if (w.expiry != 0) { + if (remainingWindowTime(auctionId) > 0) { + if (w.price < price) { + require(w.volume <= volume, "INSUFFICIENT WINDOW VOLUME"); + } else { + require(w.price < price, "INVALID WINDOW PRICE"); + } + } else { + hasExpired = true; + } + } + + if (w.price == 0 || hasExpired) { + require(gte(wrap(price), scalarPrice(auctionId)), "INVALID CURVE PRICE"); + } + + IERC20(purchaseToken(auctionId)).transferFrom(msg.sender, address(this), volume); + + require(_auctions[auctionId].reserves >= (volume / price), "INSUFFICIENT RESERVES"); + require(maximumPurchase(auctionId) >= (volume / price), "INVALID VOLUME"); + + bytes memory bidId = abi.encode(auctionId, msg.sender, price, volume); + + (uint256 refund, uint256 claim) = balancesOf(_claims[msg.sender][auctionId]); + + _claims[msg.sender][auctionId] = abi.encode(refund + volume, claim); + + if (hasExpired) { + w = _window[auctionId][windowExpiration(auctionId)]; + } + + _auctions[auctionId].windowTimestamp = block.timestamp; + + w.expiry = block.timestamp + _auctions[auctionId].windowDuration; + w.volume = volume; + w.price = price; + w.bidId = bidId; + + emit Offer(auctionId, msg.sender, w.bidId, w.expiry); + + return bidId; + } + + /* + * @dev Expire and fulfill an auction's active window + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier + */ + function windowExpiration(bytes memory auctionId) internal returns (uint256) { + uint256 windowIndex = _windows[auctionId]; + uint256 auctionElapsedTime = elapsedTime(auctionId, block.timestamp); + uint256 auctionRemainingTime = _auctions[auctionId].duration - auctionElapsedTime; + + bytes memory winningBidId = _window[auctionId][windowIndex].bidId; + + _auctions[auctionId].endTimestamp = block.timestamp + auctionRemainingTime; + _auctions[auctionId].price = _window[auctionId][windowIndex].price; + + _windows[auctionId] = windowIndex + 1; + + fulfillWindow(auctionId, windowIndex); + + emit Expiration(auctionId, winningBidId, windowIndex); + + return windowIndex + 1; + } + + /* + * @dev Fulfill a window index even if the auction is inactive + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier + */ + function fulfillWindow(bytes memory auctionId, uint256 windowId) public { + Window storage w = _window[auctionId][windowId]; + + require(w.expiry < block.timestamp, "WINDOW UNEXPIRED"); + require(!w.processed, "WINDOW ALREADY FUFILLED"); + + (, address bidder, uint256 price, uint256 volume) = abi.decode(w.bidId, (bytes, address, uint256, uint256)); + (uint256 refund, uint256 claim) = balancesOf(_claims[bidder][auctionId]); + + delete _claims[bidder][auctionId]; + + w.processed = true; + + _auctions[auctionId].reserves -= volume / price; + _auctions[auctionId].proceeds += volume; + + _claims[bidder][auctionId] = abi.encode(refund - volume, claim + (volume / price)); + + emit Fufillment(auctionId, w.bidId, windowId); + } + + /* + * @dev Helper to view an auction's remaining duration + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier + */ + function remainingTime(bytes memory auctionId) public view returns (uint256) { + uint256 endTimestamp = _auctions[auctionId].endTimestamp; + + if (endTimestamp > block.timestamp) { + return endTimestamp - block.timestamp; + } else { + return 0; + } + } + + /* + * @dev Helper to view an auction's active remaining window duration + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier + */ + function remainingWindowTime(bytes memory auctionId) public view returns (uint256) { + uint256 expiryTimestamp = _window[auctionId][_windows[auctionId]].expiry; + + if (expiryTimestamp == 0 || block.timestamp > expiryTimestamp) { + return 0; + } else { + return expiryTimestamp - block.timestamp; + } + } + + /* + * @dev Helper to view an auction's progress in unix time + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier + */ + function elapsedTime(bytes memory auctionId, uint256 timestamp) public view returns (uint256) { + uint256 windowIndex = _windows[auctionId] + 1; + uint256 windowElapsedTime = _auctions[auctionId].windowDuration * windowIndex; + + return timestamp - _auctions[auctionId].startTimestamp - windowElapsedTime; + } + + /* + * @dev Auction management redemption + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier + */ + function withdraw(bytes memory auctionId) + inactiveAuction(auctionId) + public { + uint256 proceeds = _auctions[auctionId].proceeds; + uint256 reserves = _auctions[auctionId].reserves; + + delete _auctions[auctionId].proceeds; + delete _auctions[auctionId].reserves; + + if (proceeds > 0) { + IERC20(purchaseToken(auctionId)).transfer(operatorAddress(auctionId), proceeds); + } + if (reserves > 0) { + IERC20(reserveToken(auctionId)).transfer(operatorAddress(auctionId), reserves); + } + + emit Withdraw(auctionId); + } + + /* + * @dev Auction order and refund redemption + * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier + */ + function redeem(address bidder, bytes memory auctionId) + inactiveAuction(auctionId) + public { + bytes memory claimHash = _claims[bidder][auctionId]; + + (uint256 refund, uint256 claim) = balancesOf(claimHash); + + delete _claims[bidder][auctionId]; + + if (refund > 0) { + IERC20(purchaseToken(auctionId)).transfer(bidder, refund); + } + if (claim > 0) { + IERC20(reserveToken(auctionId)).transfer(bidder, claim); + } + + emit Claim(auctionId, claimHash); + } + + event NewAuction( + bytes indexed auctionId, address reserveToken, uint256 reserves, uint256 price, uint256 endTimestamp + ); + + event Offer(bytes indexed auctionId, address indexed owner, bytes indexed bidId, uint256 expiry); + + event Fufillment(bytes indexed auctionId, bytes indexed bidId, uint256 windowId); + + event Expiration(bytes indexed auctionId, bytes indexed bidId, uint256 windowId); + + event Claim(bytes indexed auctionId, bytes indexed bidId); + + event Withdraw(bytes indexed auctionId); +} diff --git a/contracts/interfaces/IERC20.sol b/contracts/interfaces/IERC20.sol new file mode 100755 index 0000000..a0b0405 --- /dev/null +++ b/contracts/interfaces/IERC20.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Unlicense + +pragma solidity 0.8.0; + +interface IERC20 { + + function transferFrom(address from, address to, uint256 amount) external returns (bool); + + function transfer(address to, uint256 amount) external returns (bool); + + function balanceOf(address owner) external view returns (uint256); + + function approve(address spender, uint256 amount) external; + +} diff --git a/contracts/interfaces/IGovernance.sol b/contracts/interfaces/IGovernance.sol new file mode 100755 index 0000000..8c7c2d2 --- /dev/null +++ b/contracts/interfaces/IGovernance.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Unlicense + +pragma solidity 0.8.0; + +interface IGovernance { + + function lock( + address owner, + uint256 amount, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + function delegate(address to) external; + + function undelegate() external; + + function unlock(address to) external; + +} diff --git a/contracts/interfaces/IUniswapV2Router02.sol b/contracts/interfaces/IUniswapV2Router02.sol new file mode 100755 index 0000000..4c162f1 --- /dev/null +++ b/contracts/interfaces/IUniswapV2Router02.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Unlicense + +pragma solidity >=0.6.2; + +interface IUniswapV2Router02 { + + function addLiquidity( + address tokenA, + address tokenB, + uint amountADesired, + uint amountBDesired, + uint amountAMin, + uint amountBMin, + address to, + uint deadline + ) external returns ( + uint amountA, + uint amountB, + uint liquidity + ); + + function addLiquidityETH( + address token, + uint amountTokenDesired, + uint amountTokenMin, + uint amountETHMin, + address to, + uint deadline + ) external returns ( + uint amountToken, + uint amountETH, + uint liquidity + ); + + function removeLiquidity( + address tokenA, + address tokenB, + uint liquidity, + uint amountAMin, + uint amountBMin, + address to, + uint deadline + ) external returns ( + uint amountA, + uint amountB + ); + + function removeLiquidityETH( + address token, + uint liquidity, + uint amountTokenMin, + uint amountETHMin, + address to, + uint deadline + ) external returns ( + uint amountToken, + uint amountETH + ); + +} diff --git a/hardhat.config.ts b/hardhat.config.ts new file mode 100755 index 0000000..975499c --- /dev/null +++ b/hardhat.config.ts @@ -0,0 +1,42 @@ +import "@nomiclabs/hardhat-waffle" +import "@nomiclabs/hardhat-truffle5" + +import * as dotenv from "dotenv" + +import { HardhatUserConfig } from "hardhat/types" +import { solConfig } from './utils/constants' +import { task } from "hardhat/config" + +dotenv.config({ + path: `${__dirname}/.configuration.env` +}) + +let configuration: HardhatUserConfig = { + networks: { + hardhat: { + blockGasLimit: 9500000 + } + }, + solidity: { + compilers: [ + { + version: "0.8.0", + settings: solConfig + } + ], + }, + mocha: { + timeout: 500000 + } +} + +if(process.env.NETWORK){ + configuration.networks[process.env.NETWORK] = { + url: `${process.env.RPC_ENDPOINT}`, + accounts: [ + `0x${process.env.PRIVATE_KEY}` + ] + } +} + +export default configuration diff --git a/package.json b/package.json new file mode 100755 index 0000000..0ca97b1 --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "two-fold-pol-strategy", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "npx hardhat compile", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@nomiclabs/hardhat-ethers": "^2.0.2", + "@nomiclabs/hardhat-truffle5": "^2.0.0", + "@nomiclabs/hardhat-waffle": "^2.0.1", + "@nomiclabs/hardhat-web3": "^2.0.0", + "@openzeppelin/contracts": "^4.8.1", + "@types/mocha": "^9.0.0", + "@types/node": "^16.11.2", + "@uniswap/v2-periphery": "^1.1.0-beta.0", + "ethereum-waffle": "^3.4.0", + "ethers": "^5.4.2", + "prettier": "^2.8.3", + "prettier-plugin-solidity": "^1.1.2", + "ts-node": "^10.9.1", + "typescript": "^4.5.5" + }, + "dependencies": { + "dotenv": "^16.0.0" + } +} diff --git a/test/index.ts b/test/index.ts new file mode 100755 index 0000000..0db5319 --- /dev/null +++ b/test/index.ts @@ -0,0 +1,4 @@ +import { ethers, config, web3, artifacts } from "hardhat" +import { expect } from "chai" + +import { Signer, BigNumber } from "ethers" diff --git a/tsconfig.json b/tsconfig.json new file mode 100755 index 0000000..5503bdf --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,32 @@ +{ + "files": ["./hardhat.config.ts"], + "compilerOptions": { + "target": "es2018", + "module": "commonjs", + "noImplicitAny": false, + "moduleResolution": "node", + "resolveJsonModule": true, + "esModuleInterop": true, + "typeRoots": [ + "node_modules/@types" + ], + "lib": [ + "es2018", + "dom" + ] + }, + "exclude": [ + "node_modules/", + "contracts/", + ], + "include": [ + "types.d.ts", + "hre.d.ts", + "utils/", + "tests/" + ], + "types": [ + "jest", + "node" + ] +} diff --git a/utils/constants.ts b/utils/constants.ts new file mode 100755 index 0000000..142005b --- /dev/null +++ b/utils/constants.ts @@ -0,0 +1,6 @@ +export const solConfig = { + optimizer: { + enabled: true, + runs: 200, + } + }