diff --git a/docs/content/developer/getting-started/first-app/build-test.mdx b/docs/content/developer/getting-started/first-app/build-test.mdx index a1288d7f515..0877e1f00d9 100644 --- a/docs/content/developer/getting-started/first-app/build-test.mdx +++ b/docs/content/developer/getting-started/first-app/build-test.mdx @@ -38,8 +38,8 @@ $ iota move test If you execute this command for the package created in [Write a Package](write-package.mdx), you see the following output. Unsurprisingly, the test result has an `OK` status because there are no tests written yet to fail. ```shell -BUILDING Iota -BUILDING MoveStdlib +INCLUDING DEPENDENCY Iota +INCLUDING DEPENDENCY MoveStdlib BUILDING my_first_package Running Move unit tests Test result: OK. Total tests: 0; passed: 0; failed: 0 @@ -49,24 +49,23 @@ To actually test your code, you need to add test functions. Start with adding a ```move #[test] -public fun test_sword_create() { +public fun test_sword() { + // Create a dummy TxContext for testing. + let mut ctx = tx_context::dummy(); - // Create a dummy TxContext for testing - let ctx = tx_context::dummy(); - - // Create a sword + // Create a sword. let sword = Sword { id: object::new(&mut ctx), magic: 42, strength: 7, }; - // Check if accessor functions return correct values + // Check if accessor functions return correct values. assert!(magic(&sword) == 42 && strength(&sword) == 7, 1); } ``` -As the code shows, the unit test function (`test_sword_create()`) creates a dummy instance of the `TxContext` struct and assigns it to `ctx`. The function then creates a sword object using `ctx` to create a unique identifier and assigns `42` to the `magic` parameter and `7` to `strength`. Finally, the test calls the `magic` and `strength` accessor functions to verify that they return correct values. +As the code shows, the unit test function (`test_sword()`) creates a dummy instance of the `TxContext` struct and assigns it to `ctx`. The function then creates a sword object using `ctx` to create a unique identifier and assigns `42` to the `magic` parameter and `7` to `strength`. Finally, the test calls the `magic` and `strength` accessor functions to verify that they return correct values. The function passes the dummy context, `ctx`, to the `object::new` function as a mutable reference argument (`&mut`), but passes `sword` to its accessor functions as a read-only reference argument, `&sword`. @@ -80,21 +79,21 @@ After running the `test` command, however, you get a compilation error instead o ```shell error[E06001]: unused value without 'drop' - ┌─ ./sources/my_module.move:60:65 - │ + ┌─ sources/my_module.move:55:65 + │ 4 │ public struct Sword has key, store { - │ ----- To satisfy the constraint, the 'drop' ability would need to be added here - · -27 │ let sword = Sword { + │ ----- To satisfy the constraint, the 'drop' ability would need to be added here + · +48 │ let sword = Sword { │ ----- The local variable 'sword' still contains a value. The value does not have the 'drop' ability and must be consumed before the function returns │ ╭─────────────────────' -28 │ │ id: object::new(&mut ctx), -29 │ │ magic: 42, -30 │ │ strength: 7, -31 │ │ }; - │ ╰─────────' The type 'MyFirstPackage::my_module::Sword' does not have the ability 'drop' +49 │ │ id: object::new(&mut ctx), +50 │ │ magic: 42, +51 │ │ strength: 7, +52 │ │ }; + │ ╰─────────' The type 'my_first_package::my_module::Sword' does not have the ability 'drop' · │ -34 │ assert!(magic(&sword) == 42 && strength(&sword) == 7, 1); +55 │ assert!(magic(&sword) == 42 && strength(&sword) == 7, 1); │ ^ Invalid return ``` @@ -107,7 +106,7 @@ One of the solutions (as suggested in the error message), is to add the `drop` a We now transfer ownership of the `sword` to a freshly created dummy address: ```move -// Create a dummy address and transfer the sword +// Create a dummy address and transfer the sword. let dummy_address = @0xCAFE; transfer::transfer(sword, dummy_address); ``` @@ -115,11 +114,11 @@ transfer::transfer(sword, dummy_address); Run the test command again. Now the output shows a single successful test has run: ```shell -BUILDING MoveStdlib -BUILDING Iota +INCLUDING DEPENDENCY Iota +INCLUDING DEPENDENCY MoveStdlib BUILDING my_first_package Running Move unit tests -[ PASS ] 0x0::my_module::test_sword_create +[ PASS ] 0x0::my_module::test_sword Test result: OK. Total tests: 1; passed: 1; failed: 0 ``` @@ -156,24 +155,20 @@ An instance of the `Scenario` struct contains a per-address object pool emulatin Update your `my_module.move` file to include entry functions callable from IOTA that implement `sword` creation and transfer. With these in place, you can then add a multi-transaction test that uses the `test_scenario` module to test these new capabilities. Put these functions after the accessors (Part 5 in comments). ```move -public fun sword_create(magic: u64, strength: u64, recipient: address, ctx: &mut TxContext) { - - // create a sword +public fun create_sword(magic: u64, strength: u64, recipient: address, ctx: &mut TxContext) { + // Create a sword. let sword = Sword { id: object::new(ctx), magic: magic, strength: strength, }; - // transfer the sword + // Transfer the sword. transfer::transfer(sword, recipient); - } public fun sword_transfer(sword: Sword, recipient: address, _ctx: &mut TxContext) { - - // transfer the sword + // Transfer the sword. transfer::public_transfer(sword, recipient); - } ``` @@ -182,49 +177,49 @@ The code of the new functions uses struct creation and IOTA-internal modules (`T With the new entry functions included, add another test function to make sure they behave as expected. ```move - #[test] - fun test_sword_transactions() { - use iota::test_scenario; - - // create test addresses representing users - let admin = @0xBABE; - let initial_owner = @0xCAFE; - let final_owner = @0xFACE; - - // first transaction to emulate module initialization - let mut scenario_val = test_scenario::begin(admin); - let scenario = &mut scenario_val; - { - init(test_scenario::ctx(scenario)); - }; - // second transaction executed by admin to create the sword - test_scenario::next_tx(scenario, admin); - { - // create the sword and transfer it to the initial owner - sword_create(42, 7, initial_owner, test_scenario::ctx(scenario)); - }; - // third transaction executed by the initial sword owner - test_scenario::next_tx(scenario, initial_owner); - { - // extract the sword owned by the initial owner - let sword = test_scenario::take_from_sender(scenario); - // transfer the sword to the final owner - sword_transfer(sword, final_owner, test_scenario::ctx(scenario)) - }; - // fourth transaction executed by the final sword owner - test_scenario::next_tx(scenario, final_owner); - { - // extract the sword owned by the final owner - let sword = test_scenario::take_from_sender(scenario); - // verify that the sword has expected properties - assert!(magic(&sword) == 42 && strength(&sword) == 7, 1); - // return the sword to the object pool - test_scenario::return_to_sender(scenario, sword) - // or uncomment the line below to destroy the sword instead - // test_utils::destroy(sword) - }; - test_scenario::end(scenario_val); - } +#[test] +fun test_sword_transactions() { + use iota::test_scenario; + + // Create test addresses representing users. + let admin = @0xBABE; + let initial_owner = @0xCAFE; + let final_owner = @0xFACE; + + // First transaction to emulate module initialization. + let mut scenario_val = test_scenario::begin(admin); + let scenario = &mut scenario_val; + { + init(test_scenario::ctx(scenario)); + }; + // Second transaction executed by admin to create a sword. + test_scenario::next_tx(scenario, admin); + { + // Create the sword and transfer it to the initial owner. + create_sword(42, 7, initial_owner, test_scenario::ctx(scenario)); + }; + // Third transaction executed by the initial sword owner. + test_scenario::next_tx(scenario, initial_owner); + { + // Extract the sword owned by the initial owner. + let sword = test_scenario::take_from_sender(scenario); + // Transfer the sword to the final owner. + sword_transfer(sword, final_owner, test_scenario::ctx(scenario)) + }; + // Fourth transaction executed by the final sword owner. + test_scenario::next_tx(scenario, final_owner); + { + // Extract the sword owned by the final owner. + let sword = test_scenario::take_from_sender(scenario); + // Verify that the sword has expected properties. + assert!(magic(&sword) == 42 && strength(&sword) == 7, 1); + // Return the sword to the object pool + test_scenario::return_to_sender(scenario, sword) + // or uncomment the line below to destroy the sword instead. + // test_utils::destroy(sword) + }; + test_scenario::end(scenario_val); +} ``` There are some details of the new testing function to pay attention to. The first thing the code does is create some addresses that represent users participating in the testing scenario. The assumption is that there is one game administrator user and two regular users representing players. The test then creates a scenario by starting the first transaction on behalf of the administrator address. @@ -245,11 +240,11 @@ In the pure Move testing function, the function transfers the `sword` object to Run the test command again to see two successful tests for our module: ```shell -BUILDING Iota -BUILDING MoveStdlib +INCLUDING DEPENDENCY Iota +INCLUDING DEPENDENCY MoveStdlib BUILDING my_first_package Running Move unit tests -[ PASS ] 0x0::my_module::test_sword_create +[ PASS ] 0x0::my_module::test_sword [ PASS ] 0x0::my_module::test_sword_transactions Test result: OK. Total tests: 2; passed: 2; failed: 0 ``` @@ -276,37 +271,20 @@ While the `iota move` command does not support publishing explicitly, you can st The `init` function for the module in the running example creates a `Forge` object. ```move - /// Module initializer to be executed when this module is published - fun init(ctx: &mut TxContext) { - let admin = Forge { - id: object::new(ctx), - swords_created: 0, - }; - - // transfer the forge object to the module/package publisher - transfer::transfer(admin, tx_context::sender(ctx)); - } -``` - -The tests you have so far call the `init` function, but the initializer function itself isn't tested to ensure it properly creates a `Forge` object. To test this functionality, add a `new_sword` function to take the forge as a parameter and to update the number of created swords at the end of the function. If this were an actual module, you'd replace the `create_sword` function with `new_sword`. To keep the existing tests from failing, however, the example uses both functions. +/// Module initializer to be executed when this module is published. +fun init(ctx: &mut TxContext) { + let admin = Forge { + id: object::new(ctx), + swords_created: 0, + }; -```move - /// Constructor for creating swords - public fun new_sword( - forge: &mut Forge, - magic: u64, - strength: u64, - ctx: &mut TxContext, - ): Sword { - forge.swords_created = forge.swords_created + 1; - Sword { - id: object::new(ctx), - magic: magic, - strength: strength, - } - } + // Transfer the forge object to the module/package publisher. + transfer::transfer(admin, tx_context::sender(ctx)); +} ``` +The tests you have so far call the `init` function, but the initializer function itself isn't tested to ensure it properly creates a `Forge` object. + Now, create a function to test the module initialization: ```move @@ -316,27 +294,25 @@ Now, create a function to test the module initialization: #[test] public fun test_module_init() { - let ts = ts::begin(@0x0); + let mut ts = ts::begin(@0x0); - // first transaction to emulate module initialization. + // First transaction to emulate module initialization. + ts::next_tx(&mut ts, ADMIN); { - ts::next_tx(&mut ts, ADMIN); init(ts::ctx(&mut ts)); }; - // second transaction to check if the forge has been created - // and has initial value of zero swords created + // Second transaction to check if the forge has been created and has initial value of zero swords created. + ts::next_tx(&mut ts, ADMIN); { - ts::next_tx(&mut ts, ADMIN); - - // extract the Forge object - let forge: Forge = ts::take_from_sender(&mut ts); + // Extract the Forge object. + let forge: Forge = ts::take_from_sender(&ts); - // verify number of created swords + // Verify number of created swords. assert!(swords_created(&forge) == 0, 1); - // return the Forge object to the object pool - ts::return_to_sender(&mut ts, forge); + // Return the Forge object to the object pool. + ts::return_to_sender(&ts, forge); }; ts::end(ts); @@ -345,6 +321,77 @@ public fun test_module_init() { As the new test function shows, the first transaction (explicitly) calls the initializer. The next transaction checks if the `Forge` object has been created and properly initialized. +Add a `new_sword` function to take the forge as a parameter and to update the number of created swords at the end of the function. If this were an actual module, you'd replace the `create_sword` function with `new_sword`. To keep the existing tests from failing, however, the example uses both functions. + +```move +/// Constructor for creating swords. +public fun new_sword(forge: &mut Forge, magic: u64, strength: u64, ctx: &mut TxContext): Sword { + // Increment the `swords_created` counter. + forge.swords_created = forge.swords_created + 1; + + // Create a sword. + Sword { + id: object::new(ctx), + magic: magic, + strength: strength, + } +} +``` + +Once this function is added, we can use it in the `test_sword_transactions` test. + +```move +#[test] +fun test_sword_transactions() { + use iota::test_scenario; + + // Create test addresses representing users. + let admin = @0xBABE; + let initial_owner = @0xCAFE; + let final_owner = @0xFACE; + + // First transaction to emulate module initialization. + let mut scenario_val = test_scenario::begin(admin); + let scenario = &mut scenario_val; + { + init(test_scenario::ctx(scenario)); + }; + // Second transaction executed by admin to create a sword. + test_scenario::next_tx(scenario, admin); + { + let mut forge = test_scenario::take_from_sender(scenario); + + // Create the sword and transfer it to the initial owner. + let sword = new_sword(&mut forge, 42, 7, test_scenario::ctx(scenario)); + transfer::public_transfer(sword, initial_owner); + + // Return the forge to the sender. + test_scenario::return_to_sender(scenario, forge); + }; + // Third transaction executed by the initial sword owner. + test_scenario::next_tx(scenario, initial_owner); + { + // Extract the sword owned by the initial owner. + let sword = test_scenario::take_from_sender(scenario); + // Transfer the sword to the final owner. + sword_transfer(sword, final_owner, test_scenario::ctx(scenario)) + }; + // Fourth transaction executed by the final sword owner. + test_scenario::next_tx(scenario, final_owner); + { + // Extract the sword owned by the final owner. + let sword = test_scenario::take_from_sender(scenario); + // Verify that the sword has expected properties. + assert!(magic(&sword) == 42 && strength(&sword) == 7, 1); + // Return the sword to the object pool + test_scenario::return_to_sender(scenario, sword) + // or uncomment the line below to destroy the sword instead. + // test_utils::destroy(sword) + }; + test_scenario::end(scenario_val); +} +``` + You can refer to the source code for the package (with all the tests and functions properly adjusted) in the [first_package](https://github.com/iotaledger/iota/tree/develop/examples/move/first_package/sources/example.move) module in the `iota/examples` directory. ## Related links diff --git a/docs/content/developer/getting-started/first-app/debug.mdx b/docs/content/developer/getting-started/first-app/debug.mdx index 47e867b925e..35f1e3c6318 100644 --- a/docs/content/developer/getting-started/first-app/debug.mdx +++ b/docs/content/developer/getting-started/first-app/debug.mdx @@ -34,16 +34,17 @@ Alternatively, any call to abort or assertion failure also prints the stacktrace To see the module in action, update your `my_module` code to include debug calls. Specifically, update the `new_sword` function so that you print the value of `forge` before and after updating `swords_created`. Also, include a `print_stack_trace` so that the function looks like the following: ```move -public fun new_sword( - forge: &mut Forge, - magic: u64, - strength: u64, - ctx: &mut TxContext, -): Sword { +/// Constructor for creating swords. +public fun new_sword(forge: &mut Forge, magic: u64, strength: u64, ctx: &mut TxContext): Sword { debug::print(forge); + + // Increment the `swords_created` counter. forge.swords_created = forge.swords_created + 1; + debug::print(forge); debug::print_stack_trace(); + + // Create a sword. Sword { id: object::new(ctx), magic: magic, @@ -55,7 +56,7 @@ public fun new_sword( To see the results, run the module's tests. ```shell -$ iota move test +$ iota move test test_sword_transactions ``` The response prints out the expected results as the test calls the `new_sword` function. @@ -65,7 +66,6 @@ INCLUDING DEPENDENCY Iota INCLUDING DEPENDENCY MoveStdlib BUILDING my_first_package Running Move unit tests -[ PASS ] 0x0::my_module::test_module_init [debug] 0x0::my_module::Forge { id: 0x2::object::UID { id: 0x2::object::ID { @@ -86,25 +86,29 @@ Call Stack: [0] 0000000000000000000000000000000000000000000000000000000000000000::my_module::test_sword_transactions Code: - [19] LdU64(7) - [20] MutBorrowLoc(3) - [21] Call(14) - > [22] Call(4) - [23] LdConst(1) - [24] CallGeneric(2) - [25] ImmBorrowLoc(3) + [24] LdU64(7) + [25] CopyLoc(5) + [26] Call(13) + > [27] Call(6) + [28] CopyLoc(4) + [29] CallGeneric(2) + [30] CopyLoc(5) Locals: [0] - - [1] { { { } }, 1 } - [2] - - [3] { 2, { 00000000000000000000000000000000000000000000000000000000000000ad, [2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0, 0, 0 } } + [1] - + [2] 000000000000000000000000000000000000000000000000000000000000face + [3] { { { } }, 1 } + [4] 000000000000000000000000000000000000000000000000000000000000cafe + [5] (&) { 1, { 000000000000000000000000000000000000000000000000000000000000babe, [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0, 0, 0 } } + [6] { 1, { 000000000000000000000000000000000000000000000000000000000000babe, [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0, 0, 0 } } + [7] - Operand Stack: [ PASS ] 0x0::my_module::test_sword_transactions -Test result: OK. Total tests: 2; passed: 2; failed: 0 +Test result: OK. Total tests: 1; passed: 1; failed: 0 ``` ## Related links diff --git a/docs/content/developer/getting-started/first-app/publish.mdx b/docs/content/developer/getting-started/first-app/publish.mdx index d68d4940ee9..ca7936608e7 100644 --- a/docs/content/developer/getting-started/first-app/publish.mdx +++ b/docs/content/developer/getting-started/first-app/publish.mdx @@ -44,19 +44,19 @@ $ iota client objects ╭───────────────────────────────────────────────────────────────────────────────────────╮ │ ╭────────────┬──────────────────────────────────────────────────────────────────────╮ │ │ │ objectId │ │ │ -│ │ version │ 10 │ │ +│ │ version │ 3 │ │ │ │ digest │ │ │ │ │ objectType │ ::my_module::Forge │ │ │ ╰────────────┴──────────────────────────────────────────────────────────────────────╯ │ │ ╭────────────┬──────────────────────────────────────────────────────────────────────╮ │ │ │ objectId │ │ │ -│ │ version │ 10 │ │ +│ │ version │ 3 │ │ │ │ digest │ │ │ │ │ objectType │ 0x0000..0002::coin::Coin │ │ │ ╰────────────┴──────────────────────────────────────────────────────────────────────╯ │ │ ╭────────────┬──────────────────────────────────────────────────────────────────────╮ │ │ │ objectId │ │ │ -│ │ version │ 10 │ │ +│ │ version │ 3 │ │ │ │ digest │ │ │ │ │ objectType │ 0x0000..0002::package::UpgradeCap │ │ │ ╰────────────┴──────────────────────────────────────────────────────────────────────╯ │ @@ -124,25 +124,25 @@ After the transaction executes, you can check the status of the `Sword` object b ╭───────────────────────────────────────────────────────────────────────────────────────╮ │ ╭────────────┬──────────────────────────────────────────────────────────────────────╮ │ │ │ objectId │ │ │ -│ │ version │ 11 │ │ +│ │ version │ 4 │ │ │ │ digest │ │ │ │ │ objectType │ ::my_module::Forge │ │ │ ╰────────────┴──────────────────────────────────────────────────────────────────────╯ │ │ ╭────────────┬──────────────────────────────────────────────────────────────────────╮ │ │ │ objectId │ │ │ -│ │ version │ 11 │ │ +│ │ version │ 4 │ │ │ │ digest │ │ │ │ │ objectType │ 0x0000..0002::coin::Coin │ │ │ ╰────────────┴──────────────────────────────────────────────────────────────────────╯ │ │ ╭────────────┬──────────────────────────────────────────────────────────────────────╮ │ │ │ objectId │ │ │ -│ │ version │ 11 │ │ +│ │ version │ 4 │ │ │ │ digest │ │ │ │ │ objectType │ ::my_module::Sword │ │ │ ╰────────────┴──────────────────────────────────────────────────────────────────────╯ │ │ ╭────────────┬──────────────────────────────────────────────────────────────────────╮ │ │ │ objectId │ │ │ -│ │ version │ 10 │ │ +│ │ version │ 3 │ │ │ │ digest │ │ │ │ │ objectType │ 0x0000..0002::package::UpgradeCap │ │ │ ╰────────────┴──────────────────────────────────────────────────────────────────────╯ │ diff --git a/docs/content/developer/getting-started/first-app/write-package.mdx b/docs/content/developer/getting-started/first-app/write-package.mdx index a24307805ec..5a9038936f8 100644 --- a/docs/content/developer/getting-started/first-app/write-package.mdx +++ b/docs/content/developer/getting-started/first-app/write-package.mdx @@ -14,13 +14,12 @@ Running the previous command creates a directory with the name you provide (`my_ ```move title="my_first_package/Move.toml" [package] name = "my_first_package" - -# edition = "2024.beta " # To use the Move 2024 edition, currently in beta +edition = "2024.beta" # edition = "legacy" to use legacy (pre-2024) Move # license = "" # e.g., "MIT", "GPL", "Apache 2.0" # authors = ["..."] # e.g., ["Joe Smith (joesmith@noemail.com)", "John Snow (johnsnow@noemail.com)"] [dependencies] -IOTA = { git = "https://github.com/iotaledger/iota.git", subdir = "crates/iota-framework/packages/iota-framework", rev = "framework/testnet" } +Iota = { git = "https://github.com/iotaledger/iota.git", subdir = "crates/iota-framework/packages/iota-framework", rev = "framework/testnet" } # For remote import, use the `{ git = "...", subdir = "...", rev = "..." }`. # Revision can be a branch, a tag, and a commit hash. @@ -54,7 +53,7 @@ my_first_package = "0x0" The manifest file contents include available sections of the manifest and comments that provide additional information. In Move, you prepend the hash mark (`#`) to a line to denote a comment. - **[package]:** Contains metadata for the package. By default, the `iota move new` command populates only the `name` value of the metadata. In this case, the example passes `my_first_package` to the command, which becomes the name of the package. You can delete the first `#` of subsequent lines of the `[package]` section to provide values for the other available metadata fields. -- **[dependencies]:** Lists the other packages that your package depends on to run. By default, the `iota move new` command lists the `IOTA` package on GitHub (Testnet version) as the lone dependency. +- **[dependencies]:** Lists the other packages that your package depends on to run. By default, the `iota move new` command lists the `Iota` package on GitHub (Testnet version) as the lone dependency. - **[addresses]:** Declares named addresses that your package uses. By default, the section includes the package you create with the `iota move new` command and an address of `0x0`. The publish process replaces the `0x0` address with an actual on-chain address. - **[dev-dependencies]:** Includes only comments that describe the section. - **[dev-addresses]:** Includes only comments that describe the section. @@ -88,7 +87,7 @@ Populate the `my_module.move` file with the following code: ```move module my_first_package::my_module { - // Part 1: Struct definitions + // Part 1: Struct definitions. public struct Sword has key, store { id: UID, magic: u64, @@ -100,17 +99,17 @@ module my_first_package::my_module { swords_created: u64, } - // Part 2: Module initializer to be executed when this module is published + // Part 2: Module initializer to be executed when this module is published. fun init(ctx: &mut TxContext) { let admin = Forge { id: object::new(ctx), swords_created: 0, }; - // Transfer the forge object to the module/package publisher + // Transfer the forge object to the module/package publisher. transfer::public_transfer(admin, tx_context::sender(ctx)); } - // Part 3: Accessors required to read the struct attributes + // Part 3: Accessors required to read the struct attributes. public fun magic(self: &Sword): u64 { self.magic } @@ -123,9 +122,9 @@ module my_first_package::my_module { self.swords_created } - // Part 4: Public/entry functions (introduced later in the tutorial) + // Part 4: Public/entry functions (introduced later in the tutorial). - // Part 5: Private functions (if any) + // Part 5: Private functions (if any). } ``` diff --git a/docs/content/developer/iota-101/access-time.mdx b/docs/content/developer/iota-101/access-time.mdx index 7ab9a6f62fb..58f649c7635 100644 --- a/docs/content/developer/iota-101/access-time.mdx +++ b/docs/content/developer/iota-101/access-time.mdx @@ -24,7 +24,7 @@ module example::clock { use iota::clock::{Self, Clock}; use iota::event; - struct TimeEvent has copy, drop, store { + public struct TimeEvent has copy, drop, store { timestamp_ms: u64 } diff --git a/docs/content/developer/iota-101/create-coin/create-coin.mdx b/docs/content/developer/iota-101/create-coin/create-coin.mdx index 289b4f55f66..a5110b803c9 100644 --- a/docs/content/developer/iota-101/create-coin/create-coin.mdx +++ b/docs/content/developer/iota-101/create-coin/create-coin.mdx @@ -8,15 +8,12 @@ Publishing a coin on IOTA is nearly as straightforward as publishing a new type. ```move module examples::mycoin { - use std::option; - use iota::coin::{Self, Coin, TreasuryCap}; - use iota::transfer; - use iota::tx_context::{Self, TxContext}; + use iota::coin::{Self, TreasuryCap}; /// The type identifier of coin. The coin will have a type /// tag of kind: `Coin` /// Make sure that the name of the type matches the module's name. - struct MYCOIN has drop {} + public struct MYCOIN has drop {} /// Module initializer is called once on module publish. A treasury /// cap is sent to the publisher, who then controls minting and burning @@ -66,13 +63,7 @@ Amount: ## DenyList -The IOTA framework provides a `DenyList` singleton, shared object that the bearer of a `DenyCap` can access to specify a list of addresses that are unable to use a IOTA core type. The initial use case for `DenyList`, however, focuses on limiting access to coins of a specified type. This is useful, for example, when creating a regulated coin on IOTA that requires the ability to block certain addresses from using it as inputs to transactions. Regulated coins on IOTA satisfy any regulations that require the ability to prevent known bad actors from having access to those coins. - -:::info - -The `DenyList` object is a system object that has the address `0x403`. You cannot create it yourself. - -::: +See [`DenyList`](../create-coin/regulated.mdx#deny-list). ## Create regulated coin diff --git a/docs/content/developer/iota-101/create-coin/in-game-token.mdx b/docs/content/developer/iota-101/create-coin/in-game-token.mdx index 90c6f834c55..57b9a224691 100644 --- a/docs/content/developer/iota-101/create-coin/in-game-token.mdx +++ b/docs/content/developer/iota-101/create-coin/in-game-token.mdx @@ -11,9 +11,6 @@ The following example creates an in-game currency called a GEM, which represents /// that sells swords for Gems. Gems are an in-game currency that can be bought /// with IOTA. module examples::sword { - use iota::tx_context::TxContext; - use iota::object::{Self, UID}; - use iota::token::{Self, Token, ActionRequest}; use examples::gem::GEM; @@ -24,7 +21,7 @@ module examples::sword { const SWORD_PRICE: u64 = 10; /// A game item that can be purchased with Gems. - struct Sword has key, store { id: UID } + public struct Sword has key, store { id: UID } /// Purchase a sword with Gems. public fun buy_sword( @@ -44,10 +41,8 @@ module examples::gem { use std::option::none; use std::string::{Self, String}; use iota::iota::IOTA; - use iota::transfer; - use iota::object::{Self, UID}; use iota::balance::{Self, Balance}; - use iota::tx_context::{sender, TxContext}; + use iota::tx_context::sender; use iota::coin::{Self, Coin, TreasuryCap}; use iota::token::{Self, Token, ActionRequest}; @@ -70,7 +65,7 @@ module examples::gem { #[allow(lint(coin_field))] /// Gems can be purchased through the `Store`. - struct GemStore has key { + public struct GemStore has key { id: UID, /// Profits from selling Gems. profits: Balance, @@ -79,7 +74,7 @@ module examples::gem { } /// The OTW to create the in-game currency. - struct GEM has drop {} + public struct GEM has drop {} // In the module initializer we create the in-game currency and define the // rules for different types of actions. @@ -91,7 +86,7 @@ module examples::gem { ); // create a `TokenPolicy` for GEMs - let (policy, cap) = token::new_policy(&treasury_cap, ctx); + let (mut policy, cap) = token::new_policy(&treasury_cap, ctx); token::allow(&mut policy, &cap, buy_action(), ctx); token::allow(&mut policy, &cap, token::spend_action(), ctx); diff --git a/docs/content/developer/iota-101/create-coin/loyalty.mdx b/docs/content/developer/iota-101/create-coin/loyalty.mdx index 2ecdbbb81cd..29ab2135011 100644 --- a/docs/content/developer/iota-101/create-coin/loyalty.mdx +++ b/docs/content/developer/iota-101/create-coin/loyalty.mdx @@ -15,11 +15,7 @@ The following example demonstrates the creation of a loyalty token that bearers /// Actions: /// - spend - spend the token in the shop module examples::loyalty { - use std::option; - use iota::transfer; - use iota::object::{Self, UID}; use iota::coin::{Self, TreasuryCap}; - use iota::tx_context::{Self, TxContext}; use iota::token::{Self, ActionRequest, Token}; @@ -30,15 +26,15 @@ module examples::loyalty { const GIFT_PRICE: u64 = 10; /// The OTW for the Token / Coin. - struct LOYALTY has drop {} + public struct LOYALTY has drop {} /// This is the Rule requirement for the `GiftShop`. The Rules don't need /// to be separate applications, some rules make sense to be part of the /// application itself, like this one. - struct GiftShop has drop {} + public struct GiftShop has drop {} /// The Gift object - can be purchased for 10 tokens. - struct Gift has key, store { + public struct Gift has key, store { id: UID } @@ -55,7 +51,7 @@ module examples::loyalty { ctx ); - let (policy, policy_cap) = token::new_policy(&treasury_cap, ctx); + let (mut policy, policy_cap) = token::new_policy(&treasury_cap, ctx); // but we constrain spend by this shop: token::add_rule_for_action( @@ -99,7 +95,7 @@ module examples::loyalty { assert!(token::value(&token) == GIFT_PRICE, EIncorrectAmount); let gift = Gift { id: object::new(ctx) }; - let req = token::spend(token, ctx); + let mut req = token::spend(token, ctx); // only required because we've set this rule token::add_approval(GiftShop {}, &mut req, ctx); @@ -107,5 +103,4 @@ module examples::loyalty { (gift, req) } } - ``` diff --git a/docs/content/developer/iota-101/create-coin/regulated.mdx b/docs/content/developer/iota-101/create-coin/regulated.mdx index c1e11ee827c..9df655907d7 100644 --- a/docs/content/developer/iota-101/create-coin/regulated.mdx +++ b/docs/content/developer/iota-101/create-coin/regulated.mdx @@ -9,12 +9,9 @@ Behind the scenes, `create_regulated_currency` uses the `create_currency` functi ```move title="regcoin.move" module examples::regcoin { - use std::option; use iota::coin; - use iota::transfer; - use iota::tx_context::{Self, TxContext}; - struct REGCOIN has drop {} + public struct REGCOIN has drop {} fun init(witness: REGCOIN, ctx: &mut TxContext) { let (treasury, deny_cap, metadata) = coin::create_regulated_currency(witness, 6, b"REGCOIN", b"", b"", option::none(), ctx); diff --git a/docs/content/developer/iota-101/iota-move-concepts/patterns.mdx b/docs/content/developer/iota-101/iota-move-concepts/patterns.mdx index 888b59cf36c..8a891d25bab 100644 --- a/docs/content/developer/iota-101/iota-move-concepts/patterns.mdx +++ b/docs/content/developer/iota-101/iota-move-concepts/patterns.mdx @@ -11,7 +11,7 @@ A capability is a pattern that allows authorizing actions with an object. See [C ## Witness and transferrable witness -A witness is a type with `drop` that proves that its owner was present at the time of some privileged operation, for example having access to the one-time witness (OTW) for a module proves that the code is being run at the time the module was first published. See the following topics for details. +A witness is a type with `drop` that proves that its owner was present at the time of some privileged operation, for example having access to the one-time witness ([OTW](../../iota-101/iota-move-concepts/one-time-witness.mdx)) for a module proves that the code is being run at the time the module was first published. See the following topics for details. - [Witness](./patterns/witness) - [Transferrable Witness](./patterns/transferrable-witness) diff --git a/docs/content/developer/iota-101/iota-move-concepts/patterns/capabilities.mdx b/docs/content/developer/iota-101/iota-move-concepts/patterns/capabilities.mdx index fbc37855647..3ca7a8116d9 100644 --- a/docs/content/developer/iota-101/iota-move-concepts/patterns/capabilities.mdx +++ b/docs/content/developer/iota-101/iota-move-concepts/patterns/capabilities.mdx @@ -23,6 +23,13 @@ module examples::item { }, tx_context::sender(ctx)) } - /// The entry function can not be called if `AdminCap` is not passed as + /// The function can not be called if `AdminCap` is not passed as + public fun create_item(_: &AdminCap, name: String, ctx: &mut TxContext): Item { + let item = Item { + id: object::new(ctx), + name, + }; + item + } } ``` diff --git a/docs/content/developer/iota-101/iota-move-concepts/patterns/id-pointer.mdx b/docs/content/developer/iota-101/iota-move-concepts/patterns/id-pointer.mdx index 08a58b4c210..411e98ecb4f 100644 --- a/docs/content/developer/iota-101/iota-move-concepts/patterns/id-pointer.mdx +++ b/docs/content/developer/iota-101/iota-move-concepts/patterns/id-pointer.mdx @@ -14,7 +14,6 @@ The following example implements basic `Lock` and `Key` mechanics on IOTA where ```move module examples::lock_and_key { - /// Lock is empty, nothing to take. const ELockIsEmpty: u64 = 0; @@ -45,9 +44,8 @@ module examples::lock_and_key { /// Lock some content inside a shared object. A Key is created and is /// sent to the transaction sender. For example, we could turn the /// lock into a treasure chest by locking some `Coin` inside. - /// /// Sender gets the `Key` to this `Lock`. - public fun create(obj: T, ctx: &mut TxContext) { + public fun create(obj: T, ctx: &mut TxContext): Key { let id = object::new(ctx); let key_for = object::uid_to_inner(&id); @@ -56,10 +54,11 @@ module examples::lock_and_key { locked: option::some(obj), }); - transfer::transfer(Key { + let key = Key { key_for, id: object::new(ctx) - }, tx_context::sender(ctx)); + }; + key } /// Lock something inside a shared object using a Key. Aborts if diff --git a/docs/content/developer/iota-101/objects/object-model.mdx b/docs/content/developer/iota-101/objects/object-model.mdx index f860eab9fea..87552f2c39f 100644 --- a/docs/content/developer/iota-101/objects/object-model.mdx +++ b/docs/content/developer/iota-101/objects/object-model.mdx @@ -31,7 +31,7 @@ There are a few different ways to concisely refer to an object without specifyin ## The transaction-object DAG: Relating objects and transactions -Transactions take objects as input, read/write/mutate these inputs, and produce mutated or newly created objects as output. Each object knows the (hash of the) last transaction that produced it as an output. Thus, a natural way to represent the relationship between objects and transactions is a directed acyclic graph (DAG) where: +Transactions take objects as input, read/write/mutate these inputs, and produce mutated or newly created objects as output. Each object knows the (hash of the) last transaction that produced it as an output. Thus, a natural way to represent the relationship between objects and transactions is a directed acyclic graph ([DAG](https://en.wikipedia.org/wiki/Directed_acyclic_graph)) where: - Nodes are transactions. - Directed edges go from transaction `A` to transaction `B` if an output object of `A` is an input object of `B`. They are labeled by the reference of the object in question (which specifies the exact version of the object created by `A` and used by `B`). diff --git a/docs/content/developer/iota-101/objects/transfers/transfer-to-object.mdx b/docs/content/developer/iota-101/objects/transfers/transfer-to-object.mdx index af51553f307..55357347d59 100644 --- a/docs/content/developer/iota-101/objects/transfers/transfer-to-object.mdx +++ b/docs/content/developer/iota-101/objects/transfers/transfer-to-object.mdx @@ -178,7 +178,7 @@ Because the `receive_object` function is generic over the object being received, ```move module examples::shared_object_auth { - use iota::transfer::{Self, Receiving}; + use iota::transfer::Receiving; const EAccessDenied: u64 = 0; const AuthorizedReceiverAddr: address = @0xB0B; diff --git a/docs/content/developer/iota-101/transactions/sign-and-send-txn.mdx b/docs/content/developer/iota-101/transactions/sign-and-send-txn.mdx index c0431327a75..77d08e9867a 100644 --- a/docs/content/developer/iota-101/transactions/sign-and-send-txn.mdx +++ b/docs/content/developer/iota-101/transactions/sign-and-send-txn.mdx @@ -20,7 +20,7 @@ With a signature and the transaction bytes, a transaction can be submitted to be The following high-level process describes the overall workflow for constructing, signing and executing an on-chain transaction: - Construct the transaction data by creating a `TransactionBlock` where multiple transactions are chained. See [Building Programmable Transaction Blocks](ptb/building-ptb.mdx) for more information. -- The SDK's built-in gas estimation and coin selection picks the gas coin. +- The [SDK's](../../../references/iota-sdks.mdx) built-in gas estimation and coin selection picks the gas coin. - Sign the transaction to generate a [signature](../../cryptography/transaction-auth/signatures.mdx). - Submit the `TransactionBlock` and its signature for on-chain execution. @@ -40,7 +40,7 @@ The following examples demonstrate how to sign and execute transactions using Ru -There are various ways to instantiate a key pair and to derive its public key and IOTA address using the IOTA TypeScript SDK. +There are various ways to instantiate a key pair and to derive its public key and IOTA address using the [IOTA TypeScript SDK](../../../references/ts-sdk/typescript/index.mdx#iota-typescript-sdk). ```tsx import { fromHEX } from '@iota/bcs'; @@ -108,7 +108,7 @@ console.log(res); The full code example below can be found under [crates/iota-sdk](https://github.com/iotaledger/iota/blob/main/crates/iota-sdk/examples/sign_tx_guide.rs). -There are various ways to instantiate a `IotaKeyPair` and to derive its public key and IOTA address using the IOTA Rust SDK. +There are various ways to instantiate a `IotaKeyPair` and to derive its public key and IOTA address using the [IOTA Rust SDK](../../../references/rust-sdk.mdx#iota-rust-sdk). ```rust // deterministically generate a keypair, testing only, do not use for mainnet, use the next section to randomly generate a keypair instead. diff --git a/docs/content/developer/iota-101/transactions/sponsor-txn.mdx b/docs/content/developer/iota-101/transactions/sponsor-txn.mdx index f621a131568..d131b37a93b 100644 --- a/docs/content/developer/iota-101/transactions/sponsor-txn.mdx +++ b/docs/content/developer/iota-101/transactions/sponsor-txn.mdx @@ -5,7 +5,7 @@ title: Sponsored Transaction Sponsored transactions are a primitive on the IOTA blockchain that enable the execution of a transaction without a user paying the gas. It also discusses the roles in Sponsored Transaction, and a few common use cases. Then it discusses the flow of Sponsored Transaction, mostly for developers who are interested in building a Gas Station or integrate with one. Finally it talks about risk considerations of Sponsored Transaction. # Overview -A transaction on IOTA takes a payment to execute. The payment, also known as gas, is a list of `0x2::coin::Coin<0x2::iota::IOTA>` objects, and paid to IOTA validators to secure the network. Although gas is a critical piece in IOTA tokenomics, it sometimes adds challenges when new users start to navigate on IOTA, especially for web 2.0 users. +A transaction on IOTA takes a payment to execute. The payment, also known as gas, is a list of `0x2::coin::Coin<0x2::iota::IOTA>` objects, and paid to IOTA validators to secure the network. Although gas is a critical piece in [IOTA tokenomics](../../../about-iota/tokenomics/tokenomics.mdx#iota-tokenomics), it sometimes adds challenges when new users start to navigate on IOTA, especially for web 2.0 users. Sponsored transactions can reduce the onboarding friction for users because the feature streamlines the process for end users. Using sponsored transactions, you can execute a transaction without requiring the user to pay it themselves. Instead, you can act as a sponsor of the transaction, offering your own payment gas objects for the transaction. diff --git a/docs/content/developer/iota-101/using-events.mdx b/docs/content/developer/iota-101/using-events.mdx index d1918662458..89831e1cde9 100644 --- a/docs/content/developer/iota-101/using-events.mdx +++ b/docs/content/developer/iota-101/using-events.mdx @@ -124,9 +124,40 @@ Move smart contracts can call other smart contracts that emit events. For exampl This example leverages the IOTA TypeScript SDK to subscribe to events the package with ID `` emits. Each time the event fires, the code displays the response to the console. + + + +### Rust + + + +See [Rust SDK](../../references/rust-sdk.mdx#rust-sdk). + +```rust +use futures::StreamExt; +use iota_sdk::rpc_types::EventFilter; +use iota_sdk::IOTAClientBuilder; +use anyhow::Result; + +#[tokio::main] +async fn main() -> Result<()> { + let iota = IOTAClientBuilder::default() + .ws_url("wss://fullnode.mainnet.iota.io:443") + .build("https://fullnode.mainnet.iota.io:443") + .await.unwrap(); + let mut subscribe_all = iota.event_api().subscribe_event(EventFilter::All(vec![])).await?; + loop { + println!("{:?}", subscribe_all.next().await); + } +} +``` + + + + ### TypeScript -To create the event subscription, you can use a basic Node.js app. You need the IOTA TypeScript SDK, so install the module using `npm install @iota/iota-sdk` at the root of your project. In your TypeScript code, import `JsonRpcProvider` and a connection from the library. +To create the event subscription, you can use a basic Node.js app. You need the [IOTA TypeScript SDK](../../references/ts-sdk/typescript/index.mdx#iota-typescript-sdk), so install the module using `npm install @iota/iota-sdk` at the root of your project. In your TypeScript code, import `JsonRpcProvider` and a connection from the library. ```ts import { JsonRpcProvider, testnetConnection } from '@iota/iota-sdk'; @@ -185,28 +216,8 @@ subscribeEvent { } ``` -### Rust SDK - - - -```rust -use futures::StreamExt; -use iota_sdk::rpc_types::EventFilter; -use iota_sdk::IOTAClientBuilder; -use anyhow::Result; - -#[tokio::main] -async fn main() -> Result<()> { - let iota = IOTAClientBuilder::default() - .ws_url("wss://fullnode.mainnet.iota.io:443") - .build("https://fullnode.mainnet.iota.io:443") - .await.unwrap(); - let mut subscribe_all = iota.event_api().subscribe_event(EventFilter::All(vec![])).await?; - loop { - println!("{:?}", subscribe_all.next().await); - } -} -``` + + ## Filtering event queries