redact vesting expiry to auction configuration

This commit is contained in:
gozzy 2023-03-20 20:18:36 +00:00
parent 228831255e
commit 2969ace51b
9 changed files with 147 additions and 141 deletions

View File

@ -1,4 +1,4 @@
pragma solidity 0.8.0; pragma solidity 0.8.13;
import "@openzeppelin/token/ERC20/IERC20.sol"; import "@openzeppelin/token/ERC20/IERC20.sol";
import "@interfaces/IStaking.sol"; import "@interfaces/IStaking.sol";
@ -11,142 +11,142 @@ contract DelegatedInstance {
uint256 public _balance; uint256 public _balance;
address public _governanceAddress; address public _governanceAddress;
address public _stakingAddress;
address public _tokenAddress; address public _tokenAddress;
constructor( constructor(
address stakeholderAddress, address stakeholderAddress,
address governanceAddress, address governanceAddress,
address tokenAddress, address tokenAddress,
uint256 stakeAmount uint256 stakeAmount
) { ) {
_stakingAddress = IGovernance(governanceAddress).Staking(); _stakingAddress = IGovernance(governanceAddress).Staking();
_governanceAddress = governanceAddress; _governanceAddress = governanceAddress;
_tokenAddress = tokenAddress; _tokenAddress = tokenAddress;
_spender = stakeholderAddress; _spender = stakeholderAddress;
_balance = stakeAmount; _balance = stakeAmount;
_sender = msg.sender; _sender = msg.sender;
} }
modifier isSpender() { modifier isSpender() {
require(msg.sender == _spender, "INVALID SPENDER"); require(msg.sender == _spender, "INVALID SPENDER");
_; _;
} }
function delegate(address to) public isSpender { function delegate(address to) public isSpender {
IGovernance(_governanceAddress).delegate(to); IGovernance(_governanceAddress).delegate(to);
} }
function lockAndDelegate( function lockAndDelegate(
address to, address to,
uint256 amount, uint256 amount,
uint256 deadline, uint256 deadline,
uint8 v, uint8 v,
bytes32 r, bytes32 r,
bytes32 s bytes32 s
) external isSpender { ) external isSpender {
IERC20(_tokenAddress).transferFrom(msg.sender, address(this), amount); IERC20(_tokenAddress).transferFrom(msg.sender, address(this), amount);
IERC20(_tokenAddress).approve(_governanceAddress, amount); IERC20(_tokenAddress).approve(_governanceAddress, amount);
IGovernance(_governanceAddress).lock(address(this), amount, deadline, v, r, s); IGovernance(_governanceAddress).lock(address(this), amount, deadline, v, r, s);
IGovernance(_governanceAddress).delegate(to); IGovernance(_governanceAddress).delegate(to);
} }
function unlockAndRedeem() public { function unlockAndRedeem() public {
require(msg.sender == _sender, "INVALID SENDER"); require(msg.sender == _sender, "INVALID SENDER");
uint256 reward = IStaking(_stakingAddress).checkReward(); uint256 stake = _balance;
uint256 stake = _balance; uint256 reward = IStaking(_stakingAddress).checkReward(address(this));
delete _balance; delete _balance;
IGovernance(_governanceAddress).unlock(stake); IGovernance(_governanceAddress).unlock(stake);
IStaking(_stakingAddress).getReward(); IStaking(_stakingAddress).getReward();
IERC20(_tokenAddress).transfer(_spender, stake + reward); IERC20(_tokenAddress).transfer(_spender, stake + reward);
} }
} }
contract DelegatedVesting { contract DelegatedVesting {
uint256 public _period;
address public _governanceAddress;
address public _tokenAddress; address public _tokenAddress;
address public _governanceAddress;
mapping(address => uint256) _balances; mapping(address => uint256) _balances;
mapping(address => address) _instances; mapping(address => address) _instances;
mapping(address => uint256) _commitments; mapping(address => uint256) _commitments;
constructor( constructor(
uint256 vestingDuration, address governanceAddress,
address governanceAddress, address tokenAddress
address tokenAddress
) { ) {
_period = vestingDuration; _tokenAddress = tokenAddress;
_tokenAddress = tokenAddress; _governanceAddress = governanceAddress;
_governanceAddress = governanceAddress;
} }
function isActiveCommitment(address stakeholder) public view returns (bool) { function isActiveCommitment(address stakeholder) public view returns (bool) {
return _balances[stakeholder] > 0 && _commitments[stakeholder] > block.timestamp; return _balances[stakeholder] > 0 && _commitments[stakeholder] > block.timestamp;
} }
function isDelegatedCommitment(address stakeholder) public view returns (bool) { function isDelegatedCommitment(address stakeholder) public view returns (bool) {
return isActiveCommitment(stakeholder) && _instances[stakeholder] != address(0x0); return isActiveCommitment(stakeholder) && _instances[stakeholder] != address(0x0);
} }
function isFulfilledCommitment(address stakeholder) public view returns (bool) { function isFulfilledCommitment(address stakeholder) public view returns (bool) {
return _balances[stakeholder] > 0 && _commitments[stakeholder] < block.timestamp; return _balances[stakeholder] > 0 && _commitments[stakeholder] < block.timestamp;
} }
function makeCommitment(address stakeholder, uint256 amount) public { function makeCommitment(address stakeholder, uint256 amount, uint256 expiry) public {
require(IERC20(_tokenAddress).transferFrom(msg.sender, address(this), amount)); require(block.timestamp < expiry, "INVALID EXPIRY TIMESTAMP");
require(_commitments[stakeholder] == 0, "ACTIVE VESTING");
_commitments[stakeholder] = block.timestmap + _period; IERC20(_tokenAddress).transferFrom(msg.sender, address(this), amount);
_balances[stakeholder] += amount;
_commitments[stakeholder] = expiry;
_balances[stakeholder] += amount;
} }
function delegateCommitment( function delegateCommitment(
address delegateAddress, address delegateAddress,
uint256 deadline, uint256 deadline,
uint8 v, uint8 v,
bytes32 r, bytes32 r,
bytes32 s bytes32 s
) public { ) public {
require(isActiveCommitment(msg.sender), "INVALID COMMITMENT"); require(isActiveCommitment(msg.sender), "INVALID COMMITMENT");
if(isDelegatedCommitment(msg.sender)) { if(isDelegatedCommitment(msg.sender)) {
DelegatedInstance(_instance[msg.sender]).delegate(delegateAddress); DelegatedInstance(_instances[msg.sender]).delegate(delegateAddress);
} else { } else {
DelegatedInstance e = new DelegatedInstance( DelegatedInstance e = new DelegatedInstance(
msg.sender, msg.sender,
_governanceAddress, _governanceAddress,
_tokenAddress, _tokenAddress,
_balances[msg.sender], _balances[msg.sender]
deadline, );
v, r, s
);
_instances[msg.sender] = address(e); _instances[msg.sender] = address(e);
IERC20(_tokenAddress).approve(address(e), _balances[msg.sender]); IERC20(_tokenAddress).approve(address(e), _balances[msg.sender]);
e.lockAndDelegate(delegateAddress, _balances[msg.sender], deadline, v, r, s); e.lockAndDelegate(delegateAddress, _balances[msg.sender], deadline, v, r, s);
} }
} }
function fulfillCommitment() public { function fulfillCommitment() public {
require(isFulfilledCommitment(msg.sender), "INVALID FULFILLMENT"); require(isFulfilledCommitment(msg.sender), "INVALID FULFILLMENT");
uint256 stake = _balances[msg.sender]; uint256 stake = _balances[msg.sender];
address delegated = _instances[msg.sender]; address delegated = _instances[msg.sender];
delete _balances[msg.sender]; delete _commitments[msg.sender];
delete _instances[msg.sender]; delete _balances[msg.sender];
delete _instances[msg.sender];
if(delegated != address(0x0)){ if(delegated != address(0x0)){
DelegatedInstance(delegated).unlockAndRedeem(); DelegatedInstance(delegated).unlockAndRedeem();
} else { } else {
IERC20(_tokenAddress).transfer(msg.sender, stake); IERC20(_tokenAddress).transfer(msg.sender, stake);
} }
} }
} }

