Skip to main content

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}