├── .gitignore ├── Move.toml ├── README.md ├── lib └── sui-framework │ ├── Cargo.toml │ ├── Move.toml │ ├── README.md │ ├── build.rs │ ├── deps │ └── move-stdlib │ │ ├── Move.toml │ │ └── sources │ │ ├── ascii.move │ │ ├── bcs.move │ │ ├── bit_vector.move │ │ ├── capability.move │ │ ├── debug.move │ │ ├── errors.move │ │ ├── fixed_point32.move │ │ ├── guid.move │ │ ├── hash.move │ │ ├── option.move │ │ ├── signer.move │ │ ├── unit_test.move │ │ └── vector.move │ ├── sources │ ├── bag.move │ ├── balance.move │ ├── coin.move │ ├── collection.move │ ├── devnet_nft.move │ ├── epoch_time_lock.move │ ├── erc721_metadata.move │ ├── event.move │ ├── governance │ │ ├── delegation.move │ │ ├── epoch_reward_record.move │ │ ├── genesis.move │ │ ├── stake.move │ │ ├── sui_system.move │ │ ├── validator.move │ │ └── validator_set.move │ ├── locked_coin.move │ ├── math.move │ ├── object.move │ ├── object_basics.move │ ├── sui.move │ ├── test_scenario.move │ ├── transfer.move │ ├── tx_context.move │ ├── typed_id.move │ ├── url.move │ ├── utf8.move │ ├── vec_map.move │ └── vec_set.move │ ├── src │ ├── lib.rs │ └── natives │ │ ├── event.rs │ │ ├── mod.rs │ │ ├── object.rs │ │ ├── test_scenario.rs │ │ ├── transfer.rs │ │ └── tx_context.rs │ └── tests │ ├── bag_tests.move │ ├── bytecode_calibration_tests.move │ ├── coin_balance_tests.move │ ├── collection_tests.move │ ├── delegation_tests.move │ ├── governance_test_utils.move │ ├── id_tests.move │ ├── math_tests.move │ ├── natives_calibration_tests.move │ ├── test_scenario_tests.move │ ├── tx_context_tests.move │ ├── url_tests.move │ ├── validator_set_tests.move │ ├── validator_tests.move │ ├── vec_map_tests.move │ └── vec_set_tests.move └── sources └── oracle_factory.move /.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "SuiMoveTemplate" 3 | version = "0.0.1" 4 | 5 | [dependencies] 6 | Sui = { local = "./lib/sui-framework" } 7 | 8 | [addresses] 9 | oracles = "0x0" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sui-move-oracles 2 | 3 | Simple Oracle Architecture built in Move for the Sui Ecosystem. 4 | 5 | These contracts are currently built to serve as price oracles, however you can hypothetically transform them into oracles that serve any type of data that you need. Due to Sui's high throughput and fast finality, you're able to quickly and efficiently provide all kinds of off-chain data. 6 | 7 | ![Architecture](https://s3.us-west-2.amazonaws.com/secure.notion-static.com/6064f01b-0e2a-4060-829c-effd41966cb9/Untitled.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45EIPT3X45%2F20220730%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20220730T165331Z&X-Amz-Expires=86400&X-Amz-Signature=ad8a7a988613c8b25a78ab1dc40bc596e4d7e96e6db5f180c7956b98b1396e84&X-Amz-SignedHeaders=host&response-content-disposition=filename%20%3D%22Untitled.png%22&x-id=GetObject) 8 | 9 | ## Getting started 10 | 11 | Before you clone and use this repository, make sure to install the Sui command-line tool in order to test your move code. You can find it [here])(https://docs.sui.io/build/install#summary). 12 | 13 | Once you've cloned and entered into this repository, you can easily build and test the contracts as so: 14 | 15 | ```sh 16 | # Build 17 | sui move build 18 | 19 | # Test 20 | sui move test 21 | ``` 22 | -------------------------------------------------------------------------------- /lib/sui-framework/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sui-framework" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Mysten Labs "] 6 | description = "Move framework for sui platform" 7 | license = "Apache-2.0" 8 | publish = false 9 | 10 | [dependencies] 11 | anyhow = { version = "1.0.58", features = ["backtrace"] } 12 | bcs = "0.1.3" 13 | smallvec = "1.9.0" 14 | num_enum = "0.5.7" 15 | once_cell = "1.11.0" 16 | 17 | sui-types = { path = "../sui-types" } 18 | sui-framework-build = { path = "../sui-framework-build" } 19 | 20 | move-binary-format = { git = "https://github.com/move-language/move", rev = "79071528524f08b12e9abb84c1094d8e976aa17a" } 21 | move-cli = { git = "https://github.com/move-language/move", rev = "79071528524f08b12e9abb84c1094d8e976aa17a" } 22 | move-core-types = { git = "https://github.com/move-language/move", rev = "79071528524f08b12e9abb84c1094d8e976aa17a", features = ["address20"] } 23 | move-package = { git = "https://github.com/move-language/move", rev = "79071528524f08b12e9abb84c1094d8e976aa17a" } 24 | move-stdlib = { git = "https://github.com/move-language/move", rev = "79071528524f08b12e9abb84c1094d8e976aa17a" } 25 | move-unit-test = { git = "https://github.com/move-language/move", rev = "79071528524f08b12e9abb84c1094d8e976aa17a" } 26 | move-vm-runtime = { git = "https://github.com/move-language/move", rev = "79071528524f08b12e9abb84c1094d8e976aa17a" } 27 | move-vm-types = { git = "https://github.com/move-language/move", rev = "79071528524f08b12e9abb84c1094d8e976aa17a" } 28 | workspace-hack = { path = "../workspace-hack"} 29 | 30 | [build-dependencies] 31 | anyhow = { version = "1.0.58", features = ["backtrace"] } 32 | bcs = "0.1.3" 33 | sui-framework-build = { path = "../sui-framework-build" } 34 | move-binary-format = { git = "https://github.com/move-language/move", rev = "79071528524f08b12e9abb84c1094d8e976aa17a" } 35 | move-package = { git = "https://github.com/move-language/move", rev = "79071528524f08b12e9abb84c1094d8e976aa17a" } 36 | 37 | [package.metadata.cargo-udeps.ignore] 38 | normal = ["move-cli", "move-unit-test"] 39 | -------------------------------------------------------------------------------- /lib/sui-framework/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "Sui" 3 | version = "0.0.1" 4 | 5 | [dependencies] 6 | # Using a local dep for the Move stdlib instead of a git dep to avoid the overhead of fetching the git dep in 7 | # CI. The local dep is an unmodified version of the upstream stdlib 8 | MoveStdlib = { local = "deps/move-stdlib" } 9 | #MoveStdlib = { git = "https://github.com/diem/diem.git", subdir="language/move-stdlib", rev = "346301f33b3489bb4e486ae6c0aa5e030223b492" } 10 | 11 | [addresses] 12 | sui = "0x2" 13 | -------------------------------------------------------------------------------- /lib/sui-framework/README.md: -------------------------------------------------------------------------------- 1 | # Sui Programmability with Move 2 | 3 | This is a proof-of-concept Move standard library for Sui (`sources/`), along with several examples of programs that Sui users might want to write (`examples`). `custom_object_template.move` is a good starting point for understanding the proposed model. 4 | 5 | To set up and build the [Sui CLI client](https://docs.sui.io/build/cli-client) needed for Move development, follow the instructions to [install Sui](https://docs.sui.io/build/install). 6 | -------------------------------------------------------------------------------- /lib/sui-framework/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use anyhow::Result; 5 | use move_binary_format::CompiledModule; 6 | use move_package::BuildConfig; 7 | use std::thread::Builder; 8 | use std::{ 9 | env, fs, 10 | path::{Path, PathBuf}, 11 | }; 12 | 13 | /// Save revision info to environment variable 14 | fn main() { 15 | let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); 16 | 17 | let sui_framework_path = Path::new(env!("CARGO_MANIFEST_DIR")); 18 | let move_stdlib_path = sui_framework_path.join("deps").join("move-stdlib"); 19 | 20 | let stdlib_path = move_stdlib_path.clone(); 21 | let (sui_framework, move_stdlib) = Builder::new() 22 | .stack_size(16 * 1024 * 1024) // build_move_package require bigger stack size on windows. 23 | .spawn(move || build_framework_and_stdlib(sui_framework_path, &stdlib_path)) 24 | .unwrap() 25 | .join() 26 | .unwrap(); 27 | 28 | serialize_modules_to_file(sui_framework, &out_dir.join("sui-framework")).unwrap(); 29 | serialize_modules_to_file(move_stdlib, &out_dir.join("move-stdlib")).unwrap(); 30 | 31 | println!("cargo:rerun-if-changed=build.rs"); 32 | println!( 33 | "cargo:rerun-if-changed={}", 34 | sui_framework_path.join("Move.toml").display() 35 | ); 36 | println!( 37 | "cargo:rerun-if-changed={}", 38 | sui_framework_path.join("sources").display() 39 | ); 40 | println!( 41 | "cargo:rerun-if-changed={}", 42 | move_stdlib_path.join("Move.toml").display() 43 | ); 44 | println!( 45 | "cargo:rerun-if-changed={}", 46 | move_stdlib_path.join("sources").display() 47 | ); 48 | } 49 | 50 | fn build_framework_and_stdlib( 51 | sui_framework_path: &Path, 52 | move_stdlib_path: &Path, 53 | ) -> (Vec, Vec) { 54 | let sui_framework = 55 | sui_framework_build::build_move_package(sui_framework_path, BuildConfig::default()) 56 | .unwrap(); 57 | let move_stdlib = sui_framework_build::build_move_stdlib_modules(move_stdlib_path).unwrap(); 58 | (sui_framework, move_stdlib) 59 | } 60 | 61 | fn serialize_modules_to_file(modules: Vec, file: &Path) -> Result<()> { 62 | let mut serialized_modules = Vec::new(); 63 | for module in modules { 64 | let mut buf = Vec::new(); 65 | module.serialize(&mut buf)?; 66 | serialized_modules.push(buf); 67 | } 68 | 69 | let binary = bcs::to_bytes(&serialized_modules)?; 70 | 71 | fs::write(file, &binary)?; 72 | 73 | Ok(()) 74 | } 75 | -------------------------------------------------------------------------------- /lib/sui-framework/deps/move-stdlib/Move.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "MoveStdlib" 3 | version = "1.5.0" 4 | 5 | [addresses] 6 | std = "0x1" 7 | -------------------------------------------------------------------------------- /lib/sui-framework/deps/move-stdlib/sources/ascii.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// The `ASCII` module defines basic string and char newtypes in Move that verify 5 | /// that characters are valid ASCII, and that strings consist of only valid ASCII characters. 6 | module std::ascii { 7 | use std::vector; 8 | use std::errors; 9 | use std::option::{Self, Option}; 10 | 11 | /// An invalid ASCII character was encountered when creating an ASCII string. 12 | const EINVALID_ASCII_CHARACTER: u64 = 0; 13 | 14 | /// The `String` struct holds a vector of bytes that all represent 15 | /// valid ASCII characters. Note that these ASCII characters may not all 16 | /// be printable. To determine if a `String` contains only "printable" 17 | /// characters you should use the `all_characters_printable` predicate 18 | /// defined in this module. 19 | struct String has copy, drop, store { 20 | bytes: vector, 21 | } 22 | spec String { 23 | invariant forall i in 0..len(bytes): is_valid_char(bytes[i]); 24 | } 25 | 26 | /// An ASCII character. 27 | struct Char has copy, drop, store { 28 | byte: u8, 29 | } 30 | spec Char { 31 | invariant is_valid_char(byte); 32 | } 33 | 34 | /// Convert a `byte` into a `Char` that is checked to make sure it is valid ASCII. 35 | public fun char(byte: u8): Char { 36 | assert!(is_valid_char(byte), errors::invalid_argument(EINVALID_ASCII_CHARACTER)); 37 | Char { byte } 38 | } 39 | spec char { 40 | aborts_if !is_valid_char(byte) with errors::INVALID_ARGUMENT; 41 | } 42 | 43 | /// Convert a vector of bytes `bytes` into an `String`. Aborts if 44 | /// `bytes` contains non-ASCII characters. 45 | public fun string(bytes: vector): String { 46 | let x = try_string(bytes); 47 | assert!( 48 | option::is_some(&x), 49 | errors::invalid_argument(EINVALID_ASCII_CHARACTER) 50 | ); 51 | option::destroy_some(x) 52 | } 53 | spec string { 54 | aborts_if exists i in 0..len(bytes): !is_valid_char(bytes[i]) with errors::INVALID_ARGUMENT; 55 | } 56 | 57 | /// Convert a vector of bytes `bytes` into an `String`. Returns 58 | /// `Some()` if the `bytes` contains all valid ASCII 59 | /// characters. Otherwise returns `None`. 60 | public fun try_string(bytes: vector): Option { 61 | let len = vector::length(&bytes); 62 | let i = 0; 63 | while ({ 64 | spec { 65 | invariant i <= len; 66 | invariant forall j in 0..i: is_valid_char(bytes[j]); 67 | }; 68 | i < len 69 | }) { 70 | let possible_byte = *vector::borrow(&bytes, i); 71 | if (!is_valid_char(possible_byte)) return option::none(); 72 | i = i + 1; 73 | }; 74 | spec { 75 | assert i == len; 76 | assert forall j in 0..len: is_valid_char(bytes[j]); 77 | }; 78 | option::some(String { bytes }) 79 | } 80 | 81 | /// Returns `true` if all characters in `string` are printable characters 82 | /// Returns `false` otherwise. Not all `String`s are printable strings. 83 | public fun all_characters_printable(string: &String): bool { 84 | let len = vector::length(&string.bytes); 85 | let i = 0; 86 | while ({ 87 | spec { 88 | invariant i <= len; 89 | invariant forall j in 0..i: is_printable_char(string.bytes[j]); 90 | }; 91 | i < len 92 | }) { 93 | let byte = *vector::borrow(&string.bytes, i); 94 | if (!is_printable_char(byte)) return false; 95 | i = i + 1; 96 | }; 97 | spec { 98 | assert i == len; 99 | assert forall j in 0..len: is_printable_char(string.bytes[j]); 100 | }; 101 | true 102 | } 103 | spec all_characters_printable { 104 | ensures result ==> (forall j in 0..len(string.bytes): is_printable_char(string.bytes[j])); 105 | } 106 | 107 | public fun push_char(string: &mut String, char: Char) { 108 | vector::push_back(&mut string.bytes, char.byte); 109 | } 110 | spec push_char { 111 | ensures len(string.bytes) == len(old(string.bytes)) + 1; 112 | } 113 | 114 | public fun pop_char(string: &mut String): Char { 115 | Char { byte: vector::pop_back(&mut string.bytes) } 116 | } 117 | spec pop_char { 118 | ensures len(string.bytes) == len(old(string.bytes)) - 1; 119 | } 120 | 121 | public fun length(string: &String): u64 { 122 | vector::length(as_bytes(string)) 123 | } 124 | 125 | /// Get the inner bytes of the `string` as a reference 126 | public fun as_bytes(string: &String): &vector { 127 | &string.bytes 128 | } 129 | 130 | /// Unpack the `string` to get its backing bytes 131 | public fun into_bytes(string: String): vector { 132 | let String { bytes } = string; 133 | bytes 134 | } 135 | 136 | /// Unpack the `char` into its underlying byte. 137 | public fun byte(char: Char): u8 { 138 | let Char { byte } = char; 139 | byte 140 | } 141 | 142 | /// Returns `true` if `b` is a valid ASCII character. Returns `false` otherwise. 143 | public fun is_valid_char(b: u8): bool { 144 | b <= 0x7F 145 | } 146 | 147 | /// Returns `true` if `byte` is an printable ASCII character. Returns `false` otherwise. 148 | public fun is_printable_char(byte: u8): bool { 149 | byte >= 0x20 && // Disallow metacharacters 150 | byte <= 0x7E // Don't allow DEL metacharacter 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/sui-framework/deps/move-stdlib/sources/bcs.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// Utility for converting a Move value to its binary representation in BCS (Binary Canonical 5 | /// Serialization). BCS is the binary encoding for Move resources and other non-module values 6 | /// published on-chain. See https://github.com/diem/bcs#binary-canonical-serialization-bcs for more 7 | /// details on BCS. 8 | module std::bcs { 9 | /// Return the binary representation of `v` in BCS (Binary Canonical Serialization) format 10 | native public fun to_bytes(v: &MoveValue): vector; 11 | 12 | // ============================== 13 | // Module Specification 14 | spec module {} // switch to module documentation context 15 | 16 | spec module { 17 | /// Native function which is defined in the prover's prelude. 18 | native fun serialize(v: &MoveValue): vector; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/sui-framework/deps/move-stdlib/sources/bit_vector.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module std::bit_vector { 5 | use std::vector; 6 | use std::errors; 7 | 8 | /// The provided index is out of bounds 9 | const EINDEX: u64 = 0; 10 | /// An invalid length of bitvector was given 11 | const ELENGTH: u64 = 1; 12 | 13 | const WORD_SIZE: u64 = 1; 14 | /// The maximum allowed bitvector size 15 | const MAX_SIZE: u64 = 1024; 16 | 17 | struct BitVector has copy, drop, store { 18 | length: u64, 19 | bit_field: vector, 20 | } 21 | 22 | public fun new(length: u64): BitVector { 23 | assert!(length > 0, errors::invalid_argument(ELENGTH)); 24 | assert!(length < MAX_SIZE, errors::invalid_argument(ELENGTH)); 25 | let counter = 0; 26 | let bit_field = vector::empty(); 27 | while ({spec { 28 | invariant counter <= length; 29 | invariant len(bit_field) == counter; 30 | }; 31 | (counter < length)}) { 32 | vector::push_back(&mut bit_field, false); 33 | counter = counter + 1; 34 | }; 35 | spec { 36 | assert counter == length; 37 | assert len(bit_field) == length; 38 | }; 39 | 40 | BitVector { 41 | length, 42 | bit_field, 43 | } 44 | } 45 | spec new { 46 | include NewAbortsIf; 47 | ensures result.length == length; 48 | ensures len(result.bit_field) == length; 49 | } 50 | spec schema NewAbortsIf { 51 | length: u64; 52 | aborts_if length <= 0 with errors::INVALID_ARGUMENT; 53 | aborts_if length >= MAX_SIZE with errors::INVALID_ARGUMENT; 54 | } 55 | 56 | /// Set the bit at `bit_index` in the `bitvector` regardless of its previous state. 57 | public fun set(bitvector: &mut BitVector, bit_index: u64) { 58 | assert!(bit_index < vector::length(&bitvector.bit_field), errors::invalid_argument(EINDEX)); 59 | let x = vector::borrow_mut(&mut bitvector.bit_field, bit_index); 60 | *x = true; 61 | } 62 | spec set { 63 | include SetAbortsIf; 64 | ensures bitvector.bit_field[bit_index]; 65 | } 66 | spec schema SetAbortsIf { 67 | bitvector: BitVector; 68 | bit_index: u64; 69 | aborts_if bit_index >= length(bitvector) with errors::INVALID_ARGUMENT; 70 | } 71 | 72 | /// Unset the bit at `bit_index` in the `bitvector` regardless of its previous state. 73 | public fun unset(bitvector: &mut BitVector, bit_index: u64) { 74 | assert!(bit_index < vector::length(&bitvector.bit_field), errors::invalid_argument(EINDEX)); 75 | let x = vector::borrow_mut(&mut bitvector.bit_field, bit_index); 76 | *x = false; 77 | } 78 | spec set { 79 | include UnsetAbortsIf; 80 | ensures bitvector.bit_field[bit_index]; 81 | } 82 | spec schema UnsetAbortsIf { 83 | bitvector: BitVector; 84 | bit_index: u64; 85 | aborts_if bit_index >= length(bitvector) with errors::INVALID_ARGUMENT; 86 | } 87 | 88 | /// Shift the `bitvector` left by `amount`. If `amount` is greater than the 89 | /// bitvector's length the bitvector will be zeroed out. 90 | public fun shift_left(bitvector: &mut BitVector, amount: u64) { 91 | if (amount >= bitvector.length) { 92 | let len = vector::length(&bitvector.bit_field); 93 | let i = 0; 94 | while (i < len) { 95 | let elem = vector::borrow_mut(&mut bitvector.bit_field, i); 96 | *elem = false; 97 | i = i + 1; 98 | }; 99 | } else { 100 | let i = amount; 101 | 102 | while (i < bitvector.length) { 103 | if (is_index_set(bitvector, i)) set(bitvector, i - amount) 104 | else unset(bitvector, i - amount); 105 | i = i + 1; 106 | }; 107 | 108 | i = bitvector.length - amount; 109 | 110 | while (i < bitvector.length) { 111 | unset(bitvector, i); 112 | i = i + 1; 113 | }; 114 | } 115 | } 116 | 117 | /// Return the value of the bit at `bit_index` in the `bitvector`. `true` 118 | /// represents "1" and `false` represents a 0 119 | public fun is_index_set(bitvector: &BitVector, bit_index: u64): bool { 120 | assert!(bit_index < vector::length(&bitvector.bit_field), errors::invalid_argument(EINDEX)); 121 | *vector::borrow(&bitvector.bit_field, bit_index) 122 | } 123 | spec is_index_set { 124 | include IsIndexSetAbortsIf; 125 | ensures result == bitvector.bit_field[bit_index]; 126 | } 127 | spec schema IsIndexSetAbortsIf { 128 | bitvector: BitVector; 129 | bit_index: u64; 130 | aborts_if bit_index >= length(bitvector) with errors::INVALID_ARGUMENT; 131 | } 132 | spec fun spec_is_index_set(bitvector: BitVector, bit_index: u64): bool { 133 | if (bit_index >= length(bitvector)) { 134 | false 135 | } else { 136 | bitvector.bit_field[bit_index] 137 | } 138 | } 139 | 140 | /// Return the length (number of usable bits) of this bitvector 141 | public fun length(bitvector: &BitVector): u64 { 142 | vector::length(&bitvector.bit_field) 143 | } 144 | 145 | /// Returns the length of the longest sequence of set bits starting at (and 146 | /// including) `start_index` in the `bitvector`. If there is no such 147 | /// sequence, then `0` is returned. 148 | public fun longest_set_sequence_starting_at(bitvector: &BitVector, start_index: u64): u64 { 149 | assert!(start_index < bitvector.length, errors::invalid_argument(EINDEX)); 150 | let index = start_index; 151 | 152 | // Find the greatest index in the vector such that all indices less than it are set. 153 | while (index < bitvector.length) { 154 | if (!is_index_set(bitvector, index)) break; 155 | index = index + 1; 156 | }; 157 | 158 | index - start_index 159 | } 160 | 161 | #[test_only] 162 | public fun word_size(): u64 { 163 | WORD_SIZE 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /lib/sui-framework/deps/move-stdlib/sources/debug.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// Module providing debug functionality. 5 | module std::debug { 6 | native public fun print(x: &T); 7 | 8 | native public fun print_stack_trace(); 9 | } 10 | -------------------------------------------------------------------------------- /lib/sui-framework/deps/move-stdlib/sources/errors.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// Module defining error codes used in Move aborts throughout the framework. 5 | /// 6 | /// A `u64` error code is constructed from two values: 7 | /// 8 | /// 1. The *error category* which is encoded in the lower 8 bits of the code. Error categories are 9 | /// declared in this module and are globally unique across the Diem framework. There is a limited 10 | /// fixed set of predefined categories, and the framework is guaranteed to use those consistently. 11 | /// 12 | /// 2. The *error reason* which is encoded in the remaining 56 bits of the code. The reason is a unique 13 | /// number relative to the module which raised the error and can be used to obtain more information about 14 | /// the error at hand. It is mostly used for diagnosis purposes. Error reasons may change over time as the 15 | /// framework evolves. 16 | /// 17 | /// >TODO: determine what kind of stability guarantees we give about reasons/associated module. 18 | module std::errors { 19 | /// A function to create an error from from a category and a reason. 20 | fun make(category: u8, reason: u64): u64 { 21 | (category as u64) + (reason << 8) 22 | } 23 | spec make { 24 | pragma opaque = true; 25 | // The % below is to account for bits that could be shifted off the left end. 26 | // For correctness, we would like that never to happen, but I'm cautious about 27 | // using assert! in this module (no other uses), and require is just going to 28 | // cause verification errors in public calling functions below. 29 | ensures [concrete] result == category + (reason << 8) % (1 << 64); 30 | aborts_if [abstract] false; 31 | ensures [abstract] result == category; 32 | } 33 | 34 | /// The system is in a state where the performed operation is not allowed. Example: call to a function only allowed 35 | /// in genesis. 36 | const INVALID_STATE: u8 = 1; 37 | 38 | /// The signer of a transaction does not have the expected address for this operation. Example: a call to a function 39 | /// which publishes a resource under a particular address. 40 | const REQUIRES_ADDRESS: u8 = 2; 41 | 42 | /// The signer of a transaction does not have the expected role for this operation. Example: a call to a function 43 | /// which requires the signer to have the role of treasury compliance. 44 | const REQUIRES_ROLE: u8 = 3; 45 | 46 | /// The signer of a transaction does not have a required capability. 47 | const REQUIRES_CAPABILITY: u8 = 4; 48 | 49 | /// A resource is required but not published. Example: access to non-existing AccountLimits resource. 50 | const NOT_PUBLISHED: u8 = 5; 51 | 52 | /// Attempting to publish a resource that is already published. Example: calling an initialization function 53 | /// twice. 54 | const ALREADY_PUBLISHED: u8 = 6; 55 | 56 | /// An argument provided to an operation is invalid. Example: a signing key has the wrong format. 57 | const INVALID_ARGUMENT: u8 = 7; 58 | 59 | /// A limit on an amount, e.g. a currency, is exceeded. Example: withdrawal of money after account limits window 60 | /// is exhausted. 61 | const LIMIT_EXCEEDED: u8 = 8; 62 | 63 | /// An internal error (bug) has occurred. 64 | const INTERNAL: u8 = 10; 65 | 66 | /// A custom error category for extension points. 67 | const CUSTOM: u8 = 255; 68 | 69 | public fun invalid_state(reason: u64): u64 { make(INVALID_STATE, reason) } 70 | spec invalid_state { 71 | pragma opaque = true; 72 | aborts_if false; 73 | ensures result == INVALID_STATE; 74 | } 75 | 76 | public fun requires_address(reason: u64): u64 { make(REQUIRES_ADDRESS, reason) } 77 | spec requires_address { 78 | pragma opaque = true; 79 | aborts_if false; 80 | ensures result == REQUIRES_ADDRESS; 81 | } 82 | 83 | public fun requires_role(reason: u64): u64 { make(REQUIRES_ROLE, reason) } 84 | spec requires_role { 85 | pragma opaque = true; 86 | aborts_if false; 87 | ensures result == REQUIRES_ROLE; 88 | } 89 | 90 | public fun requires_capability(reason: u64): u64 { make(REQUIRES_CAPABILITY, reason) } 91 | spec requires_capability { 92 | pragma opaque = true; 93 | aborts_if false; 94 | ensures result == REQUIRES_CAPABILITY; 95 | } 96 | 97 | public fun not_published(reason: u64): u64 { make(NOT_PUBLISHED, reason) } 98 | spec not_published { 99 | pragma opaque = true; 100 | aborts_if false; 101 | ensures result == NOT_PUBLISHED; 102 | } 103 | 104 | public fun already_published(reason: u64): u64 { make(ALREADY_PUBLISHED, reason) } 105 | spec already_published { 106 | pragma opaque = true; 107 | aborts_if false; 108 | ensures result == ALREADY_PUBLISHED; 109 | } 110 | 111 | public fun invalid_argument(reason: u64): u64 { make(INVALID_ARGUMENT, reason) } 112 | spec invalid_argument { 113 | pragma opaque = true; 114 | aborts_if false; 115 | ensures result == INVALID_ARGUMENT; 116 | } 117 | 118 | public fun limit_exceeded(reason: u64): u64 { make(LIMIT_EXCEEDED, reason) } 119 | spec limit_exceeded { 120 | pragma opaque = true; 121 | aborts_if false; 122 | ensures result == LIMIT_EXCEEDED; 123 | } 124 | 125 | public fun internal(reason: u64): u64 { make(INTERNAL, reason) } 126 | spec internal { 127 | pragma opaque = true; 128 | aborts_if false; 129 | ensures result == INTERNAL; 130 | } 131 | 132 | public fun custom(reason: u64): u64 { make(CUSTOM, reason) } 133 | spec custom { 134 | pragma opaque = true; 135 | aborts_if false; 136 | ensures result == CUSTOM; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /lib/sui-framework/deps/move-stdlib/sources/fixed_point32.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// Defines a fixed-point numeric type with a 32-bit integer part and 5 | /// a 32-bit fractional part. 6 | 7 | module std::fixed_point32 { 8 | 9 | use std::errors; 10 | 11 | /// Define a fixed-point numeric type with 32 fractional bits. 12 | /// This is just a u64 integer but it is wrapped in a struct to 13 | /// make a unique type. This is a binary representation, so decimal 14 | /// values may not be exactly representable, but it provides more 15 | /// than 9 decimal digits of precision both before and after the 16 | /// decimal point (18 digits total). For comparison, double precision 17 | /// floating-point has less than 16 decimal digits of precision, so 18 | /// be careful about using floating-point to convert these values to 19 | /// decimal. 20 | struct FixedPoint32 has copy, drop, store { value: u64 } 21 | 22 | ///> TODO: This is a basic constant and should be provided somewhere centrally in the framework. 23 | const MAX_U64: u128 = 18446744073709551615; 24 | 25 | /// The denominator provided was zero 26 | const EDENOMINATOR: u64 = 0; 27 | /// The quotient value would be too large to be held in a `u64` 28 | const EDIVISION: u64 = 1; 29 | /// The multiplied value would be too large to be held in a `u64` 30 | const EMULTIPLICATION: u64 = 2; 31 | /// A division by zero was encountered 32 | const EDIVISION_BY_ZERO: u64 = 3; 33 | /// The computed ratio when converting to a `FixedPoint32` would be unrepresentable 34 | const ERATIO_OUT_OF_RANGE: u64 = 4; 35 | 36 | /// Multiply a u64 integer by a fixed-point number, truncating any 37 | /// fractional part of the product. This will abort if the product 38 | /// overflows. 39 | public fun multiply_u64(val: u64, multiplier: FixedPoint32): u64 { 40 | // The product of two 64 bit values has 128 bits, so perform the 41 | // multiplication with u128 types and keep the full 128 bit product 42 | // to avoid losing accuracy. 43 | let unscaled_product = (val as u128) * (multiplier.value as u128); 44 | // The unscaled product has 32 fractional bits (from the multiplier) 45 | // so rescale it by shifting away the low bits. 46 | let product = unscaled_product >> 32; 47 | // Check whether the value is too large. 48 | assert!(product <= MAX_U64, errors::limit_exceeded(EMULTIPLICATION)); 49 | (product as u64) 50 | } 51 | spec multiply_u64 { 52 | pragma opaque; 53 | include MultiplyAbortsIf; 54 | ensures result == spec_multiply_u64(val, multiplier); 55 | } 56 | spec schema MultiplyAbortsIf { 57 | val: num; 58 | multiplier: FixedPoint32; 59 | aborts_if spec_multiply_u64(val, multiplier) > MAX_U64 with errors::LIMIT_EXCEEDED; 60 | } 61 | spec fun spec_multiply_u64(val: num, multiplier: FixedPoint32): num { 62 | (val * multiplier.value) >> 32 63 | } 64 | 65 | /// Divide a u64 integer by a fixed-point number, truncating any 66 | /// fractional part of the quotient. This will abort if the divisor 67 | /// is zero or if the quotient overflows. 68 | public fun divide_u64(val: u64, divisor: FixedPoint32): u64 { 69 | // Check for division by zero. 70 | assert!(divisor.value != 0, errors::invalid_argument(EDIVISION_BY_ZERO)); 71 | // First convert to 128 bits and then shift left to 72 | // add 32 fractional zero bits to the dividend. 73 | let scaled_value = (val as u128) << 32; 74 | let quotient = scaled_value / (divisor.value as u128); 75 | // Check whether the value is too large. 76 | assert!(quotient <= MAX_U64, errors::limit_exceeded(EDIVISION)); 77 | // the value may be too large, which will cause the cast to fail 78 | // with an arithmetic error. 79 | (quotient as u64) 80 | } 81 | spec divide_u64 { 82 | pragma opaque; 83 | include DivideAbortsIf; 84 | ensures result == spec_divide_u64(val, divisor); 85 | } 86 | spec schema DivideAbortsIf { 87 | val: num; 88 | divisor: FixedPoint32; 89 | aborts_if divisor.value == 0 with errors::INVALID_ARGUMENT; 90 | aborts_if spec_divide_u64(val, divisor) > MAX_U64 with errors::LIMIT_EXCEEDED; 91 | } 92 | spec fun spec_divide_u64(val: num, divisor: FixedPoint32): num { 93 | (val << 32) / divisor.value 94 | } 95 | 96 | /// Create a fixed-point value from a rational number specified by its 97 | /// numerator and denominator. Calling this function should be preferred 98 | /// for using `Self::create_from_raw_value` which is also available. 99 | /// This will abort if the denominator is zero. It will also 100 | /// abort if the numerator is nonzero and the ratio is not in the range 101 | /// 2^-32 .. 2^32-1. When specifying decimal fractions, be careful about 102 | /// rounding errors: if you round to display N digits after the decimal 103 | /// point, you can use a denominator of 10^N to avoid numbers where the 104 | /// very small imprecision in the binary representation could change the 105 | /// rounding, e.g., 0.0125 will round down to 0.012 instead of up to 0.013. 106 | public fun create_from_rational(numerator: u64, denominator: u64): FixedPoint32 { 107 | // If the denominator is zero, this will abort. 108 | // Scale the numerator to have 64 fractional bits and the denominator 109 | // to have 32 fractional bits, so that the quotient will have 32 110 | // fractional bits. 111 | let scaled_numerator = (numerator as u128) << 64; 112 | let scaled_denominator = (denominator as u128) << 32; 113 | assert!(scaled_denominator != 0, errors::invalid_argument(EDENOMINATOR)); 114 | let quotient = scaled_numerator / scaled_denominator; 115 | assert!(quotient != 0 || numerator == 0, errors::invalid_argument(ERATIO_OUT_OF_RANGE)); 116 | // Return the quotient as a fixed-point number. We first need to check whether the cast 117 | // can succeed. 118 | assert!(quotient <= MAX_U64, errors::limit_exceeded(ERATIO_OUT_OF_RANGE)); 119 | FixedPoint32 { value: (quotient as u64) } 120 | } 121 | spec create_from_rational { 122 | pragma opaque; 123 | include CreateFromRationalAbortsIf; 124 | ensures result == spec_create_from_rational(numerator, denominator); 125 | } 126 | spec schema CreateFromRationalAbortsIf { 127 | numerator: u64; 128 | denominator: u64; 129 | let scaled_numerator = numerator << 64; 130 | let scaled_denominator = denominator << 32; 131 | let quotient = scaled_numerator / scaled_denominator; 132 | aborts_if scaled_denominator == 0 with errors::INVALID_ARGUMENT; 133 | aborts_if quotient == 0 && scaled_numerator != 0 with errors::INVALID_ARGUMENT; 134 | aborts_if quotient > MAX_U64 with errors::LIMIT_EXCEEDED; 135 | } 136 | spec fun spec_create_from_rational(numerator: num, denominator: num): FixedPoint32 { 137 | FixedPoint32{value: (numerator << 64) / (denominator << 32)} 138 | } 139 | 140 | /// Create a fixedpoint value from a raw value. 141 | public fun create_from_raw_value(value: u64): FixedPoint32 { 142 | FixedPoint32 { value } 143 | } 144 | spec create_from_raw_value { 145 | pragma opaque; 146 | aborts_if false; 147 | ensures result.value == value; 148 | } 149 | 150 | /// Accessor for the raw u64 value. Other less common operations, such as 151 | /// adding or subtracting FixedPoint32 values, can be done using the raw 152 | /// values directly. 153 | public fun get_raw_value(num: FixedPoint32): u64 { 154 | num.value 155 | } 156 | 157 | /// Returns true if the ratio is zero. 158 | public fun is_zero(num: FixedPoint32): bool { 159 | num.value == 0 160 | } 161 | 162 | // **************** SPECIFICATIONS **************** 163 | 164 | spec module {} // switch documentation context to module level 165 | 166 | spec module { 167 | pragma aborts_if_is_strict; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /lib/sui-framework/deps/move-stdlib/sources/guid.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// A module for generating globally unique identifiers 5 | module std::guid { 6 | use std::signer; 7 | 8 | /// A generator for new GUIDs. 9 | struct Generator has key { 10 | /// A monotonically increasing counter 11 | counter: u64, 12 | } 13 | 14 | /// A globally unique identifier derived from the sender's address and a counter 15 | struct GUID has drop, store { 16 | id: ID 17 | } 18 | 19 | /// A non-privileged identifier that can be freely created by anyone. Useful for looking up GUID's. 20 | struct ID has copy, drop, store { 21 | /// If creation_num is `i`, this is the `i+1`th GUID created by `addr` 22 | creation_num: u64, 23 | /// Address that created the GUID 24 | addr: address 25 | } 26 | 27 | /// A capability to create a privileged identifier on behalf of the given address 28 | struct CreateCapability has key, store, drop { 29 | addr: address 30 | } 31 | 32 | /// GUID generator must be published ahead of first usage of `create_with_capability` function. 33 | const EGUID_GENERATOR_NOT_PUBLISHED: u64 = 0; 34 | 35 | /// Generates a capability to create the privileged GUID on behalf of the signer 36 | // (also makes sure that the Generator is published under the signer account) 37 | public fun gen_create_capability(account: &signer): CreateCapability { 38 | let addr = signer::address_of(account); 39 | if (!exists(addr)) { 40 | move_to(account, Generator { counter: 0 }) 41 | }; 42 | CreateCapability { addr } 43 | } 44 | 45 | /// Create a non-privileged id from `addr` and `creation_num` 46 | public fun create_id(addr: address, creation_num: u64): ID { 47 | ID { creation_num, addr } 48 | } 49 | 50 | public fun create_with_capability(addr: address, _cap: &CreateCapability): GUID acquires Generator { 51 | assert!(exists(addr), EGUID_GENERATOR_NOT_PUBLISHED); 52 | create_impl(addr) 53 | } 54 | 55 | /// Create and return a new GUID. Creates a `Generator` under `account` 56 | /// if it does not already have one 57 | public fun create(account: &signer): GUID acquires Generator { 58 | let addr = signer::address_of(account); 59 | if (!exists(addr)) { 60 | move_to(account, Generator { counter: 0 }) 61 | }; 62 | create_impl(addr) 63 | } 64 | 65 | fun create_impl(addr: address): GUID acquires Generator { 66 | let generator = borrow_global_mut(addr); 67 | let creation_num = generator.counter; 68 | generator.counter = creation_num + 1; 69 | GUID { id: ID { creation_num, addr } } 70 | } 71 | 72 | /// Publish a Generator resource under `account` 73 | public fun publish_generator(account: &signer) { 74 | move_to(account, Generator { counter: 0 }) 75 | } 76 | 77 | /// Get the non-privileged ID associated with a GUID 78 | public fun id(guid: &GUID): ID { 79 | *&guid.id 80 | } 81 | 82 | /// Return the account address that created the GUID 83 | public fun creator_address(guid: &GUID): address { 84 | guid.id.addr 85 | } 86 | 87 | /// Return the account address that created the guobject::ID 88 | public fun id_creator_address(id: &ID): address { 89 | id.addr 90 | } 91 | 92 | /// Return the creation number associated with the GUID 93 | public fun creation_num(guid: &GUID): u64 { 94 | guid.id.creation_num 95 | } 96 | 97 | /// Return the creation number associated with the guobject::ID 98 | public fun id_creation_num(id: &ID): u64 { 99 | id.creation_num 100 | } 101 | 102 | /// Return true if the GUID's ID is `id` 103 | public fun eq_id(guid: &GUID, id: &ID): bool { 104 | &guid.id == id 105 | } 106 | 107 | /// Return the number of the next GUID to be created by `addr` 108 | public fun get_next_creation_num(addr: address): u64 acquires Generator { 109 | if (!exists(addr)) { 110 | 0 111 | } else { 112 | borrow_global(addr).counter 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /lib/sui-framework/deps/move-stdlib/sources/hash.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// Module which defines SHA hashes for byte vectors. 5 | /// 6 | /// The functions in this module are natively declared both in the Move runtime 7 | /// as in the Move prover's prelude. 8 | module std::hash { 9 | native public fun sha2_256(data: vector): vector; 10 | native public fun sha3_256(data: vector): vector; 11 | } 12 | -------------------------------------------------------------------------------- /lib/sui-framework/deps/move-stdlib/sources/signer.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module std::signer { 5 | // Borrows the address of the signer 6 | // Conceptually, you can think of the `signer` as being a struct wrapper arround an 7 | // address 8 | // ``` 9 | // struct signer has drop { addr: address } 10 | // ``` 11 | // `borrow_address` borrows this inner field 12 | native public fun borrow_address(s: &signer): &address; 13 | 14 | // Copies the address of the signer 15 | public fun address_of(s: &signer): address { 16 | *borrow_address(s) 17 | } 18 | 19 | /// Return true only if `s` is a transaction signer. This is a spec function only available in spec. 20 | spec native fun is_txn_signer(s: signer): bool; 21 | 22 | /// Return true only if `a` is a transaction signer address. This is a spec function only available in spec. 23 | spec native fun is_txn_signer_addr(a: address): bool; 24 | } 25 | -------------------------------------------------------------------------------- /lib/sui-framework/deps/move-stdlib/sources/unit_test.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[test_only] 5 | /// Module providing testing functionality. Only included for tests. 6 | module std::unit_test { 7 | /// Return a `num_signers` number of unique signer values. No ordering or 8 | /// starting value guarantees are made, only that the order and values of 9 | /// the signers in the returned vector is deterministic. 10 | /// 11 | /// This function is also used to poison modules compiled in `test` mode. 12 | /// This will cause a linking failure if an attempt is made to publish a 13 | /// test module in a VM that isn't in unit test mode. 14 | native public fun create_signers_for_testing(num_signers: u64): vector; 15 | } 16 | -------------------------------------------------------------------------------- /lib/sui-framework/deps/move-stdlib/sources/vector.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// A variable-sized container that can hold any type. Indexing is 0-based, and 5 | /// vectors are growable. This module has many native functions. 6 | /// Verification of modules that use this one uses model functions that are implemented 7 | /// directly in Boogie. The specification language has built-in functions operations such 8 | /// as `singleton_vector`. There are some helper functions defined here for specifications in other 9 | /// modules as well. 10 | /// 11 | /// >Note: We did not verify most of the 12 | /// Move functions here because many have loops, requiring loop invariants to prove, and 13 | /// the return on investment didn't seem worth it for these simple functions. 14 | module std::vector { 15 | 16 | /// The index into the vector is out of bounds 17 | const EINDEX_OUT_OF_BOUNDS: u64 = 0; 18 | 19 | #[bytecode_instruction] 20 | /// Create an empty vector. 21 | native public fun empty(): vector; 22 | 23 | #[bytecode_instruction] 24 | /// Return the length of the vector. 25 | native public fun length(v: &vector): u64; 26 | 27 | #[bytecode_instruction] 28 | /// Acquire an immutable reference to the `i`th element of the vector `v`. 29 | /// Aborts if `i` is out of bounds. 30 | native public fun borrow(v: &vector, i: u64): ∈ 31 | 32 | #[bytecode_instruction] 33 | /// Add element `e` to the end of the vector `v`. 34 | native public fun push_back(v: &mut vector, e: Element); 35 | 36 | #[bytecode_instruction] 37 | /// Return a mutable reference to the `i`th element in the vector `v`. 38 | /// Aborts if `i` is out of bounds. 39 | native public fun borrow_mut(v: &mut vector, i: u64): &mut Element; 40 | 41 | #[bytecode_instruction] 42 | /// Pop an element from the end of vector `v`. 43 | /// Aborts if `v` is empty. 44 | native public fun pop_back(v: &mut vector): Element; 45 | 46 | #[bytecode_instruction] 47 | /// Destroy the vector `v`. 48 | /// Aborts if `v` is not empty. 49 | native public fun destroy_empty(v: vector); 50 | 51 | #[bytecode_instruction] 52 | /// Swaps the elements at the `i`th and `j`th indices in the vector `v`. 53 | /// Aborts if `i`or `j` is out of bounds. 54 | native public fun swap(v: &mut vector, i: u64, j: u64); 55 | 56 | /// Return an vector of size one containing element `e`. 57 | public fun singleton(e: Element): vector { 58 | let v = empty(); 59 | push_back(&mut v, e); 60 | v 61 | } 62 | spec singleton { 63 | // TODO: when using opaque here, we get verification errors. 64 | // pragma opaque; 65 | aborts_if false; 66 | ensures result == vec(e); 67 | } 68 | 69 | /// Reverses the order of the elements in the vector `v` in place. 70 | public fun reverse(v: &mut vector) { 71 | let len = length(v); 72 | if (len == 0) return (); 73 | 74 | let front_index = 0; 75 | let back_index = len -1; 76 | while (front_index < back_index) { 77 | swap(v, front_index, back_index); 78 | front_index = front_index + 1; 79 | back_index = back_index - 1; 80 | } 81 | } 82 | spec reverse { 83 | pragma intrinsic = true; 84 | } 85 | 86 | 87 | /// Pushes all of the elements of the `other` vector into the `lhs` vector. 88 | public fun append(lhs: &mut vector, other: vector) { 89 | reverse(&mut other); 90 | while (!is_empty(&other)) push_back(lhs, pop_back(&mut other)); 91 | destroy_empty(other); 92 | } 93 | spec append { 94 | pragma intrinsic = true; 95 | } 96 | spec is_empty { 97 | pragma intrinsic = true; 98 | } 99 | 100 | 101 | /// Return `true` if the vector `v` has no elements and `false` otherwise. 102 | public fun is_empty(v: &vector): bool { 103 | length(v) == 0 104 | } 105 | 106 | /// Return true if `e` is in the vector `v`. 107 | public fun contains(v: &vector, e: &Element): bool { 108 | let i = 0; 109 | let len = length(v); 110 | while (i < len) { 111 | if (borrow(v, i) == e) return true; 112 | i = i + 1; 113 | }; 114 | false 115 | } 116 | spec contains { 117 | pragma intrinsic = true; 118 | } 119 | 120 | /// Return `(true, i)` if `e` is in the vector `v` at index `i`. 121 | /// Otherwise, returns `(false, 0)`. 122 | public fun index_of(v: &vector, e: &Element): (bool, u64) { 123 | let i = 0; 124 | let len = length(v); 125 | while (i < len) { 126 | if (borrow(v, i) == e) return (true, i); 127 | i = i + 1; 128 | }; 129 | (false, 0) 130 | } 131 | spec index_of { 132 | pragma intrinsic = true; 133 | } 134 | 135 | /// Remove the `i`th element of the vector `v`, shifting all subsequent elements. 136 | /// This is O(n) and preserves ordering of elements in the vector. 137 | /// Aborts if `i` is out of bounds. 138 | public fun remove(v: &mut vector, i: u64): Element { 139 | let len = length(v); 140 | // i out of bounds; abort 141 | if (i >= len) abort EINDEX_OUT_OF_BOUNDS; 142 | 143 | len = len - 1; 144 | while (i < len) swap(v, i, { i = i + 1; i }); 145 | pop_back(v) 146 | } 147 | spec remove { 148 | pragma intrinsic = true; 149 | } 150 | 151 | /// Swap the `i`th element of the vector `v` with the last element and then pop the vector. 152 | /// This is O(1), but does not preserve ordering of elements in the vector. 153 | /// Aborts if `i` is out of bounds. 154 | public fun swap_remove(v: &mut vector, i: u64): Element { 155 | assert!(!is_empty(v), EINDEX_OUT_OF_BOUNDS); 156 | let last_idx = length(v) - 1; 157 | swap(v, i, last_idx); 158 | pop_back(v) 159 | } 160 | spec swap_remove { 161 | pragma intrinsic = true; 162 | } 163 | 164 | // ================================================================= 165 | // Module Specification 166 | 167 | spec module {} // Switch to module documentation context 168 | 169 | /// # Helper Functions 170 | 171 | spec module { 172 | /// Check if `v1` is equal to the result of adding `e` at the end of `v2` 173 | fun eq_push_back(v1: vector, v2: vector, e: Element): bool { 174 | len(v1) == len(v2) + 1 && 175 | v1[len(v1)-1] == e && 176 | v1[0..len(v1)-1] == v2[0..len(v2)] 177 | } 178 | 179 | /// Check if `v` is equal to the result of concatenating `v1` and `v2` 180 | fun eq_append(v: vector, v1: vector, v2: vector): bool { 181 | len(v) == len(v1) + len(v2) && 182 | v[0..len(v1)] == v1 && 183 | v[len(v1)..len(v)] == v2 184 | } 185 | 186 | /// Check `v1` is equal to the result of removing the first element of `v2` 187 | fun eq_pop_front(v1: vector, v2: vector): bool { 188 | len(v1) + 1 == len(v2) && 189 | v1 == v2[1..len(v2)] 190 | } 191 | 192 | /// Check that `v1` is equal to the result of removing the element at index `i` from `v2`. 193 | fun eq_remove_elem_at_index(i: u64, v1: vector, v2: vector): bool { 194 | len(v1) + 1 == len(v2) && 195 | v1[0..i] == v2[0..i] && 196 | v1[i..len(v1)] == v2[i + 1..len(v2)] 197 | } 198 | } 199 | 200 | } 201 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/bag.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// A Bag is a heterogeneous collection of objects with arbitrary types, i.e. 5 | /// the objects in the bag don't need to be of the same type. 6 | /// These objects are not stored in the Bag directly, instead only a reference 7 | /// to their IDs are stored as a proof of ownership. Sui tracks the ownership 8 | /// and is aware that the Bag owns those objects in it. Only the owner of the Bag 9 | /// could mutate the objects in the Bag. 10 | /// Bag is different from the Collection type in that Collection 11 | /// only supports owning objects of the same type. 12 | module sui::bag { 13 | use sui::object::{Self, ID, Info}; 14 | use sui::transfer; 15 | use sui::typed_id::{Self, TypedID}; 16 | use sui::tx_context::{Self, TxContext}; 17 | use sui::vec_set::{Self, VecSet}; 18 | 19 | // Error codes 20 | /// Adding the same object to the collection twice is not allowed. 21 | const EObjectDoubleAdd: u64 = 0; 22 | 23 | /// The max capacity set for the collection cannot exceed the hard limit 24 | /// which is DEFAULT_MAX_CAPACITY. 25 | const EInvalidMaxCapacity: u64 = 1; 26 | 27 | /// Trying to add object to the collection when the collection is 28 | /// already at its maximum capacity. 29 | const EMaxCapacityExceeded: u64 = 2; 30 | 31 | // TODO: this is a placeholder number 32 | const DEFAULT_MAX_CAPACITY: u64 = 65536; 33 | 34 | struct Bag has key { 35 | info: Info, 36 | objects: VecSet, 37 | max_capacity: u64, 38 | } 39 | 40 | struct Item has key { 41 | info: Info, 42 | value: T, 43 | } 44 | 45 | /// Create a new Bag and return it. 46 | public fun new(ctx: &mut TxContext): Bag { 47 | new_with_max_capacity(ctx, DEFAULT_MAX_CAPACITY) 48 | } 49 | 50 | /// Create a new Bag with custom size limit and return it. 51 | public fun new_with_max_capacity(ctx: &mut TxContext, max_capacity: u64): Bag { 52 | assert!(max_capacity <= DEFAULT_MAX_CAPACITY && max_capacity > 0, EInvalidMaxCapacity); 53 | Bag { 54 | info: object::new(ctx), 55 | objects: vec_set::empty(), 56 | max_capacity, 57 | } 58 | } 59 | 60 | /// Create a new Bag and transfer it to the signer. 61 | public entry fun create(ctx: &mut TxContext) { 62 | transfer::transfer(new(ctx), tx_context::sender(ctx)) 63 | } 64 | 65 | /// Returns the size of the Bag. 66 | public fun size(c: &Bag): u64 { 67 | vec_set::size(&c.objects) 68 | } 69 | 70 | /// Add a new object to the Bag. 71 | public fun add(c: &mut Bag, value: T, ctx: &mut TxContext): TypedID> { 72 | assert!(size(c) + 1 <= c.max_capacity, EMaxCapacityExceeded); 73 | let info = object::new(ctx); 74 | vec_set::insert(&mut c.objects, *object::info_id(&info)); 75 | let item = Item { info, value }; 76 | let item_id = typed_id::new(&item); 77 | transfer::transfer_to_object(item, c); 78 | item_id 79 | } 80 | 81 | /// identified by the object id in bytes. 82 | public fun contains(c: &Bag, id: &ID): bool { 83 | vec_set::contains(&c.objects, id) 84 | } 85 | 86 | /// Remove and return the object from the Bag. 87 | /// Abort if the object is not found. 88 | public fun remove(c: &mut Bag, item: Item): T { 89 | let Item { info, value } = item; 90 | vec_set::remove(&mut c.objects, object::info_id(&info)); 91 | object::delete(info); 92 | value 93 | } 94 | 95 | /// Remove the object from the Bag, and then transfer it to the signer. 96 | public entry fun remove_and_take( 97 | c: &mut Bag, 98 | item: Item, 99 | ctx: &mut TxContext, 100 | ) { 101 | let object = remove(c, item); 102 | transfer::transfer(object, tx_context::sender(ctx)); 103 | } 104 | 105 | /// Transfer the entire Bag to `recipient`. 106 | public entry fun transfer(c: Bag, recipient: address) { 107 | transfer::transfer(c, recipient) 108 | } 109 | 110 | public fun transfer_to_object_id( 111 | obj: Bag, 112 | owner_id: &Info, 113 | ) { 114 | transfer::transfer_to_object_id(obj, owner_id) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/balance.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// A storable handler for Balances in general. Is used in the `Coin` 5 | /// module to allow balance operations and can be used to implement 6 | /// custom coins with `Supply` and `Balance`s. 7 | module sui::balance { 8 | /// For when trying to destroy a non-zero balance. 9 | const ENonZero: u64 = 0; 10 | 11 | /// For when an overflow is happening on Supply operations. 12 | const EOverflow: u64 = 1; 13 | 14 | /// For when trying to withdraw more than there is. 15 | const ENotEnough: u64 = 2; 16 | 17 | /// A Supply of T. Used for minting and burning. 18 | /// Wrapped into a `TreasuryCap` in the `Coin` module. 19 | struct Supply has store { 20 | value: u64 21 | } 22 | 23 | /// Storable balance - an inner struct of a Coin type. 24 | /// Can be used to store coins which don't need to have the 25 | /// key ability. 26 | /// Helpful in representing a Coin without having to create a stand-alone object. 27 | struct Balance has store { 28 | value: u64 29 | } 30 | 31 | /// Get the amount stored in a `Balance`. 32 | public fun value(self: &Balance): u64 { 33 | self.value 34 | } 35 | 36 | /// Get the `Supply` value. 37 | public fun supply_value(supply: &Supply): u64 { 38 | supply.value 39 | } 40 | 41 | /// Create a new supply for type T. 42 | public fun create_supply(_witness: T): Supply { 43 | Supply { value: 0 } 44 | } 45 | 46 | /// Increase supply by `value` and create a new `Balance` with this value. 47 | public fun increase_supply(self: &mut Supply, value: u64): Balance { 48 | assert!(value < (18446744073709551615u64 - self.value), EOverflow); 49 | self.value = self.value + value; 50 | Balance { value } 51 | } 52 | 53 | /// Burn a Balance and decrease Supply. 54 | public fun decrease_supply(self: &mut Supply, balance: Balance): u64 { 55 | let Balance { value } = balance; 56 | assert!(self.value >= value, EOverflow); 57 | self.value = self.value - value; 58 | value 59 | } 60 | 61 | /// Create a zero `Balance` for type `T`. 62 | public fun zero(): Balance { 63 | Balance { value: 0 } 64 | } 65 | 66 | spec zero { 67 | aborts_if false; 68 | ensures result.value == 0; 69 | } 70 | 71 | /// Join two balances together. 72 | public fun join(self: &mut Balance, balance: Balance): u64 { 73 | let Balance { value } = balance; 74 | self.value = self.value + value; 75 | self.value 76 | } 77 | 78 | spec join { 79 | ensures self.value == old(self.value) + balance.value; 80 | ensures result == self.value; 81 | } 82 | 83 | /// Split a `Balance` and take a sub balance from it. 84 | public fun split(self: &mut Balance, value: u64): Balance { 85 | assert!(self.value >= value, ENotEnough); 86 | self.value = self.value - value; 87 | Balance { value } 88 | } 89 | 90 | spec split { 91 | aborts_if self.value < value with ENotEnough; 92 | ensures self.value == old(self.value) - value; 93 | ensures result.value == value; 94 | } 95 | 96 | /// Destroy a zero `Balance`. 97 | public fun destroy_zero(balance: Balance) { 98 | assert!(balance.value == 0, ENonZero); 99 | let Balance { value: _ } = balance; 100 | } 101 | 102 | spec destroy_zero { 103 | aborts_if balance.value != 0 with ENonZero; 104 | } 105 | 106 | #[test_only] 107 | /// Create a `Balance` of any coin for testing purposes. 108 | public fun create_for_testing(value: u64): Balance { 109 | Balance { value } 110 | } 111 | 112 | #[test_only] 113 | /// Destroy a `Balance` with any value in it for testing purposes. 114 | public fun destroy_for_testing(self: Balance): u64 { 115 | let Balance { value } = self; 116 | value 117 | } 118 | 119 | #[test_only] 120 | /// Create a `Supply` of any coin for testing purposes. 121 | public fun create_supply_for_testing(value: u64): Supply { 122 | Supply { value } 123 | } 124 | } 125 | 126 | #[test_only] 127 | module sui::balance_tests { 128 | use sui::balance; 129 | use sui::sui::SUI; 130 | 131 | #[test] 132 | fun test_balance() { 133 | let balance = balance::zero(); 134 | let another = balance::create_for_testing(1000); 135 | 136 | balance::join(&mut balance, another); 137 | 138 | assert!(balance::value(&balance) == 1000, 0); 139 | 140 | let balance1 = balance::split(&mut balance, 333); 141 | let balance2 = balance::split(&mut balance, 333); 142 | let balance3 = balance::split(&mut balance, 334); 143 | 144 | balance::destroy_zero(balance); 145 | 146 | assert!(balance::value(&balance1) == 333, 1); 147 | assert!(balance::value(&balance2) == 333, 2); 148 | assert!(balance::value(&balance3) == 334, 3); 149 | 150 | balance::destroy_for_testing(balance1); 151 | balance::destroy_for_testing(balance2); 152 | balance::destroy_for_testing(balance3); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/collection.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// The `Collection` type represents a collection of objects of the same type `T`. 5 | /// In contrast to `vector` which stores the object in the vector directly, 6 | /// `Collection` only tracks the ownership indirectly, by keeping a list of 7 | /// references to the object IDs. 8 | /// When using `vector`, since the objects will be wrapped inside the vector, 9 | /// these objects will not be stored in the global object pool, and hence not 10 | /// directly accessible. 11 | /// Collection allows us to own a list of same-typed objects, but still able to 12 | /// access and operate on each individual object. 13 | /// In contrast to `Bag`, `Collection` requires all objects have the same type. 14 | module sui::collection { 15 | use sui::object::{Self, ID, Info}; 16 | use sui::transfer; 17 | use sui::typed_id::{Self, TypedID}; 18 | use sui::tx_context::{Self, TxContext}; 19 | use sui::vec_set::{Self, VecSet}; 20 | 21 | // Error codes 22 | 23 | /// The max capacity set for the collection cannot exceed the hard limit 24 | /// which is DEFAULT_MAX_CAPACITY. 25 | const EInvalidMaxCapacity: u64 = 0; 26 | 27 | /// Trying to add object to the collection when the collection is 28 | /// already at its maximum capacity. 29 | const EMaxCapacityExceeded: u64 = 1; 30 | 31 | // TODO: this is a placeholder number 32 | // We want to limit the capacity of collection because it requires O(N) 33 | // for search and removals. We could relax the capacity constraint once 34 | // we could use more efficient data structure such as set. 35 | const DEFAULT_MAX_CAPACITY: u64 = 0x10000; 36 | 37 | struct Collection has key { 38 | info: Info, 39 | objects: VecSet, 40 | max_capacity: u64, 41 | } 42 | 43 | struct Item has key { 44 | info: Info, 45 | value: T, 46 | } 47 | 48 | /// Create a new Collection and return it. 49 | public fun new(ctx: &mut TxContext): Collection { 50 | new_with_max_capacity(ctx, DEFAULT_MAX_CAPACITY) 51 | } 52 | 53 | /// Create a new Collection with custom size limit and return it. 54 | public fun new_with_max_capacity( 55 | ctx: &mut TxContext, 56 | max_capacity: u64, 57 | ): Collection { 58 | assert!(max_capacity <= DEFAULT_MAX_CAPACITY && max_capacity > 0, EInvalidMaxCapacity); 59 | Collection { 60 | info: object::new(ctx), 61 | objects: vec_set::empty(), 62 | max_capacity, 63 | } 64 | } 65 | 66 | /// Create a new Collection and transfer it to the signer. 67 | public entry fun create(ctx: &mut TxContext) { 68 | transfer::transfer(new(ctx), tx_context::sender(ctx)) 69 | } 70 | 71 | /// Returns the size of the collection. 72 | public fun size(c: &Collection): u64 { 73 | vec_set::size(&c.objects) 74 | } 75 | 76 | /// Add an object to the collection. 77 | public fun add( 78 | c: &mut Collection, 79 | value: T, 80 | ctx: &mut TxContext, 81 | ): TypedID> { 82 | assert!(size(c) + 1 <= c.max_capacity, EMaxCapacityExceeded); 83 | let info = object::new(ctx); 84 | vec_set::insert(&mut c.objects, *object::info_id(&info)); 85 | let item = Item { info, value }; 86 | let item_id = typed_id::new(&item); 87 | transfer::transfer_to_object(item, c); 88 | item_id 89 | } 90 | 91 | /// Check whether the collection contains a specific object, 92 | /// identified by the object id in bytes. 93 | public fun contains(c: &Collection, id: &ID): bool { 94 | vec_set::contains(&c.objects, id) 95 | } 96 | 97 | /// Remove and return the object from the collection. 98 | /// Abort if the object is not found. 99 | public fun remove(c: &mut Collection, item: Item): T { 100 | let Item { info, value } = item; 101 | vec_set::remove(&mut c.objects, object::info_id(&info)); 102 | object::delete(info); 103 | value 104 | } 105 | 106 | /// Remove the object from the collection, and then transfer it to the signer. 107 | public entry fun remove_and_take( 108 | c: &mut Collection, 109 | item: Item, 110 | ctx: &mut TxContext, 111 | ) { 112 | let object = remove(c, item); 113 | transfer::transfer(object, tx_context::sender(ctx)); 114 | } 115 | 116 | /// Transfer the entire collection to `recipient`. 117 | public entry fun transfer(c: Collection, recipient: address) { 118 | transfer::transfer(c, recipient) 119 | } 120 | 121 | public fun transfer_to_object_id( 122 | obj: Collection, 123 | owner_id: &Info, 124 | ) { 125 | transfer::transfer_to_object_id(obj, owner_id) 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/devnet_nft.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// A minimalist example to demonstrate how to create an NFT like object 5 | /// on Sui. The user should be able to use the wallet command line tool 6 | /// (https://docs.sui.io/build/wallet) to mint an NFT. For example, 7 | /// `wallet example-nft --name --description --url ` 8 | module sui::devnet_nft { 9 | use sui::url::{Self, Url}; 10 | use sui::utf8; 11 | use sui::object::{Self, ID, Info}; 12 | use sui::event; 13 | use sui::transfer; 14 | use sui::tx_context::{Self, TxContext}; 15 | 16 | /// An example NFT that can be minted by anybody 17 | struct DevNetNFT has key, store { 18 | info: Info, 19 | /// Name for the token 20 | name: utf8::String, 21 | /// Description of the token 22 | description: utf8::String, 23 | /// URL for the token 24 | url: Url, 25 | // TODO: allow custom attributes 26 | } 27 | 28 | struct MintNFTEvent has copy, drop { 29 | // The Object ID of the NFT 30 | object_id: ID, 31 | // The creator of the NFT 32 | creator: address, 33 | // The name of the NFT 34 | name: utf8::String, 35 | } 36 | 37 | /// Create a new devnet_nft 38 | public entry fun mint( 39 | name: vector, 40 | description: vector, 41 | url: vector, 42 | ctx: &mut TxContext 43 | ) { 44 | let nft = DevNetNFT { 45 | info: object::new(ctx), 46 | name: utf8::string_unsafe(name), 47 | description: utf8::string_unsafe(description), 48 | url: url::new_unsafe_from_bytes(url) 49 | }; 50 | let sender = tx_context::sender(ctx); 51 | event::emit(MintNFTEvent { 52 | object_id: *object::info_id(&nft.info), 53 | creator: sender, 54 | name: nft.name, 55 | }); 56 | transfer::transfer(nft, sender); 57 | } 58 | 59 | /// Transfer `nft` to `recipient` 60 | public entry fun transfer( 61 | nft: DevNetNFT, recipient: address, _: &mut TxContext 62 | ) { 63 | transfer::transfer(nft, recipient) 64 | } 65 | 66 | /// Update the `description` of `nft` to `new_description` 67 | public entry fun update_description( 68 | nft: &mut DevNetNFT, 69 | new_description: vector, 70 | _: &mut TxContext 71 | ) { 72 | nft.description = utf8::string_unsafe(new_description) 73 | } 74 | 75 | /// Permanently delete `nft` 76 | public entry fun burn(nft: DevNetNFT, _: &mut TxContext) { 77 | let DevNetNFT { info, name: _, description: _, url: _ } = nft; 78 | object::delete(info) 79 | } 80 | 81 | /// Get the NFT's `name` 82 | public fun name(nft: &DevNetNFT): &utf8::String { 83 | &nft.name 84 | } 85 | 86 | /// Get the NFT's `description` 87 | public fun description(nft: &DevNetNFT): &utf8::String { 88 | &nft.description 89 | } 90 | 91 | /// Get the NFT's `url` 92 | public fun url(nft: &DevNetNFT): &Url { 93 | &nft.url 94 | } 95 | } 96 | 97 | #[test_only] 98 | module sui::devnet_nftTests { 99 | use sui::devnet_nft::{Self, DevNetNFT}; 100 | use sui::test_scenario; 101 | use sui::utf8; 102 | 103 | #[test] 104 | fun mint_transfer_update() { 105 | let addr1 = @0xA; 106 | let addr2 = @0xB; 107 | // create the NFT 108 | let scenario = test_scenario::begin(&addr1); 109 | { 110 | devnet_nft::mint(b"test", b"a test", b"https://www.sui.io", test_scenario::ctx(&mut scenario)) 111 | }; 112 | // send it from A to B 113 | test_scenario::next_tx(&mut scenario, &addr1); 114 | { 115 | let nft = test_scenario::take_owned(&mut scenario); 116 | devnet_nft::transfer(nft, addr2, test_scenario::ctx(&mut scenario)); 117 | }; 118 | // update its description 119 | test_scenario::next_tx(&mut scenario, &addr2); 120 | { 121 | let nft = test_scenario::take_owned(&mut scenario); 122 | devnet_nft::update_description(&mut nft, b"a new description", test_scenario::ctx(&mut scenario)) ; 123 | assert!(*utf8::bytes(devnet_nft::description(&nft)) == b"a new description", 0); 124 | test_scenario::return_owned(&mut scenario, nft); 125 | }; 126 | // burn it 127 | test_scenario::next_tx(&mut scenario, &addr2); 128 | { 129 | let nft = test_scenario::take_owned(&mut scenario); 130 | devnet_nft::burn(nft, test_scenario::ctx(&mut scenario)) 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/epoch_time_lock.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module sui::epoch_time_lock { 5 | use sui::tx_context::{Self, TxContext}; 6 | 7 | /// The epoch passed into the creation of a lock has already passed. 8 | const EEPOCH_ALREADY_PASSED: u64 = 0; 9 | /// Attempt is made to unlock a lock that cannot be unlocked yet. 10 | const EEPOCH_STILL_LOCKED: u64 = 1; 11 | 12 | /// Holder of an epoch number that can only be discarded in the epoch or 13 | /// after the epoch has passed. 14 | struct EpochTimeLock has store { 15 | epoch: u64 16 | } 17 | 18 | /// Create a new epoch time lock with `epoch`. Aborts if the current epoch is less than the input epoch. 19 | public fun new(epoch: u64, ctx: &mut TxContext) : EpochTimeLock { 20 | assert!(tx_context::epoch(ctx) < epoch, EEPOCH_ALREADY_PASSED); 21 | EpochTimeLock { epoch } 22 | } 23 | 24 | /// Destroys an epoch time lock. Aborts if the current epoch is less than the locked epoch. 25 | public fun destroy(lock: EpochTimeLock, ctx: &mut TxContext) { 26 | let EpochTimeLock { epoch } = lock; 27 | assert!(tx_context::epoch(ctx) >= epoch, EEPOCH_STILL_LOCKED); 28 | } 29 | 30 | /// Getter for the epoch number. 31 | public fun epoch(lock: &EpochTimeLock): u64 { 32 | lock.epoch 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/erc721_metadata.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module sui::erc721_metadata { 5 | use std::ascii; 6 | use sui::url::{Self, Url}; 7 | use sui::utf8; 8 | 9 | // TODO: add symbol()? 10 | /// A wrapper type for the ERC721 metadata standard https://eips.ethereum.org/EIPS/eip-721 11 | struct ERC721Metadata has store { 12 | /// The token id associated with the source contract on Ethereum 13 | token_id: TokenID, 14 | /// A descriptive name for a collection of NFTs in this contract. 15 | /// This corresponds to the `name()` method in the 16 | /// ERC721Metadata interface in EIP-721. 17 | name: utf8::String, 18 | /// A distinct Uniform Resource Identifier (URI) for a given asset. 19 | /// This corresponds to the `tokenURI()` method in the ERC721Metadata 20 | /// interface in EIP-721. 21 | token_uri: Url, 22 | } 23 | 24 | // TODO: replace u64 with u256 once the latter is supported 25 | // 26 | /// An ERC721 token ID 27 | struct TokenID has store, copy { 28 | id: u64, 29 | } 30 | 31 | /// Construct a new ERC721Metadata from the given inputs. Does not perform any validation 32 | /// on `token_uri` or `name` 33 | public fun new(token_id: TokenID, name: vector, token_uri: vector): ERC721Metadata { 34 | // Note: this will abort if `token_uri` is not valid ASCII 35 | let uri_str = ascii::string(token_uri); 36 | ERC721Metadata { 37 | token_id, 38 | name: utf8::string_unsafe(name), 39 | token_uri: url::new_unsafe(uri_str), 40 | } 41 | } 42 | 43 | public fun new_token_id(id: u64): TokenID { 44 | TokenID { id } 45 | } 46 | 47 | public fun token_id(self: &ERC721Metadata): &TokenID { 48 | &self.token_id 49 | } 50 | 51 | public fun token_uri(self: &ERC721Metadata): &Url { 52 | &self.token_uri 53 | } 54 | 55 | public fun name(self: &ERC721Metadata): &utf8::String { 56 | &self.name 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/event.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module sui::event { 5 | 6 | /// Add `t` to the event log of this transaction 7 | // TODO(https://github.com/MystenLabs/sui/issues/19): 8 | // restrict to internal types once we can express this in the ability system 9 | public native fun emit(event: T); 10 | 11 | // Cost calibration functions 12 | #[test_only] 13 | public fun calibrate_emit(obj: T) { 14 | emit(obj) 15 | } 16 | #[test_only] 17 | public fun calibrate_emit_nop(_obj: T) { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/governance/delegation.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module sui::delegation { 5 | use std::option::{Self, Option}; 6 | use sui::balance::{Self, Balance}; 7 | use sui::coin::{Self, Coin}; 8 | use sui::object::{Self, Info}; 9 | use sui::locked_coin::{Self, LockedCoin}; 10 | use sui::sui::SUI; 11 | use sui::transfer; 12 | use sui::tx_context::{Self, TxContext}; 13 | use sui::epoch_time_lock::EpochTimeLock; 14 | 15 | friend sui::sui_system; 16 | 17 | /// A custodial delegation object. When the delegation is active, the delegation 18 | /// object holds the delegated stake coin. It also contains the delegation 19 | /// target validator address. 20 | /// The delegation object is required to claim delegation reward. The object 21 | /// keeps track of the next reward unclaimed epoch. One can only claim reward 22 | /// for the epoch that matches the `next_reward_unclaimed_epoch`. 23 | /// When the delegation is deactivated, we keep track of the ending epoch 24 | /// so that we know the ending epoch that the delegator can still claim reward. 25 | struct Delegation has key { 26 | info: Info, 27 | /// The delegated stake, if the delegate is still active 28 | active_delegation: Option>, 29 | /// If the delegation is inactive, `ending_epoch` will be 30 | /// set to the ending epoch, i.e. the epoch when the delegation 31 | /// was withdrawn. Delegator will not be eligible to claim reward 32 | /// for ending_epoch and after. 33 | ending_epoch: Option, 34 | /// The delegated stake amount. 35 | delegate_amount: u64, 36 | /// Delegator is able to claim reward epoch by epoch. `next_reward_unclaimed_epoch` 37 | /// is the next epoch that the delegator can claim epoch. Whenever the delegator 38 | /// claims reward for an epoch, this value increments by one. 39 | next_reward_unclaimed_epoch: u64, 40 | /// The epoch until which the delegated coin is locked. If the delegated stake 41 | /// comes from a Coin, this field is None. If it comes from a LockedCoin, this 42 | /// field is not None, and after undelegation the stake will be returned to a LockedCoin 43 | /// with locked_until_epoch set to this epoch. 44 | coin_locked_until_epoch: Option, 45 | /// The delegation target validator. 46 | validator_address: address, 47 | } 48 | 49 | public(friend) fun create( 50 | starting_epoch: u64, 51 | validator_address: address, 52 | stake: Coin, 53 | ctx: &mut TxContext, 54 | ) { 55 | let delegate_amount = coin::value(&stake); 56 | let delegation = Delegation { 57 | info: object::new(ctx), 58 | active_delegation: option::some(coin::into_balance(stake)), 59 | ending_epoch: option::none(), 60 | delegate_amount, 61 | next_reward_unclaimed_epoch: starting_epoch, 62 | coin_locked_until_epoch: option::none(), 63 | validator_address, 64 | }; 65 | transfer::transfer(delegation, tx_context::sender(ctx)) 66 | } 67 | 68 | public(friend) fun create_from_locked_coin( 69 | starting_epoch: u64, 70 | validator_address: address, 71 | stake: LockedCoin, 72 | ctx: &mut TxContext, 73 | ) { 74 | let delegate_amount = locked_coin::value(&stake); 75 | let (balance, epoch_lock) = locked_coin::into_balance(stake); 76 | let delegation = Delegation { 77 | info: object::new(ctx), 78 | active_delegation: option::some(balance), 79 | ending_epoch: option::none(), 80 | delegate_amount, 81 | next_reward_unclaimed_epoch: starting_epoch, 82 | coin_locked_until_epoch: option::some(epoch_lock), 83 | validator_address, 84 | }; 85 | transfer::transfer(delegation, tx_context::sender(ctx)) 86 | } 87 | 88 | /// Deactivate the delegation. Send back the stake and set the ending epoch. 89 | public(friend) fun undelegate( 90 | self: &mut Delegation, 91 | ending_epoch: u64, 92 | ctx: &mut TxContext, 93 | ) { 94 | assert!(is_active(self), 0); 95 | assert!(ending_epoch >= self.next_reward_unclaimed_epoch, 0); 96 | 97 | let stake = option::extract(&mut self.active_delegation); 98 | let sender = tx_context::sender(ctx); 99 | 100 | if (option::is_none(&self.coin_locked_until_epoch)) { 101 | transfer::transfer(coin::from_balance(stake, ctx), sender); 102 | } else { 103 | let locked_until_epoch = option::extract(&mut self.coin_locked_until_epoch); 104 | locked_coin::new_from_balance(stake, locked_until_epoch, sender, ctx); 105 | }; 106 | 107 | self.ending_epoch = option::some(ending_epoch); 108 | } 109 | 110 | /// Switch the delegation from the current validator to `new_validator_address`. 111 | /// The current `Delegation` object `self` becomes inactive and the balance inside is 112 | /// extracted to the new `Delegation` object. 113 | public(friend) fun switch_delegation( 114 | self: &mut Delegation, 115 | new_validator_address: address, 116 | ctx: &mut TxContext, 117 | ) { 118 | assert!(is_active(self), 0); 119 | let current_epoch = tx_context::epoch(ctx); 120 | let balance = option::extract(&mut self.active_delegation); 121 | let delegate_amount = balance::value(&balance); 122 | 123 | let new_epoch_lock = 124 | if (option::is_some(&self.coin_locked_until_epoch)) { 125 | option::some(option::extract(&mut self.coin_locked_until_epoch)) 126 | } else { 127 | option::none() 128 | }; 129 | 130 | self.ending_epoch = option::some(current_epoch); 131 | 132 | let new_delegation = Delegation { 133 | info: object::new(ctx), 134 | active_delegation: option::some(balance), 135 | ending_epoch: option::none(), 136 | delegate_amount, 137 | next_reward_unclaimed_epoch: current_epoch + 1, 138 | coin_locked_until_epoch: new_epoch_lock, 139 | validator_address: new_validator_address, 140 | }; 141 | transfer::transfer(new_delegation, tx_context::sender(ctx)) 142 | } 143 | 144 | /// Claim delegation reward. Increment next_reward_unclaimed_epoch. 145 | public(friend) fun claim_reward( 146 | self: &mut Delegation, 147 | reward: Balance, 148 | ctx: &mut TxContext, 149 | ) { 150 | let sender = tx_context::sender(ctx); 151 | coin::transfer(coin::from_balance(reward, ctx), sender); 152 | self.next_reward_unclaimed_epoch = self.next_reward_unclaimed_epoch + 1; 153 | } 154 | 155 | 156 | /// Destroy the delegation object. This can be done only when the delegation 157 | /// is inactive and all reward have been claimed. 158 | public entry fun burn(self: Delegation) { 159 | assert!(!is_active(&self), 0); 160 | 161 | let Delegation { 162 | info, 163 | active_delegation, 164 | ending_epoch, 165 | delegate_amount: _, 166 | next_reward_unclaimed_epoch, 167 | coin_locked_until_epoch, 168 | validator_address: _, 169 | } = self; 170 | object::delete(info); 171 | option::destroy_none(active_delegation); 172 | option::destroy_none(coin_locked_until_epoch); 173 | let ending_epoch = *option::borrow(&ending_epoch); 174 | assert!(next_reward_unclaimed_epoch == ending_epoch, 0); 175 | } 176 | 177 | public entry fun transfer(self: Delegation, recipient: address) { 178 | transfer::transfer(self, recipient) 179 | } 180 | 181 | /// Checks whether the delegation object is eligible to claim the reward 182 | /// given the epoch to claim and the validator address. 183 | public fun can_claim_reward( 184 | self: &Delegation, 185 | epoch_to_claim: u64, 186 | validator: address, 187 | ): bool { 188 | if (validator != self.validator_address) { 189 | false 190 | } else if (is_active(self)) { 191 | self.next_reward_unclaimed_epoch <= epoch_to_claim 192 | } else { 193 | let ending_epoch = *option::borrow(&self.ending_epoch); 194 | ending_epoch > epoch_to_claim 195 | } 196 | } 197 | 198 | public fun validator(self: &Delegation): address { 199 | self.validator_address 200 | } 201 | 202 | public fun delegate_amount(self: &Delegation): u64 { 203 | self.delegate_amount 204 | } 205 | 206 | public fun is_active(self: &Delegation): bool { 207 | option::is_some(&self.active_delegation) && option::is_none(&self.ending_epoch) 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/governance/epoch_reward_record.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module sui::epoch_reward_record { 5 | use sui::object::{Self, Info}; 6 | use sui::transfer; 7 | use sui::tx_context::TxContext; 8 | 9 | friend sui::sui_system; 10 | friend sui::validator_set; 11 | 12 | /// EpochRewardRecord is an immutable record created per epoch per active validator. 13 | /// Sufficient information is saved in the record so that delegators can claim 14 | /// delegation rewards from past epochs, and for validators that may no longer be active. 15 | /// TODO: For now we assume that validators don't charge an extra fee. 16 | /// Delegation reward is simply proportional to to overall delegation reward ratio 17 | /// and the delegation amount. 18 | struct EpochRewardRecord has key { 19 | info: Info, 20 | epoch: u64, 21 | computation_charge: u64, 22 | total_stake: u64, 23 | delegator_count: u64, 24 | validator: address, 25 | } 26 | 27 | public(friend) fun create( 28 | epoch: u64, 29 | computation_charge: u64, 30 | total_stake: u64, 31 | delegator_count: u64, 32 | validator: address, 33 | ctx: &mut TxContext, 34 | ) { 35 | transfer::share_object(EpochRewardRecord { 36 | info: object::new(ctx), 37 | epoch, 38 | computation_charge, 39 | total_stake, 40 | delegator_count, 41 | validator, 42 | }) 43 | } 44 | 45 | /// Given the delegation amount, calculate the reward, and decrement the `delegator_count`. 46 | public(friend) fun claim_reward(self: &mut EpochRewardRecord, delegation_amount: u64): u64 { 47 | self.delegator_count = self.delegator_count - 1; 48 | // TODO: Once self.delegator_count reaches 0, we should be able to delete this object. 49 | delegation_amount * self.computation_charge / self.total_stake 50 | } 51 | 52 | public fun epoch(self: &EpochRewardRecord): u64 { 53 | self.epoch 54 | } 55 | 56 | public fun validator(self: &EpochRewardRecord): address { 57 | self.validator 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/governance/genesis.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module sui::genesis { 5 | use std::vector; 6 | 7 | use sui::balance; 8 | use sui::sui; 9 | use sui::sui_system; 10 | use sui::tx_context::TxContext; 11 | use sui::validator; 12 | use std::option; 13 | 14 | /// The initial amount of SUI locked in the storage fund. 15 | /// 10^14, an arbitrary number. 16 | const INIT_STORAGE_FUND: u64 = 100000000000000; 17 | 18 | /// Initial value of the lower-bound on the amount of stake required to become a validator. 19 | const INIT_MIN_VALIDATOR_STAKE: u64 = 100000000000000; 20 | 21 | /// Initial value of the upper-bound on the number of validators. 22 | const INIT_MAX_VALIDATOR_COUNT: u64 = 100; 23 | 24 | /// Initial storage gas price 25 | const INIT_STORAGE_GAS_PRICE: u64 = 1; 26 | 27 | /// This function will be explicitly called once at genesis. 28 | /// It will create a singleton SuiSystemState object, which contains 29 | /// all the information we need in the system. 30 | fun create( 31 | validator_pubkeys: vector>, 32 | validator_sui_addresses: vector
, 33 | validator_names: vector>, 34 | validator_net_addresses: vector>, 35 | validator_stakes: vector, 36 | ctx: &mut TxContext, 37 | ) { 38 | let sui_supply = sui::new(); 39 | let storage_fund = balance::increase_supply(&mut sui_supply, INIT_STORAGE_FUND); 40 | let validators = vector::empty(); 41 | let count = vector::length(&validator_pubkeys); 42 | assert!( 43 | vector::length(&validator_sui_addresses) == count 44 | && vector::length(&validator_stakes) == count 45 | && vector::length(&validator_names) == count 46 | && vector::length(&validator_net_addresses) == count, 47 | 1 48 | ); 49 | let i = 0; 50 | while (i < count) { 51 | let sui_address = *vector::borrow(&validator_sui_addresses, i); 52 | let pubkey = *vector::borrow(&validator_pubkeys, i); 53 | let name = *vector::borrow(&validator_names, i); 54 | let net_address = *vector::borrow(&validator_net_addresses, i); 55 | let stake = *vector::borrow(&validator_stakes, i); 56 | vector::push_back(&mut validators, validator::new( 57 | sui_address, 58 | pubkey, 59 | name, 60 | net_address, 61 | balance::increase_supply(&mut sui_supply, stake), 62 | option::none(), 63 | ctx 64 | )); 65 | i = i + 1; 66 | }; 67 | sui_system::create( 68 | validators, 69 | sui_supply, 70 | storage_fund, 71 | INIT_MAX_VALIDATOR_COUNT, 72 | INIT_MIN_VALIDATOR_STAKE, 73 | INIT_STORAGE_GAS_PRICE, 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/governance/stake.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module sui::stake { 5 | use std::option::{Self, Option}; 6 | use sui::balance::Balance; 7 | use sui::object::{Self, Info}; 8 | use sui::locked_coin; 9 | use sui::sui::SUI; 10 | use sui::transfer; 11 | use sui::tx_context::{Self, TxContext}; 12 | use sui::epoch_time_lock::EpochTimeLock; 13 | use sui::epoch_time_lock; 14 | use sui::balance; 15 | use sui::math; 16 | 17 | friend sui::sui_system; 18 | friend sui::validator; 19 | 20 | /// A custodial stake object holding the staked SUI coin. 21 | struct Stake has key { 22 | info: Info, 23 | /// The staked SUI tokens. 24 | balance: Balance, 25 | /// The epoch until which the staked coin is locked. If the stake 26 | /// comes from a Coin, this field is None. If it comes from a LockedCoin, this 27 | /// field will record the original lock expiration epoch, to be used when unstaking. 28 | locked_until_epoch: Option, 29 | } 30 | 31 | /// The number of epochs the withdrawn stake is locked for. 32 | /// TODO: this is a placehodler number and may be changed. 33 | const BONDING_PERIOD: u64 = 1; 34 | 35 | /// Error number for when a Stake with nonzero balance is burnt. 36 | const ENONZERO_BALANCE: u64 = 0; 37 | 38 | /// Create a stake object from a SUI balance. If the balance comes from a 39 | /// `LockedCoin`, an EpochTimeLock is passed in to keep track of locking period. 40 | public(friend) fun create( 41 | balance: Balance, 42 | recipient: address, 43 | locked_until_epoch: Option, 44 | ctx: &mut TxContext, 45 | ) { 46 | let stake = Stake { 47 | info: object::new(ctx), 48 | balance, 49 | locked_until_epoch, 50 | }; 51 | transfer::transfer(stake, recipient) 52 | } 53 | 54 | /// Withdraw `amount` from the balance of `stake`. 55 | public(friend) fun withdraw_stake( 56 | self: &mut Stake, 57 | amount: u64, 58 | ctx: &mut TxContext, 59 | ) { 60 | let sender = tx_context::sender(ctx); 61 | let unlock_epoch = tx_context::epoch(ctx) + BONDING_PERIOD; 62 | let balance = balance::split(&mut self.balance, amount); 63 | 64 | if (option::is_none(&self.locked_until_epoch)) { 65 | // If the stake didn't come from a locked coin, we give back the stake and 66 | // lock the coin for `BONDING_PERIOD`. 67 | locked_coin::new_from_balance(balance, epoch_time_lock::new(unlock_epoch, ctx), sender, ctx); 68 | } else { 69 | // If the stake did come from a locked coin, we lock the coin for 70 | // max(BONDING_PERIOD, remaining_lock_time). 71 | let original_unlock_epoch = epoch_time_lock::epoch(option::borrow(&self.locked_until_epoch)); 72 | let unlock_epoch = math::max(original_unlock_epoch, unlock_epoch); 73 | locked_coin::new_from_balance(balance, epoch_time_lock::new(unlock_epoch, ctx), sender, ctx); 74 | }; 75 | } 76 | 77 | /// Burn the stake object. This can be done only when the stake has a zero balance. 78 | public entry fun burn(self: Stake, ctx: &mut TxContext) { 79 | let Stake { info, balance, locked_until_epoch } = self; 80 | object::delete(info); 81 | balance::destroy_zero(balance); 82 | if (option::is_some(&locked_until_epoch)) { 83 | epoch_time_lock::destroy(option::extract(&mut locked_until_epoch), ctx); 84 | }; 85 | option::destroy_none(locked_until_epoch); 86 | } 87 | 88 | public fun value(self: &Stake): u64 { 89 | balance::value(&self.balance) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/governance/validator.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module sui::validator { 5 | use std::ascii; 6 | use std::vector; 7 | 8 | use sui::balance::{Self, Balance}; 9 | use sui::sui::SUI; 10 | use sui::tx_context::TxContext; 11 | use sui::stake; 12 | use sui::stake::Stake; 13 | use sui::epoch_time_lock::EpochTimeLock; 14 | use std::option::Option; 15 | 16 | friend sui::genesis; 17 | friend sui::sui_system; 18 | friend sui::validator_set; 19 | 20 | #[test_only] 21 | friend sui::validator_tests; 22 | #[test_only] 23 | friend sui::validator_set_tests; 24 | #[test_only] 25 | friend sui::governance_test_utils; 26 | 27 | struct ValidatorMetadata has store, drop, copy { 28 | /// The Sui Address of the validator. This is the sender that created the Validator object, 29 | /// and also the address to send validator/coins to during withdraws. 30 | sui_address: address, 31 | /// The public key bytes corresponding to the private key that the validator 32 | /// holds to sign transactions. For now, this is the same as AuthorityName. 33 | pubkey_bytes: vector, 34 | /// A unique human-readable name of this validator. 35 | name: vector, 36 | /// The network address of the validator (could also contain extra info such as port, DNS and etc.). 37 | net_address: vector, 38 | /// Total amount of validator stake that would be active in the next epoch. 39 | next_epoch_stake: u64, 40 | /// Total amount of delegated stake that would be active in the next epoch. 41 | next_epoch_delegation: u64, 42 | } 43 | 44 | struct Validator has store { 45 | /// Summary of the validator. 46 | metadata: ValidatorMetadata, 47 | /// The current active stake amount. This will not change during an epoch. It can only 48 | /// be updated at the end of epoch. 49 | stake_amount: u64, 50 | /// Amount of delegated stake from token holders. 51 | delegation: u64, 52 | /// Pending stake deposit amount, processed at end of epoch. 53 | pending_stake: u64, 54 | /// Pending withdraw amount, processed at end of epoch. 55 | pending_withdraw: u64, 56 | /// Pending delegation deposits. 57 | pending_delegation: u64, 58 | /// Pending delegation withdraws. 59 | pending_delegation_withdraw: u64, 60 | /// Number of delegators that is currently delegating token to this validator. 61 | /// This is used to create EpochRewardRecord, to help track how many delegators 62 | /// have not yet claimed their reward. 63 | delegator_count: u64, 64 | /// Number of new delegators that will become effective in the next epoch. 65 | pending_delegator_count: u64, 66 | /// Number of delegators that will withdraw stake at the end of the epoch. 67 | pending_delegator_withdraw_count: u64, 68 | } 69 | 70 | public(friend) fun new( 71 | sui_address: address, 72 | pubkey_bytes: vector, 73 | name: vector, 74 | net_address: vector, 75 | stake: Balance, 76 | coin_locked_until_epoch: Option, 77 | ctx: &mut TxContext 78 | ): Validator { 79 | assert!( 80 | // TODO: These constants are arbitrary, will adjust once we know more. 81 | vector::length(&net_address) <= 100 && vector::length(&name) <= 50 && vector::length(&pubkey_bytes) <= 128, 82 | 0 83 | ); 84 | // Check that the name is human-readable. 85 | ascii::string(copy name); 86 | let stake_amount = balance::value(&stake); 87 | stake::create(stake, sui_address, coin_locked_until_epoch, ctx); 88 | Validator { 89 | metadata: ValidatorMetadata { 90 | sui_address, 91 | pubkey_bytes, 92 | name, 93 | net_address, 94 | next_epoch_stake: stake_amount, 95 | next_epoch_delegation: 0, 96 | }, 97 | stake_amount, 98 | delegation: 0, 99 | pending_stake: 0, 100 | pending_withdraw: 0, 101 | pending_delegation: 0, 102 | pending_delegation_withdraw: 0, 103 | delegator_count: 0, 104 | pending_delegator_count: 0, 105 | pending_delegator_withdraw_count: 0, 106 | } 107 | } 108 | 109 | public(friend) fun destroy(self: Validator) { 110 | let Validator { 111 | metadata: _, 112 | stake_amount: _, 113 | delegation: _, 114 | pending_stake: _, 115 | pending_withdraw: _, 116 | pending_delegation: _, 117 | pending_delegation_withdraw: _, 118 | delegator_count: _, 119 | pending_delegator_count: _, 120 | pending_delegator_withdraw_count: _, 121 | } = self; 122 | } 123 | 124 | /// Add stake to an active validator. The new stake is added to the pending_stake field, 125 | /// which will be processed at the end of epoch. 126 | public(friend) fun request_add_stake( 127 | self: &mut Validator, 128 | new_stake: Balance, 129 | coin_locked_until_epoch: Option, 130 | ctx: &mut TxContext, 131 | ) { 132 | let new_stake_value = balance::value(&new_stake); 133 | self.pending_stake = self.pending_stake + new_stake_value; 134 | self.metadata.next_epoch_stake = self.metadata.next_epoch_stake + new_stake_value; 135 | stake::create(new_stake, self.metadata.sui_address, coin_locked_until_epoch, ctx); 136 | } 137 | 138 | /// Withdraw stake from an active validator. Since it's active, we need 139 | /// to add it to the pending withdraw amount and process it at the end 140 | /// of epoch. We also need to make sure there is sufficient amount to withdraw such that the validator's 141 | /// stake still satisfy the minimum requirement. 142 | public(friend) fun request_withdraw_stake( 143 | self: &mut Validator, 144 | stake: &mut Stake, 145 | withdraw_amount: u64, 146 | min_validator_stake: u64, 147 | ctx: &mut TxContext, 148 | ) { 149 | assert!(self.metadata.next_epoch_stake >= withdraw_amount + min_validator_stake, 0); 150 | self.pending_withdraw = self.pending_withdraw + withdraw_amount; 151 | self.metadata.next_epoch_stake = self.metadata.next_epoch_stake - withdraw_amount; 152 | stake::withdraw_stake(stake, withdraw_amount, ctx); 153 | } 154 | 155 | /// Process pending stake and pending withdraws. 156 | public(friend) fun adjust_stake(self: &mut Validator) { 157 | self.stake_amount = self.stake_amount + self.pending_stake - self.pending_withdraw; 158 | self.pending_stake = 0; 159 | self.pending_withdraw = 0; 160 | assert!(self.stake_amount == self.metadata.next_epoch_stake, 0); 161 | 162 | self.delegation = self.delegation + self.pending_delegation - self.pending_delegation_withdraw; 163 | self.pending_delegation = 0; 164 | self.pending_delegation_withdraw = 0; 165 | 166 | self.delegator_count = self.delegator_count + self.pending_delegator_count - self.pending_delegator_withdraw_count; 167 | self.pending_delegator_count = 0; 168 | self.pending_delegator_withdraw_count = 0; 169 | assert!(self.delegation == self.metadata.next_epoch_delegation, 0); 170 | } 171 | 172 | public(friend) fun request_add_delegation(self: &mut Validator, delegate_amount: u64) { 173 | assert!(delegate_amount > 0, 0); 174 | self.pending_delegation = self.pending_delegation + delegate_amount; 175 | self.pending_delegator_count = self.pending_delegator_count + 1; 176 | self.metadata.next_epoch_delegation = self.metadata.next_epoch_delegation + delegate_amount; 177 | } 178 | 179 | public(friend) fun request_remove_delegation(self: &mut Validator, delegate_amount: u64) { 180 | self.pending_delegation_withdraw = self.pending_delegation_withdraw + delegate_amount; 181 | self.pending_delegator_withdraw_count = self.pending_delegator_withdraw_count + 1; 182 | self.metadata.next_epoch_delegation = self.metadata.next_epoch_delegation - delegate_amount; 183 | } 184 | 185 | public fun metadata(self: &Validator): &ValidatorMetadata { 186 | &self.metadata 187 | } 188 | 189 | public fun sui_address(self: &Validator): address { 190 | self.metadata.sui_address 191 | } 192 | 193 | public fun stake_amount(self: &Validator): u64 { 194 | self.stake_amount 195 | } 196 | 197 | public fun delegate_amount(self: &Validator): u64 { 198 | self.delegation 199 | } 200 | 201 | public fun delegator_count(self: &Validator): u64 { 202 | self.delegator_count 203 | } 204 | 205 | public fun pending_stake_amount(self: &Validator): u64 { 206 | self.pending_stake 207 | } 208 | 209 | public fun pending_withdraw(self: &Validator): u64 { 210 | self.pending_withdraw 211 | } 212 | 213 | public fun is_duplicate(self: &Validator, other: &Validator): bool { 214 | self.metadata.sui_address == other.metadata.sui_address 215 | || self.metadata.name == other.metadata.name 216 | || self.metadata.net_address == other.metadata.net_address 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/locked_coin.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module sui::locked_coin { 5 | use sui::balance::{Self, Balance}; 6 | use sui::coin::{Self, Coin}; 7 | use sui::object::{Self, Info}; 8 | use sui::transfer; 9 | use sui::tx_context::{Self, TxContext}; 10 | use sui::epoch_time_lock::{Self, EpochTimeLock}; 11 | 12 | friend sui::delegation; 13 | friend sui::sui_system; 14 | 15 | /// A coin of type `T` locked until `locked_until_epoch`. 16 | struct LockedCoin has key, store { 17 | info: Info, 18 | balance: Balance, 19 | locked_until_epoch: EpochTimeLock 20 | } 21 | 22 | /// Create a LockedCoin from `balance` and transfer it to `owner`. 23 | public fun new_from_balance(balance: Balance, locked_until_epoch: EpochTimeLock, owner: address, ctx: &mut TxContext) { 24 | let locked_coin = LockedCoin { 25 | info: object::new(ctx), 26 | balance, 27 | locked_until_epoch 28 | }; 29 | transfer::transfer(locked_coin, owner); 30 | } 31 | 32 | /// Destruct a LockedCoin wrapper and keep the balance. 33 | public(friend) fun into_balance(coin: LockedCoin): (Balance, EpochTimeLock) { 34 | let LockedCoin { info, locked_until_epoch, balance } = coin; 35 | object::delete(info); 36 | (balance, locked_until_epoch) 37 | } 38 | 39 | /// Public getter for the locked coin's value 40 | public fun value(self: &LockedCoin): u64 { 41 | balance::value(&self.balance) 42 | } 43 | 44 | /// Lock a coin up until `locked_until_epoch`. The input Coin is deleted and a LockedCoin 45 | /// is transferred to the `recipient`. This function aborts if the `locked_until_epoch` is less than 46 | /// or equal to the current epoch. 47 | public entry fun lock_coin( 48 | coin: Coin, recipient: address, locked_until_epoch: u64, ctx: &mut TxContext 49 | ) { 50 | let balance = coin::into_balance(coin); 51 | new_from_balance(balance, epoch_time_lock::new(locked_until_epoch, ctx), recipient, ctx); 52 | } 53 | 54 | /// Unlock a locked coin. The function aborts if the current epoch is less than the `locked_until_epoch` 55 | /// of the coin. If the check is successful, the locked coin is deleted and a Coin is transferred back 56 | /// to the sender. 57 | public entry fun unlock_coin(locked_coin: LockedCoin, ctx: &mut TxContext) { 58 | let LockedCoin { info, balance, locked_until_epoch } = locked_coin; 59 | object::delete(info); 60 | epoch_time_lock::destroy(locked_until_epoch, ctx); 61 | let coin = coin::from_balance(balance, ctx); 62 | transfer::transfer(coin, tx_context::sender(ctx)); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/math.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// Basic math for nicer programmability 5 | module sui::math { 6 | 7 | /// Return the larger of `x` and `y` 8 | public fun max(x: u64, y: u64): u64 { 9 | if (x > y) { 10 | x 11 | } else { 12 | y 13 | } 14 | } 15 | 16 | /// Return the smaller of `x` and `y` 17 | public fun min(x: u64, y: u64): u64 { 18 | if (x < y) { 19 | x 20 | } else { 21 | y 22 | } 23 | } 24 | 25 | /// Get a nearest lower integer Square Root for `x`. Given that this 26 | /// function can only operate with integers, it is impossible 27 | /// to get perfect (or precise) integer square root for some numbers. 28 | /// 29 | /// Example: 30 | /// ``` 31 | /// math::sqrt(9) => 3 32 | /// math::sqrt(8) => 2 // the nearest lower square root is 4; 33 | /// ``` 34 | /// 35 | /// In integer math, one of the possible ways to get results with more 36 | /// precision is to use higher values or temporarily multiply the 37 | /// value by some bigger number. Ideally if this is a square of 10 or 100. 38 | /// 39 | /// Example: 40 | /// ``` 41 | /// math::sqrt(8) => 2; 42 | /// math::sqrt(8 * 10000) => 282; 43 | /// // now we can use this value as if it was 2.82; 44 | /// // but to get the actual result, this value needs 45 | /// // to be divided by 100 (because sqrt(10000)). 46 | /// 47 | /// 48 | /// math::sqrt(8 * 1000000) => 2828; // same as above, 2828 / 1000 (2.828) 49 | /// ``` 50 | public fun sqrt(x: u64): u64 { 51 | let bit = 1u128 << 64; 52 | let res = 0u128; 53 | let x = (x as u128); 54 | 55 | while (bit != 0) { 56 | if (x >= res + bit) { 57 | x = x - (res + bit); 58 | res = (res >> 1) + bit; 59 | } else { 60 | res = res >> 1; 61 | }; 62 | bit = bit >> 2; 63 | }; 64 | 65 | (res as u64) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/object.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// Sui object identifiers 5 | module sui::object { 6 | use std::bcs; 7 | use std::vector; 8 | // use std::option::{Self, Option}; 9 | use sui::tx_context::{Self, TxContext}; 10 | 11 | friend sui::sui_system; 12 | friend sui::transfer; 13 | 14 | #[test_only] 15 | friend sui::test_scenario; 16 | 17 | /// Version of an object ID created by the current transaction. 18 | const INITIAL_VERSION: u64 = 0; 19 | 20 | /// The hardcoded ID for the singleton Sui System State Object. 21 | const SUI_SYSTEM_STATE_OBJECT_ID: address = @0x5; 22 | 23 | /// Number of bytes in an object ID 24 | const ID_SIZE: u64 = 20; 25 | 26 | /// Attempting to construct an object ID with the wrong number of bytes--expected 20. 27 | const EBadIDLength: u64 = 0; 28 | 29 | /// Attempting to delete a parent object that still has children 30 | const EParentHasChildren: u64 = 0; 31 | 32 | /// An object ID. This is *not* guaranteed to be globally 33 | /// unique--anyone can create an `ID`, and ID's can be freely copied and dropped. 34 | struct ID has copy, drop, store { 35 | // We use `address` instead of `vector` here because `address` has a more 36 | // compact serialization. `address` is serialized as a BCS fixed-length sequence, 37 | // which saves us the length prefix we would pay for if this were `vector`. 38 | // See https://github.com/diem/bcs#fixed-and-variable-length-sequences. 39 | bytes: address 40 | } 41 | 42 | /// The information that defines an object. It contains 43 | /// - A globally unique ID 44 | /// - A version 45 | /// - The number of child objects 46 | /// This is a privileged type that can only be derived from a `TxContext`. 47 | /// `Info` doesn't have the `drop` ability, so deleting a `Info` requires a call to `delete` 48 | struct Info has store { 49 | id: ID, 50 | /// Version number for the object. The version number is incremented each 51 | /// time the object with this ID is passed to a non-failing transaction 52 | /// either by value or by mutable reference. 53 | version: u64, 54 | // /// The number of child objects. In order to delete the `Info`, this must be none or 0 55 | // child_count: Option, 56 | } 57 | 58 | // === id === 59 | 60 | /// Create an `ID` from an address 61 | public fun id_from_address(a: address): ID { 62 | ID { bytes: a } 63 | } 64 | 65 | /// Create an `ID` from raw bytes. 66 | /// Aborts with `EBadIDLength` if the length of `bytes` is not `ID_SIZE` 67 | public fun id_from_bytes(bytes: vector): ID { 68 | assert!(vector::length(&bytes) == ID_SIZE, EBadIDLength); 69 | ID { bytes: bytes_to_address(bytes) } 70 | } 71 | 72 | /// Get the raw bytes of `id` 73 | public fun id_to_bytes(id: &ID): vector { 74 | bcs::to_bytes(&id.bytes) 75 | } 76 | 77 | // === info === 78 | 79 | /// Create the `Info` for the singleton `SuiSystemState` object. 80 | /// This should only be called once from `sui_system`. 81 | public(friend) fun sui_system_state(): Info { 82 | Info { 83 | id: ID { bytes: SUI_SYSTEM_STATE_OBJECT_ID }, 84 | version: INITIAL_VERSION, 85 | // child_count: option::none(), 86 | } 87 | } 88 | 89 | /// Get the inner `ID` of `versioned_id` 90 | public fun info_id(info: &Info): &ID { 91 | &info.id 92 | } 93 | 94 | /// Get the raw bytes of a `versioned_id`'s inner `ID` 95 | public fun info_id_bytes(info: &Info): vector { 96 | id_to_bytes(info_id(info)) 97 | } 98 | 99 | // /// Get the number of child objects. 100 | // /// Returns 0 if `child_count` is none 101 | // public fun info_child_count(info: &Info): u64 { 102 | // option::get_with_default(&info.child_count, 0) 103 | // } 104 | 105 | // === any object === 106 | 107 | /// Create a new object. Returns the `Info` that must be stored in a Sui object. 108 | /// This is the only way to create `Info`. 109 | public fun new(ctx: &mut TxContext): Info { 110 | Info { 111 | id: ID { bytes: tx_context::new_object(ctx) }, 112 | version: INITIAL_VERSION, 113 | // child_count: option::none(), 114 | } 115 | } 116 | 117 | /// Delete the object and it's `Info`. This is the only way to eliminate a `Info`. 118 | // This exists to inform Sui of object deletions. When an object 119 | // gets unpacked, the programmer will have to do something with its 120 | // `Info`. The implementation of this function emits a deleted 121 | // system event so Sui knows to process the object deletion 122 | public fun delete(info: Info) { 123 | // assert!(info_child_count(&info) == 0, EParentHasChildren); 124 | delete_impl(info) 125 | } 126 | 127 | /// Get the underlying `ID` of `obj` 128 | public fun id(obj: &T): &ID { 129 | info_id(get_info(obj)) 130 | } 131 | 132 | /// Get raw bytes for the underlying `ID` of `obj` 133 | public fun id_bytes(obj: &T): vector { 134 | info_id_bytes(get_info(obj)) 135 | } 136 | 137 | // /// Get the number of child objects. 138 | // public fun child_count(obj: &T): u64 { 139 | // info_child_count(get_info(obj)) 140 | // } 141 | 142 | /// Get the `version` of `obj`. 143 | // Private and unused for now, but may expose in the future 144 | fun version(obj: &T): u64 { 145 | let versioned_id = get_info(obj); 146 | versioned_id.version 147 | } 148 | 149 | /// Get the inner bytes of `id` as an address. 150 | // Only used by `Transfer` and `TestSecnario`, but may expose in the future 151 | public(friend) fun id_address(id: &ID): address { 152 | id.bytes 153 | } 154 | 155 | /// Return `true` if `obj` was created by the current transaction, 156 | /// `false` otherwise. 157 | // Private and unused for now, but may expose in the future 158 | fun created_by_current_tx(obj: &T): bool { 159 | version(obj) == INITIAL_VERSION 160 | } 161 | 162 | /// Get the `Info` for `obj`. 163 | // Safe because Sui has an extra 164 | // bytecode verifier pass that forces every struct with 165 | // the `key` ability to have a distinguished `Info` field. 166 | native fun get_info(obj: &T): &Info; 167 | 168 | // === destructors === 169 | 170 | 171 | native fun delete_impl(info: Info); 172 | 173 | // === internal functions === 174 | 175 | /// Convert raw bytes into an address 176 | native fun bytes_to_address(bytes: vector): address; 177 | 178 | // Cost calibration functions 179 | #[test_only] 180 | public fun calibrate_bytes_to_address(bytes: vector) { 181 | bytes_to_address(bytes); 182 | } 183 | #[test_only] 184 | public fun calibrate_bytes_to_address_nop(_bytes: vector) { 185 | } 186 | 187 | #[test_only] 188 | public fun calibrate_get_info(obj: &T) { 189 | get_info(obj); 190 | } 191 | #[test_only] 192 | public fun calibrate_get_info_nop(_obj: &T) { 193 | } 194 | 195 | // TBD 196 | 197 | // #[test_only] 198 | // public fun calibrate_delete_impl(info: Info) { 199 | // delete_impl(id); 200 | // } 201 | // #[test_only] 202 | // public fun calibrate_delete_impl(_id: Info) { 203 | // } 204 | 205 | #[test_only] 206 | /// Return the most recent created object ID. 207 | public fun last_created(ctx: &TxContext): ID { 208 | id_from_address(tx_context::last_created_object_id(ctx)) 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/object_basics.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// Test CTURD object basics (create, transfer, update, read, delete) 5 | module sui::object_basics { 6 | use sui::event; 7 | use sui::object::{Self, Info}; 8 | use sui::tx_context::{Self, TxContext}; 9 | use sui::transfer; 10 | 11 | struct Object has key, store { 12 | info: Info, 13 | value: u64, 14 | } 15 | 16 | struct Wrapper has key { 17 | info: Info, 18 | o: Object 19 | } 20 | 21 | struct NewValueEvent has copy, drop { 22 | new_value: u64 23 | } 24 | 25 | public entry fun create(value: u64, recipient: address, ctx: &mut TxContext) { 26 | transfer::transfer( 27 | Object { info: object::new(ctx), value }, 28 | recipient 29 | ) 30 | } 31 | 32 | public entry fun transfer(o: Object, recipient: address) { 33 | transfer::transfer(o, recipient) 34 | } 35 | 36 | public entry fun freeze_object(o: Object) { 37 | transfer::freeze_object(o) 38 | } 39 | 40 | public entry fun set_value(o: &mut Object, value: u64) { 41 | o.value = value; 42 | } 43 | 44 | // test that reading o2 and updating o1 works 45 | public entry fun update(o1: &mut Object, o2: &Object) { 46 | o1.value = o2.value; 47 | // emit an event so the world can see the new value 48 | event::emit(NewValueEvent { new_value: o2.value }) 49 | } 50 | 51 | public entry fun delete(o: Object) { 52 | let Object { info, value: _ } = o; 53 | object::delete(info); 54 | } 55 | 56 | public entry fun wrap(o: Object, ctx: &mut TxContext) { 57 | transfer::transfer(Wrapper { info: object::new(ctx), o }, tx_context::sender(ctx)) 58 | } 59 | 60 | public entry fun unwrap(w: Wrapper, ctx: &mut TxContext) { 61 | let Wrapper { info, o } = w; 62 | object::delete(info); 63 | transfer::transfer(o, tx_context::sender(ctx)) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/sui.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// Coin is the token used to pay for gas in Sui 5 | module sui::sui { 6 | use sui::coin; 7 | use sui::balance::{Self, Supply}; 8 | 9 | friend sui::genesis; 10 | 11 | /// Name of the coin 12 | struct SUI has drop {} 13 | 14 | /// Register the token to acquire its `TreasuryCap`. 15 | /// This should be called only once during genesis creation. 16 | public(friend) fun new(): Supply { 17 | balance::create_supply(SUI {}) 18 | } 19 | 20 | /// Transfer to a recipient 21 | public entry fun transfer(c: coin::Coin, recipient: address) { 22 | coin::transfer(c, recipient) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/transfer.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module sui::transfer { 5 | use sui::object::{Self, Info}; 6 | 7 | /// Transfer ownership of `obj` to `recipient`. `obj` must have the 8 | /// `key` attribute, which (in turn) ensures that `obj` has a globally 9 | /// unique ID. 10 | public fun transfer(obj: T, recipient: address) { 11 | // TODO: emit event 12 | transfer_internal(obj, recipient, false) 13 | } 14 | 15 | /// Transfer ownership of `obj` to another object `owner`. 16 | public fun transfer_to_object(obj: T, owner: &mut R) { 17 | let owner_id = object::id_address(object::id(owner)); 18 | transfer_internal(obj, owner_id, true); 19 | } 20 | 21 | /// Similar to transfer_to_object where we want to transfer an object to another object. 22 | /// However, in the case when we haven't yet created the parent object (typically during 23 | /// parent object construction), and all we have is just a parent object ID, we could 24 | /// use this function to transfer an object to the parent object identified by its id. 25 | /// Additionally, this API is useful for transfering to objects, outside of that object's 26 | /// module. The object's module can expose a function that returns a reference to the object's 27 | /// verssioned ID, `&Info`. Which can then be used with this function. 28 | /// The child object is specified in `obj`, and the parent object id is specified in `owner_id`. 29 | public fun transfer_to_object_id(obj: T, owner_id: &Info) { 30 | let inner_owner_id = *object::info_id(owner_id); 31 | transfer_internal(obj, object::id_address(&inner_owner_id), true); 32 | } 33 | 34 | /// Freeze `obj`. After freezing `obj` becomes immutable and can no 35 | /// longer be transferred or mutated. 36 | public native fun freeze_object(obj: T); 37 | 38 | /// Turn the given object into a mutable shared object that everyone 39 | /// can access and mutate. This is irreversible, i.e. once an object 40 | /// is shared, it will stay shared forever. 41 | /// Shared mutable object is not yet fully supported in Sui, which is being 42 | /// actively worked on and should be supported very soon. 43 | /// https://github.com/MystenLabs/sui/issues/633 44 | /// https://github.com/MystenLabs/sui/issues/681 45 | /// This API is exposed to demonstrate how we may be able to use it to program 46 | /// Move contracts that use shared objects. 47 | public native fun share_object(obj: T); 48 | 49 | native fun transfer_internal(obj: T, recipient: address, to_object: bool); 50 | 51 | // Cost calibration functions 52 | #[test_only] 53 | public fun calibrate_freeze_object(obj: T) { 54 | freeze_object(obj) 55 | } 56 | #[test_only] 57 | public fun calibrate_freeze_object_nop(_obj: T) { 58 | } 59 | 60 | #[test_only] 61 | public fun calibrate_share_object(obj: T) { 62 | share_object(obj) 63 | } 64 | #[test_only] 65 | public fun calibrate_share_object_nop(_obj: T) { 66 | } 67 | 68 | #[test_only] 69 | public fun calibrate_transfer_internal(obj: T, recipient: address, to_object: bool) { 70 | transfer_internal(obj, recipient, to_object) 71 | } 72 | #[test_only] 73 | public fun calibrate_transfer_internal_nop(_obj: T, _recipient: address, _to_object: bool) { 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/tx_context.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module sui::tx_context { 5 | use std::signer; 6 | 7 | friend sui::object; 8 | 9 | #[test_only] 10 | use std::vector; 11 | 12 | /// Number of bytes in an tx hash (which will be the transaction digest) 13 | const TX_HASH_LENGTH: u64 = 32; 14 | 15 | /// Expected an tx hash of length 32, but found a different length 16 | const EBadTxHashLength: u64 = 0; 17 | 18 | #[test_only] 19 | /// Attempt to get the most recent created object ID when none has been created. 20 | const ENoIDsCreated: u64 = 1; 21 | 22 | /// Information about the transaction currently being executed. 23 | /// This cannot be constructed by a transaction--it is a privileged object created by 24 | /// the VM and passed in to the entrypoint of the transaction as `&mut TxContext`. 25 | struct TxContext has drop { 26 | /// A `signer` wrapping the address of the user that signed the current transaction 27 | signer: signer, 28 | /// Hash of the current transaction 29 | tx_hash: vector, 30 | /// The current epoch number. 31 | epoch: u64, 32 | /// Counter recording the number of fresh id's created while executing 33 | /// this transaction. Always 0 at the start of a transaction 34 | ids_created: u64 35 | } 36 | 37 | /// Return the address of the user that signed the current 38 | /// transaction 39 | public fun sender(self: &TxContext): address { 40 | signer::address_of(&self.signer) 41 | } 42 | 43 | /// Return a `signer` for the user that signed the current transaction 44 | public fun signer_(self: &TxContext): &signer { 45 | &self.signer 46 | } 47 | 48 | public fun epoch(self: &TxContext): u64 { 49 | self.epoch 50 | } 51 | 52 | /// Generate a new, globally unique object ID with version 0 53 | public(friend) fun new_object(ctx: &mut TxContext): address { 54 | let ids_created = ctx.ids_created; 55 | let id = derive_id(*&ctx.tx_hash, ids_created); 56 | ctx.ids_created = ids_created + 1; 57 | id 58 | } 59 | 60 | /// Return the number of id's created by the current transaction. 61 | /// Hidden for now, but may expose later 62 | fun ids_created(self: &TxContext): u64 { 63 | self.ids_created 64 | } 65 | 66 | /// Native function for deriving an ID via hash(tx_hash || ids_created) 67 | native fun derive_id(tx_hash: vector, ids_created: u64): address; 68 | 69 | // ==== test-only functions ==== 70 | 71 | #[test_only] 72 | /// Create a `TxContext` for testing 73 | public fun new(addr: address, tx_hash: vector, epoch: u64, ids_created: u64): TxContext { 74 | assert!(vector::length(&tx_hash) == TX_HASH_LENGTH, EBadTxHashLength); 75 | TxContext { signer: new_signer_from_address(addr), tx_hash, epoch, ids_created } 76 | } 77 | 78 | #[test_only] 79 | /// Create a `TxContext` for testing, with a potentially non-zero epoch number. 80 | public fun new_from_hint(addr: address, hint: u8, epoch: u64, ids_created: u64): TxContext { 81 | new(addr, dummy_tx_hash_with_hint(hint), epoch, ids_created) 82 | } 83 | 84 | #[test_only] 85 | /// Create a dummy `TxContext` for testing 86 | public fun dummy(): TxContext { 87 | let tx_hash = x"3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"; 88 | new(@0x0, tx_hash, 0, 0) 89 | } 90 | 91 | #[test_only] 92 | /// Utility for creating 256 unique input hashes 93 | fun dummy_tx_hash_with_hint(hint: u8): vector { 94 | let tx_hash = vector::empty(); 95 | let i = 0; 96 | while (i < TX_HASH_LENGTH - 1) { 97 | vector::push_back(&mut tx_hash, 0u8); 98 | i = i + 1; 99 | }; 100 | vector::push_back(&mut tx_hash, hint); 101 | tx_hash 102 | } 103 | 104 | #[test_only] 105 | public fun get_ids_created(self: &TxContext): u64 { 106 | ids_created(self) 107 | } 108 | 109 | #[test_only] 110 | /// Return the most recent created object ID. 111 | public fun last_created_object_id(self: &TxContext): address { 112 | let ids_created = self.ids_created; 113 | assert!(ids_created > 0, ENoIDsCreated); 114 | derive_id(*&self.tx_hash, ids_created - 1) 115 | } 116 | 117 | #[test_only] 118 | public fun increment_epoch_number(self: &mut TxContext) { 119 | self.epoch = self.epoch + 1 120 | } 121 | 122 | #[test_only] 123 | /// Test-only function for creating a new signer from `signer_address`. 124 | native fun new_signer_from_address(signer_address: address): signer; 125 | 126 | // Cost calibration functions 127 | #[test_only] 128 | public fun calibrate_derive_id(tx_hash: vector, ids_created: u64) { 129 | derive_id(tx_hash, ids_created); 130 | } 131 | #[test_only] 132 | public fun calibrate_derive_id_nop(_tx_hash: vector, _ids_created: u64) { 133 | }} 134 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/typed_id.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// Typed wrappers around Sui object IDs 5 | /// While not always necessary, this is helpful for indicating the type of an object, particularly 6 | /// when storing its ID in another object. 7 | /// Additionally, it can be helpful for disambiguating between different IDs in an object. 8 | /// For example 9 | /// ``` 10 | /// struct MyObject has key { 11 | /// id: VersionedID, 12 | /// child1: TypedID, 13 | /// child2: TypedID, 14 | /// } 15 | /// ``` 16 | /// We then know that `child1` is an ID for an object of type `A` and that `child2` is an `ID` 17 | /// of an object of type `B` 18 | module sui::typed_id { 19 | use sui::object::{Self, ID}; 20 | 21 | /// An ID of an of type `T`. See `ID` for more details 22 | /// By construction, it is guaranteed that the `ID` represents an object of type `T` 23 | struct TypedID has copy, drop, store { 24 | id: ID, 25 | } 26 | 27 | /// Get the underlying `ID` of `obj`, and remember the type 28 | public fun new(obj: &T): TypedID { 29 | TypedID { id: *object::id(obj) } 30 | } 31 | 32 | /// Borrow the inner `ID` of `typed_id` 33 | public fun as_id(typed_id: &TypedID): &ID { 34 | &typed_id.id 35 | } 36 | 37 | /// Get the inner `ID` of `typed_id` 38 | public fun to_id(typed_id: TypedID): ID { 39 | let TypedID { id } = typed_id; 40 | id 41 | } 42 | 43 | /// Check that underlying `ID` in the `typed_id` equals the objects ID 44 | public fun equals_object(typed_id: &TypedID, obj: &T): bool { 45 | &typed_id.id == object::id(obj) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/url.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// URL: standard Uniform Resource Locator string 5 | /// Url: Sui type which wraps a URL 6 | /// UrlCommitment: Sui type which wraps a Url but also includes an immutable commitment 7 | /// to the hash of the resource at the given URL 8 | module sui::url { 9 | use std::ascii::{Self, String}; 10 | use std::vector; 11 | 12 | /// Length of the vector representing a resource hash 13 | const HASH_VECTOR_LENGTH: u64 = 32; 14 | 15 | /// Error code when the length of the hash vector is not HASH_VECTOR_LENGTH 16 | const EHashLengthMismatch: u64 = 0; 17 | 18 | /// Represents an arbitrary URL. Clients rendering values of this type should fetch the resource at `url` and render it using a to-be-defined Sui standard. 19 | struct Url has store, copy, drop { 20 | // TODO: validate URL format 21 | url: String, 22 | } 23 | 24 | /// Represents an arbitrary URL plus an immutable commitment to the underlying 25 | /// resource hash. Clients rendering values of this type should fetch the resource at `url`, and then compare it against `resource_hash` using a to-be-defined Sui standard, and (if the two match) render the value using the `Url` standard. 26 | struct UrlCommitment has store, copy, drop { 27 | url: Url, 28 | resource_hash: vector, 29 | } 30 | 31 | 32 | // === constructors === 33 | 34 | /// Create a `Url`, with no validation 35 | public fun new_unsafe(url: String): Url { 36 | Url { url } 37 | } 38 | 39 | /// Create a `Url` with no validation from bytes 40 | /// Note: this will abort if `bytes` is not valid ASCII 41 | public fun new_unsafe_from_bytes(bytes: vector): Url { 42 | let url = ascii::string(bytes); 43 | Url { url } 44 | } 45 | 46 | /// Create a `UrlCommitment`, and set the immutable hash 47 | public fun new_unsafe_url_commitment(url: Url, resource_hash: vector): UrlCommitment { 48 | // Length must be exact 49 | assert!(vector::length(&resource_hash) == HASH_VECTOR_LENGTH, EHashLengthMismatch); 50 | 51 | UrlCommitment { url, resource_hash } 52 | } 53 | 54 | 55 | // === `Url` functions === 56 | 57 | /// Get inner URL 58 | public fun inner_url(self: &Url): String{ 59 | self.url 60 | } 61 | 62 | /// Update the inner URL 63 | public fun update(self: &mut Url, url: String) { 64 | self.url = url; 65 | } 66 | 67 | 68 | // === `UrlCommitment` functions === 69 | 70 | /// Get the hash of the resource at the URL 71 | /// We enforce that the hash is immutable 72 | public fun url_commitment_resource_hash(self: &UrlCommitment): vector { 73 | self.resource_hash 74 | } 75 | 76 | /// Get inner URL 77 | public fun url_commitment_inner_url(self: &UrlCommitment): String{ 78 | self.url.url 79 | } 80 | 81 | /// Update the URL, but the hash of the object at the URL must never change 82 | public fun url_commitment_update(self: &mut UrlCommitment, url: String) { 83 | update(&mut self.url, url) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/utf8.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module sui::utf8 { 5 | use std::ascii; 6 | use std::option::Option; 7 | 8 | /// Wrapper type that should be interpreted as a UTF8 string by clients 9 | struct String has store, copy, drop { 10 | bytes: vector 11 | } 12 | 13 | // TODO: also include validating constructor 14 | /// Construct a UTF8 string from `bytes`. Does not 15 | /// perform any validation 16 | public fun string_unsafe(bytes: vector): String { 17 | String { bytes } 18 | } 19 | 20 | /// Construct a UTF8 string from the ASCII string `s` 21 | public fun from_ascii(s: ascii::String): String { 22 | String { bytes: ascii::into_bytes(s) } 23 | } 24 | 25 | /// Try to convert `self` to an ASCCI string 26 | public fun try_into_ascii(self: String): Option { 27 | ascii::try_string(self.bytes) 28 | } 29 | 30 | /// Return the underlying bytes of `self` 31 | public fun bytes(self: &String): &vector { 32 | &self.bytes 33 | } 34 | 35 | /// Consume `self` and return its underlying bytes 36 | public fun into_bytes(self: String): vector { 37 | let String { bytes } = self; 38 | bytes 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/vec_map.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module sui::vec_map { 5 | use std::option::{Self, Option}; 6 | use std::vector; 7 | 8 | /// This key already exists in the map 9 | const EKeyAlreadyExists: u64 = 0; 10 | 11 | /// This key does not exist in the map 12 | const EKeyDoesNotExist: u64 = 1; 13 | 14 | /// Trying to destroy a map that is not empty 15 | const EMapNotEmpty: u64 = 2; 16 | 17 | /// Trying to access an element of the map at an invalid index 18 | const EIndexOutOfBounds: u64 = 3; 19 | 20 | /// A map data structure backed by a vector. The map is guaranteed not to contain duplicate keys, but entries 21 | /// are *not* sorted by key--entries are included in insertion order. 22 | /// All operations are O(N) in the size of the map--the intention of this data structure is only to provide 23 | /// the convenience of programming against a map API. 24 | /// Large maps should use handwritten parent/child relationships instead. 25 | /// Maps that need sorted iteration rather than insertion order iteration should also be handwritten. 26 | struct VecMap has copy, drop, store { 27 | contents: vector>, 28 | } 29 | 30 | /// An entry in the map 31 | struct Entry has copy, drop, store { 32 | key: K, 33 | value: V, 34 | } 35 | 36 | /// Create an empty `VecMap` 37 | public fun empty(): VecMap { 38 | VecMap { contents: vector::empty() } 39 | } 40 | 41 | /// Insert the entry `key` |-> `value` into self. 42 | /// Aborts if `key` is already bound in `self`. 43 | public fun insert(self: &mut VecMap, key: K, value: V) { 44 | assert!(!contains(self, &key), EKeyAlreadyExists); 45 | vector::push_back(&mut self.contents, Entry { key, value }) 46 | } 47 | 48 | /// Remove the entry `key` |-> `value` from self. Aborts if `key` is not bound in `self`. 49 | public fun remove(self: &mut VecMap, key: &K): (K, V) { 50 | let idx = get_idx(self, key); 51 | let Entry { key, value } = vector::remove(&mut self.contents, idx); 52 | (key, value) 53 | } 54 | 55 | /// Get a mutable reference to the value bound to `key` in `self`. 56 | /// Aborts if `key` is not bound in `self`. 57 | public fun get_mut(self: &mut VecMap, key: &K): &mut V { 58 | let idx = get_idx(self, key); 59 | let entry = vector::borrow_mut(&mut self.contents, idx); 60 | &mut entry.value 61 | } 62 | 63 | /// Get a reference to the value bound to `key` in `self`. 64 | /// Aborts if `key` is not bound in `self`. 65 | public fun get(self: &VecMap, key: &K): &V { 66 | let idx = get_idx(self, key); 67 | let entry = vector::borrow(&self.contents, idx); 68 | &entry.value 69 | } 70 | 71 | /// Return true if `self` contains an entry for `key`, false otherwise 72 | public fun contains(self: &VecMap, key: &K): bool { 73 | option::is_some(&get_idx_opt(self, key)) 74 | } 75 | 76 | /// Return the number of entries in `self` 77 | public fun size(self: &VecMap): u64 { 78 | vector::length(&self.contents) 79 | } 80 | 81 | /// Return true if `self` has 0 elements, false otherwise 82 | public fun is_empty(self: &VecMap): bool { 83 | size(self) == 0 84 | } 85 | 86 | /// Destroy an empty map. Aborts if `self` is not empty 87 | public fun destroy_empty(self: VecMap) { 88 | let VecMap { contents } = self; 89 | assert!(vector::is_empty(&contents), EMapNotEmpty); 90 | vector::destroy_empty(contents) 91 | } 92 | 93 | /// Unpack `self` into vectors of its keys and values. 94 | /// The output keys and values are stored in insertion order, *not* sorted by key. 95 | public fun into_keys_values(self: VecMap): (vector, vector) { 96 | let VecMap { contents } = self; 97 | // reverse the vector so the output keys and values will appear in insertion order 98 | vector::reverse(&mut contents); 99 | let i = 0; 100 | let n = vector::length(&contents); 101 | let keys = vector::empty(); 102 | let values = vector::empty(); 103 | while (i < n) { 104 | let Entry { key, value } = vector::pop_back(&mut contents); 105 | vector::push_back(&mut keys, key); 106 | vector::push_back(&mut values, value); 107 | i = i + 1; 108 | }; 109 | vector::destroy_empty(contents); 110 | (keys, values) 111 | } 112 | 113 | /// Find the index of `key` in `self. Return `None` if `key` is not in `self`. 114 | /// Note that map entries are stored in insertion order, *not* sorted by key. 115 | public fun get_idx_opt(self: &VecMap, key: &K): Option { 116 | let i = 0; 117 | let n = size(self); 118 | while (i < n) { 119 | if (&vector::borrow(&self.contents, i).key == key) { 120 | return option::some(i) 121 | }; 122 | i = i + 1; 123 | }; 124 | option::none() 125 | } 126 | 127 | /// Find the index of `key` in `self. Aborts if `key` is not in `self`. 128 | /// Note that map entries are stored in insertion order, *not* sorted by key. 129 | public fun get_idx(self: &VecMap, key: &K): u64 { 130 | let idx_opt = get_idx_opt(self, key); 131 | assert!(option::is_some(&idx_opt), EKeyDoesNotExist); 132 | option::destroy_some(idx_opt) 133 | } 134 | 135 | /// Return a reference to the `idx`th entry of `self`. This gives direct access into the backing array of the map--use with caution. 136 | /// Note that map entries are stored in insertion order, *not* sorted by key. 137 | /// Aborts if `idx` is greater than or equal to `size(self)` 138 | public fun get_entry_by_idx(self: &VecMap, idx: u64): (&K, &V) { 139 | assert!(idx < size(self), EIndexOutOfBounds); 140 | let entry = vector::borrow(&self.contents, idx); 141 | (&entry.key, &entry.value) 142 | } 143 | 144 | /// Return a mutable reference to the `idx`th entry of `self`. This gives direct access into the backing array of the map--use with caution. 145 | /// Note that map entries are stored in insertion order, *not* sorted by key. 146 | /// Aborts if `idx` is greater than or equal to `size(self)` 147 | public fun get_entry_by_idx_mut(self: &mut VecMap, idx: u64): (&K, &mut V) { 148 | assert!(idx < size(self), EIndexOutOfBounds); 149 | let entry = vector::borrow_mut(&mut self.contents, idx); 150 | (&entry.key, &mut entry.value) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/sui-framework/sources/vec_set.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | module sui::vec_set { 5 | use std::option::{Self, Option}; 6 | use std::vector; 7 | 8 | /// This key already exists in the map 9 | const EKeyAlreadyExists: u64 = 0; 10 | 11 | /// This key does not exist in the map 12 | const EKeyDoesNotExist: u64 = 1; 13 | 14 | /// A set data structure backed by a vector. The set is guaranteed not to contain duplicate keys. 15 | /// All operations are O(N) in the size of the set--the intention of this data structure is only to provide 16 | /// the convenience of programming against a set API. 17 | /// Sets that need sorted iteration rather than insertion order iteration should be handwritten. 18 | struct VecSet has copy, drop, store { 19 | contents: vector, 20 | } 21 | 22 | /// Create an empty `VecSet` 23 | public fun empty(): VecSet { 24 | VecSet { contents: vector::empty() } 25 | } 26 | 27 | /// Insert a `key` into self. 28 | /// Aborts if `key` is already present in `self`. 29 | public fun insert(self: &mut VecSet, key: K) { 30 | assert!(!contains(self, &key), EKeyAlreadyExists); 31 | vector::push_back(&mut self.contents, key) 32 | } 33 | 34 | /// Remove the entry `key` from self. Aborts if `key` is not present in `self`. 35 | public fun remove(self: &mut VecSet, key: &K) { 36 | let idx = get_idx(self, key); 37 | vector::remove(&mut self.contents, idx); 38 | } 39 | 40 | /// Return true if `self` contains an entry for `key`, false otherwise 41 | public fun contains(self: &VecSet, key: &K): bool { 42 | option::is_some(&get_idx_opt(self, key)) 43 | } 44 | 45 | /// Return the number of entries in `self` 46 | public fun size(self: &VecSet): u64 { 47 | vector::length(&self.contents) 48 | } 49 | 50 | /// Return true if `self` has 0 elements, false otherwise 51 | public fun is_empty(self: &VecSet): bool { 52 | size(self) == 0 53 | } 54 | 55 | /// Unpack `self` into vectors of keys. 56 | /// The output keys are stored in insertion order, *not* sorted. 57 | public fun into_keys(self: VecSet): vector { 58 | let VecSet { contents } = self; 59 | contents 60 | } 61 | 62 | // == Helper functions == 63 | 64 | /// Find the index of `key` in `self. Return `None` if `key` is not in `self`. 65 | /// Note that keys are stored in insertion order, *not* sorted. 66 | fun get_idx_opt(self: &VecSet, key: &K): Option { 67 | let i = 0; 68 | let n = size(self); 69 | while (i < n) { 70 | if (vector::borrow(&self.contents, i) == key) { 71 | return option::some(i) 72 | }; 73 | i = i + 1; 74 | }; 75 | option::none() 76 | } 77 | 78 | /// Find the index of `key` in `self. Aborts if `key` is not in `self`. 79 | /// Note that map entries are stored in insertion order, *not* sorted. 80 | fun get_idx(self: &VecSet, key: &K): u64 { 81 | let idx_opt = get_idx_opt(self, key); 82 | assert!(option::is_some(&idx_opt), EKeyDoesNotExist); 83 | option::destroy_some(idx_opt) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/sui-framework/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use move_binary_format::CompiledModule; 5 | use move_cli::base::test::UnitTestResult; 6 | use move_package::BuildConfig; 7 | use move_unit_test::UnitTestingConfig; 8 | use num_enum::TryFromPrimitive; 9 | use once_cell::sync::Lazy; 10 | use std::path::Path; 11 | use sui_types::error::{SuiError, SuiResult}; 12 | 13 | pub mod natives; 14 | 15 | pub use sui_framework_build::build_move_stdlib_modules as get_move_stdlib_modules; 16 | pub use sui_framework_build::{build_move_package, verify_modules}; 17 | use sui_types::sui_serde::{Base64, Encoding}; 18 | 19 | // Move unit tests will halt after executing this many steps. This is a protection to avoid divergence 20 | const MAX_UNIT_TEST_INSTRUCTIONS: u64 = 100_000; 21 | 22 | static SUI_FRAMEWORK: Lazy> = Lazy::new(|| { 23 | const SUI_FRAMEWORK_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/sui-framework")); 24 | 25 | let serialized_modules: Vec> = bcs::from_bytes(SUI_FRAMEWORK_BYTES).unwrap(); 26 | 27 | serialized_modules 28 | .into_iter() 29 | .map(|module| CompiledModule::deserialize(&module).unwrap()) 30 | .collect() 31 | }); 32 | 33 | static MOVE_STDLIB: Lazy> = Lazy::new(|| { 34 | const MOVE_STDLIB_BYTES: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/move-stdlib")); 35 | 36 | let serialized_modules: Vec> = bcs::from_bytes(MOVE_STDLIB_BYTES).unwrap(); 37 | 38 | serialized_modules 39 | .into_iter() 40 | .map(|module| CompiledModule::deserialize(&module).unwrap()) 41 | .collect() 42 | }); 43 | 44 | pub fn get_sui_framework() -> Vec { 45 | Lazy::force(&SUI_FRAMEWORK).to_owned() 46 | } 47 | 48 | pub fn get_move_stdlib() -> Vec { 49 | Lazy::force(&MOVE_STDLIB).to_owned() 50 | } 51 | 52 | pub const DEFAULT_FRAMEWORK_PATH: &str = env!("CARGO_MANIFEST_DIR"); 53 | 54 | #[derive(TryFromPrimitive, PartialEq, Eq)] 55 | #[repr(u8)] 56 | pub enum EventType { 57 | /// System event: transfer between addresses 58 | TransferToAddress, 59 | /// System event: transfer object to another object 60 | TransferToObject, 61 | /// System event: freeze object 62 | FreezeObject, 63 | /// System event: turn an object into a shared object 64 | ShareObject, 65 | /// System event: an object ID is deleted. This does not necessarily 66 | /// mean an object is being deleted. However whenever an object is being 67 | /// deleted, the object ID must be deleted and this event will be 68 | /// emitted. 69 | DeleteObjectID, 70 | /// User-defined event 71 | User, 72 | } 73 | 74 | /// Given a `path` and a `build_config`, build the package in that path and return the compiled modules as base64. 75 | /// This is useful for when publishing via JSON 76 | pub fn build_move_package_to_base64( 77 | path: &Path, 78 | build_config: BuildConfig, 79 | ) -> Result, SuiError> { 80 | build_move_package_to_bytes(path, build_config) 81 | .map(|mods| mods.iter().map(Base64::encode).collect::>()) 82 | } 83 | 84 | /// Given a `path` and a `build_config`, build the package in that path and return the compiled modules as Vec>. 85 | /// This is useful for when publishing 86 | pub fn build_move_package_to_bytes( 87 | path: &Path, 88 | build_config: BuildConfig, 89 | ) -> Result>, SuiError> { 90 | build_move_package(path, build_config).map(|mods| { 91 | mods.iter() 92 | .map(|m| { 93 | let mut bytes = Vec::new(); 94 | m.serialize(&mut bytes).unwrap(); 95 | bytes 96 | }) 97 | .collect::>() 98 | }) 99 | } 100 | 101 | pub fn build_and_verify_package( 102 | path: &Path, 103 | build_config: BuildConfig, 104 | ) -> SuiResult> { 105 | let modules = build_move_package(path, build_config)?; 106 | verify_modules(&modules)?; 107 | Ok(modules) 108 | } 109 | 110 | pub fn run_move_unit_tests( 111 | path: &Path, 112 | build_config: BuildConfig, 113 | config: Option, 114 | compute_coverage: bool, 115 | ) -> anyhow::Result { 116 | use sui_types::{MOVE_STDLIB_ADDRESS, SUI_FRAMEWORK_ADDRESS}; 117 | 118 | let config = config 119 | .unwrap_or_else(|| UnitTestingConfig::default_with_bound(Some(MAX_UNIT_TEST_INSTRUCTIONS))); 120 | 121 | move_cli::base::test::run_move_unit_tests( 122 | path, 123 | build_config, 124 | UnitTestingConfig { 125 | report_stacktrace_on_abort: true, 126 | ..config 127 | }, 128 | natives::all_natives(MOVE_STDLIB_ADDRESS, SUI_FRAMEWORK_ADDRESS), 129 | compute_coverage, 130 | &mut std::io::stdout(), 131 | ) 132 | } 133 | 134 | #[cfg(test)] 135 | mod tests { 136 | use super::*; 137 | use std::path::PathBuf; 138 | 139 | #[test] 140 | fn run_framework_move_unit_tests() { 141 | get_sui_framework(); 142 | get_move_stdlib(); 143 | build_move_package( 144 | &PathBuf::from(DEFAULT_FRAMEWORK_PATH), 145 | BuildConfig::default(), 146 | ) 147 | .unwrap(); 148 | run_move_unit_tests( 149 | Path::new(env!("CARGO_MANIFEST_DIR")), 150 | BuildConfig::default(), 151 | None, 152 | false, 153 | ) 154 | .unwrap(); 155 | } 156 | 157 | #[test] 158 | fn run_examples_move_unit_tests() { 159 | let examples = vec![ 160 | "basics", 161 | "defi", 162 | "fungible_tokens", 163 | "games", 164 | "move_tutorial", 165 | "nfts", 166 | "objects_tutorial", 167 | ]; 168 | for example in examples { 169 | let path = Path::new(env!("CARGO_MANIFEST_DIR")) 170 | .join("../../sui_programmability/examples") 171 | .join(example); 172 | build_and_verify_package(&path, BuildConfig::default()).unwrap(); 173 | run_move_unit_tests(&path, BuildConfig::default(), None, false).unwrap(); 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /lib/sui-framework/src/natives/event.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use crate::EventType; 5 | use move_binary_format::errors::{PartialVMError, PartialVMResult}; 6 | use move_core_types::{gas_schedule::GasAlgebra, vm_status::StatusCode}; 7 | use move_vm_runtime::native_functions::NativeContext; 8 | use move_vm_types::{ 9 | gas_schedule::NativeCostIndex, 10 | loaded_data::runtime_types::Type, 11 | natives::function::{native_gas, NativeResult}, 12 | values::Value, 13 | }; 14 | use smallvec::smallvec; 15 | use std::collections::VecDeque; 16 | 17 | /// Implementation of Move native function `event::emit(event: T)` 18 | /// Adds an event to the transaction's event log 19 | pub fn emit( 20 | context: &mut NativeContext, 21 | mut ty_args: Vec, 22 | mut args: VecDeque, 23 | ) -> PartialVMResult { 24 | debug_assert!(ty_args.len() == 1); 25 | debug_assert!(args.len() == 1); 26 | 27 | let ty = ty_args.pop().unwrap(); 28 | let event = args.pop_back().unwrap(); 29 | 30 | // gas cost is proportional to size of event 31 | let event_size = event.size(); 32 | let cost = native_gas(context.cost_table(), NativeCostIndex::EMIT_EVENT, 1).add(event_size); 33 | match ty { 34 | Type::Struct(..) | Type::StructInstantiation(..) => (), 35 | ty => { 36 | // TODO (https://github.com/MystenLabs/sui/issues/19): ideally enforce this in the ability system 37 | return Err(PartialVMError::new(StatusCode::DATA_FORMAT_ERROR) 38 | .with_message(format!("Unsupported event type {:?} (struct expected)", ty))); 39 | } 40 | } 41 | 42 | if !context.save_event(Vec::new(), EventType::User as u64, ty, event)? { 43 | return Ok(NativeResult::err(cost, 0)); 44 | } 45 | 46 | Ok(NativeResult::ok(cost, smallvec![])) 47 | } 48 | -------------------------------------------------------------------------------- /lib/sui-framework/src/natives/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | mod event; 5 | mod object; 6 | mod test_scenario; 7 | mod transfer; 8 | mod tx_context; 9 | 10 | use move_binary_format::errors::PartialVMError; 11 | use move_core_types::{account_address::AccountAddress, identifier::Identifier}; 12 | use move_vm_runtime::native_functions::{NativeFunction, NativeFunctionTable}; 13 | use move_vm_types::values::{Struct, Value}; 14 | 15 | pub fn all_natives( 16 | move_stdlib_addr: AccountAddress, 17 | sui_framework_addr: AccountAddress, 18 | ) -> NativeFunctionTable { 19 | const SUI_NATIVES: &[(&str, &str, NativeFunction)] = &[ 20 | ("event", "emit", event::emit), 21 | ("object", "bytes_to_address", object::bytes_to_address), 22 | ("object", "delete_impl", object::delete_impl), 23 | ("object", "get_info", object::get_info), 24 | ( 25 | "test_scenario", 26 | "drop_object_for_testing", 27 | test_scenario::drop_object_for_testing, 28 | ), 29 | ( 30 | "test_scenario", 31 | "emit_wrapped_object_events", 32 | test_scenario::emit_wrapped_object_events, 33 | ), 34 | ( 35 | "test_scenario", 36 | "get_account_owned_inventory", 37 | test_scenario::get_account_owned_inventory, 38 | ), 39 | ( 40 | "test_scenario", 41 | "get_object_owned_inventory", 42 | test_scenario::get_object_owned_inventory, 43 | ), 44 | ( 45 | "test_scenario", 46 | "get_unowned_inventory", 47 | test_scenario::get_unowned_inventory, 48 | ), 49 | ("test_scenario", "num_events", test_scenario::num_events), 50 | ( 51 | "test_scenario", 52 | "update_object", 53 | test_scenario::update_object, 54 | ), 55 | ("transfer", "transfer_internal", transfer::transfer_internal), 56 | ("transfer", "freeze_object", transfer::freeze_object), 57 | ("transfer", "share_object", transfer::share_object), 58 | ("tx_context", "derive_id", tx_context::derive_id), 59 | ( 60 | "tx_context", 61 | "new_signer_from_address", 62 | tx_context::new_signer_from_address, 63 | ), 64 | ]; 65 | SUI_NATIVES 66 | .iter() 67 | .cloned() 68 | .map(|(module_name, func_name, func)| { 69 | ( 70 | sui_framework_addr, 71 | Identifier::new(module_name).unwrap(), 72 | Identifier::new(func_name).unwrap(), 73 | func, 74 | ) 75 | }) 76 | .chain(move_stdlib::natives::all_natives(move_stdlib_addr)) 77 | .collect() 78 | } 79 | 80 | // Object { info: Info { id: ID { bytes: address } } .. } 81 | // Extract the first field of the struct 3 times to get the id bytes. 82 | pub fn get_object_id(object: Value) -> Result { 83 | get_nested_struct_field(object, &[0, 0, 0]) 84 | } 85 | 86 | // Extract a field valye that's nested inside value `v`. The offset of each nesting 87 | // is determined by `offsets`. 88 | pub fn get_nested_struct_field(mut v: Value, offsets: &[usize]) -> Result { 89 | for offset in offsets { 90 | v = get_nth_struct_field(v, *offset)?; 91 | } 92 | Ok(v) 93 | } 94 | 95 | pub fn get_nth_struct_field(v: Value, n: usize) -> Result { 96 | let mut itr = v.value_as::()?.unpack()?; 97 | Ok(itr.nth(n).unwrap()) 98 | } 99 | -------------------------------------------------------------------------------- /lib/sui-framework/src/natives/object.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use crate::EventType; 5 | use move_binary_format::errors::PartialVMResult; 6 | use move_core_types::account_address::AccountAddress; 7 | use move_vm_runtime::native_functions::NativeContext; 8 | use move_vm_types::{ 9 | gas_schedule::NativeCostIndex, 10 | loaded_data::runtime_types::Type, 11 | natives::function::{native_gas, NativeResult}, 12 | pop_arg, 13 | values::{StructRef, Value}, 14 | }; 15 | use smallvec::smallvec; 16 | use std::collections::VecDeque; 17 | 18 | pub fn bytes_to_address( 19 | context: &mut NativeContext, 20 | ty_args: Vec, 21 | mut args: VecDeque, 22 | ) -> PartialVMResult { 23 | debug_assert!(ty_args.is_empty()); 24 | debug_assert!(args.len() == 1); 25 | 26 | let addr_bytes = pop_arg!(args, Vec); 27 | // unwrap safe because this native function is only called from new_from_bytes, 28 | // which already asserts the size of bytes to be equal of account address. 29 | let addr = AccountAddress::from_bytes(addr_bytes).unwrap(); 30 | 31 | // TODO: what should the cost of this be? 32 | let cost = native_gas(context.cost_table(), NativeCostIndex::CREATE_SIGNER, 0); 33 | 34 | Ok(NativeResult::ok(cost, smallvec![Value::address(addr)])) 35 | } 36 | 37 | pub fn get_info( 38 | context: &mut NativeContext, 39 | ty_args: Vec, 40 | mut args: VecDeque, 41 | ) -> PartialVMResult { 42 | debug_assert!(ty_args.len() == 1); 43 | debug_assert!(args.len() == 1); 44 | 45 | let obj = pop_arg!(args, StructRef); 46 | let info_field = obj.borrow_field(0)?; 47 | 48 | // TODO: what should the cost of this be? 49 | let cost = native_gas(context.cost_table(), NativeCostIndex::SIGNER_BORROW, 0); 50 | 51 | Ok(NativeResult::ok(cost, smallvec![info_field])) 52 | } 53 | 54 | pub fn delete_impl( 55 | context: &mut NativeContext, 56 | mut ty_args: Vec, 57 | mut args: VecDeque, 58 | ) -> PartialVMResult { 59 | debug_assert!(ty_args.len() == 1); 60 | debug_assert!(args.len() == 1); 61 | 62 | // unwrap safe because the interface of native function guarantees it. 63 | let ty = ty_args.pop().unwrap(); 64 | let info = args.pop_back().unwrap(); 65 | 66 | // TODO: what should the cost of this be? 67 | let cost = native_gas(context.cost_table(), NativeCostIndex::EMIT_EVENT, 0); 68 | 69 | if !context.save_event(vec![], EventType::DeleteObjectID as u64, ty, info)? { 70 | return Ok(NativeResult::err(cost, 0)); 71 | } 72 | 73 | Ok(NativeResult::ok(cost, smallvec![])) 74 | } 75 | -------------------------------------------------------------------------------- /lib/sui-framework/src/natives/transfer.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use crate::EventType; 5 | use move_binary_format::errors::PartialVMResult; 6 | use move_core_types::account_address::AccountAddress; 7 | use move_vm_runtime::native_functions::NativeContext; 8 | use move_vm_types::{ 9 | gas_schedule::NativeCostIndex, 10 | loaded_data::runtime_types::Type, 11 | natives::function::{native_gas, NativeResult}, 12 | pop_arg, 13 | values::Value, 14 | }; 15 | use smallvec::smallvec; 16 | use std::collections::VecDeque; 17 | 18 | /// Implementation of Move native function 19 | /// `transfer_internal(obj: T, recipient: vector, to_object: bool)` 20 | /// Here, we simply emit this event. The sui adapter 21 | /// treats this as a special event that is handled 22 | /// differently from user events: 23 | /// the adapter will change the owner of the object 24 | /// in question to `recipient`. 25 | pub fn transfer_internal( 26 | context: &mut NativeContext, 27 | mut ty_args: Vec, 28 | mut args: VecDeque, 29 | ) -> PartialVMResult { 30 | debug_assert!(ty_args.len() == 1); 31 | debug_assert!(args.len() == 3); 32 | 33 | let ty = ty_args.pop().unwrap(); 34 | let to_object = pop_arg!(args, bool); 35 | let recipient = pop_arg!(args, AccountAddress); 36 | let transferred_obj = args.pop_back().unwrap(); 37 | let event_type = if to_object { 38 | EventType::TransferToObject 39 | } else { 40 | EventType::TransferToAddress 41 | }; 42 | // Charge a constant native gas cost here, since 43 | // we will charge it properly when processing 44 | // all the events in adapter. 45 | // TODO: adjust native_gas cost size base. 46 | let cost = native_gas(context.cost_table(), NativeCostIndex::EMIT_EVENT, 1); 47 | if context.save_event(recipient.to_vec(), event_type as u64, ty, transferred_obj)? { 48 | Ok(NativeResult::ok(cost, smallvec![])) 49 | } else { 50 | Ok(NativeResult::err(cost, 0)) 51 | } 52 | } 53 | 54 | /// Implementation of Move native function 55 | /// `freeze_object(obj: T)` 56 | pub fn freeze_object( 57 | context: &mut NativeContext, 58 | mut ty_args: Vec, 59 | mut args: VecDeque, 60 | ) -> PartialVMResult { 61 | debug_assert!(ty_args.len() == 1); 62 | debug_assert!(args.len() == 1); 63 | 64 | let ty = ty_args.pop().unwrap(); 65 | let obj = args.pop_back().unwrap(); 66 | let event_type = EventType::FreezeObject; 67 | let cost = native_gas(context.cost_table(), NativeCostIndex::EMIT_EVENT, 1); 68 | if context.save_event(vec![], event_type as u64, ty, obj)? { 69 | Ok(NativeResult::ok(cost, smallvec![])) 70 | } else { 71 | Ok(NativeResult::err(cost, 0)) 72 | } 73 | } 74 | 75 | /// Implementation of Move native function 76 | /// `share_object(obj: T)` 77 | pub fn share_object( 78 | context: &mut NativeContext, 79 | mut ty_args: Vec, 80 | mut args: VecDeque, 81 | ) -> PartialVMResult { 82 | debug_assert!(ty_args.len() == 1); 83 | debug_assert!(args.len() == 1); 84 | 85 | let ty = ty_args.pop().unwrap(); 86 | let obj = args.pop_back().unwrap(); 87 | let event_type = EventType::ShareObject; 88 | let cost = native_gas(context.cost_table(), NativeCostIndex::EMIT_EVENT, 1); 89 | if context.save_event(vec![], event_type as u64, ty, obj)? { 90 | Ok(NativeResult::ok(cost, smallvec![])) 91 | } else { 92 | Ok(NativeResult::err(cost, 0)) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /lib/sui-framework/src/natives/tx_context.rs: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | use move_binary_format::errors::PartialVMResult; 5 | use move_core_types::account_address::AccountAddress; 6 | use move_vm_runtime::native_functions::NativeContext; 7 | use move_vm_types::{ 8 | gas_schedule::NativeCostIndex, 9 | loaded_data::runtime_types::Type, 10 | natives::function::{native_gas, NativeResult}, 11 | pop_arg, 12 | values::Value, 13 | }; 14 | use smallvec::smallvec; 15 | use std::{collections::VecDeque, convert::TryFrom}; 16 | use sui_types::base_types::TransactionDigest; 17 | 18 | pub fn derive_id( 19 | context: &mut NativeContext, 20 | ty_args: Vec, 21 | mut args: VecDeque, 22 | ) -> PartialVMResult { 23 | debug_assert!(ty_args.is_empty()); 24 | debug_assert!(args.len() == 2); 25 | 26 | let ids_created = pop_arg!(args, u64); 27 | let tx_hash = pop_arg!(args, Vec); 28 | 29 | // TODO(https://github.com/MystenLabs/sui/issues/58): finalize digest format 30 | // unwrap safe because all digests in Move are serialized from the Rust `TransactionDigest` 31 | let digest = TransactionDigest::try_from(tx_hash.as_slice()).unwrap(); 32 | let id = Value::address(AccountAddress::from(digest.derive_id(ids_created))); 33 | 34 | // TODO: choose cost 35 | let cost = native_gas(context.cost_table(), NativeCostIndex::CREATE_SIGNER, 0); 36 | 37 | Ok(NativeResult::ok(cost, smallvec![id])) 38 | } 39 | 40 | /// Create a new signer (for test only) from an address. 41 | pub fn new_signer_from_address( 42 | context: &mut NativeContext, 43 | ty_args: Vec, 44 | mut args: VecDeque, 45 | ) -> PartialVMResult { 46 | debug_assert!(ty_args.is_empty()); 47 | debug_assert_eq!(args.len(), 1); 48 | 49 | let address = pop_arg!(args, AccountAddress); 50 | let signer = Value::signer(address); 51 | 52 | // Gas amount doesn't matter as this is test only. 53 | let cost = native_gas(context.cost_table(), NativeCostIndex::EMIT_EVENT, 0); 54 | Ok(NativeResult::ok(cost, smallvec![signer])) 55 | } 56 | -------------------------------------------------------------------------------- /lib/sui-framework/tests/bag_tests.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[test_only] 5 | module sui::bag_tests { 6 | use sui::bag::{Self, Bag}; 7 | use sui::object::{Self, Info}; 8 | use sui::test_scenario; 9 | use sui::typed_id; 10 | use sui::tx_context; 11 | 12 | const EBAG_SIZE_MISMATCH: u64 = 0; 13 | const EOBJECT_NOT_FOUND: u64 = 1; 14 | 15 | struct Object1 has key, store { 16 | info: Info, 17 | } 18 | 19 | struct Object2 has key, store { 20 | info: Info, 21 | } 22 | 23 | #[test] 24 | fun test_bag() { 25 | let sender = @0x0; 26 | let scenario = &mut test_scenario::begin(&sender); 27 | 28 | // Create a new Bag and transfer it to the sender. 29 | test_scenario::next_tx(scenario, &sender); 30 | { 31 | bag::create(test_scenario::ctx(scenario)); 32 | }; 33 | 34 | // Add two objects of different types into the bag. 35 | test_scenario::next_tx(scenario, &sender); 36 | { 37 | let bag = test_scenario::take_owned(scenario); 38 | assert!(bag::size(&bag) == 0, EBAG_SIZE_MISMATCH); 39 | 40 | let obj1 = Object1 { info: object::new(test_scenario::ctx(scenario)) }; 41 | let obj2 = Object2 { info: object::new(test_scenario::ctx(scenario)) }; 42 | 43 | let item_id1 = bag::add(&mut bag, obj1, test_scenario::ctx(scenario)); 44 | let item_id2 = bag::add(&mut bag, obj2, test_scenario::ctx(scenario)); 45 | assert!(bag::size(&bag) == 2, EBAG_SIZE_MISMATCH); 46 | 47 | assert!(bag::contains(&bag, typed_id::as_id(&item_id1)), EOBJECT_NOT_FOUND); 48 | assert!(bag::contains(&bag, typed_id::as_id(&item_id2)), EOBJECT_NOT_FOUND); 49 | 50 | test_scenario::return_owned(scenario, bag); 51 | }; 52 | // TODO: Test object removal once we can retrieve object owned objects from test_scenario. 53 | } 54 | 55 | #[test] 56 | #[expected_failure(abort_code = 1)] 57 | fun test_init_with_invalid_max_capacity() { 58 | let ctx = tx_context::dummy(); 59 | // Sui::bag::DEFAULT_MAX_CAPACITY is not readable outside the module 60 | let max_capacity = 65536; 61 | let bag = bag::new_with_max_capacity(&mut ctx, max_capacity + 1); 62 | bag::transfer(bag, tx_context::sender(&ctx)); 63 | } 64 | 65 | #[test] 66 | #[expected_failure(abort_code = 1)] 67 | fun test_init_with_zero() { 68 | let ctx = tx_context::dummy(); 69 | let bag = bag::new_with_max_capacity(&mut ctx, 0); 70 | bag::transfer(bag, tx_context::sender(&ctx)); 71 | } 72 | 73 | #[test] 74 | #[expected_failure(abort_code = 2)] 75 | fun test_exceed_max_capacity() { 76 | let ctx = tx_context::dummy(); 77 | let bag = bag::new_with_max_capacity(&mut ctx, 1); 78 | 79 | let obj1 = Object1 { info: object::new(&mut ctx) }; 80 | bag::add(&mut bag, obj1, &mut ctx); 81 | let obj2 = Object2 { info: object::new(&mut ctx) }; 82 | bag::add(&mut bag, obj2, &mut ctx); 83 | bag::transfer(bag, tx_context::sender(&ctx)); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /lib/sui-framework/tests/bytecode_calibration_tests.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | // This module attemps to find the computational cost of bytecode instructions by measuring the time 6 | // the instruction takes to execute. 7 | // Isolating the bytecode is tricky, so we run two functions with and without the bytecode instruction 8 | // The difference in execution times is the time the instruction takes 9 | // functions prefixed with __baseline do not have the bytecode under yesy 10 | // Many parts of the code are written in such a way that the bytecode diffs yield exactly the 11 | // instruction/operation to be isolated 12 | 13 | 14 | #[test_only] 15 | module sui::BytecodeCalibrationTests { 16 | 17 | // Number of times to run the inner loop of tests 18 | // We set this value to 1 to avoid long running tests 19 | // But normally we want something like 1000000 20 | const NUM_TRIALS: u64 = 1; 21 | const U64_MAX: u64 = 18446744073709551615; 22 | 23 | // Add operation 24 | #[test] 25 | public entry fun test_calibrate_add() { 26 | let trials: u64 = NUM_TRIALS; 27 | let num: u64 = 0; 28 | while (trials > 0) { 29 | num = num + 1 + 1; 30 | trials = trials - 1; 31 | } 32 | } 33 | #[test] 34 | public entry fun test_calibrate_add__baseline() { 35 | let trials: u64 = NUM_TRIALS; 36 | let num: u64 = 0; 37 | while (trials > 0) { 38 | num = num + 1; 39 | trials = trials - 1; 40 | } 41 | } 42 | 43 | // Sub operation 44 | #[test] 45 | public entry fun test_calibrate_sub() { 46 | let trials: u64 = NUM_TRIALS; 47 | let num: u64 = U64_MAX; 48 | while (trials > 0) { 49 | num = num - 1 - 1; 50 | trials = trials - 1; 51 | } 52 | } 53 | #[test] 54 | public entry fun test_calibrate_sub__baseline() { 55 | let trials: u64 = NUM_TRIALS; 56 | let num: u64 = U64_MAX; 57 | while (trials > 0) { 58 | num = num - 1; 59 | trials = trials - 1; 60 | } 61 | } 62 | 63 | // Mul operation 64 | #[test] 65 | public entry fun test_calibrate_mul() { 66 | let trials: u64 = NUM_TRIALS; 67 | let num: u64 = 0; 68 | while (trials > 0) { 69 | num = num * 1 * 1; 70 | trials = trials - 1; 71 | } 72 | } 73 | #[test] 74 | public entry fun test_calibrate_mul__baseline() { 75 | let trials: u64 = NUM_TRIALS; 76 | let num: u64 = 0; 77 | while (trials > 0) { 78 | num = num * 1; 79 | trials = trials - 1; 80 | } 81 | } 82 | 83 | // Div operation 84 | #[test] 85 | public entry fun test_calibrate_div() { 86 | let trials: u64 = NUM_TRIALS; 87 | let num: u64 = U64_MAX; 88 | while (trials > 0) { 89 | num = num / 1 / 1; 90 | trials = trials - 1; 91 | } 92 | } 93 | #[test] 94 | public entry fun test_calibrate_div__baseline() { 95 | let trials: u64 = NUM_TRIALS; 96 | let num: u64 = U64_MAX; 97 | while (trials > 0) { 98 | num = num / 1; 99 | trials = trials - 1; 100 | } 101 | } 102 | 103 | // Mod operation 104 | #[test] 105 | public entry fun test_calibrate_mod() { 106 | let trials: u64 = NUM_TRIALS; 107 | let num: u64 = 0; 108 | while (trials > 0) { 109 | num = num % 1 % 1; 110 | trials = trials - 1; 111 | } 112 | } 113 | #[test] 114 | public entry fun test_calibrate_mod__baseline() { 115 | let trials: u64 = NUM_TRIALS; 116 | let num: u64 = 0; 117 | while (trials > 0) { 118 | num = num % 1; 119 | trials = trials - 1; 120 | } 121 | } 122 | 123 | // Logical And operation 124 | #[test] 125 | public entry fun test_calibrate_and() { 126 | let trials: u64 = NUM_TRIALS; 127 | let flag: bool = false; 128 | while (trials > 0) { 129 | flag = flag && false && false; 130 | trials = trials - 1; 131 | } 132 | } 133 | #[test] 134 | public entry fun test_calibrate_and__baseline() { 135 | let trials: u64 = NUM_TRIALS; 136 | let flag: bool = false; 137 | while (trials > 0) { 138 | flag = flag && false; 139 | trials = trials - 1; 140 | } 141 | } 142 | 143 | // Logical Or operation 144 | #[test] 145 | public entry fun test_calibrate_or() { 146 | let trials: u64 = NUM_TRIALS; 147 | let flag: bool = false; 148 | while (trials > 0) { 149 | flag = flag || false || false; 150 | trials = trials - 1; 151 | } 152 | } 153 | #[test] 154 | public entry fun test_calibrate_or__baseline() { 155 | let trials: u64 = NUM_TRIALS; 156 | let flag: bool = false; 157 | while (trials > 0) { 158 | flag = flag || false; 159 | trials = trials - 1; 160 | } 161 | } 162 | 163 | // Xor operation 164 | #[test] 165 | public entry fun test_calibrate_xor() { 166 | let trials: u64 = NUM_TRIALS; 167 | let num: u64 = U64_MAX; 168 | while (trials > 0) { 169 | num = num ^ 1 ^ 1; 170 | trials = trials - 1; 171 | } 172 | } 173 | #[test] 174 | public entry fun test_calibrate_xor__baseline() { 175 | let trials: u64 = NUM_TRIALS; 176 | let num: u64 = U64_MAX; 177 | while (trials > 0) { 178 | num = num ^ 1; 179 | trials = trials - 1; 180 | } 181 | } 182 | 183 | // Shift Right operation 184 | #[test] 185 | public entry fun test_calibrate_shr() { 186 | let trials: u64 = NUM_TRIALS; 187 | let num: u64 = U64_MAX; 188 | while (trials > 0) { 189 | num = num >> 1 >> 1; 190 | trials = trials - 1; 191 | } 192 | } 193 | #[test] 194 | public entry fun test_calibrate_shr__baseline() { 195 | let trials: u64 = NUM_TRIALS; 196 | let num: u64 = U64_MAX; 197 | while (trials > 0) { 198 | num = num >> 1; 199 | trials = trials - 1; 200 | } 201 | } 202 | 203 | // Shift num operation 204 | #[test] 205 | public entry fun test_calibrate_shl() { 206 | let trials: u64 = NUM_TRIALS; 207 | let num: u64 = U64_MAX; 208 | while (trials > 0) { 209 | num = num << 1 << 1; 210 | trials = trials - 1; 211 | } 212 | } 213 | #[test] 214 | public entry fun test_calibrate_shl__baseline() { 215 | let trials: u64 = NUM_TRIALS; 216 | let num: u64 = U64_MAX; 217 | while (trials > 0) { 218 | num = num << 1; 219 | trials = trials - 1; 220 | } 221 | } 222 | 223 | // Bitwise And operation 224 | #[test] 225 | public entry fun test_calibrate_bitand() { 226 | let trials: u64 = NUM_TRIALS; 227 | let num: u64 = U64_MAX; 228 | while (trials > 0) { 229 | num = num & 1 & 1; 230 | trials = trials - 1; 231 | } 232 | } 233 | #[test] 234 | public entry fun test_calibrate_bitand__baseline() { 235 | let trials: u64 = NUM_TRIALS; 236 | let num: u64 = U64_MAX; 237 | while (trials > 0) { 238 | num = num & 1; 239 | trials = trials - 1; 240 | } 241 | } 242 | 243 | // Bitwise Or operation 244 | #[test] 245 | public entry fun test_calibrate_bitor() { 246 | let trials: u64 = NUM_TRIALS; 247 | let num: u64 = U64_MAX; 248 | while (trials > 0) { 249 | num = num | 1 | 1; 250 | trials = trials - 1; 251 | } 252 | } 253 | #[test] 254 | public entry fun test_calibrate_bitor__baseline() { 255 | let trials: u64 = NUM_TRIALS; 256 | let num: u64 = U64_MAX; 257 | while (trials > 0) { 258 | num = num | 1; 259 | trials = trials - 1; 260 | } 261 | } 262 | 263 | // Eq operation 264 | #[test] 265 | public entry fun test_calibrate_eq() { 266 | let trials: u64 = NUM_TRIALS; 267 | let flag: bool = false; 268 | while (trials > 0) { 269 | flag = flag == true == true; 270 | trials = trials - 1; 271 | } 272 | } 273 | #[test] 274 | public entry fun test_calibrate_eq__baseline() { 275 | let trials: u64 = NUM_TRIALS; 276 | let flag: bool = false; 277 | while (trials > 0) { 278 | flag = flag == true; 279 | trials = trials - 1; 280 | } 281 | } 282 | 283 | // Neq operation 284 | #[test] 285 | public entry fun test_calibrate_neq() { 286 | let trials: u64 = NUM_TRIALS; 287 | let flag: bool = false; 288 | while (trials > 0) { 289 | flag = flag != true != true; 290 | trials = trials - 1; 291 | } 292 | } 293 | #[test] 294 | public entry fun test_calibrate_neq__baseline() { 295 | let trials: u64 = NUM_TRIALS; 296 | let flag: bool = false; 297 | while (trials > 0) { 298 | flag = flag != true; 299 | trials = trials - 1; 300 | } 301 | } 302 | 303 | // TBD 304 | // Lt, Gt, Le, Ge, Not 305 | } -------------------------------------------------------------------------------- /lib/sui-framework/tests/coin_balance_tests.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[test_only] 5 | module sui::test_coin { 6 | use sui::test_scenario::{Self, ctx}; 7 | use sui::coin; 8 | use sui::balance; 9 | use sui::sui::SUI; 10 | use sui::locked_coin::LockedCoin; 11 | use sui::tx_context; 12 | use sui::locked_coin; 13 | use sui::coin::Coin; 14 | 15 | #[test] 16 | fun type_morphing() { 17 | let test = &mut test_scenario::begin(&@0x1); 18 | 19 | let balance = balance::zero(); 20 | let coin = coin::from_balance(balance, ctx(test)); 21 | let balance = coin::into_balance(coin); 22 | 23 | balance::destroy_zero(balance); 24 | 25 | let coin = coin::mint_for_testing(100, ctx(test)); 26 | let balance_mut = coin::balance_mut(&mut coin); 27 | let sub_balance = balance::split(balance_mut, 50); 28 | 29 | assert!(balance::value(&sub_balance) == 50, 0); 30 | assert!(coin::value(&coin) == 50, 0); 31 | 32 | let balance = coin::into_balance(coin); 33 | balance::join(&mut balance, sub_balance); 34 | 35 | assert!(balance::value(&balance) == 100, 0); 36 | 37 | let coin = coin::from_balance(balance, ctx(test)); 38 | coin::keep(coin, ctx(test)); 39 | } 40 | 41 | const TEST_SENDER_ADDR: address = @0xA11CE; 42 | const TEST_RECIPIENT_ADDR: address = @0xB0B; 43 | 44 | #[test] 45 | public entry fun test_locked_coin_valid() { 46 | let scenario = &mut test_scenario::begin(&TEST_SENDER_ADDR); 47 | let ctx = test_scenario::ctx(scenario); 48 | let coin = coin::mint_for_testing(42, ctx); 49 | 50 | test_scenario::next_tx(scenario, &TEST_SENDER_ADDR); 51 | // Lock up the coin until epoch 2. 52 | locked_coin::lock_coin(coin, TEST_RECIPIENT_ADDR, 2, test_scenario::ctx(scenario)); 53 | 54 | // Advance the epoch by 2. 55 | test_scenario::next_epoch(scenario); 56 | test_scenario::next_epoch(scenario); 57 | assert!(tx_context::epoch(test_scenario::ctx(scenario)) == 2, 1); 58 | 59 | test_scenario::next_tx(scenario, &TEST_RECIPIENT_ADDR); 60 | let locked_coin = test_scenario::take_owned>(scenario); 61 | // The unlock should go through since epoch requirement is met. 62 | locked_coin::unlock_coin(locked_coin, test_scenario::ctx(scenario)); 63 | 64 | test_scenario::next_tx(scenario, &TEST_RECIPIENT_ADDR); 65 | let unlocked_coin = test_scenario::take_owned>(scenario); 66 | assert!(coin::value(&unlocked_coin) == 42, 2); 67 | coin::destroy_for_testing(unlocked_coin); 68 | } 69 | 70 | #[test] 71 | #[expected_failure(abort_code = 1)] 72 | public entry fun test_locked_coin_invalid() { 73 | let scenario = &mut test_scenario::begin(&TEST_SENDER_ADDR); 74 | let ctx = test_scenario::ctx(scenario); 75 | let coin = coin::mint_for_testing(42, ctx); 76 | 77 | test_scenario::next_tx(scenario, &TEST_SENDER_ADDR); 78 | // Lock up the coin until epoch 2. 79 | locked_coin::lock_coin(coin, TEST_RECIPIENT_ADDR, 2, test_scenario::ctx(scenario)); 80 | 81 | // Advance the epoch by 1. 82 | test_scenario::next_epoch(scenario); 83 | assert!(tx_context::epoch(test_scenario::ctx(scenario)) == 1, 1); 84 | 85 | test_scenario::next_tx(scenario, &TEST_RECIPIENT_ADDR); 86 | let locked_coin = test_scenario::take_owned>(scenario); 87 | // The unlock should fail. 88 | locked_coin::unlock_coin(locked_coin, test_scenario::ctx(scenario)); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /lib/sui-framework/tests/collection_tests.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[test_only] 5 | module sui::collection_tests { 6 | use sui::bag::{Self, Bag}; 7 | use sui::collection::{Self, Collection}; 8 | use sui::object::{Self, Info}; 9 | use sui::test_scenario; 10 | use sui::typed_id; 11 | use sui::tx_context; 12 | 13 | struct Object has key, store { 14 | info: Info, 15 | } 16 | 17 | #[test] 18 | fun test_collection() { 19 | let sender = @0x0; 20 | let scenario = &mut test_scenario::begin(&sender); 21 | 22 | // Create a new Collection and transfer it to the sender. 23 | test_scenario::next_tx(scenario, &sender); 24 | { 25 | collection::create(test_scenario::ctx(scenario)); 26 | }; 27 | 28 | // Add two objects of different types into the collection. 29 | test_scenario::next_tx(scenario, &sender); 30 | { 31 | let collection = test_scenario::take_owned>(scenario); 32 | assert!(collection::size(&collection) == 0, 0); 33 | 34 | let obj1 = Object { info: object::new(test_scenario::ctx(scenario)) }; 35 | let obj2 = Object { info: object::new(test_scenario::ctx(scenario)) }; 36 | 37 | let item_id1 = collection::add(&mut collection, obj1, test_scenario::ctx(scenario)); 38 | let item_id2 = collection::add(&mut collection, obj2, test_scenario::ctx(scenario)); 39 | assert!(collection::size(&collection) == 2, 0); 40 | 41 | assert!(collection::contains(&collection, typed_id::as_id(&item_id1)), 0); 42 | assert!(collection::contains(&collection, typed_id::as_id(&item_id2)), 0); 43 | 44 | test_scenario::return_owned(scenario, collection); 45 | }; 46 | } 47 | 48 | #[test] 49 | fun test_collection_bag_interaction() { 50 | let sender = @0x0; 51 | let scenario = &mut test_scenario::begin(&sender); 52 | 53 | // Create a new Collection and a new Bag and transfer them to the sender. 54 | test_scenario::next_tx(scenario, &sender); 55 | { 56 | collection::create(test_scenario::ctx(scenario)); 57 | bag::create(test_scenario::ctx(scenario)); 58 | }; 59 | 60 | // Add a new object to the Collection. 61 | test_scenario::next_tx(scenario, &sender); 62 | { 63 | let collection = test_scenario::take_owned>(scenario); 64 | let obj = Object { info: object::new(test_scenario::ctx(scenario)) }; 65 | collection::add(&mut collection, obj, test_scenario::ctx(scenario)); 66 | test_scenario::return_owned(scenario, collection); 67 | }; 68 | 69 | // Remove the object from the collection and add it to the bag. 70 | test_scenario::next_tx(scenario, &sender); 71 | { 72 | let collection = test_scenario::take_owned>(scenario); 73 | let bag = test_scenario::take_owned(scenario); 74 | let obj = test_scenario::take_child_object, collection::Item>(scenario, &collection); 75 | 76 | let obj = collection::remove(&mut collection, obj); 77 | let item_id = bag::add(&mut bag, obj, test_scenario::ctx(scenario)); 78 | 79 | assert!(collection::size(&collection) == 0, 0); 80 | assert!(bag::size(&bag) == 1, 0); 81 | assert!(bag::contains(&bag, typed_id::as_id(&item_id)), 0); 82 | 83 | test_scenario::return_owned(scenario, collection); 84 | test_scenario::return_owned(scenario, bag); 85 | }; 86 | 87 | // Remove the object from the bag and add it back to the collection. 88 | test_scenario::next_tx(scenario, &sender); 89 | { 90 | let collection = test_scenario::take_owned>(scenario); 91 | let bag = test_scenario::take_owned(scenario); 92 | let obj = test_scenario::take_child_object>(scenario, &bag); 93 | 94 | let obj = bag::remove(&mut bag, obj); 95 | let item_id = collection::add(&mut collection, obj, test_scenario::ctx(scenario)); 96 | 97 | assert!(collection::size(&collection) == 1, 0); 98 | assert!(bag::size(&bag) == 0, 0); 99 | assert!(collection::contains(&collection, typed_id::as_id(&item_id)), 0); 100 | 101 | test_scenario::return_owned(scenario, collection); 102 | test_scenario::return_owned(scenario, bag); 103 | }; 104 | 105 | } 106 | 107 | #[test] 108 | #[expected_failure(abort_code = 0)] 109 | fun test_init_with_invalid_max_capacity() { 110 | let ctx = tx_context::dummy(); 111 | // Sui::collection::DEFAULT_MAX_CAPACITY is not readable outside the module 112 | let max_capacity = 65536; 113 | let collection = collection::new_with_max_capacity(&mut ctx, max_capacity + 1); 114 | collection::transfer(collection, tx_context::sender(&ctx)); 115 | } 116 | 117 | #[test] 118 | #[expected_failure(abort_code = 0)] 119 | fun test_init_with_zero() { 120 | let ctx = tx_context::dummy(); 121 | let collection = collection::new_with_max_capacity(&mut ctx, 0); 122 | collection::transfer(collection, tx_context::sender(&ctx)); 123 | } 124 | 125 | #[test] 126 | #[expected_failure(abort_code = 1)] 127 | fun test_exceed_max_capacity() { 128 | let ctx = tx_context::dummy(); 129 | let collection = collection::new_with_max_capacity(&mut ctx, 1); 130 | 131 | let obj1 = Object { info: object::new(&mut ctx) }; 132 | collection::add(&mut collection, obj1, &mut ctx); 133 | let obj2 = Object { info: object::new(&mut ctx) }; 134 | collection::add(&mut collection, obj2, &mut ctx); 135 | collection::transfer(collection, tx_context::sender(&ctx)); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /lib/sui-framework/tests/delegation_tests.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[test_only] 5 | module sui::delegation_tests { 6 | use sui::coin; 7 | use sui::test_scenario::{Self, Scenario}; 8 | use sui::sui_system::{Self, SuiSystemState}; 9 | use sui::delegation::{Self, Delegation}; 10 | use sui::governance_test_utils::{ 11 | Self, 12 | create_validator_for_testing, 13 | create_sui_system_state_for_testing 14 | }; 15 | 16 | const VALIDATOR_ADDR_1: address = @0x1; 17 | const VALIDATOR_ADDR_2: address = @0x2; 18 | 19 | const DELEGATOR_ADDR_1: address = @0x42; 20 | const DELEGATOR_ADDR_2: address = @0x43; 21 | 22 | #[test] 23 | fun test_add_remove_delegation_flow() { 24 | let scenario = &mut test_scenario::begin(&VALIDATOR_ADDR_1); 25 | set_up_sui_system_state(scenario); 26 | 27 | test_scenario::next_tx(scenario, &DELEGATOR_ADDR_1); 28 | { 29 | let system_state_wrapper = test_scenario::take_shared(scenario); 30 | let system_state_mut_ref = test_scenario::borrow_mut(&mut system_state_wrapper); 31 | 32 | let ctx = test_scenario::ctx(scenario); 33 | 34 | // Create two delegations to VALIDATOR_ADDR_1. 35 | sui_system::request_add_delegation( 36 | system_state_mut_ref, coin::mint_for_testing(10, ctx), VALIDATOR_ADDR_1, ctx); 37 | sui_system::request_add_delegation( 38 | system_state_mut_ref, coin::mint_for_testing(60, ctx), VALIDATOR_ADDR_1, ctx); 39 | 40 | // Advance the epoch so that the delegation changes can take into effect. 41 | governance_test_utils::advance_epoch(system_state_mut_ref, scenario); 42 | 43 | // Check that the delegation amount and count are changed correctly. 44 | assert!(sui_system::validator_delegate_amount(system_state_mut_ref, VALIDATOR_ADDR_1) == 70, 101); 45 | assert!(sui_system::validator_delegate_amount(system_state_mut_ref, VALIDATOR_ADDR_2) == 0, 102); 46 | assert!(sui_system::validator_delegator_count(system_state_mut_ref, VALIDATOR_ADDR_1) == 2, 103); 47 | assert!(sui_system::validator_delegator_count(system_state_mut_ref, VALIDATOR_ADDR_2) == 0, 104); 48 | test_scenario::return_shared(scenario, system_state_wrapper); 49 | }; 50 | 51 | 52 | test_scenario::next_tx(scenario, &DELEGATOR_ADDR_1); 53 | { 54 | 55 | let delegation = test_scenario::take_last_created_owned(scenario); 56 | assert!(delegation::delegate_amount(&delegation) == 60, 105); 57 | 58 | 59 | let system_state_wrapper = test_scenario::take_shared(scenario); 60 | let system_state_mut_ref = test_scenario::borrow_mut(&mut system_state_wrapper); 61 | 62 | let ctx = test_scenario::ctx(scenario); 63 | 64 | // Undelegate 60 SUIs from VALIDATOR_ADDR_1 65 | sui_system::request_remove_delegation( 66 | system_state_mut_ref, &mut delegation, ctx); 67 | 68 | // Check that the delegation object indeed becomes inactive. 69 | assert!(!delegation::is_active(&delegation), 106); 70 | test_scenario::return_owned(scenario, delegation); 71 | 72 | governance_test_utils::advance_epoch(system_state_mut_ref, scenario); 73 | 74 | assert!(sui_system::validator_delegate_amount(system_state_mut_ref, VALIDATOR_ADDR_1) == 10, 107); 75 | assert!(sui_system::validator_delegator_count(system_state_mut_ref, VALIDATOR_ADDR_1) == 1, 108); 76 | test_scenario::return_shared(scenario, system_state_wrapper); 77 | }; 78 | } 79 | 80 | #[test] 81 | fun test_switch_delegation_flow() { 82 | let scenario = &mut test_scenario::begin(&VALIDATOR_ADDR_1); 83 | set_up_sui_system_state(scenario); 84 | 85 | test_scenario::next_tx(scenario, &DELEGATOR_ADDR_1); 86 | { 87 | let system_state_wrapper = test_scenario::take_shared(scenario); 88 | let system_state_mut_ref = test_scenario::borrow_mut(&mut system_state_wrapper); 89 | 90 | let ctx = test_scenario::ctx(scenario); 91 | 92 | // Create one delegation to VALIDATOR_ADDR_1, and one to VALIDATOR_ADDR_2. 93 | sui_system::request_add_delegation( 94 | system_state_mut_ref, coin::mint_for_testing(60, ctx), VALIDATOR_ADDR_1, ctx); 95 | sui_system::request_add_delegation( 96 | system_state_mut_ref, coin::mint_for_testing(10, ctx), VALIDATOR_ADDR_2, ctx); 97 | 98 | governance_test_utils::advance_epoch(system_state_mut_ref, scenario); 99 | test_scenario::return_shared(scenario, system_state_wrapper); 100 | }; 101 | 102 | test_scenario::next_tx(scenario, &DELEGATOR_ADDR_1); 103 | { 104 | 105 | let delegation = test_scenario::take_last_created_owned(scenario); 106 | 107 | let system_state_wrapper = test_scenario::take_shared(scenario); 108 | let system_state_mut_ref = test_scenario::borrow_mut(&mut system_state_wrapper); 109 | 110 | let ctx = test_scenario::ctx(scenario); 111 | 112 | // Switch the 10 SUI delegation from VALIDATOR_ADDR_2 to VALIDATOR_ADDR_1 113 | sui_system::request_switch_delegation( 114 | system_state_mut_ref, &mut delegation, VALIDATOR_ADDR_1, ctx); 115 | 116 | test_scenario::return_owned(scenario, delegation); 117 | 118 | governance_test_utils::advance_epoch(system_state_mut_ref, scenario); 119 | 120 | // Check that now VALIDATOR_ADDR_1 has all the delegations 121 | assert!(sui_system::validator_delegate_amount(system_state_mut_ref, VALIDATOR_ADDR_1) == 70, 101); 122 | assert!(sui_system::validator_delegate_amount(system_state_mut_ref, VALIDATOR_ADDR_2) == 0, 102); 123 | assert!(sui_system::validator_delegator_count(system_state_mut_ref, VALIDATOR_ADDR_1) == 2, 103); 124 | assert!(sui_system::validator_delegator_count(system_state_mut_ref, VALIDATOR_ADDR_2) == 0, 104); 125 | test_scenario::return_shared(scenario, system_state_wrapper); 126 | }; 127 | } 128 | 129 | fun set_up_sui_system_state(scenario: &mut Scenario) { 130 | let ctx = test_scenario::ctx(scenario); 131 | 132 | let validators = vector[ 133 | create_validator_for_testing(VALIDATOR_ADDR_1, 100, ctx), 134 | create_validator_for_testing(VALIDATOR_ADDR_2, 100, ctx) 135 | ]; 136 | create_sui_system_state_for_testing(validators, 300, 100); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /lib/sui-framework/tests/governance_test_utils.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[test_only] 5 | module sui::governance_test_utils { 6 | use sui::balance; 7 | use sui::sui::SUI; 8 | use sui::tx_context::{Self, TxContext}; 9 | use sui::validator::{Self, Validator}; 10 | use sui::sui_system::{Self, SuiSystemState}; 11 | use sui::test_scenario::{Self, Scenario}; 12 | use std::option; 13 | 14 | public fun create_validator_for_testing( 15 | addr: address, init_stake_amount: u64, ctx: &mut TxContext 16 | ): Validator { 17 | validator::new( 18 | addr, 19 | x"FF", 20 | b"ValidatorName", 21 | x"FFFF", 22 | balance::create_for_testing(init_stake_amount), 23 | option::none(), 24 | ctx 25 | ) 26 | } 27 | 28 | public fun create_sui_system_state_for_testing( 29 | validators: vector, sui_supply_amount: u64, storage_fund_amount: u64 30 | ) { 31 | sui_system::create( 32 | validators, 33 | balance::create_supply_for_testing(sui_supply_amount), // sui_supply 34 | balance::create_for_testing(storage_fund_amount), // storage_fund 35 | 1024, // max_validator_candidate_count 36 | 0, // min_validator_stake 37 | 1, //storage_gas_price 38 | ) 39 | } 40 | 41 | public fun advance_epoch(state: &mut SuiSystemState, scenario: &mut Scenario) { 42 | test_scenario::next_epoch(scenario); 43 | let new_epoch = tx_context::epoch(test_scenario::ctx(scenario)); 44 | sui_system::advance_epoch(state, new_epoch, 0, 0, &mut tx_context::dummy()); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/sui-framework/tests/id_tests.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[test_only] 5 | module sui::id_tests { 6 | use sui::object; 7 | use sui::tx_context; 8 | 9 | const ID_BYTES_MISMATCH: u64 = 0; 10 | 11 | struct Object has key { 12 | info: object::Info, 13 | } 14 | 15 | #[test] 16 | fun test_get_id() { 17 | let ctx = tx_context::dummy(); 18 | let info = object::new(&mut ctx); 19 | let obj_id = *object::info_id(&info); 20 | let obj = Object { info }; 21 | assert!(*object::id(&obj) == obj_id, ID_BYTES_MISMATCH); 22 | let Object { info } = obj; 23 | object::delete(info); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/sui-framework/tests/math_tests.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[test_only] 5 | module sui::math_tests { 6 | use sui::math; 7 | 8 | #[test] 9 | fun test_max() { 10 | assert!(math::max(10, 100) == 100, 1); 11 | assert!(math::max(100, 10) == 100, 2); 12 | assert!(math::max(0, 0) == 0, 3); 13 | } 14 | 15 | #[test] 16 | fun test_min() { 17 | assert!(math::min(10, 100) == 10, 1); 18 | assert!(math::min(100, 10) == 10, 2); 19 | assert!(math::min(0, 0) == 0, 3); 20 | } 21 | 22 | #[test] 23 | fun test_perfect_sqrt() { 24 | let i = 0; 25 | while (i < 1000) { 26 | assert!(math::sqrt(i * i) == i, 1); 27 | i = i + 1; 28 | } 29 | } 30 | 31 | #[test] 32 | // This function tests whether the (square root)^2 equals the 33 | // initial value OR whether it is equal to the nearest lower 34 | // number that does. 35 | fun test_imperfect_sqrt() { 36 | let i = 1; 37 | let prev = 1; 38 | while (i <= 1000) { 39 | let root = math::sqrt(i); 40 | 41 | assert!(i == root * root || root == prev, 0); 42 | 43 | prev = root; 44 | i = i + 1; 45 | } 46 | } 47 | 48 | #[test] 49 | fun test_sqrt_big_numbers() { 50 | let u64_max = 18446744073709551615; 51 | assert!(4294967295 == math::sqrt(u64_max), 0) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/sui-framework/tests/tx_context_tests.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[test_only] 5 | module sui::tx_context_tests { 6 | use sui::object; 7 | use sui::tx_context; 8 | 9 | #[test] 10 | fun test_id_generation() { 11 | let ctx = tx_context::dummy(); 12 | assert!(tx_context::get_ids_created(&ctx) == 0, 0); 13 | 14 | let id1 = object::new(&mut ctx); 15 | let id2 = object::new(&mut ctx); 16 | 17 | // new_id should always produce fresh ID's 18 | assert!(&id1 != &id2, 1); 19 | assert!(tx_context::get_ids_created(&ctx) == 2, 2); 20 | object::delete(id1); 21 | object::delete(id2); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /lib/sui-framework/tests/url_tests.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[test_only] 5 | module sui::url_tests { 6 | use sui::url; 7 | use std::ascii::Self; 8 | 9 | const EHASH_LENGTH_MISMATCH: u64 = 0; 10 | const URL_STRING_MISMATCH: u64 = 1; 11 | 12 | #[test] 13 | fun test_basic_url() { 14 | // url strings are not currently validated 15 | let url_str = ascii::string(x"414243454647"); 16 | 17 | let url = url::new_unsafe(url_str); 18 | assert!(url::inner_url(&url) == url_str, URL_STRING_MISMATCH); 19 | } 20 | 21 | #[test] 22 | #[expected_failure(abort_code = 0)] 23 | fun test_malformed_hash() { 24 | // url strings are not currently validated 25 | let url_str = ascii::string(x"414243454647"); 26 | // length too short 27 | let hash = x"badf012345"; 28 | 29 | let url = url::new_unsafe(url_str); 30 | let _ = url::new_unsafe_url_commitment(url, hash); 31 | } 32 | 33 | #[test] 34 | fun test_good_hash() { 35 | // url strings are not currently validated 36 | let url_str = ascii::string(x"414243454647"); 37 | // 32 bytes 38 | let hash = x"1234567890123456789012345678901234567890abcdefabcdefabcdefabcdef"; 39 | 40 | let url = url::new_unsafe(url_str); 41 | let url_commit = url::new_unsafe_url_commitment(url, hash); 42 | 43 | assert!(url::url_commitment_resource_hash(&url_commit) == hash, EHASH_LENGTH_MISMATCH); 44 | assert!(url::url_commitment_inner_url(&url_commit) == url_str, URL_STRING_MISMATCH); 45 | 46 | let url_str = ascii::string(x"37414243454647"); 47 | 48 | url::url_commitment_update(&mut url_commit, url_str); 49 | assert!(url::url_commitment_inner_url(&url_commit) == url_str, URL_STRING_MISMATCH); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/sui-framework/tests/validator_set_tests.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[test_only] 5 | module sui::validator_set_tests { 6 | use sui::balance; 7 | use sui::coin; 8 | use sui::sui::SUI; 9 | use sui::tx_context::TxContext; 10 | use sui::validator::{Self, Validator}; 11 | use sui::validator_set; 12 | use sui::test_scenario; 13 | use sui::stake::Stake; 14 | use std::option; 15 | 16 | #[test] 17 | fun test_validator_set_flow() { 18 | let scenario = test_scenario::begin(&@0x1); 19 | let ctx1 = test_scenario::ctx(&mut scenario); 20 | 21 | // Create 4 validators, with stake 100, 200, 300, 400. 22 | let validator1 = create_validator(@0x1, 1, ctx1); 23 | let validator2 = create_validator(@0x2, 2, ctx1); 24 | let validator3 = create_validator(@0x3, 3, ctx1); 25 | let validator4 = create_validator(@0x4, 4, ctx1); 26 | 27 | // Create a validator set with only the first validator in it. 28 | let validator_set = validator_set::new(vector[validator1]); 29 | assert!(validator_set::total_validator_candidate_count(&validator_set) == 1, 0); 30 | assert!(validator_set::total_validator_stake(&validator_set) == 100, 0); 31 | 32 | // Add the other 3 validators one by one. 33 | validator_set::request_add_validator( 34 | &mut validator_set, 35 | validator2, 36 | ); 37 | // Adding validator during the epoch should not affect stake and quorum threshold. 38 | assert!(validator_set::total_validator_candidate_count(&validator_set) == 2, 0); 39 | assert!(validator_set::total_validator_stake(&validator_set) == 100, 0); 40 | 41 | validator_set::request_add_validator( 42 | &mut validator_set, 43 | validator3, 44 | ); 45 | 46 | test_scenario::next_tx(&mut scenario, &@0x1); 47 | { 48 | let ctx1 = test_scenario::ctx(&mut scenario); 49 | validator_set::request_add_stake( 50 | &mut validator_set, 51 | coin::into_balance(coin::mint_for_testing(500, ctx1)), 52 | option::none(), 53 | ctx1, 54 | ); 55 | // Adding stake to existing active validator during the epoch 56 | // should not change total stake. 57 | assert!(validator_set::total_validator_stake(&validator_set) == 100, 0); 58 | }; 59 | 60 | test_scenario::next_tx(&mut scenario, &@0x1); 61 | { 62 | let stake1 = test_scenario::take_last_created_owned(&mut scenario); 63 | let ctx1 = test_scenario::ctx(&mut scenario); 64 | validator_set::request_withdraw_stake( 65 | &mut validator_set, 66 | &mut stake1, 67 | 500, 68 | 100 /* min_validator_stake */, 69 | ctx1, 70 | ); 71 | test_scenario::return_owned(&mut scenario, stake1); 72 | assert!(validator_set::total_validator_stake(&validator_set) == 100, 0); 73 | 74 | validator_set::request_add_validator( 75 | &mut validator_set, 76 | validator4, 77 | ); 78 | }; 79 | 80 | test_scenario::next_tx(&mut scenario, &@0x1); 81 | { 82 | let reward = balance::zero(); 83 | let ctx1 = test_scenario::ctx(&mut scenario); 84 | validator_set::advance_epoch(&mut validator_set, &mut reward, ctx1); 85 | // The total stake and quorum should reflect 4 validators. 86 | assert!(validator_set::total_validator_candidate_count(&validator_set) == 4, 0); 87 | assert!(validator_set::total_validator_stake(&validator_set) == 1000, 0); 88 | 89 | validator_set::request_remove_validator( 90 | &mut validator_set, 91 | ctx1, 92 | ); 93 | // Total validator candidate count changes, but total stake remains during epoch. 94 | assert!(validator_set::total_validator_candidate_count(&validator_set) == 3, 0); 95 | assert!(validator_set::total_validator_stake(&validator_set) == 1000, 0); 96 | validator_set::advance_epoch(&mut validator_set, &mut reward, ctx1); 97 | // Validator1 is gone. 98 | assert!(validator_set::total_validator_stake(&validator_set) == 900, 0); 99 | balance::destroy_zero(reward); 100 | }; 101 | 102 | validator_set::destroy_for_testing(validator_set); 103 | } 104 | 105 | fun create_validator(addr: address, hint: u8, ctx: &mut TxContext): Validator { 106 | let stake_value = (hint as u64) * 100; 107 | let init_stake = coin::mint_for_testing(stake_value, ctx); 108 | let init_stake = coin::into_balance(init_stake); 109 | validator::new( 110 | addr, 111 | vector[hint], 112 | vector[hint], 113 | vector[hint], 114 | init_stake, 115 | option::none(), 116 | ctx 117 | ) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /lib/sui-framework/tests/validator_tests.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[test_only] 5 | module sui::validator_tests { 6 | use sui::coin; 7 | use sui::sui::SUI; 8 | use sui::test_scenario; 9 | use sui::validator; 10 | use sui::stake::Stake; 11 | use sui::locked_coin::{Self, LockedCoin}; 12 | use sui::stake; 13 | use std::option; 14 | 15 | #[test] 16 | fun test_validator_owner_flow() { 17 | let sender = @0x1; 18 | let scenario = &mut test_scenario::begin(&sender); 19 | { 20 | let ctx = test_scenario::ctx(scenario); 21 | 22 | let init_stake = coin::into_balance(coin::mint_for_testing(10, ctx)); 23 | let validator = validator::new( 24 | sender, 25 | x"FF", 26 | b"Validator1", 27 | x"FFFF", 28 | init_stake, 29 | option::none(), 30 | ctx 31 | ); 32 | assert!(validator::stake_amount(&validator) == 10, 0); 33 | assert!(validator::sui_address(&validator) == sender, 0); 34 | 35 | validator::destroy(validator); 36 | }; 37 | 38 | // Check that after destroy, the original stake still exists. 39 | test_scenario::next_tx(scenario, &sender); 40 | { 41 | let stake = test_scenario::take_owned(scenario); 42 | assert!(stake::value(&stake) == 10, 0); 43 | test_scenario::return_owned(scenario, stake); 44 | }; 45 | } 46 | 47 | #[test] 48 | fun test_pending_validator_flow() { 49 | let sender = @0x1; 50 | let scenario = &mut test_scenario::begin(&sender); 51 | let ctx = test_scenario::ctx(scenario); 52 | let init_stake = coin::into_balance(coin::mint_for_testing(10, ctx)); 53 | let validator = validator::new( 54 | sender, 55 | x"FF", 56 | b"Validator1", 57 | x"FFFF", 58 | init_stake, 59 | option::none(), 60 | ctx 61 | ); 62 | 63 | test_scenario::next_tx(scenario, &sender); 64 | { 65 | let ctx = test_scenario::ctx(scenario); 66 | let new_stake = coin::into_balance(coin::mint_for_testing(30, ctx)); 67 | validator::request_add_stake(&mut validator, new_stake, option::none(), ctx); 68 | 69 | assert!(validator::stake_amount(&validator) == 10, 0); 70 | assert!(validator::pending_stake_amount(&validator) == 30, 0); 71 | }; 72 | 73 | test_scenario::next_tx(scenario, &sender); 74 | { 75 | let stake = test_scenario::take_last_created_owned(scenario); 76 | let ctx = test_scenario::ctx(scenario); 77 | validator::request_withdraw_stake(&mut validator, &mut stake, 5, 35, ctx); 78 | test_scenario::return_owned(scenario, stake); 79 | assert!(validator::stake_amount(&validator) == 10, 0); 80 | assert!(validator::pending_stake_amount(&validator) == 30, 0); 81 | assert!(validator::pending_withdraw(&validator) == 5, 0); 82 | 83 | // Calling `adjust_stake` will withdraw the coin and transfer to sender. 84 | validator::adjust_stake(&mut validator); 85 | 86 | assert!(validator::stake_amount(&validator) == 35, 0); 87 | assert!(validator::pending_stake_amount(&validator) == 0, 0); 88 | assert!(validator::pending_withdraw(&validator) == 0, 0); 89 | }; 90 | 91 | test_scenario::next_tx(scenario, &sender); 92 | { 93 | let withdraw = test_scenario::take_owned>(scenario); 94 | assert!(locked_coin::value(&withdraw) == 5, 0); 95 | test_scenario::return_owned(scenario, withdraw); 96 | }; 97 | 98 | validator::destroy(validator); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lib/sui-framework/tests/vec_map_tests.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[test_only] 5 | module sui::vec_map_tests { 6 | use std::vector; 7 | use sui::vec_map::{Self, VecMap}; 8 | 9 | #[test] 10 | #[expected_failure(abort_code = 0)] 11 | fun duplicate_key_abort() { 12 | let m = vec_map::empty(); 13 | vec_map::insert(&mut m, 1, true); 14 | vec_map::insert(&mut m, 1, false); 15 | } 16 | 17 | #[test] 18 | #[expected_failure(abort_code = 1)] 19 | fun nonexistent_key_get() { 20 | let m = vec_map::empty(); 21 | vec_map::insert(&mut m, 1, true); 22 | let k = 2; 23 | let _v = vec_map::get(&m, &k); 24 | } 25 | 26 | #[test] 27 | #[expected_failure(abort_code = 1)] 28 | fun nonexistent_key_get_idx_or_abort() { 29 | let m = vec_map::empty(); 30 | vec_map::insert(&mut m, 1, true); 31 | let k = 2; 32 | let _idx = vec_map::get_idx(&m, &k); 33 | } 34 | 35 | #[test] 36 | #[expected_failure(abort_code = 1)] 37 | fun nonexistent_key_get_entry_by_idx() { 38 | let m = vec_map::empty(); 39 | vec_map::insert(&mut m, 1, true); 40 | let k = 2; 41 | let _idx = vec_map::get_idx(&m, &k); 42 | } 43 | 44 | #[test] 45 | #[expected_failure(abort_code = 2)] 46 | fun destroy_non_empty() { 47 | let m = vec_map::empty(); 48 | vec_map::insert(&mut m, 1, true); 49 | vec_map::destroy_empty(m) 50 | } 51 | 52 | #[test] 53 | fun destroy_empty() { 54 | let m: VecMap = vec_map::empty(); 55 | assert!(vec_map::is_empty(&m), 0); 56 | vec_map::destroy_empty(m) 57 | } 58 | 59 | #[test] 60 | fun smoke() { 61 | let m = vec_map::empty(); 62 | let i = 0; 63 | while (i < 10) { 64 | let k = i + 2; 65 | let v = i + 5; 66 | vec_map::insert(&mut m, k, v); 67 | i = i + 1; 68 | }; 69 | assert!(!vec_map::is_empty(&m), 0); 70 | assert!(vec_map::size(&m) == 10, 1); 71 | let i = 0; 72 | // make sure the elements are as expected in all of the getter APIs we expose 73 | while (i < 10) { 74 | let k = i + 2; 75 | assert!(vec_map::contains(&m, &k), 2); 76 | let v = *vec_map::get(&m, &k); 77 | assert!(v == i + 5, 3); 78 | assert!(vec_map::get_idx(&m, &k) == i, 4); 79 | let (other_k, other_v) = vec_map::get_entry_by_idx(&m, i); 80 | assert!(*other_k == k, 5); 81 | assert!(*other_v == v, 6); 82 | i = i + 1; 83 | }; 84 | // remove all the elements 85 | let (keys, values) = vec_map::into_keys_values(copy m); 86 | let i = 0; 87 | while (i < 10) { 88 | let k = i + 2; 89 | let (other_k, v) = vec_map::remove(&mut m, &k); 90 | assert!(k == other_k, 7); 91 | assert!(v == i + 5, 8); 92 | assert!(*vector::borrow(&keys, i) == k, 9); 93 | assert!(*vector::borrow(&values, i) == v, 10); 94 | 95 | i = i + 1; 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /lib/sui-framework/tests/vec_set_tests.move: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2022, Mysten Labs, Inc. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #[test_only] 5 | module sui::vec_set_tests { 6 | use std::vector; 7 | use sui::vec_set; 8 | 9 | fun key_contains() { 10 | let m = vec_set::empty(); 11 | vec_set::insert(&mut m, 1); 12 | assert!(vec_set::contains(&m, &1), 0); 13 | } 14 | 15 | #[test] 16 | #[expected_failure(abort_code = 0)] 17 | fun duplicate_key_abort() { 18 | let m = vec_set::empty(); 19 | vec_set::insert(&mut m, 1); 20 | vec_set::insert(&mut m, 1); 21 | } 22 | 23 | #[test] 24 | #[expected_failure(abort_code = 1)] 25 | fun nonexistent_key_remove() { 26 | let m = vec_set::empty(); 27 | vec_set::insert(&mut m, 1); 28 | let k = 2; 29 | vec_set::remove(&mut m, &k); 30 | } 31 | 32 | #[test] 33 | fun smoke() { 34 | let m = vec_set::empty(); 35 | let i = 0; 36 | while (i < 10) { 37 | let k = i + 2; 38 | vec_set::insert(&mut m, k); 39 | i = i + 1; 40 | }; 41 | assert!(!vec_set::is_empty(&m), 0); 42 | assert!(vec_set::size(&m) == 10, 1); 43 | let i = 0; 44 | // make sure the elements are as expected in all of the getter APIs we expose 45 | while (i < 10) { 46 | let k = i + 2; 47 | assert!(vec_set::contains(&m, &k), 2); 48 | i = i + 1; 49 | }; 50 | // remove all the elements 51 | let keys = vec_set::into_keys(copy m); 52 | let i = 0; 53 | while (i < 10) { 54 | let k = i + 2; 55 | vec_set::remove(&mut m, &k); 56 | assert!(*vector::borrow(&keys, i) == k, 9); 57 | i = i + 1; 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /sources/oracle_factory.move: -------------------------------------------------------------------------------- 1 | // Create and share price oracles. 2 | module oracles::price_oracle_factory{ 3 | use std::vector; 4 | 5 | use sui::object::{Self, ID, Info}; 6 | use sui::tx_context::{Self, TxContext}; 7 | use sui::transfer::{Self}; 8 | 9 | 10 | ///*/////////////////////////////////////////////////////////////// 11 | // MAIN OBJECTS // 12 | /////////////////////////////////////////////////////////////////*/ 13 | 14 | // This object represents an oracle. It hold metadata for the oracle along with the data that the oracle is supposed to return. 15 | struct Oracle has key, store { 16 | info: Info, 17 | 18 | // Timestamp of the last update. 19 | last_update: u64, 20 | 21 | // Time between updates. 22 | interval: u64, 23 | 24 | // Minimum posts required to update the price. 25 | min_posts: u64, 26 | 27 | // Vector containing posted data. 28 | new_data: vector, 29 | 30 | // Most recent updated price (average of all written information since the last update). 31 | data: Data, 32 | 33 | } 34 | 35 | // Represents data held by the oracle. 36 | // Implemented to make it easier to fork the module to return more than just prices. 37 | struct Data has store, drop { 38 | // Price Data stored by the oracle. 39 | price: u64, 40 | } 41 | 42 | ///*/////////////////////////////////////////////////////////////// 43 | // CAPABILITIES // 44 | /////////////////////////////////////////////////////////////////*/ 45 | 46 | // Created when a new oracle is generated. 47 | // Represents ownership, enabling the holder to list/unlist validators. 48 | struct OwnerCap has key, store { 49 | info: Info, 50 | 51 | // The ID of the oracle that is owned by this contract. 52 | oracle_id: ID, 53 | } 54 | 55 | // Created when a validator is listed by the oracle owner, grants the ability to write data to the oracle. 56 | struct ValidatorCap has key, store { 57 | info: Info, 58 | 59 | // The ID of the oracle that the validator can push information to. 60 | oracle_id: ID, 61 | } 62 | 63 | ///*/////////////////////////////////////////////////////////////// 64 | // ERROR CODES // 65 | /////////////////////////////////////////////////////////////////*/ 66 | 67 | // Attempt to perform an operation only allowed by the owner. 68 | const EOwnerOnly: u64 = 0; 69 | 70 | // Attempt to perform an operation only allowed by a validator. 71 | const EValidatorOnly: u64 = 1; 72 | 73 | ///*/////////////////////////////////////////////////////////////// 74 | // ORACLE CREATION LOGIC // 75 | /////////////////////////////////////////////////////////////////*/ 76 | 77 | // Initialize a new oracle and send the owner capability directly to the function caller. 78 | public fun new_oracle( 79 | interval: u64, 80 | min_posts: u64, 81 | ctx: &mut TxContext, 82 | ): OwnerCap { 83 | // Create a new `Info` object and extract its id. 84 | let oracle_info = object::new(ctx); 85 | let oracle_id = *object::info_id(&oracle_info); 86 | 87 | // Create a new Oracle object. Make it shared so that it can be accessed by anyone. 88 | let oracle = Oracle { 89 | info: oracle_info, 90 | last_update: 0, 91 | interval: interval, 92 | min_posts: min_posts, 93 | new_data: vector[], 94 | data: Data { 95 | price: 0, 96 | }, 97 | }; 98 | transfer::share_object(oracle); 99 | 100 | // Create a new OwnerCap object and return it to the caller. 101 | OwnerCap { 102 | info: object::new(ctx), 103 | oracle_id: oracle_id, 104 | } 105 | } 106 | 107 | // Create a new oracle and send the owner capability to the sender of the tx origin. 108 | public entry fun create_oracle( 109 | interval: u64, 110 | min_posts: u64, 111 | ctx: &mut TxContext 112 | ) { 113 | // Create a new Oracle object and retreive its respective owner capability object. 114 | let oracle_owner_cap = new_oracle(interval, min_posts, ctx); 115 | 116 | // Transfer to Owner Capability to the original caller. 117 | transfer::transfer(oracle_owner_cap, tx_context::sender(ctx)); 118 | } 119 | 120 | ///*/////////////////////////////////////////////////////////////// 121 | // VALIDATOR LISTING FUNCTIONS // 122 | /////////////////////////////////////////////////////////////////*/ 123 | 124 | // List a new validator (can only be called by the owner). 125 | public fun list_validator( 126 | self: &mut Oracle, 127 | validator: address, 128 | oracle_owner_cap: &OwnerCap, 129 | ctx: &mut TxContext, 130 | ) { 131 | // Check if the caller is the owner of the referenced oracle. 132 | check_owner(self, oracle_owner_cap); 133 | 134 | // Create a new ValidatorCap object. 135 | let validator_cap = ValidatorCap { 136 | info: object::new(ctx), 137 | oracle_id: *object::info_id(&self.info), 138 | }; 139 | 140 | // Transfer the validator capability to the validator. 141 | transfer::transfer(validator_cap, validator); 142 | } 143 | 144 | 145 | ///*/////////////////////////////////////////////////////////////// 146 | // PRICE POSTS + UPDATE LOGIC // 147 | /////////////////////////////////////////////////////////////////*/ 148 | 149 | // Force the oracle to update its pricing data (can only be called by the owner of the oracle). 150 | public fun force_update( 151 | self: &mut Oracle, 152 | oracle_owner_cap: &OwnerCap, 153 | timestamp: u64, 154 | ): u64 { 155 | // Check if the caller is the owner of the referenced oracle. 156 | check_owner(self, oracle_owner_cap); 157 | 158 | // Update the pricing data. 159 | update_data(self, timestamp) 160 | } 161 | 162 | // Write data to the oracle (can only be called by a validator). 163 | public entry fun write_data(self: &mut Oracle, validator_cap: &mut ValidatorCap, timestamp: u64, data: Data) { 164 | // Ensure the caller is a verified validator. 165 | check_validator(self, validator_cap); 166 | 167 | // Push the data to the Oracle object's `new_data` vector. 168 | vector::push_back(&mut self.new_data, data); 169 | 170 | // If the oracle has enough posts or has not been updated within its interval, update the data. 171 | if(vector::length(&self.new_data) >= self.min_posts || timestamp - self.last_update > self.interval) { 172 | // Update the data. 173 | update_data(self, timestamp); 174 | } 175 | } 176 | 177 | // Update the oracle's data by calculating the averages of the supplied information. Return the new price. 178 | fun update_data(self: &mut Oracle, timestamp: u64): u64 { 179 | // Calculate the average of all data in the `new_data` vector. 180 | let total = 0; 181 | let data_vector = &mut self.new_data; 182 | let size = vector::length(data_vector); 183 | 184 | let i = 0; 185 | while(size>i) { 186 | let data = vector::borrow(data_vector, i); 187 | total = total + data.price; 188 | i = i + 1; 189 | }; 190 | 191 | // Calculate the average price. 192 | let average = total / size; 193 | 194 | // Update the new price, clear the `new_data` vector, and update the `last_update` timestamp. 195 | self.data.price = *&average; 196 | self.new_data = vector[]; 197 | self.last_update = timestamp; 198 | 199 | average 200 | } 201 | 202 | ///*/////////////////////////////////////////////////////////////// 203 | // VIEW METHODS // 204 | /////////////////////////////////////////////////////////////////*/ 205 | 206 | // This function can be called by anyone to read an oracle's data. 207 | public fun read_data( 208 | // The ID of the oracle object. 209 | self: &Oracle, 210 | ): u64 { 211 | self.data.price 212 | } 213 | 214 | ///*/////////////////////////////////////////////////////////////// 215 | // INTERNAL UTILITY FUNCTIONS // 216 | /////////////////////////////////////////////////////////////////*/ 217 | 218 | // Ensure that an OwnerCap object matches the oracle object. 219 | fun check_owner(self: &mut Oracle, admin_cap: &OwnerCap) { 220 | // Ensure the caller is the owner of the Oracle object. 221 | assert!(object::id(self) == &admin_cap.oracle_id, EOwnerOnly); 222 | } 223 | 224 | // Ensure that an ValidatorCap object matches the oracle object. 225 | fun check_validator(self: &mut Oracle, validator_cap: &ValidatorCap) { 226 | // Ensure the caller is a verified validator of the Oracle object. 227 | assert!(object::id(self) == &validator_cap.oracle_id, EValidatorOnly); 228 | } 229 | } --------------------------------------------------------------------------------