View File

@ -1,36 +1,40 @@
pragma solidity 0.8.0; pragma solidity 0.8.13;
import "@openzeppelin/token/ERC20/IERC20.sol"; import "@openzeppelin/token/ERC20/IERC20.sol";
import "@interfaces/IRDA.sol"; import "@root/DelegatedVesting.sol";
import "@root/RDA.sol";
contract Proposal { contract Proposal {
address tokenAddress = 0x77777FeDdddFfC19Ff86DB637967013e6C6A116C;
address wethAddress = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
address governanceAddress = 0x77777FeDdddFfC19Ff86DB637967013e6C6A116C;
address vestingAddress = address(new DelegatedVesting(tokenAddress, governanceAddress));
address auctionAddress = address(new RDA());
uint256 VESTING_PERIOD = 10 weeks;
uint256 AUCTION_START_TS = block.timestamp;
uint256 AUCTION_END_TS = AUCTION_START_TS + 1 weeks;
uint256 AUCTION_ORIGIN_PRICE = 4172000 gwei;
uint256 AUCTION_RESERVE_AMOUNT = 100000 ether;
uint256 AUCTION_MINIMUM_AMOUNT = 1 ether;
uint256 AUCTION_WINDOW_LENGTH = 8 hours;
function executeProposal() external { function executeProposal() external {
uint256 AUCTION_START_TS = block.timestamp; RDA(auctionAddress).createAuction(
uint256 AUCTION_END_TS = AUCTION_START_TS + 1 week; vestingAddress,
uint256 AUCTION_ORIGIN_PRICE = 4172000 gwei; governanceAddress,
uint256 AUCTION_RESERVE_AMOUNT = 100000 ether; tokenAddress,
uint256 AUCTION_MINIMUM_AMOUNT = 1 ether; wethAddress,
uint256 AUCTION_WINDOW_LENGTH = 8 hours; AUCTION_RESERVE_AMOUNT,
AUCTION_MINIMUM_AMOUNT,
address wethAddress; AUCTION_ORIGIN_PRICE,
address tokenAddress; AUCTION_START_TS,
address governanceAddress; AUCTION_END_TS,
address auctionAddress; AUCTION_WINDOW_LENGTH,
address vestingAddress; VESTING_PERIOD
);
IRDA(auctionAddress).createAuction(
vestingAddress,
governanceAddress,
tokenAddress,
wethAddress,
AUCTION_RESERVE_AMOUNT,
AUCTION_MINIMUM_AMOUNT,
AUCTION_ORIGIN_PRICE,
AUCTION_START_TS,
AUCTION_END_TS,
AUCTION_WINDOW_LENGTH
);
} }
} }

