Skip to content

Commit

Permalink
feedback/audit (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
0xashu committed Jul 2, 2024
1 parent 8226454 commit cda0246
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 64 deletions.
2 changes: 1 addition & 1 deletion .solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"const-name-snakecase": "off",
"func-name-mixedcase": "off",
"func-visibility": ["error", { "ignoreConstructors": true }],
"max-line-length": ["error", 123],
"max-line-length": ["warn", 123],
"named-parameters-mapping": "warn",
"no-empty-blocks": "off",
"not-rely-on-time": "off",
Expand Down
31 changes: 14 additions & 17 deletions contracts/core/SharesFactoryV1.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity 0.8.25;

import "@openzeppelin/contracts/access/Ownable2Step.sol";

Check warning on line 5 in contracts/core/SharesFactoryV1.sol

View workflow job for this annotation

GitHub Actions / lint (18)

global import of path @openzeppelin/contracts/access/Ownable2Step.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)

Check warning on line 5 in contracts/core/SharesFactoryV1.sol

View workflow job for this annotation

GitHub Actions / lint (18)

global import of path @openzeppelin/contracts/access/Ownable2Step.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

Check warning on line 6 in contracts/core/SharesFactoryV1.sol

View workflow job for this annotation

GitHub Actions / lint (18)

global import of path @openzeppelin/contracts/security/ReentrancyGuard.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)

Check warning on line 6 in contracts/core/SharesFactoryV1.sol

View workflow job for this annotation

GitHub Actions / lint (18)

