bump RDA version
This commit is contained in:
parent
6beafd9b35
commit
5b13dfe581
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.sol linguist-language=Solidity
|
277
src/RDA.sol
277
src/RDA.sol
@ -1,12 +1,10 @@
|
|||||||
pragma solidity 0.8.13;
|
pragma solidity 0.8.13;
|
||||||
|
|
||||||
import { UD60x18 } from "@prb/math/UD60x18.sol";
|
import { IRDA } from "@root/interfaces/IRDA.sol";
|
||||||
|
|
||||||
import { IRDA } from "@interfaces/IRDA.sol";
|
import { ERC20 } from "@openzeppelin/token/ERC20/ERC20.sol";
|
||||||
import { IERC20 } from "@openzeppelin/token/ERC20/IERC20.sol";
|
import { SafeERC20 } from "@openzeppelin/token/ERC20/utils/SafeERC20.sol";
|
||||||
import { IDelegatedVesting } from "@interfaces/IDelegatedVesting.sol";
|
import { ReentrancyGuard } from "@openzeppelin/security/ReentrancyGuard.sol";
|
||||||
|
|
||||||
import { add, sub, mul, wrap, unwrap, gt, mod, div } from "@prb/math/UD60x18.sol";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @title Rolling Dutch Auction (RDA)
|
* @title Rolling Dutch Auction (RDA)
|
||||||
@ -14,7 +12,9 @@ import { add, sub, mul, wrap, unwrap, gt, mod, div } from "@prb/math/UD60x18.sol
|
|||||||
* @description A dutch auction derivative with composite decay
|
* @description A dutch auction derivative with composite decay
|
||||||
*/
|
*/
|
||||||
|
|
||||||
contract RDA is IRDA {
|
contract RDA is IRDA, ReentrancyGuard {
|
||||||
|
|
||||||
|
using SafeERC20 for ERC20;
|
||||||
|
|
||||||
/* @dev Address mapping for an auction's redeemable balances */
|
/* @dev Address mapping for an auction's redeemable balances */
|
||||||
mapping(address => mapping(bytes => bytes)) public _claims;
|
mapping(address => mapping(bytes => bytes)) public _claims;
|
||||||
@ -28,8 +28,6 @@ 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 */
|
||||||
@ -49,17 +47,14 @@ 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
|
||||||
*/
|
*/
|
||||||
modifier activeAuction(bytes memory auctionId) {
|
modifier activeAuction(bytes calldata auctionId) {
|
||||||
require(remainingWindowTime(auctionId) > 0 || remainingTime(auctionId) > 0);
|
if (remainingWindowTime(auctionId) == 0 && remainingTime(auctionId) == 0) {
|
||||||
|
revert AuctionInactive();
|
||||||
|
}
|
||||||
_;
|
_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,8 +62,10 @@ contract RDA is IRDA {
|
|||||||
* @dev Conditioner to ensure an auction is inactive
|
* @dev Conditioner to ensure an auction is inactive
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
modifier inactiveAuction(bytes memory auctionId) {
|
modifier inactiveAuction(bytes calldata auctionId) {
|
||||||
require(remainingWindowTime(auctionId) == 0 && remainingTime(auctionId) == 0);
|
if (remainingWindowTime(auctionId) > 0 || remainingTime(auctionId) > 0) {
|
||||||
|
revert AuctionActive();
|
||||||
|
}
|
||||||
_;
|
_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +73,7 @@ contract RDA is IRDA {
|
|||||||
* @dev Helper to view an auction's operator address
|
* @dev Helper to view an auction's operator address
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
function operatorAddress(bytes memory auctionId) public pure returns (address opAddress) {
|
function operatorAddress(bytes calldata auctionId) public pure returns (address opAddress) {
|
||||||
(opAddress,,,,) = abi.decode(auctionId, (address, address, address, uint256, bytes));
|
(opAddress,,,,) = abi.decode(auctionId, (address, address, address, uint256, bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,15 +81,31 @@ contract RDA is IRDA {
|
|||||||
* @dev Helper to view an auction's purchase token address
|
* @dev Helper to view an auction's purchase token address
|
||||||
* @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Ancoded auction parameter identifier
|
* @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Ancoded auction parameter identifier
|
||||||
*/
|
*/
|
||||||
function purchaseToken(bytes memory auctionId) public pure returns (address tokenAddress) {
|
function purchaseToken(bytes calldata auctionId) public pure returns (address tokenAddress) {
|
||||||
(,, tokenAddress,,) = abi.decode(auctionId, (address, address, address, uint256, bytes));
|
(,, tokenAddress,,) = abi.decode(auctionId, (address, address, address, uint256, bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isWindowInit(bytes calldata auctionId) public view returns (bool) {
|
||||||
|
return _window[auctionId][_windows[auctionId]].expiry != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isWindowActive(bytes calldata auctionId) public view returns (bool) {
|
||||||
|
Window storage window = _window[auctionId][_windows[auctionId]];
|
||||||
|
|
||||||
|
return isWindowInit(auctionId) && window.expiry > block.timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isWindowExpired(bytes calldata auctionId) public view returns (bool) {
|
||||||
|
Window storage window = _window[auctionId][_windows[auctionId]];
|
||||||
|
|
||||||
|
return isWindowInit(auctionId) && window.expiry < block.timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @dev Helper to view an auction's reserve token address
|
* @dev Helper to view an auction's reserve token address
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
function reserveToken(bytes memory auctionId) public pure returns (address tokenAddress) {
|
function reserveToken(bytes calldata auctionId) public pure returns (address tokenAddress) {
|
||||||
(, tokenAddress,,,) = abi.decode(auctionId, (address, address, address, uint256, bytes));
|
(, tokenAddress,,,) = abi.decode(auctionId, (address, address, address, uint256, bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,7 +137,6 @@ contract RDA is IRDA {
|
|||||||
* @param w͟i͟n͟d͟o͟w͟D͟u͟r͟a͟t͟i͟o͟n͟ Uinx time window duration
|
* @param w͟i͟n͟d͟o͟w͟D͟u͟r͟a͟t͟i͟o͟n͟ Uinx time window duration
|
||||||
*/
|
*/
|
||||||
function createAuction(
|
function createAuction(
|
||||||
address vestingAddress,
|
|
||||||
address operatorAddress,
|
address operatorAddress,
|
||||||
address reserveToken,
|
address reserveToken,
|
||||||
address purchaseToken,
|
address purchaseToken,
|
||||||
@ -133,9 +145,8 @@ contract RDA is IRDA {
|
|||||||
uint256 startingOriginPrice,
|
uint256 startingOriginPrice,
|
||||||
uint256 startTimestamp,
|
uint256 startTimestamp,
|
||||||
uint256 endTimestamp,
|
uint256 endTimestamp,
|
||||||
uint256 windowDuration,
|
uint256 windowDuration
|
||||||
uint256 vestingDuration
|
) override external returns (bytes memory) {
|
||||||
) external returns (bytes memory) {
|
|
||||||
bytes memory auctionId = abi.encode(
|
bytes memory auctionId = abi.encode(
|
||||||
operatorAddress,
|
operatorAddress,
|
||||||
reserveToken,
|
reserveToken,
|
||||||
@ -144,24 +155,38 @@ contract RDA is IRDA {
|
|||||||
abi.encodePacked(reserveAmount, startingOriginPrice, startTimestamp, endTimestamp, windowDuration)
|
abi.encodePacked(reserveAmount, startingOriginPrice, startTimestamp, endTimestamp, windowDuration)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ERC20 tokenReserve = ERC20(reserveToken);
|
||||||
|
ERC20 tokenPurchase = ERC20(purchaseToken);
|
||||||
|
|
||||||
Auction storage state = _auctions[auctionId];
|
Auction storage state = _auctions[auctionId];
|
||||||
|
|
||||||
|
uint256 auctionDuration = endTimestamp - startTimestamp;
|
||||||
|
|
||||||
if (state.price != 0) {
|
if (state.price != 0) {
|
||||||
revert AuctionExists();
|
revert AuctionExists();
|
||||||
}
|
}
|
||||||
|
if (startingOriginPrice == 0) {
|
||||||
|
revert InvalidAuctionPrice();
|
||||||
|
}
|
||||||
|
if (startTimestamp < block.timestamp) {
|
||||||
|
revert InvalidAuctionTimestamp();
|
||||||
|
}
|
||||||
|
if (tokenReserve.decimals() != tokenPurchase.decimals()){
|
||||||
|
revert InvalidTokenDecimals();
|
||||||
|
}
|
||||||
|
if (auctionDuration < 1 days || windowDuration < 2 hours) {
|
||||||
|
revert InvalidAuctionDurations();
|
||||||
|
}
|
||||||
|
|
||||||
_vesting[auctionId].instance = vestingAddress;
|
tokenReserve.safeTransferFrom(msg.sender, address(this), reserveAmount);
|
||||||
_vesting[auctionId].period = vestingDuration;
|
|
||||||
|
|
||||||
IERC20(reserveToken).transferFrom(msg.sender, address(this), reserveAmount);
|
|
||||||
|
|
||||||
state.duration = endTimestamp - startTimestamp;
|
|
||||||
state.windowDuration = windowDuration;
|
state.windowDuration = windowDuration;
|
||||||
state.windowTimestamp = startTimestamp;
|
state.windowTimestamp = startTimestamp;
|
||||||
state.startTimestamp = startTimestamp;
|
state.startTimestamp = startTimestamp;
|
||||||
state.endTimestamp = endTimestamp;
|
state.endTimestamp = endTimestamp;
|
||||||
state.reserves = reserveAmount;
|
|
||||||
state.price = startingOriginPrice;
|
state.price = startingOriginPrice;
|
||||||
|
state.duration = auctionDuration;
|
||||||
|
state.reserves = reserveAmount;
|
||||||
|
|
||||||
emit NewAuction(auctionId, reserveToken, reserveAmount, startingOriginPrice, endTimestamp);
|
emit NewAuction(auctionId, reserveToken, reserveAmount, startingOriginPrice, endTimestamp);
|
||||||
|
|
||||||
@ -172,18 +197,10 @@ contract RDA is IRDA {
|
|||||||
* @dev Helper to view an auction's minimum purchase amount
|
* @dev Helper to view an auction's minimum purchase amount
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
function minimumPurchase(bytes memory auctionId) public pure returns (uint256 minimumAmount) {
|
function minimumPurchase(bytes calldata auctionId) public pure returns (uint256 minimumAmount) {
|
||||||
(,,, minimumAmount,) = abi.decode(auctionId, (address, address, address, uint256, bytes));
|
(,,, minimumAmount,) = abi.decode(auctionId, (address, address, address, uint256, bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* @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 calldata auctionId) external returns (uint256) {
|
|
||||||
return unwrap(scalarPrice(auctionId));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @dev Active price decay proportional to time delta (t) between the current
|
* @dev Active price decay proportional to time delta (t) between the current
|
||||||
* timestamp and the window's start timestamp or if the window is expired;
|
* timestamp and the window's start timestamp or if the window is expired;
|
||||||
@ -194,22 +211,23 @@ contract RDA is IRDA {
|
|||||||
* the origin price (y) and subtracted by y to result the decayed price
|
* the origin price (y) and subtracted by y to result the decayed price
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
function scalarPrice(bytes memory auctionId) public view returns (UD60x18) {
|
function scalarPrice(bytes calldata auctionId)
|
||||||
|
activeAuction(auctionId)
|
||||||
|
public view returns (uint256) {
|
||||||
Auction storage state = _auctions[auctionId];
|
Auction storage state = _auctions[auctionId];
|
||||||
Window storage window = _window[auctionId][_windows[auctionId]];
|
Window storage window = _window[auctionId][_windows[auctionId]];
|
||||||
|
|
||||||
bool isInitialised = window.expiry != 0;
|
uint256 ts = isWindowExpired(auctionId) ? window.expiry : state.windowTimestamp;
|
||||||
bool isExpired = window.expiry < block.timestamp && isInitialised;
|
uint256 y = !isWindowInit(auctionId) ? state.price : window.price;
|
||||||
|
|
||||||
uint256 timestamp = isExpired ? window.expiry : state.windowTimestamp;
|
uint256 t = block.timestamp - ts;
|
||||||
|
uint256 t_r = state.duration - elapsedTimeFromWindow(auctionId);
|
||||||
|
|
||||||
UD60x18 t = wrap(block.timestamp - timestamp);
|
uint256 t_mod = t % (t_r - t);
|
||||||
UD60x18 t_r = wrap(state.duration - elapsedTime(auctionId, timestamp));
|
uint256 x = (t + t_mod) * 1e18;
|
||||||
|
uint256 y_x = y * x / t_r;
|
||||||
|
|
||||||
UD60x18 x = div(add(t, mod(t, sub(t_r, t))), t_r);
|
return y - y_x / 1e18;
|
||||||
UD60x18 y = !isInitialised ? wrap(state.price) : wrap(window.price);
|
|
||||||
|
|
||||||
return sub(y, mul(y, x));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -218,9 +236,11 @@ contract RDA is IRDA {
|
|||||||
* @param p͟r͟i͟c͟e͟ Bid order price
|
* @param p͟r͟i͟c͟e͟ Bid order price
|
||||||
* @param v͟o͟l͟u͟m͟e͟ Bid order volume
|
* @param v͟o͟l͟u͟m͟e͟ Bid order volume
|
||||||
*/
|
*/
|
||||||
function commitBid(bytes memory auctionId, uint256 price, uint256 volume)
|
function commitBid(bytes calldata auctionId, uint256 price, uint256 volume)
|
||||||
activeAuction(auctionId)
|
activeAuction(auctionId)
|
||||||
external returns (bytes memory) {
|
nonReentrant
|
||||||
|
override external returns (bytes memory bidId) {
|
||||||
|
Auction storage state = _auctions[auctionId];
|
||||||
Window storage window = _window[auctionId][_windows[auctionId]];
|
Window storage window = _window[auctionId][_windows[auctionId]];
|
||||||
|
|
||||||
if (volume < minimumPurchase(auctionId)) {
|
if (volume < minimumPurchase(auctionId)) {
|
||||||
@ -229,8 +249,8 @@ contract RDA is IRDA {
|
|||||||
|
|
||||||
bool hasExpired;
|
bool hasExpired;
|
||||||
|
|
||||||
if (window.expiry != 0) {
|
if (isWindowInit(auctionId)) {
|
||||||
if (remainingWindowTime(auctionId) > 0) {
|
if (isWindowActive(auctionId)) {
|
||||||
if (window.price < price) {
|
if (window.price < price) {
|
||||||
if (volume < window.volume) {
|
if (volume < window.volume) {
|
||||||
revert InvalidWindowVolume();
|
revert InvalidWindowVolume();
|
||||||
@ -244,68 +264,85 @@ contract RDA is IRDA {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (window.price == 0 || hasExpired) {
|
if (window.price == 0 || hasExpired) {
|
||||||
if (gt(scalarPrice(auctionId), wrap(price))) {
|
if (price < scalarPrice(auctionId)) {
|
||||||
revert InvalidScalarPrice();
|
revert InvalidScalarPrice();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IERC20(purchaseToken(auctionId)).transferFrom(msg.sender, address(this), volume);
|
uint256 orderVolume = volume - (volume % price);
|
||||||
|
|
||||||
if (_auctions[auctionId].reserves < (volume / price)) {
|
if (state.reserves < orderVolume * 1e18 / price) {
|
||||||
revert InsufficientReserves();
|
revert InsufficientReserves();
|
||||||
}
|
}
|
||||||
|
if (volume < price) {
|
||||||
|
revert InvalidReserveVolume();
|
||||||
|
}
|
||||||
|
|
||||||
bytes memory bidId = abi.encode(auctionId, msg.sender, price, volume);
|
bidId = abi.encode(auctionId, msg.sender, price, orderVolume);
|
||||||
|
|
||||||
(uint256 refund, uint256 claim) = balancesOf(_claims[msg.sender][auctionId]);
|
(uint256 refund, uint256 claim) = balancesOf(_claims[msg.sender][auctionId]);
|
||||||
|
|
||||||
_claims[msg.sender][auctionId] = abi.encode(refund + volume, claim);
|
_claims[msg.sender][auctionId] = abi.encode(refund + orderVolume, claim);
|
||||||
|
|
||||||
if (hasExpired) {
|
if (hasExpired) {
|
||||||
window = _window[auctionId][windowExpiration(auctionId)];
|
window = _window[auctionId][windowExpiration(auctionId)];
|
||||||
}
|
}
|
||||||
|
|
||||||
_auctions[auctionId].windowTimestamp = block.timestamp;
|
window.expiry = block.timestamp + state.windowDuration;
|
||||||
|
window.volume = orderVolume;
|
||||||
window.expiry = block.timestamp + _auctions[auctionId].windowDuration;
|
|
||||||
window.volume = volume;
|
|
||||||
window.price = price;
|
window.price = price;
|
||||||
window.bidId = bidId;
|
window.bidId = bidId;
|
||||||
|
|
||||||
emit Offer(auctionId, msg.sender, window.bidId, window.expiry);
|
state.windowTimestamp = block.timestamp;
|
||||||
|
|
||||||
return bidId;
|
emit Offer(auctionId, msg.sender, bidId, window.expiry);
|
||||||
|
|
||||||
|
ERC20 tokenPurchase = ERC20(purchaseToken(auctionId));
|
||||||
|
|
||||||
|
tokenPurchase.safeTransferFrom(msg.sender, address(this), orderVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @dev Expire and fulfill an auction's active window
|
* @dev Expire and fulfill an auction's active window
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
function windowExpiration(bytes memory auctionId) internal returns (uint256) {
|
function windowExpiration(bytes calldata auctionId) internal returns (uint256) {
|
||||||
uint256 windowIndex = _windows[auctionId];
|
uint256 windowIndex = _windows[auctionId];
|
||||||
uint256 auctionElapsedTime = elapsedTime(auctionId, block.timestamp);
|
|
||||||
uint256 auctionRemainingTime = _auctions[auctionId].duration - auctionElapsedTime;
|
|
||||||
|
|
||||||
_auctions[auctionId].endTimestamp = block.timestamp + auctionRemainingTime;
|
Auction storage state = _auctions[auctionId];
|
||||||
_auctions[auctionId].price = _window[auctionId][windowIndex].price;
|
Window storage window = _window[auctionId][windowIndex];
|
||||||
|
|
||||||
|
state.endTimestamp = block.timestamp + remainingTime(auctionId);
|
||||||
|
state.price = window.price;
|
||||||
|
|
||||||
_windows[auctionId] = windowIndex + 1;
|
_windows[auctionId] = windowIndex + 1;
|
||||||
|
|
||||||
fulfillWindow(auctionId, windowIndex);
|
_fulfillWindow(auctionId, windowIndex);
|
||||||
|
|
||||||
emit Expiration(auctionId, _window[auctionId][windowIndex].bidId, windowIndex);
|
emit Expiration(auctionId, window.bidId, windowIndex);
|
||||||
|
|
||||||
return windowIndex + 1;
|
return windowIndex + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @dev Fulfill a window index even if the auction is inactive
|
* @dev Fulfill a window index for an inactive auction
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
function fulfillWindow(bytes memory auctionId, uint256 windowId) public {
|
function fulfillWindow(bytes calldata auctionId, uint256 windowId)
|
||||||
|
inactiveAuction(auctionId)
|
||||||
|
override public {
|
||||||
|
_fulfillWindow(auctionId, windowId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @dev Fulfill a window index
|
||||||
|
* @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier
|
||||||
|
*/
|
||||||
|
function _fulfillWindow(bytes calldata auctionId, uint256 windowId) internal {
|
||||||
|
Auction storage state = _auctions[auctionId];
|
||||||
Window storage window = _window[auctionId][windowId];
|
Window storage window = _window[auctionId][windowId];
|
||||||
|
|
||||||
if (window.expiry > block.timestamp) {
|
if (isWindowActive(auctionId)) {
|
||||||
revert WindowUnexpired();
|
revert WindowUnexpired();
|
||||||
}
|
}
|
||||||
if (window.processed) {
|
if (window.processed) {
|
||||||
@ -319,10 +356,12 @@ contract RDA is IRDA {
|
|||||||
|
|
||||||
window.processed = true;
|
window.processed = true;
|
||||||
|
|
||||||
_auctions[auctionId].reserves -= volume / price;
|
uint256 orderAmount = volume * 1e18 / price;
|
||||||
_auctions[auctionId].proceeds += volume;
|
|
||||||
|
|
||||||
_claims[bidder][auctionId] = abi.encode(refund - volume, claim + (volume / price));
|
state.reserves -= orderAmount;
|
||||||
|
state.proceeds += volume;
|
||||||
|
|
||||||
|
_claims[bidder][auctionId] = abi.encode(refund - volume, claim + orderAmount);
|
||||||
|
|
||||||
emit Fulfillment(auctionId, window.bidId, windowId);
|
emit Fulfillment(auctionId, window.bidId, windowId);
|
||||||
}
|
}
|
||||||
@ -331,53 +370,64 @@ contract RDA is IRDA {
|
|||||||
* @dev Helper to view an auction's remaining duration
|
* @dev Helper to view an auction's remaining duration
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
function remainingTime(bytes memory auctionId) public view returns (uint256) {
|
function remainingTime(bytes calldata auctionId) public view returns (uint256) {
|
||||||
uint256 endTimestamp = _auctions[auctionId].endTimestamp;
|
return _auctions[auctionId].duration - elapsedTime(auctionId);
|
||||||
|
|
||||||
if (endTimestamp > block.timestamp) {
|
|
||||||
return endTimestamp - block.timestamp;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @dev Helper to view an auction's active remaining window duration
|
* @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
|
* @param a͟u͟c͟t͟i͟o͟n͟I͟d͟ Encoded auction parameter identifier
|
||||||
*/
|
*/
|
||||||
function remainingWindowTime(bytes memory auctionId) public view returns (uint256) {
|
function remainingWindowTime(bytes calldata auctionId) public view returns (uint256) {
|
||||||
uint256 expiryTimestamp = _window[auctionId][_windows[auctionId]].expiry;
|
if (!isWindowActive(auctionId)) {
|
||||||
|
|
||||||
if (expiryTimestamp > 0 && block.timestamp < expiryTimestamp) {
|
|
||||||
return expiryTimestamp - block.timestamp;
|
|
||||||
} else {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return _window[auctionId][_windows[auctionId]].expiry - block.timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @dev Helper to view an auction's progress in unix time
|
* @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
|
* @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) {
|
function elapsedTime(bytes calldata auctionId) public view returns (uint256) {
|
||||||
uint256 windowIndex = _windows[auctionId] + 1;
|
return block.timestamp - windowElapsedTime(auctionId) - _auctions[auctionId].startTimestamp;
|
||||||
uint256 auctionElapsedTime = timestamp - _auctions[auctionId].startTimestamp;
|
|
||||||
uint256 windowElapsedTime = _auctions[auctionId].windowDuration * windowIndex;
|
|
||||||
|
|
||||||
if (auctionElapsedTime > windowElapsedTime) {
|
|
||||||
return auctionElapsedTime - windowElapsedTime;
|
|
||||||
} else {
|
|
||||||
return auctionElapsedTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function windowElapsedTime(bytes calldata auctionId) public view returns (uint256) {
|
||||||
|
if (!isWindowInit(auctionId)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint256 windowIndex = _windows[auctionId];
|
||||||
|
uint256 elapsedWindowsTime = _auctions[auctionId].windowDuration * (windowIndex + 1);
|
||||||
|
|
||||||
|
return elapsedWindowsTime - remainingWindowTime(auctionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function elapsedTimeFromWindow(bytes calldata auctionId) public view returns (uint256) {
|
||||||
|
Auction storage state = _auctions[auctionId];
|
||||||
|
|
||||||
|
uint256 endTimestamp = state.windowTimestamp;
|
||||||
|
|
||||||
|
if (isWindowExpired(auctionId)) {
|
||||||
|
endTimestamp = _window[auctionId][_windows[auctionId]].expiry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return endTimestamp - windowElapsedTime(auctionId) - state.startTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @dev Auction management redemption
|
* @dev Auction management redemption
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
function withdraw(bytes memory auctionId)
|
function withdraw(bytes calldata auctionId)
|
||||||
inactiveAuction(auctionId)
|
inactiveAuction(auctionId)
|
||||||
external {
|
override external {
|
||||||
|
ERC20 tokenReserve = ERC20(reserveToken(auctionId));
|
||||||
|
ERC20 tokenPurchase = ERC20(purchaseToken(auctionId));
|
||||||
|
|
||||||
uint256 proceeds = _auctions[auctionId].proceeds;
|
uint256 proceeds = _auctions[auctionId].proceeds;
|
||||||
uint256 reserves = _auctions[auctionId].reserves;
|
uint256 reserves = _auctions[auctionId].reserves;
|
||||||
|
|
||||||
@ -385,10 +435,10 @@ contract RDA is IRDA {
|
|||||||
delete _auctions[auctionId].reserves;
|
delete _auctions[auctionId].reserves;
|
||||||
|
|
||||||
if (proceeds > 0) {
|
if (proceeds > 0) {
|
||||||
IERC20(purchaseToken(auctionId)).transfer(operatorAddress(auctionId), proceeds);
|
tokenPurchase.safeTransfer(operatorAddress(auctionId), proceeds);
|
||||||
}
|
}
|
||||||
if (reserves > 0) {
|
if (reserves > 0) {
|
||||||
IERC20(reserveToken(auctionId)).transfer(operatorAddress(auctionId), reserves);
|
tokenReserve.safeTransfer(operatorAddress(auctionId), reserves);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit Withdraw(auctionId);
|
emit Withdraw(auctionId);
|
||||||
@ -398,24 +448,23 @@ contract RDA is IRDA {
|
|||||||
* @dev Auction order and refund redemption
|
* @dev Auction order and refund redemption
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
function redeem(address bidder, bytes memory auctionId)
|
function redeem(address bidder, bytes calldata auctionId)
|
||||||
inactiveAuction(auctionId)
|
inactiveAuction(auctionId)
|
||||||
external {
|
override external {
|
||||||
bytes memory claimHash = _claims[bidder][auctionId];
|
ERC20 tokenReserve = ERC20(reserveToken(auctionId));
|
||||||
|
ERC20 tokenPurchase = ERC20(purchaseToken(auctionId));
|
||||||
|
|
||||||
delete _claims[bidder][auctionId];
|
bytes memory claimHash = _claims[bidder][auctionId];
|
||||||
|
|
||||||
(uint256 refund, uint256 claim) = balancesOf(claimHash);
|
(uint256 refund, uint256 claim) = balancesOf(claimHash);
|
||||||
|
|
||||||
uint256 vestingTimestamp = block.timestamp + _vesting[auctionId].period;
|
delete _claims[bidder][auctionId];
|
||||||
address vestingAddress = _vesting[auctionId].instance;
|
|
||||||
|
|
||||||
if (refund > 0) {
|
if (refund > 0) {
|
||||||
IERC20(purchaseToken(auctionId)).transfer(bidder, refund);
|
tokenPurchase.safeTransfer(bidder, refund);
|
||||||
}
|
}
|
||||||
if (claim > 0) {
|
if (claim > 0) {
|
||||||
IERC20(reserveToken(auctionId)).approve(vestingAddress, claim);
|
tokenReserve.safeTransfer(bidder, claim);
|
||||||
IDelegatedVesting(vestingAddress).makeCommitment(bidder, claim, vestingTimestamp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emit Claim(auctionId, claimHash);
|
emit Claim(auctionId, claimHash);
|
||||||
|
Loading…
Reference in New Issue
Block a user