From 9cd3487cd18e359089bfc3670f9b9d85dedee7b2 Mon Sep 17 00:00:00 2001 From: garikbesson Date: Mon, 25 Aug 2025 16:20:08 +0100 Subject: [PATCH 01/13] add option to deploy a contract as a global (as account_id or hash) --- Cargo.toml | 4 +- src/deploy.rs | 16 +++--- src/deploy_as_global_account_id.rs | 72 +++++++++++++++++++++++++++ src/deploy_as_global_hash.rs | 78 ++++++++++++++++++++++++++++++ src/lib.rs | 2 + 5 files changed, 161 insertions(+), 11 deletions(-) create mode 100644 src/deploy_as_global_account_id.rs create mode 100644 src/deploy_as_global_hash.rs diff --git a/Cargo.toml b/Cargo.toml index a856f10..f21ae8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,10 +13,10 @@ crate-type = ["cdylib", "rlib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -near-sdk = { version = "5.3.0", features = ["unstable"] } +near-sdk = { version = "5.17.1", features = ["global-contracts", "unstable"] } [dev-dependencies] -near-sdk = { version = "5.3.0", features = ["unit-testing"] } +near-sdk = { version = "5.17.1", features = ["global-contracts", "unit-testing"] } near-workspaces = { version = "0.16.0", features = ["unstable"] } tokio = { version = "1.12.0", features = ["full"] } serde_json = "1" diff --git a/src/deploy.rs b/src/deploy.rs index 413d179..b01dff2 100644 --- a/src/deploy.rs +++ b/src/deploy.rs @@ -12,7 +12,7 @@ struct DonationInitArgs { #[near] impl Contract { #[payable] - pub fn create_factory_subaccount_and_deploy( + pub fn deploy( &mut self, name: String, beneficiary: AccountId, @@ -58,17 +58,15 @@ impl Contract { } // Add callback - promise.then( - Self::ext(env::current_account_id()).create_factory_subaccount_and_deploy_callback( - subaccount, - env::predecessor_account_id(), - attached, - ), - ) + promise.then(Self::ext(env::current_account_id()).deploy_callback( + subaccount, + env::predecessor_account_id(), + attached, + )) } #[private] - pub fn create_factory_subaccount_and_deploy_callback( + pub fn deploy_callback( &mut self, account: AccountId, user: AccountId, diff --git a/src/deploy_as_global_account_id.rs b/src/deploy_as_global_account_id.rs new file mode 100644 index 0000000..86bca1a --- /dev/null +++ b/src/deploy_as_global_account_id.rs @@ -0,0 +1,72 @@ +use near_sdk::{env, log, near, AccountId, NearToken, Promise, PromiseError, PublicKey}; + +use crate::{Contract, ContractExt, NEAR_PER_STORAGE}; + +#[near] +impl Contract { + #[payable] + pub fn deploy_as_global_account_id( + &mut self, + name: String, + public_key: Option, + ) -> Promise { + // Assert the sub-account is valid + let current_account = env::current_account_id().to_string(); + let subaccount: AccountId = format!("{name}.{current_account}").parse().unwrap(); + assert!( + env::is_valid_account_id(subaccount.as_bytes()), + "Invalid subaccount" + ); + + let code = self.code.clone().unwrap(); + + // Assert enough tokens are attached to create the account and deploy the contract + let attached = env::attached_deposit(); + let contract_bytes = code.len() as u128; + let contract_deploying_cost = NEAR_PER_STORAGE + .saturating_mul(contract_bytes) + .saturating_mul(10); // The cost per byte of global contract code is set as 10x the storage staking cost per byte + + // Require a little more since storage cost is not exact + // let minimum_needed = contract_deploying_cost.saturating_add(NearToken::from_millinear(100)); + assert!( + attached >= contract_deploying_cost, + "Attach at least {contract_deploying_cost} yⓃ" + ); + + let pk = public_key.unwrap_or(env::signer_account_pk()); + + let promise = Promise::new(subaccount.clone()) + .create_account() + .transfer(env::attached_deposit()) + .add_full_access_key(pk) + .deploy_global_contract_by_account_id(code); + + // Add callback + promise.then( + Self::ext(env::current_account_id()).deploy_as_global_account_id_callback( + subaccount, + env::predecessor_account_id(), + attached, + ), + ) + } + + #[private] + pub fn deploy_as_global_account_id_callback( + &mut self, + account: AccountId, + user: AccountId, + attached: NearToken, + #[callback_result] create_deploy_result: Result<(), PromiseError>, + ) -> bool { + if let Ok(_result) = create_deploy_result { + log!("Correctly created and deployed to {account}"); + return true; + }; + + log!("Error creating {account}, returning {attached}yⓃ to {user}"); + Promise::new(user).transfer(attached); + false + } +} diff --git a/src/deploy_as_global_hash.rs b/src/deploy_as_global_hash.rs new file mode 100644 index 0000000..5efcf1f --- /dev/null +++ b/src/deploy_as_global_hash.rs @@ -0,0 +1,78 @@ +use near_sdk::{ + env, json_types::Base58CryptoHash, log, near, AccountId, CryptoHash, NearToken, Promise, + PromiseError, PublicKey, +}; + +use crate::{Contract, ContractExt, NEAR_PER_STORAGE}; + +#[near] +impl Contract { + #[payable] + pub fn deploy_as_global_hash( + &mut self, + name: String, + public_key: Option, + ) -> Promise { + // Assert the sub-account is valid + let current_account = env::current_account_id().to_string(); + let subaccount: AccountId = format!("{name}.{current_account}").parse().unwrap(); + assert!( + env::is_valid_account_id(subaccount.as_bytes()), + "Invalid subaccount" + ); + + let code = self.code.clone().unwrap(); + + // Assert enough tokens are attached to create the account and deploy the contract + let attached = env::attached_deposit(); + let contract_bytes = code.len() as u128; + let contract_deploying_cost = NEAR_PER_STORAGE + .saturating_mul(contract_bytes) + .saturating_mul(10); // The cost per byte of global contract code is set as 10x the storage staking cost per byte + + assert!( + attached >= contract_deploying_cost, + "Attach at least {contract_deploying_cost} yⓃ" + ); + + let hash: CryptoHash = env::sha256(&code).try_into().unwrap(); + let hash_str = String::from(&Base58CryptoHash::from(hash)); + + let pk = public_key.unwrap_or(env::signer_account_pk()); + + let promise = Promise::new(subaccount.clone()) + .create_account() + .transfer(env::attached_deposit()) + .add_full_access_key(pk) + .deploy_global_contract(code); + + // Add callback + promise.then( + Self::ext(env::current_account_id()).deploy_as_global_hash_callback( + subaccount, + env::predecessor_account_id(), + attached, + hash_str, + ), + ) + } + + #[private] + pub fn deploy_as_global_hash_callback( + &mut self, + account: AccountId, + user: AccountId, + attached: NearToken, + hash: String, + #[callback_result] create_deploy_result: Result<(), PromiseError>, + ) -> bool { + if let Ok(_result) = create_deploy_result { + log!("Correctly created and deployed. Contract hash: {:?}", hash); + return true; + }; + + log!("Error creating {account}, returning {attached}yⓃ to {user}"); + Promise::new(user).transfer(attached); + false + } +} diff --git a/src/lib.rs b/src/lib.rs index 2e24752..d3a862a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,8 @@ use near_sdk::store::LazyOption; use near_sdk::{near, Gas, NearToken}; mod deploy; +mod deploy_as_global_account_id; +mod deploy_as_global_hash; mod manager; const NEAR_PER_STORAGE: NearToken = NearToken::from_yoctonear(10u128.pow(19)); // 10e19yⓃ From c4fb6fc23d11ae273f3f90a19ff3973d876eb34e Mon Sep 17 00:00:00 2001 From: garikbesson Date: Tue, 26 Aug 2025 11:27:13 +0100 Subject: [PATCH 02/13] fix tests --- Cargo.toml | 2 +- tests/sandbox.rs | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f21ae8e..f4b5c9a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ near-sdk = { version = "5.17.1", features = ["global-contracts", "unstable"] } [dev-dependencies] near-sdk = { version = "5.17.1", features = ["global-contracts", "unit-testing"] } -near-workspaces = { version = "0.16.0", features = ["unstable"] } +near-workspaces = { version = "0.21.0", features = ["unstable"] } tokio = { version = "1.12.0", features = ["full"] } serde_json = "1" diff --git a/tests/sandbox.rs b/tests/sandbox.rs index 69f8481..0362055 100644 --- a/tests/sandbox.rs +++ b/tests/sandbox.rs @@ -5,7 +5,7 @@ const TEN_NEAR: NearToken = NearToken::from_near(10); #[tokio::test] async fn main() -> Result<(), Box> { - let sandbox = near_workspaces::sandbox().await?; + let sandbox = near_workspaces::sandbox_with_version("2.7.0").await?; let root = sandbox.root_account()?; // Create accounts @@ -15,15 +15,20 @@ async fn main() -> Result<(), Box> { let contract_wasm = near_workspaces::compile_project("./").await?; let contract = sandbox.dev_deploy(&contract_wasm).await?; + let res_0 = alice.view(contract.id(), "get_code").args_json({}).await?; + + println!("{:?}", res_0); + // Launch new donation contract through factory let res = alice - .call(contract.id(), "create_factory_subaccount_and_deploy") + .call(contract.id(), "deploy") .args_json(json!({"name": "donation_for_alice", "beneficiary": alice.id()})) .max_gas() .deposit(NearToken::from_millinear(1700)) .transact() .await?; + println!("{:?}", res); assert!(res.is_success()); let sub_accountid: AccountId = format!("donation_for_alice.{}", contract.id()) @@ -49,7 +54,7 @@ async fn main() -> Result<(), Box> { // Try to create new donation contract with insufficient deposit let res = alice - .call(contract.id(), "create_factory_subaccount_and_deploy") + .call(contract.id(), "deploy") .args_json(json!({"name": "donation_for_alice_2", "beneficiary": alice.id()})) .max_gas() .deposit(NearToken::from_millinear(1500)) @@ -73,4 +78,4 @@ async fn create_subaccount( .unwrap(); Ok(subaccount) -} \ No newline at end of file +} From 101e343d880cb207f51f575cb34de9dd01f094f1 Mon Sep 17 00:00:00 2001 From: garikbesson Date: Tue, 26 Aug 2025 16:04:06 +0100 Subject: [PATCH 03/13] replace example with code from near-sdk-rs example and improve it --- Cargo.toml | 14 +- README.md | 209 ++++------------ src/deploy.rs | 85 ------- src/deploy_as_global_account_id.rs | 72 ------ src/deploy_as_global_hash.rs | 78 ------ src/donation-contract/donation.wasm | Bin 154707 -> 0 bytes src/lib.rs | 222 +++++++++++++++-- src/manager.rs | 19 -- .../status_message.wasm | Bin 0 -> 81689 bytes tests/sandbox.rs | 81 ------- tests/workspaces.rs | 223 ++++++++++++++++++ 11 files changed, 468 insertions(+), 535 deletions(-) delete mode 100644 src/deploy.rs delete mode 100644 src/deploy_as_global_account_id.rs delete mode 100644 src/deploy_as_global_hash.rs delete mode 100755 src/donation-contract/donation.wasm delete mode 100644 src/manager.rs create mode 100644 src/status-message-contract/status_message.wasm delete mode 100644 tests/sandbox.rs create mode 100644 tests/workspaces.rs diff --git a/Cargo.toml b/Cargo.toml index f4b5c9a..ffa7069 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,32 +1,24 @@ [package] -name = "contract" -description = "Factory Contract Example" +name = "factory-contract-global" version = "0.1.0" edition = "2021" -# TODO: Fill out the repository field to help NEAR ecosystem tools to discover your project. -# NEP-0330 is automatically implemented for all contracts built with https://github.com/near/cargo-near. -# Link to the repository will be available via `contract_source_metadata` view-function. -#repository = "https://github.com/xxx/xxx" [lib] crate-type = ["cdylib", "rlib"] -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] near-sdk = { version = "5.17.1", features = ["global-contracts", "unstable"] } [dev-dependencies] near-sdk = { version = "5.17.1", features = ["global-contracts", "unit-testing"] } -near-workspaces = { version = "0.21.0", features = ["unstable"] } +near-workspaces = { version = "0.21", features = ["unstable"] } tokio = { version = "1.12.0", features = ["full"] } -serde_json = "1" +anyhow = "1.0" [profile.release] codegen-units = 1 -# Tell `rustc` to optimize for small code size. opt-level = "z" lto = true debug = false panic = "abort" -# Opt into extra safety checks on arithmetic operations https://stackoverflow.com/a/64136471/249801 overflow-checks = true diff --git a/README.md b/README.md index 9d6c435..4adc494 100644 --- a/README.md +++ b/README.md @@ -1,198 +1,77 @@ -# Factory Contract Example - -A factory is a smart contract that stores a compiled contract on itself, and -automatizes deploying it into sub-accounts. - -This particular example presents a factory of donation contracts, and enables -to: - -1. Create a sub-account of the factory and deploy the stored contract on it - (create_factory_subaccount_and_deploy). -2. Change the stored contract using the update_stored_contract method. - -```rust -#[payable] - pub fn create_factory_subaccount_and_deploy( - &mut self, - name: String, - beneficiary: AccountId, - public_key: Option, - ) -> Promise { - // Assert the sub-account is valid - let current_account = env::current_account_id().to_string(); - let subaccount: AccountId = format!("{name}.{current_account}").parse().unwrap(); - assert!( - env::is_valid_account_id(subaccount.as_bytes()), - "Invalid subaccount" - ); - - // Assert enough tokens are attached to create the account and deploy the contract - let attached = env::attached_deposit(); - - let code = self.code.clone().unwrap(); - let contract_bytes = code.len() as u128; - let minimum_needed = NEAR_PER_STORAGE.saturating_mul(contract_bytes); - assert!( - attached >= minimum_needed, - "Attach at least {minimum_needed} yⓃ" - ); - - let init_args = near_sdk::serde_json::to_vec(&DonationInitArgs { beneficiary }).unwrap(); - - let mut promise = Promise::new(subaccount.clone()) - .create_account() - .transfer(attached) - .deploy_contract(code) - .function_call( - "init".to_owned(), - init_args, - NO_DEPOSIT, - TGAS.saturating_mul(5), - ); - - // Add full access key is the user passes one - if let Some(pk) = public_key { - promise = promise.add_full_access_key(pk); - } - - // Add callback - promise.then( - Self::ext(env::current_account_id()).create_factory_subaccount_and_deploy_callback( - subaccount, - env::predecessor_account_id(), - attached, - ), - ) - } -``` +# Factory Contract with Global Contracts Example -## How to Build Locally? +This example demonstrates how to use NEAR's global contract functionality to deploy and use global smart contracts. -Install [`cargo-near`](https://github.com/near/cargo-near) and run: +Global contracts allow sharing contract code globally across the NEAR network, reducing deployment costs and enabling efficient code reuse. -```bash -cargo near build -``` +## Key Features -## How to Test Locally? +- Deploy a global contract using `deploy_global_contract()` +- Use an existing global contract by hash with `use_global_contract()` +- Use an existing global contract by deployer account with `use_global_contract_by_account_id()` +- Integration tests using near-workspaces -```bash -cargo test -``` +## Install `cargo-near` build tool -## How to Deploy? +See [`cargo-near` installation](https://github.com/near/cargo-near#installation) -Deployment is automated with GitHub Actions CI/CD pipeline. To deploy manually, -install [`cargo-near`](https://github.com/near/cargo-near) and run: +## Build with: ```bash -cargo near deploy +cargo near build ``` -## How to Interact? - -_In this example we will be using [NEAR CLI](https://github.com/near/near-cli) -to intract with the NEAR blockchain and the smart contract_ - -_If you want full control over of your interactions we recommend using the -[near-cli-rs](https://near.cli.rs)._ - -### Deploy the Stored Contract Into a Sub-Account - -`create_factory_subaccount_and_deploy` will create a sub-account of the factory -and deploy the stored contract on it. +## Run Tests: +### Unit Tests ```bash -near call create_factory_subaccount_and_deploy '{ "name": "sub", "beneficiary": ""}' --deposit 1.24 --accountId --gas 300000000000000 +cargo test ``` -This will create the `sub.`, which will have a `donation` -contract deployed on it: - +### Integration Tests ```bash -near view sub. get_beneficiary -# expected response is: +cargo test --test workspaces +cargo test --test realistic ``` -### Update the Stored Contract - -`update_stored_contract` enables to change the compiled contract that the -factory stores. - -The method is interesting because it has no declared parameters, and yet it -takes an input: the new contract to store as a stream of bytes. - -To use it, we need to transform the contract we want to store into its `base64` -representation, and pass the result as input to the method: +## Create testnet dev-account: ```bash -# Use near-cli to update stored contract -export BYTES=`cat ./src/to/new-contract/contract.wasm | base64` -near call update_stored_contract "$BYTES" --base64 --accountId --gas 30000000000000 +cargo near create-dev-account ``` -> This works because the arguments of a call can be either a `JSON` object or a -> `String Buffer` - -## Factories - Explanations & Limitations +## Deploy to dev-account: -Factories are an interesting concept, here we further explain some of their -implementation aspects, as well as their limitations. +```bash +cargo near deploy +``` -
+## How Global Contracts Work -### Automatically Creating Accounts +1. **Deploy Global Contract**: A contract deploys bytecode as a global contract, making it available network-wide +2. **Use by Hash**: Other contracts can reference the global contract by its code hash +3. **Use by Account**: Contracts can reference a global contract by the account that deployed it -NEAR accounts can only create sub-accounts of themselves, therefore, the -`factory` can only create and deploy contracts on its own sub-accounts. +This reduces storage costs and enables code sharing across the ecosystem. -This means that the factory: +## Use Cases from NEP-591 -1. **Can** create `sub.factory.testnet` and deploy a contract on it. -2. **Cannot** create sub-accounts of the `predecessor`. -3. **Can** create new accounts (e.g. `account.testnet`), but **cannot** deploy - contracts on them. +- **Multisig Contracts**: Deploy once, use for many wallets without paying 3N each time +- **Smart Contract Wallets**: Efficient user onboarding with chain signatures +- **Business Onboarding**: Companies can deploy user accounts cost-effectively +- **DeFi Templates**: Share common contract patterns across protocols -It is important to remember that, while `factory.testnet` can create -`sub.factory.testnet`, it has no control over it after its creation. +## Runtime Requirements -### The Update Method +⚠️ **Important**: Global contracts are not yet available in released versions of nearcore. -The `update_stored_contracts` has a very short implementation: +- **Current Status**: Global contract host functions are implemented in nearcore but will first be available in version 2.7.0 +- **SDK Status**: This near-sdk-rs implementation is ready and waiting for runtime support +- **Testing**: Integration tests require a custom nearcore build with global contract support -```rust -#[private] - pub fn update_stored_contract(&mut self) { - self.code.set(env::input()); - } -``` +### When Available -On first sight it looks like the method takes no input parameters, but we can -see that its only line of code reads from `env::input()`. What is happening here -is that `update_stored_contract` **bypasses** the step of **deserializing the -input**. - -You could implement `update_stored_contract(&mut self, new_code: Vec)`, -which takes the compiled code to store as a `Vec`, but that would trigger -the contract to: - -1. Deserialize the `new_code` variable from the input. -2. Sanitize it, making sure it is correctly built. - -When dealing with big streams of input data (as is the compiled `wasm` file to -be stored), this process of deserializing/checking the input ends up **consuming -the whole GAS** for the transaction. - -## Useful Links - -- [cargo-near](https://github.com/near/cargo-near) - NEAR smart contract - development toolkit for Rust -- [near CLI-rs](https://near.cli.rs) - Iteract with NEAR blockchain from command - line -- [NEAR Rust SDK Documentation](https://docs.near.org/sdk/rust/introduction) -- [NEAR Documentation](https://docs.near.org) -- [NEAR StackOverflow](https://stackoverflow.com/questions/tagged/nearprotocol) -- [NEAR Discord](https://near.chat) -- [NEAR Telegram Developers Community Group](https://t.me/neardev) -- NEAR DevHub: [Telegram](https://t.me/neardevhub), - [Twitter](https://twitter.com/neardevhub) +Once nearcore 2.7.0 is released, you'll be able to: +- Deploy global contracts on mainnet and testnet +- Run integration tests with near-workspaces using version "2.7.0" or later +- Use all the functionality demonstrated in this example \ No newline at end of file diff --git a/src/deploy.rs b/src/deploy.rs deleted file mode 100644 index b01dff2..0000000 --- a/src/deploy.rs +++ /dev/null @@ -1,85 +0,0 @@ -use near_sdk::serde::Serialize; -use near_sdk::{env, log, near, AccountId, NearToken, Promise, PromiseError, PublicKey}; - -use crate::{Contract, ContractExt, NEAR_PER_STORAGE, NO_DEPOSIT, TGAS}; - -#[derive(Serialize)] -#[serde(crate = "near_sdk::serde")] -struct DonationInitArgs { - beneficiary: AccountId, -} - -#[near] -impl Contract { - #[payable] - pub fn deploy( - &mut self, - name: String, - beneficiary: AccountId, - public_key: Option, - ) -> Promise { - // Assert the sub-account is valid - let current_account = env::current_account_id().to_string(); - let subaccount: AccountId = format!("{name}.{current_account}").parse().unwrap(); - assert!( - env::is_valid_account_id(subaccount.as_bytes()), - "Invalid subaccount" - ); - - // Assert enough tokens are attached to create the account and deploy the contract - let attached = env::attached_deposit(); - - let code = self.code.clone().unwrap(); - let contract_bytes = code.len() as u128; - let contract_storage_cost = NEAR_PER_STORAGE.saturating_mul(contract_bytes); - // Require a little more since storage cost is not exact - let minimum_needed = contract_storage_cost.saturating_add(NearToken::from_millinear(100)); - assert!( - attached >= minimum_needed, - "Attach at least {minimum_needed} yⓃ" - ); - - let init_args = near_sdk::serde_json::to_vec(&DonationInitArgs { beneficiary }).unwrap(); - - let mut promise = Promise::new(subaccount.clone()) - .create_account() - .transfer(attached) - .deploy_contract(code) - .function_call( - "init".to_owned(), - init_args, - NO_DEPOSIT, - TGAS.saturating_mul(5), - ); - - // Add full access key is the user passes one - if let Some(pk) = public_key { - promise = promise.add_full_access_key(pk); - } - - // Add callback - promise.then(Self::ext(env::current_account_id()).deploy_callback( - subaccount, - env::predecessor_account_id(), - attached, - )) - } - - #[private] - pub fn deploy_callback( - &mut self, - account: AccountId, - user: AccountId, - attached: NearToken, - #[callback_result] create_deploy_result: Result<(), PromiseError>, - ) -> bool { - if let Ok(_result) = create_deploy_result { - log!("Correctly created and deployed to {account}"); - return true; - }; - - log!("Error creating {account}, returning {attached}yⓃ to {user}"); - Promise::new(user).transfer(attached); - false - } -} diff --git a/src/deploy_as_global_account_id.rs b/src/deploy_as_global_account_id.rs deleted file mode 100644 index 86bca1a..0000000 --- a/src/deploy_as_global_account_id.rs +++ /dev/null @@ -1,72 +0,0 @@ -use near_sdk::{env, log, near, AccountId, NearToken, Promise, PromiseError, PublicKey}; - -use crate::{Contract, ContractExt, NEAR_PER_STORAGE}; - -#[near] -impl Contract { - #[payable] - pub fn deploy_as_global_account_id( - &mut self, - name: String, - public_key: Option, - ) -> Promise { - // Assert the sub-account is valid - let current_account = env::current_account_id().to_string(); - let subaccount: AccountId = format!("{name}.{current_account}").parse().unwrap(); - assert!( - env::is_valid_account_id(subaccount.as_bytes()), - "Invalid subaccount" - ); - - let code = self.code.clone().unwrap(); - - // Assert enough tokens are attached to create the account and deploy the contract - let attached = env::attached_deposit(); - let contract_bytes = code.len() as u128; - let contract_deploying_cost = NEAR_PER_STORAGE - .saturating_mul(contract_bytes) - .saturating_mul(10); // The cost per byte of global contract code is set as 10x the storage staking cost per byte - - // Require a little more since storage cost is not exact - // let minimum_needed = contract_deploying_cost.saturating_add(NearToken::from_millinear(100)); - assert!( - attached >= contract_deploying_cost, - "Attach at least {contract_deploying_cost} yⓃ" - ); - - let pk = public_key.unwrap_or(env::signer_account_pk()); - - let promise = Promise::new(subaccount.clone()) - .create_account() - .transfer(env::attached_deposit()) - .add_full_access_key(pk) - .deploy_global_contract_by_account_id(code); - - // Add callback - promise.then( - Self::ext(env::current_account_id()).deploy_as_global_account_id_callback( - subaccount, - env::predecessor_account_id(), - attached, - ), - ) - } - - #[private] - pub fn deploy_as_global_account_id_callback( - &mut self, - account: AccountId, - user: AccountId, - attached: NearToken, - #[callback_result] create_deploy_result: Result<(), PromiseError>, - ) -> bool { - if let Ok(_result) = create_deploy_result { - log!("Correctly created and deployed to {account}"); - return true; - }; - - log!("Error creating {account}, returning {attached}yⓃ to {user}"); - Promise::new(user).transfer(attached); - false - } -} diff --git a/src/deploy_as_global_hash.rs b/src/deploy_as_global_hash.rs deleted file mode 100644 index 5efcf1f..0000000 --- a/src/deploy_as_global_hash.rs +++ /dev/null @@ -1,78 +0,0 @@ -use near_sdk::{ - env, json_types::Base58CryptoHash, log, near, AccountId, CryptoHash, NearToken, Promise, - PromiseError, PublicKey, -}; - -use crate::{Contract, ContractExt, NEAR_PER_STORAGE}; - -#[near] -impl Contract { - #[payable] - pub fn deploy_as_global_hash( - &mut self, - name: String, - public_key: Option, - ) -> Promise { - // Assert the sub-account is valid - let current_account = env::current_account_id().to_string(); - let subaccount: AccountId = format!("{name}.{current_account}").parse().unwrap(); - assert!( - env::is_valid_account_id(subaccount.as_bytes()), - "Invalid subaccount" - ); - - let code = self.code.clone().unwrap(); - - // Assert enough tokens are attached to create the account and deploy the contract - let attached = env::attached_deposit(); - let contract_bytes = code.len() as u128; - let contract_deploying_cost = NEAR_PER_STORAGE - .saturating_mul(contract_bytes) - .saturating_mul(10); // The cost per byte of global contract code is set as 10x the storage staking cost per byte - - assert!( - attached >= contract_deploying_cost, - "Attach at least {contract_deploying_cost} yⓃ" - ); - - let hash: CryptoHash = env::sha256(&code).try_into().unwrap(); - let hash_str = String::from(&Base58CryptoHash::from(hash)); - - let pk = public_key.unwrap_or(env::signer_account_pk()); - - let promise = Promise::new(subaccount.clone()) - .create_account() - .transfer(env::attached_deposit()) - .add_full_access_key(pk) - .deploy_global_contract(code); - - // Add callback - promise.then( - Self::ext(env::current_account_id()).deploy_as_global_hash_callback( - subaccount, - env::predecessor_account_id(), - attached, - hash_str, - ), - ) - } - - #[private] - pub fn deploy_as_global_hash_callback( - &mut self, - account: AccountId, - user: AccountId, - attached: NearToken, - hash: String, - #[callback_result] create_deploy_result: Result<(), PromiseError>, - ) -> bool { - if let Ok(_result) = create_deploy_result { - log!("Correctly created and deployed. Contract hash: {:?}", hash); - return true; - }; - - log!("Error creating {account}, returning {attached}yⓃ to {user}"); - Promise::new(user).transfer(attached); - false - } -} diff --git a/src/donation-contract/donation.wasm b/src/donation-contract/donation.wasm deleted file mode 100755 index 561ae4efb811b14be4b5d8a5675e82ec9c72448d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 154707 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
&4J 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�arc7)`7yiA@J!?QS9#DPctUPd7*E?;7#Xe5n+G{Fb{IH-klVuGJ6o z*P{enwV~W_t!B4RqyIBe;|cJo4E9EJi)}-Ij}_~pxn9K8l*rpACvS81E-i1H5_wxU zq12L=w>;OuOHAI9$mFf1smNPPVKm&7lDADJZ!M*hw{bF)w@s3_Eh;%zmAAWo0t2Zm zgnLX+(WL0bnzt`%ghr+4CN(ih3yOx7Wlc_&84QUm6BIUfoK%v9BssPNe$5<*uT5bJ zHc5`}pI57QAf~V>+*HSCak4S8kq|l@*4_onG%1$h2?pKC@!?MT6k?|gafOR<`BIBS zT`Yj=#63wa?#Z~tvB>$l*06)3oiz=UizgGY6i=#qYhyo4rRn>yx%mFg2zWf#af#;V zfn-XJNW`3p2t@f79mnmMkCHkA$%JFQWFH!|2}dzbNlQ2+#C$ExlAuU%QjSkc6AZi( z44jf4<|>toi9TZRCQQ`ejng(6sPO9{Fq7@++HA%k3wLDL7r0hipvJt|BA1Q2i`5C9 zVT#5uxoh>W=Oce5v=~eIGcgkJ<`Wu3C|46UE{Q0htTN6-L2w_05qyM_BcWe{DCku5 z#8l)J?s(=4NL5b7Ts64>EEj1-dNX;^JFx3P8g{D|<|QVtrY9L|ga={rHY&uyx9}_M z+(yt4o4nNTvMRu-5@1A_>#7|k)=h`Z%xJxOZpVpS(T&w(WM-kbraUV>T)81fbo0+N zLK1scZ=N%pr``~?royAmHA%6aoQz^uj%Xh+=Jmb1U_M?!p|KHUfcJMjUDl&9ObTJhVA> z@3XFQ55(LEB3`qR{{zp^NS>lhi8;zJI7yLvVX)fmTvI9Cd!w%)7m8EqpK+PMr&Uy~ zqf#v5lzk;?$|dxPayfX#TFF{-BBRuu1O-{)@(ieOba2|Xn!L1L`y2*%I*`K^SlB-- zhM%J}6ZqqDCXsBiivWvQLnU!-NTanB$j0Xh``cwb78?N3V|t1vMlaO7-J)hj<>($= z6IMAIhJepD@(C(>QanU^_hpiBtA66rf=TAss18F#TO3@@us4XvB?`4yVQm>Dcgh0- zrv#N)@Ev$!_ZwpLrbmxbWi;wC_IPew!U8w*6j>l&hWADG4dRkrUxMt!2&thT)`Mxn zx-;XL(UxDTA&TXrOg_R7GpmbV67K#HZ^1*acvyOC3H{Y+{Kn)B*T%NptOMx zVA(#CI_|>PM)Pa~2pk#P=7c3!Zc`%p2o*}ncaTUm1P?*5^n`gB^BUXijvE?oF$=h# zl-ex{uA^1>K%ww7muWP6ztU(%V7LR{0@c%q)F8I!>3_L1^oCa^5SFtysgWI}GdIj8L* z#|8`~qLuWY0Z-_C!X$bnJslfs_gj)?gRP5;zKH16SZBQ!yt(zwkGH&1T*) zLm_=e6b9q@phkyLYP=z^zCjnzKk8Go`R5EFanm5#2#i27+tbYT7w_UiMJ18+^aAoltr}YP3P%iPU1lkC)#Aa`W>s1caVe6-8Age zNn9D=m5rG9x%g+Q#lNuc#ecNE3VwAjH#ZV%|Av0c-10$8}_5e zIV9LTH*jRsn?}gOo971%h{t#1IJT28hkkn6KA}Gj{q(qf!u>Gx)BX0z6tlr~J&CfQ zC@-j#bHkW_@rTL3(-NUGkSN(5Yj8miSsDc$1xMe6y*58KdSSo+0}=+>%axQlNt8|G*bwF)4Z*Ua(|;InWu(K@%~;f z)E-9e5q_OJ&WWXY*4F$geCtZh0jdinoHbR{sNVR0sBox&=6vk)eqphq!wh|cF1X6E zmcpLE&s_yV3j{?W3WBIq%J9pIDl>~ftiRrExrJNeZiTT*6q6d~7Pb!(2kFBtlc3Ub zaNBC(i|JdP#;&!agug|V?z zigw-}qIkzq+meykF4bohq>5)^#k(wqLy#(fL%I@>V07^Os9rvr zV|y;eI#LD(LMad`oC`oI?LBW3BBpqR-C|Z)SS@cW1~>aps2}{xu(~po6+M!M`~3k- z5?H+`WHLUIRp6I=;e}U!v`H0MQh(OYC$yLS{dp!q?DpaNdsjsdf9ef!PJ8$9@A6KI zKU9{zdFn=@d!J&sRbSnHH?M9#F#YO@r+C$pezWPa=zV({Q7Tjt&|Gv`^yJrhhBEAV z;6+KcBcm7Wy3>yycu6llpD8}E88$^rypm78FTG>ks;CZ_x$p0P0F`ymwS7wrD^|`+ zSA#$1>>s*D*V}{W=bLr&k-(KxcrKU0kLmXCl$9EZPOj9(ZfF_wjQR;vV#8(lb*z6f z8PMnc3!zDMO}TD6>xSGK49pFJh#j@E2!_s7A`Dwo=t8MzJyHMF*KEGxGPGaq>`Ya3 zfW`AVpJ#|mUrI?)Q|rV3hjRJ&Z(@E6!)wWSen2$52p{)1%S?k-^iqBeY@wxF_X#c8 zeQLnvG3@L5!Vnl}KwYx$ZL{2aylB*mQe%vUx@Z6xTD&0j&u2U|s#gbCo|YBkY>aMa z*-5}Hgi{|)#JIq+Xj0eJ6Db;X9g7Xu7_nQhZS`Vzfq6k{t0Q+^w3P~TzUO&zP}ggd zdKD%Yj*2)rDPj*+r9MojD^yft^mbsd7`+T)z~Kt<4xSLt>4pk{+Cxu$=X@&FuO~Lg z-sva7EbXbF4CWDMb7gB4R61LdbUXQ6F8Wx)h5p&LIc(TGFUUg2vh5&VoFVesfJpVL z+nODsWE90zMIVUjd|A8dd7YsPIHXrUyH!<`r6Q4Q<{%*hg%_UA1i9+rLfgH{$|?9m z9SBA|;*ZcJSE|=DWLstn;DgI7lhENgB-56_pK?sCxtv|Hl8^)9dDrno};ykPId`Ff8vt;5Z@o8pBF zy}#tHaUNR{E3-Qx(Zy=BY4xW!!@R((UL=jIyp$Bcf0b&UQce!0M~}ql;9+(ZVRm4* z;LnA4p>$j~(I#FjZbPpi2hI#=#IHDT-s4xb=2Wi(so7JMW1cDb*3@E1MoZT?Nh`5u zau{y{{kKk2S3^idQ=(gs&XbsAD5laF|WDwePG#R%2Qj zU>OHk2J|@N1bP>UndmuZJ+NzjQZ4=v9_+AoyHjO&MC5I&Yy{20CBAc4K-t;iyEoG- zg^y*>PvWyggq&JqRQ48rsat*_dY3I^7FU@bX)4#ndzEX*@LYnnsZL1CxKls>GIa_- z0Bk~GjsTmyvtXMpk~g6OviT3p9IDcIrf{!6tV_}W`=5IlL1P9h z$oUxM0!PUhz{@&e=v_0kgD||OorSv_JS^Xr2OF!IG!>x%6oy*+1*7lC$0&TFS z!u?0}a9ES}CiZT6)(GZ69LJ`J)<=JreuKcPkG3Xn>ZPJuB?r?i=vI8FK6(%lSsBgs z(fr!sT^~Kh_k@t^MXBeLX_qVXY;NPh{+AT3+;fAA>`sIIIJ*=1!l3!8{n+PorO2=@ z8TUMgt3-%m5HZ!+D+p1XB6$_&>@IZq1yWXHt3G<9wgJPo=l2P_s*gacTJYjNwP0zb z1+FbRpkps>Inc&`%v(}yyl{XKvko;iu7Q{2PGdPcvv_z#t2*dYV+nneOWrh1JEI~( zRp94>EG=Nu&1)eBW$&0KfS)c_#cbdsHCHn$EJYrLCnM#x%$-aqF00MiOOZ|FC2&Rx zY{W;&5KT(4?T=jRn%cYqr==#v0%>fN%7-vO^jY?&h57fgWOKn&& zoJ4Le-G=CGH{boR*Bkxu55K>BmEtmbFN?QQsr^FKaPMUmotZX36tt(A;b`l5fHVOt zrVVfzbniCHjsf^7W8PB%VdbC}mLi?_jXjnu&R*8#)p5*$&Kn0!>yf z@`JI?Vy%_%j;hxBT~*03BvB9{b0(m05Avcm0}ivnhO=1T%wa}Qhn-5sB)FCKy|{hD za5E=7tJG^FS|yO{D_3Cx zIYLcZvF$6X@h*866Y`L@kE27~@p#hrgF8TE4phu?VIetERr-D@ee~H7r?zN{!)6QD zYH5|1M3zEdx5^*5e0@&V$|*??c|!FP*^PZ^_D%&lH%^tuqc4ef@In{@J*+MC1Z%G5kvIFr?1w`4g9xKb0t_ zQbTLd3dt%i=WCZo4_?k~hC9t#ZkU&OSXa!5pT-kf z>Q6|$4hMr7c1&pTpk=(8@{2xabH>~4uN}e?2G~)p-suxc$Ps486m0e8EqC#{gz!-7 zGPI2jWx|M+y3`i!Q0}?G&AMlHe`{VDx!)?7VUO{w4lEoebT!+~!P$~5FlqH-Qkuff z7}?Q)+`1xbVqD+o-)gnR5X2Mw27%RT(k|+Y2mw?)f_EHzp_7~Ke;@!{C6J2058GeV z7Cbxaqy3PS5p=p5AJitsUw?l9n(B%JlJa5Qj(D2}RD!LrCbDm@EuVYnIaVfF~}&&zDj8w;B1@5&RvSmzF>Qu@ikcG|`cfJE8C5e!xi-@U`2kUk>R zNWIW%hcu$-W`&(N;44OEw96>u@Mw>&No>K(Ri%m^q)AMQf-Y?r zzl)YhL#AyEn7L|cjL6~+wa5CgvP7M6osJh}sBSLPNrutDUnB z|AeEs32|G2jGZMRN<0OEU-w88xl`;OIVafQY@z0_;mdg0csiH^Xdt@0X$VZxmMo-$ z@8gt+RjkFY8n`(r6!uyoX2`Ku<|=9AJsgcydz41(45k2s!Vd#^aeyP4^Ylb}ibLTP z;ZSFDn2${{5}>iEQ~UBH?GAHHY+ykIK6+3to@O>)BV`+mBpwwEd(DBMND>PVZ7ATo zV4;Bu#j`nG;?ZoQmq`AeLPY$$0_G@{%@GidOuYbWHqx#>PFFLu!}DD*5f0U?0}C}( zo}-){d*vbWIqr*wTF?v<35Pqy5X_+iuv9(;=Mt1rg|KL`-EKo6?d3!ux;iY;WWWR* zB*wROLs01kMjW8x8?b(n16oo+7=zGqQXzpx%s8%hXwmv4?>$#sxw@j#(QRCqfY-uQ z$ep(`(PQ$=6+Tcml891dO*T&Vf2oMN0@Nd+);a-oDNo3!i?|@3{;ERC!$>BDm!~C@ z2_yt#{LaZKWFj(&WnY7Y6g{pbiY=Ld@gWXGY*{vX5^FUp5A*2a?HakNUR*S^(e zO{KY2X&f8pn`7e`xV2n7oR?Wj1uAB#{wxc#)BLlIWs^3=vC#wcT}F&CU!>S%po!(? zByJI#Va!cAd{*%&iO{qiHcV(LODr>!EWA;2(Pl+phBV9$%2mX-d7(*#z&ujbxQb(O zsZx^c6``S8VwsVA7ZM+rweQ$CZ2N3lO<6QpOnqs{=whYSsuT~e`bz2wU#zoJ)}+e? zn#e+_H{hMFB&*s5us&HBQlG-pFdIUXyIjidN`&Cq0Z(O)SA@xuKhB_M3wYbE@7 z#02Y=K+)BJ@wB&g2q<;%Po>!Pk%7ZUOwwaX25!FdZx11jq9%te3$_HDn>OB+FnL$A z)^=p%d2oDA1_Y*mf<1vI%yA{BG@;E$bqxX2FqRj_>ZrPld4k~@lNZ96y?My%8W~C$ zESZUqMwX~p^iT2(>Y{=TLk?QVt2EQhuqN`>uuxKmJxwy4=7{2{+5npiON8=Cy#jGi znb4%GO{aVc0+)7BDpzxzeZwLAv{hc`gq2W7`~ek92NfRlcjSUdWSwQ(rByS>ZwFN@ z>Si3tV5SRhE15Df-s0AY)FT*`sTsZ5_lg!1;>EG$(B2qlJ7?e5&xxY=#-) z>LF=cQ?iFG2|13)H$?lbED_ZRQ-zSD*ZS*!eQd}3-uIaY-*r{1SNw$v;~NZ9-@f(3 zfBD|e@A}FOzu}?sO`MXY-NR?x9)BD*Cm$W)Rzu|$9%aoTJ|KHUCfrcQ;8?az>wkD$ zq%|>hJciO8948hEAdMq3OE-i*pQ<@p-(&}}a3MlGxPCKAn+(s^v0DIn%JrLDKXb=5 zANacuW&3S$I-=Yb(8<8*?|<^%Pu%{ATc1xgm7}df&ST3)|4FN|(M|rU;ujo(*m;=K zx?yBw-RusR^nmmEhL;!nV42lg`kG zUQB61zcn{VGzG^S66Xz(&akzYxMtA&IL-&NJ!Jp3c-?pGutAuS-YAd>962;0^!Vgg zbrG19-Qv0aVz$<{@6y}kV0Wo=7E5m@x?9wM-elATOgH0?)GVqU_A2jZ>LGRVD+dTw zzLb1?iFPQRq8HF9wHJdGWgb;OtQ1H}6{YoQ2if2}rZz^oLNtXl5oJ?oU z{EBD>Q-Y(3AmYZVN zD3C|f<+?*^;98RD5gS!>0C;7l-C817c@3znFqPjqWhzub)xy$rXUI#d&LDl_MRnwt zSp?BxM#x;i2wNg1?SL4jc*9eis4&I@PMdj#Dc`9sFZ`~~&a?@$9n9KqCO= zk9`m+W`vOk#miK}QK3G7ZoI|idIysY8(t{xC}Pox#)%C-E=K>DC#;36kUa9RLgfg+ z6#X~U(!UN)EaS(3_^B#{`d8!OQEBlmI+Zd@J_XSi4e$^&Dtv}A!U%{d$>YvJnq;C8 z8`zRD$%DcR&yYhLJ>W>sx3Ot2akIxSgZQQRaIhY^Z?~;lU87%J$S>M(nxXhftN#hI0bov+9lN*P}wzt?!A5*~t?Hgv^l=I{ddd zB7V4hBV}q>#XqXnJS-B-CMd*oITc#%f+b2DA!_oESHD<)8Mk&_6n+kIh zA()gI%i#hFgTdm~W^lwox(4{w1{jsVbA?ONWQT8vMo)cY2q#g#tl1f1qM=$4s*;O6 zGtdqeW`;m!j0!1WF#EKjYXZ{+*>Vu=uu#8b-GW>5$4vIGr7CVmM#oVBF+4hT zF^Co^Zg#tec)|C<3r>BLjIc~Xg>#fuax%WL9?bQjO5w&fPKKld=cL1n1E|beGq#eL zV!UigI~?rRmcW-OuF}jnacvA(2wU|^<)qrzV=}q4(WZ;GiOib@(ozYDCwU z?!;&$wFX%hgTR71wcgFvv$43TtK1khu5~GmHA=u&Gm{D10iqKh%dc9DZ5CKDZrziy zP=jm;dX1puDyXA>AQrc5gKz87yO-hUeX&H zWoY)rk4jWB`3iSr%X)i)X|cmlliLkmx6SIUc(#%mT=T=lZ)}2)ws@K&O{ObpT4k~l z@Y3^;=F$Z<4sKFV<(8Z89+C#bMeNRfZqHHIxeo8iF0ziAbRoBt^><>X>^088pFX-99^qH0cb1QDkX~ zE`-^#zi3@w*rJW5@ywA+say0;ttLwkdDu6NFP3MmcvuU$n)=97N!$Q(l$Z}=`jBIL zyvur+qLSIg;uZ>k6~g8*lh}ewx17&RcV>GXrM!<#1Nh>{wXaSon zFcWI^aKJGqdxnZ9AB}D^I#k36pa)WlTdZ!q(y|8Xrq@S>E!4wqCUj%E3XA#3YKsNm zh9x~JNlraohU_t538jf#vO`vR@0jrY6<2v zrIW#PG|7pDtm#tZXJkK~24bu+RNjl7J?;Q&p<1f;;|R{D16&VPjVo|Pf-A_CHb`_~ z47`q#<3JBq!B)n&N`k1OtAo^7fXRuFWYz@`9d;H}RiB|W%Cn$jUWlql*jTm;Yauj6 zh$M!p8o5GM#E^s*R%EOLM6$_%BPlpfwnS@1Qn0`*m`gwsNvV^pXhL)VTO{QwDHA7% z^wZhyGUo7}D85TbgQS>zqGG**#>A#j1gHe9O!Sm4U#gujlDu=PA}ee-qs|yiBcHAn zP(j>rLSD%;#7M3R+xZ?76l!UcKG;X$Qw)PE^y=%{k2!!I#Vw0^`+f8$4H&{~*GEt4 zsmY$|)>l3^M;RB9MJhhi!?vtET>`;blt7;CRsuuBM0h&MC`&SCeY9uTN-8iUKs6JV zK_B^$f~|D4uwf?*L|Vdlg|K3>(ijM;V$CmnP>Y}{xIjg<@H_~{bVWu@87@FiDa^tQ z1XMSQmEgd^Vdpl$7*?qt>%=iohScMlrxf*==RVJBcu4|1n1o`4SrKN#c52fPT8@pB ztV+rC;)Zi^cz6Pd!(+5UC%q(aZ|n3n6@!-*E6#>_7YzqF2Uygh?KA6ObvA}7F^bdp z(VQ*~ka9``1$0(G^W2V zOxPp}mJgy^w0K8rvmr+uUc7lxnuY+5Nl3$-*9x8u?XI3XM!BOiRpXpRF;%k|*9FE0 znzvIGk5sBiaAKp#3UO3gQ^ha^i*lzYY}qUPTT`aN)F}0}#;;~0=5wLK>EM8Y*uv#% zCM279OG}A|Ft)~90^qIBJYwJUNej3z1mT2r$zZ6xib5a{u}Tt}+X~Yz&eOKX)-~tfcY1H5%McvU;S&P&{65}OQ>PEMoOD;rO#32%lK$VYBgkxIq zVUzieBgUG`gFsuXT?Q5Tfay2^JwcJ0m|Hj;Y#EnXpn^DtycSHh7%(t1djnI^5mhh= z6jfP+!6bGy4Q4&9NQ0SwIWWUUIZBZT2oZiKlnb2BfYlu4kR{x+Z=LMN0;(S0S$LR@ zK^8XIRA|p;=vCy;(bQoxQS2w6S(u~EI6b#4#fzPeg;sPZF(i5VOjWiwnMJL*=qL_0 zlUX&x^RDN)4yCtp>sM~|Ds0)zKeRFWiowd!AI3h|l`d?=`lk~$`)?(RGE`WcjXdf= zx=r)@Wws=y(;y~wGv}y<%~0Z1hGKfIUfAZ^z%?g@K5QtfJIZ(}wW=7LtyVdlO-+}d z!hWLSj+$)Fu>+yr01ChiCc;J|LOZjJFgIn+t#*|q6i5^{Juk3Sx3PODP|k*=YdMaE zVBs>&)g-A$yG4O0^7R%3ZR%kfemp8w_}P?cIQSbY@Dmzo@G~AphF{dl83=+uzfbTx zm$P*Md0qy7uqy)Ocfth5rvXxEhK&%=NVGIRo6K<=N9)O@Fn?m4NHGl%2$UvFH0fY(7 z1Ej8&eK4#f7O?E*W#FPN7DHUro%ojGqVS$H6?3=KTz{#AJTlevAV2E>s@k_oT6_#ILU7zNuh2G79squ z*2=}GpO_RLy(7JT^Y!!UkBT$l#UE`(fEO>tFJ4Ms{DBvabeRrVqFE3cWyYr2C&?mK zvrlqP?&3%S-MIbGI~Y^ZgKC_c?bHi#RRBc5X*HZAH566nzH4gKvTL0mO3Q(+3DyjB z?i0#&#*6$DxL~uea@&1!DuXXs#0RsPFqEaO$x1V9grQk1WK%L=3!;t8_QO%IAh{yr zp=4HOfFiiFxCTvK#h(mZ%BIY$>|{<23X{o1^HAzo zII3_f6xo~<*gz5ZQF}jMeFFnwe;0QzqjzAm=&Tx22RT%?OS#+6zY>UY7k~)Z<)A)aR zq&ZXxA_tomUO3pKuMdF@G)24A)?&ienGzS$58^@f&IHAHTqv74lKXkw24NZWkeP-izqix zR00kKMoyXgW)Z(sWTtDyO}J=7wPsW!EoNm79DfzOL)DNJg-OE}siI-Viu+Y=a*a_+ z?b-{&N+WkX!-nrkx*E*0UJgR?M_aY8LxK+#6~9S@PpWRG=GCcnFkEytHH8qjlID`| z#Xw1@OpOK_4r;F)Y*#e?ainiF?a4rvdQZJ<$r0uIHunhT`%-}F2rqP~z?3fq(f zL(b&k1tY}VL1&1kEXDqD$D`BEL|EiX3^HkKbf9n+{m7Gw>%gEq#X%Y_UBn+nE0}P^ z3S8%D*Ve&>pwHKWYr@!CD^j4EI@qX99jYTNR5?ljQN&d?iMUetR1EWM;fYtZD`Vs* zY{d4MNC*v)l-i5&u8}rlY%MM`EM~WWKDj~AE0BC5&VE!LqolpAiq5FVW8$vX$u5k4 z!YVcBfN_%3KjqsLw6%LK4)f}mfcRq~1_@zf#KSbaH)Mbudf}DD7l;X}$rd(J+%Dcs z;r2H2il5hW!Pd~lzG+4H2Qe7VZ8|I}MneA3oiKsLp&zDit1EOJ)G*k?UVB7pW)(YL z;cx~YZosm)jiG&2?>+2Subbn`-UP;FB7zR{gH0xu*q$h8ee(_F%ynUABez$bQAwi^ zCrYyL%i<3W8#{s|#3_i1rgXXeO{DF5+Chi$AoI^?D+Jpi%6*1bnhTpj!eD`GY8Nty zj(L8D8#cX^k}S>$tiLkedQ%$d2iOTAOg3u?(-1G`v5#SVpt`d`sTI+OK%v~#dYe`B z8oEAJB6GrEz51Gse!^^@onfsF8cVq9WgTM3P`l0kSm`*1O1@=O9!*jWO%1FprnlM^ zHf1g-liv^$4JS7(hTp5DF<-B3saLp1;U$TDQID)n7BEvnh8JA20a7VJu&p>U7mVy3 z`D)nW++4RNTYWM}cxyT#j%~=t5GX8NC zT-uOqUD7D#DL8L;o_!9}m>}-7aGhubrtu31$(j$b(&<7bHtbt9 zdlld5Jj`(jO;tqLx}qwjVU~!}Y~_PT&*Quk3bdPx;h#=b>z&IwO=-m8ITR!TB2wsU zksz&M2ohpS8;S(lpHP}GF$|^IPEne)F(hEM?v>Kw?h@@o=Q#RAY0?uJB?+C;-dZ|K zwbRh8+%BjSe+t7x#6>2&GvfIWhA(WH2ul^E_f>-N3Lp1bLR{Mo5&>|O5e7VkG6Y2b zwS*rJanZ3&l`7-3qS)Et5zsAxd|;};TqS9kTG+i=^586=H7z2`3{h6LU9#e4gNXLp zkv`XxRz~Nxa(2sRa7mYKaV9Btm)>tF>7X7;2o}D9J!YiU;n^!$$$RBpVy@`W#b2utxLE52 z%JpInn3HgLgUTRdK8S0|UhK-coxNO_TGF6>Ut+-4#Ta}F)F*&H0ez8B)%SIAv z6r0m;2^(79eEnw32=;5=LKQ4{-a1vf`03|J_5S#1FoGQTe+>g_i1KzO~GOcQQ z@m#3BVCTJ~GH*6lB#D?Fhz~iBPhi*#M8MRKD^%IxGs@E_c)8mWHf=VaV!>^MW+{;E}D} zgh#L{23LkTDVWoJUkfumo`QLBhZh7a4c-@+1EXJgbOPE=|1zq)86dTqWF~(B;>mzkqtTsvjBTC%1Jv_8>!_Ac3KRH=#!vO}r}lV4b=I>w zWL2#Q8b`p-l}h7FVoIFN5N|HxFe_M64Xf1jV=Hv1`~(;qSHvKH6sgcCT>l*1+?Mp8iccqqn`MciPI%wl(dY)21$O>$#+RT2K2W z9ld=$S5E8gSw5|!YeoANQ*%|%W6j#m-f7gZqJ3&lZ=O0ksdH@nQ~k-mw!XghHEa9A zzV5Jh-Nocy-WOip(RXRsy}rF?WoP&0Buw(}$Ndrf7PobDwy&@(?L8fBogIJGzA)td zPU=ybb)B6(>)P8od)qs@`r3QC+B(Dbo}TWWg<-p%$-1tqr@d|YrEM2?wuj5RSG2zd z*s#W4xT?3iYs!qN)2GguG28HA2mq%2{i1gv*Y{>UH1_KAR-XSN_56Bz=s=#6e<}R5 ztyocunxxD_$ul9&kV?O1U1wj%+RiI$Gm$RIH<5hJspo@u4pYxR^F8kkspkv$u0AaA z(?33YP5hGbzQK3l|4)ZakPJPo2#VfC)KjSQyfIvp=c#$mo0WPl@w|xVjpg(H`pEOk z=bwN6#pka)zw7*-^ZOQFFs)}@Z{PB1)90;N-Zp#wg0|%|S1gz{Yx%tSD`qX9Gh@#D z_E{@u&$_t1eP#Q_(`PPkpVrxNaZejVV|jN^yBi;n(zLb}ZELyqPHXR4x29cV1^5~= zMejW7WpdMN;P)Ef!^T?giq%u*KwEC4E${B^Y+v5j(cRTMZCzJ4Lk-Hla829VX=}Py z07XaFdWOb|kiY8~ALa6qA+Y=u*u(*T$2G%M{jWZR484{?&!ggZ#jMLSnrGo#G%${z z`Z~$GKlcal6YZ|-?pY%`>~X`+TS0!6HJRV5MSE}G3WxRXX~tY=pw~~iCs5`Q5TamP zS>X#SJ32eV%P;L_cwfA3<;wOR?{U&!bSR7@F&j~QEoWsS+rlR=klJ8 zzV@1m-jXYN2a`s)NaOY(p2zbO3~k|CI(pZ3wp|&jDeY_EN^R=Lu&2FmT~8PMsjbVQ z4o1~}#aeo~eMRS$HRDcoyjongb#`_yH*CV}JGw3rE*}JL;c`}^qk~slc4c_LU3;dj zY45wVdqpo5Os*?>ODR8B@9=U6_o6xd4&^tIAH&t2L$1pK5=2~dT6-^SY~jLnU6=Q? zt(|p3a=utCq0czzAWf(bA?2`WTeLx92q7w6MEhwaoQho745EAx3;rm zIeaQ9j(1+ZXyYd-@8s^T_S3u9w5Rc?aq}ABZ3HQ)W{L_u?=OL+jcXQJBHp>IyZglM zu1mO_erb14-)ZeD)^)9D>*_nbeQg`;w!3GEV{=DCZcE#GdfTJr%e&Wg^%*W#qr*^! zXxV;1`X|w zv63SPsojgqCJK+xlQ@ZM}VAcx5+T ze)2KVY0}G=OM4H|145yr7g}h$M8w|PcN}GFe4Ncy<8IldZC$I$ye_ol4i1=IDLjXF z_9%Hsp#UrrCeK##s$K8pDxKwSu9C0A*acR<6RS>GgzJo#9)J2NCr>>l7OjpShkRJx z9y*Z~UfRZR?70M`4g^iDmYg2Y#>T^yJ>6?UD^GV9n%UIZv8JOB;ks^lU${ifK7H@F z#*DBs5H4?ax=D#PBr@;~+z-t(Gs)C@4|${m{B;>j_tJCag=MUcsng3xmODGTR+krc zVFn#pW_Z9adsg(87oPXml_wH+wXdBuOR1~*XITD?7i^^5gmxsSMFWmm#beHp(vhPp<;&Z#@m932?e6X9i&MabyDn+J@Z$EacB9~) zD_?PXSmxTEj`i@oR4HBUm!}H_c7~DRRl2Lj(!I!-Pw@L3zi;w8l6Q&zFok=K>;H+K zHjVuHC%jGP_iA;i;qw^g;8a>*$-B?tj^UGx^Qpr+=yb8qq$#pSDZJ9EJ`gdQ3mdrbwPA@m>7S z-E-xI>)>0xc&}#1HnkI+JIA3md=-pbEVRc=-*26eT+OwzNFGr{t@XV$^YRvfAaiKJc|dt2Kc(Xz5POC zM~199X7j{ahUYyuw&-0zJ@2D3*(J%d{7I!j&+7N&`@wt{-%Xwm;rX!CbBSm1Vdc?3 zwPg-J=^*p?{fXLzQGOw#f6c6!ko|+>ik@r;!8w*+sy~MGKl4)+IihiyeimNz?)?3V zR*v+%H}X@Nf5|VMR%7W+{NBv()%pq+kyrnOvxwiTWz)mSpciPv36%Mo`3g{UrmYob zdrSdcwtvw(h4Nb%eB-#LpG8l}b1>+;p7s6JwzVSndNGmPV}qXZo(7(ixE_JhDZMQ5 zO`Nh;Olz=jBD_fUbL^X#j;DP3%;~eH&z?SK`rPUBrq7?gV8-+rGiJ=3F>A)`8FOaL zoiT65{22>oPM{)YW&7C!G*8Euu zW>23zWA@D1vu4kpJ!kgZ+4E-4pS@ttbo`Yw=ggWjd(NCWbLY&PGk?y4xzp#)m^*Xs zthux2&Y3%R?!3A4=PsBxecp_DGw02kH+$Zkd2{E@n>T;ng89?u&zL`R{;c`4=g*lx zcmBNj^XD&E0Ei2yegQ=wWIe!+4dKjnTxY_vUEYbA_${|X8T~UlA+6bIe%UNyZl0IV))5y$~=+!zd>VV<0iEH z_N-@h@rr+YH1zp5X^~yLbKG^&+ue1`7uz8_wbnIcHR8wyY1Tk^{?w1 zUO2$akmv1Qf80D>@3?vV8m^1}^VaEiaNT<7&Ii83b@pF8zq;vYum8z;SAHQn+8cQ6 zy0d1lKib=M@+E6_eCTNJsCQiS^w~c=deKcEzv7UO?Dv+X&p!2?r^lc0mcRe^lj^>) zr~fU#IsS)V4yJtkE%)8_-7Am2;-_zUM=<%w*WVZ(Gbc0i6VGj1c1*DOrKd0Z?%y19 z^_Yp%e*C)a$CMuZuirg6YjI}7V?X`JhhJKJ%}HPGX=|Nz?2p1Lzwz5KmmWKz=*EaCv&MTh(r{k{Nb>MHl^RJH| zcjCrxob#n;M=yEb`A6^h#OlRMzBFsuGk4Z)SaSZreII+>=8rDv-Sz%~8UOj8OWwZl ztNBfDopAhnS8V!X%Y;*p-~NXmtp8Tqb;nQN_^xrse(F=lzp3j7ot^jXIsT!qjN5X< z4=0@vy?D^7pB!}V3IBZ5(x+xD-Fm{(jTKIN{c{cdk3+8z;V_ZRHIeJ(-i9|LvQ9k~wAmNj)#T z{YN`5?mFq%-!EwS^dIgx>7c*b_01zb^XN&p-?DIV-#yJIuekl`5AC|K_2i?z{Dbd& zYU<@DH!XPAO&!0x=j6MN{=t7=eehE!zvJdd*4^=e{ZIM$Czp)9@x&8P89lD$neTk$ z>QnZA&*-}zobYd_ocG6nfB18E{OpwXeD1oZK5KPUUAW>yIw#0%!A&2^QnI| zXUB*Bu}`-D z}l7Z@zq1G`1Xf>{OL2^JoXJMzP9$4XS6&v z=ctz+Jp9ZtCtv&EQH#zy^J~R#-ToK(cbxgs&0jip`te^p^QeXQpYy=2zdLi~q_Ouu z|NiM`_5b{Uzv%tyinE^FecQ96{_vi&Zu;owpFiSF51;ky1+#kZJ1Kki>N(!lQrm*F zzu4+M*YU~jvp;=A|NN)E{eiPzKYQRy-`n-r*>j@ft{DCMmUDJYp7OJwo`3W?zwCM8 z&ckP4an824uOBrn`tUgq9`r9ApV|G?Id2{qxAU1h4mkIeT?ajX*NrEgJM)m~zdi2q zYtG#?X~Vn;`+efv_f0u3ck%b0Iro|u&pvu%zI@&X-Z<^%Ti@-!nf9b&AZ^NrBBqI^wR7L);#^LKyx&-1?D_kQpDzrO2#{kQA3_Py7<_S*MaXRY6TFUPZo%#>E_<#)2X zIvAB4Xy6q5-a+Wpo_MFF=QYQM1kXEta^mpme?IK=@nW04&0{g=ujVH*tc%T^YnJX* zsx;XNz782p8>t3oT&`_u_=?ZYH>U#@KWbXy^7Ut%(ykDDm&mxa4J%sqxpdoSDd=i8 zyKt+WpM2Qz(`6^7Gdk5l)%7$c^~g{y!}SqnXii)z&oz}k=43I^=~_JUPNQrYi(ALZ zA${`%O}86qXMGN21-dQs%-axozQ}D>XZ=>|mrvYIc*#z#P$0TDH+>1!)zEW~Jg;I- z(T{SsetFfy_Rv{(b#C>SlMS!kb+dGP!>WWBln_gkhrK3@4|l#2Q#YnCMy6D$4B<-* zm&o{S*Ca+6+FV)RyB|tf*Ue?8CNmL5DowOP78U=e_62AHi<(FdggFlXF*r{rw6r9?cnz zy9^&M1HD<-;}V5l+a5RkO!Iu~Rbx~Y?RkUK`>aW2<;zPt-rHPzdW42Uy+3KT^Yqip zy}R`ayfQ3bdUIR1tUu#T*`DPS{ZKg7aC-==tpR-~VLN+~hN9Zo`R$y0(^g{Nk8C#{ zFWGTpv4oF`wanMU5f(mIe3q#uSf%@vvnI^E*nZ7N&ON}f4fn-og*KOZ{LQ7l$=sPY z9cmqXyY8$!TA#h&w|S~r=dIcuUyV!OvlroJd>4h}XzOC z=KBTSU%eu;w#$#ZX?IhNB&&Z`u;Oy@^40!FJg583je`6Wl78k6=NJ3;yZnfgBs}$3 zh`TzPGe8Uo&-anj6wwdJNnx#Hca09vCQAqD$5jX16Yy(4ls_1dVBeB>^`>xO$Evta zE1FrLZhF4ndwOc1W%*6&64}dvZ*$c00z$?DNzXAi7MCpwvZkz4Ro=ff$j5fyO#Ow- zpw~UoFVvRZ40^U#SfFWkGRWf7iM-%h#o$NZbPL%UU4wUh&DquJpBr4E`s00!QF}0# zQ*6jy6I@7pxLv2pfO<%hWiX-PsBcJT=pCsGFAGBm=1~=|w|0l@D6u&tI>r@BY5Dd% zC~RG*P;{u({Rd&8#}9YCOWIfw>NeN3tcLqls8(Z0!gEt<7}r^|{zs>b!aV)+SOn9O$6hJCtA8JNMp3+tXWu8Lnz58tz%IWexch*8>sN@gHs;(QJvZKR&FcV>lZz)kKtR z`M50d(iclv&C{Nd#VY0J(+?br^mH@|ToL;q68p{m-g*(XsO06L3H$?EQK|)H%Emmw zQQ9s0SD(l(iP{_;!FfmRSyW)2$SE{avxsEJP4 zs31Z}8;W+cos`snLyLKutGVKRhgr-6?OnJx__UY}d*xI5_3C4cSN^Qw@)?hDVvRn2 zKTSIJ+fJbk>q~88e^_?->|)Q3T`707xq|an?47S6m#lfFV!waN_NX*dj$55eyY92! zEsj%LvYIvGaGdcTS^Jip`*GEiM^?><;^XtfFzH%@8u9o8nDR}}{o)7gbc{^PPsaBJ zcZ$BY?1|6LA`FVi@g&qgN%r5OxjsQQd(qvYmhgn-p_N(^F_j4gE$`F5+s zb}UTI1Fm{A=rW#Q-_XRx1h$yGvZA-YhpaF70SNa++~KxuNEmpbo^W3dq~!(n;?M?x zE1qDX-V;>R51=J}am-4F{NBX_uqAi6_3=A$Fxd1%R{cSb06Pt!25t<7JKQJ%`!U`i zbC9ZUn4do#`L!gYfIQTsE6hLEq|1&iCL(KF6TpRg4l}@odn#;8{<^0e8Wjk@BY)Xa z#>jv$a4ka}xNtrFkLJW!g6D9(gXxbn9jq$yI^hmB1``Uxz_@97>>oH7!3=Ulo3e*MqMyaoIJGU)#Q zDew*;E<|gdF9AHnNHF>GKlm2FBWr2%;ky72*U*^Z+X26P0pA07xaP+6XW|W*JTK@I zaD9%+13iepq`wOAun#i*bpQ|dcA30B;9(!e{wEi@>Hz}&7cNkR|LZSidz9h9x*RAi zayZxj#y<==k&3{=%ErzyABmHTn}?V0-&6R{{Qo`X|0_OVcz{C$7%%=CzNX-)Fmy#t z16h!VBgy<0K4!N(ib31}+8pkGGOq>eYyY~RzzhrB1>yg;-u>TS1NUVREA}t>|MPm_ z!oYtmuRjABb^nScHdY{Fwt{eFfZ!TDlW+NhPxymB^#@-7c(_Q%jK2tcjBs6%$u9vs z97CA=TX4V7{|Dd~<_|F1e+e%GcsPavXW>OZdgL#cF!jvFSJ9N;|6WM5CLPqx@E!hn z`qP8WuEax+IJ~5MBcqEIbVc6}sM9KR{db6rPr0Oyd%aGt`%aNk-@4N9%bi;Fy&vle zCYy=d7wx^yYhJR`#^CJLqn~?d>83p%#p^De=wG!*Wgs((%2q;_*n(Ajb1tWQ$q1Jc zsfc4sTawqA?u+#eJx}^$Mc!$ao>cqz>4nol_o!D1Gt?_BX(QjpmnI04-YF&>$t8W~ z>DIYc%gty?3ODp>S}b}X=*9BG*@5q{dGduj@=^tKZ^X;KEcqgtN3|Y4eil#GSfR?- z-jn7ne|1-H?zsn|Q`y_r_VjB=?mF~n!-ff7!5<+8_w?U54Hs1CKIn1?COholiPqBg zC|0X{aKW@SMXcs{PmWuz_!fsn>-TdP<_q4~6!kp0KfW){G>@0F_BGk7($2N(u)Ex$ zAxA+XeU-Sy_&`$W%L}7=Th5Vn&MOL?USfLReDnUT+eIY3`4#@QGq-e_gtANC9Wj3Np0|}x>$CdN`}~QVq?=V1Q8nv?oz1R& ztCsL&55;9XQ@r81@qEdw<2GUQ0@)P-)zWG^PBgur-DE&{DmW-e_xs}}{8JRIYLxF#@wJ|wyTaUWZOyldSh~cQ^x#8r@6&WY zzpu@UKXYln6nVPn{RRQHYrAi*6qh+QV=Q2J!Ri6Ych*1Od6R{yY7dEb_t*tf{@fa8 zPyIBZyyO^!G>1zTT!s@LAxR+P2Sn+?k<;zlo_37^|r-dkH8+ebN+iedd2CAX-nRI2}ZTqx0+P3_dHtmj=x z++C`NrY>zTZY@A%mrA+NEFhX85Sbt-o6g$cR}uYA2njlG!18!hnGP;_j9w z!u3?>nuSmG5bLoOSz>Rg0w<{(+xT{UdFNxaFL;n%LDIMy6lwhQlHP`LkyJmdzNnMi zjplCZ9XyMbA^zIYV*s!97J{i&sTXs)!}N z!}?yPi}hR(O#PuS^`-jA6}3e!n*}SslUGe{l(8_ecd}gRMg3g4OOO*Rl z!iAmRyka5JxA@=TG;bX0aHP=ElDfDP`#!M*@E%EhvWYnH$SawGxn9qUhxL$)}X7w|8UHDWg@P`>Su;Pn{(yaJ=v;`50?x`7SXaN9H_D`q7}p zC7ZjNM608X;dFayO1PM=bJS|rkn%Hgj)?-K;|(oc9uxA%E)i>%wJo<1NpSaDXMXEy zoHNhY%#-&0)O#0pdp`A^x%)=@X0J(H0;zv@MDT4s<%%92<+&j{#0{r^T>(VdV9K7;{xU53-?Him|Px~sDsEFw% z9lJ3BOLH7OD#&G6ajMm;ooY+oVRr0DW}R1YtX+60)!I(%L5b0;HT&pWW~jHnJ=cpB zxUB>!u^B7hwQ%V z2tQ8Uz%rhoKrw!|vEpnq<`q$crEAZTMZ}HI*XkP=dEOtEeq6gT>wz$9&AnsYZ_Rrp zE!Co(ttK3bQBb7viSNC@Z`zZ-T z?RlX)SGr|t-3nX4i@~iGH}aP0Gz(w0F1r`en9!fS~a~c!A*!WsVztHQBT3`rP6oR z?j`d_+>f;ExZLO&!0o3iS*0N{6{PsQIOm>(ZQ$;X%{Eg)cfK~nZF%0E5dEca?V8FT z{9L2Tts_d;=5AifUp5mpDbX$crGVb3>Y@HaplRmLe&I+ldm8QD(o2p%E3j?8VHD@V z*qAdM76*h{uX>&wcOV`q$hlp;{=N9u(44i~hm~os-h>mD;&u($%ROI~F7bsj{9gW8 z+Wmry)uYui1qn^W-u`}<03)nzt&vIS@_U`MD&sf9iyybYls&m7JF7c{`l)-%jx7;E zn@4Ngo&-nAl16Oac8^>eiK(`%%u3xGCPL?z?6+7}KW+Pb7x(s}NowNyVIf_a7Pm7- z{(+-!_me!f_by$@!^0{_GNK9Uii^INtooo(r0qiv^^YOv#|v1S*fflb&Za-L-#WeS z^(<+T(PG!yoI~rJT=qJvv$To6eIzy_TVCOrFs!}#dndc#&gX*kE0WfimtA)6nF{eE z7a!c?A@9AY>D>*@&rKg<#Kc~7i|?vWzJEo{)edKKNKiSwFB777i8Nn z?oqw@>8o=-?bJ}`R~3IXk#c8)k>B*QZLH1v)-M&J$>Uj1_}i!mo;}`GnrF_R{h~{& zEp9Pwv5=~0$^LiZIzy!@TC-vX7o)blPA$74gxRv=daLTDhqv#XNj_%L%HLjls(t-i zk~v3-U{_08nM6^x=o*~l(@?fc{W_Y*EriW@jM}iZ)JYa+Vb7m~>nWw2>>PQrFTQJ> zk2ar9SwZZ1&Rw=|y-KF-CAo_SR*ebsZ{no$;`Xk2H8uKuy-Op7Pm4WqO`bMm_|DcJ zd9)+MK?NR+`}Y?AV(!fBsq9SJyG2Du-cCKtxOgf^$E9x#^@^z3tNNVNT2skikI<-d zM0uC3T71)08)&nlyVXqq38`Q#>dQy5s=hJ{S5<8bBdy?|@ zJBaEuCMvvfJG^FXXRnB1*r4I^d&?WPT-d;v#k)Ur2-N?)<@Lzfw<9qs^_~CtTC&^}8}ClF z=%J$%4~d&-%f+N}R&n`eD{UE&)9quY+6&g5zVrBMNk(6iRmZD`#A@t;yr&&&)-0t)sZfS1a z$)oDW#Vxz&$>|ACb0sP#O{n|hg(6B5@3VTuMj0<^?tLD5nEw+Oc91hrF{{ew)Y3zi z+a$JiW6Vz4N^@TutCNT%QiZAdN1r57!~$nOPoA54LZn1&kD=u03SOVJYnD-p;URw3 z-T(0x%jbJ{6*zh01t6Xf!Z8c>~yPja?JbrdfOvA;~-9#5o>T(aA#4n9)2XhrhUxd&e6U_tLJ)cE6lGN_%Qsbcr`@(UOh6UoQM8$)FmZxp1JjGg;hsk*w3fG%ZqC^=^r;wBs|C zE6Zj_*>glDZ6BYLJzJC8aA3#b#cV_Yg_wwm`o)=*xzCRCi~E+4_-}8OlIMtvZd9DT z)EcEN8os^CWUOGu<(^HOb74-d!0pinE?e5{Fb6-!@aE@avS!V+zMX?x$+J}6-m^d7 ziMH%j@i}WGnpWMphX0GJl;ESDC09TDzwTVp*A~_^l0@FIr>Oaz;ECEE*$umnmFA22 z1e&po^$W>u7$p=6?x+^r_2tR2#phpI`eYp29CiE)In?}{Yry;Voss&=u`;>l;tpkw z$&FH651Y3)w(cOW7aF|0$?Kko7T<~VIn4*WUi^NtQ998I&O(R#4)04Daoy$I^st&XO*L$>nVlCmY8uzfW66 z-CVo5Ht$M+u*s1Vu!lh4J!Hx;?X z6EwQUL88KslTT*Zt|!!EMSC+Cg2GpA*tzL4kD^IAnz3Z9&tDa6d0yUoa(Plj_u!$Y z(_e3nztlb-`09?60G>n^$aPTPMr)f`lC-PReIVtnCOLYS( z-DOyXOwtpMq?2C+ekAq~av>E4z)q&AC5%n|*M^YeYr3 zYgeT9_qi0^(Gb0l7WAc*(82?`&G+&>%-iqlawdf51b_VxU|XDeT$acB@=H9n;r4mRN-QFUXC?Qeml+cvJR#G zq?Cz{y_L`QtCCcU9L)2a5fiR#GaHDC&S4K_UW z-TBPt@xD{kv+nK24XiANyHC6xdtPcvs?i*XZsBAv)16!SM!Dpi$XBJ2pQ}!;vhbce zApRv)M!@0o2j!o&cfxkg@tvE%*OQdqZG5v&@`=>ixr7T+5A8)iTe7CDONy>z%+2i^ zDXSKk&|Rs4#k$_QxNP~;9UqsFM^AkDg!NL_v`u}#uZYSemLdFYY2IbWpD(?yhMJMI z1iPahcMSZ9S03K!=AaXLh-|%Yrn_@8cPQw|O4s%s4Pq62cO!WYM5W${RvgiOa97Zp z8lV`o-lg+s%kCYjW$gS?QbK1f*H^A>suLy8x(}Q~rXwmk{5-EBgwJ%;fU!M|GaK@$jvyl3+-Kq-9 zzA35Scyn#oc7lK0B_f$$tYL6hUQh1YZ=(|1OcYj}j=P&uc(%WP+4l@#YO(bseYP-v z`13jArl|l*xfprsysC)NWtptNyc}EN^Me;|e5iBOo!}Wty!}yzX7s`lYvVYy;#QHE zCn4h_<#CM=wleFhlcSq>nI5QAVoH-4rNPEe!OCE%JZe|O;JS_yNfzv3K^{r>m)Xx!3jzeVx8LK ztOFkL$-fEA@FKJNH6J!;9gqKbj-+iu4i!7maQr1};q}8dO{FtMyxD?p?rz%rAQ7`M zD(}Nxv*IV@lX_7GV)rqUH_JZQs${=Z!|_49 zIb612FYm*~2WN7K{ZA|1PuZ-;7z$pR6N}$T6MntuU0px6y@2d|q(*fW^-P^;`aa|P zft$nLR4vW?K^(BkDXFV^O*?xf(2zS$PGlrPH|>_q+nCR;d)c&euTXbRtsrcV+S!^} z$I0s6?@9`4y2?t-Dh?Y=kLdPgbcrloao4A$_~PW-hx;qW#nuT(+$y{(F5c`k%ExED zx2B!Mc!+axcRMvFaK2`jfoZJhmS=gJ%|zcW>zB!V#QLOFfH%%CDC9Lg>m0o|oQ1NQ z9IM2aoW_zD|E}Hk(`pNOvAfrbvBx#ewvF~|{HpxSO0e*7s@kThtsIS&zBl&>R6v!W zg%{kCTzEnF@xlvk=`6T(Bo;8>HXUw3Ex5Krb@Ja=Vk+Fy`h9hh{~A8E2da(#z8LLd zzZnF``IqtnK<8h=17PRE@DSj+Fgyf$3M@zu0zRP%+5Ck7KjD(}{DlBO7ldztKu`$B zpT7`5=fW(neQWA1qGHe!E_yk*uBaC?FLLKM>-5VE_T9BHqrF9 z3f`YiC_yKCdhaF_*owiJ?PD>kjR`^lt{{BT1gm>t9}fz26~!d(uuXkYRMujL0A7K~ z+L3_80{0)(2P42&Dn@3Y?MzS-(ubJq1lN`WCTD<+21nyka8k^xhD#w6}}Mtj=gV#BW4{6 zsFgUkXTQF|2h9Ql6j;muLV419Td85oHWXkh)cv#eg9^z$x`u`TZ6z3DpAKBx9rfW% zHUhZiZ*>2-(*I*_Y2Q}_cq=s4Aakd6ByI}{1>lO;g-40T*Ni?eLV>ts*GIXho;#H9 z6hi^If-$Xe+*T7gZ{1N~uGq(8_m^4;&yY^IApl*nmwr=Fxl?TS4lM+zE7;V%jQ3!K z$euDT1h6Z*s<~gLL2jRoAqw0jw_Fqq@eK>B5}60^))zm|y7S<{VOJE$E1I+O-Q}w{ zQ%*RbfL>CREMugi)}5`6iU4~BdP3p{x?gD5fApP%}c79{U#10Kwnbg-On!r zwytblh5~-6^f=`w8U8zB<{S~=FHP-CgQp;iT9p$D04ClLu3G27nsitL1p-r-IScLo zy0I|+b~FMArb%d-KK*`AI^c>g0t_ZDFHbyMq9PDdX)G-Fk_qpASYMKw;qv z&ho7C9aOH(ND|{8&HBsO&MeNS0Rk}MP5;;%+vGCv}Zgz_I!Izs*AhAT| z7q+xY7YE-gQ3O!TA3SbYB3K`~@%$nLSS*y~`z-6&$QI#(N(5jm{^aJ{C|A{oe1lvF z(3rnN&0Xlq%pLycCJ5kI5Wha|h3w4btvRj;@K|iUA+NmVn$U~+VkiK4|I@Czmd#s+ z@F);jFv5JpYt6#q7kVflS#0+guKTxAmvX(2MS#g(P0QHQ2= zHh1qD1jtNUA4chMF&kc4W6o0Ljcs` z@;vGdq_h5S-d{(6)cj%DDGJ@&IA1P70o6hWLW?eDeBwSFhyts{pS3ibPYSR*d7uDm zGJj0u(mK(*-xB75);&k8g9c8;esw|t*J4ZP-8=l=w13Z2MS$02m9mTxc4zD)4h3Kf zYVAKG#dYaMZOSnOh%L$xshVKrpE3(Z0omlWu0rQqY&3>=QDC+}NA!#Sdd0Nk4>Ayd zw&)G+_iZ^^{HNSdpf>4K$I$F$F0kN@0=5N4bx*{X@!mg^VT=H`MZ$J3GL>!B+1#Uy z0JuqHzYixverD6#V-O%W)jGX2!%VZz>-shX&`ndXGq4HP3tD$65&?D-QwC@sGY__J zd1H+LyeZOoE@hv8=)5`MivYc8lNjyOJ^3SJuWurNZ=%||JqLJh4ZMFAiU7YUNm~NG z6T_Y^p`ZY8;ZOVZ^Wj1(5Sg{Is1b-0l>1eH;Oj3l2xABpB&m+&}&S1(d58$7h*joU;=^f#qba zeuw2!N+&*xpa640E}IqC%cnMsxuQUGF;8pBOPuz59y_3bb27G4)`KTx%xIzs0iFwB z1#25SO)HL_`+@+_MV&J{BL+wrCrwcxI%%rr7S*m>F~bc7qzgp6Sn>4JrYXN^E(Dk^ z!dZcpjp(>?D-H#yliI24zgXUUx55Dhs#A6H$SQK7jFj4k2w+`=y^zcCsF%b8GZeT^ zG}@IpaeaL$D+2|vQ;*4iUKYf^#o*d~1jtTXZ`aefq;lCubrjG}Brj_ZP_aoUOI1aH z?Uda3>lxDuCmsyVAOLq^anr3mVzH-Aojr~M-QTz>ZC-4*XOA5Uc&D8B8C5VEbT;5~ z1Oj{)-Wh$^V7o~k*O(OofS1TIcJjlK)_K;gK!EW4^%57^b*)G@q~-zfWy6Da50qO4 zz2`@O@#3w-4#)i6kC%j?0D1nOkCMZ6KkgpS8b*NfLYli5S>}{geF#AT^WyI6hidJ2 zeFz!RLV)w+nVVN$ov#?qmO=saf-|*peFcXkSIDD4dNJ{uZRygpYS6y^Rz*M_|)aBhZ~)hAL=NgfP9gc z_{NC*nG^#p6qrxyy>;dwex_*KCKRAgF@Qg#bupLF>s|M0sDgmt=;|?^7j_ECE~uh_SXf$J%l6B5pJGvk z0rG=41JSoj&nZ5;jVKNX?kLE6a$$?aDhpJBK+IAPYd}759-kwHC=!sp4`TbeBMJzs zP=x}4n?I7W?;UK>XG0YWMC-M-^qp`xmT@ZpQ7|BlWDIq()uo=P<3bb-1YVDd-!>dr zOgoi{C>)4vwpuRYUu84C7*#wV70O{wwjTEL4L}tTsD>*NIj3jSH*P`|5kyQRvv)Y0 zsN>F(M->tZWz|O1*j{%8qlyVsPPKJXxYnmBe5ir~ZO3<3n6wbGx(jZfZMk{gdIF5r#5_67`TMv9{f3`D$BQ()V1LxDHz5k&^dt#6-= z<5{2Oov=U@8id1Zm0yanKTO^^f+#jfw04SBl80{ASfUCJ{L^}iZERksy*O=!C^`tO zJ~XQEk=kTMK@}dvhs3B_wVRLBeHcI#ANU;^v`vzQa|5MM5d{dr3$rhedGBg`y3ZU@ zgb-6bQHdL>tr7sPeT+cME1RN>{Tai(@o(=6e~#T#o?K$2URHUsDcGG#e{Nh zs9sAr4pp=eA=emZb$q?8%!?{qkgT6egy4fOUg=tmDqd7R8a7QeDVA&IL=-S+?+3?g z$XiPLu6Q7d7{n#wevUSaw{&es6*4H*40iP@|KhZ?aYQkLmbv!!It9kg5DQd6gBX;h zZXU75+iWBQQPiNAEWfpcnwjE0poA!F2utaN1s)A5nmJmJC~inl4Gec+?(BF`KtU8Z z_-RLV5e$5st_W$jm`9x@yWx52a*uQ5YfZ#UEG0; zIlXBWL_vgnt$~GjZ_Qfk)u^I~fRZ8`C4sj?ez!5AFe0iQo9fHw?PAv*fGUm{=M`_X zxVh?cTLz*)A|Qdw5|&YG8NKF@D3XX+xJ>aKp=(=dpb90Vvdfbejb3}?(&7=t5-PcD zLS`&kIc&+if(h5IBT|HeM`pL=;gd4R0#!qi+fiZAKMRgw+GzM=8)$&W=wZ ziYY{0V~@iZ>C=6_sDcW`vOghyNahMDy%|wd5ti$bcp?5pRzMC_SdoZ+^U&yvM&D){ zs<^^0{4!l^{I=PvmmG)!i;#rSduiXvB4Jxpkwu(;jGTLl##VjtETYiD-&@`jYy65m zzc>$3Y!S>n$di9UmUk#v2>%ka?L1bFnL_Qhlvs$d#2HYdp<==$);n+7LQcn7w07;l&QG6tI&Ox!_tT8EigCI_;eO z`eW}D(ftZXIj^UK^~10jq7ZKp}Wb&-^k3ZrR?Pm(LhJ{=9(44EK9$od%w_P)orb7Io zEE#Dp&*8fpKfc$oREzRGM)uJ1a4t4Kg0HH2*Cyd_edlAYTiiEDXSvgma{IVMpO@L@ ztzQ!r!`$+%gIC|=C#DoR`>Vc+G>_H&V8U4|^gn`;1xBWzR!@kx8v>UNv>{^Lz`~F! zAc#3-@~r@G|4ufV3n5{r^S{e%V0&1Az^!^o52- zDFcNfpaJ9`3b4%Fwy6COs9OW^zf2xtd*R~Ye}Dg&FM}U2k_Ze(x_~ld&zAw}`N00b zp>gL_ymCLap!;UX=QcLn!w z(VEFyfqU5Jnf#V{eZ* zgSt&B^XaGn9+(&T?`W%kw42Q|rn3NV|F8Ib9Z(iWP!>=<#6HUZvAq+-1lK>BGR8at zdD(${n6hO-`TnL4pbNr5ofu%s0;2~=9}w(s|5Y0{P6kL$2ixE@aG@N5HWla1 zoYD>TpyANafB-sBIt1or|H&r=T2B4L3o+pb%*)Q3Qw8=@fR-t=r}{@YsC?-Hh8om1 zDXPQyOA!;4#S{>fEAXA00fOHj?3YYcOK8Rx7*Y&&iE<49=9r=7Ugu~>BJ$0q02kH^ zjAjgIcf}dp=ltRKr|w`i@P}VCGw!dlfY_bD4}P;uKlE+WF(1Hlr~?D7eW6w$QOiB( zJ+Ki2l^mgJC@|L;!Uzgu06UPtjy5#uJs$-ah4lx0vF0OYe4WGj=5I3fF9@_2<%WI#dn!EOv#7@%F-bLpc1HY?TCOdU_RLu zn7;NwQlq0KQN^a42v^DqJVW?KdA1@G;Hd} zaQ6=LTj1v#5D*9hfDahS1U4b6sVl=!@>HUSMELL1MThh+xR2~0<#kjFri9hD+5ALt8mL9DC-U(k1$lI-_;gVG^5 zI1HHC2eK+8EHDu1Gk@2PhUv|lIYzDf1prYtZ-PF|-wh~Wdjlz#r;Ozkv-ocm==mNCv>0SW= zKBzfc7xW2e3{T(>yFbC5h}hx#GY=#t3}mx zGazQ0!Fk|9-W%|6F6aXYN+~l)q*F#!>>an707A+8N+=Y5LAZ#R>0yoxB(vPnBr+Z9X%kO z)+5r%3<#%ns8rHNgpv&uMeE^L1LNs2<&a^>@B3d5K?r>)gUrE8Fa;uL1L)?c1p0kv z48({9lp^z4LmvY~gczb98)Aca{$3A=4NE9COv6HL;Q0mHfY7i&CB_Dzg%2{cm=_ek zbJzk!g#Nsh0r=Pw*kyfF;O{ZW!*2IF0a=rbS= zR}dDE3LtGjTL9St3I&u5Xg{DbKn;Le0KEY89?&;Hus*OJuq|QRK!R-ud1iY+e^@8j zF0g(~7y2{b0c;Oge`b5bbYWYzNuJj@fe7c(rg4dL%Rc>f5eec0Vx7n1IP%F1t2#-{(vF@r2)zUlnjAP&S|wfX)GG0Mr8LF`$=#h5#|gkw8#hSSKj&f&c{r3IW8F zyT53!XWT(v^abI5+0_KW=B;Y}-GC!7U*Zd9A#1ehD$MkM#RC&@ zWq0o=C*;Qf{PhMAUoLjdK_!kO{rH)cR6gaw z&_y36Iv%n=Bi!n%Zoev=u)GAnMB=RpEYN>)VPlmJ+wFXKE%?Nf-kr)+aPTp*fXa| z3zlE+=r3*P;TyWlodJ%10p@1FbSO&M9oXeZEC&4jeYh*Z6(sr^x(a5*hltp2fbSmZ zLw~cr2?BfjgTpCcXt35YA3pp)2p{N+24{u^FXCU)1_$^kfx$sW7WR$J>$W;s7;bdZ zGu&usX|W)$==u1}^lkti_6sHt_3fc7VDebdnbiURZxvT^^J2L9fCH6))d89L>d8U_ zqak2cCnNhSohXpTzqSczz+dTrUos#aSnY+${7NTsJ{@NHI6?Vfx=bG0%wEWI|G@_V zUV1_JaKPIv;8Oq(>&r}!0r-XOu?g@}3;cNjzc4?ZdA?ttrj75Qy}9x8-Yf`?x!Zj7 zj^J!y#j~G}5Z?6J)oar=5p&;uK0|o!Zl85y`>d~#rUwyTYdRsjN}_YG!t@7(PdRF( zDr3Lp;fCq22w&G)Qk7L1bj)RX8sXbAMi)g$<-d!WCd|S7{464M_8Hzgb7-0y;cf0; zaEKynCD%+-5MIOj=VtXBVpH3+D8ic_Hdq<(?ojrdX=#MNqnW(8EiJSMGoyg;UvnFC z+dFm?h|H)V{7|Kw;9*X+u@y5~2%mXwh1^fimFLW6^bmga+U5uSk6KcFW{eTOzWpI> zsowM3X)~4xf2eiwmHQ`r4iwDTBYbMHYn-%i<ZN=HofhtB3BeAcC*OD7uoOR{E*5I!d^G`+*=^!L-Vl?dOFSC(}k zQ19}s*;<76q4G6;A7|g)KYJD7FQ}?AtQJ?dPtD#!_{xNs&gOx&M<{dm5T13lyxc9K zJy0!vgzyjdhj<^6mM%A(dxr45cQGlS>vqn#%?%>_dm3rKJNwYp__+@Ve=prEfaBiP zJxAugBD?}acwnpG#jf*n(+CgWIgs4>_vd~KWn%H8k&!t#O2W&v^7i}4Dm)_XfjudX zobQpV)+UJ~qaFrxp32r5b3ga`%6aR$f%flW?k|havOp|AVK82aZ!3ycK6vamZ)IFx z>Zst7rKTs+5v$zbyr7@A9;u#m`*;#r(FDJ_s#^A}WW1)6<|(6smdSo6r59PP z0cVnk1+AYFbGY>x`LzSiIT;oyQSjcpp9kxXl=j`;K$`m~wr?rc7vYT}#*Q92<#%#D z79C~NeBx?jA~`iX7U}S-s8R^ zJS|`6!dPZS1}lCV;T2MCN{C|;opd|_NFZQ?TXo;tp(0}5YH&C)Wp_={YLrduCq8t0DZl zOMCK<2`+!shu1>*(;}~H_dgXrK7rRm_$Lwl_5O{A-t!TR5&j?NDt{YiJ%hbN!jOrJ zSqKr?--rWchx2|We;V9FdCTO>|KKbB;LptS|2BTn<>}~XL)W6qsM58x=<2Gn3(`OP zM|yC)fphT%>-+BB;lRWnSaYDmWu*mi;aC92R%Tgs{@| z5)T~)hvH1IVD27W&5XAk%;{j8F?n#3{9oF|_YeLx;Nkp~8Ga1#Mhp0R^Zb9r$1azrr{CYk07}2u?csHPcc9bL#m#uFb~*M^7rs z1ItywewE7K-{oKCX?`w|ASDownMMMr8=P;*Dk))-!qI)VKke(S4*+Xo5y&qg^mcO? zdX5`&Nf#`_fB+14O**Cyqyc{iGQIr5LcziVIF9Id5KJcs0~Jbq!EXh4X)d^*27d5; zGhK8Y3$>uA34Wl)!wBGlVG;nLTQT$Ni|8?Gn#j@_s3zQj0c)XP@e(Yhp#i}Yur?4u z!~h4g6}YghiGT?6lmuLDZAoqLQw+>BLw+}>Ggz7h$7vzk@XmB}sgIKmtVw!=YSKB; z=b-+%AWc}SQs4>#_Zo2FH~H6yFvfgT;Kz*faz5_(d>m$6knFFv5{*RPQ6h-P4G5My z8BhwKR6uli#Q?*Q^cYSB&NzCUj0~s3-#RY*!3Tc40mX>H{$N}S=!QjJFU4_9g>pccK68uh~-O~S)3-)E$j-Nqd62SF<#~g#5D+ah{`mF#R zf!Xh2SS7$q0T;2e3N{D8hd$p;|6+j^Ie-m0`U0*sgMJN9tDKHP`z~)ZSmXGW1~f>D zc61%^9QH$|KN=tPLvjLpE1)~WuLDFv7>qNB1J`=OfeU5A{0;*ebv|G4a}S;#hU~#0 z-w4=(3IU52=vw$+gXVyA;8=PZxbnbFod33&I}Kr>9*Qf$4kx;^w{RgHoG~mY;f4DTFZ>@D z0bUDo{M+Xb))^L_>tY{EMEdLo(08FcT*%8T;8y{l1$>`Of6qU7uX)~1NlD4U6&%>* zPY3E0uusas8(Clp7zkz9N8lotvvU{7mlveh4_sK@*T98+_6u;Kd`Dg_`ZA#?_lJ9p zh_zw#yi9PA$ZF(xFSscg0;fcD8T52kS=g7HF_buT??wW+a7|brxNv=PGjKssf8_%c z|H}x1M*kH*7KDf21+!fL*q-y_e=IaM>~?|=?kv6rcNN!x>%u+ae$3I0>%sM5U$VTy4HHIiqx5mYcia#BPb~M+)oV;l zvyLA>5tDJ?(8+W4J8D?jIMlV*Sbyp4B2a|XHLPtC%Fmo_Sn-m)b64hZ0uL{VEUT)f zslUO{$i&p0VSnMGh$tI7Cl^IfeU)a}>E0(C8rl2G*f>|O@$k;tPYQ5q939){`gMBF z!t!XIlJZg+t78R)r;19;s_L)aV&&qdifOLZ-%?U~uf2dxm?pJ&&D!A)V{^Bg3G_va zm&hz%scB$jY-V9)y>*+tqq7^s!zUy%E@5|3`Pu60ooCMa2V6VgC>6tkCn(}Q@K|M~ z#AGqNDxWApnnRpLo<)zqE0!=qHYIk>q$TE?fstQpct2}vQ;pOC2Y;pv9)+L-9IAvJ)ldJ6*n+U7eNII}f zIh?ed4=cvXOJGmhvy0%v!h`2vBkp(J$PucQ_>D7!J&?K~n!-)tw&4&?+?liypSGTl znrbG&%9{9Co@I?BHc$~yBjA#BBuJVp*rZOmvit#8AIM{H!tcgccdI=;v4?cq6#L7*;@^KSZg38ITOPM5Da*Kho zsI&8epd4(8ZHqZmSut2V3kxeNj*XR_jf2D~$|cNA<00~L^AU)6GMS$PsQ(azu)=s6 zn+R4ECm}$`%i|Tfl&~szRopUcDeg3`oKV631NW0<1~-eJov>|CN>(2IUv3I705F{ilrGiMX7Ton)$m)Nk;#1!`b_=LLKj~+i8|1=RCk`)?u zXo-??>Dh~ocRG6dkLu(eR>_ih@aX=WiRm^wHg-PZQsvPx{{Ri`wd?h>_gQ#`wX}A2 z^*$M%odZV?NhS9alJ(d{2&|-}O5Vf@7730d5xg)vmY_^fBe3DIY^-b~PBT6-n-v?L zAj-*sXUDVQabTq3Ca~bSSh2iR785oRHXAk^t01=-K^LzG#1n~?k6V)HrpQBDC4V?yF? z)`}u-L4s-_A*pW(8#fC}VhJ&6f(=WTV+Fo@35nP6B6vQYf0g0$vfK$GK7bg{PeISl zH3Kpmj+;#09^Aw6<8SGp7Uz)xU=YNGvLEhS!u>M)uow*UnFrt(0s32fxc}nn?TOCh z!8&s)5!uhL1Y-{zKmR@h_76aRhyC5h-P;o!xd;{|)c)ZoB#PKTHxcmQw+4ZZ*~j9H zao{h&kOk=y+;nG&F@*woITgA>K*>u5T$!_?_)ljg%q(5~_?)x)4EB{emQzwgn&*{9 z1+U90Wud}VDxxk1U&ITIb<|wU#E9p?HnnzrDH~I$FT|9Fo6IzrU<5OIPixx;8LLyu?)};aX2ht5msEpj!Tn+11m(p za)2R|MINukE+>Sg1M@Lh0z2q$Y@9eTtR{?3UabxrDQq~NfaAcj;-7*>0HtMvDdN~!IdNDO z2~~m$;90OT9NaiMs3aD@5_p1$cujU3?l2z9gJpvm;&IJ780<|+41O=xnT}!g#$gCp zPCCvE2jnv-yD*Lgn~S57d9X{^g}IdQDxhvS+)}JAXn7ot8`Mh~yBws7!?A#R%i-9u zqp;bqpfeJ|(WxMn0qj8*3?9^yAOqG7i$VGroEd%tmntC!tIj72>dA>$1!=Njweiv{ zSoSqoZk!qi7<8~sc-WMnQLx9bcy=n%vRJGDmX{6Ba+4iaMi90tcvmnS7WW>MjTN{e zI4gGeWIJpR;LE^+m&yXBXSfOQ=0T6ZW`q0)SURT+EApmTad;)r;21X0AXsw&P!y0# zG%Lsev^nT)Fe@wuyj?XG7I=qc<--6`gu$*QYyr>, - // Please note that it is much more efficient to **not** store this - // code in the state, and directly use `DEFAULT_CONTRACT` +pub struct GlobalFactoryContract { // However, this does not enable to update the stored code. + /// Store the hash of deployed global contracts for reference + pub deployed_global_contracts: std::collections::HashMap, + /// Store account IDs that have deployed global contracts + pub global_contract_deployers: std::collections::HashMap, +} + +// Example contract interface that we'll deploy as a global contract +#[ext_contract] +pub trait ExtStatusMessage { + fn set_status(&mut self, message: String); + fn get_status(&self, account_id: AccountId) -> Option; } -// Define the default, which automatically initializes the contract -impl Default for Contract { - fn default() -> Self { - Self { - code: LazyOption::new("code".as_bytes(), Some(DEFAULT_CONTRACT.to_vec())), +#[near] +impl GlobalFactoryContract { + /// Deploy a global contract with the given bytecode, identifiable by its code hash + #[payable] + pub fn deploy_global_contract(&mut self, name: String) -> Promise { + // Assert the sub-account is valid + let current_account = env::current_account_id().to_string(); + let subaccount: AccountId = format!("{name}.{current_account}").parse().unwrap(); + assert!( + env::is_valid_account_id(subaccount.as_bytes()), + "Invalid subaccount" + ); + + // Store the code hash for later reference + let code_bytes: Vec = DEFAULT_CONTRACT.to_vec(); + let code_hash_vec = env::sha256(&code_bytes); + let code_hash: CryptoHash = code_hash_vec.try_into().unwrap(); + self.deployed_global_contracts + .insert(name.clone(), code_hash); + self.global_contract_deployers + .insert(name, subaccount.clone()); + + Promise::new(subaccount) + .create_account() + .transfer(env::attached_deposit()) + .add_full_access_key(env::signer_account_pk()) + .deploy_global_contract(code_bytes) + } + + /// Deploy a global contract, identifiable by the predecessor's account ID + #[payable] + pub fn deploy_global_contract_by_account_id(&mut self, name: String) -> Promise { + // Assert the sub-account is valid + let current_account = env::current_account_id().to_string(); + let subaccount: AccountId = format!("{name}.{current_account}").parse().unwrap(); + assert!( + env::is_valid_account_id(subaccount.as_bytes()), + "Invalid subaccount" + ); + + // Store reference to this deployment + let code_bytes: Vec = DEFAULT_CONTRACT.to_vec(); + let code_hash_vec = env::sha256(&code_bytes); + let code_hash: CryptoHash = code_hash_vec.try_into().unwrap(); + self.deployed_global_contracts + .insert(name.clone(), code_hash); + self.global_contract_deployers + .insert(name, subaccount.clone()); + + Promise::new(subaccount) + .create_account() + .transfer(env::attached_deposit()) + .add_full_access_key(env::signer_account_pk()) + .deploy_global_contract_by_account_id(code_bytes) + } + + /// Use an existing global contract by its code hash + pub fn use_global_contract_by_hash( + &self, + code_hash: Base58CryptoHash, + account_id: AccountId, + ) -> Promise { + Promise::new(account_id) + .create_account() + .transfer(env::attached_deposit()) + .add_full_access_key(env::signer_account_pk()) + .use_global_contract(CryptoHash::from(code_hash).to_vec()) + } + + /// Use an existing global contract by referencing the account that deployed it + pub fn use_global_contract_by_account( + &self, + deployer_account_id: AccountId, + account_id: AccountId, + ) -> Promise { + Promise::new(account_id) + .create_account() + .transfer(env::attached_deposit()) + .add_full_access_key(env::signer_account_pk()) + .use_global_contract_by_account_id(deployer_account_id) + } + + /// Get the code hash of a deployed global contract by name + pub fn get_global_contract_hash(&self, name: String) -> Option { + self.deployed_global_contracts + .get(&name) + .cloned() + .map(|hash| hash.into()) + } + + /// Get the deployer account ID of a global contract by name + pub fn get_global_contract_deployer(&self, name: String) -> Option { + self.global_contract_deployers.get(&name).cloned() + } + + /// List all deployed global contracts + pub fn list_global_contracts(&self) -> Vec<(String, Base58CryptoHash, AccountId)> { + self.deployed_global_contracts + .iter() + .map(|(name, hash)| { + let deployer = self + .global_contract_deployers + .get(name) + .cloned() + .unwrap_or_else(|| "unknown".parse().unwrap()); + (name.clone(), (*hash).into(), deployer) + }) + .collect() + } + + /// Example of calling a status message contract that was deployed as global + pub fn call_global_status_contract(&mut self, account_id: AccountId, message: String) { + ext_status_message::ext(account_id).set_status(message); + } + + /// Example of complex call using global contracts + pub fn complex_global_call(&mut self, account_id: AccountId, message: String) -> Promise { + // 1) call global status_message to record a message from the signer. + // 2) call global status_message to retrieve the message of the signer. + // 3) return that message as its own result. + ext_status_message::ext(account_id.clone()) + .set_status(message) + .then(Self::ext(env::current_account_id()).get_result(account_id)) + } + + #[handle_result] + pub fn get_result( + &self, + account_id: AccountId, + #[callback_result] set_status_result: Result<(), PromiseError>, + ) -> Result { + match set_status_result { + Ok(_) => Ok(ext_status_message::ext(account_id).get_status(env::signer_account_id())), + Err(_) => Err("Failed to set status"), } } } + +#[cfg(test)] +mod tests { + use super::*; + use near_sdk::test_utils::{accounts, VMContextBuilder}; + use near_sdk::{testing_env, AccountId}; + + fn get_context(predecessor_account_id: AccountId) -> near_sdk::VMContext { + VMContextBuilder::new() + .current_account_id(accounts(0)) + .signer_account_id(predecessor_account_id.clone()) + .predecessor_account_id(predecessor_account_id) + .build() + } + + #[test] + fn test_deploy_global_contract() { + let context = get_context(accounts(1)); + testing_env!(context); + + let mut contract = GlobalFactoryContract::default(); + + contract.deploy_global_contract("test_contract".to_string()); + + // Check that the contract was recorded + let stored_hash = contract.get_global_contract_hash("test_contract".to_string()); + assert!(stored_hash.is_some()); + + let expected_hash_vec = near_sdk::env::sha256(&DEFAULT_CONTRACT.to_vec()); + let expected_hash: CryptoHash = expected_hash_vec.try_into().unwrap(); + assert_eq!(stored_hash.unwrap(), expected_hash.into()); + } + + #[test] + fn test_list_global_contracts() { + let context = get_context(accounts(1)); + testing_env!(context); + + let mut contract = GlobalFactoryContract::default(); + + contract.deploy_global_contract("test_contract".to_string()); + + let contracts = contract.list_global_contracts(); + assert_eq!(contracts.len(), 1); + assert_eq!(contracts[0].0, "test_contract"); + assert_eq!(contracts[0].2, format!("test_contract.{}", accounts(0))); + } +} diff --git a/src/manager.rs b/src/manager.rs deleted file mode 100644 index 63dc858..0000000 --- a/src/manager.rs +++ /dev/null @@ -1,19 +0,0 @@ -use near_sdk::{env, near}; - -use crate::{Contract, ContractExt}; - -#[near] -impl Contract { - #[private] - pub fn update_stored_contract(&mut self) { - // This method receives the code to be stored in the contract directly - // from the contract's input. In this way, it avoids the overhead of - // deserializing parameters, which would consume a huge amount of GAS - self.code.set(env::input()); - } - - pub fn get_code(&self) -> &Vec { - // If a contract wants to update themselves, they can ask for the code needed - self.code.get().as_ref().unwrap() - } -} diff --git a/src/status-message-contract/status_message.wasm b/src/status-message-contract/status_message.wasm new file mode 100644 index 0000000000000000000000000000000000000000..bfa0f8365ac7eab6107f4b104b0baf496c033402 GIT binary patch literal 81689 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 KWasV|d;SK#$M0kS literal 0 HcmV?d00001 diff --git a/tests/sandbox.rs b/tests/sandbox.rs deleted file mode 100644 index 0362055..0000000 --- a/tests/sandbox.rs +++ /dev/null @@ -1,81 +0,0 @@ -use near_workspaces::types::{AccountId, NearToken}; -use serde_json::json; - -const TEN_NEAR: NearToken = NearToken::from_near(10); - -#[tokio::test] -async fn main() -> Result<(), Box> { - let sandbox = near_workspaces::sandbox_with_version("2.7.0").await?; - let root = sandbox.root_account()?; - - // Create accounts - let alice = create_subaccount(&root, "alice").await?; - let bob = create_subaccount(&root, "bob").await?; - - let contract_wasm = near_workspaces::compile_project("./").await?; - let contract = sandbox.dev_deploy(&contract_wasm).await?; - - let res_0 = alice.view(contract.id(), "get_code").args_json({}).await?; - - println!("{:?}", res_0); - - // Launch new donation contract through factory - let res = alice - .call(contract.id(), "deploy") - .args_json(json!({"name": "donation_for_alice", "beneficiary": alice.id()})) - .max_gas() - .deposit(NearToken::from_millinear(1700)) - .transact() - .await?; - - println!("{:?}", res); - assert!(res.is_success()); - - let sub_accountid: AccountId = format!("donation_for_alice.{}", contract.id()) - .parse() - .unwrap(); - - let res = bob - .view(&sub_accountid, "get_beneficiary") - .args_json({}) - .await?; - - assert_eq!(res.json::()?, alice.id().clone()); - - let res = bob - .call(&sub_accountid, "donate") - .args_json({}) - .max_gas() - .deposit(NearToken::from_near(5)) - .transact() - .await?; - - assert!(res.is_success()); - - // Try to create new donation contract with insufficient deposit - let res = alice - .call(contract.id(), "deploy") - .args_json(json!({"name": "donation_for_alice_2", "beneficiary": alice.id()})) - .max_gas() - .deposit(NearToken::from_millinear(1500)) - .transact() - .await?; - - assert!(res.is_failure()); - - Ok(()) -} - -async fn create_subaccount( - root: &near_workspaces::Account, - name: &str, -) -> Result> { - let subaccount = root - .create_subaccount(name) - .initial_balance(TEN_NEAR) - .transact() - .await? - .unwrap(); - - Ok(subaccount) -} diff --git a/tests/workspaces.rs b/tests/workspaces.rs new file mode 100644 index 0000000..91f94aa --- /dev/null +++ b/tests/workspaces.rs @@ -0,0 +1,223 @@ +use near_sdk::json_types::Base58CryptoHash; +use near_workspaces::types::{AccountId, NearToken}; + +const DEFAULT_CONTRACT: &[u8] = + include_bytes!("../src/status-message-contract/status_message.wasm"); + +const STORAGE_DEPOSIT_PER_BYTE: NearToken = NearToken::from_near(1).saturating_div(100_000); +const GLOBAL_STORAGE_COST_PER_BYTE: NearToken = STORAGE_DEPOSIT_PER_BYTE.saturating_mul(10); + +/// Test basic global contract deployment functionality +#[tokio::test] +async fn test_deploy_global_contract() -> anyhow::Result<()> { + // Initialize the sandbox environment with specific commit that includes global contract support + println!("Initializing worker"); + let worker = near_workspaces::sandbox_with_version("2.7.0").await?; + + println!("Deploying global contract"); + + // Compile and deploy the factory contract + let factory_wasm = near_workspaces::compile_project(".").await?; + let factory_contract = worker.dev_deploy(&factory_wasm).await?; + + let status_code = DEFAULT_CONTRACT.to_vec(); + + let res = factory_contract + .call("deploy_global_contract") + .args_json(("status_message",)) + .max_gas() + .deposit(GLOBAL_STORAGE_COST_PER_BYTE.saturating_mul(status_code.len().try_into().unwrap())) + .transact() + .await?; + println!("Deployed global contract: {res:?}"); + + assert!( + res.is_success(), + "Failed to deploy global contract: {res:?}" + ); + + // Verify the global contract was recorded + let stored_hash = factory_contract + .call("get_global_contract_hash") + .args_json(("status_message",)) + .view() + .await? + .json::>()?; + + assert!( + stored_hash.is_some(), + "Global contract hash should be stored" + ); + + // Verify we can list the deployed global contracts + let contracts_list = factory_contract + .call("list_global_contracts") + .view() + .await? + .json::>()?; + + assert_eq!(contracts_list.len(), 1); + assert_eq!(contracts_list[0].0, "status_message"); + + Ok(()) +} + +/// Test using a global contract by hash +#[tokio::test] +async fn test_use_global_contract_by_hash() -> anyhow::Result<()> { + let worker = near_workspaces::sandbox_with_version("2.7.0").await?; + let factory_wasm = near_workspaces::compile_project(".").await?; + let factory_contract = worker.dev_deploy(&factory_wasm).await?; + + // First deploy a global contract + let status_code = DEFAULT_CONTRACT.to_vec(); + + let res = factory_contract + .call("deploy_global_contract") + .args_json(("status_message",)) + .max_gas() + .deposit(GLOBAL_STORAGE_COST_PER_BYTE.saturating_mul(status_code.len().try_into().unwrap())) + .transact() + .await?; + + assert!(res.is_success()); + + // Get the hash of the deployed global contract + let stored_hash = factory_contract + .call("get_global_contract_hash") + .args_json(("status_message",)) + .view() + .await? + .json::>()? + .expect("Should have stored hash"); + + // Now use the global contract by hash + let user_account_id: AccountId = format!("user.{}", factory_contract.id()).parse()?; + + let res = factory_contract + .call("use_global_contract_by_hash") + .args_json((stored_hash, &user_account_id)) + .max_gas() + .deposit(NearToken::from_millinear(1)) + .transact() + .await?; + + assert!( + res.is_success(), + "Failed to use global contract by hash: {:?}", + res.outcome() + ); + + Ok(()) +} + +/// Test using a global contract by account ID +#[tokio::test] +async fn test_use_global_contract_by_account() -> anyhow::Result<()> { + let worker = near_workspaces::sandbox_with_version("2.7.0").await?; + let factory_wasm = near_workspaces::compile_project(".").await?; + let factory_contract = worker.dev_deploy(&factory_wasm).await?; + + // First deploy a global contract + let status_code = DEFAULT_CONTRACT.to_vec(); + + let global_account_id: AccountId = + format!("status_message.{}", factory_contract.id()).parse()?; + + let res = factory_contract + .call("deploy_global_contract_by_account_id") + .args_json(("status_message",)) + .max_gas() + .deposit(GLOBAL_STORAGE_COST_PER_BYTE.saturating_mul(status_code.len().try_into().unwrap())) + .transact() + .await?; + + assert!( + res.is_success(), + "Failed to deploy global contract by account: {res:?}" + ); + + // Now use the global contract by account ID + let user_account_id: AccountId = format!("user.{}", factory_contract.id()).parse()?; + + let res = factory_contract + .call("use_global_contract_by_account") + .args_json((&global_account_id, &user_account_id)) + .max_gas() + .deposit(NearToken::from_millinear(1)) + .transact() + .await?; + + assert!( + res.is_success(), + "Failed to use global contract by account: {res:?}" + ); + + Ok(()) +} + +/// Test error cases and edge conditions +#[tokio::test] +async fn test_global_contract_edge_cases() -> anyhow::Result<()> { + let worker = near_workspaces::sandbox_with_version("2.7.0").await?; + let factory_wasm = near_workspaces::compile_project(".").await?; + let factory_contract = worker.dev_deploy(&factory_wasm).await?; + + // Test getting hash for non-existent contract + let non_existent_hash = factory_contract + .call("get_global_contract_hash") + .args_json(("non_existent",)) + .view() + .await? + .json::>()?; + + assert!( + non_existent_hash.is_none(), + "Should return None for non-existent contract" + ); + + // Test listing contracts when none are deployed + let empty_list = factory_contract + .call("list_global_contracts") + .view() + .await? + .json::>()?; + + assert!( + empty_list.is_empty(), + "Should return empty list when no contracts deployed" + ); + + // Test using non-existent contract + let user_account_id: AccountId = format!("user.{}", factory_contract.id()).parse()?; + let res = factory_contract + .call("use_global_contract_by_hash") + .args_json(("11111111111111111111111111111111", &user_account_id)) + .max_gas() + .deposit(NearToken::from_millinear(100)) + .transact() + .await?; + assert!( + res.is_failure(), + "Not failed to use global contract by hash: {res:?}" + ); + + // Test using non-existent contract + let global_contract_account_id: AccountId = + format!("non-existent-global.{}", factory_contract.id()).parse()?; + let user_account_id: AccountId = format!("user.{}", factory_contract.id()).parse()?; + let res = factory_contract + .call("use_global_contract_by_account") + .args_json((&global_contract_account_id, &user_account_id)) + .max_gas() + .deposit(NearToken::from_millinear(100)) + .transact() + .await?; + + assert!( + res.is_failure(), + "Not failed to use global contract by account: {res:?}" + ); + + Ok(()) +} From 86b64e5e781f92cc851ff11faf807a67a777fcf9 Mon Sep 17 00:00:00 2001 From: garikbesson Date: Wed, 27 Aug 2025 11:22:11 +0100 Subject: [PATCH 04/13] fix workflow tests --- .github/workflows/tests.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c9031dc..b65d0b0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -7,7 +7,15 @@ jobs: platform: [ubuntu-latest, macos-latest] runs-on: ${{ matrix.platform }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + + - name: Install Linux dependencies + if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get install -y libudev-dev pkg-config + + - name: Install cargo-near + run: cargo install --locked cargo-near + - name: Install and test modules run: | cargo test From 3a00574949dbe7b913d571647b36c52fe6b083f7 Mon Sep 17 00:00:00 2001 From: garikbesson Date: Wed, 27 Aug 2025 11:33:51 +0100 Subject: [PATCH 05/13] fix rust-toolchain version --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index a82ade3..2c35bd8 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "stable" +channel = "1.87" components = ["rustfmt"] targets = ["wasm32-unknown-unknown"] From 44d661751bb0d0232014006f9965fc5e0093843b Mon Sep 17 00:00:00 2001 From: garikbesson Date: Fri, 29 Aug 2025 12:18:29 +0100 Subject: [PATCH 06/13] rework the idea of the factory example --- Cargo.toml | 1 + rust-toolchain.toml | 2 +- src/lib.rs | 206 ++++++++++++++++---------------------------- src/manager.rs | 20 +++++ 4 files changed, 95 insertions(+), 134 deletions(-) create mode 100644 src/manager.rs diff --git a/Cargo.toml b/Cargo.toml index ffa7069..bedfa00 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" crate-type = ["cdylib", "rlib"] [dependencies] +borsh = "1.5.7" near-sdk = { version = "5.17.1", features = ["global-contracts", "unstable"] } [dev-dependencies] diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 2c35bd8..59615fb 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.87" +channel = "1.86" components = ["rustfmt"] targets = ["wasm32-unknown-unknown"] diff --git a/src/lib.rs b/src/lib.rs index c8119f6..691ddfb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,57 +1,44 @@ +use std::sync::Arc; + +use near_sdk::borsh::{BorshDeserialize, BorshSerialize}; use near_sdk::json_types::Base58CryptoHash; -use near_sdk::{env, ext_contract, near, AccountId, CryptoHash, Promise, PromiseError}; +use near_sdk::{ + borsh, env, ext_contract, near, store, AccountId, CryptoHash, Promise, PromiseError, +}; + +mod manager; -const DEFAULT_CONTRACT: &[u8] = include_bytes!("./status-message-contract/status_message.wasm"); +const DEFAULT_GLOBAL_CONTRACT_ID: &str = "ft.globals.primitives.testnet"; + +#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, PartialEq, Eq)] +pub enum GlobalContractId { + AccountId(AccountId), + CodeHash(String), +} -#[derive(Default)] #[near(contract_state)] pub struct GlobalFactoryContract { - // However, this does not enable to update the stored code. + pub global_contract_id: GlobalContractId, /// Store the hash of deployed global contracts for reference - pub deployed_global_contracts: std::collections::HashMap, - /// Store account IDs that have deployed global contracts - pub global_contract_deployers: std::collections::HashMap, + pub deployed_global_contracts: store::IterableMap, } -// Example contract interface that we'll deploy as a global contract -#[ext_contract] -pub trait ExtStatusMessage { - fn set_status(&mut self, message: String); - fn get_status(&self, account_id: AccountId) -> Option; +impl Default for GlobalFactoryContract { + fn default() -> Self { + Self { + global_contract_id: GlobalContractId::AccountId( + DEFAULT_GLOBAL_CONTRACT_ID.parse().unwrap(), + ), + deployed_global_contracts: store::IterableMap::new(b"d".to_vec()), + } + } } #[near] impl GlobalFactoryContract { /// Deploy a global contract with the given bytecode, identifiable by its code hash #[payable] - pub fn deploy_global_contract(&mut self, name: String) -> Promise { - // Assert the sub-account is valid - let current_account = env::current_account_id().to_string(); - let subaccount: AccountId = format!("{name}.{current_account}").parse().unwrap(); - assert!( - env::is_valid_account_id(subaccount.as_bytes()), - "Invalid subaccount" - ); - - // Store the code hash for later reference - let code_bytes: Vec = DEFAULT_CONTRACT.to_vec(); - let code_hash_vec = env::sha256(&code_bytes); - let code_hash: CryptoHash = code_hash_vec.try_into().unwrap(); - self.deployed_global_contracts - .insert(name.clone(), code_hash); - self.global_contract_deployers - .insert(name, subaccount.clone()); - - Promise::new(subaccount) - .create_account() - .transfer(env::attached_deposit()) - .add_full_access_key(env::signer_account_pk()) - .deploy_global_contract(code_bytes) - } - - /// Deploy a global contract, identifiable by the predecessor's account ID - #[payable] - pub fn deploy_global_contract_by_account_id(&mut self, name: String) -> Promise { + pub fn deploy(&mut self, name: String) -> Promise { // Assert the sub-account is valid let current_account = env::current_account_id().to_string(); let subaccount: AccountId = format!("{name}.{current_account}").parse().unwrap(); @@ -60,101 +47,51 @@ impl GlobalFactoryContract { "Invalid subaccount" ); - // Store reference to this deployment - let code_bytes: Vec = DEFAULT_CONTRACT.to_vec(); - let code_hash_vec = env::sha256(&code_bytes); - let code_hash: CryptoHash = code_hash_vec.try_into().unwrap(); self.deployed_global_contracts - .insert(name.clone(), code_hash); - self.global_contract_deployers - .insert(name, subaccount.clone()); - - Promise::new(subaccount) - .create_account() - .transfer(env::attached_deposit()) - .add_full_access_key(env::signer_account_pk()) - .deploy_global_contract_by_account_id(code_bytes) - } - - /// Use an existing global contract by its code hash - pub fn use_global_contract_by_hash( - &self, - code_hash: Base58CryptoHash, - account_id: AccountId, - ) -> Promise { - Promise::new(account_id) - .create_account() - .transfer(env::attached_deposit()) - .add_full_access_key(env::signer_account_pk()) - .use_global_contract(CryptoHash::from(code_hash).to_vec()) - } - - /// Use an existing global contract by referencing the account that deployed it - pub fn use_global_contract_by_account( - &self, - deployer_account_id: AccountId, - account_id: AccountId, - ) -> Promise { - Promise::new(account_id) - .create_account() - .transfer(env::attached_deposit()) - .add_full_access_key(env::signer_account_pk()) - .use_global_contract_by_account_id(deployer_account_id) - } - - /// Get the code hash of a deployed global contract by name - pub fn get_global_contract_hash(&self, name: String) -> Option { - self.deployed_global_contracts - .get(&name) - .cloned() - .map(|hash| hash.into()) - } - - /// Get the deployer account ID of a global contract by name - pub fn get_global_contract_deployer(&self, name: String) -> Option { - self.global_contract_deployers.get(&name).cloned() + .insert(subaccount.clone(), self.global_contract_id.clone()); + + match self.global_contract_id { + GlobalContractId::AccountId(ref account_id) => { + env::log_str(&format!( + "Using global contract deployed by account: {}", + account_id + )); + + Promise::new(subaccount) + .create_account() + .transfer(env::attached_deposit()) + .add_full_access_key(env::signer_account_pk()) + .use_global_contract_by_account_id(account_id.clone()) + } + GlobalContractId::CodeHash(ref code_hash) => { + env::log_str(&format!( + "Using global contract with code hash: {:?}", + code_hash + )); + Promise::new(subaccount) + .create_account() + .transfer(env::attached_deposit()) + .add_full_access_key(env::signer_account_pk()) + .use_global_contract(code_hash.as_bytes().to_vec()) + } + } } /// List all deployed global contracts - pub fn list_global_contracts(&self) -> Vec<(String, Base58CryptoHash, AccountId)> { + pub fn list_global_contracts(&self) -> Vec<(AccountId, GlobalContractId)> { self.deployed_global_contracts .iter() - .map(|(name, hash)| { - let deployer = self - .global_contract_deployers - .get(name) - .cloned() - .unwrap_or_else(|| "unknown".parse().unwrap()); - (name.clone(), (*hash).into(), deployer) + .map(|(account_id, global_contract_id)| { + (account_id.clone(), (global_contract_id.clone())) }) .collect() } - /// Example of calling a status message contract that was deployed as global - pub fn call_global_status_contract(&mut self, account_id: AccountId, message: String) { - ext_status_message::ext(account_id).set_status(message); - } - - /// Example of complex call using global contracts - pub fn complex_global_call(&mut self, account_id: AccountId, message: String) -> Promise { - // 1) call global status_message to record a message from the signer. - // 2) call global status_message to retrieve the message of the signer. - // 3) return that message as its own result. - ext_status_message::ext(account_id.clone()) - .set_status(message) - .then(Self::ext(env::current_account_id()).get_result(account_id)) - } - - #[handle_result] - pub fn get_result( + pub fn get_deployed_contract_global_id( &self, account_id: AccountId, - #[callback_result] set_status_result: Result<(), PromiseError>, - ) -> Result { - match set_status_result { - Ok(_) => Ok(ext_status_message::ext(account_id).get_status(env::signer_account_id())), - Err(_) => Err("Failed to set status"), - } + ) -> Option { + self.deployed_global_contracts.get(&account_id).cloned() } } @@ -173,21 +110,24 @@ mod tests { } #[test] - fn test_deploy_global_contract() { + fn test_deploy() { let context = get_context(accounts(1)); testing_env!(context); let mut contract = GlobalFactoryContract::default(); - contract.deploy_global_contract("test_contract".to_string()); + contract.deploy("test_contract".to_string()); // Check that the contract was recorded - let stored_hash = contract.get_global_contract_hash("test_contract".to_string()); - assert!(stored_hash.is_some()); + let stored_id = contract.get_deployed_contract_global_id( + format!("test_contract.{}", accounts(0)).parse().unwrap(), + ); + assert!(stored_id.is_some()); - let expected_hash_vec = near_sdk::env::sha256(&DEFAULT_CONTRACT.to_vec()); - let expected_hash: CryptoHash = expected_hash_vec.try_into().unwrap(); - assert_eq!(stored_hash.unwrap(), expected_hash.into()); + assert_eq!( + stored_id.unwrap(), + GlobalContractId::AccountId(DEFAULT_GLOBAL_CONTRACT_ID.parse().unwrap()), + ); } #[test] @@ -197,11 +137,11 @@ mod tests { let mut contract = GlobalFactoryContract::default(); - contract.deploy_global_contract("test_contract".to_string()); + contract.deploy("test_contract".to_string()); let contracts = contract.list_global_contracts(); assert_eq!(contracts.len(), 1); - assert_eq!(contracts[0].0, "test_contract"); - assert_eq!(contracts[0].2, format!("test_contract.{}", accounts(0))); + assert_eq!(contracts[0].0, format!("test_contract.{}", accounts(0))); + assert_eq!(contracts[0].1, contract.get_global_contract_id()); } } diff --git a/src/manager.rs b/src/manager.rs new file mode 100644 index 0000000..a2fdaa6 --- /dev/null +++ b/src/manager.rs @@ -0,0 +1,20 @@ +use near_sdk::{near, AccountId}; + +use crate::{GlobalContractId, GlobalFactoryContract, GlobalFactoryContractExt}; + +#[near] +impl GlobalFactoryContract { + #[private] + pub fn update_global_contract_id(&mut self, contract_id: String, as_hash: bool) { + if as_hash { + self.global_contract_id = GlobalContractId::CodeHash(contract_id); + } else { + let account_id: AccountId = contract_id.parse().expect("Invalid account ID"); + self.global_contract_id = GlobalContractId::AccountId(account_id); + } + } + + pub fn get_global_contract_id(&self) -> GlobalContractId { + self.global_contract_id.clone() + } +} From d93e2afb758af781522cdda4f16d17df34943546 Mon Sep 17 00:00:00 2001 From: garikbesson Date: Fri, 29 Aug 2025 12:25:33 +0100 Subject: [PATCH 07/13] remove stored contract --- src/status-message-contract/status_message.wasm | Bin 81689 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/status-message-contract/status_message.wasm diff --git a/src/status-message-contract/status_message.wasm b/src/status-message-contract/status_message.wasm deleted file mode 100644 index bfa0f8365ac7eab6107f4b104b0baf496c033402..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 81689 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 KWasV|d;SK#$M0kS From d41846fff576ae07183aa3428205b6c6e8abc254 Mon Sep 17 00:00:00 2001 From: garikbesson Date: Mon, 1 Sep 2025 19:15:47 +0100 Subject: [PATCH 08/13] refactor, adjust tests --- src/lib.rs | 110 ++++------------------- src/manager.rs | 4 +- tests/workspaces.rs | 215 ++++++++------------------------------------ 3 files changed, 56 insertions(+), 273 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 691ddfb..0d9e51e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,26 +1,28 @@ -use std::sync::Arc; - -use near_sdk::borsh::{BorshDeserialize, BorshSerialize}; -use near_sdk::json_types::Base58CryptoHash; -use near_sdk::{ - borsh, env, ext_contract, near, store, AccountId, CryptoHash, Promise, PromiseError, -}; +use near_sdk::{env, near, AccountId, Promise}; mod manager; const DEFAULT_GLOBAL_CONTRACT_ID: &str = "ft.globals.primitives.testnet"; -#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] +#[near(serializers = [borsh, json])] pub enum GlobalContractId { AccountId(AccountId), CodeHash(String), } +impl ToString for GlobalContractId { + fn to_string(&self) -> String { + match self { + GlobalContractId::AccountId(account_id) => account_id.to_string(), + GlobalContractId::CodeHash(code_hash) => code_hash.clone(), + } + } +} + #[near(contract_state)] pub struct GlobalFactoryContract { pub global_contract_id: GlobalContractId, - /// Store the hash of deployed global contracts for reference - pub deployed_global_contracts: store::IterableMap, } impl Default for GlobalFactoryContract { @@ -29,7 +31,6 @@ impl Default for GlobalFactoryContract { global_contract_id: GlobalContractId::AccountId( DEFAULT_GLOBAL_CONTRACT_ID.parse().unwrap(), ), - deployed_global_contracts: store::IterableMap::new(b"d".to_vec()), } } } @@ -47,9 +48,10 @@ impl GlobalFactoryContract { "Invalid subaccount" ); - self.deployed_global_contracts - .insert(subaccount.clone(), self.global_contract_id.clone()); - + let promise = Promise::new(subaccount) + .create_account() + .transfer(env::attached_deposit()) + .add_full_access_key(env::signer_account_pk()); match self.global_contract_id { GlobalContractId::AccountId(ref account_id) => { env::log_str(&format!( @@ -57,91 +59,15 @@ impl GlobalFactoryContract { account_id )); - Promise::new(subaccount) - .create_account() - .transfer(env::attached_deposit()) - .add_full_access_key(env::signer_account_pk()) - .use_global_contract_by_account_id(account_id.clone()) + promise.use_global_contract_by_account_id(account_id.clone()) } GlobalContractId::CodeHash(ref code_hash) => { env::log_str(&format!( "Using global contract with code hash: {:?}", code_hash )); - Promise::new(subaccount) - .create_account() - .transfer(env::attached_deposit()) - .add_full_access_key(env::signer_account_pk()) - .use_global_contract(code_hash.as_bytes().to_vec()) + promise.use_global_contract(code_hash.as_bytes().to_vec()) } } } - - /// List all deployed global contracts - pub fn list_global_contracts(&self) -> Vec<(AccountId, GlobalContractId)> { - self.deployed_global_contracts - .iter() - .map(|(account_id, global_contract_id)| { - (account_id.clone(), (global_contract_id.clone())) - }) - .collect() - } - - pub fn get_deployed_contract_global_id( - &self, - account_id: AccountId, - ) -> Option { - self.deployed_global_contracts.get(&account_id).cloned() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use near_sdk::test_utils::{accounts, VMContextBuilder}; - use near_sdk::{testing_env, AccountId}; - - fn get_context(predecessor_account_id: AccountId) -> near_sdk::VMContext { - VMContextBuilder::new() - .current_account_id(accounts(0)) - .signer_account_id(predecessor_account_id.clone()) - .predecessor_account_id(predecessor_account_id) - .build() - } - - #[test] - fn test_deploy() { - let context = get_context(accounts(1)); - testing_env!(context); - - let mut contract = GlobalFactoryContract::default(); - - contract.deploy("test_contract".to_string()); - - // Check that the contract was recorded - let stored_id = contract.get_deployed_contract_global_id( - format!("test_contract.{}", accounts(0)).parse().unwrap(), - ); - assert!(stored_id.is_some()); - - assert_eq!( - stored_id.unwrap(), - GlobalContractId::AccountId(DEFAULT_GLOBAL_CONTRACT_ID.parse().unwrap()), - ); - } - - #[test] - fn test_list_global_contracts() { - let context = get_context(accounts(1)); - testing_env!(context); - - let mut contract = GlobalFactoryContract::default(); - - contract.deploy("test_contract".to_string()); - - let contracts = contract.list_global_contracts(); - assert_eq!(contracts.len(), 1); - assert_eq!(contracts[0].0, format!("test_contract.{}", accounts(0))); - assert_eq!(contracts[0].1, contract.get_global_contract_id()); - } } diff --git a/src/manager.rs b/src/manager.rs index a2fdaa6..87419ad 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -14,7 +14,7 @@ impl GlobalFactoryContract { } } - pub fn get_global_contract_id(&self) -> GlobalContractId { - self.global_contract_id.clone() + pub fn get_global_contract_id(&self) -> String { + self.global_contract_id.to_string() } } diff --git a/tests/workspaces.rs b/tests/workspaces.rs index 91f94aa..9efe288 100644 --- a/tests/workspaces.rs +++ b/tests/workspaces.rs @@ -1,158 +1,51 @@ -use near_sdk::json_types::Base58CryptoHash; -use near_workspaces::types::{AccountId, NearToken}; +const DEFAULT_GLOBAL_CONTRACT_ACCOUNT_ID: &str = "ft.globals.primitives.testnet"; +const DEFAULT_GLOBAL_CONTRACT_HASH: &str = "3vaopJ7aRoivvzZLngPQRBEd8VJr2zPLTxQfnRCoFgNX"; -const DEFAULT_CONTRACT: &[u8] = - include_bytes!("../src/status-message-contract/status_message.wasm"); +/// TODO: add tests for deploy method as soon as near-workspaces-rs supports deploying global contracts. +/// Currently it does not, therefore it's impossible to deploy global contract to use it in tests. -const STORAGE_DEPOSIT_PER_BYTE: NearToken = NearToken::from_near(1).saturating_div(100_000); -const GLOBAL_STORAGE_COST_PER_BYTE: NearToken = STORAGE_DEPOSIT_PER_BYTE.saturating_mul(10); - -/// Test basic global contract deployment functionality +/// Test management of global contract ID #[tokio::test] -async fn test_deploy_global_contract() -> anyhow::Result<()> { - // Initialize the sandbox environment with specific commit that includes global contract support - println!("Initializing worker"); +async fn test_manager() -> anyhow::Result<()> { let worker = near_workspaces::sandbox_with_version("2.7.0").await?; - - println!("Deploying global contract"); - - // Compile and deploy the factory contract let factory_wasm = near_workspaces::compile_project(".").await?; let factory_contract = worker.dev_deploy(&factory_wasm).await?; - let status_code = DEFAULT_CONTRACT.to_vec(); - - let res = factory_contract - .call("deploy_global_contract") - .args_json(("status_message",)) + let change_contract_id_res_1 = factory_contract + .call("update_global_contract_id") + .args_json((DEFAULT_GLOBAL_CONTRACT_HASH.to_string(), true)) .max_gas() - .deposit(GLOBAL_STORAGE_COST_PER_BYTE.saturating_mul(status_code.len().try_into().unwrap())) .transact() .await?; - println!("Deployed global contract: {res:?}"); + println!("change_contract_id_res: {change_contract_id_res_1:?}"); + assert!(change_contract_id_res_1.is_success()); - assert!( - res.is_success(), - "Failed to deploy global contract: {res:?}" - ); - - // Verify the global contract was recorded - let stored_hash = factory_contract - .call("get_global_contract_hash") - .args_json(("status_message",)) + let global_contract_id = factory_contract + .call("get_global_contract_id") + .args_json(()) .view() .await? - .json::>()?; - - assert!( - stored_hash.is_some(), - "Global contract hash should be stored" - ); + .json::>()? + .expect("Should have stored global contract ID"); + assert_eq!(global_contract_id, DEFAULT_GLOBAL_CONTRACT_HASH); - // Verify we can list the deployed global contracts - let contracts_list = factory_contract - .call("list_global_contracts") - .view() - .await? - .json::>()?; - - assert_eq!(contracts_list.len(), 1); - assert_eq!(contracts_list[0].0, "status_message"); - - Ok(()) -} - -/// Test using a global contract by hash -#[tokio::test] -async fn test_use_global_contract_by_hash() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox_with_version("2.7.0").await?; - let factory_wasm = near_workspaces::compile_project(".").await?; - let factory_contract = worker.dev_deploy(&factory_wasm).await?; - - // First deploy a global contract - let status_code = DEFAULT_CONTRACT.to_vec(); - - let res = factory_contract - .call("deploy_global_contract") - .args_json(("status_message",)) + let change_contract_id_res_2 = factory_contract + .call("update_global_contract_id") + .args_json((DEFAULT_GLOBAL_CONTRACT_ACCOUNT_ID.to_string(), false)) .max_gas() - .deposit(GLOBAL_STORAGE_COST_PER_BYTE.saturating_mul(status_code.len().try_into().unwrap())) .transact() .await?; + println!("change_contract_id_res: {change_contract_id_res_2:?}"); + assert!(change_contract_id_res_2.is_success()); - assert!(res.is_success()); - - // Get the hash of the deployed global contract - let stored_hash = factory_contract - .call("get_global_contract_hash") - .args_json(("status_message",)) + let global_contract_id = factory_contract + .call("get_global_contract_id") + .args_json(()) .view() .await? - .json::>()? - .expect("Should have stored hash"); - - // Now use the global contract by hash - let user_account_id: AccountId = format!("user.{}", factory_contract.id()).parse()?; - - let res = factory_contract - .call("use_global_contract_by_hash") - .args_json((stored_hash, &user_account_id)) - .max_gas() - .deposit(NearToken::from_millinear(1)) - .transact() - .await?; - - assert!( - res.is_success(), - "Failed to use global contract by hash: {:?}", - res.outcome() - ); - - Ok(()) -} - -/// Test using a global contract by account ID -#[tokio::test] -async fn test_use_global_contract_by_account() -> anyhow::Result<()> { - let worker = near_workspaces::sandbox_with_version("2.7.0").await?; - let factory_wasm = near_workspaces::compile_project(".").await?; - let factory_contract = worker.dev_deploy(&factory_wasm).await?; - - // First deploy a global contract - let status_code = DEFAULT_CONTRACT.to_vec(); - - let global_account_id: AccountId = - format!("status_message.{}", factory_contract.id()).parse()?; - - let res = factory_contract - .call("deploy_global_contract_by_account_id") - .args_json(("status_message",)) - .max_gas() - .deposit(GLOBAL_STORAGE_COST_PER_BYTE.saturating_mul(status_code.len().try_into().unwrap())) - .transact() - .await?; - - assert!( - res.is_success(), - "Failed to deploy global contract by account: {res:?}" - ); - - // Now use the global contract by account ID - let user_account_id: AccountId = format!("user.{}", factory_contract.id()).parse()?; - - let res = factory_contract - .call("use_global_contract_by_account") - .args_json((&global_account_id, &user_account_id)) - .max_gas() - .deposit(NearToken::from_millinear(1)) - .transact() - .await?; - - assert!( - res.is_success(), - "Failed to use global contract by account: {res:?}" - ); - + .json::>()? + .expect("Should have stored global contract ID"); + assert_eq!(global_contract_id, DEFAULT_GLOBAL_CONTRACT_ACCOUNT_ID); Ok(()) } @@ -163,60 +56,24 @@ async fn test_global_contract_edge_cases() -> anyhow::Result<()> { let factory_wasm = near_workspaces::compile_project(".").await?; let factory_contract = worker.dev_deploy(&factory_wasm).await?; - // Test getting hash for non-existent contract - let non_existent_hash = factory_contract - .call("get_global_contract_hash") - .args_json(("non_existent",)) - .view() - .await? - .json::>()?; - - assert!( - non_existent_hash.is_none(), - "Should return None for non-existent contract" - ); - - // Test listing contracts when none are deployed - let empty_list = factory_contract - .call("list_global_contracts") - .view() - .await? - .json::>()?; - - assert!( - empty_list.is_empty(), - "Should return empty list when no contracts deployed" - ); - - // Test using non-existent contract - let user_account_id: AccountId = format!("user.{}", factory_contract.id()).parse()?; - let res = factory_contract - .call("use_global_contract_by_hash") - .args_json(("11111111111111111111111111111111", &user_account_id)) + let change_contract_id_res = factory_contract + .call("update_global_contract_id") + .args_json(("11111111111111111111111111111111".to_string(), true)) .max_gas() - .deposit(NearToken::from_millinear(100)) .transact() .await?; - assert!( - res.is_failure(), - "Not failed to use global contract by hash: {res:?}" - ); + assert!(change_contract_id_res.is_success()); - // Test using non-existent contract - let global_contract_account_id: AccountId = - format!("non-existent-global.{}", factory_contract.id()).parse()?; - let user_account_id: AccountId = format!("user.{}", factory_contract.id()).parse()?; + // Test using non-existent global contract let res = factory_contract - .call("use_global_contract_by_account") - .args_json((&global_contract_account_id, &user_account_id)) + .call("deploy") + .args_json(("new_ft",)) .max_gas() - .deposit(NearToken::from_millinear(100)) .transact() .await?; - assert!( res.is_failure(), - "Not failed to use global contract by account: {res:?}" + "Not failed to use global contract by hash: {res:?}" ); Ok(()) From 32248a4af5d6b5886d75d0cd4b1622aa4fbccdd7 Mon Sep 17 00:00:00 2001 From: garikbesson Date: Mon, 1 Sep 2025 19:34:37 +0100 Subject: [PATCH 09/13] add to/from to GlobalContractId enum --- src/lib.rs | 10 ++++++++++ src/manager.rs | 11 +++-------- tests/workspaces.rs | 6 +++--- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0d9e51e..39bb810 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,16 @@ impl ToString for GlobalContractId { } } +impl From for GlobalContractId { + fn from(s: String) -> Self { + if s.parse::().is_ok() { + GlobalContractId::AccountId(s.parse().unwrap()) + } else { + GlobalContractId::CodeHash(s) + } + } +} + #[near(contract_state)] pub struct GlobalFactoryContract { pub global_contract_id: GlobalContractId, diff --git a/src/manager.rs b/src/manager.rs index 87419ad..9c867bb 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -1,17 +1,12 @@ -use near_sdk::{near, AccountId}; +use near_sdk::near; use crate::{GlobalContractId, GlobalFactoryContract, GlobalFactoryContractExt}; #[near] impl GlobalFactoryContract { #[private] - pub fn update_global_contract_id(&mut self, contract_id: String, as_hash: bool) { - if as_hash { - self.global_contract_id = GlobalContractId::CodeHash(contract_id); - } else { - let account_id: AccountId = contract_id.parse().expect("Invalid account ID"); - self.global_contract_id = GlobalContractId::AccountId(account_id); - } + pub fn update_global_contract_id(&mut self, contract_id: String) { + self.global_contract_id = GlobalContractId::from(contract_id); } pub fn get_global_contract_id(&self) -> String { diff --git a/tests/workspaces.rs b/tests/workspaces.rs index 9efe288..ed8843e 100644 --- a/tests/workspaces.rs +++ b/tests/workspaces.rs @@ -13,7 +13,7 @@ async fn test_manager() -> anyhow::Result<()> { let change_contract_id_res_1 = factory_contract .call("update_global_contract_id") - .args_json((DEFAULT_GLOBAL_CONTRACT_HASH.to_string(), true)) + .args_json((DEFAULT_GLOBAL_CONTRACT_HASH.to_string(),)) .max_gas() .transact() .await?; @@ -31,7 +31,7 @@ async fn test_manager() -> anyhow::Result<()> { let change_contract_id_res_2 = factory_contract .call("update_global_contract_id") - .args_json((DEFAULT_GLOBAL_CONTRACT_ACCOUNT_ID.to_string(), false)) + .args_json((DEFAULT_GLOBAL_CONTRACT_ACCOUNT_ID.to_string(),)) .max_gas() .transact() .await?; @@ -58,7 +58,7 @@ async fn test_global_contract_edge_cases() -> anyhow::Result<()> { let change_contract_id_res = factory_contract .call("update_global_contract_id") - .args_json(("11111111111111111111111111111111".to_string(), true)) + .args_json(("11111111111111111111111111111111".to_string(),)) .max_gas() .transact() .await?; From ca30dec29eaee9f981befbd52aaa07df888ec868 Mon Sep 17 00:00:00 2001 From: garikbesson Date: Tue, 2 Sep 2025 13:22:26 +0100 Subject: [PATCH 10/13] fix using global hash to deploy contract --- Cargo.toml | 1 + src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index bedfa00..7df05bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] borsh = "1.5.7" +bs58 = "0.5.1" near-sdk = { version = "5.17.1", features = ["global-contracts", "unstable"] } [dev-dependencies] diff --git a/src/lib.rs b/src/lib.rs index 39bb810..bb089d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -76,7 +76,7 @@ impl GlobalFactoryContract { "Using global contract with code hash: {:?}", code_hash )); - promise.use_global_contract(code_hash.as_bytes().to_vec()) + promise.use_global_contract(bs58::decode(code_hash).into_vec().unwrap()) } } } From c1fda02aa9547456764d72f8761e2598ba33ca0a Mon Sep 17 00:00:00 2001 From: garikbesson Date: Fri, 5 Sep 2025 14:26:50 +0100 Subject: [PATCH 11/13] add checking min deposit requirement to deploy method --- src/lib.rs | 13 ++++++++++++- src/manager.rs | 11 ++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index bb089d4..b4df86f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,9 @@ -use near_sdk::{env, near, AccountId, Promise}; +use near_sdk::{env, near, AccountId, NearToken, Promise}; mod manager; const DEFAULT_GLOBAL_CONTRACT_ID: &str = "ft.globals.primitives.testnet"; +const DEFAULT_DEPOSIT_AMOUNT: u128 = 200; // 0.2 NEAR #[derive(Clone, Debug, PartialEq, Eq)] #[near(serializers = [borsh, json])] @@ -33,6 +34,7 @@ impl From for GlobalContractId { #[near(contract_state)] pub struct GlobalFactoryContract { pub global_contract_id: GlobalContractId, + pub min_deposit_amount: NearToken, } impl Default for GlobalFactoryContract { @@ -41,6 +43,7 @@ impl Default for GlobalFactoryContract { global_contract_id: GlobalContractId::AccountId( DEFAULT_GLOBAL_CONTRACT_ID.parse().unwrap(), ), + min_deposit_amount: NearToken::from_millinear(DEFAULT_DEPOSIT_AMOUNT), // 0.2 NEAR } } } @@ -50,6 +53,14 @@ impl GlobalFactoryContract { /// Deploy a global contract with the given bytecode, identifiable by its code hash #[payable] pub fn deploy(&mut self, name: String) -> Promise { + // Assert enough tokens are attached to cover minimal initial deposit on created account + let attached = env::attached_deposit(); + let minimum_needed = self.min_deposit_amount.exact_amount_display(); + assert!( + attached.ge(&self.min_deposit_amount), + "Attach at least {minimum_needed}" + ); + // Assert the sub-account is valid let current_account = env::current_account_id().to_string(); let subaccount: AccountId = format!("{name}.{current_account}").parse().unwrap(); diff --git a/src/manager.rs b/src/manager.rs index 9c867bb..10c8c8d 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -1,4 +1,4 @@ -use near_sdk::near; +use near_sdk::{near, NearToken}; use crate::{GlobalContractId, GlobalFactoryContract, GlobalFactoryContractExt}; @@ -12,4 +12,13 @@ impl GlobalFactoryContract { pub fn get_global_contract_id(&self) -> String { self.global_contract_id.to_string() } + + #[private] + pub fn update_min_deposit(&mut self, amount: NearToken) { + self.min_deposit_amount = amount; + } + + pub fn get_min_deposit(&self) -> NearToken { + self.min_deposit_amount + } } From 69494e257301e5c0b7356267dfeb1d9679482577 Mon Sep 17 00:00:00 2001 From: garikbesson Date: Fri, 5 Sep 2025 14:42:00 +0100 Subject: [PATCH 12/13] add tests --- tests/workspaces.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/workspaces.rs b/tests/workspaces.rs index ed8843e..8211f82 100644 --- a/tests/workspaces.rs +++ b/tests/workspaces.rs @@ -1,5 +1,8 @@ +use near_sdk::NearToken; + const DEFAULT_GLOBAL_CONTRACT_ACCOUNT_ID: &str = "ft.globals.primitives.testnet"; const DEFAULT_GLOBAL_CONTRACT_HASH: &str = "3vaopJ7aRoivvzZLngPQRBEd8VJr2zPLTxQfnRCoFgNX"; +const DEFAULT_DEPOSIT_AMOUNT: u128 = 100; // 0.1 NEAR /// TODO: add tests for deploy method as soon as near-workspaces-rs supports deploying global contracts. /// Currently it does not, therefore it's impossible to deploy global contract to use it in tests. @@ -17,7 +20,6 @@ async fn test_manager() -> anyhow::Result<()> { .max_gas() .transact() .await?; - println!("change_contract_id_res: {change_contract_id_res_1:?}"); assert!(change_contract_id_res_1.is_success()); let global_contract_id = factory_contract @@ -35,7 +37,6 @@ async fn test_manager() -> anyhow::Result<()> { .max_gas() .transact() .await?; - println!("change_contract_id_res: {change_contract_id_res_2:?}"); assert!(change_contract_id_res_2.is_success()); let global_contract_id = factory_contract @@ -46,6 +47,23 @@ async fn test_manager() -> anyhow::Result<()> { .json::>()? .expect("Should have stored global contract ID"); assert_eq!(global_contract_id, DEFAULT_GLOBAL_CONTRACT_ACCOUNT_ID); + + let change_min_deposit_res = factory_contract + .call("update_min_deposit") + .args_json((NearToken::from_millinear(100),)) + .max_gas() + .transact() + .await?; + assert!(change_min_deposit_res.is_success()); + + let min_deposit = factory_contract + .call("get_min_deposit") + .args_json(()) + .view() + .await? + .json::>()? + .expect("Should have stored global contract ID"); + assert!(min_deposit.eq(&NearToken::from_millinear(DEFAULT_DEPOSIT_AMOUNT))); Ok(()) } From 4b0bf376869fbcde84b32be8f36bac8f962c6155 Mon Sep 17 00:00:00 2001 From: Guillermo Alejandro Gallardo Diez Date: Tue, 9 Sep 2025 16:42:46 +0200 Subject: [PATCH 13/13] chore: simplify code --- src/lib.rs | 20 +-------------- src/manager.rs | 14 ++++------ tests/workspaces.rs | 62 ++++++++++++++++++++++++++++++--------------- 3 files changed, 47 insertions(+), 49 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b4df86f..1ac6706 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,25 +12,6 @@ pub enum GlobalContractId { CodeHash(String), } -impl ToString for GlobalContractId { - fn to_string(&self) -> String { - match self { - GlobalContractId::AccountId(account_id) => account_id.to_string(), - GlobalContractId::CodeHash(code_hash) => code_hash.clone(), - } - } -} - -impl From for GlobalContractId { - fn from(s: String) -> Self { - if s.parse::().is_ok() { - GlobalContractId::AccountId(s.parse().unwrap()) - } else { - GlobalContractId::CodeHash(s) - } - } -} - #[near(contract_state)] pub struct GlobalFactoryContract { pub global_contract_id: GlobalContractId, @@ -73,6 +54,7 @@ impl GlobalFactoryContract { .create_account() .transfer(env::attached_deposit()) .add_full_access_key(env::signer_account_pk()); + match self.global_contract_id { GlobalContractId::AccountId(ref account_id) => { env::log_str(&format!( diff --git a/src/manager.rs b/src/manager.rs index 10c8c8d..8f3f57c 100644 --- a/src/manager.rs +++ b/src/manager.rs @@ -5,17 +5,13 @@ use crate::{GlobalContractId, GlobalFactoryContract, GlobalFactoryContractExt}; #[near] impl GlobalFactoryContract { #[private] - pub fn update_global_contract_id(&mut self, contract_id: String) { - self.global_contract_id = GlobalContractId::from(contract_id); + pub fn update_global_contract_id(&mut self, contract_id: GlobalContractId, min_deposit: NearToken) { + self.global_contract_id = contract_id; + self.min_deposit_amount = min_deposit; } - pub fn get_global_contract_id(&self) -> String { - self.global_contract_id.to_string() - } - - #[private] - pub fn update_min_deposit(&mut self, amount: NearToken) { - self.min_deposit_amount = amount; + pub fn get_global_contract_id(&self) -> GlobalContractId { + self.global_contract_id.clone() } pub fn get_min_deposit(&self) -> NearToken { diff --git a/tests/workspaces.rs b/tests/workspaces.rs index 8211f82..2d7a32e 100644 --- a/tests/workspaces.rs +++ b/tests/workspaces.rs @@ -1,8 +1,11 @@ -use near_sdk::NearToken; +use factory_contract_global::GlobalContractId; +use near_sdk::{serde_json::json, NearToken}; const DEFAULT_GLOBAL_CONTRACT_ACCOUNT_ID: &str = "ft.globals.primitives.testnet"; -const DEFAULT_GLOBAL_CONTRACT_HASH: &str = "3vaopJ7aRoivvzZLngPQRBEd8VJr2zPLTxQfnRCoFgNX"; -const DEFAULT_DEPOSIT_AMOUNT: u128 = 100; // 0.1 NEAR + +const TEST_GLOBAL_CONTRACT_ACCOUNT_ID: &str = "ft.globals.primitives.testnet"; +const TEST_GLOBAL_CONTRACT_HASH: &str = "3vaopJ7aRoivvzZLngPQRBEd8VJr2zPLTxQfnRCoFgNX"; +const TEST_DEPOSIT_AMOUNT: u128 = 100; // 0.1 NEAR /// TODO: add tests for deploy method as soon as near-workspaces-rs supports deploying global contracts. /// Currently it does not, therefore it's impossible to deploy global contract to use it in tests. @@ -14,9 +17,23 @@ async fn test_manager() -> anyhow::Result<()> { let factory_wasm = near_workspaces::compile_project(".").await?; let factory_contract = worker.dev_deploy(&factory_wasm).await?; + let default_contract_id = factory_contract + .call("get_global_contract_id") + .view() + .await? + .json::>()? + .expect("Should have stored global contract ID"); + assert_eq!( + default_contract_id, + GlobalContractId::AccountId(DEFAULT_GLOBAL_CONTRACT_ACCOUNT_ID.parse().unwrap()) + ); + let change_contract_id_res_1 = factory_contract .call("update_global_contract_id") - .args_json((DEFAULT_GLOBAL_CONTRACT_HASH.to_string(),)) + .args_json(json!({ + "contract_id": GlobalContractId::CodeHash(TEST_GLOBAL_CONTRACT_HASH.to_string()), + "min_deposit": NearToken::from_millinear(TEST_DEPOSIT_AMOUNT) + })) .max_gas() .transact() .await?; @@ -24,16 +41,21 @@ async fn test_manager() -> anyhow::Result<()> { let global_contract_id = factory_contract .call("get_global_contract_id") - .args_json(()) .view() .await? - .json::>()? + .json::>()? .expect("Should have stored global contract ID"); - assert_eq!(global_contract_id, DEFAULT_GLOBAL_CONTRACT_HASH); + assert_eq!( + global_contract_id, + GlobalContractId::CodeHash(TEST_GLOBAL_CONTRACT_HASH.to_string()) + ); let change_contract_id_res_2 = factory_contract .call("update_global_contract_id") - .args_json((DEFAULT_GLOBAL_CONTRACT_ACCOUNT_ID.to_string(),)) + .args_json(json!({ + "contract_id": GlobalContractId::AccountId(TEST_GLOBAL_CONTRACT_ACCOUNT_ID.parse().unwrap()), + "min_deposit": NearToken::from_millinear(TEST_DEPOSIT_AMOUNT) + })) .max_gas() .transact() .await?; @@ -41,20 +63,14 @@ async fn test_manager() -> anyhow::Result<()> { let global_contract_id = factory_contract .call("get_global_contract_id") - .args_json(()) .view() .await? - .json::>()? + .json::>()? .expect("Should have stored global contract ID"); - assert_eq!(global_contract_id, DEFAULT_GLOBAL_CONTRACT_ACCOUNT_ID); - - let change_min_deposit_res = factory_contract - .call("update_min_deposit") - .args_json((NearToken::from_millinear(100),)) - .max_gas() - .transact() - .await?; - assert!(change_min_deposit_res.is_success()); + assert_eq!( + global_contract_id, + GlobalContractId::AccountId(TEST_GLOBAL_CONTRACT_ACCOUNT_ID.parse().unwrap()) + ); let min_deposit = factory_contract .call("get_min_deposit") @@ -63,7 +79,7 @@ async fn test_manager() -> anyhow::Result<()> { .await? .json::>()? .expect("Should have stored global contract ID"); - assert!(min_deposit.eq(&NearToken::from_millinear(DEFAULT_DEPOSIT_AMOUNT))); + assert!(min_deposit.eq(&NearToken::from_millinear(TEST_DEPOSIT_AMOUNT))); Ok(()) } @@ -76,7 +92,11 @@ async fn test_global_contract_edge_cases() -> anyhow::Result<()> { let change_contract_id_res = factory_contract .call("update_global_contract_id") - .args_json(("11111111111111111111111111111111".to_string(),)) + .args_json( + json!({ + "contract_id": GlobalContractId::CodeHash("11111111111111111111111111111111".to_string()), + "min_deposit": NearToken::from_millinear(TEST_DEPOSIT_AMOUNT) } + )) .max_gas() .transact() .await?;