diff --git a/soroban-react-dapp/README.md b/soroban-react-dapp/README.md index 766edd8..3ed1492 100644 --- a/soroban-react-dapp/README.md +++ b/soroban-react-dapp/README.md @@ -43,6 +43,148 @@ The contracts workflow happens in the `contracts/` folder. Here you can see that Every new contract should be in its own folder, and the folder should be named the same name as the name of the contract in its `cargo.toml` file. You can check how the `tweaked_greeting` contract is changed from the `greeting` contract and you can also start from this to build your own contract. +**Challenge contract** + +> _This challenge contract is to add authorization controls to the contract so only specific addresses can modify a title. Implement address management and `required_auth` to verify authorized changes._ + +- `init` instruction + +add init instruction to set admin for contract initial. + +```rust +// initialize the contract and set the admin + pub fn init(env: Env, admin: Address) -> Result<(), Error> { + let storage = env.storage().instance(); + admin.require_auth(); + if storage.has(&Assets::Admin) { + return Err(Error::AlreadyInitialized); + } + storage.set(&Assets::Admin, &admin); + Ok(()) + } +``` + +- `add_editor` instruction + +admin can add editors who can modify the title + +```rust +pub fn add_editor(env: Env, new_editor: Address) -> Result<(), Error>{ + let storage = env.storage().instance(); + let admin: Address = storage.get(&Assets::Admin).unwrap(); + admin.require_auth(); + + let mut editors: Vec
= storage.get(&Assets::Editors).unwrap_or(Vec::new(&env)); + if !editors.contains(&new_editor) { + editors.push_front(new_editor); + env.storage().instance().set(&Assets::Editors, &editors); + Ok(()) + } else { + Err(Error::AlreadyExist) + } + } +``` + +- `remove_editor` instruction + +admin can remove editor from their list. + +```rust +// remove wallets from editors + pub fn remove_editor(env: Env, editor_to_remove: Address) { + let storage = env.storage().instance(); + let admin: Address = storage.get(&Assets::Admin).unwrap(); + admin.require_auth(); + + let mut editors: Vec = storage.get(&Assets::Editors).unwrap_or(Vec::new(&env)); + editors + .first_index_of(&editor_to_remove) + .map(|index| editors.remove(index)); + env.storage().instance().set(&Assets::Editors, &editors); + } +``` + +- `set_title` instruction + +editors can modify the tile, if not editor, it cause UnAthorized Error. + +```rust +// set the title only available editors + pub fn set_title(env: Env, user: Address, title: String) -> Result<(), Error> { + user.require_auth(); + let storage = env.storage().instance(); + let admin: Address = storage.get(&Assets::Admin).unwrap_or_else(|| { + // You can log or handle the error here + // In this case, we'll return an error + return Err(Error::NotInitialized); + })?; + + let editors: Vec = storage.get(&Assets::Editors).unwrap_or(Vec::new(&env)); + if editors.contains(&user) || user.eq(&admin) { + env.storage().instance().set(&Assets::Title, &title); + Ok(()) + } else { + Err(Error::Unauthorized) + } + } +``` + +**Test Contract** + +- `require_auth` test + +```rust +// mofify the title with editors + client.set_title(&new_editor, &String::from_str(&env, "Hello, Stellar")); + + // test for require_auth + assert_eq!( + env.auths(), + std::vec![( + // Address for which authorization check is performed + new_editor.clone(), + // Invocation tree that needs to be authorized + AuthorizedInvocation { + // Function that is authorized. Can be a contract function or + // a host function that requires authorization. + function: AuthorizedFunction::Contract(( + // Address of the called contract + contract_id.clone(), + // Name of the called function + symbol_short!("set_title"), + // Arguments used to call `set_title` + vec![&env, new_editor.to_val(), String::from_str(&env, "Hello, Stellar").into()] + )), + // The contract doesn't call any other contracts that require + // authorization, + sub_invocations: std::vec![] + } + )] + ); +``` + +- `set_title` and `add_editor` test + +```rust +// test either everyone access to modify title or not + let _ = client.try_set_title(&new_editor, &String::from_str(&env, "Hello, Stellar")); + let client_title = client.read_title(); + assert_eq!(client_title, String::from_str(&env, "Default Title")); + +// test with new title + let client_new_title = client.read_title(); + assert_eq!(client_new_title, String::from_str(&env, "Hello, Stellar")); + + // remove editors by admin + let _ = client.remove_editor(&new_editor); + let admins = client.fetch_editors(); + assert_eq!(admins.len(), 1); +``` + + + + + To build the contracts you can simply invoke the `make` command which will recursively build all contracts by propagating the `make` command to subfolders. Each contract needs to have its own `Makefile` for this to work. The `Makefile` from the greeting contract is a generic one and can be copied and paste to use with any of your new contract. If you are not familiar or comfortable with Makefiles you can simply go in the directory of the contract you want to compile and run @@ -96,4 +238,4 @@ You then need to adapt the `contractInvoke()` calls in these functions to match Finally feel, of course, free to change the front-end how you wish, to match your desired functionalities. -*Good luck building!* \ No newline at end of file +*Good luck building!* diff --git a/soroban-react-dapp/contracts/deployments.json b/soroban-react-dapp/contracts/deployments.json index da99780..2c87ca0 100644 --- a/soroban-react-dapp/contracts/deployments.json +++ b/soroban-react-dapp/contracts/deployments.json @@ -7,6 +7,6 @@ { "contractId": "greeting", "networkPassphrase": "Test SDF Network ; September 2015", - "contractAddress": "CDWGVPSUXXSGABQ663FVV4TZJH4Q2R3HVAKTKWFFFMWPF23O7KMNS4KU" + "contractAddress": "CC7LM32RVA7FSWIDQS7DSYAZWKA2NBIS2PSIDX2NZOCKFVZWBMDP3ZV3" } ] \ No newline at end of file diff --git a/soroban-react-dapp/contracts/greeting/src/lib.rs b/soroban-react-dapp/contracts/greeting/src/lib.rs index b6c9990..0f91f60 100755 --- a/soroban-react-dapp/contracts/greeting/src/lib.rs +++ b/soroban-react-dapp/contracts/greeting/src/lib.rs @@ -1,24 +1,106 @@ #![no_std] -use soroban_sdk::{contract, contractimpl, Env, Symbol, symbol_short, String}; - -const TITLE: Symbol = symbol_short!("TITLE"); - +use soroban_sdk::{contract, contracterror, contractimpl, contracttype, Address, Env, String, Vec}; #[contract] pub struct TitleContract; +#[contracterror] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +#[repr(u32)] +pub enum Error { + Unauthorized = 1, + AlreadyInitialized = 2, + NotInitialized = 3, + AlreadyExist = 4, // when editor already exist, add_editor invoke this error +} + #[contractimpl] impl TitleContract { + // initialize the contract and set the admin + pub fn init(env: Env, admin: Address) -> Result<(), Error> { + let storage = env.storage().instance(); + admin.require_auth(); + if storage.has(&Assets::Admin) { + return Err(Error::AlreadyInitialized); + } + storage.set(&Assets::Admin, &admin); + Ok(()) + } - pub fn set_title(env: Env, title: String) { - env.storage().instance().set(&TITLE, &title) + // set the title only available editors + pub fn set_title(env: Env, user: Address, title: String) -> Result<(), Error> { + user.require_auth(); + let storage = env.storage().instance(); + let admin: Address = storage.get(&Assets::Admin).unwrap_or_else(|| { + // You can log or handle the error here + // In this case, we'll return an error + return Err(Error::NotInitialized); + })?; + + let editors: Vec = storage.get(&Assets::Editors).unwrap_or(Vec::new(&env)); + if editors.contains(&user) || user.eq(&admin) { + env.storage().instance().set(&Assets::Title, &title); + Ok(()) + } else { + Err(Error::Unauthorized) + } } + // read the title pub fn read_title(env: Env) -> String { - env.storage().instance().get(&TITLE) + env.storage() + .instance() + .get(&Assets::Title) .unwrap_or(String::from_str(&env, "Default Title")) } - -} + + /// ***** Address Management ***** /// + + // add wallet address for editors + pub fn add_editor(env: Env, new_editor: Address) -> Result<(), Error>{ + let storage = env.storage().instance(); + let admin: Address = storage.get(&Assets::Admin).unwrap(); + admin.require_auth(); + + let mut editors: Vec = storage.get(&Assets::Editors).unwrap_or(Vec::new(&env)); + if !editors.contains(&new_editor) { + editors.push_front(new_editor); + env.storage().instance().set(&Assets::Editors, &editors); + Ok(()) + } else { + Err(Error::AlreadyExist) + } + } + + // remove wallets from editors + pub fn remove_editor(env: Env, editor_to_remove: Address) { + let storage = env.storage().instance(); + let admin: Address = storage.get(&Assets::Admin).unwrap(); + admin.require_auth(); + + let mut editors: Vec = storage.get(&Assets::Editors).unwrap_or(Vec::new(&env)); + editors + .first_index_of(&editor_to_remove) + .map(|index| editors.remove(index)); + env.storage().instance().set(&Assets::Editors, &editors); + } + + // fetch the editor lists + pub fn fetch_editors(env: Env) -> Vec { + let storage = env.storage().instance(); + let admin: Address = storage.get(&Assets::Admin).unwrap(); + let mut editors: Vec = storage.get(&Assets::Editors).unwrap_or(Vec::new(&env)); + editors.push_front(admin); + editors + } +} + +#[derive(Clone)] +#[contracttype] +pub enum Assets { + Admin, + Editors, + Title, +} mod test; diff --git a/soroban-react-dapp/contracts/greeting/src/test.rs b/soroban-react-dapp/contracts/greeting/src/test.rs index b95c919..e0851cf 100755 --- a/soroban-react-dapp/contracts/greeting/src/test.rs +++ b/soroban-react-dapp/contracts/greeting/src/test.rs @@ -1,21 +1,73 @@ #![cfg(test)] -use super::*; -use soroban_sdk::{Env, String}; +extern crate std; +use super::*; +use soroban_sdk::{symbol_short, testutils::{Address as _, AuthorizedFunction, AuthorizedInvocation}, vec, Address, Env, String}; #[test] fn test() { + // test to mock the all auths let env = Env::default(); + env.mock_all_auths(); + let contract_id = env.register_contract(None, TitleContract); let client = TitleContractClient::new(&env, &contract_id); - let client_default_title = client.read_title(); - assert_eq!(client_default_title, String::from_slice(&env, "Default Title")); + let admin = Address::generate(&env); + let new_editor = Address::generate(&env); + + // init by admin + client.init(&admin); + + let client_default_title = client.read_title(); + assert_eq!( + client_default_title, + String::from_str(&env, "Default Title") + ); + + // test either everyone access to modify title or not + let _ = client.try_set_title(&new_editor, &String::from_str(&env, "Hello, Stellar")); + let client_title = client.read_title(); + assert_eq!(client_title, String::from_str(&env, "Default Title")); + + // give edit access + client.add_editor(&new_editor); + + // mofify the title with editors + client.set_title(&new_editor, &String::from_str(&env, "Hello, Stellar")); - client.set_title(&String::from_slice(&env, "My New Title")); - let client_new_title = client.read_title(); + // test for require_auth + assert_eq!( + env.auths(), + std::vec![( + // Address for which authorization check is performed + new_editor.clone(), + // Invocation tree that needs to be authorized + AuthorizedInvocation { + // Function that is authorized. Can be a contract function or + // a host function that requires authorization. + function: AuthorizedFunction::Contract(( + // Address of the called contract + contract_id.clone(), + // Name of the called function + symbol_short!("set_title"), + // Arguments used to call `set_title` + vec![&env, new_editor.to_val(), String::from_str(&env, "Hello, Stellar").into()] + )), + // The contract doesn't call any other contracts that require + // authorization, + sub_invocations: std::vec![] + } + )] + ); - assert_eq!(client_new_title, String::from_slice(&env, "My New Title")); + // test with new title + let client_new_title = client.read_title(); + assert_eq!(client_new_title, String::from_str(&env, "Hello, Stellar")); + // remove editors by admin + let _ = client.remove_editor(&new_editor); + let admins = client.fetch_editors(); + assert_eq!(admins.len(), 1); } diff --git a/soroban-react-dapp/contracts/greeting/test_snapshots/test/test.1.json b/soroban-react-dapp/contracts/greeting/test_snapshots/test/test.1.json new file mode 100644 index 0000000..d48b4d1 --- /dev/null +++ b/soroban-react-dapp/contracts/greeting/test_snapshots/test/test.1.json @@ -0,0 +1,840 @@ +{ + "generators": { + "address": 3, + "nonce": 0 + }, + "auth": [ + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "init", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [], + [], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "add_editor", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "set_title", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "string": "Hello, Stellar" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [], + [ + [ + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + { + "function": { + "contract_fn": { + "contract_address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "function_name": "remove_editor", + "args": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + ] + } + }, + "sub_invocations": [] + } + ] + ], + [] + ], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": [ + { + "key": { + "vec": [ + { + "symbol": "Admin" + } + ] + }, + "val": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + }, + { + "key": { + "vec": [ + { + "symbol": "Editors" + } + ] + }, + "val": { + "vec": [] + } + }, + { + "key": { + "vec": [ + { + "symbol": "Title" + } + ] + }, + "val": { + "string": "Hello, Stellar" + } + } + ] + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 801925984706572462 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 1033654523790656264 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 2032731177588607455 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4", + "key": { + "ledger_key_nonce": { + "nonce": 2032731177588607455 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": 4837995959683129791 + } + }, + "durability": "temporary" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M", + "key": { + "ledger_key_nonce": { + "nonce": 4837995959683129791 + } + }, + "durability": "temporary", + "val": "void" + } + }, + "ext": "v0" + }, + 6311999 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "init" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "init" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "read_title" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "read_title" + } + ], + "data": { + "string": "Default Title" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "set_title" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "string": "Hello, Stellar" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "set_title" + } + ], + "data": { + "error": { + "contract": 1 + } + } + } + } + }, + "failed_call": true + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "error" + }, + { + "error": { + "contract": 1 + } + } + ], + "data": { + "string": "escalating Ok(ScErrorType::Contract) frame-exit to Err" + } + } + } + }, + "failed_call": true + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "error" + }, + { + "error": { + "contract": 1 + } + } + ], + "data": { + "vec": [ + { + "string": "contract try_call failed" + }, + { + "symbol": "set_title" + }, + { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "string": "Hello, Stellar" + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "read_title" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "read_title" + } + ], + "data": { + "string": "Default Title" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "add_editor" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "add_editor" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "set_title" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + }, + { + "string": "Hello, Stellar" + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "set_title" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "read_title" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "read_title" + } + ], + "data": { + "string": "Hello, Stellar" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "remove_editor" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHK3M" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "remove_editor" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "fetch_editors" + } + ], + "data": "void" + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_return" + }, + { + "symbol": "fetch_editors" + } + ], + "data": { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + } + } + }, + "failed_call": false + } + ] +} \ No newline at end of file diff --git a/soroban-react-dapp/contracts/greeting/test_snapshots/test/test_unauthorized_set_greet.1.json b/soroban-react-dapp/contracts/greeting/test_snapshots/test/test_unauthorized_set_greet.1.json new file mode 100644 index 0000000..cf80df5 --- /dev/null +++ b/soroban-react-dapp/contracts/greeting/test_snapshots/test/test_unauthorized_set_greet.1.json @@ -0,0 +1,248 @@ +{ + "generators": { + "address": 4, + "nonce": 0 + }, + "auth": [ + [] + ], + "ledger": { + "protocol_version": 21, + "sequence_number": 0, + "timestamp": 0, + "network_id": "0000000000000000000000000000000000000000000000000000000000000000", + "base_reserve": 0, + "min_persistent_entry_ttl": 4096, + "min_temp_entry_ttl": 16, + "max_entry_ttl": 6312000, + "ledger_entries": [ + [ + { + "contract_data": { + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_data": { + "ext": "v0", + "contract": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "key": "ledger_key_contract_instance", + "durability": "persistent", + "val": { + "contract_instance": { + "executable": { + "wasm": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + "storage": null + } + } + } + }, + "ext": "v0" + }, + 4095 + ] + ], + [ + { + "contract_code": { + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } + }, + [ + { + "last_modified_ledger_seq": 0, + "data": { + "contract_code": { + "ext": "v0", + "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "code": "" + } + }, + "ext": "v0" + }, + 4095 + ] + ] + ] + }, + "events": [ + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "fn_call" + }, + { + "bytes": "0000000000000000000000000000000000000000000000000000000000000001" + }, + { + "symbol": "init" + } + ], + "data": { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "error" + }, + { + "error": { + "auth": "invalid_action" + } + } + ], + "data": { + "vec": [ + { + "string": "Unauthorized function call for address" + }, + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + } + } + }, + "failed_call": true + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "error" + }, + { + "error": { + "auth": "invalid_action" + } + } + ], + "data": { + "string": "escalating error to panic" + } + } + } + }, + "failed_call": true + }, + { + "event": { + "ext": "v0", + "contract_id": "0000000000000000000000000000000000000000000000000000000000000001", + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "error" + }, + { + "error": { + "auth": "invalid_action" + } + } + ], + "data": { + "string": "caught error from function" + } + } + } + }, + "failed_call": true + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "error" + }, + { + "error": { + "auth": "invalid_action" + } + } + ], + "data": { + "vec": [ + { + "string": "contract call failed" + }, + { + "symbol": "init" + }, + { + "vec": [ + { + "address": "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFCT4" + } + ] + } + ] + } + } + } + }, + "failed_call": false + }, + { + "event": { + "ext": "v0", + "contract_id": null, + "type_": "diagnostic", + "body": { + "v0": { + "topics": [ + { + "symbol": "error" + }, + { + "error": { + "auth": "invalid_action" + } + } + ], + "data": { + "string": "escalating error to panic" + } + } + } + }, + "failed_call": false + } + ] +} \ No newline at end of file diff --git a/soroban-react-dapp/contracts/scripts/deploy.ts b/soroban-react-dapp/contracts/scripts/deploy.ts index 8cc5614..19622f4 100644 --- a/soroban-react-dapp/contracts/scripts/deploy.ts +++ b/soroban-react-dapp/contracts/scripts/deploy.ts @@ -1,6 +1,6 @@ -import { Horizon } from '@stellar/stellar-sdk'; +import { Address, Horizon, nativeToScVal, xdr } from '@stellar/stellar-sdk'; import { AddressBook } from '../utils/address_book.js'; -import { airdropAccount, deployContract, installContract} from '../utils/contract.js'; +import { airdropAccount, deployContract, installContract, invokeContract} from '../utils/contract.js'; import { config } from '../utils/env_config.js'; export async function deployContracts(addressBook: AddressBook, contracts_to_deploy: Array- {trimAddress(editor)}
+ {i !== 0 && } +- {d.networkPassphrase}
- )) + )) }- + {contractAddressStored ? {contractAddressStored} : "Loading address.."}