global import of path @openzeppelin/contracts/security/ReentrancyGuard.sol is not allowed. Specify names to import individually or bind all exports of the module into a name (import "path" as Name)
import { SafeCastLib } from "solady/utils/SafeCastLib.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IShare } from "../interface/IShare.sol";
Expand Down Expand Up @@ -34,7 +35,7 @@ contract SharesFactoryV1 is Ownable2Step, ReentrancyGuard {

uint256 public shareIndex;
uint256 public depositedETHAmount;
uint256 public referralFeePercent = 5 * 1e16;
uint256 public referralFeePercent = 2 * 1e16;
uint256 public creatorFeePercent = 5 * 1e16;
uint256 public migrationDeadline;

Expand All @@ -43,7 +44,7 @@ contract SharesFactoryV1 is Ownable2Step, ReentrancyGuard {
address public blankAggregator;

event QueueMigrateYield(address indexed newAggregator, uint256 deadline);
event MigrateYield(address indexed newAggregator, uint256 timestamp);
event MigrateYield(address indexed newAggregator);
event ClaimYield(uint256 amount, address indexed to);
event SetCurve(uint8 indexed curveType);
event SetFee(uint256 indexed feePercent, string feeType);
Expand All @@ -63,9 +64,9 @@ contract SharesFactoryV1 is Ownable2Step, ReentrancyGuard {

// Set default curve params
curvesMap[0] = Curve({
basePrice: _basePrice, // 5000000000000000;
inflectionPoint: _inflectionPoint, // 1500;
inflectionPrice: _inflectionPrice, // 102500000000000000;
basePrice: _basePrice, // 0.001 ether;
inflectionPoint: _inflectionPoint, // 1000;
inflectionPrice: _inflectionPrice, // 0.1 ether;
linearPriceSlope: _linearPriceSlope, // 0;
exists: true
});
Expand All @@ -84,17 +85,12 @@ contract SharesFactoryV1 is Ownable2Step, ReentrancyGuard {
function getCurve(uint8 curveType) public view returns (uint96, uint32, uint128, uint128, bool) {
require(curvesMap[curveType].exists, "Invalid curveType");
Curve memory curve = curvesMap[curveType];
uint96 basePrice = curve.basePrice;
uint32 g = curve.inflectionPoint;
uint128 h = curve.inflectionPrice;
uint128 m = curve.linearPriceSlope;
bool exists = curve.exists;
return (basePrice, g, h, m, exists);
return (curve.basePrice, curve.inflectionPoint, curve.inflectionPrice, curve.linearPriceSlope, curve.exists);
}

function getSubTotal(uint32 fromSupply, uint32 quantity, uint8 curveType) public view returns (uint256) {
(uint96 basePrice, uint32 g, uint128 h, uint128 m,) = getCurve(curveType);
return _subTotal(fromSupply, quantity, basePrice, g, h, m);
(uint96 basePrice, uint32 inflectionPoint, uint128 inflectionPrice, uint128 linearPriceSlope,) = getCurve(curveType);

Check warning on line 92 in contracts/core/SharesFactoryV1.sol

View workflow job for this annotation

GitHub Actions / lint (18)

Line length must be no more than 123 but current length is 125

Check warning on line 92 in contracts/core/SharesFactoryV1.sol

View workflow job for this annotation

GitHub Actions / lint (18)

Line length must be no more than 123 but current length is 125
return _subTotal(fromSupply, quantity, basePrice, inflectionPoint, inflectionPrice, linearPriceSlope);
}

function setReferralFeePercent(uint256 _feePercent) external onlyOwner {
Expand Down Expand Up @@ -313,8 +309,9 @@ contract SharesFactoryV1 is Ownable2Step, ReentrancyGuard {
(, uint8 curveType) = getShare(shareId);
uint256 fromSupply = IShare(ERC1155).shareFromSupply(shareId);
uint256 actualReferralFeePercent = referral != address(0) ? referralFeePercent : 0;
require(fromSupply + uint256(quantity) <= type(uint32).max, "Exceeds max supply");

buyPrice = getSubTotal(uint32(fromSupply), quantity, curveType);
buyPrice = getSubTotal(SafeCastLib.toUint32(fromSupply), quantity, curveType);
referralFee = (buyPrice * actualReferralFeePercent) / 1 ether;
creatorFee = (buyPrice * creatorFeePercent) / 1 ether;
buyPriceAfterFee = buyPrice + referralFee + creatorFee;
Expand Down Expand Up @@ -346,7 +343,7 @@ contract SharesFactoryV1 is Ownable2Step, ReentrancyGuard {
uint256 actualReferralFeePercent = referral != address(0) ? referralFeePercent : 0;
require(fromSupply >= quantity, "Exceeds supply");

sellPrice = getSubTotal(uint32(fromSupply) - quantity, quantity, curveType);
sellPrice = getSubTotal(SafeCastLib.toUint32(fromSupply) - quantity, quantity, curveType);
referralFee = (sellPrice * actualReferralFeePercent) / 1 ether;
creatorFee = (sellPrice * creatorFeePercent) / 1 ether;
sellPriceAfterFee = sellPrice - referralFee - creatorFee;
Expand Down Expand Up @@ -381,7 +378,7 @@ contract SharesFactoryV1 is Ownable2Step, ReentrancyGuard {
// Step 4: Deposit all ETH into the new yieldAggregator as yieldToken.
_depositAllETHToYieldToken();

emit MigrateYield(address(_yieldAggregator), block.timestamp);
emit MigrateYield(address(_yieldAggregator));
}

/**
Expand Down Expand Up @@ -414,7 +411,7 @@ contract SharesFactoryV1 is Ownable2Step, ReentrancyGuard {
uint32 inflectionPoint,
uint128 inflectionPrice,
uint128 linearPriceSlope
) public pure returns (uint256 subTotal) {
) internal pure returns (uint256 subTotal) {
unchecked {
subTotal = basePrice * quantity;
subTotal += BondingCurveLib.linearSum(linearPriceSlope, fromSupply, quantity);
Expand Down
32 changes: 0 additions & 32 deletions contracts/core/aggregator/AaveYieldAggregator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ contract AaveYieldAggregator is Ownable, IYieldAggregator {
IAaveGateway public immutable AAVE_WETH_GATEWAY;
IERC20 public aWETH;

uint256 internal constant ACTIVE_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFF;
uint256 internal constant FROZEN_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF;
uint256 internal constant PAUSED_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFF;

constructor(address _factory, address _weth, address _aavePool, address _aaveGateway) {
FACTORY = _factory;
WETH = _weth;
Expand Down Expand Up @@ -59,7 +55,6 @@ contract AaveYieldAggregator is Ownable, IYieldAggregator {
* Only callable by the factory contract.
*/
function yieldDeposit() external onlyFactory {
require(_checkAavePoolState(), "Aave paused");
uint256 ethAmount = address(this).balance;
if (ethAmount > 0) {
AAVE_WETH_GATEWAY.depositETH{ value: ethAmount }(address(AAVE_POOL), FACTORY, 0);
Expand All @@ -71,7 +66,6 @@ contract AaveYieldAggregator is Ownable, IYieldAggregator {
* Only callable by the factory contract.
*/
function yieldWithdraw(uint256 amount) external onlyFactory {
require(_checkAavePoolState(), "Aave paused");
if (amount > 0) {
aWETH.safeTransferFrom(FACTORY, address(this), amount);
AAVE_WETH_GATEWAY.withdrawETH(address(AAVE_POOL), amount, FACTORY);
Expand All @@ -98,30 +92,4 @@ contract AaveYieldAggregator is Ownable, IYieldAggregator {
return withdrawableETHAmount - depositedETHAmount - yieldBuffer;
}
}

/**
* @notice Check Aave pool state
* @return bool true if Aave pool is active, false otherwise
* @dev For more information, see:
* https://github.com/aave/aave-v3-core/blob/master/contracts/protocol/libraries/configuration/ReserveConfiguration.sol
*/
function _checkAavePoolState() internal view returns (bool) {
uint256 configData = AAVE_POOL.getReserveData(WETH).configuration.data;
if (!(_getActive(configData) && !_getFrozen(configData) && !_getPaused(configData))) {
return false;
}
return true;
}

function _getActive(uint256 configData) internal pure returns (bool) {
return configData & ~ACTIVE_MASK != 0;
}

function _getFrozen(uint256 configData) internal pure returns (bool) {
return configData & ~FROZEN_MASK != 0;
}

function _getPaused(uint256 configData) internal pure returns (bool) {
return configData & ~PAUSED_MASK != 0;
}
}
3 changes: 3 additions & 0 deletions scripts/Base.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ contract BaseScript is Script {
mapping(uint chainid => address aave_weth_gateway) public AAVE_WETH_GATEWAY;

constructor() {
// After the factory contract has been deployed,
// set the factory address here so that the yieldAggregator can be deployed.

// Optimism Sepolia
SHARES_FACTORY[OPTIMISM_TESTNET] = 0x9F94C75341D23EAb48793b2879F6062a400132e3;
WETH[OPTIMISM_TESTNET] = 0x4200000000000000000000000000000000000006;
Expand Down
6 changes: 3 additions & 3 deletions scripts/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ contract DeployScript is BaseScript {
BlankYieldAggregator public blankYieldAggregator;

string public constant BASE_URI = "https://vv.meme/shares/uri/";
uint96 public constant BASE_PRICE = 0.005 ether;
uint32 public constant INFLECTION_POINT = 1500;
uint128 public constant INFLECTION_PRICE = 0.1025 ether;
uint96 public constant BASE_PRICE = 0.001 ether;
uint32 public constant INFLECTION_POINT = 1000;
uint128 public constant INFLECTION_PRICE = 0.1 ether;
uint128 public constant LINEAR_PRICE_SLOPE = 0;

function run() public virtual broadcast() {
Expand Down
46 changes: 35 additions & 11 deletions test/unit/SharesFactory.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity 0.8.25;

import { console } from "forge-std/console.sol";
import { SafeCastLib } from "solady/utils/SafeCastLib.sol";
import { SharesFactoryV1 } from "contracts/core/SharesFactoryV1.sol";
import { IYieldAggregator } from "contracts/interface/IYieldAggregator.sol";
import { BaseTest } from "../BaseTest.t.sol";
Expand Down Expand Up @@ -68,21 +69,25 @@ contract SharesFactoryTests is BaseTest {
uint256 aliceBalBefore = addrAlice.balance;
uint256 bobBalBefore = addrBob.balance;
uint256 referrerBalBefore = referralReceiver.balance;
// uint256 factoryBalBefore = aWETH.balanceOf(address(sharesFactory));
uint256 depositedETHAmountBefore = sharesFactory.depositedETHAmount();

(
uint256 buyPriceAfterFee,
uint256 buyPrice,
uint256 referralFee,
uint256 creatorFee
) = sharesFactory.getBuyPriceAfterFee(0, 1, referralReceiver);
_buyShare(addrBob, 0, 1, referralReceiver);
console.log(buyPriceAfterFee, buyPrice, referralFee, creatorFee);

uint256 aliceBalAfter = addrAlice.balance;
uint256 bobBalAfter = addrBob.balance;
uint256 referrerBalAfter = referralReceiver.balance;
// uint256 factoryBalAfter = aWETH.balanceOf(address(sharesFactory));
uint256 depositedETHAmountAfter = sharesFactory.depositedETHAmount();

assertEq(bobBalBefore - bobBalAfter, 5500450999999993); // Bob buy 1 share
assertEq(bobBalBefore - bobBalAfter, 5350438699999993); // Bob buy 1 share
assertEq(aliceBalAfter - aliceBalBefore, 250020499999999); // Alice receive creator fee
assertEq(referrerBalAfter - referrerBalBefore, 250020499999999); // referral receive fee
// assertEq(factoryBalAfter - factoryBalBefore, 5000409999999995); // Factory aWETH balance with rounding error
assertEq(referrerBalAfter - referrerBalBefore, 100008199999999); // referral receive fee
assertEq(depositedETHAmountAfter - depositedETHAmountBefore, 5000409999999995); // Factory records ETH Amount

uint256 bobShareBal = sharesNFT.balanceOf(addrBob, 0);
Expand All @@ -93,21 +98,25 @@ contract SharesFactoryTests is BaseTest {
uint256 aliceBalBefore = addrAlice.balance;
uint256 bobBalBefore = addrBob.balance;
uint256 referrerBalBefore = referralReceiver.balance;
// uint256 factoryBalBefore = aWETH.balanceOf(address(sharesFactory));
uint256 depositedETHAmountBefore = sharesFactory.depositedETHAmount();

(
uint256 sellPriceAfterFee,
uint256 sellPrice,
uint256 referralFee,
uint256 creatorFee
) = sharesFactory.getSellPriceAfterFee(1, 1, referralReceiver);
_sellShare(addrAlice, 1, 1, referralReceiver);
console.log(sellPriceAfterFee, sellPrice, referralFee, creatorFee);

uint256 aliceBalAfter = addrAlice.balance;
uint256 bobBalAfter = addrBob.balance;
uint256 referrerBalAfter = referralReceiver.balance;
// uint256 factoryBalAfter = aWETH.balanceOf(address(sharesFactory));
uint256 depositedETHAmountAfter = sharesFactory.depositedETHAmount();

assertEq(aliceBalAfter - aliceBalBefore, 4500163999999998); // Alice sell 1 share
assertEq(aliceBalAfter - aliceBalBefore, 4650169466666665); // Alice sell 1 share
assertEq(bobBalAfter - bobBalBefore, 250009111111111); // Bob receive creator fee
assertEq(referrerBalAfter - referrerBalBefore, 250009111111111); // Referral receive fee
// assertEq(factoryBalBefore - factoryBalAfter, 5000182222222220); // Factory aWETH balance with rounding error
assertEq(referrerBalAfter - referrerBalBefore, 100003644444444); // Referral receive fee
assertEq(depositedETHAmountBefore - depositedETHAmountAfter, 5000182222222220); // Factory records ETH Amount

uint256 aliceShareBal = sharesNFT.balanceOf(addrAlice, 1);
Expand Down Expand Up @@ -374,6 +383,22 @@ contract SharesFactoryTests is BaseTest {
}

function test_getBuyPriceAfterFeeFailed() public {
// When the quantity is 5_000 that reach th
uint256 gasBefore = gasleft();
sharesFactory.getBuyPriceAfterFee(0, 5_000, referralReceiver);
uint256 gasAfter = gasleft();
console.log("gas usage", gasBefore - gasAfter);

vm.expectRevert(bytes("Exceeds max supply"));
sharesFactory.getBuyPriceAfterFee(0, type(uint32).max, referralReceiver);

// Expect revert if supply is over `2**32 -1` (uint32)
vm.expectRevert();
sharesFactory.getSubTotal(SafeCastLib.toUint32(2**32), 1, 0);

// Expect success if supply is lower than `2**32` (uint32)
sharesFactory.getSubTotal(SafeCastLib.toUint32(2**32 - 1), 1, 0);

vm.expectRevert(bytes("Invalid shareId"));
sharesFactory.getBuyPriceAfterFee(999, 0, referralReceiver);

Expand Down Expand Up @@ -514,7 +539,6 @@ contract SharesFactoryTests is BaseTest {

function _buyShare(address sender, uint256 shareId, uint32 quantity, address referral) internal {
(uint256 buyPriceAfterFee,,,) = sharesFactory.getBuyPriceAfterFee(shareId, quantity, referral);
// console.log("buyPriceAfterFee", buyPriceAfterFee, shareId, quantity, referral);
vm.prank(address(sender));
sharesFactory.buyShare{ value: buyPriceAfterFee }(shareId, quantity, referral);
}
Expand Down

0 comments on commit cda0246

Please sign in to comment.