SecureMintPolicy

The SecureMintPolicy ensures the total supply of a token does not exceed the actual reserves of the underlying asset. Before every mint, it reads the latest reserve value from a Chainlink Proof of Reserve data feed and checks whether the new total supply (current supply plus the requested mint amount) would exceed what the reserves can back. If it would, the policy rejects the transaction.

Configuration

All properties are set when the policy is first deployed and can be updated individually afterward by the policy owner.

Proof of Reserve feed

The policy needs the address of a data feed contract that reports the reserves backing your token. This is typically a Chainlink Proof of Reserve feed, but any contract that implements the AggregatorV3Interface will work.

You can find available Proof of Reserve feeds on the Data Feeds page. Each SecureMintPolicy instance supports one feed. If your token is backed by multiple reserve sources, deploy a separate SecureMintPolicy instance for each feed and add them all to the same policy chain.

Token metadata

Provide the address of the token this policy protects and its number of decimals (between 1 and 18). The policy needs both values to work correctly:

  • Token address โ€” At runtime, the policy calls totalSupply() on the protected contract to determine the current supply. The token address you provide here is used during configuration to validate the decimals value against the token's on-chain metadata.
  • Token decimals โ€” The policy uses the decimals to scale the reserve feed's value so the comparison is accurate. If the token exposes a decimals() function on-chain, the policy validates your input against it automatically. If the token does not expose decimals(), the value you provide is used as-is โ€” make sure it is correct.

Reserve margin

By default, the maximum mintable supply equals the reported reserves exactly. A reserve margin lets you adjust this relationship โ€” either requiring reserves to exceed the supply by a buffer (positive margin) or allowing the supply to slightly exceed reserves for operational flexibility (negative margin).

The margin has two parts: a mode that determines how the margin is calculated, and an amount that sets how large the margin is.

Mode
Effect
Example (reserves = 1000)
NoneNo margin. Maximum supply equals reserves directly.Limit = 1000
PositivePercentageReserves must exceed supply by a percentage. Keeps a safety cushion.10% margin -> limit = 900
PositiveAbsoluteReserves must exceed supply by a fixed amount.50 margin -> limit = 950
NegativePercentageSupply can exceed reserves by a percentage. Provides operational headroom.10% margin -> limit = 1100
NegativeAbsoluteSupply can exceed reserves by a fixed amount.50 margin -> limit = 1050

For percentage-based modes, the amount is specified in basis points (hundredths of a percent). For example, 12.34% is represented as 1234, and 100% is 10000.

Staleness threshold

Reserve data can become outdated if the feed hasn't been updated recently. The staleness threshold defines the maximum age (in seconds) of the reserve data before the policy rejects. If the feed's last update is older than this threshold, any mint attempt will fail.

To choose the right value, check the heartbeat of your Proof of Reserve feed on the Data Feeds page. The heartbeat tells you the maximum interval between feed updates. Your staleness threshold should typically match or slightly exceed the heartbeat.

Setting the staleness threshold to 0 disables the check entirely โ€” the policy will accept reserve data of any age.

Runtime behavior

The policy expects one parameter from the extractor:

ParameterTypeDescription
amountuint256The number of tokens being minted.
  • run() โ€” Fetches the latest reserve value from the configured data feed. Rejects if the reserve value is negative. If a staleness threshold is set and the data is too old, rejects. Scales the reserve value to match the token's decimals, then calculates the maximum mintable supply using the reserve margin configuration. Rejects if totalSupply + amount would exceed this limit. Returns Continue otherwise.
  • postRun() โ€” No state changes.

API reference

Setter functions

  • setReservesFeed(address reservesFeed) โ€” Updates the data feed address. Reverts if the new address is the same as the current one.
  • setTokenMetadata(address tokenAddress, uint8 tokenDecimals) โ€” Updates the token decimals. The token address must match the current one (you cannot change the protected token). Decimals must be between 1 and 18, must differ from the current value, and are validated against the token's on-chain metadata when available.
  • setReserveMargin(ReserveMarginMode mode, uint256 amount) โ€” Updates the margin mode and amount. For percentage modes, the amount is in basis points and must be <= 10000. For absolute modes, the amount must be > 0. Reverts if both values are identical to the current configuration.
  • setMaxStalenessSeconds(uint256 value) โ€” Updates the staleness threshold in seconds. Set to 0 to disable the check. Reverts if the value is the same as the current one.

View functions

  • reservesFeed() โ€” Returns the address of the current data feed.
  • reserveMarginMode() โ€” Returns the current margin mode.
  • reserveMarginAmount() โ€” Returns the current margin amount.
  • maxStalenessSeconds() โ€” Returns the current staleness threshold in seconds.

Use cases

  • Collateralized token minting โ€” Ensure that a stablecoin or asset-backed token cannot be minted beyond the value of its verified reserves.

Source

SecureMintPolicy.sol

Get the latest Chainlink content straight to your inbox.