initialise
This commit is contained in:
commit
26b65e95e2
159
contracts/DelegatedVesting.sol
Executable file
159
contracts/DelegatedVesting.sol
Executable file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
83
contracts/LiquidityManagement.sol
Executable file
83
contracts/LiquidityManagement.sol
Executable file
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
408
contracts/RollingDutchAuction.sol
Normal file
408
contracts/RollingDutchAuction.sol
Normal file
@ -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);
|
||||||
|
}
|
15
contracts/interfaces/IERC20.sol
Executable file
15
contracts/interfaces/IERC20.sol
Executable file
@ -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;
|
||||||
|
|
||||||
|
}
|
22
contracts/interfaces/IGovernance.sol
Executable file
22
contracts/interfaces/IGovernance.sol
Executable file
@ -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;
|
||||||
|
|
||||||
|
}
|
60
contracts/interfaces/IUniswapV2Router02.sol
Executable file
60
contracts/interfaces/IUniswapV2Router02.sol
Executable file
@ -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
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
42
hardhat.config.ts
Executable file
42
hardhat.config.ts
Executable file
@ -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
|
31
package.json
Executable file
31
package.json
Executable file
@ -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"
|
||||||
|
}
|
||||||
|
}
|
4
test/index.ts
Executable file
4
test/index.ts
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
import { ethers, config, web3, artifacts } from "hardhat"
|
||||||
|
import { expect } from "chai"
|
||||||
|
|
||||||
|
import { Signer, BigNumber } from "ethers"
|
32
tsconfig.json
Executable file
32
tsconfig.json
Executable file
@ -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"
|
||||||
|
]
|
||||||
|
}
|
6
utils/constants.ts
Executable file
6
utils/constants.ts
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
export const solConfig = {
|
||||||
|
optimizer: {
|
||||||
|
enabled: true,
|
||||||
|
runs: 200,
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user