From a1be45691dc6ea8127e7e472d3c572dfc80c6e31 Mon Sep 17 00:00:00 2001 From: damienen Date: Mon, 25 Jul 2022 11:25:58 +0300 Subject: [PATCH 01/25] Solved issue #10 --- src/lib.rs | 10 +++++++++- tests/empty_rust_test.rs | 2 +- wasm/src/lib.rs | 1 + 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ac14a87..39a3251 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,7 +31,15 @@ pub trait ClaimsContract: #[only_owner] #[endpoint(pause)] fn pause(&self) { - self.is_paused().set(!self.is_paused().get()); + require!(!self.is_paused().get(), "Contract is already paused"); + self.is_paused().set(true); + } + + #[only_owner] + #[endpoint(unpause)] + fn unpause(&self) { + require!(self.is_paused().get(), "Contract is already unpaused"); + self.is_paused().set(false); } #[only_owner] diff --git a/tests/empty_rust_test.rs b/tests/empty_rust_test.rs index 5aced4b..a798413 100644 --- a/tests/empty_rust_test.rs +++ b/tests/empty_rust_test.rs @@ -66,7 +66,7 @@ where blockchain_wrapper .execute_tx(&owner_address, &cf_wrapper, &rust_zero, |sc| { - sc.pause(); + sc.unpause(); }) .assert_ok(); diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 3029c7d..26065fa 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -15,6 +15,7 @@ elrond_wasm_node::wasm_endpoints! { removeClaim removeClaims setRewardToken + unpause viewClaim viewClaimAddDate viewClaimWithDate From 96c4876a88f17d4ee3ab87a9a7577d8e7b5d05b5 Mon Sep 17 00:00:00 2001 From: damienen Date: Mon, 25 Jul 2022 11:37:48 +0300 Subject: [PATCH 02/25] Solved issue #9 --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 39a3251..765c94a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -151,10 +151,10 @@ pub trait ClaimsContract: #[endpoint(claim)] fn harvest_claim(&self, claim_type: OptionalValue) { + require!(!self.is_paused().get(), "Contract is paused"); require!(!self.reward_token().is_empty(), "Reward token is not set"); let reward_token = self.reward_token().get(); let caller = self.blockchain().get_caller(); - require!(!self.is_paused().get(), "Contract is paused"); if let OptionalValue::Some(what_type_to_claim) = claim_type { let claim = self.claim(&caller, &what_type_to_claim).get(); require!(claim > BigUint::zero(), "Cannot claim 0 tokens"); From 2f526f10f0bc6b5d24de50284057583f2353d69b Mon Sep 17 00:00:00 2001 From: damienen Date: Mon, 25 Jul 2022 12:50:14 +0300 Subject: [PATCH 03/25] Solved issue #8 --- src/lib.rs | 57 ++++++++++++++-------------------------- src/requirements.rs | 30 +++++++++++++++++++++ tests/empty_rust_test.rs | 2 +- 3 files changed, 50 insertions(+), 39 deletions(-) create mode 100644 src/requirements.rs diff --git a/src/lib.rs b/src/lib.rs index 765c94a..fe25564 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,12 +6,16 @@ elrond_wasm::imports!(); use crate::storage::ClaimType; pub mod events; +pub mod requirements; pub mod storage; pub mod views; #[elrond_wasm::contract] pub trait ClaimsContract: - storage::StorageModule + events::EventsModule + views::ViewsModule + storage::StorageModule + + events::EventsModule + + views::ViewsModule + + requirements::RequirementsModule { #[init] fn init(&self) { @@ -46,19 +50,12 @@ pub trait ClaimsContract: #[payable("*")] #[endpoint(addClaim)] fn add_claim(&self, address: &ManagedAddress, claim_type: ClaimType) { - require!(!self.reward_token().is_empty(), "Reward token is not set"); + self.require_reward_token_is_set(); let (payment_amount, payment_token) = self.call_value().payment_token_pair(); let current_claim = self.claim(address, &claim_type).get(); - let reward_token = self.reward_token().get(); let timestamp = self.blockchain().get_block_timestamp(); - require!( - payment_token == reward_token, - "Can only add designated token" - ); - require!( - payment_amount > BigUint::zero(), - "Must add more than 0 tokens" - ); + self.require_token_is_reward(payment_token); + self.require_value_not_zero(&payment_amount); self.claim(address, &claim_type) .set(current_claim + &payment_amount); self.claim_add_date(address, &claim_type).set(timestamp); @@ -72,18 +69,11 @@ pub trait ClaimsContract: &self, claims: MultiValueEncoded>, ) { - require!(!self.reward_token().is_empty(), "Reward token is not set"); + self.require_reward_token_is_set(); let (payment_amount, payment_token) = self.call_value().payment_token_pair(); - let reward_token = self.reward_token().get(); let timestamp = self.blockchain().get_block_timestamp(); - require!( - payment_token == reward_token, - "Can only add designated token" - ); - require!( - payment_amount > BigUint::zero(), - "Must add more than 0 tokens" - ); + self.require_token_is_reward(payment_token); + self.require_value_not_zero(&payment_amount); let mut sum_of_claims = BigUint::zero(); for item in claims.into_iter() { let tuple = item.into_tuple(); @@ -102,15 +92,12 @@ pub trait ClaimsContract: #[only_owner] #[endpoint(removeClaim)] fn remove_claim(&self, address: &ManagedAddress, claim_type: ClaimType, amount: BigUint) { - require!(!self.reward_token().is_empty(), "Reward token is not set"); + self.require_reward_token_is_set(); let current_claim = self.claim(address, &claim_type).get(); let owner = self.blockchain().get_owner_address(); let reward_token = self.reward_token().get(); let timestamp = self.blockchain().get_block_timestamp(); - require!( - current_claim >= amount, - "Cannot remove more than current claim" - ); + self.require_remove_claim_is_valid(¤t_claim, &amount); self.claim(address, &claim_type) .set(current_claim - &amount); self.claim_add_date(address, &claim_type).set(timestamp); @@ -124,25 +111,19 @@ pub trait ClaimsContract: &self, claims: MultiValueEncoded>, ) { - require!(!self.reward_token().is_empty(), "Reward token is not set"); + self.require_reward_token_is_set(); let mut sum_of_claims = BigUint::zero(); let timestamp = self.blockchain().get_block_timestamp(); for item in claims.into_iter() { let tuple = item.into_tuple(); let current_claim = self.claim(&tuple.0, &tuple.1).get(); self.claim_add_date(&tuple.0, &tuple.1).set(timestamp); - require!( - current_claim >= tuple.2, - "Cannot remove more than current claim" - ); + self.require_remove_claim_is_valid(¤t_claim, &tuple.2); sum_of_claims += &tuple.2; self.claim(&tuple.0, &tuple.1).set(current_claim - &tuple.2); self.claim_removed_event(&tuple.0, &tuple.1, tuple.2); } - require!( - sum_of_claims > BigUint::zero(), - "Claims removed must be greater than 0" - ); + self.require_value_not_zero(&sum_of_claims); let owner = self.blockchain().get_owner_address(); let reward_token = self.reward_token().get(); self.send() @@ -152,19 +133,19 @@ pub trait ClaimsContract: #[endpoint(claim)] fn harvest_claim(&self, claim_type: OptionalValue) { require!(!self.is_paused().get(), "Contract is paused"); - require!(!self.reward_token().is_empty(), "Reward token is not set"); + self.require_reward_token_is_set(); let reward_token = self.reward_token().get(); let caller = self.blockchain().get_caller(); if let OptionalValue::Some(what_type_to_claim) = claim_type { let claim = self.claim(&caller, &what_type_to_claim).get(); - require!(claim > BigUint::zero(), "Cannot claim 0 tokens"); + self.require_value_not_zero(&claim); self.send().direct(&caller, &reward_token, 0, &claim, &[]); self.claim(&caller, &what_type_to_claim) .set(BigUint::zero()); self.claim_collected_event(&caller, &what_type_to_claim, claim); } else { let claim = self.view_claims(&caller); - require!(claim > BigUint::zero(), "Cannot claim 0 tokens"); + self.require_value_not_zero(&claim); self.send().direct(&caller, &reward_token, 0, &claim, &[]); self.claim(&caller, &ClaimType::Reward).set(BigUint::zero()); self.claim(&caller, &ClaimType::Airdrop) diff --git a/src/requirements.rs b/src/requirements.rs new file mode 100644 index 0000000..c8a8985 --- /dev/null +++ b/src/requirements.rs @@ -0,0 +1,30 @@ +elrond_wasm::imports!(); +elrond_wasm::derive_imports!(); + +#[elrond_wasm::module] +pub trait RequirementsModule: crate::storage::StorageModule { + fn require_reward_token_is_set(&self) { + require!(!self.reward_token().is_empty(), "Reward token is not set"); + } + + fn require_token_is_reward(&self, token: TokenIdentifier) { + require!( + token == self.reward_token().get(), + "Can only add designated token" + ); + } + + fn require_value_not_zero(&self, value: &BigUint) { + require!( + value > &BigUint::zero(), + "Operations must have non-zero value" + ); + } + + fn require_remove_claim_is_valid(&self, current_claim: &BigUint, amount: &BigUint) { + require!( + current_claim >= amount, + "Cannot remove more than current claim" + ); + } +} diff --git a/tests/empty_rust_test.rs b/tests/empty_rust_test.rs index a798413..7463f5b 100644 --- a/tests/empty_rust_test.rs +++ b/tests/empty_rust_test.rs @@ -412,7 +412,7 @@ fn harvest_wrong_claim_type_test() { sc.harvest_claim(OptionalValue::Some(storage::ClaimType::Reward)); }, ) - .assert_user_error("Cannot claim 0 tokens"); + .assert_user_error("Operations must have non-zero value"); } #[test] //Tests whether claiming all claim types at once works From 61f6d6e6c81a971a99a949a7201aaa7ce3c72f17 Mon Sep 17 00:00:00 2001 From: damienen Date: Mon, 25 Jul 2022 13:01:33 +0300 Subject: [PATCH 04/25] Solved issue #13 --- src/events.rs | 8 ++++---- src/lib.rs | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/events.rs b/src/events.rs index b074aa5..ec17051 100644 --- a/src/events.rs +++ b/src/events.rs @@ -10,7 +10,7 @@ pub trait EventsModule { &self, #[indexed] address: &ManagedAddress, #[indexed] claim_type: &ClaimType, - amount: BigUint, + amount: &BigUint, ); #[event("claimRemoved")] @@ -18,7 +18,7 @@ pub trait EventsModule { &self, #[indexed] address: &ManagedAddress, #[indexed] claim_type: &ClaimType, - amount: BigUint, + amount: &BigUint, ); #[event("claimCollected")] @@ -26,9 +26,9 @@ pub trait EventsModule { &self, #[indexed] address: &ManagedAddress, #[indexed] claim_type: &ClaimType, - amount: BigUint, + amount: &BigUint, ); #[event("allClaimsCollected")] - fn all_claims_collected_event(&self, #[indexed] address: &ManagedAddress, amount: BigUint); + fn all_claims_collected_event(&self, #[indexed] address: &ManagedAddress, amount: &BigUint); } diff --git a/src/lib.rs b/src/lib.rs index fe25564..b51474c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,7 +59,7 @@ pub trait ClaimsContract: self.claim(address, &claim_type) .set(current_claim + &payment_amount); self.claim_add_date(address, &claim_type).set(timestamp); - self.claim_added_event(address, &claim_type, payment_amount); + self.claim_added_event(address, &claim_type, &payment_amount); } #[only_owner] @@ -81,7 +81,7 @@ pub trait ClaimsContract: self.claim(&tuple.0, &tuple.1).set(current_claim + &tuple.2); self.claim_add_date(&tuple.0, &tuple.1).set(timestamp); sum_of_claims += &tuple.2; - self.claim_added_event(&tuple.0, &tuple.1, tuple.2); + self.claim_added_event(&tuple.0, &tuple.1, &tuple.2); } require!( sum_of_claims == payment_amount, @@ -101,8 +101,8 @@ pub trait ClaimsContract: self.claim(address, &claim_type) .set(current_claim - &amount); self.claim_add_date(address, &claim_type).set(timestamp); + self.claim_removed_event(address, &claim_type, &amount); self.send().direct(&owner, &reward_token, 0, &amount, &[]); - self.claim_removed_event(address, &claim_type, amount); } #[only_owner] @@ -121,7 +121,7 @@ pub trait ClaimsContract: self.require_remove_claim_is_valid(¤t_claim, &tuple.2); sum_of_claims += &tuple.2; self.claim(&tuple.0, &tuple.1).set(current_claim - &tuple.2); - self.claim_removed_event(&tuple.0, &tuple.1, tuple.2); + self.claim_removed_event(&tuple.0, &tuple.1, &tuple.2); } self.require_value_not_zero(&sum_of_claims); let owner = self.blockchain().get_owner_address(); @@ -136,23 +136,23 @@ pub trait ClaimsContract: self.require_reward_token_is_set(); let reward_token = self.reward_token().get(); let caller = self.blockchain().get_caller(); + let claim; if let OptionalValue::Some(what_type_to_claim) = claim_type { - let claim = self.claim(&caller, &what_type_to_claim).get(); + claim = self.claim(&caller, &what_type_to_claim).get(); self.require_value_not_zero(&claim); - self.send().direct(&caller, &reward_token, 0, &claim, &[]); self.claim(&caller, &what_type_to_claim) .set(BigUint::zero()); - self.claim_collected_event(&caller, &what_type_to_claim, claim); + self.claim_collected_event(&caller, &what_type_to_claim, &claim); } else { - let claim = self.view_claims(&caller); + claim = self.view_claims(&caller); self.require_value_not_zero(&claim); - self.send().direct(&caller, &reward_token, 0, &claim, &[]); self.claim(&caller, &ClaimType::Reward).set(BigUint::zero()); self.claim(&caller, &ClaimType::Airdrop) .set(BigUint::zero()); self.claim(&caller, &ClaimType::Allocation) .set(BigUint::zero()); - self.all_claims_collected_event(&caller, claim); + self.all_claims_collected_event(&caller, &claim); } + self.send().direct(&caller, &reward_token, 0, &claim, &[]); } } From 115f078f9ca74813f22f11c845c95d9307990f74 Mon Sep 17 00:00:00 2001 From: damienen Date: Mon, 25 Jul 2022 13:13:28 +0300 Subject: [PATCH 05/25] Solved issue #11 --- src/events.rs | 3 --- src/lib.rs | 32 ++++++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/events.rs b/src/events.rs index ec17051..4ec85b7 100644 --- a/src/events.rs +++ b/src/events.rs @@ -28,7 +28,4 @@ pub trait EventsModule { #[indexed] claim_type: &ClaimType, amount: &BigUint, ); - - #[event("allClaimsCollected")] - fn all_claims_collected_event(&self, #[indexed] address: &ManagedAddress, amount: &BigUint); } diff --git a/src/lib.rs b/src/lib.rs index b51474c..d3d1ab2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -136,7 +136,7 @@ pub trait ClaimsContract: self.require_reward_token_is_set(); let reward_token = self.reward_token().get(); let caller = self.blockchain().get_caller(); - let claim; + let mut claim = BigUint::zero(); if let OptionalValue::Some(what_type_to_claim) = claim_type { claim = self.claim(&caller, &what_type_to_claim).get(); self.require_value_not_zero(&claim); @@ -144,14 +144,30 @@ pub trait ClaimsContract: .set(BigUint::zero()); self.claim_collected_event(&caller, &what_type_to_claim, &claim); } else { - claim = self.view_claims(&caller); + let reward_claim = self.claim(&caller, &ClaimType::Reward).get(); + if reward_claim > BigUint::zero() { + claim += &reward_claim; + self.claim_collected_event(&caller, &ClaimType::Reward, &reward_claim); + self.claim(&caller, &ClaimType::Reward).set(BigUint::zero()); + } + + let airdrop_claim = self.claim(&caller, &ClaimType::Airdrop).get(); + if airdrop_claim > BigUint::zero() { + claim += &airdrop_claim; + self.claim_collected_event(&caller, &ClaimType::Airdrop, &airdrop_claim); + self.claim(&caller, &ClaimType::Airdrop) + .set(BigUint::zero()); + } + + let allocation_claim = self.claim(&caller, &ClaimType::Allocation).get(); + if allocation_claim > BigUint::zero() { + claim += &allocation_claim; + self.claim_collected_event(&caller, &ClaimType::Allocation, &allocation_claim); + self.claim(&caller, &ClaimType::Allocation) + .set(BigUint::zero()); + } + self.require_value_not_zero(&claim); - self.claim(&caller, &ClaimType::Reward).set(BigUint::zero()); - self.claim(&caller, &ClaimType::Airdrop) - .set(BigUint::zero()); - self.claim(&caller, &ClaimType::Allocation) - .set(BigUint::zero()); - self.all_claims_collected_event(&caller, &claim); } self.send().direct(&caller, &reward_token, 0, &claim, &[]); } From cb536abd149ff870679e3129a44225395f160bb9 Mon Sep 17 00:00:00 2001 From: damienen Date: Mon, 25 Jul 2022 13:22:58 +0300 Subject: [PATCH 06/25] Solved issue #6 --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index d3d1ab2..8b4ab15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -97,6 +97,7 @@ pub trait ClaimsContract: let owner = self.blockchain().get_owner_address(); let reward_token = self.reward_token().get(); let timestamp = self.blockchain().get_block_timestamp(); + self.require_value_not_zero(&amount); self.require_remove_claim_is_valid(¤t_claim, &amount); self.claim(address, &claim_type) .set(current_claim - &amount); From 5ec7e693b6628c110888b20a5d8f768d9ab9f5e3 Mon Sep 17 00:00:00 2001 From: damienen Date: Mon, 25 Jul 2022 13:28:05 +0300 Subject: [PATCH 07/25] Solved issue #7 --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 8b4ab15..4ea8ac5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,12 +119,12 @@ pub trait ClaimsContract: let tuple = item.into_tuple(); let current_claim = self.claim(&tuple.0, &tuple.1).get(); self.claim_add_date(&tuple.0, &tuple.1).set(timestamp); + self.require_value_not_zero(&tuple.2); self.require_remove_claim_is_valid(¤t_claim, &tuple.2); sum_of_claims += &tuple.2; self.claim(&tuple.0, &tuple.1).set(current_claim - &tuple.2); self.claim_removed_event(&tuple.0, &tuple.1, &tuple.2); } - self.require_value_not_zero(&sum_of_claims); let owner = self.blockchain().get_owner_address(); let reward_token = self.reward_token().get(); self.send() From 1e97fb79093371f119e73d12e26d8e0e19ddd3ce Mon Sep 17 00:00:00 2001 From: damienen Date: Tue, 26 Jul 2022 14:13:36 +0300 Subject: [PATCH 08/25] Solved issue #3 --- deployOutput | 32 +++++++++++++++----------------- src/lib.rs | 8 ++++++++ 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/deployOutput b/deployOutput index c689914..669097f 100644 --- a/deployOutput +++ b/deployOutput @@ -1,19 +1,17 @@ { - "emitted_tx": { - "tx": { - "nonce": 3676, - "value": "0", - "receiver": "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu", - "sender": "erd130qhxzu6lh29g6srnsa65pplxaf9sgsspczvljvxk624upwt7yqstswx9l", - "gasPrice": 1000000000, - "gasLimit": 150000000, - "data": "MDA2MTczNmQwMTAwMDAwMDAxNmUxMzYwMDAwMDYwMDE3ZjAxN2Y2MDAyN2Y3ZjAxN2Y2MDAyN2Y3ZjAwNjAwMDAxN2Y2MDAxN2YwMDYwMDM3ZjdmN2YwMDYwMDQ3ZjdmN2Y3ZjAwNjAwMzdmN2Y3ZjAxN2Y2MDA1N2Y3ZjdlN2Y3ZjAxN2Y2MDAyN2Y3ZTAwNjAwMTdmMDE3ZTYwMDE3ZTAwNjAwNDdmN2Y3ZjdmMDE3ZjYwMDAwMTdlNjAwNTdmN2Y3ZjdmN2YwMDYwMDM3ZjdlN2YwMDYwMDQ3ZjdlN2Y3ZjAwNjAwMjdmN2YwMTdlMDJhODA3MjcwMzY1NmU3NjBiNzM2OTY3NmU2MTZjNDU3MjcyNmY3MjAwMDMwMzY1NmU3NjBhNmQ0Mjc1NjY2NjY1NzI0ZTY1NzcwMDA0MDM2NTZlNzYwZDZkNDI3NTY2NjY2NTcyNDE3MDcwNjU2ZTY0MDAwMjAzNjU2ZTc2MDk2ZDQyNzU2NjY2NjU3MjQ1NzEwMDAyMDM2NTZlNzYxYjZkNjE2ZTYxNjc2NTY0NTQ3MjYxNmU3MzY2NjU3MjU2NjE2Yzc1NjU0NTc4NjU2Mzc1NzQ2NTAwMDkwMzY1NmU3NjA5NjI2OTY3NDk2ZTc0NDE2NDY0MDAwNjAzNjU2ZTc2MGE2MjY5Njc0OTZlNzQ1MzY5Njc2ZTAwMDEwMzY1NmU3NjEyNmQ0Mjc1NjY2NjY1NzI0MTcwNzA2NTZlNjQ0Mjc5NzQ2NTczMDAwODAzNjU2ZTc2MjI2ZDYxNmU2MTY3NjU2NDRkNzU2Yzc0Njk1NDcyNjE2ZTczNjY2NTcyNDU1MzQ0NTQ0ZTQ2NTQ0NTc4NjU2Mzc1NzQ2NTAwMDkwMzY1NmU3NjBkNmQ2MTZlNjE2NzY1NjQ0MzYxNmM2YzY1NzIwMDA1MDM2NTZlNzYxMzZkNjE2ZTYxNjc2NTY0NGY3NzZlNjU3MjQxNjQ2NDcyNjU3MzczMDAwNTAzNjU2ZTc2MTM2NzY1NzQ0ZTc1NmQ0NTUzNDQ1NDU0NzI2MTZlNzM2NjY1NzI3MzAwMDQwMzY1NmU3NjEyNjI2OTY3NDk2ZTc0NDc2NTc0NDM2MTZjNmM1NjYxNmM3NTY1MDAwNTAzNjU2ZTc2MTY2MjY5Njc0OTZlNzQ0NzY1NzQ0NTUzNDQ1NDQzNjE2YzZjNTY2MTZjNzU2NTAwMDUwMzY1NmU3NjEwNjc2NTc0NDU1MzQ0NTQ1NDZmNmI2NTZlNGU2MTZkNjUwMDAxMDM2NTZlNzYwZjZkNDI3NTY2NjY2NTcyNTM2NTc0NDI3OTc0NjU3MzAwMDgwMzY1NmU3NjEyNmQ0Mjc1NjY2NjY1NzI0NzY1NzQ0MTcyNjc3NTZkNjU2ZTc0MDAwMjAzNjU2ZTc2MTI2ZDYxNmU2MTY3NjU2NDUzNjk2NzZlNjE2YzQ1NzI3MjZmNzIwMDA1MDM2NTZlNzYxMDZkNDI3NTY2NjY2NTcyNDc2NTc0NGM2NTZlNjc3NDY4MDAwMTAzNjU2ZTc2MTk2MjY5Njc0OTZlNzQ0NzY1NzQ1NTZlNzM2OTY3NmU2NTY0NDE3MjY3NzU2ZDY1NmU3NDAwMDMwMzY1NmU3NjBmNjc2NTc0NGU3NTZkNDE3MjY3NzU2ZDY1NmU3NDczMDAwNDAzNjU2ZTc2MDk2MjY5Njc0OTZlNzQ1Mzc1NjIwMDA2MDM2NTZlNzYxOTZkNDI3NTY2NjY2NTcyNDY3MjZmNmQ0MjY5Njc0OTZlNzQ1NTZlNzM2OTY3NmU2NTY0MDAwMjAzNjU2ZTc2MTc2ZDQyNzU2NjY2NjU3MjU0NmY0MjY5Njc0OTZlNzQ1NTZlNzM2OTY3NmU2NTY0MDAwMjAzNjU2ZTc2MTI2ZDQyNzU2NjY2NjU3MjUzNzQ2ZjcyNjE2NzY1NGM2ZjYxNjQwMDAyMDM2NTZlNzYxMzZkNDI3NTY2NjY2NTcyNTM3NDZmNzI2MTY3NjU1Mzc0NmY3MjY1MDAwMjAzNjU2ZTc2MGU2MjY5Njc0OTZlNzQ1MzY1NzQ0OTZlNzQzNjM0MDAwYTAzNjU2ZTc2MDk2MjY5Njc0OTZlNzQ0MzZkNzAwMDAyMDM2NTZlNzYwZjZkNjE2ZTYxNjc2NTY0NTc3MjY5NzQ2NTRjNmY2NzAwMDMwMzY1NmU3NjExNjc2NTc0NDE3MjY3NzU2ZDY1NmU3NDRjNjU2ZTY3NzQ2ODAwMDEwMzY1NmU3NjFiNzM2ZDYxNmM2YzQ5NmU3NDQ3NjU3NDU1NmU3MzY5Njc2ZTY1NjQ0MTcyNjc3NTZkNjU2ZTc0MDAwYjAzNjU2ZTc2MGU2MzY4NjU2MzZiNGU2ZjUwNjE3OTZkNjU2ZTc0MDAwMDAzNjU2ZTc2MTE2NzY1NzQ0MjZjNmY2MzZiNTQ2OTZkNjU3Mzc0NjE2ZDcwMDAwZTAzNjU2ZTc2MTQ3MzZkNjE2YzZjNDk2ZTc0NDY2OTZlNjk3MzY4NTM2OTY3NmU2NTY0MDAwYzAzNjU2ZTc2MTQ2MjY5Njc0OTZlNzQ0NjY5NmU2OTczNjg1NTZlNzM2OTY3NmU2NTY0MDAwNTAzNjU2ZTc2MTY3MzZkNjE2YzZjNDk2ZTc0NDY2OTZlNjk3MzY4NTU2ZTczNjk2NzZlNjU2NDAwMGMwMzY1NmU3NjBkNmQ0Mjc1NjY2NjY1NzI0NjY5NmU2OTczNjgwMDAxMDM2NTZlNzYwNjY2Njk2ZTY5NzM2ODAwMDMwMzY1NmU3NjEzNmQ0Mjc1NjY2NjY1NzI0NzY1NzQ0Mjc5NzQ2NTUzNmM2OTYzNjUwMDBkMDM1YjVhMGYwMDAxMDIwMzA2MDIwNDAxMDQwNTA0MDQwMDA1MDEwNzA4MDEwMTAzMDQwMTA1MDUwMDAwMDMwMjAyMGQwMzAzMDIwMjAxMDEwNDEwMDcwNzA1MDEwMzAxMDEwMTBiMDMwYTExMDYwMzAzMDEwMTAzMDMwMjA2MTIwMTAzMDYwNjAyMDIwNzA2MDYwNDAyMDIwNDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDUwMzAxMDAxMTA2MTkwMzdmMDE0MTgwODBjMDAwMGI3ZjAwNDFiY2QzYzAwMDBiN2YwMDQxYzBkM2MwMDAwYjA3ZWQwMTEyMDY2ZDY1NmQ2ZjcyNzkwMjAwMDQ2OTZlNjk3NDAwNzEwODYxNjQ2NDQzNmM2MTY5NmQwMDcyMDk2MTY0NjQ0MzZjNjE2OTZkNzMwMDczMDU2MzZjNjE2OTZkMDA3NDA4Njk3MzUwNjE3NTczNjU2NDAwNzUwNTcwNjE3NTczNjUwMDc2MGI3MjY1NmQ2Zjc2NjU0MzZjNjE2OTZkMDA3NzBjNzI2NTZkNmY3NjY1NDM2YzYxNjk2ZDczMDA3ODBlNzM2NTc0NTI2NTc3NjE3MjY0NTQ2ZjZiNjU2ZTAwNzkwOTc2Njk2NTc3NDM2YzYxNjk2ZDAwN2ExMDc2Njk2NTc3NDM2YzYxNjk2ZDQxNjQ2NDQ0NjE3NDY1MDA3YjExNzY2OTY1Nzc0MzZjNjE2OTZkNTc2OTc0Njg0NDYxNzQ2NTAwN2MwYTc2Njk2NTc3NDM2YzYxNjk2ZDczMDA3ZDEzNzY2OTY1Nzc1NDZmNmI2NTZlNDk2NDY1NmU3NDY5NjY2OTY1NzIwMDdlMDg2MzYxNmM2YzQyNjE2MzZiMDA3ZjBhNWY1ZjY0NjE3NDYxNWY2NTZlNjQwMzAxMGI1ZjVmNjg2NTYxNzA1ZjYyNjE3MzY1MDMwMjBhOWQzMDVhMmUwMDAyNDAyMDAxMjAwMjRkMDQ0MDIwMDIyMDA0NGQwZDAxMTAyODAwMGIxMDI4MDAwYjIwMDAyMDAyMjAwMTZiMzYwMjA0MjAwMDIwMDEyMDAzNmEzNjAyMDAwYjA2MDAxMDgwMDEwMDBiMGYwMTAxN2YxMDAxMjIwMTIwMDAxMDAyMWEyMDAxMGIwYjAwMjAwMDIwMDExMDAzNDEwMDRhMGIwOTAwMjAwMDIwMDExMDAwMDAwYmNiMDEwMTA1N2YyMzAwNDExMDZiMjIwMzI0MDA0MWRjODRjMDAwNDEwMDEwMmQyMTA1MTAyZTIxMDYwMjQwMjAwMTEwMmYwNDQwMjAwMDIwMDI0MjAwMjAwNTIwMDYxMDA0MWEwYzAxMGIxMDJlMjEwNzIwMDExMDI5MjEwNDEwMzAyMjAxMTAzMTIwMDEyMDAxMjAwMjEwMDUyMDAxMTAwNjA0NDAyMDA0MTAyZjFhMGIyMDAzNDIwMDM3MDIwNDIwMDMyMDAxNDEwODc0NDE4MDgwZmMwNzcxMjAwMTQxMTg3NDcyMjAwMTQxMDg3NjQxODBmZTAzNzEyMDAxNDExODc2NzI3MjM2MDIwYzIwMDMyMDA0NDEwODc0NDE4MDgwZmMwNzcxMjAwNDQxMTg3NDcyMjAwNDQxMDg3NjQxODBmZTAzNzEyMDA0NDExODc2NzI3MjM2MDIwMDIwMDcyMDAzNDExMDEwMDcxYTIwMDAyMDA3NDIwMDIwMDUyMDA2MTAwODFhMGIyMDAzNDExMDZhMjQwMDBiMTEwMTAxN2YxMDMwMjIwMjIwMDAyMDAxMTAwZjFhMjAwMjBiMTQwMTAxN2YxMDMwMjIwMDQxZGM4NGMwMDA0MTAwMTAwZjFhMjAwMDBiMDcwMDIwMDAxMDEyNDUwYjFiMDEwMTdmNDE5Yzg1YzAwMDQxOWM4NWMwMDAyODAyMDA0MTAxNmIyMjAwMzYwMjAwMjAwMDBiMDgwMDIwMDA0MjAwMTAxYTBiMGMwMTAxN2YxMDMwMjIwMDEwMDkyMDAwMGIwYzAxMDE3ZjEwMzAyMjAwMTAwYTIwMDAwYjE2MDAxMDMzMTAzMjEwMmEwNDQwMGYwYjQxODA4MGMwMDA0MTI0MTAwMDAwMGJhOTAxMDEwMzdmMjMwMDQxMjA2YjIyMDEyNDAwMDI0MDAyNDAwMjQwMDI0MDEwMGI0NTA0NDA0MTc1MjEwMjQxNzUxMDBjMGMwMTBiNDE3MzIxMDI0MTczMTAwZDEwMGIwZDAxMGIxMDJlMjEwMzBjMDEwYjIwMDE0MTE4NmE0MjAwMzcwMzAwMjAwMTQxMTA2YTQyMDAzNzAzMDAyMDAxNDEwODZhNDIwMDM3MDMwMDIwMDE0MjAwMzcwMzAwMDI0MDIwMDExMDBlMjIwMjQ1MDQ0MDEwMzAyMjAzNDFkYzg0YzAwMDQxMDAxMDBmMWEwYzAxMGIyMDAyNDEyMTRmMGQwMjIwMDEyMDAyMTAyZDIxMDMwYjQxNzMyMTAyMGIyMDAwMjAwMzM2MDIwNDIwMDAyMDAyMzYwMjAwMjAwMTQxMjA2YTI0MDAwZjBiMTAyODAwMGIwZDAwMjAwMDEwMzAyMjAwMTAxMDFhMjAwMDBiMzAwMTAxN2Y0MWE0ODBjMDAwNDExNzEwMmQyMjA0MjAwMDIwMDExMDA3MWEyMDA0NDFiYjgwYzAwMDQxMDMxMDA3MWEyMDA0MjAwMjIwMDMxMDA3MWEyMDA0MTAxMTAwMGIzMTAxMDE3ZjIwMDAyODAyMDAyMjAzNDFiOGQzYzAwMDI4MDIwMDRlMDQ0MDIwMDEyMDAyNDFiZTgwYzAwMDQxMTExMDM3MDAwYjIwMDAyMDAzNDEwMTZhMzYwMjAwMjAwMzBiNTIwMTAxN2UyMDAwMTAxZDQ1MDQ0MDQxMDAwZjBiMDI0MDIwMDAxMDFlMjIwMTQyODAwMjU0MDQ0MDIwMDFhNzIyMDA0MWZmMDE3MTQxMDM0ZjBkMDEyMDAwMGYwYjQxYWI4NGMwMDA0MTBhNDFmYTgwYzAwMDQxMGUxMDM3MDAwYjQxYWI4NGMwMDA0MTBhNDFjMjg0YzAwMDQxMGQxMDM3MDAwYjMyMDEwMTdmMTAyZTIxMDEwMzQwMjAwMDI4MDIwMDQxYjhkM2MwMDAyODAyMDA0ZTQ1MDQ0MDIwMDEyMDAwNDFiYzg0YzAwMDQxMDYxMDM4MTAzNjEwM2IwYzAxMGIwYjIwMDEwYjQ3MDEwMTdmMjMwMDQxMTA2YjIyMDIyNDAwMjAwMjIwMDE0MTA4NzQ0MTgwODBmYzA3NzEyMDAxNDExODc0NzIyMDAxNDEwODc2NDE4MGZlMDM3MTIwMDE0MTE4NzY3MjcyMzYwMjBjMjAwMDIwMDI0MTBjNmE0MTA0MTAwNzFhMjAwMjQxMTA2YTI0MDAwYjI1MDEwMTdmNDEwMDEwMzYyMjAwMTAxMjQxMjA0NzA0NDA0MWI1ODRjMDAwNDEwNzQxYjQ4MWMwMDA0MTEwMTAzNzAwMGIyMDAwMGI1MTAxMDE3ZjIzMDA0MTEwNmIyMjAxMjQwMDAyNDAyMDAwMTAxMjQxMDQ0NzBkMDAyMDAxNDEwMDM2MDIwYzIwMDA0MTAwMjAwMTQxMGM2YTQxMDQxMDQ1MWEyMDAxMjgwMjBjNDFjNThlYjFhMjA0NDcwZDAwMjAwMDQxZGM4NGMwMDA0MTAwMTAwZjFhMGIyMDAxNDExMDZhMjQwMDIwMDAwYjFhMDAyMDAwNDFiOGQzYzAwMDI4MDIwMDQ4MDQ0MDQxY2Y4MGMwMDA0MTEyMTAwMDAwMGIwYjE1MDAxMDE0MjAwMDQ2MDQ0MDBmMGI0MWUxODBjMDAwNDExOTEwMDAwMDBiMWIwMDQxYjhkM2MwMDAyODAyMDA0MTAwNGUwNDQwMGYwYjQxYmU4MGMwMDA0MTExMTAwMDAwMGIwYzAwNDFiOGQzYzAwMDEwMTQzNjAyMDAwYjIyMDEwMTdmMjAwMTEwMTIyMTAyMjAwMDQxMDAzNjAyMDgyMDAwMjAwMTM2MDIwMDIwMDAyMDAyNDEwMjc2MzYwMjA0MGIwODAwMjAwMDIwMDExMDQ0MGIxNTAwNDE3ZjIwMDAyMDAxMTAxYjIyMDA0MTAwNDcyMDAwNDEwMDQ4MWIwYjBmMDAyMDAwMjAwMTIwMDMyMDAyMTAyNjQxMDA0NzBiMDkwMDIwMDAyMDAxMTAwMjFhMGIwYTAwMjAwMDIwMDAyMDAxMTAwNTBiMGMwMDIwMDAyMDAwMjAwMTEwMDUyMDAwMGIyMDAwMjAwMDIwMDAyMDAxMTAxNTIwMDAxMDA2NDEwMDQ4MDQ0MDQxZGM4NGMwMDA0MTMwMTAwMDAwMGIyMDAwMGIwZjAxMDE3ZjEwMzAyMjAxMjAwMDEwMTYxYTIwMDEwYjBkMDAyMDAwMTAzMDIyMDAxMDE3MWEyMDAwMGIwYzAxMDE3ZjEwMzAyMjAwMTAzMTIwMDAwYmZlMDEwMTAyN2YyMzAwNDEzMDZiMjIwMzI0MDAyMDAzNDEyMDZhNDEwMDM2MDIwMDIwMDM0MjAwMzcwMzE4MjAwMzIwMDI0MTA4NzQ0MTgwODBmYzA3NzEyMDAyNDExODc0NzIyMDAyNDEwODc2NDE4MGZlMDM3MTIwMDI0MTE4NzY3MjcyMzYwMjI4MjAwMzQxMTA2YTIwMDM0MTE4NmEyMjAyNDEwMDQxMDQxMDRlMjAwMzI4MDIxMDIwMDMyODAyMTQyMDAzNDEyODZhMjIwNDQxMDQxMDRmMjAwMzIwMDE0MjI4ODY0MjgwODA4MDgwODA4MGMwZmYwMDgzMjAwMTQyMzg4Njg0MjAwMTQyMTg4NjQyODA4MDgwODA4MGUwM2Y4MzIwMDE0MjA4ODY0MjgwODA4MDgwZjAxZjgzODQ4NDIwMDE0MjA4ODg0MjgwODA4MGY4MGY4MzIwMDE0MjE4ODg0MjgwODBmYzA3ODM4NDIwMDE0MjI4ODg0MjgwZmUwMzgzMjAwMTQyMzg4ODg0ODQ4NDM3MDMyODIwMDM0MTA4NmEyMDAyNDEwNDQxMGMxMDRlMjAwMzI4MDIwODIwMDMyODAyMGMyMDA0NDEwODEwNGYyMDAwMjgwMjAwMjAwMjQxMGMxMDA3MWEyMDAzNDEzMDZhMjQwMDBiM2IwMTAxN2YyMzAwNDExMDZiMjIwNDI0MDAyMDA0NDEwODZhMjAwMjIwMDMyMDAxNDEwYzEwMjcyMDA0MjgwMjBjMjEwMTIwMDAyMDA0MjgwMjA4MzYwMjAwMjAwMDIwMDEzNjAyMDQyMDA0NDExMDZhMjQwMDBiYjUwMjAxMDU3ZjIwMDEyMDAzNDYwNDQwMjAwMTIyMDM0MTBmNGIwNDQwMjAwMDQxMDAyMDAwNmI0MTAzNzEyMjA0NmEyMTA1MjAwNDA0NDAyMDAyMjEwMTAzNDAyMDAwMjAwMTJkMDAwMDNhMDAwMDIwMDE0MTAxNmEyMTAxMjAwMDQxMDE2YTIyMDAyMDA1NDkwZDAwMGIwYjIwMDUyMDAzMjAwNDZiMjIwMzQxN2M3MTIyMDY2YTIxMDAwMjQwMjAwMjIwMDQ2YTIyMDQ0MTAzNzEwNDQwMjAwNjQxMDA0YzBkMDEyMDA0NDEwMzc0MjIwMTQxMTg3MTIxMDc0MTAwMjAwMTZiNDExODcxMjEwODIwMDQ0MTdjNzEyMjAxNDEwNDZhMjEwMjIwMDEyODAyMDAyMTAxMDM0MDIwMDUyMDAxMjAwNzc2MjAwMjI4MDIwMDIyMDEyMDA4NzQ3MjM2MDIwMDIwMDI0MTA0NmEyMTAyMjAwNTQxMDQ2YTIyMDUyMDAwNDkwZDAwMGIwYzAxMGIyMDA2NDEwMDRjMGQwMDIwMDQyMTAyMDM0MDIwMDUyMDAyMjgwMjAwMzYwMjAwMjAwMjQxMDQ2YTIxMDIyMDA1NDEwNDZhMjIwNTIwMDA0OTBkMDAwYjBiMjAwMzQxMDM3MTIxMDMyMDA0MjAwNjZhMjEwMjBiMjAwMzA0NDAyMDAwMjAwMzZhMjEwMTAzNDAyMDAwMjAwMjJkMDAwMDNhMDAwMDIwMDI0MTAxNmEyMTAyMjAwMDQxMDE2YTIyMDAyMDAxNDkwZDAwMGIwYjBmMGIxMDI4MDAwYjU1MDEwMTdmMjAwMDJkMDAwNDIxMDEyMDAwNDEwMDNhMDAwNDAyNDAyMDAxNDEwMTcxMDQ0MDQxYTA4NWMwMDAyODAyMDAyMjAxNDE5MWNlMDA0ZjBkMDEyMDAwMjgwMjAwNDFhNDg1YzAwMDIwMDExMDA3MWE0MWEwODVjMDAwNDEwMDM2MDIwMDQxYjRkM2MwMDA0MTAwM2EwMDAwMGIwZjBiMTAyODAwMGIwZDAwMjAwMDEwMzAyMjAwMTAxODFhMjAwMDBiM2EwMTAxN2YyMzAwNDExMDZiMjIwMjI0MDAyMDAyMjAwMTQxZmYwMTcxNDEwMjc0NDFkMDg0YzAwMDZhMjgwMjAwMmQwMDAwM2EwMDBmMjAwMDIwMDI0MTBmNmE0MTAxMTAwNzFhMjAwMjQxMTA2YTI0MDAwYjA4MDAyMDAwMTA1MTEwM2QwYjA4MDAyMDAwMTA1MTEwNGIwYjJmMDEwMTdlMDI3ZjAyNDAyMDAwMTA1NjIyMDE0MjAxNTgwNDQwNDEwMDIwMDFhNzQxMDE2YjBkMDIxYTBjMDEwYjQxOGM4MWMwMDA0MTEyMTA1NzAwMGI0MTAxMGIwYjVkMDIwMjdmMDE3ZTIzMDA0MTEwNmIyMjAxMjQwMDIwMDE0MjAwMzcwMzA4MjAwMDEwNTEyMjAwMTAxMjIyMDI0MTA5NGYwNDQwNDFmYTgwYzAwMDQxMGUxMDU3MDAwYjIwMDEyMDAxNDEwODZhMjAwMjEwNjIyMDAwNDEwMDIwMDEyODAyMDAyMjAwMjAwMTI4MDIwNDIyMDIxMDQ1MWEyMDAwMjAwMjEwNjMyMDAxNDExMDZhMjQwMDBiMWIwMTAxN2Y0MTllODFjMDAwNDExNjEwMmQyMjAyMjAwMDIwMDExMDA3MWEyMDAyMTAxMTAwMGIzNjAxMDE3ZjIzMDA0MTEwNmIyMjAyMjQwMDIwMDI0MjAwMzcwMzA4MjAwMjIwMDE0MTAwMjAwMjQxMDg2YTEwNTkyMDAwMjAwMjI4MDIwMDIwMDIyODAyMDQxMDVhMjAwMjQxMTA2YTI0MDAwYjljMDIwMjA0N2YwMTdlMjAwMzIwMDE0MjI4ODY0MjgwODA4MDgwODA4MGMwZmYwMDgzMjAwMTQyMzg4Njg0MjAwMTQyMTg4NjQyODA4MDgwODA4MGUwM2Y4MzIwMDE0MjA4ODY0MjgwODA4MDgwZjAxZjgzODQ4NDIwMDE0MjA4ODg0MjgwODA4MGY4MGY4MzIwMDE0MjE4ODg0MjgwODBmYzA3ODM4NDIwMDE0MjI4ODg0MjgwZmUwMzgzMjAwMTQyMzg4ODg0ODQ4NDIyMDgzNzAwMDAwMjQwMDI0MDIwMDAwMjdmMjAwMTUwMDQ0MDQxZGM4NGMwMDAyMTAzNDEwMDBjMDEwYjIwMDI0MTAwMjAwMTQyN2Y1MTFiNDUwNDQwNDEwMDIwMDIyMDA4NDI4MDAxODM0MjA3ODhhNzcxMjIwNTZiNDFmZjAxNzEyMTA2MDM0MDIwMDQ0MTA4NDYwZDAzMjAwNjIwMDMyMDA0NmEyZDAwMDAyMjA3NDcwNDQwMjAwNDIwMDIyMDA3NDEwNzc2MjAwNTQ3NzEyMjAyNmI0MTA5NGYwZDA1MjAwMzQxMDAyMDAyNmIyMjAyNmEyMDA0NmEyMTAzNDEwODIwMDIyMDA0NmE2YjBjMDMwNTIwMDQ0MTAxNmEyMTA0MGMwMTBiMDAwYjAwMGIyMDAzNDEwNzZhMjEwMzQxMDEwYjM2MDIwNDIwMDAyMDAzMzYwMjAwMGYwYjEwODAwMTAwMGIxMDgwMDEwMDBiMGQwMDIwMDAyMDAxMjAwMjEwMmQxMDE5MWEwYjM3MDEwMTdmMjMwMDQxMTA2YjIyMDIyNDAwMjAwMjQyMDAzNzAzMDgyMDAyMjAwMWFkNDEwMTIwMDI0MTA4NmExMDU5MjAwMDIwMDIyODAyMDAyMDAyMjgwMjA0MTA1YTIwMDI0MTEwNmEyNDAwMGIwYjAwMjAwMDIwMDExMDRhMTAxOTFhMGIwZTAwMjAwMDQxNjcxMDE4MWE0MTY3MTAxMjQ1MGIwOTAwMTAyZTFhMjAwMDEwNGEwYjBkMDAxMDJlMWEyMDAwMjAwMTEwMjkxMDNiMGI1YTAyMDI3ZjAxN2UyMzAwNDExMDZiMjIwMjI0MDAxMDJlMjEwMzIwMDE0MWZmMDE3MTQxMDI3NDQxZDA4NGMwMDA2YTI4MDIwMDMxMDAwMDIxMDQyMDAyNDIwMDM3MDMwODIwMDIyMDA0NDEwMDIwMDI0MTA4NmExMDU5MjAwMzIwMDIyODAyMDAyMDAyMjgwMjA0MTAwZjFhMjAwMDIwMDMxMDNiMjAwMjQxMTA2YTI0MDAwYjEyMDEwMTdmMTAyZTIyMDIyMDAwMjAwMTEwMmQxMDNiMjAwMjBiMzkwMTAxN2YyMzAwNDExMDZiMjIwMzI0MDAyMDAzNDEwODZhMjAwMTQxMDgyMDAyMTA2YTIwMDMyODAyMGMyMTAxMjAwMDIwMDMyODAyMDgzNjAyMDAyMDAwMjAwMTM2MDIwNDIwMDM0MTEwNmEyNDAwMGIzNDAxMDE3ZTAyNDAyMDAxNDUwZDAwMDM0MDIwMDE0NTBkMDEyMDAxNDEwMTZiMjEwMTIwMDAzMTAwMDAyMDAyNDIwODg2ODQyMTAyMjAwMDQxMDE2YTIxMDAwYzAwMGIwMDBiMjAwMjBiODAwMTAxMDM3ZjIzMDA0MTEwNmIyMjAxMjQwMDIwMDAyODAyMDgyMTAzMjAwMTQxMDAzNjAyMGMyMDAwMjgwMjAwMjAwMzQxMDI3NDIwMDE0MTBjNmE0MTA0MTA0NTQ1MDQ0MDIwMDEyODAyMGMyMTAyMjAwMDIwMDM0MTAxNmEzNjAyMDgyMDAyNDEwODc0NDE4MDgwZmMwNzcxMjAwMjQxMTg3NDcyMjAwMjQxMDg3NjQxODBmZTAzNzEyMDAyNDExODc2NzI3MjEwMjkyMDAxNDExMDZhMjQwMDBmMGI0MWM0ODFjMDAwNDEwODQxYmU4MGMwMDA0MTExMTAzNzAwMGJmYjAxMDIwNDdmMDE3ZTIzMDA0MTEwNmIyMjAzMjQwMDAyNDAwMjQwMDI0MDAyNDAyMDAxMjgwMjA4MjAwMTI4MDIwNDRmMDQ0MDIwMDA0MTAzM2EwMDA0MGMwMTBiMjAwMTEwNjQxMDI5MjIwNTEwMTI0MTIwNDcwZDAxMDI0MDIwMDExMDY0MjIwMjEwMTI0NTA0NDA0MTAwMjEwMjBjMDEwYjIwMDM0MjAwMzcwMzA4MjAwMjEwMTIyMjA0NDEwOTRmMGQwNDIwMDMyMDAzNDEwODZhMjAwNDEwNjIyMDAyNDEwMDIwMDMyODAyMDAyMjAyMjAwMzI4MDIwNDIyMDQxMDQ1MWEyMDAyMjAwNDEwNjMyMjA2NDI4MDAyNWEwZDA0MjAwNmE3MjIwMjQxZmYwMTcxNDEwMzRmMGQwMzBiMjAwMDQxMDg2YTIwMDExMDY0MTA0YjM2MDIwMDIwMDAyMDAyM2EwMDA0MjAwMDIwMDUzNjAyMDAwYjIwMDM0MTEwNmEyNDAwMGYwYjQxYzQ4MWMwMDA0MTA4NDFiNDgxYzAwMDQxMTAxMDM3MDAwYjQxYzQ4MWMwMDA0MTA4NDFjMjg0YzAwMDQxMGQxMDM3MDAwYjQxYzQ4MWMwMDA0MTA4NDFmYTgwYzAwMDQxMGUxMDM3MDAwYjgwMDEwMTAyN2YyMzAwNDExMDZiMjIwMzI0MDAwMjQwMDI0MDIwMDAyZDAwMDQwNDQwNDE5MGNlMDA0MWEwODVjMDAwMjgwMjAwMjIwNDZiMjAwMjQ5MGQwMTIwMDM0MTA4NmEyMDA0MjAwMjIwMDQ2YTIyMDAxMDY3MjAwMzI4MDIwODIwMDMyODAyMGMyMDAxMjAwMjEwNGY0MWEwODVjMDAwMjAwMDM2MDIwMDBjMDIwYjIwMDAyODAyMDAyMDAxMjAwMjEwMDcxYTBjMDEwYjIwMDAxMDUwMjAwMDI4MDIwMDIwMDEyMDAyMTAwNzFhMGIyMDAzNDExMDZhMjQwMDBiNDAwMTAxN2YyMzAwNDExMDZiMjIwMzI0MDAyMDAzNDEwODZhMjAwMTIwMDI0MWE0ODVjMDAwNDE5MGNlMDAxMDI3MjAwMzI4MDIwYzIxMDEyMDAwMjAwMzI4MDIwODM2MDIwMDIwMDAyMDAxMzYwMjA0MjAwMzQxMTA2YTI0MDAwYjBmMDAyMDAwMjAwMTEwNDM0MWZmMDE3MTQxMDI0OTBiMGYwMDIwMDAyMDAxMTA0MzQxZmYwMTcxNDEwMTQ2MGIzYjAxMDE3ZjIzMDA0MTEwNmIyMjA0MjQwMDIwMDQ0MTA4NmE0MTAwMjAwMzIwMDEyMDAyMTAyNzIwMDQyODAyMGMyMTAxMjAwMDIwMDQyODAyMDgzNjAyMDAyMDAwMjAwMTM2MDIwNDIwMDQ0MTEwNmEyNDAwMGIyMTAxMDE3ZjQxY2M4MWMwMDA0MTBhMTA2MTIyMDMyMDAwMTA1ZjIwMDMyMDAxMTA2MDIwMDMyMDAyMTA1ZTEwMWMwYjIxMDEwMTdmNDFkNjgxYzAwMDQxMGMxMDYxMjIwMzIwMDAxMDVmMjAwMzIwMDExMDYwMjAwMzIwMDIxMDVlMTAxYzBiMGIwMDQxODI4MmMwMDA0MTBmMTAyZDBiMWUwMTAxN2Y0MTkxODJjMDAwNDEwOTEwMmQyMjAyMjAwMDEwNDYyMDAyMjAwMTJkMDAwMDEwNTIyMDAyMGIxZTAxMDE3ZjQxOWE4MmMwMDA0MTA1MTAyZDIyMDIyMDAwMTA0NjIwMDIyMDAxMmQwMDAwMTA1MjIwMDIwYjBiMDA0MTlmODJjMDAwNDEwODEwMmQwYjBlMDAxMDFmNDEwMDEwM2YxMDcwNDEwMTEwNWIwYmIwMDEwMjA3N2YwMTdlMjMwMDQxMTA2YjIyMDAyNDAwMTAzNDQxMDIxMDNmMTAzYzIxMDEyMDAwNDEwMTEwMzkyMjA0M2EwMDBmMDI0MDAyNDAxMDZkMTA1ZDQ1MDQ0MDIwMDAxMDM1MjAwMDI4MDIwMDIxMDIyMDAwMjgwMjA0MjAwMTIwMDA0MTBmNmExMDZmMTA1NDIxMDUxMDZkMTA1MzEwMjAyMTA3MTAyYTQ1MGQwMTIwMDIxMDRjMTA2OTQ1MGQwMjIwMDEyMDAwNDEwZjZhMjIwMzEwNmYyMDA1MjAwMjEwNDgxMDVjMjAwMTIwMDMxMDZlMjAwNzEwNTgyMDAxMjAwNDIwMDIxMDZiMjAwMDQxMTA2YTI0MDAwZjBiNDFhNzgyYzAwMDQxMTcxMDJiMDAwYjQxYmU4MmMwMDA0MTFkMTAyYjAwMGI0MWRiODJjMDAwNDExYjEwMmIwMDBiYzcwMjAyMDc3ZjAyN2UyMzAwNDFkMDAwNmIyMjAwMjQwMDEwMzQxMDQxMTA0MDIwMDA0MTAwMzYwMjQwMjAwMDQxNDA2YjEwM2EyMTAxMjAwMDI4MDI0MDEwM2UxMDZkMTA1ZDQ1MDQ0MDIwMDA0MTA4NmExMDM1MjAwMDI4MDIwODIxMDUyMDAwMjgwMjBjMTA2ZDEwNTMxMDIwMjEwNzEwMmEwNDQwMjAwNTEwNGMxMDY5MDQ0MDEwNGMyMTAzMjAwMDQxMTA2YTIwMDExMDQyMjAwMDQxMjg2YTIwMDA0MTE4NmEyODAyMDAzNjAyMDAyMDAwMjAwMDI5MDMxMDM3MDMyMDIwMDA0MTQwNmI0MTA0NzIyMTAxMDM0MDAyNDAyMDAwNDEzMDZhMjAwMDQxMjA2YTEwNjUyMDAwMmQwMDM0NDEwMzQ2MDQ0MDIwMDMyMDA1MTA0NDQxZmYwMTcxMGQwMTIwMDA0MWQwMDA2YTI0MDAwZjA1MjAwMDQxYzgwMDZhMjIwNDIwMDA0MTM4NmEyODAyMDAzNjAyMDAyMDAwMjAwMDI5MDMzMDIyMDgzNzAzNDAyMDA4YTcyMjAyMjAwMTEwNmYxMDU0MjEwNjIwMDIyMDAxMTA2ZjIwMDYyMDA0MjgwMjAwMjIwNDEwNDgxMDVjMjAwMjIwMDExMDZlMjAwNzEwNTgyMDAzMjAwNDEwNDcyMDAyMjAwMDJkMDA0NDIwMDQxMDZiMGMwMjBiMDAwYjBiNDFmNjgyYzAwMDQxMjYxMDJiMDAwYjQxZGI4MmMwMDA0MTFiMTAyYjAwMGI0MWJlODJjMDAwNDExZDEwMmIwMDBiNDFhNzgyYzAwMDQxMTcxMDJiMDAwYmY0MDIwMTA1N2YyMzAwNDExMDZiMjIwMjI0MDAxMDFmMTA0MTEwNDAyMDAyNDEwMDM2MDIwODIwMDI0MTA4NmEyMjAwMjgwMjAwNDFiOGQzYzAwMDI4MDIwMDQ4MDQ3ZjIwMDA0MWFiODRjMDAwNDEwYTEwMzgxMDM5MDU0MTAzMGIyMTAxMjAwMjI4MDIwODEwM2UwMjQwMDI0MDEwNmQxMDVkNDUwNDQwMTA2ZDEwNTMyMTAzMTAzMjIxMDAxMDcwMTA1NTBkMDEwMjQwMjAwMTQxZmYwMTcxNDEwMzQ3MDQ0MDIwMDIyMDAxM2EwMDBmMjAwMDIwMDI0MTBmNmExMDZmMTA1NDIyMDQxMDRjMTA2OTQ1MGQwMTIwMDAyMDAzMjAwNDEwMmMyMDAwMjAwMjQxMGY2YTEwNmYxMDRjMTA1YzQxZTI4MWMwMDA0MTBlMTA2MTIyMDMyMDAwMTA1ZjIwMDMyMDAxMTA2MDIwMDMyMDA0MTA1ZTEwMWMwYzA0MGIxMDRjMjIwMTIwMDA0MWU4ODNjMDAwMTA2ZjEwNTQxMDQ3MjAwMTIwMDA0MWU5ODNjMDAwMTA2ZjEwNTQxMDQ3MjAwMTIwMDA0MWVhODNjMDAwMTA2ZjEwNTQxMDQ3MjAwMTEwNGMxMDY5MDQ0MDIwMDAyMDAzMjAwMTEwMmMyMDAwNDFlODgzYzAwMDEwNmYxMDRjMTA1YzIwMDA0MWU5ODNjMDAwMTA2ZjEwNGMxMDVjMjAwMDQxZWE4M2MwMDAxMDZmMTA0YzEwNWM0MWYwODFjMDAwNDExMjEwNjEyMjAzMjAwMDEwNWYyMDAzMjAwMTEwNWUxMDFjMGMwNDBiNDFkMzgzYzAwMDQxMTUxMDJiMDAwYjQxZDM4M2MwMDA0MTE1MTAyYjAwMGI0MWE3ODJjMDAwNDExNzEwMmIwMDBiNDFjMTgzYzAwMDQxMTIxMDJiMDAwYjIwMDI0MTEwNmEyNDAwMGIxNjAwMTAxZjQxMDAxMDNmNDE5ZjgyYzAwMDQxMDgxMDJkMTA1NWFkMTAyMTBiMTUwMDEwMWYxMDM0NDEwMDEwM2YxMDcwMTA3MDEwNTU0MTAxNzMxMDViMGJhMDAxMDIwODdmMDE3ZTIzMDA0MTEwNmIyMjAxMjQwMDEwMWYxMDM0NDEwMzEwM2YxMDNjMjEwMjQxMDExMDM5MjEwMzQxMDIxMDMwMjIwMDEwMTMyMDAxMjAwMzNhMDAwZjAyNDAxMDZkMTA1ZDQ1MDQ0MDIwMDIyMDAxNDEwZjZhMTA2ZjEwNTQyMTA0MTAzMzEwNmQxMDUzMTAyMDIxMDgyMDA0MjAwMDEwNjg0NTBkMDEyMDAyMjAwMTQxMGY2YTIyMDcxMDZmMjAwNDIwMDAxMDQ5MTA1YzIwMDIyMDA3MTA2ZTIwMDgxMDU4MjAwMDEwMmMyMDAyMjAwMzIwMDAxMDZjMjAwMTQxMTA2YTI0MDAwZjBiNDFhNzgyYzAwMDQxMTcxMDJiMDAwYjQxOWM4M2MwMDA0MTI1MTAyYjAwMGJhMTAyMDIwNjdmMDI3ZTIzMDA0MTQwNmEyMjAwMjQwMDEwMWYxMDM0MTA0MTEwNDAyMDAwNDEwMDM2MDIzMDIwMDA0MTMwNmExMDNhMjEwMTIwMDAyODAyMzAxMDNlMTA2ZDEwNWQ0NTA0NDAxMDRjMjEwMzEwMjAyMTA2MjAwMDIwMDExMDQyMjAwMDQxMTg2YTIwMDA0MTA4NmEyODAyMDAzNjAyMDAyMDAwMjAwMDI5MDMwMDM3MDMxMDIwMDA0MTMwNmE0MTA0NzIyMTAxMDI0MDAzNDAyMDAwNDEyMDZhMjAwMDQxMTA2YTEwNjUyMDAwMmQwMDI0NDEwMzQ2MDQ0MDIwMDMxMDRjMTA2OTBkMDI0MWViODNjMDAwNDEyNTEwMmIwMDBiMjAwMDQxMzg2YTIyMDIyMDAwNDEyODZhMjgwMjAwMzYwMjAwMjAwMDIwMDAyOTAzMjAyMjA3MzcwMzMwMjAwN2E3MjIwNDIwMDExMDZmMTA1NDIxMDUyMDA0MjAwMTEwNmUyMDA2MTA1ODIwMDUyMDAyMjgwMjAwMjIwMjEwNjgwNDQwMjAwMzIwMDIxMDQ3MjAwNDIwMDExMDZmMjAwNTIwMDIxMDQ5MTA1YzIwMDQyMDAwMmQwMDM0MjAwMjEwNmMwYzAxMGIwYjQxOWM4M2MwMDA0MTI1MTAyYjAwMGIxMDMzMTA2ZDEwNTMyMDAzMTAyYzIwMDA0MTQwNmIyNDAwMGYwYjQxYTc4MmMwMDA0MTE3MTAyYjAwMGI0NTAxMDI3ZjEwMWYxMDM0NDEwMTEwM2Y0MTAwMTAzNjEwM2QyMTAwMDI0MDEwNmQxMDVkMDQ0MDEwNmQyMTAxMjAwMDEwMmYwZDAxMjAwMTIwMDAxMDE5MWEwZjBiNDE5MDg0YzAwMDQxMWIxMDJiMDAwYjIwMDE0MTg4ODFjMDAwNDEwNDEwNWEwYjMwMDEwMzdmMTAxZjQxMDIxMDNmMTAzYzIxMDE0MTAxMTAzOTIxMDI0MTlhODJjMDAwNDEwNTEwMmQyMjAwMjAwMTEwMDIxYTIwMDAyMDAyMTA1MjIwMDAxMDU0MTAyMjBiMzAwMTAzN2YxMDFmNDEwMjEwM2YxMDNjMjEwMTQxMDExMDM5MjEwMjQxOTE4MmMwMDA0MTA5MTAyZDIyMDAyMDAxMTAwMjFhMjAwMDIwMDIxMDUyMjAwMDEwNTYxMDIzMGJhNTA1MDIwODdmMDE3ZTIzMDA0MTMwNmIyMjAwMjQwMDEwMWY0MTAxMjEwNDQxMDExMDNmMTAzYzIxMDEyMDAwMTAyZTIyMDUzNjAyMjAyMDAxNDFlODgzYzAwMDEwNmYxMDU0MjEwMjIwMDA0MTIwNmEyMjAzMjAwMTQxZTg4M2MwMDAxMDZlMTA1NjIwMDIxMDRkMjAwMTQxZTk4M2MwMDAxMDZmMTA1NDIxMDIyMDAzMjAwMTQxZTk4M2MwMDAxMDZlMTA1NjIwMDIxMDRkMjAwMTQxZWE4M2MwMDAxMDZmMTA1NDIxMDIyMDAzMjAwMTQxZWE4M2MwMDAxMDZlMTA1NjIwMDIxMDRkMDI3ZjQxYjRkM2MwMDAyZDAwMDA0NTA0NDA0MWI0ZDNjMDAwNDEwMTNhMDAwMDQxYTA4NWMwMDA0MTAwMzYwMjAwMjAwMDQxMTA2YTQxYTQ4NWMwMDA0MTkwY2UwMDQxMDAxMDZhMjAwMDI4MDIxMDIwMDAyODAyMTQ0MWRjODRjMDAwNDEwMDEwNGYxMDJlMGMwMTBiNDEwMDIxMDQ0MWRjODRjMDAwNDEwMDEwMmQwYjIxMDEyMDAwMjAwNDNhMDAxYzIwMDAyMDAxMzYwMjE4MjAwNTEwMTIyMTA2NDEwMDIxMDEwMzQwMjAwMTQxMGM2YTIyMDQyMDA2NGI0NTA0NDAyMDAwNDEyODZhNDEwMDM2MDIwMDIwMDA0MjAwMzcwMzIwMjAwNTIwMDEyMDAwNDEyMDZhMjIwMzQxMGMxMDQ1MWEyMDAwMjkwMjI0MjEwODIwMDAyMDAwMjgwMjIwMjIwMTQxMTg3NDIwMDE0MTA4NzQ0MTgwODBmYzA3NzE3MjIwMDE0MTA4NzY0MTgwZmUwMzcxMjAwMTQxMTg3NjcyNzIxMDRhMjIwMTEwMTIyMjAyNDExODc0MjAwMjQxMDg3NDQxODA4MGZjMDc3MTcyMjAwMjQxMDg3NjQxODBmZTAzNzEyMDAyNDExODc2NzI3MjM2MDIyMDIwMDA0MTE4NmEyMDAzNDEwNDEwNjYyMDAwMmQwMDFjMjEwMjIwMDA0MTAwM2EwMDFjMDI0MDAyNDAwMjQwMjAwMjQxMDE3MTIyMDIwNDQwMjAwMTEwMTIyMjA3NDE5MGNlMDA0MWEwODVjMDAwMjgwMjAwMjIwMzZiNGIwZDAyMjAwMDQxMDg2YTIwMDMyMDAzMjAwNzZhMjIwMzEwNjcyMDAxNDEwMDIwMDAyODAyMDgyMDAwMjgwMjBjMTA0NTFhNDFhMDg1YzAwMDIwMDMzNjAyMDAwYzAxMGIyMDAwMjgwMjE4MjAwMTEwMDIxYTBiMjAwMDIwMDIzYTAwMWMwYzAxMGIyMDAwNDExODZhMTA1MDIwMDAyODAyMTgyMDAxMTAwMjFhMjAwMDJkMDAxYzIwMDAyMDAyM2EwMDFjNDEwMTcxNDUwZDAwNDFhMDg1YzAwMDQxMDAzNjAyMDA0MWI0ZDNjMDAwNDEwMDNhMDAwMDBiMjAwMDIwMDgzNzAzMjAyMDAwNDExODZhMjAwMDQxMjA2YTQxMDgxMDY2MjAwNDIxMDEwYzAxMGIwYjIwMDAyODAyMTgyMTAxMjAwMDIwMDAyZDAwMWMzYTAwMjQyMDAwMjAwMTM2MDIyMDIwMDA0MTIwNmExMDUwMjAwMDI4MDIyMDIwMDAyZDAwMjQwNDQwNDFhMDg1YzAwMDQxMDAzNjAyMDA0MWI0ZDNjMDAwNDEwMDNhMDAwMDBiMTAyNDFhMjAwMDQxMzA2YTI0MDAwYjQxMDEwMjdmMTAxZjQxMDExMDNmMTAzYzIxMDAxMDRjMjIwMTIwMDA0MWU4ODNjMDAwMTA2ZjEwNTQxMDQ3MjAwMTIwMDA0MWU5ODNjMDAwMTA2ZjEwNTQxMDQ3MjAwMTIwMDA0MWVhODNjMDAwMTA2ZjEwNTQxMDQ3MjAwMTEwMjIwYjJjMDEwMTdmMTAxZjQxMDAxMDNmNDE4MjgyYzAwMDQxMGYxMDJkMTA1MzIyMDAxMDJmNDUwNDQwMjAwMDEwMjQxYTBmMGI0MTg4ODFjMDAwNDEwNDEwMjUwYjAzMDAwMTBiMGMwMDQxOGM4NWMwMDA0MTBlMTAwMDAwMGIwYmIwMDUwMjAwNDE4MDgwYzAwMDBiOWEwNTQ1NmU2NDcwNmY2OTZlNzQyMDYzNjE2ZTIwNmY2ZTZjNzkyMDYyNjUyMDYzNjE2YzZjNjU2NDIwNjI3OTIwNmY3NzZlNjU3MjYxNzI2Nzc1NmQ2NTZlNzQyMDY0NjU2MzZmNjQ2NTIwNjU3MjcyNmY3MjIwMjgyOTNhMjA3NDZmNmYyMDY2NjU3NzIwNjE3MjY3NzU2ZDY1NmU3NDczNzQ2ZjZmMjA2ZDYxNmU3OTIwNjE3MjY3NzU2ZDY1NmU3NDczNzc3MjZmNmU2NzIwNmU3NTZkNjI2NTcyMjA2ZjY2MjA2MTcyNjc3NTZkNjU2ZTc0NzM2OTZlNzA3NTc0MjA3NDZmNmYyMDZjNmY2ZTY3NDU0NzRjNDQ2OTZlNzA3NTc0MjA2Zjc1NzQyMDZmNjYyMDcyNjE2ZTY3NjU3Mzc0NmY3MjYxNjc2NTIwNjQ2NTYzNmY2NDY1MjA2NTcyNzI2ZjcyM2EyMDYyNjE2NDIwNjE3MjcyNjE3OTIwNmM2NTZlNjc3NDY4NzY2MTcyMjA2MTcyNjc3MzYzNmM2MTY5NmQ0MTY0NjQ2NTY0NjM2YzYxNjk2ZDUyNjU2ZDZmNzY2NTY0NjM2YzYxNjk2ZDQzNmY2YzZjNjU2Mzc0NjU2NDYxNmM2YzQzNmM2MTY5NmQ3MzQzNmY2YzZjNjU2Mzc0NjU2NDc0NmY2YjY1NmU0OTY0NjU2ZTc0Njk2NjY5NjU3MjYzNmM2MTY5NmQ0NDYxNzQ2NTYzNmM2MTY5NmQ2OTczNTA2MTc1NzM2NTY0NTI2NTc3NjE3MjY0MjA3NDZmNmI2NTZlMjA2OTczMjA2ZTZmNzQyMDczNjU3NDQzNjE2ZTIwNmY2ZTZjNzkyMDYxNjQ2NDIwNjQ2NTczNjk2NzZlNjE3NDY1NjQyMDc0NmY2YjY1NmU0ZDc1NzM3NDIwNjE2NDY0MjA2ZDZmNzI2NTIwNzQ2ODYxNmUyMDMwMjA3NDZmNmI2NTZlNzM0MzZjNjE2OTZkNzMyMDYxNjQ2NDY1NjQyMDZkNzU3Mzc0MjA2NTcxNzU2MTZjMjA3MDYxNzk2ZDY1NmU3NDIwNjE2ZDZmNzU2ZTc0NDM2MTZlNmU2Zjc0MjA3MjY1NmQ2Zjc2NjUyMDZkNmY3MjY1MjA3NDY4NjE2ZTIwNjM3NTcyNzI2NTZlNzQyMDYzNmM2MTY5NmQ0MzZmNmU3NDcyNjE2Mzc0MjA2OTczMjA3MDYxNzU3MzY1NjQ0MzYxNmU2ZTZmNzQyMDYzNmM2MTY5NmQyMDMwMjA3NDZmNmI2NTZlNzMwMDAxMDI0MzZjNjE2OTZkNzMyMDcyNjU2ZDZmNzY2NTY0MjA2ZDc1NzM3NDIwNjI2NTIwNjc3MjY1NjE3NDY1NzIyMDc0Njg2MTZlMjAzMDUyNjU3NzYxNzI2NDIwNzQ2ZjZiNjU2ZTIwNjk3MzIwNjE2YzcyNjU2MTY0NzkyMDczNjU3NDYzNmM2MTY5NmQ1Zjc0Nzk3MDY1NjE2NDY0NzI2NTczNzM2MzZjNjE2OTZkNzM2OTZlNzY2MTZjNjk2NDIwNzY2MTZjNzU2NTAwZTgwMTEwMDBlOTAxMTAwMGVhMDExMDAwNjM2MTZlNmU2Zjc0MjA3Mzc1NjI3NDcyNjE2Mzc0MjA2MjY1NjM2MTc1NzM2NTIwNzI2NTczNzU2Yzc0MjA3NzZmNzU2YzY0MjA2MjY1MjA2ZTY1Njc2MTc0Njk3NjY1NzA2MTZlNjk2MzIwNmY2MzYzNzU3MjcyNjU2NDAwNDE5Yzg1YzAwMDBiMDQ5Y2ZmZmZmZkAwNTAwQDA1MDI=", - "chainID": "D", - "version": 1, - "signature": "c98aaed578a089e4a034fbc75e8ccae200d0f489923cb441ea943da755e99b7e69889d7509ca489fc3de10d190378d0c887e8b804c09e57a224723f634d75509" - }, - "hash": "10ce205dd38daf38cd03ca2dc0682519c7f26f90cc971b82d0c5077764336288", - "data": "0061736d01000000016e1360000060017f017f60027f7f017f60027f7f006000017f60017f0060037f7f7f0060047f7f7f7f0060037f7f7f017f60057f7f7e7f7f017f60027f7e0060017f017e60017e0060047f7f7f7f017f6000017e60057f7f7f7f7f0060037f7e7f0060047f7e7f7f0060027f7f017e02a8072703656e760b7369676e616c4572726f72000303656e760a6d4275666665724e6577000403656e760d6d427566666572417070656e64000203656e76096d4275666665724571000203656e761b6d616e616765645472616e7366657256616c756545786563757465000903656e7609626967496e74416464000603656e760a626967496e745369676e000103656e76126d427566666572417070656e644279746573000803656e76226d616e616765644d756c74695472616e73666572455344544e465445786563757465000903656e760d6d616e6167656443616c6c6572000503656e76136d616e616765644f776e657241646472657373000503656e76136765744e756d455344545472616e7366657273000403656e7612626967496e7447657443616c6c56616c7565000503656e7616626967496e744765744553445443616c6c56616c7565000503656e761067657445534454546f6b656e4e616d65000103656e760f6d4275666665725365744279746573000803656e76126d427566666572476574417267756d656e74000203656e76126d616e616765645369676e616c4572726f72000503656e76106d4275666665724765744c656e677468000103656e7619626967496e74476574556e7369676e6564417267756d656e74000303656e760f6765744e756d417267756d656e7473000403656e7609626967496e74537562000603656e76196d42756666657246726f6d426967496e74556e7369676e6564000203656e76176d427566666572546f426967496e74556e7369676e6564000203656e76126d42756666657253746f726167654c6f6164000203656e76136d42756666657253746f7261676553746f7265000203656e760e626967496e74536574496e743634000a03656e7609626967496e74436d70000203656e760f6d616e6167656457726974654c6f67000303656e7611676574417267756d656e744c656e677468000103656e761b736d616c6c496e74476574556e7369676e6564417267756d656e74000b03656e760e636865636b4e6f5061796d656e74000003656e7611676574426c6f636b54696d657374616d70000e03656e7614736d616c6c496e7446696e6973685369676e6564000c03656e7614626967496e7446696e697368556e7369676e6564000503656e7616736d616c6c496e7446696e697368556e7369676e6564000c03656e760d6d42756666657246696e697368000103656e760666696e697368000303656e76136d42756666657247657442797465536c696365000d035b5a0f00010203060204010405040400050107080101030401050500000302020d030302020101041007070501030101010b030a1106030301010303020612010306060202070606040202040000000000000000000000000000000005030100110619037f01418080c0000b7f0041bcd3c0000b7f0041c0d3c0000b07ed0112066d656d6f7279020004696e6974007108616464436c61696d007209616464436c61696d73007305636c61696d0074086973506175736564007505706175736500760b72656d6f7665436c61696d00770c72656d6f7665436c61696d7300780e736574526577617264546f6b656e00790976696577436c61696d007a1076696577436c61696d41646444617465007b1176696577436c61696d5769746844617465007c0a76696577436c61696d73007d1376696577546f6b656e4964656e746966696572007e0863616c6c4261636b007f0a5f5f646174615f656e6403010b5f5f686561705f6261736503020a9d305a2e000240200120024d0440200220044d0d011028000b1028000b2000200220016b3602042000200120036a3602000b0600108001000b0f01017f10012201200010021a20010b0b0020002001100341004a0b0900200020011000000bcb0101057f230041106b2203240041dc84c0004100102d2105102e210602402001102f04402000200242002005200610041a0c010b102e210720011029210410302201103120012001200210052001100604402004102f1a0b2003420037020420032001410874418080fc077120014118747220014108764180fe03712001411876727236020c20032004410874418080fc077120044118747220044108764180fe03712004411876727236020020072003411010071a2000200742002005200610081a0b200341106a24000b1101017f1030220220002001100f1a20020b1401017f1030220041dc84c0004100100f1a20000b070020001012450b1b01017f419c85c000419c85c00028020041016b220036020020000b080020004200101a0b0c01017f10302200100920000b0c01017f10302200100a20000b160010331032102a04400f0b418080c00041241000000ba90101037f230041206b220124000240024002400240100b450440417521024175100c0c010b417321024173100d100b0d010b102e21030c010b200141186a4200370300200141106a4200370300200141086a42003703002001420037030002402001100e22024504401030220341dc84c0004100100f1a0c010b200241214f0d0220012002102d21030b417321020b2000200336020420002002360200200141206a24000f0b1028000b0d0020001030220010101a20000b3001017f41a480c0004117102d22042000200110071a200441bb80c000410310071a20042002200310071a20041011000b3101017f2000280200220341b8d3c0002802004e04402001200241be80c00041111037000b2000200341016a36020020030b5201017e2000101d45044041000f0b02402000101e22014280025404402001a7220041ff017141034f0d0120000f0b41ab84c000410a41fa80c000410e1037000b41ab84c000410a41c284c000410d1037000b3201017f102e21010340200028020041b8d3c0002802004e4504402001200041bc84c000410610381036103b0c010b0b20010b4701017f230041106b2202240020022001410874418080fc077120014118747220014108764180fe03712001411876727236020c20002002410c6a410410071a200241106a24000b2501017f4100103622001012412047044041b584c000410741b481c00041101037000b20000b5101017f230041106b220124000240200010124104470d002001410036020c200041002001410c6a410410451a200128020c41c58eb1a204470d00200041dc84c0004100100f1a0b200141106a240020000b1a00200041b8d3c00028020048044041cf80c00041121000000b0b1500101420004604400f0b41e180c00041191000000b1b0041b8d3c00028020041004e04400f0b41be80c00041111000000b0c0041b8d3c00010143602000b2201017f2001101221022000410036020820002001360200200020024102763602040b08002000200110440b1500417f20002001101b220041004720004100481b0b0f00200020012003200210264100470b09002000200110021a0b0a0020002000200110050b0c00200020002001100520000b2000200020002001101520001006410048044041dc84c00041301000000b20000b0f01017f10302201200010161a20010b0d0020001030220010171a20000b0c01017f10302200103120000bfe0101027f230041306b22032400200341206a41003602002003420037031820032002410874418080fc077120024118747220024108764180fe037120024118767272360228200341106a200341186a220241004104104e20032802102003280214200341286a22044104104f2003200142288642808080808080c0ff00832001423886842001421886428080808080e03f8320014208864280808080f01f838484200142088842808080f80f832001421888428080fc07838420014228884280fe03832001423888848484370328200341086a20024104410c104e2003280208200328020c20044108104f20002802002002410c10071a200341306a24000b3b01017f230041106b22042400200441086a200220032001410c1027200428020c21012000200428020836020020002001360204200441106a24000bb50201057f2001200346044020012203410f4b04402000410020006b41037122046a210520040440200221010340200020012d00003a0000200141016a2101200041016a22002005490d000b0b2005200320046b2203417c7122066a21000240200220046a22044103710440200641004c0d01200441037422014118712107410020016b41187121082004417c71220141046a2102200128020021010340200520012007762002280200220120087472360200200241046a2102200541046a22052000490d000b0c010b200641004c0d0020042102034020052002280200360200200241046a2102200541046a22052000490d000b0b20034103712103200420066a21020b20030440200020036a21010340200020022d00003a0000200241016a2102200041016a22002001490d000b0b0f0b1028000b5501017f20002d00042101200041003a000402402001410171044041a085c00028020022014191ce004f0d01200028020041a485c000200110071a41a085c000410036020041b4d3c00041003a00000b0f0b1028000b0d0020001030220010181a20000b3a01017f230041106b220224002002200141ff017141027441d084c0006a2802002d00003a000f20002002410f6a410110071a200241106a24000b080020001051103d0b080020001051104b0b2f01017e027f0240200010562201420158044041002001a741016b0d021a0c010b418c81c00041121057000b41010b0b5d02027f017e230041106b22012400200142003703082000105122001012220241094f044041fa80c000410e1057000b2001200141086a2002106220004100200128020022002001280204220210451a200020021063200141106a24000b1b01017f419e81c0004116102d22022000200110071a20021011000b3601017f230041106b2202240020024200370308200220014100200241086a1059200020022802002002280204105a200241106a24000b9c0202047f017e2003200142288642808080808080c0ff00832001423886842001421886428080808080e03f8320014208864280808080f01f838484200142088842808080f80f832001421888428080fc07838420014228884280fe038320014238888484842208370000024002402000027f200150044041dc84c000210341000c010b200241002001427f511b45044041002002200842800183420788a77122056b41ff01712106034020044108460d032006200320046a2d000022074704402004200220074107762005477122026b41094f0d052003410020026b22026a20046a21034108200220046a6b0c0305200441016a21040c010b000b000b200341076a210341010b360204200020033602000f0b108001000b108001000b0d00200020012002102d10191a0b3701017f230041106b220224002002420037030820022001ad4101200241086a1059200020022802002002280204105a200241106a24000b0b0020002001104a10191a0b0e002000416710181a41671012450b0900102e1a2000104a0b0d00102e1a200020011029103b0b5a02027f017e230041106b22022400102e2103200141ff017141027441d084c0006a280200310000210420024200370308200220044100200241086a1059200320022802002002280204100f1a20002003103b200241106a24000b1201017f102e220220002001102d103b20020b3901017f230041106b22032400200341086a200141082002106a200328020c21012000200328020836020020002001360204200341106a24000b3401017e02402001450d0003402001450d01200141016b210120003100002002420886842102200041016a21000c000b000b20020b800101037f230041106b22012400200028020821032001410036020c200028020020034102742001410c6a41041045450440200128020c21022000200341016a3602082002410874418080fc077120024118747220024108764180fe0371200241187672721029200141106a24000f0b41c481c000410841be80c00041111037000bfb0102047f017e230041106b220324000240024002400240200128020820012802044f0440200041033a00040c010b200110641029220510124120470d0102402001106422021012450440410021020c010b2003420037030820021012220441094f0d042003200341086a2004106220024100200328020022022003280204220410451a20022004106322064280025a0d042006a7220241ff017141034f0d030b200041086a20011064104b360200200020023a0004200020053602000b200341106a24000f0b41c481c000410841b481c00041101037000b41c481c000410841c284c000410d1037000b41c481c000410841fa80c000410e1037000b800101027f230041106b220324000240024020002d000404404190ce0041a085c00028020022046b2002490d01200341086a2004200220046a220010672003280208200328020c20012002104f41a085c00020003602000c020b20002802002001200210071a0c010b2000105020002802002001200210071a0b200341106a24000b4001017f230041106b22032400200341086a2001200241a485c0004190ce001027200328020c21012000200328020836020020002001360204200341106a24000b0f0020002001104341ff01714102490b0f0020002001104341ff01714101460b3b01017f230041106b22042400200441086a41002003200120021027200428020c21012000200428020836020020002001360204200441106a24000b2101017f41cc81c000410a106122032000105f20032001106020032002105e101c0b2101017f41d681c000410c106122032000105f20032001106020032002105e101c0b0b00418282c000410f102d0b1e01017f419182c0004109102d220220001046200220012d0000105220020b1e01017f419a82c0004105102d220220001046200220012d0000105220020b0b00419f82c0004108102d0b0e00101f4100103f10704101105b0bb00102077f017e230041106b2200240010344102103f103c210120004101103922043a000f02400240106d105d4504402000103520002802002102200028020420012000410f6a106f10542105106d105310202107102a450d012002104c1069450d0220012000410f6a2203106f200520021048105c20012003106e20071058200120042002106b200041106a24000f0b41a782c0004117102b000b41be82c000411d102b000b41db82c000411b102b000bc70202077f027e230041d0006b2200240010341041104020004100360240200041406b103a21012000280240103e106d105d450440200041086a103520002802082105200028020c106d105310202107102a04402005104c10690440104c2103200041106a20011042200041286a200041186a28020036020020002000290310370320200041406b410472210103400240200041306a200041206a106520002d0034410346044020032005104441ff01710d01200041d0006a24000f05200041c8006a2204200041386a2802003602002000200029033022083703402008a722022001106f1054210620022001106f2006200428020022041048105c20022001106e20071058200320041047200220002d00442004106b0c020b000b0b41f682c0004126102b000b41db82c000411b102b000b41be82c000411d102b000b41a782c0004117102b000bf40201057f230041106b22022400101f1041104020024100360208200241086a220028020041b8d3c00028020048047f200041ab84c000410a103810390541030b21012002280208103e02400240106d105d450440106d1053210310322100107010550d010240200141ff01714103470440200220013a000f20002002410f6a106f10542204104c1069450d01200020032004102c20002002410f6a106f104c105c41e281c000410e106122032000105f20032001106020032004105e101c0c040b104c2201200041e883c000106f105410472001200041e983c000106f105410472001200041ea83c000106f105410472001104c10690440200020032001102c200041e883c000106f104c105c200041e983c000106f104c105c200041ea83c000106f104c105c41f081c0004112106122032000105f20032001105e101c0c040b41d383c0004115102b000b41d383c0004115102b000b41a782c0004117102b000b41c183c0004112102b000b200241106a24000b1600101f4100103f419f82c0004108102d1055ad10210b1500101f10344100103f107010701055410173105b0ba00102087f017e230041106b22012400101f10344103103f103c21024101103921034102103022001013200120033a000f0240106d105d45044020022001410f6a106f105421041033106d105310202108200420001068450d0120022001410f6a2207106f200420001049105c20022007106e200810582000102c200220032000106c200141106a24000f0b41a782c0004117102b000b419c83c0004125102b000ba10202067f027e230041406a22002400101f10341041104020004100360230200041306a103a21012000280230103e106d105d450440104c210310202106200020011042200041186a200041086a28020036020020002000290300370310200041306a410472210102400340200041206a200041106a106520002d002441034604402003104c10690d0241eb83c0004125102b000b200041386a2202200041286a2802003602002000200029032022073703302007a722042001106f1054210520042001106e200610582005200228020022021068044020032002104720042001106f200520021049105c200420002d00342002106c0c010b0b419c83c0004125102b000b1033106d10532003102c200041406b24000f0b41a782c0004117102b000b4501027f101f10344101103f41001036103d21000240106d105d0440106d21012000102f0d012001200010191a0f0b419084c000411b102b000b2001418881c0004104105a0b3001037f101f4102103f103c2101410110392102419a82c0004105102d2200200110021a2000200210522000105410220b3001037f101f4102103f103c2101410110392102419182c0004109102d2200200110021a2000200210522000105610230ba50502087f017e230041306b22002400101f410121044101103f103c21012000102e2205360220200141e883c000106f10542102200041206a2203200141e883c000106e10562002104d200141e983c000106f105421022003200141e983c000106e10562002104d200141ea83c000106f105421022003200141ea83c000106e10562002104d027f41b4d3c0002d000045044041b4d3c00041013a000041a085c0004100360200200041106a41a485c0004190ce004100106a2000280210200028021441dc84c0004100104f102e0c010b4100210441dc84c0004100102d0b2101200020043a001c200020013602182005101221064100210103402001410c6a220420064b450440200041286a41003602002000420037032020052001200041206a2203410c10451a200029022421082000200028022022014118742001410874418080fc07717220014108764180fe037120014118767272104a2201101222024118742002410874418080fc07717220024108764180fe037120024118767272360220200041186a20034104106620002d001c2102200041003a001c0240024002402002410171220204402001101222074190ce0041a085c00028020022036b4b0d02200041086a2003200320076a22031067200141002000280208200028020c10451a41a085c00020033602000c010b2000280218200110021a0b200020023a001c0c010b200041186a10502000280218200110021a20002d001c200020023a001c410171450d0041a085c000410036020041b4d3c00041003a00000b20002008370320200041186a200041206a41081066200421010c010b0b20002802182101200020002d001c3a002420002001360220200041206a1050200028022020002d0024044041a085c000410036020041b4d3c00041003a00000b10241a200041306a24000b4101027f101f4101103f103c2100104c2201200041e883c000106f105410472001200041e983c000106f105410472001200041ea83c000106f10541047200110220b2c01017f101f4100103f418282c000410f102d10532200102f450440200010241a0f0b418881c000410410250b0300010b0c00418c85c000410e1000000b0bb0050200418080c0000b9a05456e64706f696e742063616e206f6e6c792062652063616c6c6564206279206f776e6572617267756d656e74206465636f6465206572726f722028293a20746f6f2066657720617267756d656e7473746f6f206d616e7920617267756d656e747377726f6e67206e756d626572206f6620617267756d656e7473696e70757420746f6f206c6f6e6745474c44696e707574206f7574206f662072616e676573746f72616765206465636f6465206572726f723a20626164206172726179206c656e6774687661722061726773636c61696d4164646564636c61696d52656d6f766564636c61696d436f6c6c6563746564616c6c436c61696d73436f6c6c6563746564746f6b656e4964656e746966696572636c61696d44617465636c61696d697350617573656452657761726420746f6b656e206973206e6f742073657443616e206f6e6c79206164642064657369676e6174656420746f6b656e4d75737420616464206d6f7265207468616e203020746f6b656e73436c61696d73206164646564206d75737420657175616c207061796d656e7420616d6f756e7443616e6e6f742072656d6f7665206d6f7265207468616e2063757272656e7420636c61696d436f6e74726163742069732070617573656443616e6e6f7420636c61696d203020746f6b656e73000102436c61696d732072656d6f766564206d7573742062652067726561746572207468616e203052657761726420746f6b656e20697320616c726561647920736574636c61696d5f7479706561646472657373636c61696d73696e76616c69642076616c756500e8011000e9011000ea01100063616e6e6f74207375627472616374206265636175736520726573756c7420776f756c64206265206e6567617469766570616e6963206f6363757272656400419c85c0000b049cffffff@0500@0502", - "address": "erd1qqqqqqqqqqqqqpgqtywnp7z0war94rpzk00p2n2wjwaws2xr7yqsejxy7f" - } + "emittedTransaction": { + "nonce": 3889, + "value": "0", + "receiver": "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu", + "sender": "erd130qhxzu6lh29g6srnsa65pplxaf9sgsspczvljvxk624upwt7yqstswx9l", + "gasPrice": 1000000000, + "gasLimit": 150000000, + "data": "MDA2MTczNmQwMTAwMDAwMDAxNmUxMzYwMDAwMDYwMDE3ZjAxN2Y2MDAyN2Y3ZjAxN2Y2MDAyN2Y3ZjAwNjAwMTdmMDA2MDAwMDE3ZjYwMDM3ZjdmN2YwMDYwMDQ3ZjdmN2Y3ZjAwNjAwMzdmN2Y3ZjAxN2Y2MDA1N2Y3ZjdlN2Y3ZjAxN2Y2MDAyN2Y3ZTAwNjAwMTdmMDE3ZTYwMDE3ZTAwNjAwNDdmN2Y3ZjdmMDE3ZjYwMDAwMTdlNjAwNTdmN2Y3ZjdmN2YwMDYwMDM3ZjdlN2YwMDYwMDQ3ZjdlN2Y3ZjAwNjAwMjdmN2YwMTdlMDJhODA3MjcwMzY1NmU3NjBiNzM2OTY3NmU2MTZjNDU3MjcyNmY3MjAwMDMwMzY1NmU3NjBhNmQ0Mjc1NjY2NjY1NzI0ZTY1NzcwMDA1MDM2NTZlNzYwZDZkNDI3NTY2NjY2NTcyNDE3MDcwNjU2ZTY0MDAwMjAzNjU2ZTc2MDk2ZDQyNzU2NjY2NjU3MjQ1NzEwMDAyMDM2NTZlNzYxYjZkNjE2ZTYxNjc2NTY0NTQ3MjYxNmU3MzY2NjU3MjU2NjE2Yzc1NjU0NTc4NjU2Mzc1NzQ2NTAwMDkwMzY1NmU3NjA5NjI2OTY3NDk2ZTc0NDE2NDY0MDAwNjAzNjU2ZTc2MGE2MjY5Njc0OTZlNzQ1MzY5Njc2ZTAwMDEwMzY1NmU3NjEyNmQ0Mjc1NjY2NjY1NzI0MTcwNzA2NTZlNjQ0Mjc5NzQ2NTczMDAwODAzNjU2ZTc2MjI2ZDYxNmU2MTY3NjU2NDRkNzU2Yzc0Njk1NDcyNjE2ZTczNjY2NTcyNDU1MzQ0NTQ0ZTQ2NTQ0NTc4NjU2Mzc1NzQ2NTAwMDkwMzY1NmU3NjBkNmQ2MTZlNjE2NzY1NjQ0MzYxNmM2YzY1NzIwMDA0MDM2NTZlNzYxMzZkNjE2ZTYxNjc2NTY0NGY3NzZlNjU3MjQxNjQ2NDcyNjU3MzczMDAwNDAzNjU2ZTc2MTM2NzY1NzQ0ZTc1NmQ0NTUzNDQ1NDU0NzI2MTZlNzM2NjY1NzI3MzAwMDUwMzY1NmU3NjEyNjI2OTY3NDk2ZTc0NDc2NTc0NDM2MTZjNmM1NjYxNmM3NTY1MDAwNDAzNjU2ZTc2MTY2MjY5Njc0OTZlNzQ0NzY1NzQ0NTUzNDQ1NDQzNjE2YzZjNTY2MTZjNzU2NTAwMDQwMzY1NmU3NjEwNjc2NTc0NDU1MzQ0NTQ1NDZmNmI2NTZlNGU2MTZkNjUwMDAxMDM2NTZlNzYwZjZkNDI3NTY2NjY2NTcyNTM2NTc0NDI3OTc0NjU3MzAwMDgwMzY1NmU3NjEyNmQ0Mjc1NjY2NjY1NzI0NzY1NzQ0MTcyNjc3NTZkNjU2ZTc0MDAwMjAzNjU2ZTc2MTI2ZDYxNmU2MTY3NjU2NDUzNjk2NzZlNjE2YzQ1NzI3MjZmNzIwMDA0MDM2NTZlNzYxMDZkNDI3NTY2NjY2NTcyNDc2NTc0NGM2NTZlNjc3NDY4MDAwMTAzNjU2ZTc2MTk2MjY5Njc0OTZlNzQ0NzY1NzQ1NTZlNzM2OTY3NmU2NTY0NDE3MjY3NzU2ZDY1NmU3NDAwMDMwMzY1NmU3NjBmNjc2NTc0NGU3NTZkNDE3MjY3NzU2ZDY1NmU3NDczMDAwNTAzNjU2ZTc2MDk2MjY5Njc0OTZlNzQ1Mzc1NjIwMDA2MDM2NTZlNzYxOTZkNDI3NTY2NjY2NTcyNDY3MjZmNmQ0MjY5Njc0OTZlNzQ1NTZlNzM2OTY3NmU2NTY0MDAwMjAzNjU2ZTc2MTc2ZDQyNzU2NjY2NjU3MjU0NmY0MjY5Njc0OTZlNzQ1NTZlNzM2OTY3NmU2NTY0MDAwMjAzNjU2ZTc2MTI2ZDQyNzU2NjY2NjU3MjUzNzQ2ZjcyNjE2NzY1NGM2ZjYxNjQwMDAyMDM2NTZlNzYxMzZkNDI3NTY2NjY2NTcyNTM3NDZmNzI2MTY3NjU1Mzc0NmY3MjY1MDAwMjAzNjU2ZTc2MGU2MjY5Njc0OTZlNzQ1MzY1NzQ0OTZlNzQzNjM0MDAwYTAzNjU2ZTc2MDk2MjY5Njc0OTZlNzQ0MzZkNzAwMDAyMDM2NTZlNzYwZjZkNjE2ZTYxNjc2NTY0NTc3MjY5NzQ2NTRjNmY2NzAwMDMwMzY1NmU3NjExNjc2NTc0NDE3MjY3NzU2ZDY1NmU3NDRjNjU2ZTY3NzQ2ODAwMDEwMzY1NmU3NjFiNzM2ZDYxNmM2YzQ5NmU3NDQ3NjU3NDU1NmU3MzY5Njc2ZTY1NjQ0MTcyNjc3NTZkNjU2ZTc0MDAwYjAzNjU2ZTc2MGU2MzY4NjU2MzZiNGU2ZjUwNjE3OTZkNjU2ZTc0MDAwMDAzNjU2ZTc2MTE2NzY1NzQ0MjZjNmY2MzZiNTQ2OTZkNjU3Mzc0NjE2ZDcwMDAwZTAzNjU2ZTc2MTQ3MzZkNjE2YzZjNDk2ZTc0NDY2OTZlNjk3MzY4NTM2OTY3NmU2NTY0MDAwYzAzNjU2ZTc2MTQ2MjY5Njc0OTZlNzQ0NjY5NmU2OTczNjg1NTZlNzM2OTY3NmU2NTY0MDAwNDAzNjU2ZTc2MTY3MzZkNjE2YzZjNDk2ZTc0NDY2OTZlNjk3MzY4NTU2ZTczNjk2NzZlNjU2NDAwMGMwMzY1NmU3NjBkNmQ0Mjc1NjY2NjY1NzI0NjY5NmU2OTczNjgwMDAxMDM2NTZlNzYwNjY2Njk2ZTY5NzM2ODAwMDMwMzY1NmU3NjEzNmQ0Mjc1NjY2NjY1NzI0NzY1NzQ0Mjc5NzQ2NTUzNmM2OTYzNjUwMDBkMDM2MTYwMGYwMDAxMDIwMzA2MDIwNTAxMDUwNDA1MDUwMDA0MDEwNzAxMDgwMzAxMDUwMTA0MDQwMDAwMDMwMjAyMGQwMzAzMDIwMjAxMDEwNTEwMDcwNzA0MDEwMzAxMDEwMTBiMDMwMzExMDYwMzBhMDEwMTAzMDMwMjA2MTIwMTAzMDYwNjAyMDcwNjA2MDYwNTAyMDIwNTA0MDQwMDAzMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDUwMzAxMDAxMTA2MTkwMzdmMDE0MTgwODBjMDAwMGI3ZjAwNDFiMGQzYzAwMDBiN2YwMDQxYjBkM2MwMDAwYjA3ZmMwMTEzMDY2ZDY1NmQ2ZjcyNzkwMjAwMDQ2OTZlNjk3NDAwNzUwODYxNjQ2NDQzNmM2MTY5NmQwMDc2MDk2MTY0NjQ0MzZjNjE2OTZkNzMwMDc3MDU2MzZjNjE2OTZkMDA3ODA4Njk3MzUwNjE3NTczNjU2NDAwNzkwNTcwNjE3NTczNjUwMDdhMGI3MjY1NmQ2Zjc2NjU0MzZjNjE2OTZkMDA3YjBjNzI2NTZkNmY3NjY1NDM2YzYxNjk2ZDczMDA3YzBlNzM2NTc0NTI2NTc3NjE3MjY0NTQ2ZjZiNjU2ZTAwN2QwNzc1NmU3MDYxNzU3MzY1MDA3ZTA5NzY2OTY1Nzc0MzZjNjE2OTZkMDA3ZjEwNzY2OTY1Nzc0MzZjNjE2OTZkNDE2NDY0NDQ2MTc0NjUwMDgwMDExMTc2Njk2NTc3NDM2YzYxNjk2ZDU3Njk3NDY4NDQ2MTc0NjUwMDgxMDEwYTc2Njk2NTc3NDM2YzYxNjk2ZDczMDA4MjAxMTM3NjY5NjU3NzU0NmY2YjY1NmU0OTY0NjU2ZTc0Njk2NjY5NjU3MjAwODMwMTA4NjM2MTZjNmM0MjYxNjM2YjAwODQwMTBhNWY1ZjY0NjE3NDYxNWY2NTZlNjQwMzAxMGI1ZjVmNjg2NTYxNzA1ZjYyNjE3MzY1MDMwMjBhZTQyZjYwMmUwMDAyNDAyMDAxMjAwMjRkMDQ0MDIwMDIyMDA0NGQwZDAxMTAyODAwMGIxMDI4MDAwYjIwMDAyMDAyMjAwMTZiMzYwMjA0MjAwMDIwMDEyMDAzNmEzNjAyMDAwYjA2MDAxMDg2MDEwMDBiMGYwMTAxN2YxMDAxMjIwMTIwMDAxMDAyMWEyMDAxMGIwYjAwMjAwMDIwMDExMDAzNDEwMDRhMGIwOTAwMjAwMDIwMDExMDAwMDAwYmNiMDEwMTA1N2YyMzAwNDExMDZiMjIwMzI0MDA0MWQwODRjMDAwNDEwMDEwMmQyMTA1MTAyZTIxMDYwMjQwMjAwMTEwMmYwNDQwMjAwMDIwMDI0MjAwMjAwNTIwMDYxMDA0MWEwYzAxMGIxMDJlMjEwNzIwMDExMDI5MjEwNDEwMzAyMjAxMTAzMTIwMDEyMDAxMjAwMjEwMDUyMDAxMTAwNjA0NDAyMDA0MTAyZjFhMGIyMDAzNDIwMDM3MDIwNDIwMDMyMDAxNDEwODc0NDE4MDgwZmMwNzcxMjAwMTQxMTg3NDcyMjAwMTQxMDg3NjQxODBmZTAzNzEyMDAxNDExODc2NzI3MjM2MDIwYzIwMDMyMDA0NDEwODc0NDE4MDgwZmMwNzcxMjAwNDQxMTg3NDcyMjAwNDQxMDg3NjQxODBmZTAzNzEyMDA0NDExODc2NzI3MjM2MDIwMDIwMDcyMDAzNDExMDEwMDcxYTIwMDAyMDA3NDIwMDIwMDUyMDA2MTAwODFhMGIyMDAzNDExMDZhMjQwMDBiMTEwMTAxN2YxMDMwMjIwMjIwMDAyMDAxMTAwZjFhMjAwMjBiMTQwMTAxN2YxMDMwMjIwMDQxZDA4NGMwMDA0MTAwMTAwZjFhMjAwMDBiMDcwMDIwMDAxMDEyNDUwYjFiMDEwMTdmNDE5MDg1YzAwMDQxOTA4NWMwMDAyODAyMDA0MTAxNmIyMjAwMzYwMjAwMjAwMDBiMDgwMDIwMDA0MjAwMTAxYTBiMGMwMTAxN2YxMDMwMjIwMDEwMDkyMDAwMGIwYzAxMDE3ZjEwMzAyMjAwMTAwYTIwMDAwYjE2MDAxMDMzMTAzMjEwMmEwNDQwMGYwYjQxODA4MGMwMDA0MTI0MTAwMDAwMGJhOTAxMDEwMzdmMjMwMDQxMjA2YjIyMDEyNDAwMDI0MDAyNDAwMjQwMDI0MDEwMGI0NTA0NDA0MTc1MjEwMjQxNzUxMDBjMGMwMTBiNDE3MzIxMDI0MTczMTAwZDEwMGIwZDAxMGIxMDJlMjEwMzBjMDEwYjIwMDE0MTE4NmE0MjAwMzcwMzAwMjAwMTQxMTA2YTQyMDAzNzAzMDAyMDAxNDEwODZhNDIwMDM3MDMwMDIwMDE0MjAwMzcwMzAwMDI0MDIwMDExMDBlMjIwMjQ1MDQ0MDEwMzAyMjAzNDFkMDg0YzAwMDQxMDAxMDBmMWEwYzAxMGIyMDAyNDEyMTRmMGQwMjIwMDEyMDAyMTAyZDIxMDMwYjQxNzMyMTAyMGIyMDAwMjAwMzM2MDIwNDIwMDAyMDAyMzYwMjAwMjAwMTQxMjA2YTI0MDAwZjBiMTAyODAwMGIwZDAwMjAwMDEwMzAyMjAwMTAxMDFhMjAwMDBiMzAwMTAxN2Y0MWE0ODBjMDAwNDExNzEwMmQyMjA0MjAwMDIwMDExMDA3MWEyMDA0NDFiYjgwYzAwMDQxMDMxMDA3MWEyMDA0MjAwMjIwMDMxMDA3MWEyMDA0MTAxMTAwMGIzMjAxMDE3ZjEwMmUyMTAxMDM0MDIwMDAyODAyMDA0MWFjZDNjMDAwMjgwMjAwNGU0NTA0NDAyMDAxMjAwMDQxYWU4NGMwMDA0MTA2MTAzOTEwMzYxMDNhMGMwMTBiMGIyMDAxMGIzMTAxMDE3ZjIwMDAyODAyMDAyMjAzNDFhY2QzYzAwMDI4MDIwMDRlMDQ0MDIwMDEyMDAyNDFiZTgwYzAwMDQxMTExMDM3MDAwYjIwMDAyMDAzNDEwMTZhMzYwMjAwMjAwMzBiNDcwMTAxN2YyMzAwNDExMDZiMjIwMjI0MDAyMDAyMjAwMTQxMDg3NDQxODA4MGZjMDc3MTIwMDE0MTE4NzQ3MjIwMDE0MTA4NzY0MTgwZmUwMzcxMjAwMTQxMTg3NjcyNzIzNjAyMGMyMDAwMjAwMjQxMGM2YTQxMDQxMDA3MWEyMDAyNDExMDZhMjQwMDBiNTIwMTAxN2UyMDAwMTAxZDQ1MDQ0MDQxMDAwZjBiMDI0MDIwMDAxMDFlMjIwMTQyODAwMjU0MDQ0MDIwMDFhNzIyMDA0MWZmMDE3MTQxMDM0ZjBkMDEyMDAwMGYwYjQxOWQ4NGMwMDA0MTBhNDFmYTgwYzAwMDQxMGUxMDM3MDAwYjQxOWQ4NGMwMDA0MTBhNDFiNDg0YzAwMDQxMGQxMDM3MDAwYjI1MDEwMTdmNDEwMDEwMzYyMjAwMTAxMjQxMjA0NzA0NDA0MWE3ODRjMDAwNDEwNzQxYjQ4MWMwMDA0MTEwMTAzNzAwMGIyMDAwMGI1MTAxMDE3ZjIzMDA0MTEwNmIyMjAxMjQwMDAyNDAyMDAwMTAxMjQxMDQ0NzBkMDAyMDAxNDEwMDM2MDIwYzIwMDA0MTAwMjAwMTQxMGM2YTQxMDQxMDQ1MWEyMDAxMjgwMjBjNDFjNThlYjFhMjA0NDcwZDAwMjAwMDQxZDA4NGMwMDA0MTAwMTAwZjFhMGIyMDAxNDExMDZhMjQwMDIwMDAwYjFhMDAyMDAwNDFhY2QzYzAwMDI4MDIwMDQ4MDQ0MDQxY2Y4MGMwMDA0MTEyMTAwMDAwMGIwYjE1MDAxMDE0MjAwMDQ2MDQ0MDBmMGI0MWUxODBjMDAwNDExOTEwMDAwMDBiMWIwMDQxYWNkM2MwMDAyODAyMDA0MTAwNGUwNDQwMGYwYjQxYmU4MGMwMDA0MTExMTAwMDAwMGIwYzAwNDFhY2QzYzAwMDEwMTQzNjAyMDAwYjIyMDEwMTdmMjAwMTEwMTIyMTAyMjAwMDQxMDAzNjAyMDgyMDAwMjAwMTM2MDIwMDIwMDAyMDAyNDEwMjc2MzYwMjA0MGIwODAwMjAwMDIwMDExMDQ0MGIxNTAwNDE3ZjIwMDAyMDAxMTAxYjIyMDA0MTAwNDcyMDAwNDEwMDQ4MWIwYjBmMDAyMDAwMjAwMTIwMDMyMDAyMTAyNjQxMDA0NzBiMDkwMDIwMDAyMDAxMTAwMjFhMGIwYTAwMjAwMDIwMDAyMDAxMTAwNTBiMGMwMDIwMDAyMDAwMjAwMTEwMDUyMDAwMGIyMDAwMjAwMDIwMDAyMDAxMTAxNTIwMDAxMDA2NDEwMDQ4MDQ0MDQxZDA4NGMwMDA0MTMwMTAwMDAwMGIyMDAwMGIwZjAxMDE3ZjEwMzAyMjAxMjAwMDEwMTYxYTIwMDEwYjBkMDAyMDAwMTAzMDIyMDAxMDE3MWEyMDAwMGIwYzAxMDE3ZjEwMzAyMjAwMTAzMTIwMDAwYmZlMDEwMTAyN2YyMzAwNDEzMDZiMjIwMzI0MDAyMDAzNDEyMDZhNDEwMDM2MDIwMDIwMDM0MjAwMzcwMzE4MjAwMzIwMDI0MTA4NzQ0MTgwODBmYzA3NzEyMDAyNDExODc0NzIyMDAyNDEwODc2NDE4MGZlMDM3MTIwMDI0MTE4NzY3MjcyMzYwMjI4MjAwMzQxMTA2YTIwMDM0MTE4NmEyMjAyNDEwMDQxMDQxMDRlMjAwMzI4MDIxMDIwMDMyODAyMTQyMDAzNDEyODZhMjIwNDQxMDQxMDRmMjAwMzIwMDE0MjI4ODY0MjgwODA4MDgwODA4MGMwZmYwMDgzMjAwMTQyMzg4Njg0MjAwMTQyMTg4NjQyODA4MDgwODA4MGUwM2Y4MzIwMDE0MjA4ODY0MjgwODA4MDgwZjAxZjgzODQ4NDIwMDE0MjA4ODg0MjgwODA4MGY4MGY4MzIwMDE0MjE4ODg0MjgwODBmYzA3ODM4NDIwMDE0MjI4ODg0MjgwZmUwMzgzMjAwMTQyMzg4ODg0ODQ4NDM3MDMyODIwMDM0MTA4NmEyMDAyNDEwNDQxMGMxMDRlMjAwMzI4MDIwODIwMDMyODAyMGMyMDA0NDEwODEwNGYyMDAwMjgwMjAwMjAwMjQxMGMxMDA3MWEyMDAzNDEzMDZhMjQwMDBiM2IwMTAxN2YyMzAwNDExMDZiMjIwNDI0MDAyMDA0NDEwODZhMjAwMjIwMDMyMDAxNDEwYzEwMjcyMDA0MjgwMjBjMjEwMTIwMDAyMDA0MjgwMjA4MzYwMjAwMjAwMDIwMDEzNjAyMDQyMDA0NDExMDZhMjQwMDBiYjEwMjAxMDY3ZjIwMDEyMDAzNDYwNDQwMjAwMTIyMDM0MTBmNGIwNDQwMjAwMDQxMDAyMDAwNmI0MTAzNzEyMjA1NmEyMTA0MjAwNTA0NDAyMDAyMjEwMTAzNDAyMDAwMjAwMTJkMDAwMDNhMDAwMDIwMDE0MTAxNmEyMTAxMjAwMDQxMDE2YTIyMDAyMDA0NDkwZDAwMGIwYjIwMDQyMDAzMjAwNTZiMjIwMzQxN2M3MTIyMDY2YTIxMDAwMjQwMjAwMjIwMDU2YTIyMDU0MTAzNzEyMjAxMDQ0MDIwMDY0MTAwNGMwZDAxMjAwNTQxN2M3MTIyMDc0MTA0NmEyMTAyNDEwMDIwMDE0MTAzNzQyMjA4NmI0MTE4NzEyMTA5MjAwNzI4MDIwMDIxMDEwMzQwMjAwNDIwMDEyMDA4NzYyMDAyMjgwMjAwMjIwMTIwMDk3NDcyMzYwMjAwMjAwMjQxMDQ2YTIxMDIyMDA0NDEwNDZhMjIwNDIwMDA0OTBkMDAwYjBjMDEwYjIwMDY0MTAwNGMwZDAwMjAwNTIxMDIwMzQwMjAwNDIwMDIyODAyMDAzNjAyMDAyMDAyNDEwNDZhMjEwMjIwMDQ0MTA0NmEyMjA0MjAwMDQ5MGQwMDBiMGIyMDAzNDEwMzcxMjEwMzIwMDUyMDA2NmEyMTAyMGIyMDAzMDQ0MDIwMDAyMDAzNmEyMTAxMDM0MDIwMDAyMDAyMmQwMDAwM2EwMDAwMjAwMjQxMDE2YTIxMDIyMDAwNDEwMTZhMjIwMDIwMDE0OTBkMDAwYjBiMGYwYjEwODYwMTAwMGI1NTAxMDE3ZjIwMDAyZDAwMDQyMTAxMjAwMDQxMDAzYTAwMDQwMjQwMjAwMTQxMDE3MTA0NDA0MTk0ODVjMDAwMjgwMjAwMjIwMTQxOTFjZTAwNGYwZDAxMjAwMDI4MDIwMDQxOTg4NWMwMDAyMDAxMTAwNzFhNDE5NDg1YzAwMDQxMDAzNjAyMDA0MWE4ZDNjMDAwNDEwMDNhMDAwMDBiMGYwYjEwMjgwMDBiMGQwMDIwMDAxMDMwMjIwMDEwMTgxYTIwMDAwYjNhMDEwMTdmMjMwMDQxMTA2YjIyMDIyNDAwMjAwMjIwMDE0MWZmMDE3MTQxMDI3NDQxYzQ4NGMwMDA2YTI4MDIwMDJkMDAwMDNhMDAwZjIwMDAyMDAyNDEwZjZhNDEwMTEwMDcxYTIwMDI0MTEwNmEyNDAwMGIwODAwMjAwMDEwNTExMDRiMGIwODAwMjAwMDEwNTExMDNkMGIyZjAxMDE3ZTAyN2YwMjQwMjAwMDEwNTYyMjAxNDIwMTU4MDQ0MDQxMDAyMDAxYTc0MTAxNmIwZDAyMWEwYzAxMGI0MThjODFjMDAwNDExMjEwNTcwMDBiNDEwMTBiMGI1ZDAyMDI3ZjAxN2UyMzAwNDExMDZiMjIwMTI0MDAyMDAxNDIwMDM3MDMwODIwMDAxMDUxMjIwMDEwMTIyMjAyNDEwOTRmMDQ0MDQxZmE4MGMwMDA0MTBlMTA1NzAwMGIyMDAxMjAwMTQxMDg2YTIwMDIxMDYyMjAwMDQxMDAyMDAxMjgwMjAwMjIwMDIwMDEyODAyMDQyMjAyMTA0NTFhMjAwMDIwMDIxMDYzMjAwMTQxMTA2YTI0MDAwYjFiMDEwMTdmNDE5ZTgxYzAwMDQxMTYxMDJkMjIwMjIwMDAyMDAxMTAwNzFhMjAwMjEwMTEwMDBiMzcwMTAxN2YyMzAwNDExMDZiMjIwMjI0MDAyMDAyNDIwMDM3MDMwODIwMDIyMDAxYWQ0MTAxMjAwMjQxMDg2YTEwNTkyMDAwMjAwMjI4MDIwMDIwMDIyODAyMDQxMDVhMjAwMjQxMTA2YTI0MDAwYjljMDIwMjA0N2YwMTdlMjAwMzIwMDE0MjI4ODY0MjgwODA4MDgwODA4MGMwZmYwMDgzMjAwMTQyMzg4Njg0MjAwMTQyMTg4NjQyODA4MDgwODA4MGUwM2Y4MzIwMDE0MjA4ODY0MjgwODA4MDgwZjAxZjgzODQ4NDIwMDE0MjA4ODg0MjgwODA4MGY4MGY4MzIwMDE0MjE4ODg0MjgwODBmYzA3ODM4NDIwMDE0MjI4ODg0MjgwZmUwMzgzMjAwMTQyMzg4ODg0ODQ4NDIyMDgzNzAwMDAwMjQwMDI0MDIwMDAwMjdmMjAwMTUwMDQ0MDQxZDA4NGMwMDAyMTAzNDEwMDBjMDEwYjIwMDI0MTAwMjAwMTQyN2Y1MTFiNDUwNDQwNDEwMDIwMDIyMDA4NDI4MDAxODM0MjA3ODhhNzcxMjIwNTZiNDFmZjAxNzEyMTA2MDM0MDIwMDQ0MTA4NDYwZDAzMjAwNjIwMDMyMDA0NmEyZDAwMDAyMjA3NDcwNDQwMjAwNDIwMDIyMDA3NDEwNzc2MjAwNTQ3NzEyMjAyNmI0MTA5NGYwZDA1MjAwMzQxMDAyMDAyNmIyMjAyNmEyMDA0NmEyMTAzNDEwODIwMDIyMDA0NmE2YjBjMDMwNTIwMDQ0MTAxNmEyMTA0MGMwMTBiMDAwYjAwMGIyMDAzNDEwNzZhMjEwMzQxMDEwYjM2MDIwNDIwMDAyMDAzMzYwMjAwMGYwYjEwODUwMTAwMGIxMDg1MDEwMDBiMGQwMDIwMDAyMDAxMjAwMjEwMmQxMDE5MWEwYjBiMDAyMDAwMjAwMTEwNGExMDE5MWEwYjM2MDEwMTdmMjMwMDQxMTA2YjIyMDIyNDAwMjAwMjQyMDAzNzAzMDgyMDAyMjAwMTQxMDAyMDAyNDEwODZhMTA1OTIwMDAyMDAyMjgwMjAwMjAwMjI4MDIwNDEwNWEyMDAyNDExMDZhMjQwMDBiMGUwMDIwMDA0MTY3MTAxODFhNDE2NzEwMTI0NTBiMDkwMDEwMmUxYTIwMDAxMDRhMGIwZDAwMTAyZTFhMjAwMDIwMDExMDI5MTAzYTBiNWEwMjAyN2YwMTdlMjMwMDQxMTA2YjIyMDIyNDAwMTAyZTIxMDMyMDAxNDFmZjAxNzE0MTAyNzQ0MWM0ODRjMDAwNmEyODAyMDAzMTAwMDAyMTA0MjAwMjQyMDAzNzAzMDgyMDAyMjAwNDQxMDAyMDAyNDEwODZhMTA1OTIwMDMyMDAyMjgwMjAwMjAwMjI4MDIwNDEwMGYxYTIwMDAyMDAzMTAzYTIwMDI0MTEwNmEyNDAwMGIxMjAxMDE3ZjEwMmUyMjAyMjAwMDIwMDExMDJkMTAzYTIwMDIwYjM5MDEwMTdmMjMwMDQxMTA2YjIyMDMyNDAwMjAwMzQxMDg2YTIwMDE0MTA4MjAwMjEwNjkyMDAzMjgwMjBjMjEwMTIwMDAyMDAzMjgwMjA4MzYwMjAwMjAwMDIwMDEzNjAyMDQyMDAzNDExMDZhMjQwMDBiMzQwMTAxN2UwMjQwMjAwMTQ1MGQwMDAzNDAyMDAxNDUwZDAxMjAwMTQxMDE2YjIxMDEyMDAwMzEwMDAwMjAwMjQyMDg4Njg0MjEwMjIwMDA0MTAxNmEyMTAwMGMwMDBiMDAwYjIwMDIwYjgwMDEwMTAzN2YyMzAwNDExMDZiMjIwMTI0MDAyMDAwMjgwMjA4MjEwMzIwMDE0MTAwMzYwMjBjMjAwMDI4MDIwMDIwMDM0MTAyNzQyMDAxNDEwYzZhNDEwNDEwNDU0NTA0NDAyMDAxMjgwMjBjMjEwMjIwMDAyMDAzNDEwMTZhMzYwMjA4MjAwMjQxMDg3NDQxODA4MGZjMDc3MTIwMDI0MTE4NzQ3MjIwMDI0MTA4NzY0MTgwZmUwMzcxMjAwMjQxMTg3NjcyNzIxMDI5MjAwMTQxMTA2YTI0MDAwZjBiNDFjNDgxYzAwMDQxMDg0MWJlODBjMDAwNDExMTEwMzcwMDBiZmIwMTAyMDQ3ZjAxN2UyMzAwNDExMDZiMjIwMzI0MDAwMjQwMDI0MDAyNDAwMjQwMjAwMTI4MDIwODIwMDEyODAyMDQ0ZjA0NDAyMDAwNDEwMzNhMDAwNDBjMDEwYjIwMDExMDY0MTAyOTIyMDUxMDEyNDEyMDQ3MGQwMTAyNDAyMDAxMTA2NDIyMDIxMDEyNDUwNDQwNDEwMDIxMDIwYzAxMGIyMDAzNDIwMDM3MDMwODIwMDIxMDEyMjIwNDQxMDk0ZjBkMDQyMDAzMjAwMzQxMDg2YTIwMDQxMDYyMjAwMjQxMDAyMDAzMjgwMjAwMjIwMjIwMDMyODAyMDQyMjA0MTA0NTFhMjAwMjIwMDQxMDYzMjIwNjQyODAwMjVhMGQwNDIwMDZhNzIyMDI0MWZmMDE3MTQxMDM0ZjBkMDMwYjIwMDA0MTA4NmEyMDAxMTA2NDEwNGIzNjAyMDAyMDAwMjAwMjNhMDAwNDIwMDAyMDA1MzYwMjAwMGIyMDAzNDExMDZhMjQwMDBmMGI0MWM0ODFjMDAwNDEwODQxYjQ4MWMwMDA0MTEwMTAzNzAwMGI0MWM0ODFjMDAwNDEwODQxYjQ4NGMwMDA0MTBkMTAzNzAwMGI0MWM0ODFjMDAwNDEwODQxZmE4MGMwMDA0MTBlMTAzNzAwMGI4MDAxMDEwMjdmMjMwMDQxMTA2YjIyMDMyNDAwMDI0MDAyNDAyMDAwMmQwMDA0MDQ0MDQxOTBjZTAwNDE5NDg1YzAwMDI4MDIwMDIyMDQ2YjIwMDI0OTBkMDEyMDAzNDEwODZhMjAwNDIwMDIyMDA0NmEyMjAwMTA2NzIwMDMyODAyMDgyMDAzMjgwMjBjMjAwMTIwMDIxMDRmNDE5NDg1YzAwMDIwMDAzNjAyMDAwYzAyMGIyMDAwMjgwMjAwMjAwMTIwMDIxMDA3MWEwYzAxMGIyMDAwMTA1MDIwMDAyODAyMDAyMDAxMjAwMjEwMDcxYTBiMjAwMzQxMTA2YTI0MDAwYjQwMDEwMTdmMjMwMDQxMTA2YjIyMDMyNDAwMjAwMzQxMDg2YTIwMDEyMDAyNDE5ODg1YzAwMDQxOTBjZTAwMTAyNzIwMDMyODAyMGMyMTAxMjAwMDIwMDMyODAyMDgzNjAyMDAyMDAwMjAwMTM2MDIwNDIwMDM0MTEwNmEyNDAwMGIwZjAwMjAwMDIwMDExMDQzNDFmZjAxNzE0MTAxNDYwYjNiMDEwMTdmMjMwMDQxMTA2YjIyMDQyNDAwMjAwNDQxMDg2YTQxMDAyMDAzMjAwMTIwMDIxMDI3MjAwNDI4MDIwYzIxMDEyMDAwMjAwNDI4MDIwODM2MDIwMDIwMDAyMDAxMzYwMjA0MjAwNDQxMTA2YTI0MDAwYjIxMDEwMTdmNDFjYzgxYzAwMDQxMGExMDYxMjIwMzIwMDAxMDVmMjAwMzIwMDExMDYwMjAwMzIwMDIxMDVlMTAxYzBiMjEwMTAxN2Y0MWQ2ODFjMDAwNDEwYzEwNjEyMjAzMjAwMDEwNWYyMDAzMjAwMTEwNjAyMDAzMjAwMjEwNWUxMDFjMGIyMTAxMDE3ZjQxZTI4MWMwMDA0MTBlMTA2MTIyMDMyMDAwMTA1ZjIwMDMyMDAxMTA2MDIwMDMyMDAyMTA1ZTEwMWMwYjBiMDA0MWYwODFjMDAwNDEwZjEwMmQwYjFlMDEwMTdmNDFmZjgxYzAwMDQxMDkxMDJkMjIwMjIwMDAxMDQ2MjAwMjIwMDEyZDAwMDAxMDUyMjAwMjBiMWUwMTAxN2Y0MTg4ODJjMDAwNDEwNTEwMmQyMjAyMjAwMDEwNDYyMDAyMjAwMTJkMDAwMDEwNTIyMDAyMGIwYjAwNDE4ZDgyYzAwMDQxMDgxMDJkMGIxNjAwMjAwMDEwNGMxMDY4MDQ0MDBmMGI0MTk1ODJjMDAwNDEyMzEwMmIwMDBiMTgwMDIwMDAxMDZkMTA1NDEwMmEwNDQwMGYwYjQxYjg4MmMwMDA0MTFkMTAyYjAwMGIxNTAwMTA2ZDEwNWQ0NTA0NDAwZjBiNDFkNTgyYzAwMDQxMTcxMDJiMDAwYjFkMDAyMDAwMjAwMTEwNDM0MWZmMDE3MTQxMDE0ZDA0NDAwZjBiNDFlYzgyYzAwMDQxMjUxMDJiMDAwYjBlMDAxMDFmNDEwMDEwM2YxMDcwNDEwMTEwNTgwYjc2MDIwNzdmMDE3ZTIzMDA0MTEwNmIyMjAwMjQwMDEwMzQ0MTAyMTAzZjEwM2MyMTAxMjAwMDQxMDExMDNiMjIwNDNhMDAwZjEwNzMyMDAwMTAzNTIwMDAyODAyMDAyMTAyMjAwMDI4MDIwNDIwMDEyMDAwNDEwZjZhMjIwMzEwNmYxMDUzMjEwNjEwMjAyMTA3MTA3MjIwMDIxMDcxMjAwMTIwMDMxMDZmMjAwNjIwMDIxMDQ4MTA1YjIwMDEyMDAzMTA2ZTIwMDcxMDVjMjAwMTIwMDQyMDAyMTA2YTIwMDA0MTEwNmEyNDAwMGI5NjAyMDIwNzdmMDI3ZTIzMDA0MWQwMDA2YjIyMDAyNDAwMTAzNDEwNDExMDQwMjAwMDQxMDAzNjAyNDAyMDAwNDE0MDZiMjIwMTEwMzgyMTAyMjAwMDI4MDI0MDEwM2UxMDczMjAwMDQxMDg2YTEwMzUyMDAwMjgwMjA4MjEwNDIwMDAyODAyMGMxMDIwMjEwNzEwNzIyMDA0MTA3MTEwNGMyMTA1MjAwMDQxMTA2YTIwMDIxMDQyMjAwMDQxMjg2YTIwMDA0MTE4NmEyODAyMDAzNjAyMDAyMDAwMjAwMDI5MDMxMDM3MDMyMDIwMDE0MTA0NzIyMTAyMDM0MDAyNDAyMDAwNDEzMDZhMjAwMDQxMjA2YTEwNjUyMDAwMmQwMDM0NDEwMzQ2MDQ0MDIwMDUyMDA0MTA0NDQxZmYwMTcxMGQwMTIwMDA0MWQwMDA2YTI0MDAwZjA1MjAwMDQxYzgwMDZhMjIwMTIwMDA0MTM4NmEyODAyMDAzNjAyMDAyMDAwMjAwMDI5MDMzMDIyMDgzNzAzNDAyMDA4YTcyMjAzMjAwMjEwNmYxMDUzMjEwNjIwMDMyMDAyMTA2ZjIwMDYyMDAxMjgwMjAwMjIwMTEwNDgxMDViMjAwMzIwMDIxMDZlMjAwNzEwNWMyMDA1MjAwMTEwNDcyMDAzMjAwMDJkMDA0NDIwMDExMDZhMGMwMjBiMDAwYjBiNDE5MTgzYzAwMDQxMjYxMDJiMDAwYmNlMDIwMTA1N2YyMzAwNDExMDZiMjIwMjI0MDAxMDFmMTA0MTEwNDAyMDAyNDEwMDM2MDIwODIwMDI0MTA4NmEyMjAwMjgwMjAwNDFhY2QzYzAwMDI4MDIwMDQ4MDQ3ZjIwMDA0MTlkODRjMDAwNDEwYTEwMzkxMDNiMDU0MTAzMGIyMTAxMjAwMjI4MDIwODEwM2UwMjQwMDI0MDAyNDAxMDcwMTA1NTQ1MDQ0MDEwNzMxMDZkMTA1NDIxMDQxMDMyMjEwMDEwNGMyMTAzMjAwMTQxZmYwMTcxNDEwMzQ3MDQ0MDIwMDIyMDAxM2EwMDBmMjAwMDIwMDI0MTBmNmExMDZmMTA1MzIyMDMxMDcxMjAwMDIwMDI0MTBmNmExMDZmMTA0YzEwNWIyMDAwMjAwMTIwMDMxMDZjMGMwNDBiMjAwMDQxYzk4M2MwMDAxMDZmMTA1MzIyMDExMDRjMTA2ODBkMDEwYzAyMGI0MWI3ODNjMDAwNDExMjEwMmIwMDBiMjAwMzIwMDExMDQ3MjAwMDQxMDAyMDAxMTA2YzIwMDA0MWM5ODNjMDAwMTA2ZjEwNGMxMDViMGIyMDAwNDFjYTgzYzAwMDEwNmYxMDUzMjIwMTEwNGMxMDY4MDQ0MDIwMDMyMDAxMTA0NzIwMDA0MTAxMjAwMTEwNmMyMDAwNDFjYTgzYzAwMDEwNmYxMDRjMTA1YjBiMjAwMDQxY2I4M2MwMDAxMDZmMTA1MzIyMDExMDRjMTA2ODA0NDAyMDAzMjAwMTEwNDcyMDAwNDEwMjIwMDExMDZjMjAwMDQxY2I4M2MwMDAxMDZmMTA0YzEwNWIwYjIwMDMxMDcxMGIyMDAwMjAwNDIwMDMxMDJjMjAwMjQxMTA2YTI0MDAwYjE2MDAxMDFmNDEwMDEwM2Y0MThkODJjMDAwNDEwODEwMmQxMDU1YWQxMDIxMGIyMTAwMTAxZjEwMzQ0MTAwMTAzZjEwNzAxMDU1MDQ0MDQxZTc4M2MwMDA0MTFhMTAyYjAwMGIxMDcwNDEwMTEwNTgwYjgwMDEwMjA4N2YwMTdlMjMwMDQxMTA2YjIyMDIyNDAwMTAxZjEwMzQ0MTAzMTAzZjEwM2MyMTAxNDEwMTEwM2IyMTAzNDEwMjEwMzAyMjAwMTAxMzIwMDIyMDAzM2EwMDBmMTA3MzIwMDEyMDAyNDEwZjZhMjIwNDEwNmYxMDUzMjEwNTEwMzMxMDZkMTA1NDEwMjAyMTA4MjAwMDEwNzEyMDA1MjAwMDEwNzQyMDAxMjAwNDEwNmYyMDA1MjAwMDEwNDkxMDViMjAwMTIwMDQxMDZlMjAwODEwNWMyMDAxMjAwMzIwMDAxMDZiMjAwMDEwMmMyMDAyNDExMDZhMjQwMDBiZjIwMTAyMDY3ZjAyN2UyMzAwNDE0MDZhMjIwMDI0MDAxMDFmMTAzNDEwNDExMDQwMjAwMDQxMDAzNjAyMzAyMDAwNDEzMDZhMjIwMjEwMzgyMTAxMjAwMDI4MDIzMDEwM2UxMDczMTA0YzIxMDQxMDIwMjEwNjIwMDAyMDAxMTA0MjIwMDA0MTE4NmEyMDAwNDEwODZhMjgwMjAwMzYwMjAwMjAwMDIwMDAyOTAzMDAzNzAzMTAyMDAyNDEwNDcyMjEwMjAzNDAyMDAwNDEyMDZhMjAwMDQxMTA2YTEwNjUyMDAwMmQwMDI0NDEwMzQ2NDUwNDQwMjAwMDQxMzg2YTIyMDEyMDAwNDEyODZhMjgwMjAwMzYwMjAwMjAwMDIwMDAyOTAzMjAyMjA3MzcwMzMwMjAwN2E3MjIwMzIwMDIxMDZmMTA1MzIxMDUyMDAzMjAwMjEwNmUyMDA2MTA1YzIwMDEyODAyMDAyMjAxMTA3MTIwMDUyMDAxMTA3NDIwMDQyMDAxMTA0NzIwMDMyMDAyMTA2ZjIwMDUyMDAxMTA0OTEwNWIyMDAzMjAwMDJkMDAzNDIwMDExMDZiMGMwMTBiMGIxMDMzMTA2ZDEwNTQyMDA0MTAyYzIwMDA0MTQwNmIyNDAwMGI0NTAxMDI3ZjEwMWYxMDM0NDEwMTEwM2Y0MTAwMTAzNjEwM2QyMTAwMDI0MDEwNmQxMDVkMDQ0MDEwNmQyMTAxMjAwMDEwMmYwZDAxMjAwMTIwMDAxMDE5MWEwZjBiNDFjYzgzYzAwMDQxMWIxMDJiMDAwYjIwMDE0MTg4ODFjMDAwNDEwNDEwNWEwYjIyMDAxMDFmMTAzNDQxMDAxMDNmMTA3MDEwNTU0NTA0NDA0MTgxODRjMDAwNDExYzEwMmIwMDBiMTA3MDQxMDAxMDU4MGIzMDAxMDM3ZjEwMWY0MTAyMTAzZjEwM2MyMTAxNDEwMTEwM2IyMTAyNDE4ODgyYzAwMDQxMDUxMDJkMjIwMDIwMDExMDAyMWEyMDAwMjAwMjEwNTIyMDAwMTA1MzEwMjIwYjMwMDEwMzdmMTAxZjQxMDIxMDNmMTAzYzIxMDE0MTAxMTAzYjIxMDI0MWZmODFjMDAwNDEwOTEwMmQyMjAwMjAwMTEwMDIxYTIwMDAyMDAyMTA1MjIwMDAxMDU2MTAyMzBiYTUwNTAyMDg3ZjAxN2UyMzAwNDEzMDZiMjIwMDI0MDAxMDFmNDEwMTIxMDQ0MTAxMTAzZjEwM2MyMTAxMjAwMDEwMmUyMjA1MzYwMjIwMjAwMTQxYzk4M2MwMDAxMDZmMTA1MzIxMDIyMDAwNDEyMDZhMjIwMzIwMDE0MWM5ODNjMDAwMTA2ZTEwNTYyMDAyMTA0ZDIwMDE0MWNhODNjMDAwMTA2ZjEwNTMyMTAyMjAwMzIwMDE0MWNhODNjMDAwMTA2ZTEwNTYyMDAyMTA0ZDIwMDE0MWNiODNjMDAwMTA2ZjEwNTMyMTAyMjAwMzIwMDE0MWNiODNjMDAwMTA2ZTEwNTYyMDAyMTA0ZDAyN2Y0MWE4ZDNjMDAwMmQwMDAwNDUwNDQwNDFhOGQzYzAwMDQxMDEzYTAwMDA0MTk0ODVjMDAwNDEwMDM2MDIwMDIwMDA0MTEwNmE0MTk4ODVjMDAwNDE5MGNlMDA0MTAwMTA2OTIwMDAyODAyMTAyMDAwMjgwMjE0NDFkMDg0YzAwMDQxMDAxMDRmMTAyZTBjMDEwYjQxMDAyMTA0NDFkMDg0YzAwMDQxMDAxMDJkMGIyMTAxMjAwMDIwMDQzYTAwMWMyMDAwMjAwMTM2MDIxODIwMDUxMDEyMjEwNjQxMDAyMTAxMDM0MDIwMDE0MTBjNmEyMjA0MjAwNjRiNDUwNDQwMjAwMDQxMjg2YTQxMDAzNjAyMDAyMDAwNDIwMDM3MDMyMDIwMDUyMDAxMjAwMDQxMjA2YTIyMDM0MTBjMTA0NTFhMjAwMDI5MDIyNDIxMDgyMDAwMjAwMDI4MDIyMDIyMDE0MTE4NzQyMDAxNDEwODc0NDE4MDgwZmMwNzcxNzIyMDAxNDEwODc2NDE4MGZlMDM3MTIwMDE0MTE4NzY3MjcyMTA0YTIyMDExMDEyMjIwMjQxMTg3NDIwMDI0MTA4NzQ0MTgwODBmYzA3NzE3MjIwMDI0MTA4NzY0MTgwZmUwMzcxMjAwMjQxMTg3NjcyNzIzNjAyMjAyMDAwNDExODZhMjAwMzQxMDQxMDY2MjAwMDJkMDAxYzIxMDIyMDAwNDEwMDNhMDAxYzAyNDAwMjQwMDI0MDIwMDI0MTAxNzEyMjAyMDQ0MDIwMDExMDEyMjIwNzQxOTBjZTAwNDE5NDg1YzAwMDI4MDIwMDIyMDM2YjRiMGQwMjIwMDA0MTA4NmEyMDAzMjAwMzIwMDc2YTIyMDMxMDY3MjAwMTQxMDAyMDAwMjgwMjA4MjAwMDI4MDIwYzEwNDUxYTQxOTQ4NWMwMDAyMDAzMzYwMjAwMGMwMTBiMjAwMDI4MDIxODIwMDExMDAyMWEwYjIwMDAyMDAyM2EwMDFjMGMwMTBiMjAwMDQxMTg2YTEwNTAyMDAwMjgwMjE4MjAwMTEwMDIxYTIwMDAyZDAwMWMyMDAwMjAwMjNhMDAxYzQxMDE3MTQ1MGQwMDQxOTQ4NWMwMDA0MTAwMzYwMjAwNDFhOGQzYzAwMDQxMDAzYTAwMDAwYjIwMDAyMDA4MzcwMzIwMjAwMDQxMTg2YTIwMDA0MTIwNmE0MTA4MTA2NjIwMDQyMTAxMGMwMTBiMGIyMDAwMjgwMjE4MjEwMTIwMDAyMDAwMmQwMDFjM2EwMDI0MjAwMDIwMDEzNjAyMjAyMDAwNDEyMDZhMTA1MDIwMDAyODAyMjAyMDAwMmQwMDI0MDQ0MDQxOTQ4NWMwMDA0MTAwMzYwMjAwNDFhOGQzYzAwMDQxMDAzYTAwMDAwYjEwMjQxYTIwMDA0MTMwNmEyNDAwMGI0MTAxMDI3ZjEwMWY0MTAxMTAzZjEwM2MyMTAwMTA0YzIyMDEyMDAwNDFjOTgzYzAwMDEwNmYxMDUzMTA0NzIwMDEyMDAwNDFjYTgzYzAwMDEwNmYxMDUzMTA0NzIwMDEyMDAwNDFjYjgzYzAwMDEwNmYxMDUzMTA0NzIwMDExMDIyMGIyYzAxMDE3ZjEwMWY0MTAwMTAzZjQxZjA4MWMwMDA0MTBmMTAyZDEwNTQyMjAwMTAyZjQ1MDQ0MDIwMDAxMDI0MWEwZjBiNDE4ODgxYzAwMDQxMDQxMDI1MGIwMzAwMDEwYjBjMDA0MTgwODVjMDAwNDEwZTEwMDAwMDBiMDYwMDEwODUwMTAwMGIwYmE0MDUwMjAwNDE4MDgwYzAwMDBiOGUwNTQ1NmU2NDcwNmY2OTZlNzQyMDYzNjE2ZTIwNmY2ZTZjNzkyMDYyNjUyMDYzNjE2YzZjNjU2NDIwNjI3OTIwNmY3NzZlNjU3MjYxNzI2Nzc1NmQ2NTZlNzQyMDY0NjU2MzZmNjQ2NTIwNjU3MjcyNmY3MjIwMjgyOTNhMjA3NDZmNmYyMDY2NjU3NzIwNjE3MjY3NzU2ZDY1NmU3NDczNzQ2ZjZmMjA2ZDYxNmU3OTIwNjE3MjY3NzU2ZDY1NmU3NDczNzc3MjZmNmU2NzIwNmU3NTZkNjI2NTcyMjA2ZjY2MjA2MTcyNjc3NTZkNjU2ZTc0NzM2OTZlNzA3NTc0MjA3NDZmNmYyMDZjNmY2ZTY3NDU0NzRjNDQ2OTZlNzA3NTc0MjA2Zjc1NzQyMDZmNjYyMDcyNjE2ZTY3NjU3Mzc0NmY3MjYxNjc2NTIwNjQ2NTYzNmY2NDY1MjA2NTcyNzI2ZjcyM2EyMDYyNjE2NDIwNjE3MjcyNjE3OTIwNmM2NTZlNjc3NDY4NzY2MTcyMjA2MTcyNjc3MzYzNmM2MTY5NmQ0MTY0NjQ2NTY0NjM2YzYxNjk2ZDUyNjU2ZDZmNzY2NTY0NjM2YzYxNjk2ZDQzNmY2YzZjNjU2Mzc0NjU2NDc0NmY2YjY1NmU0OTY0NjU2ZTc0Njk2NjY5NjU3MjYzNmM2MTY5NmQ0NDYxNzQ2NTYzNmM2MTY5NmQ2OTczNTA2MTc1NzM2NTY0NGY3MDY1NzI2MTc0Njk2ZjZlNzMyMDZkNzU3Mzc0MjA2ODYxNzY2NTIwNmU2ZjZlMmQ3YTY1NzI2ZjIwNzY2MTZjNzU2NTQzNjE2ZTIwNmY2ZTZjNzkyMDYxNjQ2NDIwNjQ2NTczNjk2NzZlNjE3NDY1NjQyMDc0NmY2YjY1NmU1MjY1Nzc2MTcyNjQyMDc0NmY2YjY1NmUyMDY5NzMyMDZlNmY3NDIwNzM2NTc0NDM2MTZlNmU2Zjc0MjA3MjY1NmQ2Zjc2NjUyMDZkNmY3MjY1MjA3NDY4NjE2ZTIwNjM3NTcyNzI2NTZlNzQyMDYzNmM2MTY5NmQ0MzZjNjE2OTZkNzMyMDYxNjQ2NDY1NjQyMDZkNzU3Mzc0MjA2NTcxNzU2MTZjMjA3MDYxNzk2ZDY1NmU3NDIwNjE2ZDZmNzU2ZTc0NDM2ZjZlNzQ3MjYxNjM3NDIwNjk3MzIwNzA2MTc1NzM2NTY0MDAwMTAyNTI2NTc3NjE3MjY0MjA3NDZmNmI2NTZlMjA2OTczMjA2MTZjNzI2NTYxNjQ3OTIwNzM2NTc0NDM2ZjZlNzQ3MjYxNjM3NDIwNjk3MzIwNjE2YzcyNjU2MTY0NzkyMDcwNjE3NTczNjU2NDQzNmY2ZTc0NzI2MTYzNzQyMDY5NzMyMDYxNmM3MjY1NjE2NDc5MjA3NTZlNzA2MTc1NzM2NTY0NjM2YzYxNjk2ZDVmNzQ3OTcwNjU2MTY0NjQ3MjY1NzM3MzYzNmM2MTY5NmQ3MzY5NmU3NjYxNmM2OTY0MjA3NjYxNmM3NTY1MDAwMDAwYzkwMTEwMDBjYTAxMTAwMGNiMDExMDAwNjM2MTZlNmU2Zjc0MjA3Mzc1NjI3NDcyNjE2Mzc0MjA2MjY1NjM2MTc1NzM2NTIwNzI2NTczNzU2Yzc0MjA3NzZmNzU2YzY0MjA2MjY1MjA2ZTY1Njc2MTc0Njk3NjY1NzA2MTZlNjk2MzIwNmY2MzYzNzU3MjcyNjU2NDAwNDE5MDg1YzAwMDBiMDQ5Y2ZmZmZmZkAwNTAwQDA1MDI=", + "chainID": "D", + "version": 1, + "signature": "614598914feaee64f84d0913dc6fa4529c29999cd0c76f5221f4d6740e8dc1eddf3a8666eb8064a902dd271ea1f713d708ad62d3967afe95b161b531de61c30e" + }, + "emittedTransactionData": "0061736d01000000016e1360000060017f017f60027f7f017f60027f7f0060017f006000017f60037f7f7f0060047f7f7f7f0060037f7f7f017f60057f7f7e7f7f017f60027f7e0060017f017e60017e0060047f7f7f7f017f6000017e60057f7f7f7f7f0060037f7e7f0060047f7e7f7f0060027f7f017e02a8072703656e760b7369676e616c4572726f72000303656e760a6d4275666665724e6577000503656e760d6d427566666572417070656e64000203656e76096d4275666665724571000203656e761b6d616e616765645472616e7366657256616c756545786563757465000903656e7609626967496e74416464000603656e760a626967496e745369676e000103656e76126d427566666572417070656e644279746573000803656e76226d616e616765644d756c74695472616e73666572455344544e465445786563757465000903656e760d6d616e6167656443616c6c6572000403656e76136d616e616765644f776e657241646472657373000403656e76136765744e756d455344545472616e7366657273000503656e7612626967496e7447657443616c6c56616c7565000403656e7616626967496e744765744553445443616c6c56616c7565000403656e761067657445534454546f6b656e4e616d65000103656e760f6d4275666665725365744279746573000803656e76126d427566666572476574417267756d656e74000203656e76126d616e616765645369676e616c4572726f72000403656e76106d4275666665724765744c656e677468000103656e7619626967496e74476574556e7369676e6564417267756d656e74000303656e760f6765744e756d417267756d656e7473000503656e7609626967496e74537562000603656e76196d42756666657246726f6d426967496e74556e7369676e6564000203656e76176d427566666572546f426967496e74556e7369676e6564000203656e76126d42756666657253746f726167654c6f6164000203656e76136d42756666657253746f7261676553746f7265000203656e760e626967496e74536574496e743634000a03656e7609626967496e74436d70000203656e760f6d616e6167656457726974654c6f67000303656e7611676574417267756d656e744c656e677468000103656e761b736d616c6c496e74476574556e7369676e6564417267756d656e74000b03656e760e636865636b4e6f5061796d656e74000003656e7611676574426c6f636b54696d657374616d70000e03656e7614736d616c6c496e7446696e6973685369676e6564000c03656e7614626967496e7446696e697368556e7369676e6564000403656e7616736d616c6c496e7446696e697368556e7369676e6564000c03656e760d6d42756666657246696e697368000103656e760666696e697368000303656e76136d42756666657247657442797465536c696365000d0361600f00010203060205010504050500040107010803010501040400000302020d030302020101051007070401030101010b03031106030a01010303020612010306060207060606050202050404000300000000000000000000000000000000000005030100110619037f01418080c0000b7f0041b0d3c0000b7f0041b0d3c0000b07fc0113066d656d6f7279020004696e6974007508616464436c61696d007609616464436c61696d73007705636c61696d00780869735061757365640079057061757365007a0b72656d6f7665436c61696d007b0c72656d6f7665436c61696d73007c0e736574526577617264546f6b656e007d07756e7061757365007e0976696577436c61696d007f1076696577436c61696d416464446174650080011176696577436c61696d57697468446174650081010a76696577436c61696d730082011376696577546f6b656e4964656e7469666965720083010863616c6c4261636b0084010a5f5f646174615f656e6403010b5f5f686561705f6261736503020ae42f602e000240200120024d0440200220044d0d011028000b1028000b2000200220016b3602042000200120036a3602000b0600108601000b0f01017f10012201200010021a20010b0b0020002001100341004a0b0900200020011000000bcb0101057f230041106b2203240041d084c0004100102d2105102e210602402001102f04402000200242002005200610041a0c010b102e210720011029210410302201103120012001200210052001100604402004102f1a0b2003420037020420032001410874418080fc077120014118747220014108764180fe03712001411876727236020c20032004410874418080fc077120044118747220044108764180fe03712004411876727236020020072003411010071a2000200742002005200610081a0b200341106a24000b1101017f1030220220002001100f1a20020b1401017f1030220041d084c0004100100f1a20000b070020001012450b1b01017f419085c000419085c00028020041016b220036020020000b080020004200101a0b0c01017f10302200100920000b0c01017f10302200100a20000b160010331032102a04400f0b418080c00041241000000ba90101037f230041206b220124000240024002400240100b450440417521024175100c0c010b417321024173100d100b0d010b102e21030c010b200141186a4200370300200141106a4200370300200141086a42003703002001420037030002402001100e22024504401030220341d084c0004100100f1a0c010b200241214f0d0220012002102d21030b417321020b2000200336020420002002360200200141206a24000f0b1028000b0d0020001030220010101a20000b3001017f41a480c0004117102d22042000200110071a200441bb80c000410310071a20042002200310071a20041011000b3201017f102e21010340200028020041acd3c0002802004e4504402001200041ae84c000410610391036103a0c010b0b20010b3101017f2000280200220341acd3c0002802004e04402001200241be80c00041111037000b2000200341016a36020020030b4701017f230041106b2202240020022001410874418080fc077120014118747220014108764180fe03712001411876727236020c20002002410c6a410410071a200241106a24000b5201017e2000101d45044041000f0b02402000101e22014280025404402001a7220041ff017141034f0d0120000f0b419d84c000410a41fa80c000410e1037000b419d84c000410a41b484c000410d1037000b2501017f4100103622001012412047044041a784c000410741b481c00041101037000b20000b5101017f230041106b220124000240200010124104470d002001410036020c200041002001410c6a410410451a200128020c41c58eb1a204470d00200041d084c0004100100f1a0b200141106a240020000b1a00200041acd3c00028020048044041cf80c00041121000000b0b1500101420004604400f0b41e180c00041191000000b1b0041acd3c00028020041004e04400f0b41be80c00041111000000b0c0041acd3c00010143602000b2201017f2001101221022000410036020820002001360200200020024102763602040b08002000200110440b1500417f20002001101b220041004720004100481b0b0f00200020012003200210264100470b09002000200110021a0b0a0020002000200110050b0c00200020002001100520000b2000200020002001101520001006410048044041d084c00041301000000b20000b0f01017f10302201200010161a20010b0d0020001030220010171a20000b0c01017f10302200103120000bfe0101027f230041306b22032400200341206a41003602002003420037031820032002410874418080fc077120024118747220024108764180fe037120024118767272360228200341106a200341186a220241004104104e20032802102003280214200341286a22044104104f2003200142288642808080808080c0ff00832001423886842001421886428080808080e03f8320014208864280808080f01f838484200142088842808080f80f832001421888428080fc07838420014228884280fe03832001423888848484370328200341086a20024104410c104e2003280208200328020c20044108104f20002802002002410c10071a200341306a24000b3b01017f230041106b22042400200441086a200220032001410c1027200428020c21012000200428020836020020002001360204200441106a24000bb10201067f2001200346044020012203410f4b04402000410020006b41037122056a210420050440200221010340200020012d00003a0000200141016a2101200041016a22002004490d000b0b2004200320056b2203417c7122066a21000240200220056a220541037122010440200641004c0d012005417c71220741046a21024100200141037422086b4118712109200728020021010340200420012008762002280200220120097472360200200241046a2102200441046a22042000490d000b0c010b200641004c0d0020052102034020042002280200360200200241046a2102200441046a22042000490d000b0b20034103712103200520066a21020b20030440200020036a21010340200020022d00003a0000200241016a2102200041016a22002001490d000b0b0f0b108601000b5501017f20002d00042101200041003a0004024020014101710440419485c00028020022014191ce004f0d012000280200419885c000200110071a419485c000410036020041a8d3c00041003a00000b0f0b1028000b0d0020001030220010181a20000b3a01017f230041106b220224002002200141ff017141027441c484c0006a2802002d00003a000f20002002410f6a410110071a200241106a24000b080020001051104b0b080020001051103d0b2f01017e027f0240200010562201420158044041002001a741016b0d021a0c010b418c81c00041121057000b41010b0b5d02027f017e230041106b22012400200142003703082000105122001012220241094f044041fa80c000410e1057000b2001200141086a2002106220004100200128020022002001280204220210451a200020021063200141106a24000b1b01017f419e81c0004116102d22022000200110071a20021011000b3701017f230041106b220224002002420037030820022001ad4101200241086a1059200020022802002002280204105a200241106a24000b9c0202047f017e2003200142288642808080808080c0ff00832001423886842001421886428080808080e03f8320014208864280808080f01f838484200142088842808080f80f832001421888428080fc07838420014228884280fe038320014238888484842208370000024002402000027f200150044041d084c000210341000c010b200241002001427f511b45044041002002200842800183420788a77122056b41ff01712106034020044108460d032006200320046a2d000022074704402004200220074107762005477122026b41094f0d052003410020026b22026a20046a21034108200220046a6b0c0305200441016a21040c010b000b000b200341076a210341010b360204200020033602000f0b108501000b108501000b0d00200020012002102d10191a0b0b0020002001104a10191a0b3601017f230041106b2202240020024200370308200220014100200241086a1059200020022802002002280204105a200241106a24000b0e002000416710181a41671012450b0900102e1a2000104a0b0d00102e1a200020011029103a0b5a02027f017e230041106b22022400102e2103200141ff017141027441c484c0006a280200310000210420024200370308200220044100200241086a1059200320022802002002280204100f1a20002003103a200241106a24000b1201017f102e220220002001102d103a20020b3901017f230041106b22032400200341086a2001410820021069200328020c21012000200328020836020020002001360204200341106a24000b3401017e02402001450d0003402001450d01200141016b210120003100002002420886842102200041016a21000c000b000b20020b800101037f230041106b22012400200028020821032001410036020c200028020020034102742001410c6a41041045450440200128020c21022000200341016a3602082002410874418080fc077120024118747220024108764180fe0371200241187672721029200141106a24000f0b41c481c000410841be80c00041111037000bfb0102047f017e230041106b220324000240024002400240200128020820012802044f0440200041033a00040c010b200110641029220510124120470d0102402001106422021012450440410021020c010b2003420037030820021012220441094f0d042003200341086a2004106220024100200328020022022003280204220410451a20022004106322064280025a0d042006a7220241ff017141034f0d030b200041086a20011064104b360200200020023a0004200020053602000b200341106a24000f0b41c481c000410841b481c00041101037000b41c481c000410841b484c000410d1037000b41c481c000410841fa80c000410e1037000b800101027f230041106b220324000240024020002d000404404190ce00419485c00028020022046b2002490d01200341086a2004200220046a220010672003280208200328020c20012002104f419485c00020003602000c020b20002802002001200210071a0c010b2000105020002802002001200210071a0b200341106a24000b4001017f230041106b22032400200341086a20012002419885c0004190ce001027200328020c21012000200328020836020020002001360204200341106a24000b0f0020002001104341ff01714101460b3b01017f230041106b22042400200441086a41002003200120021027200428020c21012000200428020836020020002001360204200441106a24000b2101017f41cc81c000410a106122032000105f20032001106020032002105e101c0b2101017f41d681c000410c106122032000105f20032001106020032002105e101c0b2101017f41e281c000410e106122032000105f20032001106020032002105e101c0b0b0041f081c000410f102d0b1e01017f41ff81c0004109102d220220001046200220012d0000105220020b1e01017f418882c0004105102d220220001046200220012d0000105220020b0b00418d82c0004108102d0b16002000104c106804400f0b419582c0004123102b000b18002000106d1054102a04400f0b41b882c000411d102b000b1500106d105d4504400f0b41d582c0004117102b000b1d0020002001104341ff017141014d04400f0b41ec82c0004125102b000b0e00101f4100103f1070410110580b7602077f017e230041106b2200240010344102103f103c210120004101103b22043a000f10732000103520002802002102200028020420012000410f6a2203106f105321061020210710722002107120012003106f200620021048105b20012003106e2007105c200120042002106a200041106a24000b960202077f027e230041d0006b2200240010341041104020004100360240200041406b2201103821022000280240103e1073200041086a103520002802082104200028020c10202107107220041071104c2105200041106a20021042200041286a200041186a280200360200200020002903103703202001410472210203400240200041306a200041206a106520002d0034410346044020052004104441ff01710d01200041d0006a24000f05200041c8006a2201200041386a2802003602002000200029033022083703402008a722032002106f1053210620032002106f2006200128020022011048105b20032002106e2007105c200520011047200320002d00442001106a0c020b000b0b419183c0004126102b000bce0201057f230041106b22022400101f1041104020024100360208200241086a220028020041acd3c00028020048047f2000419d84c000410a1039103b0541030b21012002280208103e024002400240107010554504401073106d1054210410322100104c2103200141ff01714103470440200220013a000f20002002410f6a106f10532203107120002002410f6a106f104c105b200020012003106c0c040b200041c983c000106f10532201104c10680d010c020b41b783c0004112102b000b200320011047200041002001106c200041c983c000106f104c105b0b200041ca83c000106f10532201104c10680440200320011047200041012001106c200041ca83c000106f104c105b0b200041cb83c000106f10532201104c10680440200320011047200041022001106c200041cb83c000106f104c105b0b200310710b200020042003102c200241106a24000b1600101f4100103f418d82c0004108102d1055ad10210b2100101f10344100103f10701055044041e783c000411a102b000b1070410110580b800102087f017e230041106b22022400101f10344103103f103c21014101103b21034102103022001013200220033a000f107320012002410f6a2204106f105321051033106d1054102021082000107120052000107420012004106f200520001049105b20012004106e2008105c200120032000106b2000102c200241106a24000bf20102067f027e230041406a22002400101f10341041104020004100360230200041306a2202103821012000280230103e1073104c210410202106200020011042200041186a200041086a28020036020020002000290300370310200241047221020340200041206a200041106a106520002d0024410346450440200041386a2201200041286a2802003602002000200029032022073703302007a722032002106f1053210520032002106e2006105c20012802002201107120052001107420042001104720032002106f200520011049105b200320002d00342001106b0c010b0b1033106d10542004102c200041406b24000b4501027f101f10344101103f41001036103d21000240106d105d0440106d21012000102f0d012001200010191a0f0b41cc83c000411b102b000b2001418881c0004104105a0b2200101f10344100103f10701055450440418184c000411c102b000b1070410010580b3001037f101f4102103f103c21014101103b2102418882c0004105102d2200200110021a2000200210522000105310220b3001037f101f4102103f103c21014101103b210241ff81c0004109102d2200200110021a2000200210522000105610230ba50502087f017e230041306b22002400101f410121044101103f103c21012000102e2205360220200141c983c000106f10532102200041206a2203200141c983c000106e10562002104d200141ca83c000106f105321022003200141ca83c000106e10562002104d200141cb83c000106f105321022003200141cb83c000106e10562002104d027f41a8d3c0002d000045044041a8d3c00041013a0000419485c0004100360200200041106a419885c0004190ce00410010692000280210200028021441d084c0004100104f102e0c010b4100210441d084c0004100102d0b2101200020043a001c200020013602182005101221064100210103402001410c6a220420064b450440200041286a41003602002000420037032020052001200041206a2203410c10451a200029022421082000200028022022014118742001410874418080fc07717220014108764180fe037120014118767272104a2201101222024118742002410874418080fc07717220024108764180fe037120024118767272360220200041186a20034104106620002d001c2102200041003a001c0240024002402002410171220204402001101222074190ce00419485c00028020022036b4b0d02200041086a2003200320076a22031067200141002000280208200028020c10451a419485c00020033602000c010b2000280218200110021a0b200020023a001c0c010b200041186a10502000280218200110021a20002d001c200020023a001c410171450d00419485c000410036020041a8d3c00041003a00000b20002008370320200041186a200041206a41081066200421010c010b0b20002802182101200020002d001c3a002420002001360220200041206a1050200028022020002d00240440419485c000410036020041a8d3c00041003a00000b10241a200041306a24000b4101027f101f4101103f103c2100104c2201200041c983c000106f105310472001200041ca83c000106f105310472001200041cb83c000106f10531047200110220b2c01017f101f4100103f41f081c000410f102d10542200102f450440200010241a0f0b418881c000410410250b0300010b0c00418085c000410e1000000b0600108501000b0ba4050200418080c0000b8e05456e64706f696e742063616e206f6e6c792062652063616c6c6564206279206f776e6572617267756d656e74206465636f6465206572726f722028293a20746f6f2066657720617267756d656e7473746f6f206d616e7920617267756d656e747377726f6e67206e756d626572206f6620617267756d656e7473696e70757420746f6f206c6f6e6745474c44696e707574206f7574206f662072616e676573746f72616765206465636f6465206572726f723a20626164206172726179206c656e6774687661722061726773636c61696d4164646564636c61696d52656d6f766564636c61696d436f6c6c6563746564746f6b656e4964656e746966696572636c61696d44617465636c61696d69735061757365644f7065726174696f6e73206d7573742068617665206e6f6e2d7a65726f2076616c756543616e206f6e6c79206164642064657369676e6174656420746f6b656e52657761726420746f6b656e206973206e6f742073657443616e6e6f742072656d6f7665206d6f7265207468616e2063757272656e7420636c61696d436c61696d73206164646564206d75737420657175616c207061796d656e7420616d6f756e74436f6e74726163742069732070617573656400010252657761726420746f6b656e20697320616c726561647920736574436f6e747261637420697320616c726561647920706175736564436f6e747261637420697320616c726561647920756e706175736564636c61696d5f7479706561646472657373636c61696d73696e76616c69642076616c7565000000c9011000ca011000cb01100063616e6e6f74207375627472616374206265636175736520726573756c7420776f756c64206265206e6567617469766570616e6963206f6363757272656400419085c0000b049cffffff@0500@0502", + "emittedTransactionHash": "e1971d180adf53bafca974012f6e47418c67ef38280f7d2c2542dd8acbe5b0c2", + "contractAddress": "erd1qqqqqqqqqqqqqpgq4srlh59un9c0qqsqcr3mkja2s5gllx6q7yqsr6s5p3" } diff --git a/src/lib.rs b/src/lib.rs index 4ea8ac5..b5dcd9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,6 +70,10 @@ pub trait ClaimsContract: claims: MultiValueEncoded>, ) { self.require_reward_token_is_set(); + require!( + claims.len() <= 200, + "Exceeded maximum number of claims per operation (200)" + ); let (payment_amount, payment_token) = self.call_value().payment_token_pair(); let timestamp = self.blockchain().get_block_timestamp(); self.require_token_is_reward(payment_token); @@ -113,6 +117,10 @@ pub trait ClaimsContract: claims: MultiValueEncoded>, ) { self.require_reward_token_is_set(); + require!( + claims.len() <= 200, + "Exceeded maximum number of claims per operation (20)" + ); let mut sum_of_claims = BigUint::zero(); let timestamp = self.blockchain().get_block_timestamp(); for item in claims.into_iter() { From d35fc4f39a3db062f0e11de84a67d3346294f438 Mon Sep 17 00:00:00 2001 From: damienen Date: Tue, 26 Jul 2022 14:14:29 +0300 Subject: [PATCH 09/25] Removed cache --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5462a86..6f55e82 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ # The erdpy output output* +deplyOutput wallets/ commands.txt From a75af98a88f05a8a5994e3f25831bb2e0078f173 Mon Sep 17 00:00:00 2001 From: dmn Date: Tue, 26 Jul 2022 20:50:33 +0300 Subject: [PATCH 10/25] Contributed towards solving issues #4 and #12; created github documentation structure --- README.md | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index a7955e1..33cf098 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,33 @@ -# Itheum Core Elrond - Claim Smart Contract -The core itheum elrond smart contract for `claims` +# Itheum Core Elrond - Claims Smart Contract -### How to Dev -- describe steps to get running in dev mode +## Abstract -### How to Test -- describe steps to running tests +The claims smart contract is the tool that stands at the heart of collaboration between Itheum and its community. Whether it's a reward for helping the project, an airdrop or some allocation of tokens, the claims smart contract is the tool that allows Itheum to give tokens to all community members that are using the Elrond blockchain. -### Deployed Contract Addresses -Devnet | Mainnet ---- | --- -0x | 0x +## Introduction +This contract allows the owner of it to send tokens to the smart contract and reserve them for a specific address of their choice. There are 3 types of claims that are defined in the smart contract: rewards, airdrops and allocations. If a user has claims, they can harvest each type individually or can choose to harvest all of them in the same transaction. The contract is designed such that a user can only take their designated tokens from the contract. -## Known Issues +## Itheum deployed claims contract addresses + +| Devnet | Mainnet | +| -------------------------------------------------------------- | ---------------- | +| erd1qqqqqqqqqqqqqpgqtywnp7z0war94rpzk00p2n2wjwaws2xr7yqsejxy7f | Not deployed yet | + +## Endpoints + +### Setup endpoints + +### Only owner endpoints + +### Public endpoints + +## Development + +### Architecture + +### How to test + +### How to deploy + +## Contributing From d9758859efc040d9424da2a9fe03f97e46b6364e Mon Sep 17 00:00:00 2001 From: damienen Date: Tue, 26 Jul 2022 21:00:05 +0300 Subject: [PATCH 11/25] Contributed towards issue #12 --- .gitignore | 2 +- src/lib.rs | 30 +++++++++++---------- tests/empty_mandos_go_test.rs | 4 --- tests/empty_mandos_rs_test.rs | 13 --------- tests/{empty_rust_test.rs => rust_tests.rs} | 0 5 files changed, 17 insertions(+), 32 deletions(-) delete mode 100644 tests/empty_mandos_go_test.rs delete mode 100644 tests/empty_mandos_rs_test.rs rename tests/{empty_rust_test.rs => rust_tests.rs} (100%) diff --git a/.gitignore b/.gitignore index 6f55e82..7b5aea5 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ # The erdpy output output* -deplyOutput +deployOutput wallets/ commands.txt diff --git a/src/lib.rs b/src/lib.rs index b5dcd9c..8c9ccce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,12 +80,13 @@ pub trait ClaimsContract: self.require_value_not_zero(&payment_amount); let mut sum_of_claims = BigUint::zero(); for item in claims.into_iter() { - let tuple = item.into_tuple(); - let current_claim = self.claim(&tuple.0, &tuple.1).get(); - self.claim(&tuple.0, &tuple.1).set(current_claim + &tuple.2); - self.claim_add_date(&tuple.0, &tuple.1).set(timestamp); - sum_of_claims += &tuple.2; - self.claim_added_event(&tuple.0, &tuple.1, &tuple.2); + let (address, claim_type, amount) = item.into_tuple(); + let current_claim = self.claim(&address, &claim_type).get(); + self.claim(&address, &claim_type) + .set(current_claim + &amount); + self.claim_add_date(&address, &claim_type).set(timestamp); + sum_of_claims += &amount; + self.claim_added_event(&address, &claim_type, &amount); } require!( sum_of_claims == payment_amount, @@ -124,14 +125,15 @@ pub trait ClaimsContract: let mut sum_of_claims = BigUint::zero(); let timestamp = self.blockchain().get_block_timestamp(); for item in claims.into_iter() { - let tuple = item.into_tuple(); - let current_claim = self.claim(&tuple.0, &tuple.1).get(); - self.claim_add_date(&tuple.0, &tuple.1).set(timestamp); - self.require_value_not_zero(&tuple.2); - self.require_remove_claim_is_valid(¤t_claim, &tuple.2); - sum_of_claims += &tuple.2; - self.claim(&tuple.0, &tuple.1).set(current_claim - &tuple.2); - self.claim_removed_event(&tuple.0, &tuple.1, &tuple.2); + let (address, claim_type, amount) = item.into_tuple(); + let current_claim = self.claim(&address, &claim_type).get(); + self.claim_add_date(&address, &claim_type).set(timestamp); + self.require_value_not_zero(&amount); + self.require_remove_claim_is_valid(¤t_claim, &amount); + sum_of_claims += &amount; + self.claim(&address, &claim_type) + .set(current_claim - &amount); + self.claim_removed_event(&address, &claim_type, &amount); } let owner = self.blockchain().get_owner_address(); let reward_token = self.reward_token().get(); diff --git a/tests/empty_mandos_go_test.rs b/tests/empty_mandos_go_test.rs deleted file mode 100644 index b989e39..0000000 --- a/tests/empty_mandos_go_test.rs +++ /dev/null @@ -1,4 +0,0 @@ -#[test] -fn claims_go() { - elrond_wasm_debug::mandos_go("mandos/claims.scen.json"); -} diff --git a/tests/empty_mandos_rs_test.rs b/tests/empty_mandos_rs_test.rs deleted file mode 100644 index 9622fbc..0000000 --- a/tests/empty_mandos_rs_test.rs +++ /dev/null @@ -1,13 +0,0 @@ -use elrond_wasm_debug::*; - -fn world() -> BlockchainMock { - let mut blockchain = BlockchainMock::new(); - - blockchain.register_contract_builder("file:output/claims.wasm", claims::ContractBuilder); - blockchain -} - -#[test] -fn claims_rs() { - elrond_wasm_debug::mandos_rs("mandos/claims.scen.json", world()); -} diff --git a/tests/empty_rust_test.rs b/tests/rust_tests.rs similarity index 100% rename from tests/empty_rust_test.rs rename to tests/rust_tests.rs From ccd1f1cb480f2c2f2f374d688f491d173628a116 Mon Sep 17 00:00:00 2001 From: damienen Date: Tue, 26 Jul 2022 21:00:29 +0300 Subject: [PATCH 12/25] Contributed towards issue #12 --- deployOutput | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 deployOutput diff --git a/deployOutput b/deployOutput deleted file mode 100644 index 669097f..0000000 --- a/deployOutput +++ /dev/null @@ -1,17 +0,0 @@ -{ - "emittedTransaction": { - "nonce": 3889, - "value": "0", - "receiver": "erd1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq6gq4hu", - "sender": "erd130qhxzu6lh29g6srnsa65pplxaf9sgsspczvljvxk624upwt7yqstswx9l", - "gasPrice": 1000000000, - "gasLimit": 150000000, - "data": "MDA2MTczNmQwMTAwMDAwMDAxNmUxMzYwMDAwMDYwMDE3ZjAxN2Y2MDAyN2Y3ZjAxN2Y2MDAyN2Y3ZjAwNjAwMTdmMDA2MDAwMDE3ZjYwMDM3ZjdmN2YwMDYwMDQ3ZjdmN2Y3ZjAwNjAwMzdmN2Y3ZjAxN2Y2MDA1N2Y3ZjdlN2Y3ZjAxN2Y2MDAyN2Y3ZTAwNjAwMTdmMDE3ZTYwMDE3ZTAwNjAwNDdmN2Y3ZjdmMDE3ZjYwMDAwMTdlNjAwNTdmN2Y3ZjdmN2YwMDYwMDM3ZjdlN2YwMDYwMDQ3ZjdlN2Y3ZjAwNjAwMjdmN2YwMTdlMDJhODA3MjcwMzY1NmU3NjBiNzM2OTY3NmU2MTZjNDU3MjcyNmY3MjAwMDMwMzY1NmU3NjBhNmQ0Mjc1NjY2NjY1NzI0ZTY1NzcwMDA1MDM2NTZlNzYwZDZkNDI3NTY2NjY2NTcyNDE3MDcwNjU2ZTY0MDAwMjAzNjU2ZTc2MDk2ZDQyNzU2NjY2NjU3MjQ1NzEwMDAyMDM2NTZlNzYxYjZkNjE2ZTYxNjc2NTY0NTQ3MjYxNmU3MzY2NjU3MjU2NjE2Yzc1NjU0NTc4NjU2Mzc1NzQ2NTAwMDkwMzY1NmU3NjA5NjI2OTY3NDk2ZTc0NDE2NDY0MDAwNjAzNjU2ZTc2MGE2MjY5Njc0OTZlNzQ1MzY5Njc2ZTAwMDEwMzY1NmU3NjEyNmQ0Mjc1NjY2NjY1NzI0MTcwNzA2NTZlNjQ0Mjc5NzQ2NTczMDAwODAzNjU2ZTc2MjI2ZDYxNmU2MTY3NjU2NDRkNzU2Yzc0Njk1NDcyNjE2ZTczNjY2NTcyNDU1MzQ0NTQ0ZTQ2NTQ0NTc4NjU2Mzc1NzQ2NTAwMDkwMzY1NmU3NjBkNmQ2MTZlNjE2NzY1NjQ0MzYxNmM2YzY1NzIwMDA0MDM2NTZlNzYxMzZkNjE2ZTYxNjc2NTY0NGY3NzZlNjU3MjQxNjQ2NDcyNjU3MzczMDAwNDAzNjU2ZTc2MTM2NzY1NzQ0ZTc1NmQ0NTUzNDQ1NDU0NzI2MTZlNzM2NjY1NzI3MzAwMDUwMzY1NmU3NjEyNjI2OTY3NDk2ZTc0NDc2NTc0NDM2MTZjNmM1NjYxNmM3NTY1MDAwNDAzNjU2ZTc2MTY2MjY5Njc0OTZlNzQ0NzY1NzQ0NTUzNDQ1NDQzNjE2YzZjNTY2MTZjNzU2NTAwMDQwMzY1NmU3NjEwNjc2NTc0NDU1MzQ0NTQ1NDZmNmI2NTZlNGU2MTZkNjUwMDAxMDM2NTZlNzYwZjZkNDI3NTY2NjY2NTcyNTM2NTc0NDI3OTc0NjU3MzAwMDgwMzY1NmU3NjEyNmQ0Mjc1NjY2NjY1NzI0NzY1NzQ0MTcyNjc3NTZkNjU2ZTc0MDAwMjAzNjU2ZTc2MTI2ZDYxNmU2MTY3NjU2NDUzNjk2NzZlNjE2YzQ1NzI3MjZmNzIwMDA0MDM2NTZlNzYxMDZkNDI3NTY2NjY2NTcyNDc2NTc0NGM2NTZlNjc3NDY4MDAwMTAzNjU2ZTc2MTk2MjY5Njc0OTZlNzQ0NzY1NzQ1NTZlNzM2OTY3NmU2NTY0NDE3MjY3NzU2ZDY1NmU3NDAwMDMwMzY1NmU3NjBmNjc2NTc0NGU3NTZkNDE3MjY3NzU2ZDY1NmU3NDczMDAwNTAzNjU2ZTc2MDk2MjY5Njc0OTZlNzQ1Mzc1NjIwMDA2MDM2NTZlNzYxOTZkNDI3NTY2NjY2NTcyNDY3MjZmNmQ0MjY5Njc0OTZlNzQ1NTZlNzM2OTY3NmU2NTY0MDAwMjAzNjU2ZTc2MTc2ZDQyNzU2NjY2NjU3MjU0NmY0MjY5Njc0OTZlNzQ1NTZlNzM2OTY3NmU2NTY0MDAwMjAzNjU2ZTc2MTI2ZDQyNzU2NjY2NjU3MjUzNzQ2ZjcyNjE2NzY1NGM2ZjYxNjQwMDAyMDM2NTZlNzYxMzZkNDI3NTY2NjY2NTcyNTM3NDZmNzI2MTY3NjU1Mzc0NmY3MjY1MDAwMjAzNjU2ZTc2MGU2MjY5Njc0OTZlNzQ1MzY1NzQ0OTZlNzQzNjM0MDAwYTAzNjU2ZTc2MDk2MjY5Njc0OTZlNzQ0MzZkNzAwMDAyMDM2NTZlNzYwZjZkNjE2ZTYxNjc2NTY0NTc3MjY5NzQ2NTRjNmY2NzAwMDMwMzY1NmU3NjExNjc2NTc0NDE3MjY3NzU2ZDY1NmU3NDRjNjU2ZTY3NzQ2ODAwMDEwMzY1NmU3NjFiNzM2ZDYxNmM2YzQ5NmU3NDQ3NjU3NDU1NmU3MzY5Njc2ZTY1NjQ0MTcyNjc3NTZkNjU2ZTc0MDAwYjAzNjU2ZTc2MGU2MzY4NjU2MzZiNGU2ZjUwNjE3OTZkNjU2ZTc0MDAwMDAzNjU2ZTc2MTE2NzY1NzQ0MjZjNmY2MzZiNTQ2OTZkNjU3Mzc0NjE2ZDcwMDAwZTAzNjU2ZTc2MTQ3MzZkNjE2YzZjNDk2ZTc0NDY2OTZlNjk3MzY4NTM2OTY3NmU2NTY0MDAwYzAzNjU2ZTc2MTQ2MjY5Njc0OTZlNzQ0NjY5NmU2OTczNjg1NTZlNzM2OTY3NmU2NTY0MDAwNDAzNjU2ZTc2MTY3MzZkNjE2YzZjNDk2ZTc0NDY2OTZlNjk3MzY4NTU2ZTczNjk2NzZlNjU2NDAwMGMwMzY1NmU3NjBkNmQ0Mjc1NjY2NjY1NzI0NjY5NmU2OTczNjgwMDAxMDM2NTZlNzYwNjY2Njk2ZTY5NzM2ODAwMDMwMzY1NmU3NjEzNmQ0Mjc1NjY2NjY1NzI0NzY1NzQ0Mjc5NzQ2NTUzNmM2OTYzNjUwMDBkMDM2MTYwMGYwMDAxMDIwMzA2MDIwNTAxMDUwNDA1MDUwMDA0MDEwNzAxMDgwMzAxMDUwMTA0MDQwMDAwMDMwMjAyMGQwMzAzMDIwMjAxMDEwNTEwMDcwNzA0MDEwMzAxMDEwMTBiMDMwMzExMDYwMzBhMDEwMTAzMDMwMjA2MTIwMTAzMDYwNjAyMDcwNjA2MDYwNTAyMDIwNTA0MDQwMDAzMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDUwMzAxMDAxMTA2MTkwMzdmMDE0MTgwODBjMDAwMGI3ZjAwNDFiMGQzYzAwMDBiN2YwMDQxYjBkM2MwMDAwYjA3ZmMwMTEzMDY2ZDY1NmQ2ZjcyNzkwMjAwMDQ2OTZlNjk3NDAwNzUwODYxNjQ2NDQzNmM2MTY5NmQwMDc2MDk2MTY0NjQ0MzZjNjE2OTZkNzMwMDc3MDU2MzZjNjE2OTZkMDA3ODA4Njk3MzUwNjE3NTczNjU2NDAwNzkwNTcwNjE3NTczNjUwMDdhMGI3MjY1NmQ2Zjc2NjU0MzZjNjE2OTZkMDA3YjBjNzI2NTZkNmY3NjY1NDM2YzYxNjk2ZDczMDA3YzBlNzM2NTc0NTI2NTc3NjE3MjY0NTQ2ZjZiNjU2ZTAwN2QwNzc1NmU3MDYxNzU3MzY1MDA3ZTA5NzY2OTY1Nzc0MzZjNjE2OTZkMDA3ZjEwNzY2OTY1Nzc0MzZjNjE2OTZkNDE2NDY0NDQ2MTc0NjUwMDgwMDExMTc2Njk2NTc3NDM2YzYxNjk2ZDU3Njk3NDY4NDQ2MTc0NjUwMDgxMDEwYTc2Njk2NTc3NDM2YzYxNjk2ZDczMDA4MjAxMTM3NjY5NjU3NzU0NmY2YjY1NmU0OTY0NjU2ZTc0Njk2NjY5NjU3MjAwODMwMTA4NjM2MTZjNmM0MjYxNjM2YjAwODQwMTBhNWY1ZjY0NjE3NDYxNWY2NTZlNjQwMzAxMGI1ZjVmNjg2NTYxNzA1ZjYyNjE3MzY1MDMwMjBhZTQyZjYwMmUwMDAyNDAyMDAxMjAwMjRkMDQ0MDIwMDIyMDA0NGQwZDAxMTAyODAwMGIxMDI4MDAwYjIwMDAyMDAyMjAwMTZiMzYwMjA0MjAwMDIwMDEyMDAzNmEzNjAyMDAwYjA2MDAxMDg2MDEwMDBiMGYwMTAxN2YxMDAxMjIwMTIwMDAxMDAyMWEyMDAxMGIwYjAwMjAwMDIwMDExMDAzNDEwMDRhMGIwOTAwMjAwMDIwMDExMDAwMDAwYmNiMDEwMTA1N2YyMzAwNDExMDZiMjIwMzI0MDA0MWQwODRjMDAwNDEwMDEwMmQyMTA1MTAyZTIxMDYwMjQwMjAwMTEwMmYwNDQwMjAwMDIwMDI0MjAwMjAwNTIwMDYxMDA0MWEwYzAxMGIxMDJlMjEwNzIwMDExMDI5MjEwNDEwMzAyMjAxMTAzMTIwMDEyMDAxMjAwMjEwMDUyMDAxMTAwNjA0NDAyMDA0MTAyZjFhMGIyMDAzNDIwMDM3MDIwNDIwMDMyMDAxNDEwODc0NDE4MDgwZmMwNzcxMjAwMTQxMTg3NDcyMjAwMTQxMDg3NjQxODBmZTAzNzEyMDAxNDExODc2NzI3MjM2MDIwYzIwMDMyMDA0NDEwODc0NDE4MDgwZmMwNzcxMjAwNDQxMTg3NDcyMjAwNDQxMDg3NjQxODBmZTAzNzEyMDA0NDExODc2NzI3MjM2MDIwMDIwMDcyMDAzNDExMDEwMDcxYTIwMDAyMDA3NDIwMDIwMDUyMDA2MTAwODFhMGIyMDAzNDExMDZhMjQwMDBiMTEwMTAxN2YxMDMwMjIwMjIwMDAyMDAxMTAwZjFhMjAwMjBiMTQwMTAxN2YxMDMwMjIwMDQxZDA4NGMwMDA0MTAwMTAwZjFhMjAwMDBiMDcwMDIwMDAxMDEyNDUwYjFiMDEwMTdmNDE5MDg1YzAwMDQxOTA4NWMwMDAyODAyMDA0MTAxNmIyMjAwMzYwMjAwMjAwMDBiMDgwMDIwMDA0MjAwMTAxYTBiMGMwMTAxN2YxMDMwMjIwMDEwMDkyMDAwMGIwYzAxMDE3ZjEwMzAyMjAwMTAwYTIwMDAwYjE2MDAxMDMzMTAzMjEwMmEwNDQwMGYwYjQxODA4MGMwMDA0MTI0MTAwMDAwMGJhOTAxMDEwMzdmMjMwMDQxMjA2YjIyMDEyNDAwMDI0MDAyNDAwMjQwMDI0MDEwMGI0NTA0NDA0MTc1MjEwMjQxNzUxMDBjMGMwMTBiNDE3MzIxMDI0MTczMTAwZDEwMGIwZDAxMGIxMDJlMjEwMzBjMDEwYjIwMDE0MTE4NmE0MjAwMzcwMzAwMjAwMTQxMTA2YTQyMDAzNzAzMDAyMDAxNDEwODZhNDIwMDM3MDMwMDIwMDE0MjAwMzcwMzAwMDI0MDIwMDExMDBlMjIwMjQ1MDQ0MDEwMzAyMjAzNDFkMDg0YzAwMDQxMDAxMDBmMWEwYzAxMGIyMDAyNDEyMTRmMGQwMjIwMDEyMDAyMTAyZDIxMDMwYjQxNzMyMTAyMGIyMDAwMjAwMzM2MDIwNDIwMDAyMDAyMzYwMjAwMjAwMTQxMjA2YTI0MDAwZjBiMTAyODAwMGIwZDAwMjAwMDEwMzAyMjAwMTAxMDFhMjAwMDBiMzAwMTAxN2Y0MWE0ODBjMDAwNDExNzEwMmQyMjA0MjAwMDIwMDExMDA3MWEyMDA0NDFiYjgwYzAwMDQxMDMxMDA3MWEyMDA0MjAwMjIwMDMxMDA3MWEyMDA0MTAxMTAwMGIzMjAxMDE3ZjEwMmUyMTAxMDM0MDIwMDAyODAyMDA0MWFjZDNjMDAwMjgwMjAwNGU0NTA0NDAyMDAxMjAwMDQxYWU4NGMwMDA0MTA2MTAzOTEwMzYxMDNhMGMwMTBiMGIyMDAxMGIzMTAxMDE3ZjIwMDAyODAyMDAyMjAzNDFhY2QzYzAwMDI4MDIwMDRlMDQ0MDIwMDEyMDAyNDFiZTgwYzAwMDQxMTExMDM3MDAwYjIwMDAyMDAzNDEwMTZhMzYwMjAwMjAwMzBiNDcwMTAxN2YyMzAwNDExMDZiMjIwMjI0MDAyMDAyMjAwMTQxMDg3NDQxODA4MGZjMDc3MTIwMDE0MTE4NzQ3MjIwMDE0MTA4NzY0MTgwZmUwMzcxMjAwMTQxMTg3NjcyNzIzNjAyMGMyMDAwMjAwMjQxMGM2YTQxMDQxMDA3MWEyMDAyNDExMDZhMjQwMDBiNTIwMTAxN2UyMDAwMTAxZDQ1MDQ0MDQxMDAwZjBiMDI0MDIwMDAxMDFlMjIwMTQyODAwMjU0MDQ0MDIwMDFhNzIyMDA0MWZmMDE3MTQxMDM0ZjBkMDEyMDAwMGYwYjQxOWQ4NGMwMDA0MTBhNDFmYTgwYzAwMDQxMGUxMDM3MDAwYjQxOWQ4NGMwMDA0MTBhNDFiNDg0YzAwMDQxMGQxMDM3MDAwYjI1MDEwMTdmNDEwMDEwMzYyMjAwMTAxMjQxMjA0NzA0NDA0MWE3ODRjMDAwNDEwNzQxYjQ4MWMwMDA0MTEwMTAzNzAwMGIyMDAwMGI1MTAxMDE3ZjIzMDA0MTEwNmIyMjAxMjQwMDAyNDAyMDAwMTAxMjQxMDQ0NzBkMDAyMDAxNDEwMDM2MDIwYzIwMDA0MTAwMjAwMTQxMGM2YTQxMDQxMDQ1MWEyMDAxMjgwMjBjNDFjNThlYjFhMjA0NDcwZDAwMjAwMDQxZDA4NGMwMDA0MTAwMTAwZjFhMGIyMDAxNDExMDZhMjQwMDIwMDAwYjFhMDAyMDAwNDFhY2QzYzAwMDI4MDIwMDQ4MDQ0MDQxY2Y4MGMwMDA0MTEyMTAwMDAwMGIwYjE1MDAxMDE0MjAwMDQ2MDQ0MDBmMGI0MWUxODBjMDAwNDExOTEwMDAwMDBiMWIwMDQxYWNkM2MwMDAyODAyMDA0MTAwNGUwNDQwMGYwYjQxYmU4MGMwMDA0MTExMTAwMDAwMGIwYzAwNDFhY2QzYzAwMDEwMTQzNjAyMDAwYjIyMDEwMTdmMjAwMTEwMTIyMTAyMjAwMDQxMDAzNjAyMDgyMDAwMjAwMTM2MDIwMDIwMDAyMDAyNDEwMjc2MzYwMjA0MGIwODAwMjAwMDIwMDExMDQ0MGIxNTAwNDE3ZjIwMDAyMDAxMTAxYjIyMDA0MTAwNDcyMDAwNDEwMDQ4MWIwYjBmMDAyMDAwMjAwMTIwMDMyMDAyMTAyNjQxMDA0NzBiMDkwMDIwMDAyMDAxMTAwMjFhMGIwYTAwMjAwMDIwMDAyMDAxMTAwNTBiMGMwMDIwMDAyMDAwMjAwMTEwMDUyMDAwMGIyMDAwMjAwMDIwMDAyMDAxMTAxNTIwMDAxMDA2NDEwMDQ4MDQ0MDQxZDA4NGMwMDA0MTMwMTAwMDAwMGIyMDAwMGIwZjAxMDE3ZjEwMzAyMjAxMjAwMDEwMTYxYTIwMDEwYjBkMDAyMDAwMTAzMDIyMDAxMDE3MWEyMDAwMGIwYzAxMDE3ZjEwMzAyMjAwMTAzMTIwMDAwYmZlMDEwMTAyN2YyMzAwNDEzMDZiMjIwMzI0MDAyMDAzNDEyMDZhNDEwMDM2MDIwMDIwMDM0MjAwMzcwMzE4MjAwMzIwMDI0MTA4NzQ0MTgwODBmYzA3NzEyMDAyNDExODc0NzIyMDAyNDEwODc2NDE4MGZlMDM3MTIwMDI0MTE4NzY3MjcyMzYwMjI4MjAwMzQxMTA2YTIwMDM0MTE4NmEyMjAyNDEwMDQxMDQxMDRlMjAwMzI4MDIxMDIwMDMyODAyMTQyMDAzNDEyODZhMjIwNDQxMDQxMDRmMjAwMzIwMDE0MjI4ODY0MjgwODA4MDgwODA4MGMwZmYwMDgzMjAwMTQyMzg4Njg0MjAwMTQyMTg4NjQyODA4MDgwODA4MGUwM2Y4MzIwMDE0MjA4ODY0MjgwODA4MDgwZjAxZjgzODQ4NDIwMDE0MjA4ODg0MjgwODA4MGY4MGY4MzIwMDE0MjE4ODg0MjgwODBmYzA3ODM4NDIwMDE0MjI4ODg0MjgwZmUwMzgzMjAwMTQyMzg4ODg0ODQ4NDM3MDMyODIwMDM0MTA4NmEyMDAyNDEwNDQxMGMxMDRlMjAwMzI4MDIwODIwMDMyODAyMGMyMDA0NDEwODEwNGYyMDAwMjgwMjAwMjAwMjQxMGMxMDA3MWEyMDAzNDEzMDZhMjQwMDBiM2IwMTAxN2YyMzAwNDExMDZiMjIwNDI0MDAyMDA0NDEwODZhMjAwMjIwMDMyMDAxNDEwYzEwMjcyMDA0MjgwMjBjMjEwMTIwMDAyMDA0MjgwMjA4MzYwMjAwMjAwMDIwMDEzNjAyMDQyMDA0NDExMDZhMjQwMDBiYjEwMjAxMDY3ZjIwMDEyMDAzNDYwNDQwMjAwMTIyMDM0MTBmNGIwNDQwMjAwMDQxMDAyMDAwNmI0MTAzNzEyMjA1NmEyMTA0MjAwNTA0NDAyMDAyMjEwMTAzNDAyMDAwMjAwMTJkMDAwMDNhMDAwMDIwMDE0MTAxNmEyMTAxMjAwMDQxMDE2YTIyMDAyMDA0NDkwZDAwMGIwYjIwMDQyMDAzMjAwNTZiMjIwMzQxN2M3MTIyMDY2YTIxMDAwMjQwMjAwMjIwMDU2YTIyMDU0MTAzNzEyMjAxMDQ0MDIwMDY0MTAwNGMwZDAxMjAwNTQxN2M3MTIyMDc0MTA0NmEyMTAyNDEwMDIwMDE0MTAzNzQyMjA4NmI0MTE4NzEyMTA5MjAwNzI4MDIwMDIxMDEwMzQwMjAwNDIwMDEyMDA4NzYyMDAyMjgwMjAwMjIwMTIwMDk3NDcyMzYwMjAwMjAwMjQxMDQ2YTIxMDIyMDA0NDEwNDZhMjIwNDIwMDA0OTBkMDAwYjBjMDEwYjIwMDY0MTAwNGMwZDAwMjAwNTIxMDIwMzQwMjAwNDIwMDIyODAyMDAzNjAyMDAyMDAyNDEwNDZhMjEwMjIwMDQ0MTA0NmEyMjA0MjAwMDQ5MGQwMDBiMGIyMDAzNDEwMzcxMjEwMzIwMDUyMDA2NmEyMTAyMGIyMDAzMDQ0MDIwMDAyMDAzNmEyMTAxMDM0MDIwMDAyMDAyMmQwMDAwM2EwMDAwMjAwMjQxMDE2YTIxMDIyMDAwNDEwMTZhMjIwMDIwMDE0OTBkMDAwYjBiMGYwYjEwODYwMTAwMGI1NTAxMDE3ZjIwMDAyZDAwMDQyMTAxMjAwMDQxMDAzYTAwMDQwMjQwMjAwMTQxMDE3MTA0NDA0MTk0ODVjMDAwMjgwMjAwMjIwMTQxOTFjZTAwNGYwZDAxMjAwMDI4MDIwMDQxOTg4NWMwMDAyMDAxMTAwNzFhNDE5NDg1YzAwMDQxMDAzNjAyMDA0MWE4ZDNjMDAwNDEwMDNhMDAwMDBiMGYwYjEwMjgwMDBiMGQwMDIwMDAxMDMwMjIwMDEwMTgxYTIwMDAwYjNhMDEwMTdmMjMwMDQxMTA2YjIyMDIyNDAwMjAwMjIwMDE0MWZmMDE3MTQxMDI3NDQxYzQ4NGMwMDA2YTI4MDIwMDJkMDAwMDNhMDAwZjIwMDAyMDAyNDEwZjZhNDEwMTEwMDcxYTIwMDI0MTEwNmEyNDAwMGIwODAwMjAwMDEwNTExMDRiMGIwODAwMjAwMDEwNTExMDNkMGIyZjAxMDE3ZTAyN2YwMjQwMjAwMDEwNTYyMjAxNDIwMTU4MDQ0MDQxMDAyMDAxYTc0MTAxNmIwZDAyMWEwYzAxMGI0MThjODFjMDAwNDExMjEwNTcwMDBiNDEwMTBiMGI1ZDAyMDI3ZjAxN2UyMzAwNDExMDZiMjIwMTI0MDAyMDAxNDIwMDM3MDMwODIwMDAxMDUxMjIwMDEwMTIyMjAyNDEwOTRmMDQ0MDQxZmE4MGMwMDA0MTBlMTA1NzAwMGIyMDAxMjAwMTQxMDg2YTIwMDIxMDYyMjAwMDQxMDAyMDAxMjgwMjAwMjIwMDIwMDEyODAyMDQyMjAyMTA0NTFhMjAwMDIwMDIxMDYzMjAwMTQxMTA2YTI0MDAwYjFiMDEwMTdmNDE5ZTgxYzAwMDQxMTYxMDJkMjIwMjIwMDAyMDAxMTAwNzFhMjAwMjEwMTEwMDBiMzcwMTAxN2YyMzAwNDExMDZiMjIwMjI0MDAyMDAyNDIwMDM3MDMwODIwMDIyMDAxYWQ0MTAxMjAwMjQxMDg2YTEwNTkyMDAwMjAwMjI4MDIwMDIwMDIyODAyMDQxMDVhMjAwMjQxMTA2YTI0MDAwYjljMDIwMjA0N2YwMTdlMjAwMzIwMDE0MjI4ODY0MjgwODA4MDgwODA4MGMwZmYwMDgzMjAwMTQyMzg4Njg0MjAwMTQyMTg4NjQyODA4MDgwODA4MGUwM2Y4MzIwMDE0MjA4ODY0MjgwODA4MDgwZjAxZjgzODQ4NDIwMDE0MjA4ODg0MjgwODA4MGY4MGY4MzIwMDE0MjE4ODg0MjgwODBmYzA3ODM4NDIwMDE0MjI4ODg0MjgwZmUwMzgzMjAwMTQyMzg4ODg0ODQ4NDIyMDgzNzAwMDAwMjQwMDI0MDIwMDAwMjdmMjAwMTUwMDQ0MDQxZDA4NGMwMDAyMTAzNDEwMDBjMDEwYjIwMDI0MTAwMjAwMTQyN2Y1MTFiNDUwNDQwNDEwMDIwMDIyMDA4NDI4MDAxODM0MjA3ODhhNzcxMjIwNTZiNDFmZjAxNzEyMTA2MDM0MDIwMDQ0MTA4NDYwZDAzMjAwNjIwMDMyMDA0NmEyZDAwMDAyMjA3NDcwNDQwMjAwNDIwMDIyMDA3NDEwNzc2MjAwNTQ3NzEyMjAyNmI0MTA5NGYwZDA1MjAwMzQxMDAyMDAyNmIyMjAyNmEyMDA0NmEyMTAzNDEwODIwMDIyMDA0NmE2YjBjMDMwNTIwMDQ0MTAxNmEyMTA0MGMwMTBiMDAwYjAwMGIyMDAzNDEwNzZhMjEwMzQxMDEwYjM2MDIwNDIwMDAyMDAzMzYwMjAwMGYwYjEwODUwMTAwMGIxMDg1MDEwMDBiMGQwMDIwMDAyMDAxMjAwMjEwMmQxMDE5MWEwYjBiMDAyMDAwMjAwMTEwNGExMDE5MWEwYjM2MDEwMTdmMjMwMDQxMTA2YjIyMDIyNDAwMjAwMjQyMDAzNzAzMDgyMDAyMjAwMTQxMDAyMDAyNDEwODZhMTA1OTIwMDAyMDAyMjgwMjAwMjAwMjI4MDIwNDEwNWEyMDAyNDExMDZhMjQwMDBiMGUwMDIwMDA0MTY3MTAxODFhNDE2NzEwMTI0NTBiMDkwMDEwMmUxYTIwMDAxMDRhMGIwZDAwMTAyZTFhMjAwMDIwMDExMDI5MTAzYTBiNWEwMjAyN2YwMTdlMjMwMDQxMTA2YjIyMDIyNDAwMTAyZTIxMDMyMDAxNDFmZjAxNzE0MTAyNzQ0MWM0ODRjMDAwNmEyODAyMDAzMTAwMDAyMTA0MjAwMjQyMDAzNzAzMDgyMDAyMjAwNDQxMDAyMDAyNDEwODZhMTA1OTIwMDMyMDAyMjgwMjAwMjAwMjI4MDIwNDEwMGYxYTIwMDAyMDAzMTAzYTIwMDI0MTEwNmEyNDAwMGIxMjAxMDE3ZjEwMmUyMjAyMjAwMDIwMDExMDJkMTAzYTIwMDIwYjM5MDEwMTdmMjMwMDQxMTA2YjIyMDMyNDAwMjAwMzQxMDg2YTIwMDE0MTA4MjAwMjEwNjkyMDAzMjgwMjBjMjEwMTIwMDAyMDAzMjgwMjA4MzYwMjAwMjAwMDIwMDEzNjAyMDQyMDAzNDExMDZhMjQwMDBiMzQwMTAxN2UwMjQwMjAwMTQ1MGQwMDAzNDAyMDAxNDUwZDAxMjAwMTQxMDE2YjIxMDEyMDAwMzEwMDAwMjAwMjQyMDg4Njg0MjEwMjIwMDA0MTAxNmEyMTAwMGMwMDBiMDAwYjIwMDIwYjgwMDEwMTAzN2YyMzAwNDExMDZiMjIwMTI0MDAyMDAwMjgwMjA4MjEwMzIwMDE0MTAwMzYwMjBjMjAwMDI4MDIwMDIwMDM0MTAyNzQyMDAxNDEwYzZhNDEwNDEwNDU0NTA0NDAyMDAxMjgwMjBjMjEwMjIwMDAyMDAzNDEwMTZhMzYwMjA4MjAwMjQxMDg3NDQxODA4MGZjMDc3MTIwMDI0MTE4NzQ3MjIwMDI0MTA4NzY0MTgwZmUwMzcxMjAwMjQxMTg3NjcyNzIxMDI5MjAwMTQxMTA2YTI0MDAwZjBiNDFjNDgxYzAwMDQxMDg0MWJlODBjMDAwNDExMTEwMzcwMDBiZmIwMTAyMDQ3ZjAxN2UyMzAwNDExMDZiMjIwMzI0MDAwMjQwMDI0MDAyNDAwMjQwMjAwMTI4MDIwODIwMDEyODAyMDQ0ZjA0NDAyMDAwNDEwMzNhMDAwNDBjMDEwYjIwMDExMDY0MTAyOTIyMDUxMDEyNDEyMDQ3MGQwMTAyNDAyMDAxMTA2NDIyMDIxMDEyNDUwNDQwNDEwMDIxMDIwYzAxMGIyMDAzNDIwMDM3MDMwODIwMDIxMDEyMjIwNDQxMDk0ZjBkMDQyMDAzMjAwMzQxMDg2YTIwMDQxMDYyMjAwMjQxMDAyMDAzMjgwMjAwMjIwMjIwMDMyODAyMDQyMjA0MTA0NTFhMjAwMjIwMDQxMDYzMjIwNjQyODAwMjVhMGQwNDIwMDZhNzIyMDI0MWZmMDE3MTQxMDM0ZjBkMDMwYjIwMDA0MTA4NmEyMDAxMTA2NDEwNGIzNjAyMDAyMDAwMjAwMjNhMDAwNDIwMDAyMDA1MzYwMjAwMGIyMDAzNDExMDZhMjQwMDBmMGI0MWM0ODFjMDAwNDEwODQxYjQ4MWMwMDA0MTEwMTAzNzAwMGI0MWM0ODFjMDAwNDEwODQxYjQ4NGMwMDA0MTBkMTAzNzAwMGI0MWM0ODFjMDAwNDEwODQxZmE4MGMwMDA0MTBlMTAzNzAwMGI4MDAxMDEwMjdmMjMwMDQxMTA2YjIyMDMyNDAwMDI0MDAyNDAyMDAwMmQwMDA0MDQ0MDQxOTBjZTAwNDE5NDg1YzAwMDI4MDIwMDIyMDQ2YjIwMDI0OTBkMDEyMDAzNDEwODZhMjAwNDIwMDIyMDA0NmEyMjAwMTA2NzIwMDMyODAyMDgyMDAzMjgwMjBjMjAwMTIwMDIxMDRmNDE5NDg1YzAwMDIwMDAzNjAyMDAwYzAyMGIyMDAwMjgwMjAwMjAwMTIwMDIxMDA3MWEwYzAxMGIyMDAwMTA1MDIwMDAyODAyMDAyMDAxMjAwMjEwMDcxYTBiMjAwMzQxMTA2YTI0MDAwYjQwMDEwMTdmMjMwMDQxMTA2YjIyMDMyNDAwMjAwMzQxMDg2YTIwMDEyMDAyNDE5ODg1YzAwMDQxOTBjZTAwMTAyNzIwMDMyODAyMGMyMTAxMjAwMDIwMDMyODAyMDgzNjAyMDAyMDAwMjAwMTM2MDIwNDIwMDM0MTEwNmEyNDAwMGIwZjAwMjAwMDIwMDExMDQzNDFmZjAxNzE0MTAxNDYwYjNiMDEwMTdmMjMwMDQxMTA2YjIyMDQyNDAwMjAwNDQxMDg2YTQxMDAyMDAzMjAwMTIwMDIxMDI3MjAwNDI4MDIwYzIxMDEyMDAwMjAwNDI4MDIwODM2MDIwMDIwMDAyMDAxMzYwMjA0MjAwNDQxMTA2YTI0MDAwYjIxMDEwMTdmNDFjYzgxYzAwMDQxMGExMDYxMjIwMzIwMDAxMDVmMjAwMzIwMDExMDYwMjAwMzIwMDIxMDVlMTAxYzBiMjEwMTAxN2Y0MWQ2ODFjMDAwNDEwYzEwNjEyMjAzMjAwMDEwNWYyMDAzMjAwMTEwNjAyMDAzMjAwMjEwNWUxMDFjMGIyMTAxMDE3ZjQxZTI4MWMwMDA0MTBlMTA2MTIyMDMyMDAwMTA1ZjIwMDMyMDAxMTA2MDIwMDMyMDAyMTA1ZTEwMWMwYjBiMDA0MWYwODFjMDAwNDEwZjEwMmQwYjFlMDEwMTdmNDFmZjgxYzAwMDQxMDkxMDJkMjIwMjIwMDAxMDQ2MjAwMjIwMDEyZDAwMDAxMDUyMjAwMjBiMWUwMTAxN2Y0MTg4ODJjMDAwNDEwNTEwMmQyMjAyMjAwMDEwNDYyMDAyMjAwMTJkMDAwMDEwNTIyMDAyMGIwYjAwNDE4ZDgyYzAwMDQxMDgxMDJkMGIxNjAwMjAwMDEwNGMxMDY4MDQ0MDBmMGI0MTk1ODJjMDAwNDEyMzEwMmIwMDBiMTgwMDIwMDAxMDZkMTA1NDEwMmEwNDQwMGYwYjQxYjg4MmMwMDA0MTFkMTAyYjAwMGIxNTAwMTA2ZDEwNWQ0NTA0NDAwZjBiNDFkNTgyYzAwMDQxMTcxMDJiMDAwYjFkMDAyMDAwMjAwMTEwNDM0MWZmMDE3MTQxMDE0ZDA0NDAwZjBiNDFlYzgyYzAwMDQxMjUxMDJiMDAwYjBlMDAxMDFmNDEwMDEwM2YxMDcwNDEwMTEwNTgwYjc2MDIwNzdmMDE3ZTIzMDA0MTEwNmIyMjAwMjQwMDEwMzQ0MTAyMTAzZjEwM2MyMTAxMjAwMDQxMDExMDNiMjIwNDNhMDAwZjEwNzMyMDAwMTAzNTIwMDAyODAyMDAyMTAyMjAwMDI4MDIwNDIwMDEyMDAwNDEwZjZhMjIwMzEwNmYxMDUzMjEwNjEwMjAyMTA3MTA3MjIwMDIxMDcxMjAwMTIwMDMxMDZmMjAwNjIwMDIxMDQ4MTA1YjIwMDEyMDAzMTA2ZTIwMDcxMDVjMjAwMTIwMDQyMDAyMTA2YTIwMDA0MTEwNmEyNDAwMGI5NjAyMDIwNzdmMDI3ZTIzMDA0MWQwMDA2YjIyMDAyNDAwMTAzNDEwNDExMDQwMjAwMDQxMDAzNjAyNDAyMDAwNDE0MDZiMjIwMTEwMzgyMTAyMjAwMDI4MDI0MDEwM2UxMDczMjAwMDQxMDg2YTEwMzUyMDAwMjgwMjA4MjEwNDIwMDAyODAyMGMxMDIwMjEwNzEwNzIyMDA0MTA3MTEwNGMyMTA1MjAwMDQxMTA2YTIwMDIxMDQyMjAwMDQxMjg2YTIwMDA0MTE4NmEyODAyMDAzNjAyMDAyMDAwMjAwMDI5MDMxMDM3MDMyMDIwMDE0MTA0NzIyMTAyMDM0MDAyNDAyMDAwNDEzMDZhMjAwMDQxMjA2YTEwNjUyMDAwMmQwMDM0NDEwMzQ2MDQ0MDIwMDUyMDA0MTA0NDQxZmYwMTcxMGQwMTIwMDA0MWQwMDA2YTI0MDAwZjA1MjAwMDQxYzgwMDZhMjIwMTIwMDA0MTM4NmEyODAyMDAzNjAyMDAyMDAwMjAwMDI5MDMzMDIyMDgzNzAzNDAyMDA4YTcyMjAzMjAwMjEwNmYxMDUzMjEwNjIwMDMyMDAyMTA2ZjIwMDYyMDAxMjgwMjAwMjIwMTEwNDgxMDViMjAwMzIwMDIxMDZlMjAwNzEwNWMyMDA1MjAwMTEwNDcyMDAzMjAwMDJkMDA0NDIwMDExMDZhMGMwMjBiMDAwYjBiNDE5MTgzYzAwMDQxMjYxMDJiMDAwYmNlMDIwMTA1N2YyMzAwNDExMDZiMjIwMjI0MDAxMDFmMTA0MTEwNDAyMDAyNDEwMDM2MDIwODIwMDI0MTA4NmEyMjAwMjgwMjAwNDFhY2QzYzAwMDI4MDIwMDQ4MDQ3ZjIwMDA0MTlkODRjMDAwNDEwYTEwMzkxMDNiMDU0MTAzMGIyMTAxMjAwMjI4MDIwODEwM2UwMjQwMDI0MDAyNDAxMDcwMTA1NTQ1MDQ0MDEwNzMxMDZkMTA1NDIxMDQxMDMyMjEwMDEwNGMyMTAzMjAwMTQxZmYwMTcxNDEwMzQ3MDQ0MDIwMDIyMDAxM2EwMDBmMjAwMDIwMDI0MTBmNmExMDZmMTA1MzIyMDMxMDcxMjAwMDIwMDI0MTBmNmExMDZmMTA0YzEwNWIyMDAwMjAwMTIwMDMxMDZjMGMwNDBiMjAwMDQxYzk4M2MwMDAxMDZmMTA1MzIyMDExMDRjMTA2ODBkMDEwYzAyMGI0MWI3ODNjMDAwNDExMjEwMmIwMDBiMjAwMzIwMDExMDQ3MjAwMDQxMDAyMDAxMTA2YzIwMDA0MWM5ODNjMDAwMTA2ZjEwNGMxMDViMGIyMDAwNDFjYTgzYzAwMDEwNmYxMDUzMjIwMTEwNGMxMDY4MDQ0MDIwMDMyMDAxMTA0NzIwMDA0MTAxMjAwMTEwNmMyMDAwNDFjYTgzYzAwMDEwNmYxMDRjMTA1YjBiMjAwMDQxY2I4M2MwMDAxMDZmMTA1MzIyMDExMDRjMTA2ODA0NDAyMDAzMjAwMTEwNDcyMDAwNDEwMjIwMDExMDZjMjAwMDQxY2I4M2MwMDAxMDZmMTA0YzEwNWIwYjIwMDMxMDcxMGIyMDAwMjAwNDIwMDMxMDJjMjAwMjQxMTA2YTI0MDAwYjE2MDAxMDFmNDEwMDEwM2Y0MThkODJjMDAwNDEwODEwMmQxMDU1YWQxMDIxMGIyMTAwMTAxZjEwMzQ0MTAwMTAzZjEwNzAxMDU1MDQ0MDQxZTc4M2MwMDA0MTFhMTAyYjAwMGIxMDcwNDEwMTEwNTgwYjgwMDEwMjA4N2YwMTdlMjMwMDQxMTA2YjIyMDIyNDAwMTAxZjEwMzQ0MTAzMTAzZjEwM2MyMTAxNDEwMTEwM2IyMTAzNDEwMjEwMzAyMjAwMTAxMzIwMDIyMDAzM2EwMDBmMTA3MzIwMDEyMDAyNDEwZjZhMjIwNDEwNmYxMDUzMjEwNTEwMzMxMDZkMTA1NDEwMjAyMTA4MjAwMDEwNzEyMDA1MjAwMDEwNzQyMDAxMjAwNDEwNmYyMDA1MjAwMDEwNDkxMDViMjAwMTIwMDQxMDZlMjAwODEwNWMyMDAxMjAwMzIwMDAxMDZiMjAwMDEwMmMyMDAyNDExMDZhMjQwMDBiZjIwMTAyMDY3ZjAyN2UyMzAwNDE0MDZhMjIwMDI0MDAxMDFmMTAzNDEwNDExMDQwMjAwMDQxMDAzNjAyMzAyMDAwNDEzMDZhMjIwMjEwMzgyMTAxMjAwMDI4MDIzMDEwM2UxMDczMTA0YzIxMDQxMDIwMjEwNjIwMDAyMDAxMTA0MjIwMDA0MTE4NmEyMDAwNDEwODZhMjgwMjAwMzYwMjAwMjAwMDIwMDAyOTAzMDAzNzAzMTAyMDAyNDEwNDcyMjEwMjAzNDAyMDAwNDEyMDZhMjAwMDQxMTA2YTEwNjUyMDAwMmQwMDI0NDEwMzQ2NDUwNDQwMjAwMDQxMzg2YTIyMDEyMDAwNDEyODZhMjgwMjAwMzYwMjAwMjAwMDIwMDAyOTAzMjAyMjA3MzcwMzMwMjAwN2E3MjIwMzIwMDIxMDZmMTA1MzIxMDUyMDAzMjAwMjEwNmUyMDA2MTA1YzIwMDEyODAyMDAyMjAxMTA3MTIwMDUyMDAxMTA3NDIwMDQyMDAxMTA0NzIwMDMyMDAyMTA2ZjIwMDUyMDAxMTA0OTEwNWIyMDAzMjAwMDJkMDAzNDIwMDExMDZiMGMwMTBiMGIxMDMzMTA2ZDEwNTQyMDA0MTAyYzIwMDA0MTQwNmIyNDAwMGI0NTAxMDI3ZjEwMWYxMDM0NDEwMTEwM2Y0MTAwMTAzNjEwM2QyMTAwMDI0MDEwNmQxMDVkMDQ0MDEwNmQyMTAxMjAwMDEwMmYwZDAxMjAwMTIwMDAxMDE5MWEwZjBiNDFjYzgzYzAwMDQxMWIxMDJiMDAwYjIwMDE0MTg4ODFjMDAwNDEwNDEwNWEwYjIyMDAxMDFmMTAzNDQxMDAxMDNmMTA3MDEwNTU0NTA0NDA0MTgxODRjMDAwNDExYzEwMmIwMDBiMTA3MDQxMDAxMDU4MGIzMDAxMDM3ZjEwMWY0MTAyMTAzZjEwM2MyMTAxNDEwMTEwM2IyMTAyNDE4ODgyYzAwMDQxMDUxMDJkMjIwMDIwMDExMDAyMWEyMDAwMjAwMjEwNTIyMDAwMTA1MzEwMjIwYjMwMDEwMzdmMTAxZjQxMDIxMDNmMTAzYzIxMDE0MTAxMTAzYjIxMDI0MWZmODFjMDAwNDEwOTEwMmQyMjAwMjAwMTEwMDIxYTIwMDAyMDAyMTA1MjIwMDAxMDU2MTAyMzBiYTUwNTAyMDg3ZjAxN2UyMzAwNDEzMDZiMjIwMDI0MDAxMDFmNDEwMTIxMDQ0MTAxMTAzZjEwM2MyMTAxMjAwMDEwMmUyMjA1MzYwMjIwMjAwMTQxYzk4M2MwMDAxMDZmMTA1MzIxMDIyMDAwNDEyMDZhMjIwMzIwMDE0MWM5ODNjMDAwMTA2ZTEwNTYyMDAyMTA0ZDIwMDE0MWNhODNjMDAwMTA2ZjEwNTMyMTAyMjAwMzIwMDE0MWNhODNjMDAwMTA2ZTEwNTYyMDAyMTA0ZDIwMDE0MWNiODNjMDAwMTA2ZjEwNTMyMTAyMjAwMzIwMDE0MWNiODNjMDAwMTA2ZTEwNTYyMDAyMTA0ZDAyN2Y0MWE4ZDNjMDAwMmQwMDAwNDUwNDQwNDFhOGQzYzAwMDQxMDEzYTAwMDA0MTk0ODVjMDAwNDEwMDM2MDIwMDIwMDA0MTEwNmE0MTk4ODVjMDAwNDE5MGNlMDA0MTAwMTA2OTIwMDAyODAyMTAyMDAwMjgwMjE0NDFkMDg0YzAwMDQxMDAxMDRmMTAyZTBjMDEwYjQxMDAyMTA0NDFkMDg0YzAwMDQxMDAxMDJkMGIyMTAxMjAwMDIwMDQzYTAwMWMyMDAwMjAwMTM2MDIxODIwMDUxMDEyMjEwNjQxMDAyMTAxMDM0MDIwMDE0MTBjNmEyMjA0MjAwNjRiNDUwNDQwMjAwMDQxMjg2YTQxMDAzNjAyMDAyMDAwNDIwMDM3MDMyMDIwMDUyMDAxMjAwMDQxMjA2YTIyMDM0MTBjMTA0NTFhMjAwMDI5MDIyNDIxMDgyMDAwMjAwMDI4MDIyMDIyMDE0MTE4NzQyMDAxNDEwODc0NDE4MDgwZmMwNzcxNzIyMDAxNDEwODc2NDE4MGZlMDM3MTIwMDE0MTE4NzY3MjcyMTA0YTIyMDExMDEyMjIwMjQxMTg3NDIwMDI0MTA4NzQ0MTgwODBmYzA3NzE3MjIwMDI0MTA4NzY0MTgwZmUwMzcxMjAwMjQxMTg3NjcyNzIzNjAyMjAyMDAwNDExODZhMjAwMzQxMDQxMDY2MjAwMDJkMDAxYzIxMDIyMDAwNDEwMDNhMDAxYzAyNDAwMjQwMDI0MDIwMDI0MTAxNzEyMjAyMDQ0MDIwMDExMDEyMjIwNzQxOTBjZTAwNDE5NDg1YzAwMDI4MDIwMDIyMDM2YjRiMGQwMjIwMDA0MTA4NmEyMDAzMjAwMzIwMDc2YTIyMDMxMDY3MjAwMTQxMDAyMDAwMjgwMjA4MjAwMDI4MDIwYzEwNDUxYTQxOTQ4NWMwMDAyMDAzMzYwMjAwMGMwMTBiMjAwMDI4MDIxODIwMDExMDAyMWEwYjIwMDAyMDAyM2EwMDFjMGMwMTBiMjAwMDQxMTg2YTEwNTAyMDAwMjgwMjE4MjAwMTEwMDIxYTIwMDAyZDAwMWMyMDAwMjAwMjNhMDAxYzQxMDE3MTQ1MGQwMDQxOTQ4NWMwMDA0MTAwMzYwMjAwNDFhOGQzYzAwMDQxMDAzYTAwMDAwYjIwMDAyMDA4MzcwMzIwMjAwMDQxMTg2YTIwMDA0MTIwNmE0MTA4MTA2NjIwMDQyMTAxMGMwMTBiMGIyMDAwMjgwMjE4MjEwMTIwMDAyMDAwMmQwMDFjM2EwMDI0MjAwMDIwMDEzNjAyMjAyMDAwNDEyMDZhMTA1MDIwMDAyODAyMjAyMDAwMmQwMDI0MDQ0MDQxOTQ4NWMwMDA0MTAwMzYwMjAwNDFhOGQzYzAwMDQxMDAzYTAwMDAwYjEwMjQxYTIwMDA0MTMwNmEyNDAwMGI0MTAxMDI3ZjEwMWY0MTAxMTAzZjEwM2MyMTAwMTA0YzIyMDEyMDAwNDFjOTgzYzAwMDEwNmYxMDUzMTA0NzIwMDEyMDAwNDFjYTgzYzAwMDEwNmYxMDUzMTA0NzIwMDEyMDAwNDFjYjgzYzAwMDEwNmYxMDUzMTA0NzIwMDExMDIyMGIyYzAxMDE3ZjEwMWY0MTAwMTAzZjQxZjA4MWMwMDA0MTBmMTAyZDEwNTQyMjAwMTAyZjQ1MDQ0MDIwMDAxMDI0MWEwZjBiNDE4ODgxYzAwMDQxMDQxMDI1MGIwMzAwMDEwYjBjMDA0MTgwODVjMDAwNDEwZTEwMDAwMDBiMDYwMDEwODUwMTAwMGIwYmE0MDUwMjAwNDE4MDgwYzAwMDBiOGUwNTQ1NmU2NDcwNmY2OTZlNzQyMDYzNjE2ZTIwNmY2ZTZjNzkyMDYyNjUyMDYzNjE2YzZjNjU2NDIwNjI3OTIwNmY3NzZlNjU3MjYxNzI2Nzc1NmQ2NTZlNzQyMDY0NjU2MzZmNjQ2NTIwNjU3MjcyNmY3MjIwMjgyOTNhMjA3NDZmNmYyMDY2NjU3NzIwNjE3MjY3NzU2ZDY1NmU3NDczNzQ2ZjZmMjA2ZDYxNmU3OTIwNjE3MjY3NzU2ZDY1NmU3NDczNzc3MjZmNmU2NzIwNmU3NTZkNjI2NTcyMjA2ZjY2MjA2MTcyNjc3NTZkNjU2ZTc0NzM2OTZlNzA3NTc0MjA3NDZmNmYyMDZjNmY2ZTY3NDU0NzRjNDQ2OTZlNzA3NTc0MjA2Zjc1NzQyMDZmNjYyMDcyNjE2ZTY3NjU3Mzc0NmY3MjYxNjc2NTIwNjQ2NTYzNmY2NDY1MjA2NTcyNzI2ZjcyM2EyMDYyNjE2NDIwNjE3MjcyNjE3OTIwNmM2NTZlNjc3NDY4NzY2MTcyMjA2MTcyNjc3MzYzNmM2MTY5NmQ0MTY0NjQ2NTY0NjM2YzYxNjk2ZDUyNjU2ZDZmNzY2NTY0NjM2YzYxNjk2ZDQzNmY2YzZjNjU2Mzc0NjU2NDc0NmY2YjY1NmU0OTY0NjU2ZTc0Njk2NjY5NjU3MjYzNmM2MTY5NmQ0NDYxNzQ2NTYzNmM2MTY5NmQ2OTczNTA2MTc1NzM2NTY0NGY3MDY1NzI2MTc0Njk2ZjZlNzMyMDZkNzU3Mzc0MjA2ODYxNzY2NTIwNmU2ZjZlMmQ3YTY1NzI2ZjIwNzY2MTZjNzU2NTQzNjE2ZTIwNmY2ZTZjNzkyMDYxNjQ2NDIwNjQ2NTczNjk2NzZlNjE3NDY1NjQyMDc0NmY2YjY1NmU1MjY1Nzc2MTcyNjQyMDc0NmY2YjY1NmUyMDY5NzMyMDZlNmY3NDIwNzM2NTc0NDM2MTZlNmU2Zjc0MjA3MjY1NmQ2Zjc2NjUyMDZkNmY3MjY1MjA3NDY4NjE2ZTIwNjM3NTcyNzI2NTZlNzQyMDYzNmM2MTY5NmQ0MzZjNjE2OTZkNzMyMDYxNjQ2NDY1NjQyMDZkNzU3Mzc0MjA2NTcxNzU2MTZjMjA3MDYxNzk2ZDY1NmU3NDIwNjE2ZDZmNzU2ZTc0NDM2ZjZlNzQ3MjYxNjM3NDIwNjk3MzIwNzA2MTc1NzM2NTY0MDAwMTAyNTI2NTc3NjE3MjY0MjA3NDZmNmI2NTZlMjA2OTczMjA2MTZjNzI2NTYxNjQ3OTIwNzM2NTc0NDM2ZjZlNzQ3MjYxNjM3NDIwNjk3MzIwNjE2YzcyNjU2MTY0NzkyMDcwNjE3NTczNjU2NDQzNmY2ZTc0NzI2MTYzNzQyMDY5NzMyMDYxNmM3MjY1NjE2NDc5MjA3NTZlNzA2MTc1NzM2NTY0NjM2YzYxNjk2ZDVmNzQ3OTcwNjU2MTY0NjQ3MjY1NzM3MzYzNmM2MTY5NmQ3MzY5NmU3NjYxNmM2OTY0MjA3NjYxNmM3NTY1MDAwMDAwYzkwMTEwMDBjYTAxMTAwMGNiMDExMDAwNjM2MTZlNmU2Zjc0MjA3Mzc1NjI3NDcyNjE2Mzc0MjA2MjY1NjM2MTc1NzM2NTIwNzI2NTczNzU2Yzc0MjA3NzZmNzU2YzY0MjA2MjY1MjA2ZTY1Njc2MTc0Njk3NjY1NzA2MTZlNjk2MzIwNmY2MzYzNzU3MjcyNjU2NDAwNDE5MDg1YzAwMDBiMDQ5Y2ZmZmZmZkAwNTAwQDA1MDI=", - "chainID": "D", - "version": 1, - "signature": "614598914feaee64f84d0913dc6fa4529c29999cd0c76f5221f4d6740e8dc1eddf3a8666eb8064a902dd271ea1f713d708ad62d3967afe95b161b531de61c30e" - }, - "emittedTransactionData": "0061736d01000000016e1360000060017f017f60027f7f017f60027f7f0060017f006000017f60037f7f7f0060047f7f7f7f0060037f7f7f017f60057f7f7e7f7f017f60027f7e0060017f017e60017e0060047f7f7f7f017f6000017e60057f7f7f7f7f0060037f7e7f0060047f7e7f7f0060027f7f017e02a8072703656e760b7369676e616c4572726f72000303656e760a6d4275666665724e6577000503656e760d6d427566666572417070656e64000203656e76096d4275666665724571000203656e761b6d616e616765645472616e7366657256616c756545786563757465000903656e7609626967496e74416464000603656e760a626967496e745369676e000103656e76126d427566666572417070656e644279746573000803656e76226d616e616765644d756c74695472616e73666572455344544e465445786563757465000903656e760d6d616e6167656443616c6c6572000403656e76136d616e616765644f776e657241646472657373000403656e76136765744e756d455344545472616e7366657273000503656e7612626967496e7447657443616c6c56616c7565000403656e7616626967496e744765744553445443616c6c56616c7565000403656e761067657445534454546f6b656e4e616d65000103656e760f6d4275666665725365744279746573000803656e76126d427566666572476574417267756d656e74000203656e76126d616e616765645369676e616c4572726f72000403656e76106d4275666665724765744c656e677468000103656e7619626967496e74476574556e7369676e6564417267756d656e74000303656e760f6765744e756d417267756d656e7473000503656e7609626967496e74537562000603656e76196d42756666657246726f6d426967496e74556e7369676e6564000203656e76176d427566666572546f426967496e74556e7369676e6564000203656e76126d42756666657253746f726167654c6f6164000203656e76136d42756666657253746f7261676553746f7265000203656e760e626967496e74536574496e743634000a03656e7609626967496e74436d70000203656e760f6d616e6167656457726974654c6f67000303656e7611676574417267756d656e744c656e677468000103656e761b736d616c6c496e74476574556e7369676e6564417267756d656e74000b03656e760e636865636b4e6f5061796d656e74000003656e7611676574426c6f636b54696d657374616d70000e03656e7614736d616c6c496e7446696e6973685369676e6564000c03656e7614626967496e7446696e697368556e7369676e6564000403656e7616736d616c6c496e7446696e697368556e7369676e6564000c03656e760d6d42756666657246696e697368000103656e760666696e697368000303656e76136d42756666657247657442797465536c696365000d0361600f00010203060205010504050500040107010803010501040400000302020d030302020101051007070401030101010b03031106030a01010303020612010306060207060606050202050404000300000000000000000000000000000000000005030100110619037f01418080c0000b7f0041b0d3c0000b7f0041b0d3c0000b07fc0113066d656d6f7279020004696e6974007508616464436c61696d007609616464436c61696d73007705636c61696d00780869735061757365640079057061757365007a0b72656d6f7665436c61696d007b0c72656d6f7665436c61696d73007c0e736574526577617264546f6b656e007d07756e7061757365007e0976696577436c61696d007f1076696577436c61696d416464446174650080011176696577436c61696d57697468446174650081010a76696577436c61696d730082011376696577546f6b656e4964656e7469666965720083010863616c6c4261636b0084010a5f5f646174615f656e6403010b5f5f686561705f6261736503020ae42f602e000240200120024d0440200220044d0d011028000b1028000b2000200220016b3602042000200120036a3602000b0600108601000b0f01017f10012201200010021a20010b0b0020002001100341004a0b0900200020011000000bcb0101057f230041106b2203240041d084c0004100102d2105102e210602402001102f04402000200242002005200610041a0c010b102e210720011029210410302201103120012001200210052001100604402004102f1a0b2003420037020420032001410874418080fc077120014118747220014108764180fe03712001411876727236020c20032004410874418080fc077120044118747220044108764180fe03712004411876727236020020072003411010071a2000200742002005200610081a0b200341106a24000b1101017f1030220220002001100f1a20020b1401017f1030220041d084c0004100100f1a20000b070020001012450b1b01017f419085c000419085c00028020041016b220036020020000b080020004200101a0b0c01017f10302200100920000b0c01017f10302200100a20000b160010331032102a04400f0b418080c00041241000000ba90101037f230041206b220124000240024002400240100b450440417521024175100c0c010b417321024173100d100b0d010b102e21030c010b200141186a4200370300200141106a4200370300200141086a42003703002001420037030002402001100e22024504401030220341d084c0004100100f1a0c010b200241214f0d0220012002102d21030b417321020b2000200336020420002002360200200141206a24000f0b1028000b0d0020001030220010101a20000b3001017f41a480c0004117102d22042000200110071a200441bb80c000410310071a20042002200310071a20041011000b3201017f102e21010340200028020041acd3c0002802004e4504402001200041ae84c000410610391036103a0c010b0b20010b3101017f2000280200220341acd3c0002802004e04402001200241be80c00041111037000b2000200341016a36020020030b4701017f230041106b2202240020022001410874418080fc077120014118747220014108764180fe03712001411876727236020c20002002410c6a410410071a200241106a24000b5201017e2000101d45044041000f0b02402000101e22014280025404402001a7220041ff017141034f0d0120000f0b419d84c000410a41fa80c000410e1037000b419d84c000410a41b484c000410d1037000b2501017f4100103622001012412047044041a784c000410741b481c00041101037000b20000b5101017f230041106b220124000240200010124104470d002001410036020c200041002001410c6a410410451a200128020c41c58eb1a204470d00200041d084c0004100100f1a0b200141106a240020000b1a00200041acd3c00028020048044041cf80c00041121000000b0b1500101420004604400f0b41e180c00041191000000b1b0041acd3c00028020041004e04400f0b41be80c00041111000000b0c0041acd3c00010143602000b2201017f2001101221022000410036020820002001360200200020024102763602040b08002000200110440b1500417f20002001101b220041004720004100481b0b0f00200020012003200210264100470b09002000200110021a0b0a0020002000200110050b0c00200020002001100520000b2000200020002001101520001006410048044041d084c00041301000000b20000b0f01017f10302201200010161a20010b0d0020001030220010171a20000b0c01017f10302200103120000bfe0101027f230041306b22032400200341206a41003602002003420037031820032002410874418080fc077120024118747220024108764180fe037120024118767272360228200341106a200341186a220241004104104e20032802102003280214200341286a22044104104f2003200142288642808080808080c0ff00832001423886842001421886428080808080e03f8320014208864280808080f01f838484200142088842808080f80f832001421888428080fc07838420014228884280fe03832001423888848484370328200341086a20024104410c104e2003280208200328020c20044108104f20002802002002410c10071a200341306a24000b3b01017f230041106b22042400200441086a200220032001410c1027200428020c21012000200428020836020020002001360204200441106a24000bb10201067f2001200346044020012203410f4b04402000410020006b41037122056a210420050440200221010340200020012d00003a0000200141016a2101200041016a22002004490d000b0b2004200320056b2203417c7122066a21000240200220056a220541037122010440200641004c0d012005417c71220741046a21024100200141037422086b4118712109200728020021010340200420012008762002280200220120097472360200200241046a2102200441046a22042000490d000b0c010b200641004c0d0020052102034020042002280200360200200241046a2102200441046a22042000490d000b0b20034103712103200520066a21020b20030440200020036a21010340200020022d00003a0000200241016a2102200041016a22002001490d000b0b0f0b108601000b5501017f20002d00042101200041003a0004024020014101710440419485c00028020022014191ce004f0d012000280200419885c000200110071a419485c000410036020041a8d3c00041003a00000b0f0b1028000b0d0020001030220010181a20000b3a01017f230041106b220224002002200141ff017141027441c484c0006a2802002d00003a000f20002002410f6a410110071a200241106a24000b080020001051104b0b080020001051103d0b2f01017e027f0240200010562201420158044041002001a741016b0d021a0c010b418c81c00041121057000b41010b0b5d02027f017e230041106b22012400200142003703082000105122001012220241094f044041fa80c000410e1057000b2001200141086a2002106220004100200128020022002001280204220210451a200020021063200141106a24000b1b01017f419e81c0004116102d22022000200110071a20021011000b3701017f230041106b220224002002420037030820022001ad4101200241086a1059200020022802002002280204105a200241106a24000b9c0202047f017e2003200142288642808080808080c0ff00832001423886842001421886428080808080e03f8320014208864280808080f01f838484200142088842808080f80f832001421888428080fc07838420014228884280fe038320014238888484842208370000024002402000027f200150044041d084c000210341000c010b200241002001427f511b45044041002002200842800183420788a77122056b41ff01712106034020044108460d032006200320046a2d000022074704402004200220074107762005477122026b41094f0d052003410020026b22026a20046a21034108200220046a6b0c0305200441016a21040c010b000b000b200341076a210341010b360204200020033602000f0b108501000b108501000b0d00200020012002102d10191a0b0b0020002001104a10191a0b3601017f230041106b2202240020024200370308200220014100200241086a1059200020022802002002280204105a200241106a24000b0e002000416710181a41671012450b0900102e1a2000104a0b0d00102e1a200020011029103a0b5a02027f017e230041106b22022400102e2103200141ff017141027441c484c0006a280200310000210420024200370308200220044100200241086a1059200320022802002002280204100f1a20002003103a200241106a24000b1201017f102e220220002001102d103a20020b3901017f230041106b22032400200341086a2001410820021069200328020c21012000200328020836020020002001360204200341106a24000b3401017e02402001450d0003402001450d01200141016b210120003100002002420886842102200041016a21000c000b000b20020b800101037f230041106b22012400200028020821032001410036020c200028020020034102742001410c6a41041045450440200128020c21022000200341016a3602082002410874418080fc077120024118747220024108764180fe0371200241187672721029200141106a24000f0b41c481c000410841be80c00041111037000bfb0102047f017e230041106b220324000240024002400240200128020820012802044f0440200041033a00040c010b200110641029220510124120470d0102402001106422021012450440410021020c010b2003420037030820021012220441094f0d042003200341086a2004106220024100200328020022022003280204220410451a20022004106322064280025a0d042006a7220241ff017141034f0d030b200041086a20011064104b360200200020023a0004200020053602000b200341106a24000f0b41c481c000410841b481c00041101037000b41c481c000410841b484c000410d1037000b41c481c000410841fa80c000410e1037000b800101027f230041106b220324000240024020002d000404404190ce00419485c00028020022046b2002490d01200341086a2004200220046a220010672003280208200328020c20012002104f419485c00020003602000c020b20002802002001200210071a0c010b2000105020002802002001200210071a0b200341106a24000b4001017f230041106b22032400200341086a20012002419885c0004190ce001027200328020c21012000200328020836020020002001360204200341106a24000b0f0020002001104341ff01714101460b3b01017f230041106b22042400200441086a41002003200120021027200428020c21012000200428020836020020002001360204200441106a24000b2101017f41cc81c000410a106122032000105f20032001106020032002105e101c0b2101017f41d681c000410c106122032000105f20032001106020032002105e101c0b2101017f41e281c000410e106122032000105f20032001106020032002105e101c0b0b0041f081c000410f102d0b1e01017f41ff81c0004109102d220220001046200220012d0000105220020b1e01017f418882c0004105102d220220001046200220012d0000105220020b0b00418d82c0004108102d0b16002000104c106804400f0b419582c0004123102b000b18002000106d1054102a04400f0b41b882c000411d102b000b1500106d105d4504400f0b41d582c0004117102b000b1d0020002001104341ff017141014d04400f0b41ec82c0004125102b000b0e00101f4100103f1070410110580b7602077f017e230041106b2200240010344102103f103c210120004101103b22043a000f10732000103520002802002102200028020420012000410f6a2203106f105321061020210710722002107120012003106f200620021048105b20012003106e2007105c200120042002106a200041106a24000b960202077f027e230041d0006b2200240010341041104020004100360240200041406b2201103821022000280240103e1073200041086a103520002802082104200028020c10202107107220041071104c2105200041106a20021042200041286a200041186a280200360200200020002903103703202001410472210203400240200041306a200041206a106520002d0034410346044020052004104441ff01710d01200041d0006a24000f05200041c8006a2201200041386a2802003602002000200029033022083703402008a722032002106f1053210620032002106f2006200128020022011048105b20032002106e2007105c200520011047200320002d00442001106a0c020b000b0b419183c0004126102b000bce0201057f230041106b22022400101f1041104020024100360208200241086a220028020041acd3c00028020048047f2000419d84c000410a1039103b0541030b21012002280208103e024002400240107010554504401073106d1054210410322100104c2103200141ff01714103470440200220013a000f20002002410f6a106f10532203107120002002410f6a106f104c105b200020012003106c0c040b200041c983c000106f10532201104c10680d010c020b41b783c0004112102b000b200320011047200041002001106c200041c983c000106f104c105b0b200041ca83c000106f10532201104c10680440200320011047200041012001106c200041ca83c000106f104c105b0b200041cb83c000106f10532201104c10680440200320011047200041022001106c200041cb83c000106f104c105b0b200310710b200020042003102c200241106a24000b1600101f4100103f418d82c0004108102d1055ad10210b2100101f10344100103f10701055044041e783c000411a102b000b1070410110580b800102087f017e230041106b22022400101f10344103103f103c21014101103b21034102103022001013200220033a000f107320012002410f6a2204106f105321051033106d1054102021082000107120052000107420012004106f200520001049105b20012004106e2008105c200120032000106b2000102c200241106a24000bf20102067f027e230041406a22002400101f10341041104020004100360230200041306a2202103821012000280230103e1073104c210410202106200020011042200041186a200041086a28020036020020002000290300370310200241047221020340200041206a200041106a106520002d0024410346450440200041386a2201200041286a2802003602002000200029032022073703302007a722032002106f1053210520032002106e2006105c20012802002201107120052001107420042001104720032002106f200520011049105b200320002d00342001106b0c010b0b1033106d10542004102c200041406b24000b4501027f101f10344101103f41001036103d21000240106d105d0440106d21012000102f0d012001200010191a0f0b41cc83c000411b102b000b2001418881c0004104105a0b2200101f10344100103f10701055450440418184c000411c102b000b1070410010580b3001037f101f4102103f103c21014101103b2102418882c0004105102d2200200110021a2000200210522000105310220b3001037f101f4102103f103c21014101103b210241ff81c0004109102d2200200110021a2000200210522000105610230ba50502087f017e230041306b22002400101f410121044101103f103c21012000102e2205360220200141c983c000106f10532102200041206a2203200141c983c000106e10562002104d200141ca83c000106f105321022003200141ca83c000106e10562002104d200141cb83c000106f105321022003200141cb83c000106e10562002104d027f41a8d3c0002d000045044041a8d3c00041013a0000419485c0004100360200200041106a419885c0004190ce00410010692000280210200028021441d084c0004100104f102e0c010b4100210441d084c0004100102d0b2101200020043a001c200020013602182005101221064100210103402001410c6a220420064b450440200041286a41003602002000420037032020052001200041206a2203410c10451a200029022421082000200028022022014118742001410874418080fc07717220014108764180fe037120014118767272104a2201101222024118742002410874418080fc07717220024108764180fe037120024118767272360220200041186a20034104106620002d001c2102200041003a001c0240024002402002410171220204402001101222074190ce00419485c00028020022036b4b0d02200041086a2003200320076a22031067200141002000280208200028020c10451a419485c00020033602000c010b2000280218200110021a0b200020023a001c0c010b200041186a10502000280218200110021a20002d001c200020023a001c410171450d00419485c000410036020041a8d3c00041003a00000b20002008370320200041186a200041206a41081066200421010c010b0b20002802182101200020002d001c3a002420002001360220200041206a1050200028022020002d00240440419485c000410036020041a8d3c00041003a00000b10241a200041306a24000b4101027f101f4101103f103c2100104c2201200041c983c000106f105310472001200041ca83c000106f105310472001200041cb83c000106f10531047200110220b2c01017f101f4100103f41f081c000410f102d10542200102f450440200010241a0f0b418881c000410410250b0300010b0c00418085c000410e1000000b0600108501000b0ba4050200418080c0000b8e05456e64706f696e742063616e206f6e6c792062652063616c6c6564206279206f776e6572617267756d656e74206465636f6465206572726f722028293a20746f6f2066657720617267756d656e7473746f6f206d616e7920617267756d656e747377726f6e67206e756d626572206f6620617267756d656e7473696e70757420746f6f206c6f6e6745474c44696e707574206f7574206f662072616e676573746f72616765206465636f6465206572726f723a20626164206172726179206c656e6774687661722061726773636c61696d4164646564636c61696d52656d6f766564636c61696d436f6c6c6563746564746f6b656e4964656e746966696572636c61696d44617465636c61696d69735061757365644f7065726174696f6e73206d7573742068617665206e6f6e2d7a65726f2076616c756543616e206f6e6c79206164642064657369676e6174656420746f6b656e52657761726420746f6b656e206973206e6f742073657443616e6e6f742072656d6f7665206d6f7265207468616e2063757272656e7420636c61696d436c61696d73206164646564206d75737420657175616c207061796d656e7420616d6f756e74436f6e74726163742069732070617573656400010252657761726420746f6b656e20697320616c726561647920736574436f6e747261637420697320616c726561647920706175736564436f6e747261637420697320616c726561647920756e706175736564636c61696d5f7479706561646472657373636c61696d73696e76616c69642076616c7565000000c9011000ca011000cb01100063616e6e6f74207375627472616374206265636175736520726573756c7420776f756c64206265206e6567617469766570616e6963206f6363757272656400419085c0000b049cffffff@0500@0502", - "emittedTransactionHash": "e1971d180adf53bafca974012f6e47418c67ef38280f7d2c2542dd8acbe5b0c2", - "contractAddress": "erd1qqqqqqqqqqqqqpgq4srlh59un9c0qqsqcr3mkja2s5gllx6q7yqsr6s5p3" -} From 3b6fdb86f071be815c237bd7f0c301614f6cf05d Mon Sep 17 00:00:00 2001 From: damienen Date: Wed, 27 Jul 2022 23:40:55 +0300 Subject: [PATCH 13/25] Added code commentaries, contributed to issue #12 --- README.md | 2 -- src/events.rs | 4 +++ src/lib.rs | 87 ++++++++++++++++++++++++++++----------------- src/requirements.rs | 21 ++++++++--- src/storage.rs | 12 +++++-- src/views.rs | 12 +++++-- tests/rust_tests.rs | 4 +-- wasm/src/lib.rs | 4 +-- 8 files changed, 98 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 181cc3b..33cf098 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # Itheum Core Elrond - Claims Smart Contract -<<<<<<< HEAD - ## Abstract The claims smart contract is the tool that stands at the heart of collaboration between Itheum and its community. Whether it's a reward for helping the project, an airdrop or some allocation of tokens, the claims smart contract is the tool that allows Itheum to give tokens to all community members that are using the Elrond blockchain. diff --git a/src/events.rs b/src/events.rs index 4ec85b7..53f8584 100644 --- a/src/events.rs +++ b/src/events.rs @@ -3,8 +3,10 @@ elrond_wasm::derive_imports!(); use crate::storage::ClaimType; +//Module that handles event emitting for important smart contract event in order to facilitate logging, debugging and monitoring with ease #[elrond_wasm::module] pub trait EventsModule { + //Emitted whenever the owner adds a new claim to the smart contract #[event("claimAdded")] fn claim_added_event( &self, @@ -13,6 +15,7 @@ pub trait EventsModule { amount: &BigUint, ); + //Emitted whenever the owner removes a claim from the smart contract #[event("claimRemoved")] fn claim_removed_event( &self, @@ -21,6 +24,7 @@ pub trait EventsModule { amount: &BigUint, ); + //Emitted whenever an address harvests a claim from the smart contract #[event("claimCollected")] fn claim_collected_event( &self, diff --git a/src/lib.rs b/src/lib.rs index 8c9ccce..1f20a6c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,21 +17,21 @@ pub trait ClaimsContract: + views::ViewsModule + requirements::RequirementsModule { + //When the smart contract is deployed claim harvesting is paused #[init] fn init(&self) { self.is_paused().set(true); } + //Endpoint available for the owner of the smart contract to set the token used by the smart contract for claims. Can only be called once successfully. #[only_owner] - #[endpoint(setRewardToken)] - fn set_reward_token(&self, token: TokenIdentifier) { - require!( - self.reward_token().is_empty(), - "Reward token is already set" - ); - self.reward_token().set(&token); + #[endpoint(setClaimToken)] + fn set_claim_token(&self, token: TokenIdentifier) { + require!(self.claim_token().is_empty(), "Claim token is already set"); + self.claim_token().set(&token); } + //Endpoint available for the owner of the smart contract to pause claim harvesting. Cannot be called while harvesting is already paused. #[only_owner] #[endpoint(pause)] fn pause(&self) { @@ -39,6 +39,7 @@ pub trait ClaimsContract: self.is_paused().set(true); } + //Endpoint avbailable for the owner of the smart contract to resume claim harvesting. Cannot be called while harvesting is already unpaused. #[only_owner] #[endpoint(unpause)] fn unpause(&self) { @@ -46,22 +47,26 @@ pub trait ClaimsContract: self.is_paused().set(false); } + //Endpoint available for the owner of the smart contract to add a claim of a specific claim type for a specific address. #[only_owner] #[payable("*")] #[endpoint(addClaim)] fn add_claim(&self, address: &ManagedAddress, claim_type: ClaimType) { - self.require_reward_token_is_set(); + self.require_claim_token_is_set(); let (payment_amount, payment_token) = self.call_value().payment_token_pair(); let current_claim = self.claim(address, &claim_type).get(); let timestamp = self.blockchain().get_block_timestamp(); - self.require_token_is_reward(payment_token); + self.require_token_is_correct(payment_token); self.require_value_not_zero(&payment_amount); + //Add the amount of the tokens sent to the current claim reservation self.claim(address, &claim_type) .set(current_claim + &payment_amount); - self.claim_add_date(address, &claim_type).set(timestamp); + //Update the last modification date of the claim to the current timestamp + self.claim_modify_date(address, &claim_type).set(timestamp); self.claim_added_event(address, &claim_type, &payment_amount); } + //Endpoint available for the owner of the smart contract to add a bulk of claims of different claim types for different specific addresses. #[only_owner] #[payable("*")] #[endpoint(addClaims)] @@ -69,65 +74,70 @@ pub trait ClaimsContract: &self, claims: MultiValueEncoded>, ) { - self.require_reward_token_is_set(); - require!( - claims.len() <= 200, - "Exceeded maximum number of claims per operation (200)" - ); + self.require_claim_token_is_set(); + self.require_number_of_claims_in_bulk_is_valid(claims.len()); let (payment_amount, payment_token) = self.call_value().payment_token_pair(); let timestamp = self.blockchain().get_block_timestamp(); - self.require_token_is_reward(payment_token); + self.require_token_is_correct(payment_token); self.require_value_not_zero(&payment_amount); + //Initialize the sum of claims to be added to zero let mut sum_of_claims = BigUint::zero(); + //Iterate over the claims provided as argument and proceeds similarly to the add_claim endpoint for each one for item in claims.into_iter() { let (address, claim_type, amount) = item.into_tuple(); let current_claim = self.claim(&address, &claim_type).get(); self.claim(&address, &claim_type) .set(current_claim + &amount); - self.claim_add_date(&address, &claim_type).set(timestamp); + self.claim_modify_date(&address, &claim_type).set(timestamp); sum_of_claims += &amount; self.claim_added_event(&address, &claim_type, &amount); } + //Panic if the amount of tokens sent by the owner to the endpoint are not equal to the sum of the claims added to the contract require!( sum_of_claims == payment_amount, "Claims added must equal payment amount" ); } + //Endpoint available for the owner of the smart contract to remove a claim of a specific claim type for a specific address. #[only_owner] #[endpoint(removeClaim)] fn remove_claim(&self, address: &ManagedAddress, claim_type: ClaimType, amount: BigUint) { - self.require_reward_token_is_set(); + self.require_claim_token_is_set(); let current_claim = self.claim(address, &claim_type).get(); let owner = self.blockchain().get_owner_address(); - let reward_token = self.reward_token().get(); + let claim_token = self.claim_token().get(); let timestamp = self.blockchain().get_block_timestamp(); self.require_value_not_zero(&amount); self.require_remove_claim_is_valid(¤t_claim, &amount); + //Remove the amount of tokens given as argument from the current claim reservation self.claim(address, &claim_type) .set(current_claim - &amount); - self.claim_add_date(address, &claim_type).set(timestamp); + //Update the modification date of the claim to the current timestamp + self.claim_modify_date(address, &claim_type).set(timestamp); self.claim_removed_event(address, &claim_type, &amount); - self.send().direct(&owner, &reward_token, 0, &amount, &[]); + //Send the removed tokens from the claim back to the owner of the smart contract + self.send().direct(&owner, &claim_token, 0, &amount, &[]); } + //Endpoint available for the owner of the smart contract to remove a bulk of claims of different claim types for different specific addresses. #[only_owner] #[endpoint(removeClaims)] fn remove_claims( &self, claims: MultiValueEncoded>, ) { - self.require_reward_token_is_set(); - require!( - claims.len() <= 200, - "Exceeded maximum number of claims per operation (20)" - ); + self.require_claim_token_is_set(); + //Panics if the user tries to add more than 200 claims per operation. Implemented in order to ensure + self.require_number_of_claims_in_bulk_is_valid(claims.len()); + //Initialize the sum of claims to be removed to zero let mut sum_of_claims = BigUint::zero(); let timestamp = self.blockchain().get_block_timestamp(); + //Iterate over the claims provided as argument and proceeds similarly to the remove_claim endpoint for each one for item in claims.into_iter() { let (address, claim_type, amount) = item.into_tuple(); let current_claim = self.claim(&address, &claim_type).get(); - self.claim_add_date(&address, &claim_type).set(timestamp); + self.claim_modify_date(&address, &claim_type).set(timestamp); self.require_value_not_zero(&amount); self.require_remove_claim_is_valid(¤t_claim, &amount); sum_of_claims += &amount; @@ -136,25 +146,35 @@ pub trait ClaimsContract: self.claim_removed_event(&address, &claim_type, &amount); } let owner = self.blockchain().get_owner_address(); - let reward_token = self.reward_token().get(); + let claim_token = self.claim_token().get(); + //Send the removed tokens from the claim back to the owner of the smart contract self.send() - .direct(&owner, &reward_token, 0, &sum_of_claims, &[]); + .direct(&owner, &claim_token, 0, &sum_of_claims, &[]); } + //Endpoint available for the public to claim tokens reserved for the calling address. Cannot be called while contract is paused for the public/(harvesting is paused). + //Can be given an argument as a claim type to harvest only specific claim type. If the claim_type argument is not provided, all claim types for the calling addresses will be harvested. #[endpoint(claim)] fn harvest_claim(&self, claim_type: OptionalValue) { require!(!self.is_paused().get(), "Contract is paused"); - self.require_reward_token_is_set(); - let reward_token = self.reward_token().get(); + self.require_claim_token_is_set(); + let claim_token = self.claim_token().get(); let caller = self.blockchain().get_caller(); + //Initializes the amount of tokens to be harvested to zero. let mut claim = BigUint::zero(); + //Checks whether the claim type argument is provided. if let OptionalValue::Some(what_type_to_claim) = claim_type { + //Sets claim to the given amount of tokens reserved for the calling address and the given claim type. claim = self.claim(&caller, &what_type_to_claim).get(); self.require_value_not_zero(&claim); + //Resets the reserved tokens for the given claim type of the calling address to zero. self.claim(&caller, &what_type_to_claim) .set(BigUint::zero()); self.claim_collected_event(&caller, &what_type_to_claim, &claim); } else { + //Sets claim to the sum of all reserved tokens for the calling address. + + //Checks claims of the reward type and adds them to the sum if they are not zero. let reward_claim = self.claim(&caller, &ClaimType::Reward).get(); if reward_claim > BigUint::zero() { claim += &reward_claim; @@ -162,6 +182,7 @@ pub trait ClaimsContract: self.claim(&caller, &ClaimType::Reward).set(BigUint::zero()); } + //Checks claims of the airdrop type and adds them to the sum if they are not zero. let airdrop_claim = self.claim(&caller, &ClaimType::Airdrop).get(); if airdrop_claim > BigUint::zero() { claim += &airdrop_claim; @@ -170,6 +191,7 @@ pub trait ClaimsContract: .set(BigUint::zero()); } + //Checks claims of the allocation type and adds them to the sum if they are not zero. let allocation_claim = self.claim(&caller, &ClaimType::Allocation).get(); if allocation_claim > BigUint::zero() { claim += &allocation_claim; @@ -180,6 +202,7 @@ pub trait ClaimsContract: self.require_value_not_zero(&claim); } - self.send().direct(&caller, &reward_token, 0, &claim, &[]); + //Send the amount of tokens harvested (all tokens of a given claim type or the sum for all claim types) to the calling address. + self.send().direct(&caller, &claim_token, 0, &claim, &[]); } } diff --git a/src/requirements.rs b/src/requirements.rs index c8a8985..4c9693f 100644 --- a/src/requirements.rs +++ b/src/requirements.rs @@ -1,19 +1,23 @@ elrond_wasm::imports!(); elrond_wasm::derive_imports!(); +//Module that handles generic (commonly used, which are not specific to one function) requirements which should stop execution and rollback if not met #[elrond_wasm::module] pub trait RequirementsModule: crate::storage::StorageModule { - fn require_reward_token_is_set(&self) { - require!(!self.reward_token().is_empty(), "Reward token is not set"); + // Checks whether the owner of the smart contract designated a token to be used by the smart contract for all the claims + fn require_claim_token_is_set(&self) { + require!(!self.claim_token().is_empty(), "Claims token is not set"); } - fn require_token_is_reward(&self, token: TokenIdentifier) { + //Checks whether a given token identifier is equal to the token identifier of the token used by the smart contract claims + fn require_token_is_correct(&self, token: TokenIdentifier) { require!( - token == self.reward_token().get(), + token == self.claim_token().get(), "Can only add designated token" ); } + //Checks whether a value is not zero fn require_value_not_zero(&self, value: &BigUint) { require!( value > &BigUint::zero(), @@ -21,10 +25,19 @@ pub trait RequirementsModule: crate::storage::StorageModule { ); } + //Checks whether a claim that is intended to be removed is smaller than the amount reserved in the claim fn require_remove_claim_is_valid(&self, current_claim: &BigUint, amount: &BigUint) { require!( current_claim >= amount, "Cannot remove more than current claim" ); } + + //Checks whether the number of claims added or removed is smaller than 200. Implemented in order to ensure no call will fail due to consuming more than the maxium gas allowed per transaciton on Elrond. + fn require_number_of_claims_in_bulk_is_valid(&self, number_of_claims: &usize) { + require!( + number_of_claims <= &200usize, + "Exceeded maximum number of claims per operation (200)" + ); + } } diff --git a/src/storage.rs b/src/storage.rs index ca3a1e0..1f15e71 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -1,6 +1,7 @@ elrond_wasm::imports!(); elrond_wasm::derive_imports!(); +// Enumeration used to define claim types and increase readability of the code #[derive(TopEncode, TopDecode, NestedEncode, NestedDecode, PartialEq, Clone, Debug, TypeAbi)] pub enum ClaimType { Reward, @@ -8,25 +9,30 @@ pub enum ClaimType { Allocation, } +// Module that handles the common storage of the smart contract #[elrond_wasm::module] pub trait StorageModule { + // Stores the token identifier of the token that is used for claims in the smart contract #[view(viewTokenIdentifier)] #[storage_mapper("tokenIdentifier")] - fn reward_token(&self) -> SingleValueMapper; + fn claim_token(&self) -> SingleValueMapper; + // Stores the amount available to claim for each address and claim type #[view(viewClaim)] #[storage_mapper("claim")] fn claim(&self, address: &ManagedAddress, claim_type: &ClaimType) -> SingleValueMapper; - #[view(viewClaimAddDate)] + // Stores the last timestamp at which the claim has been modified by the owner for each address and claim type + #[view(viewClaimModifyDate)] #[storage_mapper("claimDate")] - fn claim_add_date( + fn claim_modify_date( &self, address: &ManagedAddress, claim_type: &ClaimType, ) -> SingleValueMapper; + // Stores whether claim harvesting is paused or not #[view(isPaused)] #[storage_mapper("isPaused")] fn is_paused(&self) -> SingleValueMapper; diff --git a/src/views.rs b/src/views.rs index 9df217c..020875c 100644 --- a/src/views.rs +++ b/src/views.rs @@ -3,14 +3,17 @@ elrond_wasm::derive_imports!(); use crate::storage::ClaimType; +//Structure that is used in order to return claims with their last modification timestamp #[derive(ManagedVecItem, Clone, NestedEncode, NestedDecode, TopEncode, TopDecode, TypeAbi)] pub struct Claim { pub amount: BigUint, pub date: u64, } +//Module that implements views, by which we understand read-only endpoints #[elrond_wasm::module] pub trait ViewsModule: crate::storage::StorageModule { + //View that returns the sum of all claims, from all claim types, for a given address #[view(viewClaims)] fn view_claims(&self, address: &ManagedAddress) -> BigUint { let mut claim = BigUint::zero(); @@ -20,20 +23,23 @@ pub trait ViewsModule: crate::storage::StorageModule { claim } + //View that returns all claims with the last timestamp at which the claims have been modified by the owner for a given address #[view(viewClaimWithDate)] fn view_claims_with_date(&self, address: &ManagedAddress) -> ManagedVec> { let mut claims = ManagedVec::new(); claims.push(Claim { amount: self.claim(address, &ClaimType::Reward).get(), - date: self.claim_add_date(address, &ClaimType::Reward).get(), + date: self.claim_modify_date(address, &ClaimType::Reward).get(), }); claims.push(Claim { amount: self.claim(address, &ClaimType::Airdrop).get(), - date: self.claim_add_date(address, &ClaimType::Airdrop).get(), + date: self.claim_modify_date(address, &ClaimType::Airdrop).get(), }); claims.push(Claim { amount: self.claim(address, &ClaimType::Allocation).get(), - date: self.claim_add_date(address, &ClaimType::Allocation).get(), + date: self + .claim_modify_date(address, &ClaimType::Allocation) + .get(), }); claims } diff --git a/tests/rust_tests.rs b/tests/rust_tests.rs index 7463f5b..58b7600 100644 --- a/tests/rust_tests.rs +++ b/tests/rust_tests.rs @@ -54,7 +54,7 @@ where .assert_ok(); blockchain_wrapper .execute_tx(&owner_address, &cf_wrapper, &rust_zero, |sc| { - sc.set_reward_token(managed_token_id!(TOKEN_ID)); + sc.set_claim_token(managed_token_id!(TOKEN_ID)); }) .assert_ok(); @@ -309,7 +309,7 @@ fn reset_reward_token_test() { &setup.contract_wrapper, &rust_biguint!(0), |sc| { - sc.set_reward_token(managed_token_id!(TOKEN_ID)); + sc.set_claim_token(managed_token_id!(TOKEN_ID)); }, ) .assert_user_error("Reward token is already set"); diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 26065fa..3f18181 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -14,10 +14,10 @@ elrond_wasm_node::wasm_endpoints! { pause removeClaim removeClaims - setRewardToken + setClaimToken unpause viewClaim - viewClaimAddDate + viewClaimModifyDate viewClaimWithDate viewClaims viewTokenIdentifier From 3a3b6b9470569f13fda8d28c93da189de193d9cb Mon Sep 17 00:00:00 2001 From: damienen Date: Wed, 27 Jul 2022 23:43:13 +0300 Subject: [PATCH 14/25] Referenced claims length when given to requirement --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1f20a6c..d8fb751 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -75,7 +75,7 @@ pub trait ClaimsContract: claims: MultiValueEncoded>, ) { self.require_claim_token_is_set(); - self.require_number_of_claims_in_bulk_is_valid(claims.len()); + self.require_number_of_claims_in_bulk_is_valid(&claims.len()); let (payment_amount, payment_token) = self.call_value().payment_token_pair(); let timestamp = self.blockchain().get_block_timestamp(); self.require_token_is_correct(payment_token); @@ -129,7 +129,7 @@ pub trait ClaimsContract: ) { self.require_claim_token_is_set(); //Panics if the user tries to add more than 200 claims per operation. Implemented in order to ensure - self.require_number_of_claims_in_bulk_is_valid(claims.len()); + self.require_number_of_claims_in_bulk_is_valid(&claims.len()); //Initialize the sum of claims to be removed to zero let mut sum_of_claims = BigUint::zero(); let timestamp = self.blockchain().get_block_timestamp(); From fc8d1a02d8086a946871feaee7479821d6f24640 Mon Sep 17 00:00:00 2001 From: damienen Date: Thu, 28 Jul 2022 00:40:45 +0300 Subject: [PATCH 15/25] Solved issue #15 --- src/lib.rs | 1 + tests/rust_tests.rs | 440 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 435 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d8fb751..ed49cef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,6 +85,7 @@ pub trait ClaimsContract: //Iterate over the claims provided as argument and proceeds similarly to the add_claim endpoint for each one for item in claims.into_iter() { let (address, claim_type, amount) = item.into_tuple(); + self.require_value_not_zero(&amount); let current_claim = self.claim(&address, &claim_type).get(); self.claim(&address, &claim_type) .set(current_claim + &amount); diff --git a/tests/rust_tests.rs b/tests/rust_tests.rs index 58b7600..de0550d 100644 --- a/tests/rust_tests.rs +++ b/tests/rust_tests.rs @@ -97,6 +97,75 @@ fn deploy_test() { .assert_ok(); } +#[test] //Tests wether pausing and unpausing the contract works correctly +fn pause_unpause_test() { + let mut setup = setup_contract(claims::contract_obj); + let b_wrapper = &mut setup.blockchain_wrapper; + let owner_address = &setup.owner_address; + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!(sc.is_paused().get(), false); + }) + .assert_ok(); + + b_wrapper + .execute_tx( + &owner_address, + &setup.contract_wrapper, + &rust_biguint!(0), + |sc| { + sc.unpause(); + }, + ) + .assert_user_error("Contract is already unpaused"); + + b_wrapper + .execute_tx( + &owner_address, + &setup.contract_wrapper, + &rust_biguint!(0), + |sc| { + sc.pause(); + }, + ) + .assert_ok(); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!(sc.is_paused().get(), true); + }) + .assert_ok(); + + b_wrapper + .execute_tx( + &owner_address, + &setup.contract_wrapper, + &rust_biguint!(0), + |sc| { + sc.pause(); + }, + ) + .assert_user_error("Contract is already paused"); + + b_wrapper + .execute_tx( + &owner_address, + &setup.contract_wrapper, + &rust_biguint!(0), + |sc| { + sc.unpause(); + }, + ) + .assert_ok(); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!(sc.is_paused().get(), false); + }) + .assert_ok(); +} + #[test] //Tests wether adding and removing singular claims works and also if removing returns an error if trying to remove more than the available claim fn add_and_remove_claim_test() { let mut setup = setup_contract(claims::contract_obj); @@ -116,6 +185,40 @@ fn add_and_remove_claim_test() { }, ) .assert_ok(); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim(&managed_address!(user_addr), &storage::ClaimType::Airdrop) + .get(), + 1_000_000 + ); + }) + .assert_ok(); + + b_wrapper + .execute_esdt_transfer( + owner_address, + &setup.contract_wrapper, + TOKEN_ID, + 0, + &rust_biguint!(0), + |sc| { + sc.add_claim(&managed_address!(user_addr), storage::ClaimType::Airdrop); + }, + ) + .assert_user_error("Operations must have non-zero value"); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim(&managed_address!(user_addr), &storage::ClaimType::Airdrop) + .get(), + 1_000_000 + ); + }) + .assert_ok(); + b_wrapper .execute_esdt_transfer( owner_address, @@ -132,6 +235,34 @@ fn add_and_remove_claim_test() { }, ) .assert_ok(); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim(&managed_address!(user_addr), &storage::ClaimType::Airdrop) + .get(), + 500_000 + ); + }) + .assert_ok(); + + b_wrapper + .execute_esdt_transfer( + owner_address, + &setup.contract_wrapper, + TOKEN_ID, + 0, + &rust_biguint!(0), + |sc| { + sc.remove_claim( + &managed_address!(user_addr), + storage::ClaimType::Airdrop, + managed_biguint!(0), + ); + }, + ) + .assert_user_error("Operations must have non-zero value"); + b_wrapper .execute_esdt_transfer( owner_address, @@ -148,6 +279,16 @@ fn add_and_remove_claim_test() { }, ) .assert_user_error("Cannot remove more than current claim"); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim(&managed_address!(user_addr), &storage::ClaimType::Airdrop) + .get(), + 500_000 + ); + }) + .assert_ok(); } #[test] //Same tests as the ones for singular claims, but for multiple claims + testing whether adding claims, but not sending enough tokens returns an error @@ -187,6 +328,33 @@ fn add_and_remove_claims_test() { }, ) .assert_ok(); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim( + &managed_address!(first_user_addr), + &storage::ClaimType::Airdrop + ) + .get(), + 1_000_000 + ); + }) + .assert_ok(); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim( + &managed_address!(second_user_addr), + &storage::ClaimType::Allocation + ) + .get(), + 1_000_000 + ); + }) + .assert_ok(); + b_wrapper .execute_esdt_transfer( owner_address, @@ -216,6 +384,63 @@ fn add_and_remove_claims_test() { }, ) .assert_user_error("Claims added must equal payment amount"); + + b_wrapper + .execute_esdt_transfer( + owner_address, + &setup.contract_wrapper, + TOKEN_ID, + 0, + &rust_biguint!(1_700_000), + |sc| { + let mut args = MultiValueEncoded::new(); + args.push(MultiValue3( + ( + managed_address!(first_user_addr), + storage::ClaimType::Airdrop, + managed_biguint!(1_700_000), + ) + .into(), + )); + args.push(MultiValue3( + ( + managed_address!(second_user_addr), + storage::ClaimType::Allocation, + managed_biguint!(0), + ) + .into(), + )); + sc.add_claims(args); + }, + ) + .assert_user_error("Operations must have non-zero value"); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim( + &managed_address!(first_user_addr), + &storage::ClaimType::Airdrop + ) + .get(), + 1_000_000 + ); + }) + .assert_ok(); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim( + &managed_address!(second_user_addr), + &storage::ClaimType::Allocation + ) + .get(), + 1_000_000 + ); + }) + .assert_ok(); + b_wrapper .execute_esdt_transfer( owner_address, @@ -229,7 +454,7 @@ fn add_and_remove_claims_test() { ( managed_address!(first_user_addr), storage::ClaimType::Airdrop, - managed_biguint!(1_000_000), + managed_biguint!(700_000), ) .into(), )); @@ -245,6 +470,33 @@ fn add_and_remove_claims_test() { }, ) .assert_ok(); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim( + &managed_address!(first_user_addr), + &storage::ClaimType::Airdrop + ) + .get(), + 300_000 + ); + }) + .assert_ok(); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim( + &managed_address!(second_user_addr), + &storage::ClaimType::Allocation + ) + .get(), + 500_000 + ); + }) + .assert_ok(); + b_wrapper .execute_esdt_transfer( owner_address, @@ -258,7 +510,37 @@ fn add_and_remove_claims_test() { ( managed_address!(first_user_addr), storage::ClaimType::Airdrop, - managed_biguint!(300_000), + managed_biguint!(200_000), + ) + .into(), + )); + args.push(MultiValue3( + ( + managed_address!(second_user_addr), + storage::ClaimType::Allocation, + managed_biguint!(0), + ) + .into(), + )); + sc.remove_claims(args); + }, + ) + .assert_user_error("Operations must have non-zero value"); + + b_wrapper + .execute_esdt_transfer( + owner_address, + &setup.contract_wrapper, + TOKEN_ID, + 0, + &rust_biguint!(0), + |sc| { + let mut args = MultiValueEncoded::new(); + args.push(MultiValue3( + ( + managed_address!(first_user_addr), + storage::ClaimType::Airdrop, + managed_biguint!(400_000), ) .into(), )); @@ -274,9 +556,35 @@ fn add_and_remove_claims_test() { }, ) .assert_user_error("Cannot remove more than current claim"); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim( + &managed_address!(first_user_addr), + &storage::ClaimType::Airdrop + ) + .get(), + 300_000 + ); + }) + .assert_ok(); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim( + &managed_address!(second_user_addr), + &storage::ClaimType::Allocation + ) + .get(), + 500_000 + ); + }) + .assert_ok(); } -#[test] //Tests whether the transaction to add a token fails in the case in which a different token than the reward token is sent +#[test] //Tests whether the transaction to add a token fails in the case in which a different token than the claim token is sent fn add_claim_wrong_token_test() { let mut setup = setup_contract(claims::contract_obj); let b_wrapper = &mut setup.blockchain_wrapper; @@ -295,10 +603,85 @@ fn add_claim_wrong_token_test() { }, ) .assert_user_error("Can only add designated token"); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim(&managed_address!(user_addr), &storage::ClaimType::Airdrop) + .get(), + 0 + ); + }) + .assert_ok(); +} + +#[test] //Tests whether the transaction to add tokens fails in the case in which a different token than the claim token is sent +fn add_claims_wrong_token_test() { + let mut setup = setup_contract(claims::contract_obj); + let b_wrapper = &mut setup.blockchain_wrapper; + let owner_address = &setup.owner_address; + let first_user_addr = &setup.first_user_address; + let second_user_addr = &setup.second_user_address; + + b_wrapper + .execute_esdt_transfer( + owner_address, + &setup.contract_wrapper, + WRONG_TOKEN_ID, + 0, + &rust_biguint!(500_000), + |sc| { + let mut args = MultiValueEncoded::new(); + args.push(MultiValue3( + ( + managed_address!(first_user_addr), + storage::ClaimType::Airdrop, + managed_biguint!(200_000), + ) + .into(), + )); + args.push(MultiValue3( + ( + managed_address!(second_user_addr), + storage::ClaimType::Allocation, + managed_biguint!(300_000), + ) + .into(), + )); + sc.add_claims(args); + }, + ) + .assert_user_error("Can only add designated token"); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim( + &managed_address!(first_user_addr), + &storage::ClaimType::Airdrop + ) + .get(), + 0 + ); + }) + .assert_ok(); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim( + &managed_address!(second_user_addr), + &storage::ClaimType::Allocation + ) + .get(), + 0 + ); + }) + .assert_ok(); } -#[test] //Tests whether one can set the reward token only once -fn reset_reward_token_test() { +#[test] //Tests whether one can set the claim token only once +fn reset_claim_token_test() { let mut setup = setup_contract(claims::contract_obj); let b_wrapper = &mut setup.blockchain_wrapper; let owner_address = &setup.owner_address; @@ -312,7 +695,7 @@ fn reset_reward_token_test() { sc.set_claim_token(managed_token_id!(TOKEN_ID)); }, ) - .assert_user_error("Reward token is already set"); + .assert_user_error("Claim token is already set"); } #[test] //Tests whether claiming is impossible in pause state @@ -379,6 +762,18 @@ fn harvest_claim_test() { }, ) .assert_ok(); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim(&managed_address!(user_addr), &storage::ClaimType::Airdrop) + .get(), + 0 + ); + }) + .assert_ok(); + + b_wrapper.check_esdt_balance(user_addr, TOKEN_ID, &rust_biguint!(1_000_000)); } #[test] //Test wether the transaction to claim returns an error if no claims are present for the user for the type he tries to claim @@ -413,6 +808,18 @@ fn harvest_wrong_claim_type_test() { }, ) .assert_user_error("Operations must have non-zero value"); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim(&managed_address!(user_addr), &storage::ClaimType::Airdrop) + .get(), + 1_000_000 + ); + }) + .assert_ok(); + + b_wrapper.check_esdt_balance(user_addr, TOKEN_ID, &rust_biguint!(0)); } #[test] //Tests whether claiming all claim types at once works @@ -460,5 +867,26 @@ fn harvest_all_claims_test() { }, ) .assert_ok(); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim(&managed_address!(user_addr), &storage::ClaimType::Airdrop) + .get(), + 0 + ); + }) + .assert_ok(); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim(&managed_address!(user_addr), &storage::ClaimType::Reward) + .get(), + 0 + ); + }) + .assert_ok(); + b_wrapper.check_esdt_balance(user_addr, TOKEN_ID, &rust_biguint!(2_000_000)); } From 501049e2594b684283a286ce37d1fb3402f767a1 Mon Sep 17 00:00:00 2001 From: damienen Date: Thu, 28 Jul 2022 00:51:10 +0300 Subject: [PATCH 16/25] Small addition to issue #15; contributed to issue #12 --- tests/rust_tests.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/tests/rust_tests.rs b/tests/rust_tests.rs index de0550d..3b53dda 100644 --- a/tests/rust_tests.rs +++ b/tests/rust_tests.rs @@ -81,7 +81,7 @@ where } } -#[test] //Tests whether the contrat is deployed and initialized after deployment correctly +#[test] //Tests whether the contrat is deployed and initialized correctly after deployment fn deploy_test() { let mut setup = setup_contract(claims::contract_obj); setup @@ -166,7 +166,9 @@ fn pause_unpause_test() { .assert_ok(); } -#[test] //Tests wether adding and removing singular claims works and also if removing returns an error if trying to remove more than the available claim +#[test] //Tests wether adding and removing singular claims works as expected + //Tests if adding and removing a zero value claim returns an error + //Tests if removing more than the amount reserved in claims returns an error fn add_and_remove_claim_test() { let mut setup = setup_contract(claims::contract_obj); let b_wrapper = &mut setup.blockchain_wrapper; @@ -291,7 +293,10 @@ fn add_and_remove_claim_test() { .assert_ok(); } -#[test] //Same tests as the ones for singular claims, but for multiple claims + testing whether adding claims, but not sending enough tokens returns an error +#[test] //Same tests as the ones for singular claims, but for multiple claims + //Tests if adding multiple claims, but not sending the right amount of tokens for it returns an error + //Tests if adding or removing zero valued claims returns an error + //Tests if removing more than the amount reserved in claims returns an error fn add_and_remove_claims_test() { let mut setup = setup_contract(claims::contract_obj); let b_wrapper = &mut setup.blockchain_wrapper; @@ -819,6 +824,16 @@ fn harvest_wrong_claim_type_test() { }) .assert_ok(); + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.claim(&managed_address!(user_addr), &storage::ClaimType::Reward) + .get(), + 0 + ); + }) + .assert_ok(); + b_wrapper.check_esdt_balance(user_addr, TOKEN_ID, &rust_biguint!(0)); } From 5e2105bdd649b83f2e30d2d562fea879cf87e2ee Mon Sep 17 00:00:00 2001 From: damienen Date: Thu, 28 Jul 2022 01:53:38 +0300 Subject: [PATCH 17/25] Contributed to #4, added interaction snippets --- .gitignore | 2 + interaction/devnet.snippets.sh | 125 +++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 interaction/devnet.snippets.sh diff --git a/.gitignore b/.gitignore index 827277d..902511c 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ deployOutput wallets/ commands.txt wallet.pem +erdpy.data-storage.json +deploy-devnet.interaction.json diff --git a/interaction/devnet.snippets.sh b/interaction/devnet.snippets.sh new file mode 100644 index 0000000..412004f --- /dev/null +++ b/interaction/devnet.snippets.sh @@ -0,0 +1,125 @@ +PROXY=https://devnet-gateway.elrond.com +CHAIN_ID="D" + +WALLET="./wallet.pem" + +MY_ADDRESS="erd130qhxzu6lh29g6srnsa65pplxaf9sgsspczvljvxk624upwt7yqstswx9l" +ADDRESS=$(erdpy data load --key=address-devnet) +DEPLOY_TRANSACTION=$(erdpy data load --key=deployTransaction-devnet) + +TOKEN="ITHEUM-a61317" +TOKEN_HEX="0x$(echo -n ${TOKEN} | xxd -p -u | tr -d '\n')" + +deploy(){ + erdpy --verbose contract deploy \ + --bytecode output/claims.wasm \ + --outfile deployOutput \ + --metadata-payable \ + --pem wallet.pem \ + --proxy ${PROXY} \ + --chain ${CHAIN_ID} \ + --gas-limit 150000000 \ + --send \ + --recall-nonce \ + --outfile="./interaction/deploy-devnet.interaction.json" || return + + TRANSACTION=$(erdpy data parse --file="./interaction/deploy-devnet.interaction.json" --expression="data['emittedTransactionHash']") + ADDRESS=$(erdpy data parse --file="./interaction/deploy-devnet.interaction.json" --expression="data['contractAddress']") + + erdpy data store --key=address-devnet --value=${ADDRESS} + erdpy data store --key=deployTransaction-devnet --value=${TRANSACTION} +} + +setClaimToken(){ + erdpy --verbose contract call ${ADDRESS} \ + --recall-nonce \ + --pem=${WALLET} \ + --gas-limit=6000000 \ + --function "setClaimToken" \ + --arguments ${TOKEN_HEX} \ + --proxy ${PROXY} \ + --chain ${CHAIN_ID} \ + --send || return +} + +pause(){ + erdpy --verbose contract call ${ADDRESS} \ + --recall-nonce \ + --pem=${WALLET} \ + --gas-limit=6000000 \ + --function "pause" \ + --proxy ${PROXY} \ + --chain ${CHAIN_ID} \ + --send || return +} + +unpause(){ + erdpy --verbose contract call ${ADDRESS} \ + --recall-nonce \ + --pem=${WALLET} \ + --gas-limit=6000000 \ + --function "unpause" \ + --proxy ${PROXY} \ + --chain ${CHAIN_ID} \ + --send || return +} + +addClaim(){ + # $1 = amount to add to claim + # $2 = address to which to attribute the claim + # $3 = claim type (0 = reward, 1 = aidrop, 2 = allocation) + + method="0x$(echo -n 'addClaim' | xxd -p -u | tr -d '\n')" + address="0x$(erdpy wallet bech32 --decode ${2})" + erdpy --verbose contract call ${ADDRESS} \ + --recall-nonce \ + --pem=${WALLET} \ + --gas-limit=6000000 \ + --function "ESDTTransfer" \ + --arguments ${TOKEN_HEX} $1 $method $address $3 \ + --proxy ${PROXY} \ + --chain ${CHAIN_ID} \ + --send || return +} + +removeClaim(){ + # $1 = address from which to remove the claim + # $2 = claim type (0 = reward, 1 = aidrop, 2 = allocation) + # $3 = amount to remove from claim + + address="0x$(erdpy wallet bech32 --decode ${1})" + erdpy --verbose contract call ${ADDRESS} \ + --recall-nonce \ + --pem=${WALLET} \ + --gas-limit=6000000 \ + --function "removeClaim" \ + --arguments $address $2 $3 \ + --proxy ${PROXY} \ + --chain ${CHAIN_ID} \ + --send || return +} + +harvestAllClaims(){ + erdpy --verbose contract call ${ADDRESS} \ + --recall-nonce \ + --pem=${WALLET} \ + --gas-limit=6000000 \ + --function "claim" \ + --proxy ${PROXY} \ + --chain ${CHAIN_ID} \ + --send || return +} + +harvestClaim(){ + # $1 = claim type (0 = reward, 1 = aidrop, 3 = allocation) + + erdpy --verbose contract call ${ADDRESS} \ + --recall-nonce \ + --pem=${WALLET} \ + --gas-limit=6000000 \ + --function "claim" \ + --arguments $1 \ + --proxy ${PROXY} \ + --chain ${CHAIN_ID} \ + --send || return +} \ No newline at end of file From 57c3b32c85743f89dd5777ccee02603c6b4a3ee9 Mon Sep 17 00:00:00 2001 From: damienen Date: Sun, 31 Jul 2022 01:38:38 +0300 Subject: [PATCH 18/25] Added more tests --- tests/rust_tests.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/rust_tests.rs b/tests/rust_tests.rs index 3b53dda..00d3cc1 100644 --- a/tests/rust_tests.rs +++ b/tests/rust_tests.rs @@ -390,6 +390,30 @@ fn add_and_remove_claims_test() { ) .assert_user_error("Claims added must equal payment amount"); + b_wrapper + .execute_esdt_transfer( + owner_address, + &setup.contract_wrapper, + TOKEN_ID, + 0, + &rust_biguint!(201_000), + |sc| { + let mut args = MultiValueEncoded::new(); + for _i in 0..201 { + args.push(MultiValue3( + ( + managed_address!(first_user_addr), + storage::ClaimType::Airdrop, + managed_biguint!(1_000), + ) + .into(), + )); + } + sc.add_claims(args); + }, + ) + .assert_user_error("Exceeded maximum number of claims per operation (200)"); + b_wrapper .execute_esdt_transfer( owner_address, @@ -562,6 +586,30 @@ fn add_and_remove_claims_test() { ) .assert_user_error("Cannot remove more than current claim"); + b_wrapper + .execute_esdt_transfer( + owner_address, + &setup.contract_wrapper, + TOKEN_ID, + 0, + &rust_biguint!(0), + |sc| { + let mut args = MultiValueEncoded::new(); + for _i in 0..201 { + args.push(MultiValue3( + ( + managed_address!(first_user_addr), + storage::ClaimType::Airdrop, + managed_biguint!(1_000), + ) + .into(), + )); + } + sc.remove_claims(args); + }, + ) + .assert_user_error("Exceeded maximum number of claims per operation (200)"); + b_wrapper .execute_query(&setup.contract_wrapper, |sc| { assert_eq!( From 15679c3385f73e606bf01b6a05aeae3d6b9eb605 Mon Sep 17 00:00:00 2001 From: damienen Date: Sun, 31 Jul 2022 03:27:18 +0300 Subject: [PATCH 19/25] Solved issues #4 and #12 --- README.md | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) diff --git a/README.md b/README.md index 33cf098..96a8241 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,12 @@ The claims smart contract is the tool that stands at the heart of collaboration This contract allows the owner of it to send tokens to the smart contract and reserve them for a specific address of their choice. There are 3 types of claims that are defined in the smart contract: rewards, airdrops and allocations. If a user has claims, they can harvest each type individually or can choose to harvest all of them in the same transaction. The contract is designed such that a user can only take their designated tokens from the contract. +## Prerequisites + +This documentation assumes the user has previous programming experience. Moreover, the user should have a basic understanding of the Elrond blockchain. If you are new to the blockchain, please refer to the [Elrond documentation](https://docs.elrond.com/). In order to develop Elrond smart contract related solutions, one needs to have installed [erdpy](https://docs.elrond.com/sdk-and-tools/erdpy/installing-erdpy/). + +Understanding this document is also easier if one knows how [ESDT token transactions](https://docs.elrond.com/developers/esdt-tokens/#transfers-to-a-smart-contract) are structured on the Elrond blockchain. + ## Itheum deployed claims contract addresses | Devnet | Mainnet | @@ -18,16 +24,181 @@ This contract allows the owner of it to send tokens to the smart contract and re ### Setup endpoints +The setup workflow for the claims smart contract is as follows: + +- The SC deployment +- Setting up the claims token. + +#### init + +```rust + #[init] + fn init(&self); +``` + +The init function is called when deploying or upgrading the smart contract. It receives no arguments and it the only thing it does for the claims smart contract is to pause it. + +#### setClaimToken + +```rust + #[endpoint(setClaimToken)] + fn set_claim_token(&self, + token: TokenIdentifier + ); +``` + +Endpoint that sets the claims token. It can only be used once and it can only be called by the owner of the contract. +Call structure: "setRewardToken" + "@" + TokenIdentifier hex encoded +Example: “setRewardToken@49544845554d2d613631333137” + ### Only owner endpoints +#### pause + +```rust + #[endpoint(pause)] + fn pause(&self); +``` + +Endpoint that pauses the claims harvesting from the smart contract. +Call structure: "pause" +Example: "pause" + +#### unpause + +```rust + #[endpoint(unpause)] + fn unpause(&self); +``` + +Endpoint that unpauses the claims harvesting from the smart contract. +Call structure: "unpause" +Example: "unpause" + +#### addClaim + +```rust + #[payable("*")] + #[endpoint(addClaim)] + fn add_claim(&self, + address: &ManagedAddress, + claim_type: ClaimType + ); +``` + +Endpoint that allows the owner of the smart contract to add a claim to the smart contract. Receives an address and the claim type as arguments. The claim is set for the address and the claim type received as arguments. +Call structure:"ESDTTransfer"+ "@" + TokenIdentifier hex encoded + "@" + amount hex encoded + "@" + "addClaim" hex encoded + "@" + address hex encoded + "@" + claim type hex encoded +Example: "ESDTTransfer@49544845554d2d613631333137@8ac7230489e80000@616464436c61696d@8bc1730b9afdd4546a039c3baa043f37525822100e04cfc986b6955e05cbf101@00" + +#### addClaims + +```rust + #[payable("*")] + #[endpoint(addClaims)] + fn add_claims(&self, + claims: MultiValueEncoded> + ); +``` + +Similar to the addClaim endpoint, but it allows the owner to add multiple claims to the smart contract through a single transaction. Receives a list of claims as arguments. +Call structure: "ESDTTransfer" + "@" + TokenIdentifier hex encoded + "@" + total amounts of tokens added to claims hex encoded + "@" + "addClaims" hex encoded + "@" + address hex encoded + "@" + claim type hex encoded + "@" + amount for this address hex encoded (but can add as many address/claim type/amount pairs as needed) +Example: "ESDTTransfer@49544845554d2d613631333137@8ac7230489e80000@616464436c61696d73@8bc1730b9afdd4546a039c3baa043f37525822100e04cfc986b6955e05cbf101@00@8ac7230489e80000" + +#### removeClaim + +```rust + #[endpoint(removeClaim)] + fn remove_claim(&self, + address: &ManagedAddress, + claim_type: ClaimType, + amount: BigUint + ); +``` + +Endpoint that allows the owner of the smart contract to remove a claim from the smart contract. Receives an address, the claim type and the amount of tokens to remove as arguments. +Call structure: "removeClaim" + "@" +address hex encoded + "@" + claim type hex encoded + "@" + amount to remove hex encoded +Example: "removeClaim@8bc1730b9afdd4546a039c3baa043f37525822100e04cfc986b6955e05cbf101@01@8ac7230489e80000" + +#### removeClaims + +```rust + #[endpoint(removeClaims)] + fn remove_claims(&self, + claims: MultiValueEncoded>, + ); +``` + +Similar to the removeClaim endpoint, but it allows the owner to remove multiple claims from the smart contract through a single transaction. Receives a list of claims as arguments. +Call structure: "removeClaims" + "@" + address hex encoded + "@" + claim type hex encoded + "@" + amount to remove hex encoded (but can add as many pairs as needed) +Example: "removeClaims@8bc1730b9afdd4546a039c3baa043f37525822100e04cfc986b6955e05cbf101@01@8ac7230489e80000" + ### Public endpoints +#### claim + +```rust + #[endpoint(claim)] + fn harvest_claim(&self, + claim_type: OptionalValue + ); +``` + +Endpoint that allows anyone to harvest their designated claims. Allows the user to input a claim type as argument, but that argument is optional. If no claim type is provided, the user will receive all claims attributed to themseles. If a claim type is provided as argument, the user will only receive that claim type. + +Call structure without claim type: "harvestClaim" +Example without claim type: "harvestClaim" + +Call structure wit claim type: "harvestClaim" + "@" + claim type hex encoded +Example with claim type: "harvestClaim@02" + ## Development +This smart contract, albeit being a simple one, aims to set the standard when it comes to the quality of testing and documentation for which smart contract developers should aim. The above average level of documentation present aims specifically to take advantage of our open source codebase in order to learn, contribute and take good practices from the smart contract. + ### Architecture +The Claims Smart Contract is structured in 5 files: + +- events: This files has all the defined events of the smart contract. They are emitted whenever something relevant happens in the smart contract. Their role is to make debugging and logging easier and to allow data collecting based on the smart contract. +- storage: This file has all the storage/memory declaration of the smart contract. This is the main file that allows the smart contract to save data in the blockchain. +- views: This file contains all the read-only endpoints of the smart contract. These endpoints are used to retrieve relevant data from the smart contract. +- requirements: This file contains requirements for the endpoints of the smart contract. In order to avoid code duplication, encourage a healthy project structure and increase code readability we have decided to separate most of the requirements that would otherwise have been duplicated from the endpoints and put them here. +- lib: This is the main file of the smart contract, where all the logic of the smart contract is implemented. This connects all the other files (modules) and uses them to implement what is the claims contract itself. + ### How to test +The tests are located in the tests folder, in the rust_tests file. In order to run the tests one can use the command: + +```shell + cargo test --package claims --test rust_tests -- --nocapture +``` + +Another way of running the tests is by using the rust-analyzer extension in Visual Studio Code, which is also very helpful for Elrond Smart Contract development. If one has the extension installed, they can go open and go to the top of the rust_tests file and click the Run Tests button. + +Note: In order to run the tests, one has to use the rust nightly version. One can switch to the nightly version by using: + +```shell + rustup default nightly +``` + ### How to deploy +In order to deploy the smart contract on devnet one can use the interaction snippets present in the devnet.snippets file (which is located in the interactions folder). To run the functions from the interaction file, one can use: + +```shell + source interaction/devnet.snippets.sh +``` + +After using that, to deploy one can simply use: + +```shell + deploy +``` + +### How to interact + +After deployment, one can interact with the smart contract and test its functionality. To do so, one can use the interaction snippets already presented above. More explanations can be found about the snippets inside the devnet.snippets file. + ## Contributing + +Feel free the contact the development team if you wish to contribute or if you have any questions. If you find any issues, please report them in the Issues sections of the repository. You can also create your own pull requests which will be analyzed by the team. From 27c303ea4bf71c59d4829b60738b50bb96be073d Mon Sep 17 00:00:00 2001 From: damienen Date: Mon, 1 Aug 2022 08:57:27 +0300 Subject: [PATCH 20/25] Added pem file instruction for snippets --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 96a8241..64ecbec 100644 --- a/README.md +++ b/README.md @@ -183,7 +183,7 @@ Note: In order to run the tests, one has to use the rust nightly version. One ca ### How to deploy -In order to deploy the smart contract on devnet one can use the interaction snippets present in the devnet.snippets file (which is located in the interactions folder). To run the functions from the interaction file, one can use: +In order to deploy the smart contract on devnet one can use the interaction snippets present in the devnet.snippets file (which is located in the interactions folder). Before using the snippets, make sure to add your pem file in the root of the project under the name "wallet.pem" (or change the name to whichever one you wish to use in the interaction snippets). If you need info about how to derive a pem file you can find them [here](https://docs.elrond.com/sdk-and-tools/erdpy/deriving-the-wallet-pem-file/). To run the functions from the interaction file, one can use: ```shell source interaction/devnet.snippets.sh From 780176eeaa4be050348d5790e6b9baad76b011e1 Mon Sep 17 00:00:00 2001 From: damienen Date: Wed, 3 Aug 2022 02:02:38 +0300 Subject: [PATCH 21/25] Solved issue #16 --- README.md | 88 ++++++++---- interaction/devnet.snippets.sh | 31 ++++- src/events.rs | 29 +++- src/lib.rs | 56 ++++++-- src/requirements.rs | 9 ++ src/storage.rs | 4 + tests/rust_tests.rs | 244 +++++++++++++++++++++++++++++++++ wasm/src/lib.rs | 3 + 8 files changed, 420 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 64ecbec..e0dbb89 100644 --- a/README.md +++ b/README.md @@ -49,21 +49,10 @@ The init function is called when deploying or upgrading the smart contract. It r Endpoint that sets the claims token. It can only be used once and it can only be called by the owner of the contract. Call structure: "setRewardToken" + "@" + TokenIdentifier hex encoded -Example: “setRewardToken@49544845554d2d613631333137” +Example: "setRewardToken@49544845554d2d613631333137" ### Only owner endpoints -#### pause - -```rust - #[endpoint(pause)] - fn pause(&self); -``` - -Endpoint that pauses the claims harvesting from the smart contract. -Call structure: "pause" -Example: "pause" - #### unpause ```rust @@ -75,34 +64,31 @@ Endpoint that unpauses the claims harvesting from the smart contract. Call structure: "unpause" Example: "unpause" -#### addClaim +#### addPrivilegedAddress ```rust - #[payable("*")] - #[endpoint(addClaim)] - fn add_claim(&self, - address: &ManagedAddress, - claim_type: ClaimType + #[endpoint(addPrivilegedAddress)] + fn add_privileged_address(&self, + address: ManagedAddress ); ``` -Endpoint that allows the owner of the smart contract to add a claim to the smart contract. Receives an address and the claim type as arguments. The claim is set for the address and the claim type received as arguments. -Call structure:"ESDTTransfer"+ "@" + TokenIdentifier hex encoded + "@" + amount hex encoded + "@" + "addClaim" hex encoded + "@" + address hex encoded + "@" + claim type hex encoded -Example: "ESDTTransfer@49544845554d2d613631333137@8ac7230489e80000@616464436c61696d@8bc1730b9afdd4546a039c3baa043f37525822100e04cfc986b6955e05cbf101@00" +Endpoint that gives an address privileges to add claims or pause the contract. The contract can only store up to two privileged addresses at a time. +Call structure: "addPrivilegedAddress" + "@" + Address hex encoded +Example: "addPrivilegedAddress@8bc1730b9afdd4546a039c3baa043f37525822100e04cfc986b6955e05cbf101" -#### addClaims +#### removePrivilegedAddress ```rust - #[payable("*")] - #[endpoint(addClaims)] - fn add_claims(&self, - claims: MultiValueEncoded> + #[endpoint(removePrivilegedAddress)] + fn remove_privileged_address(&self, + address: ManagedAddress ); ``` -Similar to the addClaim endpoint, but it allows the owner to add multiple claims to the smart contract through a single transaction. Receives a list of claims as arguments. -Call structure: "ESDTTransfer" + "@" + TokenIdentifier hex encoded + "@" + total amounts of tokens added to claims hex encoded + "@" + "addClaims" hex encoded + "@" + address hex encoded + "@" + claim type hex encoded + "@" + amount for this address hex encoded (but can add as many address/claim type/amount pairs as needed) -Example: "ESDTTransfer@49544845554d2d613631333137@8ac7230489e80000@616464436c61696d73@8bc1730b9afdd4546a039c3baa043f37525822100e04cfc986b6955e05cbf101@00@8ac7230489e80000" +Endpoint that removes privileges of an already privileged address. +Call structure: "removePrivilegedAddress" + "@" + Address hex encoded +Example: "removePrivilegedAddress@8bc1730b9afdd4546a039c3baa043f37525822100e04cfc986b6955e05cbf101" #### removeClaim @@ -132,6 +118,50 @@ Similar to the removeClaim endpoint, but it allows the owner to remove multiple Call structure: "removeClaims" + "@" + address hex encoded + "@" + claim type hex encoded + "@" + amount to remove hex encoded (but can add as many pairs as needed) Example: "removeClaims@8bc1730b9afdd4546a039c3baa043f37525822100e04cfc986b6955e05cbf101@01@8ac7230489e80000" +### Priviledged address endpoints + +These endpoints are endpoints that are callable by both the owner of the Smart Contract and up to two other addresses designated by the owner to have extra privileges. + +#### pause + +```rust + #[endpoint(pause)] + fn pause(&self); +``` + +Endpoint that pauses the claims harvesting from the smart contract. +Call structure: "pause" +Example: "pause" + +#### addClaim + +```rust + #[payable("*")] + #[endpoint(addClaim)] + fn add_claim(&self, + address: &ManagedAddress, + claim_type: ClaimType + ); +``` + +Endpoint that allows the owner of the smart contract to add a claim to the smart contract. Receives an address and the claim type as arguments. The claim is set for the address and the claim type received as arguments. +Call structure:"ESDTTransfer"+ "@" + TokenIdentifier hex encoded + "@" + amount hex encoded + "@" + "addClaim" hex encoded + "@" + address hex encoded + "@" + claim type hex encoded +Example: "ESDTTransfer@49544845554d2d613631333137@8ac7230489e80000@616464436c61696d@8bc1730b9afdd4546a039c3baa043f37525822100e04cfc986b6955e05cbf101@00" + +#### addClaims + +```rust + #[payable("*")] + #[endpoint(addClaims)] + fn add_claims(&self, + claims: MultiValueEncoded> + ); +``` + +Similar to the addClaim endpoint, but it allows the owner to add multiple claims to the smart contract through a single transaction. Receives a list of claims as arguments. +Call structure: "ESDTTransfer" + "@" + TokenIdentifier hex encoded + "@" + total amounts of tokens added to claims hex encoded + "@" + "addClaims" hex encoded + "@" + address hex encoded + "@" + claim type hex encoded + "@" + amount for this address hex encoded (but can add as many address/claim type/amount pairs as needed) +Example: "ESDTTransfer@49544845554d2d613631333137@8ac7230489e80000@616464436c61696d73@8bc1730b9afdd4546a039c3baa043f37525822100e04cfc986b6955e05cbf101@00@8ac7230489e80000" + ### Public endpoints #### claim diff --git a/interaction/devnet.snippets.sh b/interaction/devnet.snippets.sh index 412004f..37a9d24 100644 --- a/interaction/devnet.snippets.sh +++ b/interaction/devnet.snippets.sh @@ -3,7 +3,6 @@ CHAIN_ID="D" WALLET="./wallet.pem" -MY_ADDRESS="erd130qhxzu6lh29g6srnsa65pplxaf9sgsspczvljvxk624upwt7yqstswx9l" ADDRESS=$(erdpy data load --key=address-devnet) DEPLOY_TRANSACTION=$(erdpy data load --key=deployTransaction-devnet) @@ -64,6 +63,36 @@ unpause(){ --send || return } +addPrivilegedAddress(){ + # $1 = address to which to give privileges + + address="0x$(erdpy wallet bech32 --decode ${2})" + erdpy --verbose contract call ${ADDRESS} \ + --recall-nonce \ + --pem=${WALLET} \ + --gas-limit=10000000 \ + --function "addPrivilegedAddress" \ + --arguments $address \ + --proxy ${PROXY} \ + --chain ${CHAIN_ID} \ + --send || return +} + +removePrivilegedAddress(){ + # $1 = address to which to remove privileges + + address="0x$(erdpy wallet bech32 --decode ${2})" + erdpy --verbose contract call ${ADDRESS} \ + --recall-nonce \ + --pem=${WALLET} \ + --gas-limit=10000000 \ + --function "removePrivilegedAddress" \ + --arguments $address \ + --proxy ${PROXY} \ + --chain ${CHAIN_ID} \ + --send || return +} + addClaim(){ # $1 = amount to add to claim # $2 = address to which to attribute the claim diff --git a/src/events.rs b/src/events.rs index 53f8584..15ac1ce 100644 --- a/src/events.rs +++ b/src/events.rs @@ -3,25 +3,42 @@ elrond_wasm::derive_imports!(); use crate::storage::ClaimType; -//Module that handles event emitting for important smart contract event in order to facilitate logging, debugging and monitoring with ease +//Module that handles event emitting for important smart contract events in order to facilitate logging, debugging and monitoring with ease #[elrond_wasm::module] pub trait EventsModule { - //Emitted whenever the owner adds a new claim to the smart contract + //Emitted whenever a privileged address pauses claim harvesting + #[event("harvestPaused")] + fn harvest_paused_event(&self, #[indexed] operator: &ManagedAddress); + + //Emitted whenever the owner unpauses claim harvesting + #[event("harvestUnpaused")] + fn harvest_unpaused_event(&self); + + //Emitted whenever the owner adds a privileged address + #[event("privilegedAddressAdded")] + fn privileged_address_added_event(&self, #[indexed] address: &ManagedAddress); + + //Emitted whenever the owner removes a privileged address + #[event("privledgedAddressRemoved")] + fn privileged_address_removed_event(&self, #[indexed] address: &ManagedAddress); + + //Emitted whenever a new claim is added to the smart contract #[event("claimAdded")] fn claim_added_event( &self, + #[indexed] operator: &ManagedAddress, #[indexed] address: &ManagedAddress, #[indexed] claim_type: &ClaimType, - amount: &BigUint, + #[indexed] amount: &BigUint, ); - //Emitted whenever the owner removes a claim from the smart contract + //Emitted whenever a claim is removed from the smart contract #[event("claimRemoved")] fn claim_removed_event( &self, #[indexed] address: &ManagedAddress, #[indexed] claim_type: &ClaimType, - amount: &BigUint, + #[indexed] amount: &BigUint, ); //Emitted whenever an address harvests a claim from the smart contract @@ -30,6 +47,6 @@ pub trait EventsModule { &self, #[indexed] address: &ManagedAddress, #[indexed] claim_type: &ClaimType, - amount: &BigUint, + #[indexed] amount: &BigUint, ); } diff --git a/src/lib.rs b/src/lib.rs index ed49cef..4a7df57 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,12 +31,14 @@ pub trait ClaimsContract: self.claim_token().set(&token); } - //Endpoint available for the owner of the smart contract to pause claim harvesting. Cannot be called while harvesting is already paused. - #[only_owner] + //Endpoint available for privileged addresses of the smart contract to pause claim harvesting. Cannot be called while harvesting is already paused. #[endpoint(pause)] fn pause(&self) { require!(!self.is_paused().get(), "Contract is already paused"); + let caller = self.blockchain().get_caller(); + self.require_address_is_privileged(&caller); self.is_paused().set(true); + self.harvest_paused_event(&caller); } //Endpoint avbailable for the owner of the smart contract to resume claim harvesting. Cannot be called while harvesting is already unpaused. @@ -45,14 +47,51 @@ pub trait ClaimsContract: fn unpause(&self) { require!(self.is_paused().get(), "Contract is already unpaused"); self.is_paused().set(false); + self.harvest_unpaused_event(); } - //Endpoint available for the owner of the smart contract to add a claim of a specific claim type for a specific address. + //Endpoint available for owner in order to add an address to the list of privileged addresses #[only_owner] + #[endpoint(addPrivilegedAddress)] + fn add_privileged_address(&self, address: ManagedAddress) { + let owner = self.blockchain().get_owner_address(); + require!( + owner != address, + "Owner cannot be added to priviledged addresses" + ); + let privileged_addresses = self.privileged_addresses(); + require!( + !privileged_addresses.contains(&address), + "Address is already privileged" + ); + require!( + privileged_addresses.len() < 2usize, + "Maximum number of priviledged addresses reached" + ); + self.privileged_address_added_event(&address); + self.privileged_addresses().insert(address); + } + + //Endpoint available for owner in order to remove an address from the list of privileged addresses + #[only_owner] + #[endpoint(removePrivilegedAddress)] + fn remove_privileged_address(&self, address: ManagedAddress) { + let privileged_addresses = self.privileged_addresses(); + require!( + privileged_addresses.contains(&address), + "Address is not privileged" + ); + self.privileged_address_removed_event(&address); + self.privileged_addresses().remove(&address); + } + + //Endpoint available for privileged addresses of the smart contract to add a claim of a specific claim type for a specific address. #[payable("*")] #[endpoint(addClaim)] fn add_claim(&self, address: &ManagedAddress, claim_type: ClaimType) { self.require_claim_token_is_set(); + let caller = self.blockchain().get_caller(); + self.require_address_is_privileged(&caller); let (payment_amount, payment_token) = self.call_value().payment_token_pair(); let current_claim = self.claim(address, &claim_type).get(); let timestamp = self.blockchain().get_block_timestamp(); @@ -63,11 +102,10 @@ pub trait ClaimsContract: .set(current_claim + &payment_amount); //Update the last modification date of the claim to the current timestamp self.claim_modify_date(address, &claim_type).set(timestamp); - self.claim_added_event(address, &claim_type, &payment_amount); + self.claim_added_event(&caller, &address, &claim_type, &payment_amount); } - //Endpoint available for the owner of the smart contract to add a bulk of claims of different claim types for different specific addresses. - #[only_owner] + //Endpoint available for privileged addresses of the smart contract to add a bulk of claims of different claim types for different specific addresses. #[payable("*")] #[endpoint(addClaims)] fn add_claims( @@ -76,6 +114,8 @@ pub trait ClaimsContract: ) { self.require_claim_token_is_set(); self.require_number_of_claims_in_bulk_is_valid(&claims.len()); + let caller = self.blockchain().get_caller(); + self.require_address_is_privileged(&caller); let (payment_amount, payment_token) = self.call_value().payment_token_pair(); let timestamp = self.blockchain().get_block_timestamp(); self.require_token_is_correct(payment_token); @@ -91,7 +131,7 @@ pub trait ClaimsContract: .set(current_claim + &amount); self.claim_modify_date(&address, &claim_type).set(timestamp); sum_of_claims += &amount; - self.claim_added_event(&address, &claim_type, &amount); + self.claim_added_event(&caller, &address, &claim_type, &amount); } //Panic if the amount of tokens sent by the owner to the endpoint are not equal to the sum of the claims added to the contract require!( @@ -116,7 +156,7 @@ pub trait ClaimsContract: .set(current_claim - &amount); //Update the modification date of the claim to the current timestamp self.claim_modify_date(address, &claim_type).set(timestamp); - self.claim_removed_event(address, &claim_type, &amount); + self.claim_removed_event(&address, &claim_type, &amount); //Send the removed tokens from the claim back to the owner of the smart contract self.send().direct(&owner, &claim_token, 0, &amount, &[]); } diff --git a/src/requirements.rs b/src/requirements.rs index 4c9693f..236bdb4 100644 --- a/src/requirements.rs +++ b/src/requirements.rs @@ -40,4 +40,13 @@ pub trait RequirementsModule: crate::storage::StorageModule { "Exceeded maximum number of claims per operation (200)" ); } + + //Checks whether the address has the special rights needed in case of some special operations + fn require_address_is_privileged(&self, address: &ManagedAddress) { + require!( + self.privileged_addresses().contains(address) + || &self.blockchain().get_owner_address() == address, + "Address doesn't have the privilege to use this operation" + ); + } } diff --git a/src/storage.rs b/src/storage.rs index 1f15e71..33522c0 100644 --- a/src/storage.rs +++ b/src/storage.rs @@ -36,4 +36,8 @@ pub trait StorageModule { #[view(isPaused)] #[storage_mapper("isPaused")] fn is_paused(&self) -> SingleValueMapper; + + #[view(viewPrivilegedAddresses)] + #[storage_mapper("privilegedAddresses")] + fn privileged_addresses(&self) -> SetMapper; } diff --git a/tests/rust_tests.rs b/tests/rust_tests.rs index 00d3cc1..4959385 100644 --- a/tests/rust_tests.rs +++ b/tests/rust_tests.rs @@ -23,6 +23,7 @@ where pub contract_wrapper: ContractObjWrapper, ContractObjBuilder>, pub first_user_address: Address, pub second_user_address: Address, + pub third_user_address: Address, } fn setup_contract( @@ -35,6 +36,7 @@ where let mut blockchain_wrapper = BlockchainStateWrapper::new(); let first_user_address = blockchain_wrapper.create_user_account(&rust_zero); let second_user_address = blockchain_wrapper.create_user_account(&rust_zero); + let third_user_address = blockchain_wrapper.create_user_account(&rust_zero); let owner_address = blockchain_wrapper.create_user_account(&rust_biguint!(OWNER_EGLD_BALANCE)); let cf_wrapper = blockchain_wrapper.create_sc_account( &rust_zero, @@ -46,6 +48,7 @@ where blockchain_wrapper.set_esdt_balance(&owner_address, WRONG_TOKEN_ID, &rust_biguint!(1_000_000)); blockchain_wrapper.set_esdt_balance(&first_user_address, TOKEN_ID, &rust_biguint!(1_000)); blockchain_wrapper.set_esdt_balance(&second_user_address, TOKEN_ID, &rust_biguint!(0)); + blockchain_wrapper.set_esdt_balance(&third_user_address, TOKEN_ID, &rust_biguint!(1_000)); blockchain_wrapper .execute_tx(&owner_address, &cf_wrapper, &rust_zero, |sc| { @@ -58,6 +61,12 @@ where }) .assert_ok(); + blockchain_wrapper + .execute_tx(&owner_address, &cf_wrapper, &rust_zero, |sc| { + sc.add_privileged_address(managed_address!(&first_user_address)); + }) + .assert_ok(); + blockchain_wrapper .execute_query(&cf_wrapper, |sc| { assert_eq!(sc.is_paused().get(), true); @@ -77,6 +86,7 @@ where owner_address, first_user_address, second_user_address, + third_user_address, contract_wrapper: cf_wrapper, } } @@ -98,10 +108,14 @@ fn deploy_test() { } #[test] //Tests wether pausing and unpausing the contract works correctly + //Tests wether trying to change the pause state to the already set state returns an error + //Tests wether privileged addresses can pause harvesting, but normal addresses cannnot fn pause_unpause_test() { let mut setup = setup_contract(claims::contract_obj); let b_wrapper = &mut setup.blockchain_wrapper; let owner_address = &setup.owner_address; + let first_user_address = &setup.first_user_address; + let second_user_address = &setup.second_user_address; b_wrapper .execute_query(&setup.contract_wrapper, |sc| { @@ -164,6 +178,139 @@ fn pause_unpause_test() { assert_eq!(sc.is_paused().get(), false); }) .assert_ok(); + + b_wrapper + .execute_tx( + &first_user_address, + &setup.contract_wrapper, + &rust_biguint!(0), + |sc| { + sc.pause(); + }, + ) + .assert_ok(); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!(sc.is_paused().get(), true); + }) + .assert_ok(); + + b_wrapper + .execute_tx( + &owner_address, + &setup.contract_wrapper, + &rust_biguint!(0), + |sc| { + sc.unpause(); + }, + ) + .assert_ok(); + + b_wrapper + .execute_tx( + &second_user_address, + &setup.contract_wrapper, + &rust_biguint!(0), + |sc| { + sc.pause(); + }, + ) + .assert_user_error("Address doesn't have the privilege to use this operation"); + + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!(sc.is_paused().get(), false); + }) + .assert_ok(); +} + +#[test] //Tests wether adding and removing privileged addresses works as expected + //Tests if trying give privileges to an address that already has them returns an error + //Tests if trying to offer privileges to the owner of the smart contract returns an error + //Tests if trying to remove the privileges that is not privileged returns an error +fn add_and_remove_privileged_addresses_test() { + let mut setup = setup_contract(claims::contract_obj); + let b_wrapper = &mut setup.blockchain_wrapper; + let owner_address = &setup.owner_address; + let first_user_addr = &setup.first_user_address; + let second_user_addr = &setup.second_user_address; + let third_user_addr = &setup.third_user_address; + + b_wrapper + .execute_tx( + &owner_address, + &setup.contract_wrapper, + &rust_biguint!(0), + |sc| { + sc.add_privileged_address(managed_address!(second_user_addr)); + }, + ) + .assert_ok(); + b_wrapper + .execute_tx( + &owner_address, + &setup.contract_wrapper, + &rust_biguint!(0), + |sc| { + sc.add_privileged_address(managed_address!(owner_address)); + }, + ) + .assert_user_error("Owner cannot be added to priviledged addresses"); + b_wrapper + .execute_tx( + &owner_address, + &setup.contract_wrapper, + &rust_biguint!(0), + |sc| { + sc.add_privileged_address(managed_address!(third_user_addr)); + }, + ) + .assert_user_error("Maximum number of priviledged addresses reached"); + b_wrapper + .execute_tx( + &owner_address, + &setup.contract_wrapper, + &rust_biguint!(0), + |sc| { + sc.remove_privileged_address(managed_address!(third_user_addr)); + }, + ) + .assert_user_error("Address is not privileged"); + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.privileged_addresses() + .contains(&managed_address!(first_user_addr)) + && sc + .privileged_addresses() + .contains(&managed_address!(second_user_addr)), + true + ); + }) + .assert_ok(); + b_wrapper + .execute_tx( + &owner_address, + &setup.contract_wrapper, + &rust_biguint!(0), + |sc| { + sc.remove_privileged_address(managed_address!(second_user_addr)); + }, + ) + .assert_ok(); + b_wrapper + .execute_query(&setup.contract_wrapper, |sc| { + assert_eq!( + sc.privileged_addresses() + .contains(&managed_address!(first_user_addr)) + && !sc + .privileged_addresses() + .contains(&managed_address!(second_user_addr)), + true + ); + }) + .assert_ok(); } #[test] //Tests wether adding and removing singular claims works as expected @@ -637,6 +784,103 @@ fn add_and_remove_claims_test() { .assert_ok(); } +#[test] //Tests wether privileged addresses can add a claim, but a non-priviledged address cannot +fn add_claim_privileged_test() { + let mut setup = setup_contract(claims::contract_obj); + let b_wrapper = &mut setup.blockchain_wrapper; + let user_addr = &setup.first_user_address; + let user_addr_3 = &setup.third_user_address; + b_wrapper + .execute_esdt_transfer( + user_addr, + &setup.contract_wrapper, + TOKEN_ID, + 0, + &rust_biguint!(1_000), + |sc| { + sc.add_claim(&managed_address!(user_addr), storage::ClaimType::Airdrop); + }, + ) + .assert_ok(); + b_wrapper + .execute_esdt_transfer( + user_addr_3, + &setup.contract_wrapper, + TOKEN_ID, + 0, + &rust_biguint!(1_000), + |sc| { + sc.add_claim(&managed_address!(user_addr), storage::ClaimType::Airdrop); + }, + ) + .assert_user_error("Address doesn't have the privilege to use this operation"); +} + +#[test] //Tests wether privileged addresses can add claims, but a non-priviledged address cannot +fn add_claims_privileged_test() { + let mut setup = setup_contract(claims::contract_obj); + let b_wrapper = &mut setup.blockchain_wrapper; + let user_addr = &setup.first_user_address; + let user_addr_3 = &setup.third_user_address; + b_wrapper + .execute_esdt_transfer( + user_addr, + &setup.contract_wrapper, + TOKEN_ID, + 0, + &rust_biguint!(1_000), + |sc| { + let mut args = MultiValueEncoded::new(); + args.push(MultiValue3( + ( + managed_address!(user_addr), + storage::ClaimType::Airdrop, + managed_biguint!(600), + ) + .into(), + )); + args.push(MultiValue3( + ( + managed_address!(user_addr_3), + storage::ClaimType::Allocation, + managed_biguint!(400), + ) + .into(), + )); + sc.add_claims(args); + }, + ) + .assert_ok(); + b_wrapper + .execute_esdt_transfer( + user_addr_3, + &setup.contract_wrapper, + TOKEN_ID, + 0, + &rust_biguint!(100), + |sc| { + let mut args = MultiValueEncoded::new(); + args.push(MultiValue3( + ( + managed_address!(user_addr), + storage::ClaimType::Airdrop, + managed_biguint!(600), + ) + .into(), + )); + args.push(MultiValue3( + ( + managed_address!(user_addr_3), + storage::ClaimType::Allocation, + managed_biguint!(400), + ) + .into(), + )); + sc.add_claims(args); + }, + ) + .assert_user_error("Address doesn't have the privilege to use this operation"); +} #[test] //Tests whether the transaction to add a token fails in the case in which a different token than the claim token is sent fn add_claim_wrong_token_test() { let mut setup = setup_contract(claims::contract_obj); diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index 3f18181..4e0869a 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -9,17 +9,20 @@ elrond_wasm_node::wasm_endpoints! { ( addClaim addClaims + addPrivilegedAddress claim isPaused pause removeClaim removeClaims + removePrivilegedAddress setClaimToken unpause viewClaim viewClaimModifyDate viewClaimWithDate viewClaims + viewPrivilegedAddresses viewTokenIdentifier ) } From d41b4bb0ecdf73ab79cac9eefe2ade6fe1da53c9 Mon Sep 17 00:00:00 2001 From: damienen Date: Wed, 3 Aug 2022 23:08:30 +0300 Subject: [PATCH 22/25] Modified requirements order --- src/lib.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4a7df57..333493f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,11 +54,6 @@ pub trait ClaimsContract: #[only_owner] #[endpoint(addPrivilegedAddress)] fn add_privileged_address(&self, address: ManagedAddress) { - let owner = self.blockchain().get_owner_address(); - require!( - owner != address, - "Owner cannot be added to priviledged addresses" - ); let privileged_addresses = self.privileged_addresses(); require!( !privileged_addresses.contains(&address), @@ -68,6 +63,11 @@ pub trait ClaimsContract: privileged_addresses.len() < 2usize, "Maximum number of priviledged addresses reached" ); + let owner = self.blockchain().get_owner_address(); + require!( + owner != address, + "Owner cannot be added to priviledged addresses" + ); self.privileged_address_added_event(&address); self.privileged_addresses().insert(address); } @@ -90,13 +90,13 @@ pub trait ClaimsContract: #[endpoint(addClaim)] fn add_claim(&self, address: &ManagedAddress, claim_type: ClaimType) { self.require_claim_token_is_set(); + let (payment_amount, payment_token) = self.call_value().payment_token_pair(); + self.require_token_is_correct(payment_token); + self.require_value_not_zero(&payment_amount); let caller = self.blockchain().get_caller(); self.require_address_is_privileged(&caller); - let (payment_amount, payment_token) = self.call_value().payment_token_pair(); let current_claim = self.claim(address, &claim_type).get(); let timestamp = self.blockchain().get_block_timestamp(); - self.require_token_is_correct(payment_token); - self.require_value_not_zero(&payment_amount); //Add the amount of the tokens sent to the current claim reservation self.claim(address, &claim_type) .set(current_claim + &payment_amount); @@ -114,12 +114,12 @@ pub trait ClaimsContract: ) { self.require_claim_token_is_set(); self.require_number_of_claims_in_bulk_is_valid(&claims.len()); - let caller = self.blockchain().get_caller(); - self.require_address_is_privileged(&caller); let (payment_amount, payment_token) = self.call_value().payment_token_pair(); - let timestamp = self.blockchain().get_block_timestamp(); self.require_token_is_correct(payment_token); self.require_value_not_zero(&payment_amount); + let caller = self.blockchain().get_caller(); + self.require_address_is_privileged(&caller); + let timestamp = self.blockchain().get_block_timestamp(); //Initialize the sum of claims to be added to zero let mut sum_of_claims = BigUint::zero(); //Iterate over the claims provided as argument and proceeds similarly to the add_claim endpoint for each one @@ -145,12 +145,12 @@ pub trait ClaimsContract: #[endpoint(removeClaim)] fn remove_claim(&self, address: &ManagedAddress, claim_type: ClaimType, amount: BigUint) { self.require_claim_token_is_set(); + self.require_value_not_zero(&amount); let current_claim = self.claim(address, &claim_type).get(); + self.require_remove_claim_is_valid(¤t_claim, &amount); let owner = self.blockchain().get_owner_address(); let claim_token = self.claim_token().get(); let timestamp = self.blockchain().get_block_timestamp(); - self.require_value_not_zero(&amount); - self.require_remove_claim_is_valid(¤t_claim, &amount); //Remove the amount of tokens given as argument from the current claim reservation self.claim(address, &claim_type) .set(current_claim - &amount); @@ -177,10 +177,10 @@ pub trait ClaimsContract: //Iterate over the claims provided as argument and proceeds similarly to the remove_claim endpoint for each one for item in claims.into_iter() { let (address, claim_type, amount) = item.into_tuple(); - let current_claim = self.claim(&address, &claim_type).get(); - self.claim_modify_date(&address, &claim_type).set(timestamp); self.require_value_not_zero(&amount); + let current_claim = self.claim(&address, &claim_type).get(); self.require_remove_claim_is_valid(¤t_claim, &amount); + self.claim_modify_date(&address, &claim_type).set(timestamp); sum_of_claims += &amount; self.claim(&address, &claim_type) .set(current_claim - &amount); From 089ca1f5171e25740bc968891349bbf1c455e311 Mon Sep 17 00:00:00 2001 From: damienen Date: Wed, 3 Aug 2022 23:17:26 +0300 Subject: [PATCH 23/25] Changed privileged test --- tests/rust_tests.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/rust_tests.rs b/tests/rust_tests.rs index 4959385..cf2587b 100644 --- a/tests/rust_tests.rs +++ b/tests/rust_tests.rs @@ -247,16 +247,6 @@ fn add_and_remove_privileged_addresses_test() { }, ) .assert_ok(); - b_wrapper - .execute_tx( - &owner_address, - &setup.contract_wrapper, - &rust_biguint!(0), - |sc| { - sc.add_privileged_address(managed_address!(owner_address)); - }, - ) - .assert_user_error("Owner cannot be added to priviledged addresses"); b_wrapper .execute_tx( &owner_address, @@ -299,6 +289,16 @@ fn add_and_remove_privileged_addresses_test() { }, ) .assert_ok(); + b_wrapper + .execute_tx( + &owner_address, + &setup.contract_wrapper, + &rust_biguint!(0), + |sc| { + sc.add_privileged_address(managed_address!(owner_address)); + }, + ) + .assert_user_error("Owner cannot be added to priviledged addresses"); b_wrapper .execute_query(&setup.contract_wrapper, |sc| { assert_eq!( From ab9036d5a3e3707af480f1ca92b949e17b2c8bf3 Mon Sep 17 00:00:00 2001 From: Mark Paul Date: Thu, 4 Aug 2022 23:33:35 +1000 Subject: [PATCH 24/25] fixed small issue in readme --- README.md | 4 ++-- tests/rust_tests.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e0dbb89..ad3d8f6 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,8 @@ The init function is called when deploying or upgrading the smart contract. It r ``` Endpoint that sets the claims token. It can only be used once and it can only be called by the owner of the contract. -Call structure: "setRewardToken" + "@" + TokenIdentifier hex encoded -Example: "setRewardToken@49544845554d2d613631333137" +Call structure: "setClaimToken" + "@" + TokenIdentifier hex encoded +Example: "setClaimToken@49544845554d2d613631333137" ### Only owner endpoints diff --git a/tests/rust_tests.rs b/tests/rust_tests.rs index cf2587b..929778e 100644 --- a/tests/rust_tests.rs +++ b/tests/rust_tests.rs @@ -881,6 +881,7 @@ fn add_claims_privileged_test() { ) .assert_user_error("Address doesn't have the privilege to use this operation"); } + #[test] //Tests whether the transaction to add a token fails in the case in which a different token than the claim token is sent fn add_claim_wrong_token_test() { let mut setup = setup_contract(claims::contract_obj); From 2bb100415ed080f0f2e4845988bc01f01e4d1fd7 Mon Sep 17 00:00:00 2001 From: Mark Paul Date: Fri, 5 Aug 2022 16:53:03 +1000 Subject: [PATCH 25/25] fixed small issues in interaction snippets --- interaction/devnet.snippets.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interaction/devnet.snippets.sh b/interaction/devnet.snippets.sh index 37a9d24..99acd7c 100644 --- a/interaction/devnet.snippets.sh +++ b/interaction/devnet.snippets.sh @@ -13,7 +13,7 @@ deploy(){ erdpy --verbose contract deploy \ --bytecode output/claims.wasm \ --outfile deployOutput \ - --metadata-payable \ + --metadata-not-readable \ --pem wallet.pem \ --proxy ${PROXY} \ --chain ${CHAIN_ID} \ @@ -66,7 +66,7 @@ unpause(){ addPrivilegedAddress(){ # $1 = address to which to give privileges - address="0x$(erdpy wallet bech32 --decode ${2})" + address="0x$(erdpy wallet bech32 --decode ${1})" erdpy --verbose contract call ${ADDRESS} \ --recall-nonce \ --pem=${WALLET} \ @@ -81,7 +81,7 @@ addPrivilegedAddress(){ removePrivilegedAddress(){ # $1 = address to which to remove privileges - address="0x$(erdpy wallet bech32 --decode ${2})" + address="0x$(erdpy wallet bech32 --decode ${1})" erdpy --verbose contract call ${ADDRESS} \ --recall-nonce \ --pem=${WALLET} \