Skip to content

Commit 5a41655

Browse files
authored
sui: rephrase to remove em dashes (#187)
* sui: change em-dashes into regular dashes * Revert "sui: change em-dashes into regular dashes" This reverts commit 2e4f192. * sui: rephrase to remove em dashes
1 parent 886d8ce commit 5a41655

4 files changed

Lines changed: 24 additions & 24 deletions

File tree

content/contracts-sui/1.x/api/fixed-point.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ Returns the largest `UD30x9` value `r` such that `r * r <= x`. The result is rou
245245
Returns the natural logarithm of `x`, derived from `log2` via `ln(x) = log2(x) * ln(2)`. The result is rounded down and may sit up to 2 ulps below the true value.
246246

247247
Aborts with `ELogUndefined` if `x` is zero.
248-
Aborts with `ELogResultUnrepresentable` if `x` is in `(0, 1)` (the result would be negative and cannot be represented in `UD30x9` use [`SD29x9::ln`](#sd29x9_base-ln) instead).
248+
Aborts with `ELogResultUnrepresentable` if `x` is in `(0, 1)` (the result would be negative and cannot be represented in `UD30x9`, so use [`SD29x9::ln`](#sd29x9_base-ln) instead).
249249
</APIItem>
250250

251251
<APIItem
@@ -256,7 +256,7 @@ Aborts with `ELogResultUnrepresentable` if `x` is in `(0, 1)` (the result would
256256
Returns the base-2 logarithm of `x`. The result is rounded down and sits at most 2 ulps below the true `log2(x)`. For inputs in `[1, 2)` the result is exact.
257257

258258
Aborts with `ELogUndefined` if `x` is zero.
259-
Aborts with `ELogResultUnrepresentable` if `x` is in `(0, 1)` (the result would be negative and cannot be represented in `UD30x9` use [`SD29x9::log2`](#sd29x9_base-log2) instead).
259+
Aborts with `ELogResultUnrepresentable` if `x` is in `(0, 1)` (the result would be negative and cannot be represented in `UD30x9`, so use [`SD29x9::log2`](#sd29x9_base-log2) instead).
260260
</APIItem>
261261

262262
<APIItem
@@ -267,7 +267,7 @@ Aborts with `ELogResultUnrepresentable` if `x` is in `(0, 1)` (the result would
267267
Returns the base-10 logarithm of `x`. Exact when `x` is an integer power of ten (`10^k`, `k >= 0`). Otherwise derived from `log2` via `log10(x) = log2(x) * log10(2)` and rounded down; the result may sit up to 2 ulps below the true value.
268268

269269
Aborts with `ELogUndefined` if `x` is zero.
270-
Aborts with `ELogResultUnrepresentable` if `x` is in `(0, 1)` (the result would be negative and cannot be represented in `UD30x9` use [`SD29x9::log10`](#sd29x9_base-log10) instead).
270+
Aborts with `ELogResultUnrepresentable` if `x` is in `(0, 1)` (the result would be negative and cannot be represented in `UD30x9`, so use [`SD29x9::log10`](#sd29x9_base-log10) instead).
271271
</APIItem>
272272

273273
<APIItem

content/contracts-sui/1.x/api/utils.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ The variant fields are not directly accessible from outside the module; read the
8282
>
8383
Creates a token bucket with an explicit initial balance and refill anchor. `available` accrues `refill_amount` every `refill_interval_ms`, capped at `capacity`.
8484

85-
`last_refill_ms` anchors the refill schedule; for a greenfield limiter pass `clock.timestamp_ms()`. Pass an earlier value to preserve the refill phase across a reconstruction, but only when the rate (`refill_amount` / `refill_interval_ms`) is unchanged — carrying an old anchor into a new rate pre-credits elapsed time at the new rate and can briefly exceed it. `initial_available` is the starting balance and must be `<= capacity`; `0` forces a wait for the first refill.
85+
`last_refill_ms` anchors the refill schedule; for a greenfield limiter pass `clock.timestamp_ms()`. Pass an earlier value to preserve the refill phase across a reconstruction, but only when the rate (`refill_amount` / `refill_interval_ms`) is unchanged. Carrying an old anchor into a new rate pre-credits elapsed time at the new rate and can briefly exceed it. `initial_available` is the starting balance and must be `<= capacity`; `0` forces a wait for the first refill.
8686

8787
Aborts with `EZeroCapacity` if `capacity` is `0`.
8888

@@ -124,7 +124,7 @@ A caller can spend the full quota at the end of one window and again at the star
124124
>
125125
Creates a cooldown limiter. Up to `capacity` units may be consumed (in any combination of per-call amounts) before the limiter gates. Once `available` reaches `0`, `cooldown_end_ms` is set to `now + cooldown_ms`; no further consume succeeds until `now >= cooldown_end_ms`, at which point `available` resets to `capacity`.
126126

127-
`cooldown_end_ms <= now` means no gate is armed. The valid seed combinations are: `initial_available > 0` with `cooldown_end_ms <= now` (granted up to `initial_available` units before the first arm); `initial_available == 0` with `cooldown_end_ms > now` (gated reconstructing a limiter mid-throttle, or arming a delay in front of an action); and `initial_available == 0` with `cooldown_end_ms <= now` (released projects to fully available on the next read or consume).
127+
`cooldown_end_ms <= now` means no gate is armed. The valid seed combinations are: `initial_available > 0` with `cooldown_end_ms <= now` (granted - up to `initial_available` units before the first arm); `initial_available == 0` with `cooldown_end_ms > now` (gated - reconstructing a limiter mid-throttle, or arming a delay in front of an action); and `initial_available == 0` with `cooldown_end_ms <= now` (released - projects to fully available on the next read or consume).
128128

129129
Aborts with `EZeroCapacity` if `capacity` is `0`.
130130

content/contracts-sui/1.x/rate-limiter.mdx

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ title: Rate Limiter
66
The example code snippets used in this guide are experimental and have not been audited. They simply help exemplify usage of the OpenZeppelin Sui Package.
77
</Callout>
88

9-
The `rate_limiter` module provides an embeddable, multi-strategy rate-limiting primitive for Sui Move. `RateLimiter` is a plain `store + drop` value **not** a Sui object. You embed it as a field inside an object you already own (a vault, a capability, a per-player record) and call one function on the hot path. There is no registry, no shared policy object, no admin cap, and no separate ID to track: the limiter's scope is whatever object it lives inside.
9+
The `rate_limiter` module provides an embeddable, multi-strategy rate-limiting primitive for Sui Move. `RateLimiter` is a plain `store + drop` value, **not** a Sui object. You embed it as a field inside an object you already own (a vault, a capability, a per-player record) and call one function on the hot path. There is no registry, no shared policy object, no admin cap, and no separate ID to track: the limiter's scope is whatever object it lives inside.
1010

11-
Rate limiting is otherwise reimplemented ad hoc by every protocol that needs itwithdrawal throttles, daily caps, action cooldowns. `rate_limiter` generalizes the proven rate limiting math into a reusable primitive and exposes three strategies (bucket, fixed window and cooldown) that share a single API.
11+
Rate limiting is otherwise reimplemented ad hoc by every protocol that needs it, whether for withdrawal throttles, daily caps, or action cooldowns. `rate_limiter` generalizes the proven rate limiting math into a reusable primitive and exposes three strategies (bucket, fixed window and cooldown) that share a single API.
1212

1313
## Use cases
1414

@@ -18,7 +18,7 @@ Use `rate_limiter` when your protocol needs:
1818
- Independent per-user or per-object budgets that refill over time.
1919
- A hard per-window quota, such as "100 mints per hour".
2020
- A cooldown that gates the reuse of an action after a burst.
21-
- A delay that must elapse before an action — like a claim or an unstake can execute.
21+
- A delay that must elapse before an action such as a claim or an unstake can execute.
2222
- A regenerating resource in non-financial logic, such as health, mana, stamina, or energy.
2323

2424
## Import
@@ -60,7 +60,7 @@ public struct Vault has key {
6060
6161
public fun create(clock: &Clock, ctx: &mut TxContext) {
6262
// Bucket: cap 1_000 units, refilling 100 units every 6 s, starting full.
63-
// (units are whatever you meterhere MIST)
63+
// (units are whatever you meter; here, MIST)
6464
let limiter = rate_limiter::new_bucket(1_000, 100, 6_000, clock.timestamp_ms(), 1_000, clock);
6565
transfer::share_object(Vault { id: object::new(ctx), limiter, funds: balance::zero() });
6666
}
@@ -75,7 +75,7 @@ The limiter check runs *before* the side effect, so a denied withdrawal never to
7575

7676
## Gating reuse with a cooldown
7777

78-
`Cooldown` allows up to `capacity` uses, then locks for `cooldown_ms` before the batch refreshes. The consume call is identical to the bucket only the constructor changes and `available` surfaces the remaining uses without mutating:
78+
`Cooldown` allows up to `capacity` uses, then locks for `cooldown_ms` before the batch refreshes. The consume call is identical to the bucket (only the constructor changes), and `available` surfaces the remaining uses without mutating:
7979

8080
```move
8181
module my_game::ability;
@@ -99,7 +99,7 @@ public fun dash(self: &mut Hero, clock: &Clock) {
9999
// ... apply the dash ...
100100
}
101101
102-
/// Dashes still available right now 0 while the cooldown is active.
102+
/// Dashes still available right now; 0 while the cooldown is active.
103103
public fun dashes_left(self: &Hero, clock: &Clock): u64 {
104104
self.dash.available(clock)
105105
}
@@ -109,7 +109,7 @@ The same shape arms a one-shot delay in front of an action: construct with `init
109109

110110
## Reconfiguring a limiter
111111

112-
There are no in-place setters. To change a limiter's configuration, snapshot its live state through the getters, build a fresh `RateLimiter`, and overwrite the field `drop` lets the old value go. Gate the entry function with the same authority that guards your other privileged operations:
112+
There are no in-place setters. To change a limiter's configuration, snapshot its live state through the getters, build a fresh `RateLimiter`, and overwrite the field, since `drop` lets the old value go. Gate the entry function with the same authority that guards your other privileged operations:
113113

114114
```move
115115
/// Raise the vault's withdrawal ceiling, preserving the current balance and refill phase.
@@ -132,54 +132,54 @@ public fun set_capacity(self: &mut Vault, new_capacity: u64, clock: &Clock) {
132132
}
133133
```
134134

135-
Reading `last_refill_ms(clock)` and `available(clock)` together keeps the refill phase intact across the swap. Other policiesre-anchoring, a full reset, or switching variant entirely are just different constructor calls over the same snapshot.
135+
Reading `last_refill_ms(clock)` and `available(clock)` together keeps the refill phase intact across the swap. Other policies, such as re-anchoring, a full reset, or switching variant entirely, are just different constructor calls over the same snapshot.
136136

137137
<Callout type="warn">
138-
Preserving the anchor while *also* changing the refill rate re-prices the elapsed time under the new rate. A long gap since `last_refill_ms` can mint up to `capacity` tokens the instant the new limiter is constructed. If you change `refill_amount` or `refill_interval_ms`, re-anchor to `clock.timestamp_ms()` (and pick `initial_available` explicitly) instead of carrying the old anchor forward. Preserving the anchor is safe only when the rate is unchanged as in the `set_capacity` example above.
138+
Preserving the anchor while *also* changing the refill rate re-prices the elapsed time under the new rate. A long gap since `last_refill_ms` can mint up to `capacity` tokens the instant the new limiter is constructed. If you change `refill_amount` or `refill_interval_ms`, re-anchor to `clock.timestamp_ms()` (and pick `initial_available` explicitly) instead of carrying the old anchor forward. Preserving the anchor is safe only when the rate is unchanged, as in the `set_capacity` example above.
139139
</Callout>
140140

141141
## Key concepts
142142

143-
- **Embedded value, not an object.** `RateLimiter` has `store + drop` and no `key`/`UID`. It can only exist as a field of a parent value you own, and its scope is that parent no global state, no contention, no registry to index. Withholding `copy` is what prevents over-issuance: a duplicable limiter would multiply your configured capacity by N.
143+
- **Embedded value, not an object.** `RateLimiter` has `store + drop` and no `key`/`UID`. It can only exist as a field of a parent value you own, and its scope is that parent, with no global state, no contention, and no registry to index. Withholding `copy` is what prevents over-issuance: a duplicable limiter would multiply your configured capacity by N.
144144

145145
- **Project-on-read.** `available(clock)` returns the units consumable *right now*, projecting any pending refill, window rollover, or cooldown release without mutating. `try_consume` commits that projection only on success.
146146

147147
- **All-or-nothing consume.** `try_consume` either consumes `amount` and commits the time projection, or returns `false` and writes nothing (the lone exception is a pathological `Cooldown` config whose next deadline would overflow `u64`, which aborts `ECooldownDeadlineOverflow`). There is no "denied but charged", so you can safely probe with `try_consume` without skewing state. `consume_or_abort` is the ergonomic wrapper that turns a refusal into an `ERateLimited` abort.
148148

149-
- **Reconfigure by reconstruction.** There are no in-place setters; you change a limiter by overwriting the field with a fresh one (see [Reconfiguring a limiter](#reconfiguring-a-limiter)). Every policy preserve anchor, re-anchor, full reset, proportional carry, freeze an in-flight gate is expressible in your own code; the library validates only structural validity on construction.
149+
- **Reconfigure by reconstruction.** There are no in-place setters; you change a limiter by overwriting the field with a fresh one (see [Reconfiguring a limiter](#reconfiguring-a-limiter)). Every policy (preserve anchor, re-anchor, full reset, proportional carry, freeze an in-flight gate) is expressible in your own code; the library validates only structural validity on construction.
150150

151-
- **Authorization is yours.** Whoever holds `&mut` to the field may consume and reconfigure. Gate your entry functions with whatever model fits a capability, [`openzeppelin_access`](/contracts-sui/1.x/access), governance, or a multisig. The module makes no access-control claim.
151+
- **Authorization is yours.** Whoever holds `&mut` to the field may consume and reconfigure. Gate your entry functions with whatever model fits, such as a capability, [`openzeppelin_access`](/contracts-sui/1.x/access), governance, or a multisig. The module makes no access-control claim.
152152

153153
## Common mistakes
154154

155155
| Mistake | What happens | How to fix |
156156
|---|---|---|
157-
| `try_consume(rl.available(clock), clock)` when empty | Returns `false` (`available()` is `0`, and a zero-unit consume is rejected) surprising if you expect "consume all available" to succeed as a no-op | Guard: `let n = rl.available(clock); if (n > 0) { rl.try_consume(n, clock); }` |
157+
| `try_consume(rl.available(clock), clock)` when empty | Returns `false` (`available()` is `0`, and a zero-unit consume is rejected), which is surprising if you expect "consume all available" to succeed as a no-op | Guard: `let n = rl.available(clock); if (n > 0) { rl.try_consume(n, clock); }` |
158158
| Calling `consume_or_abort` with `amount == 0` | Aborts `EInvalidAmount` (`try_consume` returns `false` instead) | Treat zero-unit work as a no-op in your own code; never pass `0`. |
159159
| Reading `cooldown_end_ms()` while `available > 0` | Returns a stale value (the gate is only consulted when `available == 0`) | Only interpret `cooldown_end_ms()` when `available(clock) == 0`. |
160-
| Expecting a partially drained `Cooldown` to refill on each cooldown | It does not — the gate arms and the batch refreshes to `capacity` only once the balance hits exactly `0`. Capacity 10 with 7 spent reports `3` forever until those `3` are consumed and the gate then elapses | Drain the batch fully before the cooldown applies; the refresh is per-emptied-batch, not per-elapsed-interval. |
160+
| Expecting a partially drained `Cooldown` to refill on each cooldown | It does not. The gate arms and the batch refreshes to `capacity` only once the balance hits exactly `0`. Capacity 10 with 7 spent reports `3` forever until those `3` are consumed and the gate then elapses | Drain the batch fully before the cooldown applies; the refresh is per-emptied-batch, not per-elapsed-interval. |
161161
| Seeding `new_cooldown` with `initial_available > 0` and `cooldown_end_ms > now` | Aborts `ECooldownArmedWithTokens` | Use one of the valid seed combinations (see the [API reference](/contracts-sui/1.x/api/utils)). |
162162
| Passing a future `last_refill_ms` / `window_start_ms` | Aborts `EBucketAnchorInFuture` / `EWindowAnchorInFuture` | Pass `clock.timestamp_ms()` (or an earlier value). |
163163
| Expecting `FixedWindow` to prevent boundary bursts | A caller can spend `capacity` at the end of one window and `capacity` at the start of the next (a `2 * capacity` burst) | Use `Bucket` if a smooth ceiling matters; `FixedWindow` only guarantees the per-window cap. |
164164
| Preserving the bucket anchor while changing the refill rate | Elapsed time is re-priced under the new rate, minting up to `capacity` phantom tokens the instant the limiter is rebuilt | When changing `refill_amount` / `refill_interval_ms`, re-anchor to `clock.timestamp_ms()` and set `initial_available` explicitly; only preserve the anchor when the rate is unchanged. |
165-
| Assuming the limiter authorizes callers | It does not anyone with `&mut` to the field can consume and reconfigure | Gate your entry functions with a capability, `openzeppelin_access`, governance, or a multisig. |
165+
| Assuming the limiter authorizes callers | It does not; anyone with `&mut` to the field can consume and reconfigure | Gate your entry functions with a capability, `openzeppelin_access`, governance, or a multisig. |
166166

167167
## FAQ
168168

169169
**Do I need to register or track the limiter anywhere?**
170170
No. It is a field of your object, not a separate object. There is no registry, no ID, nothing for a front end or indexer to track per environment.
171171

172172
**How do I disable a limiter (pause)?**
173-
The module has no `enabled` toggle by design. The simplest pause is in your own object a `bool` checked before the consume. There is no literal deny-all config (every constructor requires positive parameters; `capacity == 0` aborts `EZeroCapacity`, and a `Bucket`/`FixedWindow` seeded empty just refills over time), but a `Cooldown` seeded with `initial_available = 0` and a far-future `cooldown_end_ms` stays gated and denies every consume until you reconstruct it.
173+
The module has no `enabled` toggle by design. The simplest pause is in your own object: a `bool` checked before the consume. There is no literal deny-all config (every constructor requires positive parameters; `capacity == 0` aborts `EZeroCapacity`, and a `Bucket`/`FixedWindow` seeded empty just refills over time), but a `Cooldown` seeded with `initial_available = 0` and a far-future `cooldown_end_ms` stays gated and denies every consume until you reconstruct it.
174174

175175
**Does the library emit events?**
176176
No. Observability is left to you, since you hold the context an event needs (which user, which object). Emit your own event after a successful consume if you want indexing.
177177

178178
**Can I probe the limiter without affecting it?**
179-
Yes. You can use the `available(clock)` getter (projects pending refills). Also, a failed `try_consume` (returns `false`) is observably a no-op across all three variants no anchor advance, no balance change, no gate re-arm.
179+
Yes. You can use the `available(clock)` getter (projects pending refills). Also, a failed `try_consume` (returns `false`) is observably a no-op across all three variants, with no anchor advance, no balance change, and no gate re-arm.
180180

181181
**How do I inspect a limiter whose variant I don't know (for example, a `Table` mixing variants)?**
182-
Branch on `is_bucket()` / `is_fixed_window()` / `is_cooldown()` before calling a variant-typed getter (`refill_amount`, `window_ms`, `cooldown_ms`, ...). Those getters abort `EWrongVariant` on a mismatch, and Move cannot recover from an abort. Some variant-typed getters also take a `&Clock` and return a projected anchor `last_refill_ms(clock)` (`Bucket`) and `window_start_ms(clock)` (`FixedWindow`) so don't assume every variant getter is clock-free. `capacity()` and `available(clock)` are variant-agnostic and need no guard.
182+
Branch on `is_bucket()` / `is_fixed_window()` / `is_cooldown()` before calling a variant-typed getter (`refill_amount`, `window_ms`, `cooldown_ms`, ...). Those getters abort `EWrongVariant` on a mismatch, and Move cannot recover from an abort. Some variant-typed getters also take a `&Clock` and return a projected anchor (`last_refill_ms(clock)` for `Bucket` and `window_start_ms(clock)` for `FixedWindow`), so don't assume every variant getter is clock-free. `capacity()` and `available(clock)` are variant-agnostic and need no guard.
183183

184184
## Learn more
185185

0 commit comments

Comments
 (0)