Skip to content

Commit

Permalink
refactor: Adhere to best practices
Browse files Browse the repository at this point in the history
  • Loading branch information
0xlucian committed Jul 31, 2023
1 parent dc4ce6f commit 9170b5c
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 123 deletions.
185 changes: 126 additions & 59 deletions contracts/PegStability/PegStability.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeab
import "@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol";
import { ResilientOracleInterface } from "@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol";

interface VAI {
interface IVAI {
function balanceOf(address usr) external returns (uint256);

function transferFrom(address src, address dst, uint amount) external returns (bool);
Expand All @@ -17,7 +17,7 @@ interface VAI {
function burn(address usr, uint wad) external;
}

interface DecimalProvider {
interface IDecimalProvider {
function decimals() external view returns (uint8);
}

Expand Down Expand Up @@ -46,9 +46,9 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
uint256 public immutable ONE_DOLLAR;

/// @notice The address of the VAI token contract.
/// @notice VAI token contract.
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address public immutable VAI_ADDRESS;
IVAI public immutable VAI;

/// @notice The address of the stable token contract.
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
Expand Down Expand Up @@ -88,7 +88,7 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
event FeeOutChanged(uint256 oldFeeOut, uint256 newFeeOut);

/// @notice Event emitted when vaiMintCap state var is modified.
event VaiMintCapChanged(uint256 oldCap, uint256 newCap);
event VAIMintCapChanged(uint256 oldCap, uint256 newCap);

/// @notice Event emitted when venusTreasury state var is modified.
event VenusTreasuryChanged(address indexed oldTreasury, address indexed newTreasury);
Expand All @@ -100,26 +100,65 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
event StableForVAISwapped(uint256 stableIn, uint256 vaiOut, uint256 fee);

/// @notice Event emitted when stable token is swapped for VAI.
event VaiForStableSwapped(uint256 vaiBurnt, uint256 vaiFee, uint256 stableOut);
event VAIForStableSwapped(uint256 vaiBurnt, uint256 stableOut, uint256 vaiFee);

/// @notice thrown when contract is in paused state
error Paused();

/// @notice thrown when attempted to pause an already paused contract
error AlreadyPaused();

/// @notice thrown when attempted to resume the contract if it is already resumed
error NotPaused();

/// @notice thrown when stable token has more than 18 decimals
error TooManyDecimals();

/// @notice thrown when fee is >= 100%
error InvalidFee();

/// @notice thrown when a zero address is passed as a function parameter
error ZeroAddress();

/// @notice thrown when a zero amount is passed as stable token amount parameter
error ZeroAmount();

/// @notice thrown when the user doesn't have enough VAI balance to provide for the amount of stable tokens he wishes to get
error NotEnoughVAI();

/// @notice thrown when the amount of VAI to be burnt exceeds the vaiMinted amount
error VAIMintedUnderflow();

/// @notice thrown when the VAI transfer to treasury fails
error VAITransferFail();

/// @notice thrown when VAI to be minted will go beyond the mintCap threshold
error VAIMintCapReached();
/// @notice thrown when fee calculation will result in rounding down to 0 due to stable token amount being a too small number
error AmountTooSmall();

/**
* @dev Prevents functions to execute when contract is paused.
*/
modifier isActive() {
require(!isPaused, "Contract is paused.");
if (isPaused) revert Paused();
_;
}

/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address stableTokenAddress_, address vaiAddress_) {
ensureNonzeroAddress(stableTokenAddress_);
ensureNonzeroAddress(vaiAddress_);
_ensureNonzeroAddress(stableTokenAddress_);
_ensureNonzeroAddress(vaiAddress_);

uint256 decimals_ = IDecimalProvider(stableTokenAddress_).decimals();

if (decimals_ > 18) {
revert TooManyDecimals();
}

uint256 decimals_ = DecimalProvider(stableTokenAddress_).decimals();
require(decimals_ <= 18, "too much decimals");
ONE_DOLLAR = 10 ** (36 - decimals_); // 1$ scaled to the decimals returned by our Oracle
STABLE_TOKEN_ADDRESS = stableTokenAddress_;
VAI_ADDRESS = vaiAddress_;
VAI = IVAI(vaiAddress_);
_disableInitializers();
}

Expand All @@ -140,14 +179,15 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
uint256 feeOut_,
uint256 vaiMintCap_
) external initializer {
ensureNonzeroAddress(accessControlManager_);
ensureNonzeroAddress(venusTreasury_);
ensureNonzeroAddress(oracleAddress_);
_ensureNonzeroAddress(accessControlManager_);
_ensureNonzeroAddress(venusTreasury_);
_ensureNonzeroAddress(oracleAddress_);
__AccessControlled_init(accessControlManager_);
__ReentrancyGuard_init();

require(feeIn_ < BASIS_POINTS_DIVISOR, "Invalid fee in.");
require(feeOut_ < BASIS_POINTS_DIVISOR, "Invalid fee out.");
if (feeIn_ >= BASIS_POINTS_DIVISOR || feeOut_ >= BASIS_POINTS_DIVISOR) {
revert InvalidFee();
}

feeIn = feeIn_;
feeOut = feeOut_;
Expand All @@ -164,34 +204,40 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
* @param stableTknAmount The amount of stable tokens to receive.
* @return The amount of VAI received and burnt from the sender.
*/
// @custom:event Emits VaiForStableSwapped event.
// @custom:event Emits VAIForStableSwapped event.
function swapVAIForStable(
address receiver,
uint256 stableTknAmount
) external isActive nonReentrant returns (uint256) {
ensureNonzeroAddress(receiver);
require(stableTknAmount > 0, "Amount must be greater than zero.");
_ensureNonzeroAddress(receiver);
_ensureNonzeroAmount(stableTknAmount);

// update oracle price and calculate USD value of the stable token amount scaled in 18 decimals
oracle.updateAssetPrice(STABLE_TOKEN_ADDRESS);
uint256 stableTknAmountUSD = previewTokenUSDAmount(stableTknAmount, FeeDirection.OUT);
uint256 stableTknAmountUSD = _previewTokenUSDAmount(stableTknAmount, FeeDirection.OUT);
uint256 fee = _calculateFee(stableTknAmountUSD, FeeDirection.OUT);

require(VAI(VAI_ADDRESS).balanceOf(msg.sender) >= stableTknAmountUSD + fee, "Not enough VAI.");
require(vaiMinted >= stableTknAmountUSD, "Can't burn more VAI than minted.");
if (VAI.balanceOf(msg.sender) < stableTknAmountUSD + fee) {
revert NotEnoughVAI();
}
if (vaiMinted < stableTknAmountUSD) {
revert VAIMintedUnderflow();
}

unchecked {
vaiMinted -= stableTknAmountUSD;
}

if (fee != 0) {
bool success = VAI(VAI_ADDRESS).transferFrom(msg.sender, venusTreasury, fee);
require(success, "VAI fee transfer failed.");
bool success = VAI.transferFrom(msg.sender, venusTreasury, fee);
if (!success) {
revert VAITransferFail();
}
}

VAI(VAI_ADDRESS).burn(msg.sender, stableTknAmountUSD);
VAI.burn(msg.sender, stableTknAmountUSD);
IERC20Upgradeable(STABLE_TOKEN_ADDRESS).safeTransfer(receiver, stableTknAmount);
emit VaiForStableSwapped(stableTknAmountUSD, fee, stableTknAmount);
emit VAIForStableSwapped(stableTknAmountUSD, stableTknAmount, fee);
return stableTknAmountUSD;
}

Expand All @@ -207,9 +253,8 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
address receiver,
uint256 stableTknAmount
) external isActive nonReentrant returns (uint256) {
ensureNonzeroAddress(receiver);
require(stableTknAmount > 0, "Amount must be greater than zero.");

_ensureNonzeroAddress(receiver);
_ensureNonzeroAmount(stableTknAmount);
// transfer IN, supporting fee-on-transfer tokens
uint256 balanceBefore = IERC20Upgradeable(STABLE_TOKEN_ADDRESS).balanceOf(address(this));
IERC20Upgradeable(STABLE_TOKEN_ADDRESS).safeTransferFrom(msg.sender, address(this), stableTknAmount);
Expand All @@ -220,23 +265,25 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {

// update oracle price and calculate USD value of the stable token amount scaled in 18 decimals
oracle.updateAssetPrice(STABLE_TOKEN_ADDRESS);
uint256 actualTransferAmtInUSD = previewTokenUSDAmount(actualTransferAmt, FeeDirection.IN);
uint256 actualTransferAmtInUSD = _previewTokenUSDAmount(actualTransferAmt, FeeDirection.IN);

//calculate feeIn
uint256 fee = _calculateFee(actualTransferAmtInUSD, FeeDirection.IN);
uint256 vaiToMint = actualTransferAmtInUSD - fee;

require(vaiMinted + actualTransferAmtInUSD <= vaiMintCap, "VAI mint cap reached.");
if (vaiMinted + actualTransferAmtInUSD > vaiMintCap) {
revert VAIMintCapReached();
}
unchecked {
vaiMinted += actualTransferAmtInUSD;
}

// mint VAI to receiver
VAI(VAI_ADDRESS).mint(receiver, vaiToMint);
VAI.mint(receiver, vaiToMint);

// mint VAI fee to venus treasury
if (fee != 0) {
VAI(VAI_ADDRESS).mint(venusTreasury, fee);
VAI.mint(venusTreasury, fee);
}

emit StableForVAISwapped(actualTransferAmt, vaiToMint, fee);
Expand All @@ -252,7 +299,9 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
// @custom:event Emits PSMPaused event.
function pause() external {
_checkAccessAllowed("pause()");
require(!isPaused, "PSM is already paused.");
if (isPaused) {
revert AlreadyPaused();
}
isPaused = true;
emit PSMPaused(msg.sender);
}
Expand All @@ -264,7 +313,9 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
// @custom:event Emits PSMResumed event.
function resume() external {
_checkAccessAllowed("resume()");
require(isPaused, "PSM is not paused.");
if (!isPaused) {
revert NotPaused();
}
isPaused = false;
emit PSMResumed(msg.sender);
}
Expand All @@ -278,7 +329,9 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
function setFeeIn(uint256 feeIn_) external {
_checkAccessAllowed("setFeeIn(uint256)");
// feeIn = 10000 = 100%
require(feeIn_ < BASIS_POINTS_DIVISOR, "Invalid fee.");
if (feeIn_ >= BASIS_POINTS_DIVISOR) {
revert InvalidFee();
}
uint256 oldFeeIn = feeIn;
feeIn = feeIn_;
emit FeeInChanged(oldFeeIn, feeIn_);
Expand All @@ -293,7 +346,9 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
function setFeeOut(uint256 feeOut_) external {
_checkAccessAllowed("setFeeOut(uint256)");
// feeOut = 10000 = 100%
require(feeOut_ < BASIS_POINTS_DIVISOR, "Invalid fee.");
if (feeOut_ >= BASIS_POINTS_DIVISOR) {
revert InvalidFee();
}
uint256 oldFeeOut = feeOut;
feeOut = feeOut_;
emit FeeOutChanged(oldFeeOut, feeOut_);
Expand All @@ -303,12 +358,12 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
* @dev Set the maximum amount of VAI that can be minted through this contract.
* @param vaiMintCap_ The new maximum amount of VAI that can be minted.
*/
// @custom:event Emits VaiMintCapChanged event.
function setVaiMintCap(uint256 vaiMintCap_) external {
_checkAccessAllowed("setVaiMintCap(uint256)");
uint256 oldVaiMintCap = vaiMintCap;
// @custom:event Emits VAIMintCapChanged event.
function setVAIMintCap(uint256 vaiMintCap_) external {
_checkAccessAllowed("setVAIMintCap(uint256)");
uint256 oldVAIMintCap = vaiMintCap;
vaiMintCap = vaiMintCap_;
emit VaiMintCapChanged(oldVaiMintCap, vaiMintCap_);
emit VAIMintCapChanged(oldVAIMintCap, vaiMintCap_);
}

/**
Expand All @@ -319,7 +374,7 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
// @custom:event Emits VenusTreasuryChanged event.
function setVenusTreasury(address venusTreasury_) external {
_checkAccessAllowed("setVenusTreasury(address)");
ensureNonzeroAddress(venusTreasury_);
_ensureNonzeroAddress(venusTreasury_);
address oldTreasuryAddress = venusTreasury;
venusTreasury = venusTreasury_;
emit VenusTreasuryChanged(oldTreasuryAddress, venusTreasury_);
Expand All @@ -333,7 +388,7 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
// @custom:event Emits OracleChanged event.
function setOracle(address oracleAddress_) external {
_checkAccessAllowed("setOracle(address)");
ensureNonzeroAddress(oracleAddress_);
_ensureNonzeroAddress(oracleAddress_);
address oldOracleAddress = address(oracle);
oracle = ResilientOracleInterface(oracleAddress_);
emit OracleChanged(oldOracleAddress, oracleAddress_);
Expand All @@ -353,12 +408,13 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
* @return The amount of VAI that would be taken from the user.
*/
function previewSwapVAIForStable(uint256 stableTknAmount) external view returns (uint256) {
require(stableTknAmount > 0, "Amount must be greater than zero.");

uint256 stableTknAmountUSD = previewTokenUSDAmount(stableTknAmount, FeeDirection.OUT);
_ensureNonzeroAmount(stableTknAmount);
uint256 stableTknAmountUSD = _previewTokenUSDAmount(stableTknAmount, FeeDirection.OUT);
uint256 fee = _calculateFee(stableTknAmountUSD, FeeDirection.OUT);

require(vaiMinted >= stableTknAmountUSD, "Can't burn more VAI than minted.");
if (vaiMinted < stableTknAmountUSD) {
revert VAIMintedUnderflow();
}

return stableTknAmountUSD + fee;
}
Expand All @@ -370,15 +426,16 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
* @return The amount of VAI that would be sent to the receiver.
*/
function previewSwapStableForVAI(uint256 stableTknAmount) external view returns (uint256) {
require(stableTknAmount > 0, "Amount must be greater than zero.");

uint256 stableTknAmountUSD = previewTokenUSDAmount(stableTknAmount, FeeDirection.IN);
_ensureNonzeroAmount(stableTknAmount);
uint256 stableTknAmountUSD = _previewTokenUSDAmount(stableTknAmount, FeeDirection.IN);

//calculate feeIn
uint256 fee = _calculateFee(stableTknAmountUSD, FeeDirection.IN);
uint256 vaiToMint = stableTknAmountUSD - fee;

require(vaiMinted + stableTknAmountUSD <= vaiMintCap, "VAI mint cap reached.");
if (vaiMinted + stableTknAmountUSD > vaiMintCap) {
revert VAIMintCapReached();
}

return vaiToMint;
}
Expand All @@ -389,8 +446,8 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
* @param direction The direction of the swap.
* @return The USD value of the given amount of stable tokens scaled by 1e18 taking into account the direction of the swap
*/
function previewTokenUSDAmount(uint256 amount, FeeDirection direction) internal view returns (uint256) {
return (amount * getPriceInUSD(direction)) / MANTISSA_ONE;
function _previewTokenUSDAmount(uint256 amount, FeeDirection direction) internal view returns (uint256) {
return (amount * _getPriceInUSD(direction)) / MANTISSA_ONE;
}

/**
Expand All @@ -399,7 +456,7 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
* @param direction The direction of the swap: FeeDirection.IN or FeeDirection.OUT.
* @return The price in USD, adjusted based on the selected direction.
*/
function getPriceInUSD(FeeDirection direction) internal view returns (uint256) {
function _getPriceInUSD(FeeDirection direction) internal view returns (uint256) {
// get price with a scale = (36 - asset_decimals)
uint256 price = oracle.getPrice(STABLE_TOKEN_ADDRESS);

Expand Down Expand Up @@ -430,7 +487,9 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
return 0;
} else {
// checking if the percent calculation will result in rounding down to 0
require(amount * feePercent >= BASIS_POINTS_DIVISOR, "Amount too small.");
if (amount * feePercent < BASIS_POINTS_DIVISOR) {
revert AmountTooSmall();
}
return (amount * feePercent) / BASIS_POINTS_DIVISOR;
}
}
Expand All @@ -439,7 +498,15 @@ contract PegStability is AccessControlledV8, ReentrancyGuardUpgradeable {
* @notice Checks that the address is not the zero address.
* @param someone The address to check.
*/
function ensureNonzeroAddress(address someone) private pure {
require(someone != address(0), "Can't be zero address.");
function _ensureNonzeroAddress(address someone) private pure {
if (someone == address(0)) revert ZeroAddress();
}

/**
* @notice Checks that the amount passed as stable tokens is bigger than zero
* @param amount The amount to validate
*/
function _ensureNonzeroAmount(uint256 amount) private pure {
if (amount == 0) revert ZeroAmount();
}
}
Loading

0 comments on commit 9170b5c

Please sign in to comment.