diff --git a/src/DelegatedVesting.sol b/src/DelegatedVesting.sol index e4300ea..a96a0b8 100755 --- a/src/DelegatedVesting.sol +++ b/src/DelegatedVesting.sol @@ -1,4 +1,4 @@ -pragma solidity 0.8.0; +pragma solidity 0.8.13; import "@openzeppelin/token/ERC20/IERC20.sol"; import "@interfaces/IStaking.sol"; @@ -11,142 +11,142 @@ contract DelegatedInstance { uint256 public _balance; address public _governanceAddress; + address public _stakingAddress; address public _tokenAddress; constructor( - address stakeholderAddress, - address governanceAddress, - address tokenAddress, - uint256 stakeAmount + address stakeholderAddress, + address governanceAddress, + address tokenAddress, + uint256 stakeAmount ) { - _stakingAddress = IGovernance(governanceAddress).Staking(); - _governanceAddress = governanceAddress; - _tokenAddress = tokenAddress; + _stakingAddress = IGovernance(governanceAddress).Staking(); + _governanceAddress = governanceAddress; + _tokenAddress = tokenAddress; - _spender = stakeholderAddress; - _balance = stakeAmount; - _sender = msg.sender; + _spender = stakeholderAddress; + _balance = stakeAmount; + _sender = msg.sender; } modifier isSpender() { - require(msg.sender == _spender, "INVALID SPENDER"); - _; + require(msg.sender == _spender, "INVALID SPENDER"); + _; } function delegate(address to) public isSpender { - IGovernance(_governanceAddress).delegate(to); + IGovernance(_governanceAddress).delegate(to); } function lockAndDelegate( - address to, - uint256 amount, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s + address to, + uint256 amount, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s ) external isSpender { - IERC20(_tokenAddress).transferFrom(msg.sender, address(this), amount); - IERC20(_tokenAddress).approve(_governanceAddress, amount); - IGovernance(_governanceAddress).lock(address(this), amount, deadline, v, r, s); - IGovernance(_governanceAddress).delegate(to); + IERC20(_tokenAddress).transferFrom(msg.sender, address(this), amount); + IERC20(_tokenAddress).approve(_governanceAddress, amount); + IGovernance(_governanceAddress).lock(address(this), amount, deadline, v, r, s); + IGovernance(_governanceAddress).delegate(to); } 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); - IStaking(_stakingAddress).getReward(); - IERC20(_tokenAddress).transfer(_spender, stake + reward); + IGovernance(_governanceAddress).unlock(stake); + IStaking(_stakingAddress).getReward(); + IERC20(_tokenAddress).transfer(_spender, stake + reward); } } contract DelegatedVesting { - uint256 public _period; - address public _governanceAddress; address public _tokenAddress; + address public _governanceAddress; mapping(address => uint256) _balances; mapping(address => address) _instances; mapping(address => uint256) _commitments; constructor( - uint256 vestingDuration, - address governanceAddress, - address tokenAddress + address governanceAddress, + address tokenAddress ) { - _period = vestingDuration; - _tokenAddress = tokenAddress; - _governanceAddress = governanceAddress; + _tokenAddress = tokenAddress; + _governanceAddress = governanceAddress; } 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) { - return isActiveCommitment(stakeholder) && _instances[stakeholder] != address(0x0); + return isActiveCommitment(stakeholder) && _instances[stakeholder] != address(0x0); } 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 { - require(IERC20(_tokenAddress).transferFrom(msg.sender, address(this), amount)); + function makeCommitment(address stakeholder, uint256 amount, uint256 expiry) public { + require(block.timestamp < expiry, "INVALID EXPIRY TIMESTAMP"); + require(_commitments[stakeholder] == 0, "ACTIVE VESTING"); - _commitments[stakeholder] = block.timestmap + _period; - _balances[stakeholder] += amount; + IERC20(_tokenAddress).transferFrom(msg.sender, address(this), amount); + + _commitments[stakeholder] = expiry; + _balances[stakeholder] += amount; } function delegateCommitment( - address delegateAddress, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s + address delegateAddress, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s ) public { - require(isActiveCommitment(msg.sender), "INVALID COMMITMENT"); + require(isActiveCommitment(msg.sender), "INVALID COMMITMENT"); - if(isDelegatedCommitment(msg.sender)) { - DelegatedInstance(_instance[msg.sender]).delegate(delegateAddress); - } else { - DelegatedInstance e = new DelegatedInstance( - msg.sender, - _governanceAddress, - _tokenAddress, - _balances[msg.sender], - deadline, - v, r, s - ); + if(isDelegatedCommitment(msg.sender)) { + DelegatedInstance(_instances[msg.sender]).delegate(delegateAddress); + } else { + DelegatedInstance e = new DelegatedInstance( + msg.sender, + _governanceAddress, + _tokenAddress, + _balances[msg.sender] + ); - _instances[msg.sender] = address(e); + _instances[msg.sender] = address(e); - IERC20(_tokenAddress).approve(address(e), _balances[msg.sender]); - e.lockAndDelegate(delegateAddress, _balances[msg.sender], deadline, v, r, s); - } + IERC20(_tokenAddress).approve(address(e), _balances[msg.sender]); + e.lockAndDelegate(delegateAddress, _balances[msg.sender], deadline, v, r, s); + } } function fulfillCommitment() public { - require(isFulfilledCommitment(msg.sender), "INVALID FULFILLMENT"); + require(isFulfilledCommitment(msg.sender), "INVALID FULFILLMENT"); - uint256 stake = _balances[msg.sender]; - address delegated = _instances[msg.sender]; + uint256 stake = _balances[msg.sender]; + address delegated = _instances[msg.sender]; - delete _balances[msg.sender]; - delete _instances[msg.sender]; + delete _commitments[msg.sender]; + delete _balances[msg.sender]; + delete _instances[msg.sender]; - if(delegated != address(0x0)){ - DelegatedInstance(delegated).unlockAndRedeem(); - } else { - IERC20(_tokenAddress).transfer(msg.sender, stake); - } + if(delegated != address(0x0)){ + DelegatedInstance(delegated).unlockAndRedeem(); + } else { + IERC20(_tokenAddress).transfer(msg.sender, stake); + } } } diff --git a/src/Proposal.sol b/src/Proposal.sol index f075466..ff4920b 100644 --- a/src/Proposal.sol +++ b/src/Proposal.sol @@ -1,36 +1,40 @@ -pragma solidity 0.8.0; +pragma solidity 0.8.13; import "@openzeppelin/token/ERC20/IERC20.sol"; -import "@interfaces/IRDA.sol"; +import "@root/DelegatedVesting.sol"; +import "@root/RDA.sol"; 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 { - uint256 AUCTION_START_TS = block.timestamp; - uint256 AUCTION_END_TS = AUCTION_START_TS + 1 week; - uint256 AUCTION_ORIGIN_PRICE = 4172000 gwei; - uint256 AUCTION_RESERVE_AMOUNT = 100000 ether; - uint256 AUCTION_MINIMUM_AMOUNT = 1 ether; - uint256 AUCTION_WINDOW_LENGTH = 8 hours; - - address wethAddress; - address tokenAddress; - address governanceAddress; - address auctionAddress; - address vestingAddress; - - 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 - ); + RDA(auctionAddress).createAuction( + vestingAddress, + governanceAddress, + tokenAddress, + wethAddress, + AUCTION_RESERVE_AMOUNT, + AUCTION_MINIMUM_AMOUNT, + AUCTION_ORIGIN_PRICE, + AUCTION_START_TS, + AUCTION_END_TS, + AUCTION_WINDOW_LENGTH, + VESTING_PERIOD + ); } } diff --git a/src/RDA.sol b/src/RDA.sol index 0554ba5..cf7975b 100755 --- a/src/RDA.sol +++ b/src/RDA.sol @@ -3,8 +3,8 @@ pragma solidity 0.8.13; import { UD60x18 } from "@prb/math/UD60x18.sol"; import { IRDA } from "@interfaces/IRDA.sol"; -import { IVesting } from "@interfaces/IVesting.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"; @@ -28,6 +28,8 @@ contract RDA is IRDA { /* @dev Auction mapping for the window index */ mapping(bytes => uint256) public _windows; + mapping(bytes => Vesting) public _vesting; + struct Auction { uint256 windowDuration; /* @dev Unix time window duration */ uint256 windowTimestamp; /* @dev Unix timestamp for window start */ @@ -47,6 +49,11 @@ contract RDA is IRDA { bool processed; /* @dev Window fuflfillment state */ } + struct Vesting { + address instance; + uint256 period; + } + /* * @dev Conditioner to ensure an auction is active * @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 startTimestamp, uint256 endTimestamp, - uint256 windowDuration + uint256 windowDuration, + uint256 vestingDuration ) external returns (bytes memory) { bytes memory auctionId = abi.encode( operatorAddress, @@ -142,6 +150,9 @@ contract RDA is IRDA { revert AuctionExists(); } + _vesting[auctionId].instance = vestingAddress; + _vesting[auctionId].period = vestingDuration; + IERC20(reserveToken).transferFrom(msg.sender, address(this), reserveAmount); state.duration = endTimestamp - startTimestamp; @@ -151,7 +162,6 @@ contract RDA is IRDA { state.endTimestamp = endTimestamp; state.reserves = reserveAmount; state.price = startingOriginPrice; - state.vesting = vestingAddress; emit NewAuction(auctionId, reserveToken, reserveAmount, startingOriginPrice, endTimestamp); @@ -392,17 +402,20 @@ contract RDA is IRDA { inactiveAuction(auctionId) external { bytes memory claimHash = _claims[bidder][auctionId]; - address vestingAddress = _auctions[auctionId].vesting; + + delete _claims[bidder][auctionId]; (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) { IERC20(purchaseToken(auctionId)).transfer(bidder, refund); } if (claim > 0) { - IVesting(vestingAddress).makeCommitment(bidder, claim); + IERC20(reserveToken(auctionId)).approve(vestingAddress, claim); + IDelegatedVesting(vestingAddress).makeCommitment(bidder, claim, vestingTimestamp); } emit Claim(auctionId, claimHash); diff --git a/src/interfaces/IDelegatedVesting.sol b/src/interfaces/IDelegatedVesting.sol new file mode 100644 index 0000000..921afac --- /dev/null +++ b/src/interfaces/IDelegatedVesting.sol @@ -0,0 +1,7 @@ +pragma solidity 0.8.13; + +interface IDelegatedVesting { + + function makeCommitment(address stakeholder, uint256 amount, uint256 expiry) external; + +} diff --git a/src/interfaces/IERC20.sol b/src/interfaces/IERC20.sol deleted file mode 100755 index 294db0e..0000000 --- a/src/interfaces/IERC20.sol +++ /dev/null @@ -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; - -} diff --git a/src/interfaces/IGovernance.sol b/src/interfaces/IGovernance.sol index 62cf4b9..9963a50 100755 --- a/src/interfaces/IGovernance.sol +++ b/src/interfaces/IGovernance.sol @@ -1,4 +1,4 @@ -pragma solidity 0.8.0; +pragma solidity 0.8.13; interface IGovernance { diff --git a/src/interfaces/IRDA.sol b/src/interfaces/IRDA.sol index 51abc76..e7975c3 100644 --- a/src/interfaces/IRDA.sol +++ b/src/interfaces/IRDA.sol @@ -2,24 +2,25 @@ pragma solidity 0.8.13; 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( - address operatorAddress, + address vestingAddress, + address operatorAddress, address reserveToken, address purchaseToken, uint256 reserveAmount, @@ -27,6 +28,7 @@ interface IRDA { uint256 startingOriginPrice, uint256 startTimestamp, uint256 endTimestamp, + uint256 vestingTimestamp, uint256 windowDuration ) external returns (bytes memory); diff --git a/src/interfaces/IStaking.sol b/src/interfaces/IStaking.sol index c770795..260780d 100644 --- a/src/interfaces/IStaking.sol +++ b/src/interfaces/IStaking.sol @@ -1,4 +1,4 @@ -pragma solidity 0.8.0; +pragma solidity 0.8.13; interface IStaking { diff --git a/src/interfaces/IVesting.sol b/src/interfaces/IVesting.sol deleted file mode 100644 index eab4103..0000000 --- a/src/interfaces/IVesting.sol +++ /dev/null @@ -1,7 +0,0 @@ -pragma solidity 0.8.0; - -interface IVesting { - - function makeCommitment(address stakeholder, uint256 amount) external; - -}