Skip to content

Commit d5b541f

Browse files
committed
feat(docs): direct minting rate limits guide
1 parent 72f546a commit d5b541f

4 files changed

Lines changed: 375 additions & 6 deletions

File tree

docs/fassets/03-direct-minting.mdx

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,14 @@ If the preferred executor does not act within `othersCanExecuteAfterSeconds`, an
9494

9595
Direct minting has multiple rate-limiting safeguards to protect against compromises, such as FDC exploits.
9696

97-
| Limit | Description |
98-
| :-------------------------------------- | :---------------------------------------------------- |
99-
| `directMintingHourlyLimitUBA` | Hourly minting cap |
100-
| `directMintingDailyLimitUBA` | Daily minting cap |
101-
| `directMintingLargeMintingThresholdUBA` | Threshold above which a minting is considered "large" |
102-
| `directMintingLargeMintingDelaySeconds` | Automatic delay for large mintings |
97+
| Limit | Description |
98+
| :------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------- |
99+
| [`directMintingHourlyLimitUBA`](/fassets/reference/IAssetManager#getdirectmintinghourlylimituba) | Hourly minting cap |
100+
| [`directMintingDailyLimitUBA`](/fassets/reference/IAssetManager#getdirectmintingdailylimituba) | Daily minting cap |
101+
| [`directMintingLargeMintingThresholdUBA`](/fassets/reference/IAssetManager#getdirectmintinglargemintingthresholduba) | Threshold above which a minting is considered "large" |
102+
| [`directMintingLargeMintingDelaySeconds`](/fassets/reference/IAssetManager#getdirectmintinglargemintingdelayseconds) | Automatic delay for large mintings |
103+
104+
To inspect the live limiter and pre-flight a mint against the current windows, use the [Check Direct Minting Limits guide](/fassets/developer-guides/fassets-direct-minting-limits) along with [`getDirectMintingHourlyLimiterState`](/fassets/reference/IAssetManager#getdirectmintinghourlylimiterstate), [`getDirectMintingDailyLimiterState`](/fassets/reference/IAssetManager#getdirectmintingdailylimiterstate), and [`getDirectMintingsUnblockUntilTimestamp`](/fassets/reference/IAssetManager#getdirectmintingsunblockuntiltimestamp).
103105

104106
Rate limits **throttle** rather than reject.
105107
When limits are hit, further mintings are delayed proportionally to the accumulated backlog.
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
---
2+
title: Check Direct Minting Limits
3+
tags: [intermediate, fassets]
4+
slug: fassets-direct-minting-limits
5+
description: Read the live hourly and daily direct-minting rate limits and compute the maximum mint that fits both windows.
6+
keywords: [fassets, fxrp, direct-minting, rate-limits, flare-network, xrpl]
7+
sidebar_position: 16
8+
---
9+
10+
import CodeBlock from "@theme/CodeBlock";
11+
import FAssetsDirectMintingLimits from "!!raw-loader!/examples/developer-hub-javascript/fassetsDirectMintingLimits.ts";
12+
13+
## Overview
14+
15+
This guide reads the on-chain [direct minting](/fassets/direct-minting#rate-limits) rate limits, replays the tumbling-window state off-chain, and prints the maximum amount a single mint can request right now without being delayed.
16+
17+
Direct minting is throttled by the `MintingRateLimiter` library: an hourly and a daily window cap how much FXRP can be minted before the asset manager starts pushing new mintings into a future window via [`DirectMintingDelayed`](/fassets/reference/IAssetManagerEvents#directmintingdelayed).
18+
A pre-flight check lets a frontend or executor avoid surprising the user with a delay.
19+
20+
The complete runnable example is available in the [flare-viem-starter](https://github.com/flare-foundation/flare-viem-starter/blob/main/src/fassets/direct-minting-limits.ts) repository.
21+
22+
## How the windows work
23+
24+
`MintingRateLimiter` library uses **clock-aligned tumbling windows**, not rolling windows.
25+
On initialization, the window start is snapped to a multiple of `windowSizeSeconds`:
26+
27+
```text
28+
windowStartTimestamp = block.timestamp - block.timestamp % windowSizeSeconds
29+
```
30+
31+
With `windowSizeSeconds = 3600`, the hourly window aligns to UTC hour boundaries (00:00-01:00, 01:00-02:00, …).
32+
The daily window (`86400` seconds) aligns to 00:00 UTC.
33+
34+
On every write, the limiter advances the window:
35+
36+
```text
37+
windowsElapsed = (now - windowStartTimestamp) / windowSizeSeconds
38+
mintedInCurrentWindow = subOrZero(mintedInCurrentWindow, windowsElapsed * maxPerWindow)
39+
windowStartTimestamp += windowsElapsed * windowSizeSeconds
40+
```
41+
42+
Two consequences worth noting:
43+
44+
1. **Unused capacity does not roll over.**
45+
`subOrZero` clamps at zero, so an idle hour does not give twice the cap the next hour — every fresh window starts at exactly `maxPerWindow`.
46+
2. **Over-cap mints are delayed, not rejected.**
47+
`executionAllowedAt` is set to `windowStartTimestamp + windowSize * mintedInCurrentWindow / maxPerWindow`, so overflow drains hour-by-hour through the `subOrZero` step.
48+
49+
Reading the limiter state on its own returns stale `(windowStart, minted)` values until the next write touches the contract, so this script replays the slide off-chain to show the values as they would be right now.
50+
51+
A mint can be delayed by either the hourly/daily window or the large-minting threshold — whichever pushes [`executionAllowedAt`](/fassets/reference/IAssetManagerEvents#directmintingdelayed) further into the future wins.
52+
53+
## Prerequisites
54+
55+
- The [flare-viem-starter](https://github.com/flare-foundation/flare-viem-starter) cloned locally with dependencies installed.
56+
- A configured `publicClient` pointing at the [Flare network](/network/overview) you want to query (Coston2 by default in the starter).
57+
58+
## Direct Minting Limits Script
59+
60+
<CodeBlock language="typescript" title="src/fassets/direct-minting-limits.ts">
61+
{FAssetsDirectMintingLimits}
62+
</CodeBlock>
63+
64+
## Code Breakdown
65+
66+
1. **Window sizes.**
67+
`HOURLY_WINDOW_SECONDS` and `DAILY_WINDOW_SECONDS` are the `windowSizeSeconds` values the `MintingRateLimiter.sol` library uses — `3600` aligns to UTC hour boundaries, `86400` aligns to 00:00 UTC.
68+
2. **Format helpers.**
69+
`formatUba` prints raw UBA (units of basis assets) alongside the XRP equivalent via `dropsToXrp`, and `formatTimestamp` shows ISO time with a relative offset so it is clear whether a timestamp is in the future or the past.
70+
3. **Replay the limiter slide.**
71+
`computeWindowState` mirrors the window-advancement logic in `MintingRateLimiter.sol`: it advances `windowStart` past every tumble that has elapsed and drains `mintedInCurrentWindow` by `windowsElapsed * limit`.
72+
Without this off-chain replay, you would see stale numbers between writes.
73+
4. **Print one window's live state.**
74+
`printWindow` shows the live cap, used amount, and percentage, remaining headroom, the effective window start, and the next UTC tumble.
75+
5. **Resolve `AssetManagerFXRP`.**
76+
`getContractAddressByName("AssetManagerFXRP")` looks up the asset manager through the [Flare Contract Registry](/network/guides/flare-contracts-registry).
77+
6. **Read everything in parallel.**
78+
`Promise.all` fetches the [AMG granularity](/fassets/reference/IAssetManager#assetmintinggranularityuba), the [hourly](/fassets/reference/IAssetManager#getdirectmintinghourlylimituba) and [daily](/fassets/reference/IAssetManager#getdirectmintingdailylimituba) caps, the raw limiter state for [hourly](/fassets/reference/IAssetManager#getdirectmintinghourlylimiterstate) and [daily](/fassets/reference/IAssetManager#getdirectmintingdailylimiterstate) windows, the [`unblockUntilTimestamp`](/fassets/reference/IAssetManager#getdirectmintingsunblockuntiltimestamp), and the [large-minting threshold](/fassets/reference/IAssetManager#getdirectmintinglargemintingthresholduba) and [delay](/fassets/reference/IAssetManager#getdirectmintinglargemintingdelayseconds) in one round trip.
79+
7. **Convert AMG to UBA.**
80+
The limiter stores `mintedInCurrentWindow` in AMG (`uint64`) for cheap on-chain storage.
81+
Multiplying by [`assetMintingGranularityUBA`](/fassets/reference/IAssetManager#assetmintinggranularityuba) rebases it into UBA (units of basis assets) so it can be compared against the UBA-denominated cap.
82+
8. **Honor the unblock flag.**
83+
When [`getDirectMintingsUnblockUntilTimestamp`](/fassets/reference/IAssetManager#getdirectmintingsunblockuntiltimestamp) returns a future timestamp, governance has temporarily turned off the limiter; treat full caps as available.
84+
9. **Pre-flight gate.**
85+
`bigintMin(hourlyHeadroom, dailyHeadroom)` is the largest amount that fits both windows.
86+
A request larger than this will not revert — it will mint with [`DirectMintingDelayed`](/fassets/reference/IAssetManagerEvents#directmintingdelayed) and an `executionAllowedAt` in the future.
87+
88+
## Important Notes
89+
90+
- **Large mintings have an independent fixed delay.**
91+
Mintings above [`directMintingLargeMintingThresholdUBA`](/fassets/reference/IAssetManager#getdirectmintinglargemintingthresholduba) are delayed by [`directMintingLargeMintingDelaySeconds`](/fassets/reference/IAssetManager#getdirectmintinglargemintingdelayseconds) even if the hourly and daily windows have headroom.
92+
- **Reads are stale between writes.**
93+
Always advance the window off-chain when displaying live limiter state.
94+
- **Plan UI flows around [`DirectMintingDelayed`](/fassets/reference/IAssetManagerEvents#directmintingdelayed)** so users are not surprised by a deferred execution.
95+
96+
:::tip[What's next]
97+
98+
To continue your FAssets development journey, you can:
99+
100+
- Mint with the 32-byte memo flow in [Direct Mint FXRP](/fassets/developer-guides/fassets-direct-minting).
101+
- Mint with a reserved tag in [Direct Mint FXRP with Tag](/fassets/developer-guides/fassets-direct-minting-tag).
102+
- Read the protocol details in [Direct Minting](/fassets/direct-minting).
103+
104+
:::

docs/fassets/reference/IAssetManager.mdx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,55 @@ function getDirectMintingDailyLimitUBA()
176176
returns (uint256);
177177
```
178178

179+
### `getDirectMintingHourlyLimiterState`
180+
181+
Returns the raw state of the hourly direct-minting [rate limiter](/fassets/direct-minting#rate-limits).
182+
183+
Returns:
184+
185+
- `_windowStartTimestamp`: Unix timestamp when the current hourly window started, snapped to a multiple of `windowSizeSeconds` (UTC hour boundaries).
186+
- `_mintedInCurrentWindow`: Amount minted in the current window, denominated in AMG.
187+
Multiply by [`assetMintingGranularityUBA`](#assetmintinggranularityuba) to convert to UBA.
188+
189+
The value is only updated on the next limiter write, so consumers that need a live view should advance the window off-chain — see the [Check Direct Minting Limits guide](/fassets/developer-guides/fassets-direct-minting-limits).
190+
191+
```solidity
192+
function getDirectMintingHourlyLimiterState()
193+
external view
194+
returns (uint64 _windowStartTimestamp, uint64 _mintedInCurrentWindow);
195+
```
196+
197+
### `getDirectMintingDailyLimiterState`
198+
199+
Returns the raw state of the daily direct-minting [rate limiter](/fassets/direct-minting#rate-limits).
200+
201+
Returns:
202+
203+
- `_windowStartTimestamp`: Unix timestamp when the current daily window started, snapped to 00:00 UTC.
204+
- `_mintedInCurrentWindow`: Amount minted in the current window, denominated in AMG.
205+
Multiply by [`assetMintingGranularityUBA`](#assetmintinggranularityuba) to convert to UBA.
206+
207+
The value is only updated on the next limiter write, so consumers that need a live view should advance the window off-chain — see the [Check Direct Minting Limits guide](/fassets/developer-guides/fassets-direct-minting-limits).
208+
209+
```solidity
210+
function getDirectMintingDailyLimiterState()
211+
external view
212+
returns (uint64 _windowStartTimestamp, uint64 _mintedInCurrentWindow);
213+
```
214+
215+
### `getDirectMintingsUnblockUntilTimestamp`
216+
217+
Returns the Unix timestamp until which the direct-minting [rate limiter](/fassets/direct-minting#rate-limits) is bypassed.
218+
219+
While `block.timestamp` is below this value, the hourly and daily caps are not enforced and queued mintings can execute without delay.
220+
Governance sets this via `unblockDirectMintingsUntil` to drain a backlog after manual review; the value is `0` when the limiter is active.
221+
222+
```solidity
223+
function getDirectMintingsUnblockUntilTimestamp()
224+
external view
225+
returns (uint256);
226+
```
227+
179228
### `getDirectMintingLargeMintingThresholdUBA`
180229

181230
Returns the threshold above which direct minting is considered large, in UBA.
@@ -196,6 +245,19 @@ function getDirectMintingLargeMintingDelaySeconds()
196245
returns (uint256);
197246
```
198247

248+
### `assetMintingGranularityUBA`
249+
250+
Returns the asset minting granularity — the smallest unit of f-asset stored internally within this asset manager instance.
251+
252+
The direct-minting [rate limiter](/fassets/direct-minting#rate-limits) stores `mintedInCurrentWindow` as `uint64` AMG (asset minting granularity) for cheap on-chain storage.
253+
Multiplying the limiter state by `assetMintingGranularityUBA` rebases it into UBA so it can be compared against the UBA-denominated hourly and daily caps.
254+
255+
```solidity
256+
function assetMintingGranularityUBA()
257+
external view
258+
returns (uint256);
259+
```
260+
199261
### `getDirectMintingFeeReceiver`
200262

201263
Returns the address that receives direct minting fees.

0 commit comments

Comments
 (0)