Skip to content

Commit

Permalink
Merge pull request #10 from sofa-org/fix/c4-audit
Browse files Browse the repository at this point in the history
C4 audit
  • Loading branch information
sofa-org authored May 18, 2024
2 parents 7e7e787 + a83bfbf commit d90d4c0
Show file tree
Hide file tree
Showing 19 changed files with 230 additions and 219 deletions.
15 changes: 0 additions & 15 deletions contracts/libs/SignatureDecoding.sol

This file was deleted.

17 changes: 16 additions & 1 deletion contracts/oracles/HlOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import "../interfaces/IAutomatedFunctionsConsumer.sol";
contract HlOracle is AutomationCompatibleInterface {
mapping(uint256 => uint256[2]) public settlePrices;
IAutomatedFunctionsConsumer internal immutable AUTOMATED_FUNCTIONS_CONSUMER;
uint256 private latestExpiryUpdated = 0;

event Settled(uint256 expiry, uint256[2] settlePrices);

Expand Down Expand Up @@ -48,7 +49,21 @@ contract HlOracle is AutomationCompatibleInterface {
uint256 expiry = block.timestamp - block.timestamp % 86400 + 28800;
require(settlePrices[expiry][1] == 0, "Oracle: already settled");

settlePrices[expiry] = getLatestPrice();
uint256[2] memory currentPrices = getLatestPrice();
if (latestExpiryUpdated != 0 && latestExpiryUpdated < expiry - 86400 * 2) {
uint256 missedDays = (expiry - latestExpiryUpdated) / 86400;
uint256[2] memory startPrices = settlePrices[latestExpiryUpdated];

for (uint256 i = 1; i < missedDays; i++) {
uint256 missedExpiry = latestExpiryUpdated + i * 86400;
settlePrices[missedExpiry][0] = startPrices[0] + (currentPrices[0] - startPrices[0]) * i / missedDays;
settlePrices[missedExpiry][1] = startPrices[1] + (currentPrices[1] - startPrices[1]) * i / missedDays;
emit Settled(missedExpiry, settlePrices[missedExpiry]);
}
}

settlePrices[expiry] = currentPrices;
latestExpiryUpdated = expiry;

emit Settled(expiry, settlePrices[expiry]);
}
Expand Down
20 changes: 18 additions & 2 deletions contracts/oracles/SpotOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
contract SpotOracle {
mapping(uint256 => uint256) public settlePrices;
AggregatorV3Interface immutable internal PRICEFEED;
uint256 private latestExpiryUpdated = 0;

event Settled(uint256 expiry, uint256 settlePrice);

Expand All @@ -20,9 +21,24 @@ contract SpotOracle {
function settle() public {
uint256 expiry = block.timestamp - block.timestamp % 86400 + 28800;
require(settlePrices[expiry] == 0, "Oracle: already settled");
settlePrices[expiry] = uint256(getLatestPrice());

emit Settled(expiry, settlePrices[expiry]);
uint256 currentPrice = uint256(getLatestPrice());
if (latestExpiryUpdated != 0 && latestExpiryUpdated < expiry - 86400 * 2) {
uint256 missedDays = (expiry - latestExpiryUpdated) / 86400;
uint256 startPrice = settlePrices[latestExpiryUpdated];

for (uint256 i = 1; i < missedDays; i++) {
uint256 missedExpiry = latestExpiryUpdated + i * 86400;
uint256 missedDayPrice = startPrice + (currentPrice - startPrice) * i / missedDays;
settlePrices[missedExpiry] = missedDayPrice;
emit Settled(missedExpiry, missedDayPrice);
}
}

settlePrices[expiry] = currentPrice;
latestExpiryUpdated = expiry;

emit Settled(expiry, currentPrice);
}

function getLatestPrice() internal view returns (int) {
Expand Down
6 changes: 4 additions & 2 deletions contracts/tokenomics/FeeCollector.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ contract FeeCollector is Ownable {
function swapRCH(
address token,
uint256 minPrice,
uint256 deadline,
address[] calldata path
) external onlyOwner {
// last element of path should be rch
Expand All @@ -52,13 +53,14 @@ contract FeeCollector is Ownable {
amountIn * minPrice / 1e18,
path,
address(this),
block.timestamp + 10 minutes
deadline
);
}

function swapRCH(
address token,
uint256 minPrice,
uint256 deadline,
bytes calldata path
) external onlyOwner {
uint256 balanceBefore = IERC20(rch).balanceOf(address(this));
Expand All @@ -67,7 +69,7 @@ contract FeeCollector is Ownable {
ISwapRouter.ExactInputParams({
path: path,
recipient: address(this),
deadline: block.timestamp + 10 minutes,
deadline: deadline,
amountIn: amountIn,
amountOutMinimum: amountIn * minPrice / 1e18
});
Expand Down
1 change: 1 addition & 0 deletions contracts/tokenomics/MerkleAirdrop.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ contract MerkleAirdrop is Ownable {

function claim(uint256 index, uint256 amount, bytes32[] calldata merkleProof) external {
require(!isClaimed(index), "MerkleAirdrop: Drop already claimed.");
require(merkleRoots[index] != 0, "Airdrop: MerkleRoot not set for this day.");

bytes32 node = keccak256(abi.encodePacked(_msgSender(), amount));
require(MerkleProof.verify(merkleProof, merkleRoots[index], node), "MerkleAirdrop: Invalid proof.");
Expand Down
56 changes: 28 additions & 28 deletions contracts/vaults/AAVEDNTVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import "@openzeppelin/contracts-upgradeable/token/ERC1155/ERC1155Upgradeable.sol
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/SignatureCheckerUpgradeable.sol";
import {IPool} from "@aave/core-v3/contracts/interfaces/IPool.sol";
import {DataTypes} from "@aave/core-v3/contracts/protocol/libraries/types/DataTypes.sol";
import {ReserveLogic} from "@aave/core-v3/contracts/protocol/libraries/logic/ReserveLogic.sol";
Expand All @@ -18,13 +19,12 @@ import "../interfaces/IPermit2.sol";
import "../interfaces/IDNTStrategy.sol";
import "../interfaces/IHlOracle.sol";
import "../interfaces/IFeeCollector.sol";
import "../libs/SignatureDecoding.sol";
import "../utils/SignatureBitMap.sol";

contract AAVEDNTVault is Initializable, ContextUpgradeable, ERC1155Upgradeable, ReentrancyGuardUpgradeable, SignatureBitMap {
using SafeERC20 for IERC20Metadata;
using ReserveLogic for DataTypes.ReserveData;
using SignatureDecoding for bytes;
using SignatureCheckerUpgradeable for address;

struct Product {
uint256 term;
Expand Down Expand Up @@ -172,7 +172,6 @@ contract AAVEDNTVault is Initializable, ContextUpgradeable, ERC1155Upgradeable,
// require expiry must be 8:00 UTC
require(params.expiry % 86400 == 28800, "Vault: invalid expiry");
require(params.anchorPrices[0] < params.anchorPrices[1], "Vault: invalid strike prices");
require(params.collateralAtRisk <= totalCollateral, "Vault: invalid collateral");
require(!isSignatureConsumed(params.makerSignature), "Vault: signature consumed");
require(referral != _msgSender(), "Vault: invalid referral");

Expand All @@ -192,8 +191,7 @@ contract AAVEDNTVault is Initializable, ContextUpgradeable, ERC1155Upgradeable,
params.deadline,
address(this)))
));
(uint8 v, bytes32 r, bytes32 s) = params.makerSignature.decodeSignature();
require(params.maker == ecrecover(digest, v, r, s), "Vault: invalid maker signature");
require(params.maker.isValidSignatureNow(digest, params.makerSignature), "Vault: invalid maker signature");
consumeSignature(params.makerSignature);

// transfer makercollateral
Expand All @@ -203,6 +201,7 @@ contract AAVEDNTVault is Initializable, ContextUpgradeable, ERC1155Upgradeable,
uint256 term;
uint256 tradingFee = IFeeCollector(feeCollector).tradingFeeRate() * (params.collateralAtRisk - params.makerCollateral) / 1e18;
uint256 collateralAtRiskPercentage = params.collateralAtRisk * 1e18 / (totalCollateral - tradingFee);
require(collateralAtRiskPercentage > 0 && collateralAtRiskPercentage <= 1e18, "Vault: invalid collateral");
{
uint256 aTokenShare;
POOL.supply(address(COLLATERAL), totalCollateral, address(this), REFERRAL_CODE);
Expand All @@ -217,16 +216,16 @@ contract AAVEDNTVault is Initializable, ContextUpgradeable, ERC1155Upgradeable,
// trading fee
uint256 tradingFeeShare = aTokenShare * tradingFee / totalCollateral;
aTokenShare -= tradingFeeShare;
totalFee += tradingFeeShare / SHARE_MULTIPLIER;
totalFee += tradingFeeShare;

// mint product
// startDate = ((expiry-28800)/86400+1)*86400+28800
term = (params.expiry - (((block.timestamp - 28800) / 86400 + 1) * 86400 + 28800)) / 86400;
require(term > 0, "Vault: invalid term");
uint256 productId = getProductId(term, params.expiry, params.anchorPrices, collateralAtRiskPercentage, uint256(0));
uint256 makerProductId = getProductId(term, params.expiry, params.anchorPrices, collateralAtRiskPercentage, uint256(1));
_mint(_msgSender(), productId, aTokenShare / SHARE_MULTIPLIER, "");
_mint(params.maker, makerProductId, aTokenShare / SHARE_MULTIPLIER, "");
_mint(_msgSender(), productId, aTokenShare, "");
_mint(params.maker, makerProductId, aTokenShare, "");
}

emit Minted(_msgSender(), params.maker, referral, totalCollateral, term, params.expiry, params.anchorPrices, params.makerCollateral, collateralAtRiskPercentage);
Expand All @@ -244,16 +243,16 @@ contract AAVEDNTVault is Initializable, ContextUpgradeable, ERC1155Upgradeable,
if (payoff > 0) {
require(POOL.withdraw(address(COLLATERAL), payoff, address(this)) > 0, "Vault: withdraw failed");
WETH.withdraw(payoff);
payable(_msgSender()).transfer(payoff);
(bool success, ) = _msgSender().call{value: payoff, gas: 100_000}("");
require(success, "Failed to send ETH");
}
}

function _burn(uint256 term, uint256 expiry, uint256[2] memory anchorPrices, uint256 collateralAtRiskPercentage, uint256 isMaker) internal nonReentrant returns (uint256 payoff) {
(uint256 latestTerm, bool _isBurnable) = isBurnable(term, expiry, anchorPrices);
(uint256 latestTerm, uint256 latestExpiry, bool _isBurnable) = isBurnable(term, expiry, anchorPrices);
require(_isBurnable, "Vault: not burnable");

// check if settled
uint256 latestExpiry = (block.timestamp - 28800) / 86400 * 86400 + 28800;
require(ORACLE.settlePrices(latestExpiry, 1) > 0, "Vault: not settled");

uint256 productId = getProductId(term, expiry, anchorPrices, collateralAtRiskPercentage, isMaker);
Expand All @@ -274,8 +273,8 @@ contract AAVEDNTVault is Initializable, ContextUpgradeable, ERC1155Upgradeable,

// check self balance of collateral and transfer payoff
if (payoffShare > 0) {
payoff = payoffShare * ATOKEN.balanceOf(address(this)) * SHARE_MULTIPLIER / totalSupply;
totalSupply -= payoffShare * SHARE_MULTIPLIER;
payoff = payoffShare * ATOKEN.balanceOf(address(this)) / totalSupply;
totalSupply -= payoffShare;
emit Burned(_msgSender(), productId, amount, payoff);
} else {
emit Burned(_msgSender(), productId, amount, 0);
Expand All @@ -297,7 +296,8 @@ contract AAVEDNTVault is Initializable, ContextUpgradeable, ERC1155Upgradeable,
if (totalPayoff > 0) {
require(POOL.withdraw(address(COLLATERAL), totalPayoff, address(this)) > 0, "Vault: withdraw failed");
WETH.withdraw(totalPayoff);
payable(_msgSender()).transfer(totalPayoff);
(bool success, ) = _msgSender().call{value: totalPayoff, gas: 100_000}("");
require(success, "Failed to send ETH");
}
}

Expand All @@ -309,14 +309,14 @@ contract AAVEDNTVault is Initializable, ContextUpgradeable, ERC1155Upgradeable,
uint256 aTokenBalance = ATOKEN.balanceOf(address(this));
uint256 settlementFee;
for (uint256 i = 0; i < products.length; i++) {
// check if settled
uint256 latestExpiry = (block.timestamp - 28800) / 86400 * 86400 + 28800;
require(ORACLE.settlePrices(latestExpiry, 1) > 0, "Vault: not settled");

Product memory product = products[i];
(uint256 latestTerm, bool _isBurnable) = isBurnable(product.term, product.expiry, product.anchorPrices);

(uint256 latestTerm, uint256 latestExpiry, bool _isBurnable) = isBurnable(product.term, product.expiry, product.anchorPrices);
require(_isBurnable, "Vault: not burnable");

// check if settled
require(ORACLE.settlePrices(latestExpiry, 1) > 0, "Vault: not settled");

uint256 productId = getProductId(product.term, product.expiry, product.anchorPrices, product.collateralAtRiskPercentage, product.isMaker);
uint256 amount = balanceOf(_msgSender(), productId);
require(amount > 0, "Vault: zero amount");
Expand All @@ -338,15 +338,15 @@ contract AAVEDNTVault is Initializable, ContextUpgradeable, ERC1155Upgradeable,

productIds[i] = productId;
amounts[i] = amount;
payoffs[i] = payoffShare * aTokenBalance * SHARE_MULTIPLIER / totalSupply;
payoffs[i] = payoffShare * aTokenBalance / totalSupply;
}
if (settlementFee > 0) {
totalFee += settlementFee;
}
// check self balance of collateral and transfer payoff
if (totalPayoffShare > 0) {
totalPayoff = totalPayoffShare * aTokenBalance * SHARE_MULTIPLIER / totalSupply;
totalSupply -= totalPayoffShare * SHARE_MULTIPLIER;
totalPayoff = totalPayoffShare * aTokenBalance / totalSupply;
totalSupply -= totalPayoffShare;
}

// burn product
Expand All @@ -359,8 +359,8 @@ contract AAVEDNTVault is Initializable, ContextUpgradeable, ERC1155Upgradeable,
require(totalFee > 0, "Vault: zero fee");
uint256 fee = totalFee;
totalFee = 0;
uint256 payoff = fee * ATOKEN.balanceOf(address(this)) * SHARE_MULTIPLIER / totalSupply;
totalSupply -= fee * SHARE_MULTIPLIER;
uint256 payoff = fee * ATOKEN.balanceOf(address(this)) / totalSupply;
totalSupply -= fee;
require(POOL.withdraw(address(COLLATERAL), payoff, feeCollector) > 0, "Vault: withdraw failed");

emit FeeCollected(_msgSender(), payoff);
Expand Down Expand Up @@ -392,19 +392,19 @@ contract AAVEDNTVault is Initializable, ContextUpgradeable, ERC1155Upgradeable,
function isBurnable(uint256 term, uint256 expiry, uint256[2] memory anchorPrices)
public
view
returns (uint256, bool)
returns (uint256, uint256, bool)
{
if (expiry <= block.timestamp) {
return (term, true);
return (term, expiry, true);
} else {
uint256 latestExpiry = (block.timestamp - 28800) / 86400 * 86400 + 28800;
uint256 termGap = (expiry - latestExpiry) / 86400;
if (termGap > term) {
return (term, false);
return (term, latestExpiry, false);
} else {
uint256 latestTerm = term - termGap;
uint256[2] memory prices = ORACLE.getHlPrices(latestTerm, latestExpiry);
return(latestTerm, prices[0] <= anchorPrices[0] || prices[1] >= anchorPrices[1]);
return(latestTerm, latestExpiry, prices[0] <= anchorPrices[0] || prices[1] >= anchorPrices[1]);
}
}
}
Expand Down
Loading

0 comments on commit d90d4c0

Please sign in to comment.