sui_framework_sdk/lib.rs
1//! Move types for the core `sui` Sui package located at "0x2" onchain.
2
3pub use af_move_type;
4use af_move_type::{MoveInstance, MoveType};
5pub use af_sui_types::Address;
6use move_stdlib_sdk::type_name::TypeName;
7
8use self::bag::*;
9use self::balance::*;
10// These are used far too often.
11pub use self::dynamic_field::{Field, FieldTypeTag};
12pub use self::object::{ID, UID};
13use self::table::*;
14use self::url::*;
15use self::vec_map::*;
16use self::vec_set::*;
17use self::versioned::*;
18
19af_sui_pkg_sdk::sui_pkg_sdk!(sui @ "0x2" {
20 module bag {
21 struct Bag has key, store {
22 /// the ID of this bag
23 id: UID,
24 /// the number of key-value pairs in the bag
25 size: u64,
26 }
27 }
28
29 module balance {
30 /// A Supply of T. Used for minting and burning.
31 /// Wrapped into a `TreasuryCap` in the `Coin` module.
32 struct Supply<!phantom T> has store {
33 value: u64
34 }
35
36 /// Storable balance - an inner struct of a Coin type.
37 /// Can be used to store coins which don't need the key ability.
38 struct Balance<!phantom T> has store {
39 value: u64
40 }
41 }
42
43 module bcs {
44 /// A helper struct that saves resources on operations. For better
45 /// vector performance, it stores reversed bytes of the BCS and
46 /// enables use of `vector::pop_back`.
47 struct BCS has store, copy, drop {
48 bytes: vector<u8>
49 }
50 }
51
52 module borrow {
53 /// An object wrapping a `T` and providing the borrow API.
54 struct Referent<T: key + store> has store {
55 id: address,
56 value: move_stdlib_sdk::option::Option<T>
57 }
58
59 /// A hot potato making sure the object is put back once borrowed.
60 struct Borrow { #[serde(rename = "ref")] ref_: address, obj: ID }
61 }
62
63 module clock {
64 /// Singleton shared object that exposes time to Move calls. This
65 /// object is found at address 0x6, and can only be read (accessed
66 /// via an immutable reference) by entry functions.
67 ///
68 /// Entry Functions that attempt to accept `Clock` by mutable
69 /// reference or value will fail to verify, and honest validators
70 /// will not sign or execute transactions that use `Clock` as an
71 /// input parameter, unless it is passed by immutable reference.
72 struct Clock has key {
73 id: UID,
74 /// The clock's timestamp, which is set automatically by a
75 /// system transaction every time consensus commits a
76 /// schedule, or by `sui::clock::increment_for_testing` during
77 /// testing.
78 timestamp_ms: u64,
79 }
80 }
81
82 module coin {
83 /// A coin of type `T` worth `value`. Transferable and storable
84 struct Coin<!phantom T> has key, store {
85 id: UID,
86 balance: Balance<T>
87 }
88
89 /// Each Coin type T created through `create_currency` function will have a
90 /// unique instance of `CoinMetadata<T>` that stores the metadata for this coin type.
91 struct CoinMetadata<!phantom T> has key, store {
92 id: UID,
93 /// Number of decimal places the coin uses.
94 /// A coin with `value ` N and `decimals` D should be shown as N / 10^D
95 /// E.g., a coin with `value` 7002 and decimals 3 should be displayed as 7.002
96 /// This is metadata for display usage only.
97 decimals: u8,
98 /// Name for the token
99 name: String, // from std::string::String
100 /// Symbol for the token
101 symbol: String, // from std::ascii::String
102 /// Description of the token
103 description: String, // from std::string::String
104 /// URL for the token logo
105 icon_url: move_stdlib_sdk::option::Option<Url>
106 }
107
108 /// Similar to CoinMetadata, but created only for regulated coins that use the DenyList.
109 /// This object is always immutable.
110 struct RegulatedCoinMetadata<!phantom T> has key {
111 id: UID,
112 /// The ID of the coin's CoinMetadata object.
113 coin_metadata_object: ID,
114 /// The ID of the coin's DenyCap object.
115 deny_cap_object: ID,
116 }
117
118 /// Capability allowing the bearer to mint and burn
119 /// coins of type `T`. Transferable
120 struct TreasuryCap<!phantom T> has key, store {
121 id: UID,
122 total_supply: Supply<T>
123 }
124
125 /// Capability allowing the bearer to freeze addresses, preventing those addresses from
126 /// interacting with the coin as an input to a transaction.
127 struct DenyCap<!phantom T> has key, store {
128 id: UID,
129 }
130 }
131
132 module deny_list {
133 /// A shared object that stores the addresses that are blocked for a given core type.
134 struct DenyList has key {
135 id: UID,
136 /// The individual deny lists.
137 lists: Bag,
138 }
139
140 /// Stores the addresses that are denied for a given core type.
141 struct PerTypeList has key, store {
142 id: UID,
143 /// Number of object types that have been banned for a given address.
144 /// Used to quickly skip checks for most addresses.
145 denied_count: Table<address, u64>,
146 /// Set of addresses that are banned for a given type.
147 /// For example with `sui::coin::Coin`: If addresses A and B are banned from using
148 /// "0...0123::my_coin::MY_COIN", this will be "0...0123::my_coin::MY_COIN" -> {A, B}.
149 denied_addresses: Table<vector<u8>, VecSet<address>>,
150 }
151 }
152
153 module display {
154 /// The `Display<T>` object. Defines the way a T instance should be
155 /// displayed. Display object can only be created and modified with
156 /// a PublisherCap, making sure that the rules are set by the owner
157 /// of the type.
158 ///
159 /// Each of the display properties should support patterns outside
160 /// of the system, making it simpler to customize Display based
161 /// on the property values of an Object.
162 /// ```move
163 /// // Example of a display object
164 /// Display<0x...::capy::Capy> {
165 /// fields:
166 /// <name, "Capy { genes }">
167 /// <link, "https://capy.art/capy/{ id }">
168 /// <image, "https://api.capy.art/capy/{ id }/svg">
169 /// <description, "Lovely Capy, one of many">
170 /// }
171 /// ```
172 ///
173 /// Uses only String type due to external-facing nature of the object,
174 /// the property names have a priority over their types.
175 struct Display<!phantom T: key> has key, store {
176 id: UID,
177 /// Contains fields for display. Currently supported
178 /// fields are: name, link, image and description.
179 fields: VecMap<String, String>,
180 /// Version that can only be updated manually by the Publisher.
181 version: u16
182 }
183
184 /// Event: emitted when a new Display object has been created for type T.
185 /// Type signature of the event corresponds to the type while id serves for
186 /// the discovery.
187 ///
188 /// Since Sui RPC supports querying events by type, finding a Display for the T
189 /// would be as simple as looking for the first event with `Display<T>`.
190 struct DisplayCreated<!phantom T: key> has copy, drop {
191 id: ID
192 }
193
194 /// Version of Display got updated -
195 struct VersionUpdated<!phantom T: key> has copy, drop {
196 id: ID,
197 version: u16,
198 fields: VecMap<String, String>,
199 }
200 }
201
202 module dynamic_field {
203 /// Internal object used for storing the field and value
204 struct Field<Name: copy + drop + store, Value: store> has key {
205 /// Determined by the hash of the object ID, the field name value and it's type,
206 /// i.e. hash(parent.id || name || Name)
207 id: UID,
208 /// The value for the name of this field
209 name: Name,
210 /// The value bound to this field
211 value: Value,
212 }
213 }
214
215 module dynamic_object_field {
216 // Internal object used for storing the field and the name associated with the value
217 // The separate type is necessary to prevent key collision with direct usage of dynamic_field
218 struct Wrapper<Name> has copy, drop, store {
219 name: Name,
220 }
221 }
222
223 module linked_table {
224 struct LinkedTable<K: copy + drop + store, !phantom V: store> has key, store {
225 /// the ID of this table
226 id: UID,
227 /// the number of key-value pairs in the table
228 size: u64,
229 /// the front of the table, i.e. the key of the first entry
230 head: move_stdlib_sdk::option::Option<K>,
231 /// the back of the table, i.e. the key of the last entry
232 tail: move_stdlib_sdk::option::Option<K>,
233 }
234
235 struct Node<K: copy + drop + store, V: store> has store {
236 /// the previous key
237 prev: move_stdlib_sdk::option::Option<K>,
238 /// the next key
239 next: move_stdlib_sdk::option::Option<K>,
240 /// the value being stored
241 value: V
242 }
243 }
244
245 module object_bag {
246 struct ObjectBag has key, store {
247 /// the ID of this bag
248 id: UID,
249 /// the number of key-value pairs in the bag
250 size: u64,
251 }
252 }
253
254 module object_table {
255 struct ObjectTable<!phantom K: copy + drop + store, !phantom V: key + store> has key, store {
256 /// the ID of this table
257 id: UID,
258 /// the number of key-value pairs in the table
259 size: u64,
260 }
261 }
262
263 module package {
264 /// This type can only be created in the transaction that
265 /// generates a module, by consuming its one-time witness, so it
266 /// can be used to identify the address that published the package
267 /// a type originated from.
268 struct Publisher has key, store {
269 id: UID,
270 package: String,
271 module_name: String,
272 }
273
274 /// Capability controlling the ability to upgrade a package.
275 struct UpgradeCap has key, store {
276 id: UID,
277 /// (Mutable) ID of the package that can be upgraded.
278 package: ID,
279 /// (Mutable) The number of upgrades that have been applied
280 /// successively to the original package. Initially 0.
281 version: u64,
282 /// What kind of upgrades are allowed.
283 policy: u8,
284 }
285
286 /// Permission to perform a particular upgrade (for a fixed version of
287 /// the package, bytecode to upgrade with and transitive dependencies to
288 /// depend against).
289 ///
290 /// An `UpgradeCap` can only issue one ticket at a time, to prevent races
291 /// between concurrent updates or a change in its upgrade policy after
292 /// issuing a ticket, so the ticket is a "Hot Potato" to preserve forward
293 /// progress.
294 struct UpgradeTicket {
295 /// (Immutable) ID of the `UpgradeCap` this originated from.
296 cap: ID,
297 /// (Immutable) ID of the package that can be upgraded.
298 package: ID,
299 /// (Immutable) The policy regarding what kind of upgrade this ticket
300 /// permits.
301 policy: u8,
302 /// (Immutable) SHA256 digest of the bytecode and transitive
303 /// dependencies that will be used in the upgrade.
304 digest: vector<u8>,
305 }
306
307 /// Issued as a result of a successful upgrade, containing the
308 /// information to be used to update the `UpgradeCap`. This is a "Hot
309 /// Potato" to ensure that it is used to update its `UpgradeCap` before
310 /// the end of the transaction that performed the upgrade.
311 struct UpgradeReceipt {
312 /// (Immutable) ID of the `UpgradeCap` this originated from.
313 cap: ID,
314 /// (Immutable) ID of the package after it was upgraded.
315 package: ID,
316 }
317 }
318
319 module priority_queue {
320 /// Struct representing a priority queue. The `entries` vector represents a max
321 /// heap structure, where entries\[0\] is the root, entries\[1\] and entries\[2\] are the
322 /// left child and right child of the root, etc. More generally, the children of
323 /// entries\[i\] are at at i * 2 + 1 and i * 2 + 2. The max heap should have the invariant
324 /// that the parent node's priority is always higher than its child nodes' priorities.
325 struct PriorityQueue<T: drop> has store, drop {
326 entries: vector<Entry<T>>,
327 }
328
329 struct Entry<T: drop> has store, drop {
330 priority: u64, // higher value means higher priority and will be popped first
331 value: T,
332 }
333 }
334
335 module random {
336 /// Singleton shared object which stores the global randomness state.
337 /// The actual state is stored in a versioned inner field.
338 struct Random has key {
339 id: UID,
340 inner: Versioned,
341 }
342
343 struct RandomInner has store {
344 version: u64,
345
346 epoch: u64,
347 randomness_round: u64,
348 random_bytes: vector<u8>,
349 }
350 }
351
352 module sui {
353 /// Name of the coin
354 struct SUI has drop {}
355 }
356
357 module table {
358 struct Table<!phantom K: copy + drop + store, !phantom V: store> has key, store {
359 /// the ID of this table
360 id: UID,
361 /// the number of key-value pairs in the table
362 size: u64,
363 }
364 }
365
366 module table_vec {
367 struct TableVec<!phantom Element: store> has store {
368 /// The contents of the table vector.
369 contents: Table<u64, Element>,
370 }
371 }
372
373 module token {
374 /// A single `Token` with `Balance` inside. Can only be owned by an address,
375 /// and actions performed on it must be confirmed in a matching `TokenPolicy`.
376 struct Token<!phantom T> has key {
377 id: UID,
378 /// The Balance of the `Token`.
379 balance: Balance<T>,
380 }
381
382 /// A Capability that manages a single `TokenPolicy` specified in the `for`
383 /// field. Created together with `TokenPolicy` in the `new` function.
384 struct TokenPolicyCap<!phantom T> has key, store {
385 id: UID,
386 #[serde(rename = "for")]
387 for_: ID
388 }
389
390 /// `TokenPolicy` represents a set of rules that define what actions can be
391 /// performed on a `Token` and which `Rules` must be satisfied for the
392 /// action to succeed.
393 ///
394 /// - For the sake of availability, `TokenPolicy` is a `key`-only object.
395 /// - Each `TokenPolicy` is managed by a matching `TokenPolicyCap`.
396 /// - For an action to become available, there needs to be a record in the
397 /// `rules` VecMap. To allow an action to be performed freely, there's an
398 /// `allow` function that can be called by the `TokenPolicyCap` owner.
399 struct TokenPolicy<!phantom T> has key {
400 id: UID,
401 /// The balance that is effectively spent by the user on the "spend"
402 /// action. However, actual decrease of the supply can only be done by
403 /// the `TreasuryCap` owner when `flush` is called.
404 ///
405 /// This balance is effectively spent and cannot be accessed by anyone
406 /// but the `TreasuryCap` owner.
407 spent_balance: Balance<T>,
408 /// The set of rules that define what actions can be performed on the
409 /// token. For each "action" there's a set of Rules that must be
410 /// satisfied for the `ActionRequest` to be confirmed.
411 rules: VecMap<String, VecSet<TypeName>>
412 }
413
414 /// A request to perform an "Action" on a token. Stores the information
415 /// about the action to be performed and must be consumed by the `confirm_request`
416 /// or `confirm_request_mut` functions when the Rules are satisfied.
417 struct ActionRequest<!phantom T> {
418 /// Name of the Action to look up in the Policy. Name can be one of the
419 /// default actions: `transfer`, `spend`, `to_coin`, `from_coin` or a
420 /// custom action.
421 name: String,
422 /// Amount is present in all of the txs
423 amount: u64,
424 /// Sender is a permanent field always
425 sender: address,
426 /// Recipient is only available in `transfer` action.
427 recipient: move_stdlib_sdk::option::Option<address>,
428 /// The balance to be "spent" in the `TokenPolicy`, only available
429 /// in the `spend` action.
430 spent_balance: move_stdlib_sdk::option::Option<Balance<T>>,
431 /// Collected approvals (stamps) from completed `Rules`. They're matched
432 /// against `TokenPolicy.rules` to determine if the request can be
433 /// confirmed.
434 approvals: VecSet<TypeName>,
435 }
436
437 /// Dynamic field key for the `TokenPolicy` to store the `Config` for a
438 /// specific action `Rule`. There can be only one configuration per
439 /// `Rule` per `TokenPolicy`.
440 struct RuleKey<!phantom T> has store, copy, drop { is_protected: bool }
441
442 /// An event emitted when a `TokenPolicy` is created and shared. Because
443 /// `TokenPolicy` can only be shared (and potentially frozen in the future),
444 /// we emit this event in the `share_policy` function and mark it as mutable.
445 struct TokenPolicyCreated<!phantom T> has copy, drop {
446 /// ID of the `TokenPolicy` that was created.
447 id: ID,
448 /// Whether the `TokenPolicy` is "shared" (mutable) or "frozen"
449 /// (immutable) - TBD.
450 is_mutable: bool,
451 }
452 }
453
454 module transfer {
455 /// This represents the ability to `receive` an object of type `T`.
456 /// This type is ephemeral per-transaction and cannot be stored on-chain.
457 /// This does not represent the obligation to receive the object that it
458 /// references, but simply the ability to receive the object with object ID
459 /// `id` at version `version` if you can prove mutable access to the parent
460 /// object during the transaction.
461 /// Internals of this struct are opaque outside this module.
462 struct Receiving<!phantom T: key> has drop {
463 id: ID,
464 version: u64,
465 }
466 }
467
468 module tx_context {
469 /// Information about the transaction currently being executed.
470 /// This cannot be constructed by a transaction--it is a privileged object created by
471 /// the VM and passed in to the entrypoint of the transaction as `&mut TxContext`.
472 struct TxContext has drop {
473 /// The address of the user that signed the current transaction
474 sender: address,
475 /// Hash of the current transaction
476 tx_hash: vector<u8>,
477 /// The current epoch number
478 epoch: u64,
479 /// Timestamp that the epoch started at
480 epoch_timestamp_ms: u64,
481 /// Counter recording the number of fresh id's created while executing
482 /// this transaction. Always 0 at the start of a transaction
483 ids_created: u64
484 }
485 }
486
487 module url {
488 /// Standard Uniform Resource Locator (URL) string.
489 struct Url has store, copy, drop {
490 url: String,
491 }
492 }
493
494 module vec_map {
495 /// A map data structure backed by a vector. The map is guaranteed not to contain duplicate keys, but entries
496 /// are *not* sorted by key--entries are included in insertion order.
497 /// All operations are O(N) in the size of the map--the intention of this data structure is only to provide
498 /// the convenience of programming against a map API.
499 /// Large maps should use handwritten parent/child relationships instead.
500 /// Maps that need sorted iteration rather than insertion order iteration should also be handwritten.
501 struct VecMap<K: copy, V> has copy, drop, store {
502 contents: vector<Entry<K, V>>,
503 }
504
505 /// An entry in the map
506 struct Entry<K: copy, V> has copy, drop, store {
507 key: K,
508 value: V,
509 }
510 }
511
512 module vec_set {
513 /// A set data structure backed by a vector. The set is guaranteed not to
514 /// contain duplicate keys. All operations are O(N) in the size of the set
515 /// - the intention of this data structure is only to provide the convenience
516 /// of programming against a set API. Sets that need sorted iteration rather
517 /// than insertion order iteration should be handwritten.
518 struct VecSet<K: copy + drop> has copy, drop, store {
519 contents: vector<K>,
520 }
521 }
522
523 module versioned {
524 /// A wrapper type that supports versioning of the inner type.
525 /// The inner type is a dynamic field of the Versioned object, and is keyed using version.
526 /// User of this type could load the inner object using corresponding type based on the version.
527 /// You can also upgrade the inner object to a new type version.
528 /// If you want to support lazy upgrade of the inner type, one caveat is that all APIs would have
529 /// to use mutable reference even if it's a read-only API.
530 struct Versioned has key, store {
531 id: UID,
532 version: u64,
533 }
534
535 /// Represents a hot potato object generated when we take out the dynamic field.
536 /// This is to make sure that we always put a new value back.
537 struct VersionChangeCap {
538 versioned_id: ID,
539 old_version: u64,
540 }
541 }
542});
543
544/// Custom `ID` and `UID` impls with better [`Display`](std::fmt::Display).
545pub mod object {
546 #![expect(
547 clippy::too_long_first_doc_paragraph,
548 reason = "Docs for the sui-framework have long first paragraphs."
549 )]
550 use super::Address;
551
552 /// An object ID. This is used to reference Sui Objects.
553 /// This is *not* guaranteed to be globally unique--anyone can create an `ID` from a `UID` or
554 /// from an object, and ID's can be freely copied and dropped.
555 /// Here, the values are not globally unique because there can be multiple values of type `ID`
556 /// with the same underlying bytes. For example, `object::id(&obj)` can be called as many times
557 /// as you want for a given `obj`, and each `ID` value will be identical.
558 #[derive(
559 af_sui_pkg_sdk::MoveStruct,
560 af_sui_pkg_sdk::serde::Deserialize,
561 af_sui_pkg_sdk::serde::Serialize,
562 af_sui_pkg_sdk::Tabled,
563 derive_more::From,
564 Clone,
565 Debug,
566 PartialEq,
567 Eq,
568 Hash,
569 )]
570 #[move_(crate = af_sui_pkg_sdk::af_move_type)]
571 #[serde(crate = "af_sui_pkg_sdk::serde", transparent)]
572 #[tabled(crate = "af_sui_pkg_sdk::tabled")]
573 pub struct ID {
574 pub bytes: Address,
575 }
576
577 impl ID {
578 pub const fn new(object_id: Address) -> Self {
579 Self { bytes: object_id }
580 }
581 }
582
583 impl std::fmt::Display for ID {
584 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
585 write!(f, "{}", self.bytes)
586 }
587 }
588
589 impl From<ID> for Address {
590 fn from(value: ID) -> Self {
591 value.bytes
592 }
593 }
594
595 /// Globally unique IDs that define an object's ID in storage. Any Sui Object, that is a struct
596 /// with the `key` ability, must have `id: UID` as its first field.
597 /// These are globally unique in the sense that no two values of type `UID` are ever equal, in
598 /// other words for any two values `id1: UID` and `id2: UID`, `id1` != `id2`.
599 /// This is a privileged type that can only be derived from a `TxContext`.
600 /// `UID` doesn't have the `drop` ability, so deleting a `UID` requires a call to `delete`.
601 #[derive(
602 af_sui_pkg_sdk::MoveStruct,
603 af_sui_pkg_sdk::serde::Deserialize,
604 af_sui_pkg_sdk::serde::Serialize,
605 af_sui_pkg_sdk::Tabled,
606 Clone,
607 Debug,
608 PartialEq,
609 Eq,
610 Hash,
611 )]
612 #[move_(crate = af_sui_pkg_sdk::af_move_type)]
613 #[serde(crate = "af_sui_pkg_sdk::serde")]
614 #[tabled(crate = "af_sui_pkg_sdk::tabled")]
615 pub struct UID {
616 pub id: ID,
617 }
618
619 impl UID {
620 pub const fn new(object_id: Address) -> Self {
621 Self {
622 id: ID::new(object_id),
623 }
624 }
625 }
626
627 impl std::fmt::Display for UID {
628 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
629 write!(f, "{}", self.id)
630 }
631 }
632
633 impl From<Address> for UID {
634 fn from(value: Address) -> Self {
635 Self::new(value)
636 }
637 }
638
639 impl From<UID> for Address {
640 fn from(value: UID) -> Self {
641 value.id.bytes
642 }
643 }
644}
645
646// =============================================================================
647// Convenience functions
648// =============================================================================
649
650impl<K: MoveType, V: MoveType> Field<K, V> {
651 /// Unpack an instance of a dynamic field into its name and value instances.
652 pub fn unpack_instance(this: MoveInstance<Self>) -> (MoveInstance<K>, MoveInstance<V>) {
653 #[expect(deprecated)]
654 unpack_field_instance(this)
655 }
656}
657
658#[deprecated = "Use Field::unpack_instance"]
659pub fn unpack_field_instance<K: MoveType, V: MoveType>(
660 field: MoveInstance<Field<K, V>>,
661) -> (MoveInstance<K>, MoveInstance<V>) {
662 let MoveInstance {
663 type_: FieldTypeTag {
664 name: name_type,
665 value: value_type,
666 },
667 value: Field { name, value, .. },
668 } = field;
669 (
670 MoveInstance {
671 type_: name_type,
672 value: name,
673 },
674 MoveInstance {
675 type_: value_type,
676 value,
677 },
678 )
679}