Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions packages/policy-management/src/policies/SecureMintPolicy.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.26;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/shared/interfaces/AggregatorV3Interface.sol";
import {IPolicyEngine} from "@chainlink/policy-management/interfaces/IPolicyEngine.sol";
import {Policy} from "@chainlink/policy-management/core/Policy.sol";
Expand Down Expand Up @@ -214,14 +214,14 @@ contract SecureMintPolicy is Policy {
} else if ($.reserveMarginMode == ReserveMarginMode.PositivePercentage) {
// WARNING: May round to zero for very small reserves with high margins
// e.g., reserves=1, margin=9999 → 1 * 1 / BASIS_POINTS = 0
return reserves * (BASIS_POINTS - $.reserveMarginAmount) / BASIS_POINTS;
return (reserves * (BASIS_POINTS - $.reserveMarginAmount)) / BASIS_POINTS;
} else if ($.reserveMarginMode == ReserveMarginMode.PositiveAbsolute) {
if (reserves < $.reserveMarginAmount) {
return 0;
}
return reserves - $.reserveMarginAmount;
} else if ($.reserveMarginMode == ReserveMarginMode.NegativePercentage) {
return reserves * (BASIS_POINTS + $.reserveMarginAmount) / BASIS_POINTS;
return (reserves * (BASIS_POINTS + $.reserveMarginAmount)) / BASIS_POINTS;
} else if ($.reserveMarginMode == ReserveMarginMode.NegativeAbsolute) {
return reserves + $.reserveMarginAmount;
}
Expand Down Expand Up @@ -261,11 +261,30 @@ contract SecureMintPolicy is Policy {
revert IPolicyEngine.PolicyRejected("reserve data is stale");
}

IERC20 token = IERC20(subject);
if (amount + token.totalSupply() > totalMintableSupply(uint256(reserve))) {
IERC20Metadata token = IERC20Metadata(subject);
uint8 tokenDecimals = _tokenDecimals(token);
uint8 feedDecimals = $.reservesFeed.decimals();
// Scale reserve to token decimals even when the token omits metadata
uint256 scaledReserve = uint256(reserve);
if (tokenDecimals > feedDecimals) {
uint256 factor = 10 ** (uint256(tokenDecimals) - uint256(feedDecimals));
scaledReserve *= factor;
} else if (tokenDecimals < feedDecimals) {
uint256 factor = 10 ** (uint256(feedDecimals) - uint256(tokenDecimals));
scaledReserve /= factor;
}
if (amount + token.totalSupply() > totalMintableSupply(scaledReserve)) {
revert IPolicyEngine.PolicyRejected("mint would exceed available reserves");
}

return IPolicyEngine.PolicyResult.Continue;
}

function _tokenDecimals(IERC20Metadata token) private view returns (uint8) {
try token.decimals() returns (uint8 value) {
return value;
} catch {
return 18;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be 18 as per ERC20, or 0 like datafeeds have?

}
}
}
Loading