View File

@ -3,8 +3,8 @@ pragma solidity 0.8.13;
import { UD60x18 } from "@prb/math/UD60x18.sol"; import { UD60x18 } from "@prb/math/UD60x18.sol";
import { IRDA } from "@interfaces/IRDA.sol"; import { IRDA } from "@interfaces/IRDA.sol";
import { IVesting } from "@interfaces/IVesting.sol";
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol"; import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
import { IDelegatedVesting } from "@interfaces/IDelegatedVesting.sol";
import { add, sub, mul, wrap, unwrap, gt, mod, div } from "@prb/math/UD60x18.sol"; import { add, sub, mul, wrap, unwrap, gt, mod, div } from "@prb/math/UD60x18.sol";
@ -28,6 +28,8 @@ contract RDA is IRDA {
/* @dev Auction mapping for the window index */ /* @dev Auction mapping for the window index */
mapping(bytes => uint256) public _windows; mapping(bytes => uint256) public _windows;
mapping(bytes => Vesting) public _vesting;
struct Auction { struct Auction {
uint256 windowDuration; /* @dev Unix time window duration */ uint256 windowDuration; /* @dev Unix time window duration */
uint256 windowTimestamp; /* @dev Unix timestamp for window start */ uint256 windowTimestamp; /* @dev Unix timestamp for window start */
@ -47,6 +49,11 @@ contract RDA is IRDA {
bool processed; /* @dev Window fuflfillment state */ bool processed; /* @dev Window fuflfillment state */
} }
struct Vesting {
address instance;
uint256 period;
}
/* /*
* @dev Conditioner to ensure an auction is active * @dev Conditioner to ensure an auction is active
* @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier * @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier
@ -126,7 +133,8 @@ contract RDA is IRDA {
uint256 startingOriginPrice, uint256 startingOriginPrice,
uint256 startTimestamp, uint256 startTimestamp,
uint256 endTimestamp, uint256 endTimestamp,
uint256 windowDuration uint256 windowDuration,
uint256 vestingDuration
) external returns (bytes memory) { ) external returns (bytes memory) {
bytes memory auctionId = abi.encode( bytes memory auctionId = abi.encode(
operatorAddress, operatorAddress,
@ -142,6 +150,9 @@ contract RDA is IRDA {
revert AuctionExists(); revert AuctionExists();
} }
_vesting[auctionId].instance = vestingAddress;
_vesting[auctionId].period = vestingDuration;
IERC20(reserveToken).transferFrom(msg.sender, address(this), reserveAmount); IERC20(reserveToken).transferFrom(msg.sender, address(this), reserveAmount);
state.duration = endTimestamp - startTimestamp; state.duration = endTimestamp - startTimestamp;
@ -151,7 +162,6 @@ contract RDA is IRDA {
state.endTimestamp = endTimestamp; state.endTimestamp = endTimestamp;
state.reserves = reserveAmount; state.reserves = reserveAmount;
state.price = startingOriginPrice; state.price = startingOriginPrice;
state.vesting = vestingAddress;
emit NewAuction(auctionId, reserveToken, reserveAmount, startingOriginPrice, endTimestamp); emit NewAuction(auctionId, reserveToken, reserveAmount, startingOriginPrice, endTimestamp);
@ -392,17 +402,20 @@ contract RDA is IRDA {
inactiveAuction(auctionId) inactiveAuction(auctionId)
external { external {
bytes memory claimHash = _claims[bidder][auctionId]; bytes memory claimHash = _claims[bidder][auctionId];
address vestingAddress = _auctions[auctionId].vesting;
delete _claims[bidder][auctionId];
(uint256 refund, uint256 claim) = balancesOf(claimHash); (uint256 refund, uint256 claim) = balancesOf(claimHash);
delete _claims[bidder][auctionId]; uint256 vestingTimestamp = block.timestamp + _vesting[auctionId].period;
address vestingAddress = _vesting[auctionId].instance;
if (refund > 0) { if (refund > 0) {
IERC20(purchaseToken(auctionId)).transfer(bidder, refund); IERC20(purchaseToken(auctionId)).transfer(bidder, refund);
} }
if (claim > 0) { if (claim > 0) {
IVesting(vestingAddress).makeCommitment(bidder, claim); IERC20(reserveToken(auctionId)).approve(vestingAddress, claim);
IDelegatedVesting(vestingAddress).makeCommitment(bidder, claim, vestingTimestamp);
} }
emit Claim(auctionId, claimHash); emit Claim(auctionId, claimHash);

View File

@ -0,0 +1,7 @@
pragma solidity 0.8.13;
interface IDelegatedVesting {
function makeCommitment(address stakeholder, uint256 amount, uint256 expiry) external;
}

View File

@ -1,13 +0,0 @@
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;
}

View File

@ -1,4 +1,4 @@
pragma solidity 0.8.0; pragma solidity 0.8.13;
interface IGovernance { interface IGovernance {

View File

@ -2,24 +2,25 @@ pragma solidity 0.8.13;
interface IRDA { interface IRDA {
error InvalidPurchaseVolume(); error InvalidPurchaseVolume();
error InvalidWindowVolume(); error InvalidWindowVolume();
error InvalidWindowPrice(); error InvalidWindowPrice();
error InsufficientReserves(); error InsufficientReserves();
error InvalidScalarPrice(); error InvalidScalarPrice();
error WindowUnexpired(); error WindowUnexpired();
error WindowFulfilled(); error WindowFulfilled();
error AuctionExists(); error AuctionExists();
function createAuction( function createAuction(
address operatorAddress, address vestingAddress,
address operatorAddress,
address reserveToken, address reserveToken,
address purchaseToken, address purchaseToken,
uint256 reserveAmount, uint256 reserveAmount,
@ -27,6 +28,7 @@ interface IRDA {
uint256 startingOriginPrice, uint256 startingOriginPrice,
uint256 startTimestamp, uint256 startTimestamp,
uint256 endTimestamp, uint256 endTimestamp,
uint256 vestingTimestamp,
uint256 windowDuration uint256 windowDuration
) external returns (bytes memory); ) external returns (bytes memory);

View File

@ -1,4 +1,4 @@
pragma solidity 0.8.0; pragma solidity 0.8.13;
interface IStaking { interface IStaking {

View File

@ -1,7 +0,0 @@
pragma solidity 0.8.0;
interface IVesting {
function makeCommitment(address stakeholder, uint256 amount) external;
}