From 7a9962c23b1d032951e6ba7de66966335a7ce749 Mon Sep 17 00:00:00 2001 From: Guy Date: Thu, 18 Nov 2021 22:43:35 -0400 Subject: [PATCH 1/6] fixed potential amount precision errors --- contracts/airdrop/src/contract.rs | 6 +++- contracts/airdrop/src/handle.rs | 35 ++++++++++++++----- contracts/airdrop/src/state.rs | 22 +++++++++++- .../src/contract_helpers/initializer.rs | 1 - .../tests/testnet_integration.rs | 8 ++--- packages/shade_protocol/src/airdrop.rs | 1 + 6 files changed, 57 insertions(+), 16 deletions(-) diff --git a/contracts/airdrop/src/contract.rs b/contracts/airdrop/src/contract.rs index afe707a81..f9e331232 100644 --- a/contracts/airdrop/src/contract.rs +++ b/contracts/airdrop/src/contract.rs @@ -5,7 +5,7 @@ use shade_protocol::{ QueryMsg, Config } }; -use crate::{state::{config_w, reward_w, claim_status_w}, +use crate::{state::{config_w, reward_w, claim_status_w, user_total_claimed_w, total_claimed_w}, handle::{try_update_config, try_add_tasks, try_complete_task, try_claim}, query }; use shade_protocol::airdrop::RequiredTask; @@ -55,10 +55,14 @@ pub fn init( let key = reward.address.to_string(); reward_w(&mut deps.storage).save(key.as_bytes(), &reward)?; + user_total_claimed_w(&mut deps.storage).save(key.as_bytes(), &Uint128::zero())?; // Save the initial claim claim_status_w(&mut deps.storage, 0).save(key.as_bytes(), &false)?; } + // Initialize claim amount + total_claimed_w(&mut deps.storage).save(&Uint128::zero())?; + Ok(InitResponse { messages: vec![], log: vec![] diff --git a/contracts/airdrop/src/handle.rs b/contracts/airdrop/src/handle.rs index 677734297..08003a214 100644 --- a/contracts/airdrop/src/handle.rs +++ b/contracts/airdrop/src/handle.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{to_binary, Api, Env, Extern, HandleResponse, Querier, StdError, StdResult, Storage, HumanAddr, Uint128}; -use crate::state::{config_r, config_w, reward_r, claim_status_w, claim_status_r}; +use crate::state::{config_r, config_w, reward_r, claim_status_w, claim_status_r, user_total_claimed_w, total_claimed_w}; use shade_protocol::airdrop::{HandleAnswer, RequiredTask}; use shade_protocol::generic_response::ResponseStatus; use secret_toolkit::snip20::mint_msg; @@ -95,7 +95,7 @@ pub fn try_complete_task( Ok(false) } else { - Err(StdError::Unauthorized { backtrace: None }) + Err(StdError::unauthorized()) } })?; @@ -131,26 +131,45 @@ pub fn try_claim( let user = env.message.sender.clone(); let user_key = user.to_string(); - let eligible_amount = reward_r(&deps.storage).load( - user.to_string().as_bytes())?.amount; + let eligible_amount = reward_r(&deps.storage).load(user_key.as_bytes())?.amount; + let mut claimed_percentage = Uint128::zero(); let mut total = Uint128::zero(); for (i, task) in config.task_claim.iter().enumerate() { // Check if completed - let state = claim_status_r(&deps.storage, i).may_load(user_key.as_bytes())?; + let state = claim_status_r(&deps.storage, i).may_load( + user_key.as_bytes())?; match state { None => {} Some(claimed) => { + claimed_percentage += task.percent; if !claimed { - claim_status_w(&mut deps.storage, i).save(user_key.as_bytes(), &true)?; - total += task.percent.multiply_ratio(eligible_amount, Uint128(100)); + claim_status_w(&mut deps.storage, i).save( + user_key.as_bytes(), &true)?; + total += task.percent.multiply_ratio( + eligible_amount, Uint128(100)); } } }; } + // Update redeem info + let mut redeem_amount = total; + user_total_claimed_w(&mut deps.storage).update( + user_key.as_bytes(), |total_claimed| { + // Fix division issues + if claimed_percentage == Uint128(100) { + redeem_amount = (eligible_amount - total_claimed.unwrap())?; + } + Ok(total_claimed.unwrap() + redeem_amount) + })?; + + total_claimed_w(&mut deps.storage).update(|total_claimed| { + Ok(total_claimed + claimed_percentage) + })?; + // Redeem - let messages = vec![mint_msg(user, total, + let messages = vec![mint_msg(user, redeem_amount, None, 1, config.airdrop_snip20.code_hash, config.airdrop_snip20.address)?]; diff --git a/contracts/airdrop/src/state.rs b/contracts/airdrop/src/state.rs index 1c7f6cd0e..6f4ce14b8 100644 --- a/contracts/airdrop/src/state.rs +++ b/contracts/airdrop/src/state.rs @@ -1,9 +1,11 @@ -use cosmwasm_std::Storage; +use cosmwasm_std::{Storage, Uint128}; use cosmwasm_storage::{singleton, singleton_read, ReadonlySingleton, Singleton, bucket, Bucket, bucket_read, ReadonlyBucket}; use shade_protocol::airdrop::{Config, Reward}; pub static CONFIG_KEY: &[u8] = b"config"; pub static REWARDS_KEY: &[u8] = b"rewards"; +pub static TOTAL_CLAIMED_KEY: &[u8] = b"total_claimed"; +pub static USER_TOTAL_CLAIMED_KEY: &[u8] = b"user_total_claimed"; pub fn config_w(storage: &mut S) -> Singleton { singleton(storage, CONFIG_KEY) @@ -28,4 +30,22 @@ pub fn claim_status_r(storage: & S, index: usize) -> ReadonlyBucket< pub fn claim_status_w(storage: &mut S, index: usize) -> Bucket { bucket(&[index as u8], storage) +} + +// Total claimed +pub fn total_claimed_r(storage: &S) -> ReadonlySingleton { + singleton_read(storage, TOTAL_CLAIMED_KEY) +} + +pub fn total_claimed_w(storage: &mut S) -> Singleton { + singleton(storage, TOTAL_CLAIMED_KEY) +} + +// Total user claimed +pub fn user_total_claimed_r(storage: & S) -> ReadonlyBucket { + bucket_read(USER_TOTAL_CLAIMED_KEY, storage) +} + +pub fn user_total_claimed_w(storage: &mut S) -> Bucket { + bucket(USER_TOTAL_CLAIMED_KEY, storage) } \ No newline at end of file diff --git a/packages/network_integration/src/contract_helpers/initializer.rs b/packages/network_integration/src/contract_helpers/initializer.rs index 6987dd6b0..9d872c369 100644 --- a/packages/network_integration/src/contract_helpers/initializer.rs +++ b/packages/network_integration/src/contract_helpers/initializer.rs @@ -4,7 +4,6 @@ use shade_protocol::{snip20::{InitialBalance}, snip20, initializer, initializer::Snip20ContractInfo}; use crate::{utils::{print_header, generate_label, print_contract, print_warning, STORE_GAS, GAS, VIEW_KEY, ACCOUNT_KEY, INITIALIZER_FILE}, - contract_helpers::governance::add_contract, contract_helpers::minter::get_balance}; use secretcli::{cli_types::NetContract, secretcli::{test_contract_handle, test_inst_init, list_contracts_by_code}}; diff --git a/packages/network_integration/tests/testnet_integration.rs b/packages/network_integration/tests/testnet_integration.rs index 1fdd7c240..2160d8cbf 100644 --- a/packages/network_integration/tests/testnet_integration.rs +++ b/packages/network_integration/tests/testnet_integration.rs @@ -1,11 +1,9 @@ use colored::*; use serde_json::Result; use cosmwasm_std::{HumanAddr, Uint128, to_binary}; -use secretcli::{cli_types::NetContract, - secretcli::{account_address, query_contract, test_contract_handle, - test_inst_init, list_contracts_by_code}}; -use shade_protocol::{snip20::{InitConfig, InitialBalance}, snip20, governance, staking, - micro_mint, band, oracle, asset::Contract, airdrop, +use secretcli::{secretcli::{account_address, query_contract, test_contract_handle, test_inst_init}}; +use shade_protocol::{snip20::{InitConfig}, snip20, governance, staking, + band, oracle, asset::Contract, airdrop, airdrop::{Reward, RequiredTask}, governance::{UserVote, Vote, ProposalStatus}, generic_response::ResponseStatus}; use network_integration::{utils::{print_header, print_warning, generate_label, print_contract, diff --git a/packages/shade_protocol/src/airdrop.rs b/packages/shade_protocol/src/airdrop.rs index e79573f8f..9d001e831 100644 --- a/packages/shade_protocol/src/airdrop.rs +++ b/packages/shade_protocol/src/airdrop.rs @@ -96,6 +96,7 @@ impl Query for QueryMsg { #[derive(Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum QueryAnswer { + // TODO: add total claimed in config Config { config: Config }, Dates { start: u64, end: Option }, Eligibility { From 4e003bf24e07dfadea8c74fe7a4a6ccb2abaed22 Mon Sep 17 00:00:00 2001 From: Guy Garcia Date: Fri, 19 Nov 2021 23:21:19 -0500 Subject: [PATCH 2/6] Replaced minting with sending --- contracts/airdrop/src/handle.rs | 6 ++-- .../tests/testnet_integration.rs | 36 ++++++++++++------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/contracts/airdrop/src/handle.rs b/contracts/airdrop/src/handle.rs index 08003a214..229116431 100644 --- a/contracts/airdrop/src/handle.rs +++ b/contracts/airdrop/src/handle.rs @@ -2,7 +2,7 @@ use cosmwasm_std::{to_binary, Api, Env, Extern, HandleResponse, Querier, StdErro use crate::state::{config_r, config_w, reward_r, claim_status_w, claim_status_r, user_total_claimed_w, total_claimed_w}; use shade_protocol::airdrop::{HandleAnswer, RequiredTask}; use shade_protocol::generic_response::ResponseStatus; -use secret_toolkit::snip20::mint_msg; +use secret_toolkit::snip20::send_msg; pub fn try_update_config( deps: &mut Extern, @@ -169,8 +169,8 @@ pub fn try_claim( })?; // Redeem - let messages = vec![mint_msg(user, redeem_amount, - None, 1, + let messages = vec![send_msg(user, redeem_amount, + None, None, 0, config.airdrop_snip20.code_hash, config.airdrop_snip20.address)?]; diff --git a/packages/network_integration/tests/testnet_integration.rs b/packages/network_integration/tests/testnet_integration.rs index 2160d8cbf..93a4855ae 100644 --- a/packages/network_integration/tests/testnet_integration.rs +++ b/packages/network_integration/tests/testnet_integration.rs @@ -16,11 +16,15 @@ use network_integration::{utils::{print_header, print_warning, generate_label, p minter::{initialize_minter, setup_minters, get_balance}, stake::setup_staker}}; use std::{thread, time}; +use shade_protocol::snip20::InitialBalance; #[test] fn run_airdrop() -> Result<()> { let account = account_address(ACCOUNT_KEY)?; + let half_airdrop = Uint128(500000); + let full_airdrop = Uint128(1000000); + /// Initialize dummy snip20 print_header("\nInitializing snip20"); @@ -29,7 +33,9 @@ fn run_airdrop() -> Result<()> { admin: None, symbol: "TEST".to_string(), decimals: 6, - initial_balances: None, + initial_balances: Some(vec![InitialBalance{ + address: HumanAddr::from(account.clone()), + amount: full_airdrop }]), prng_seed: Default::default(), config: Some(InitConfig { public_total_supply: Some(true), @@ -53,12 +59,6 @@ fn run_airdrop() -> Result<()> { Some("test"), None)?; } - /// Assert that we start with nothing - assert_eq!(Uint128(0), get_balance(&snip, account.clone())); - - let half_airdrop = Uint128(500000); - let full_airdrop = Uint128(1000000); - print_header("Initializing airdrop"); let airdrop_init_msg = airdrop::InitMsg { @@ -84,6 +84,18 @@ fn run_airdrop() -> Result<()> { Some("test"))?; print_contract(&airdrop); + /// Assert that we start with nothing + { + test_contract_handle(&snip20::HandleMsg::Send { + recipient: HumanAddr::from(airdrop.address.clone()), + amount: full_airdrop, + msg: None, + memo: None, + padding: None + }, &snip, ACCOUNT_KEY, Some(GAS), Some("test"), None)?; + } + assert_eq!(Uint128(0), get_balance(&snip, account.clone())); + /// Query that airdrop is allowed { let msg = airdrop::QueryMsg::GetEligibility { @@ -101,11 +113,11 @@ fn run_airdrop() -> Result<()> { } } - /// Register airdrop as allowed minter - test_contract_handle(&snip20::HandleMsg::SetMinters { - minters: vec![HumanAddr::from(airdrop.address.clone())], padding: None }, - &snip, ACCOUNT_KEY, Some(GAS), - Some("test"), None)?; + // /// Register airdrop as allowed minter + // test_contract_handle(&snip20::HandleMsg::SetMinters { + // minters: vec![HumanAddr::from(airdrop.address.clone())], padding: None }, + // &snip, ACCOUNT_KEY, Some(GAS), + // Some("test"), None)?; print_warning("Claiming half of the airdrop"); /// Claim airdrop From 57d5919529c905a054afbebde959918e1b1b1274 Mon Sep 17 00:00:00 2001 From: Guy Garcia Date: Fri, 19 Nov 2021 23:21:36 -0500 Subject: [PATCH 3/6] removed unused file --- packages/network_integration/src/main.rs | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 packages/network_integration/src/main.rs diff --git a/packages/network_integration/src/main.rs b/packages/network_integration/src/main.rs deleted file mode 100644 index 1d20dd688..000000000 --- a/packages/network_integration/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Network integration test crate") -} \ No newline at end of file From c5d3ba4f26fa96e6ed57932251857399cc42d998 Mon Sep 17 00:00:00 2001 From: Guy Garcia Date: Sat, 20 Nov 2021 00:56:44 -0500 Subject: [PATCH 4/6] Expanded queries --- contracts/airdrop/src/contract.rs | 23 +++++++++++++---------- contracts/airdrop/src/query.rs | 12 +++++++----- packages/shade_protocol/src/airdrop.rs | 7 ++++--- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/contracts/airdrop/src/contract.rs b/contracts/airdrop/src/contract.rs index f9e331232..951b4ef02 100644 --- a/contracts/airdrop/src/contract.rs +++ b/contracts/airdrop/src/contract.rs @@ -34,12 +34,25 @@ pub fn init( return Err(StdError::GenericErr { msg: "tasks above 100%".to_string(), backtrace: None }) } + // Store the delegators list + let mut airdrop_total = Uint128::zero(); + for reward in msg.rewards { + let key = reward.address.to_string(); + + reward_w(&mut deps.storage).save(key.as_bytes(), &reward)?; + airdrop_total += reward.amount; + user_total_claimed_w(&mut deps.storage).save(key.as_bytes(), &Uint128::zero())?; + // Save the initial claim + claim_status_w(&mut deps.storage, 0).save(key.as_bytes(), &false)?; + } + let config = Config{ admin: match msg.admin { None => { env.message.sender.clone() } Some(admin) => { admin } }, airdrop_snip20: msg.airdrop_token.clone(), + airdrop_total, task_claim, start_date: match msg.start_time { None => env.block.time, @@ -50,16 +63,6 @@ pub fn init( config_w(&mut deps.storage).save(&config)?; - // Store the delegators list - for reward in msg.rewards { - let key = reward.address.to_string(); - - reward_w(&mut deps.storage).save(key.as_bytes(), &reward)?; - user_total_claimed_w(&mut deps.storage).save(key.as_bytes(), &Uint128::zero())?; - // Save the initial claim - claim_status_w(&mut deps.storage, 0).save(key.as_bytes(), &false)?; - } - // Initialize claim amount total_claimed_w(&mut deps.storage).save(&Uint128::zero())?; diff --git a/contracts/airdrop/src/query.rs b/contracts/airdrop/src/query.rs index b191ecb0b..0c15d2ce1 100644 --- a/contracts/airdrop/src/query.rs +++ b/contracts/airdrop/src/query.rs @@ -1,11 +1,13 @@ use cosmwasm_std::{Api, Extern, Querier, StdResult, Storage, HumanAddr, Uint128}; use shade_protocol::airdrop::{QueryAnswer}; use crate::{state::{config_r, reward_r}}; -use crate::state::claim_status_r; +use crate::state::{claim_status_r, total_claimed_r, user_total_claimed_r}; pub fn config (deps: &Extern) -> StdResult { - Ok(QueryAnswer::Config { config: config_r(&deps.storage).load()? + Ok(QueryAnswer::Config { + config: config_r(&deps.storage).load()?, + total_claimed: total_claimed_r(&deps.storage).load()?, }) } @@ -23,7 +25,7 @@ pub fn airdrop_amount let eligible_amount = reward_r(&deps.storage).load(key.as_bytes())?.amount; let mut finished_tasks = vec![]; - let mut claimed = Uint128::zero(); + let mut claimed = user_total_claimed_r(&deps.storage).load(key.as_bytes())?; let mut unclaimed = Uint128::zero(); let config = config_r(&deps.storage).load()?; @@ -35,8 +37,8 @@ pub fn airdrop_amount let calc = task.percent.multiply_ratio(eligible_amount.clone(), Uint128(100)); match task_claimed { - true => claimed += calc, - false => unclaimed += calc + false => unclaimed += calc, + _ => {} }; } } diff --git a/packages/shade_protocol/src/airdrop.rs b/packages/shade_protocol/src/airdrop.rs index 9d001e831..2ce1f0893 100644 --- a/packages/shade_protocol/src/airdrop.rs +++ b/packages/shade_protocol/src/airdrop.rs @@ -23,6 +23,8 @@ pub struct Config { pub admin: HumanAddr, // The snip20 to be minted pub airdrop_snip20: Contract, + // Total claimable amount + pub airdrop_total: Uint128, // Required tasks pub task_claim: Vec, // Checks if airdrop has started / ended @@ -38,7 +40,7 @@ pub struct InitMsg { pub start_time: Option, // Can be set to never end pub end_time: Option, - // Secret network delegators snapshot + // Delegators snapshot pub rewards: Vec, // Default gifted amount pub default_claim: Uint128, @@ -96,8 +98,7 @@ impl Query for QueryMsg { #[derive(Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum QueryAnswer { - // TODO: add total claimed in config - Config { config: Config }, + Config { config: Config, total_claimed: Uint128 }, Dates { start: u64, end: Option }, Eligibility { // Total eligible From cf6eb3712e1e718d95981d12150058976076aadc Mon Sep 17 00:00:00 2001 From: Guy Garcia Date: Sat, 20 Nov 2021 02:42:01 -0500 Subject: [PATCH 5/6] Can now dump to treasury; fixed bug with total sent tokens --- contracts/airdrop/src/contract.rs | 10 ++-- contracts/airdrop/src/handle.rs | 49 ++++++++++++++++--- contracts/airdrop/src/query.rs | 2 +- packages/network_integration/Cargo.toml | 1 + .../tests/testnet_integration.rs | 36 ++++++++++---- packages/shade_protocol/src/airdrop.rs | 11 ++++- 6 files changed, 85 insertions(+), 24 deletions(-) diff --git a/contracts/airdrop/src/contract.rs b/contracts/airdrop/src/contract.rs index 951b4ef02..dcf00bfac 100644 --- a/contracts/airdrop/src/contract.rs +++ b/contracts/airdrop/src/contract.rs @@ -6,7 +6,7 @@ use shade_protocol::{ } }; use crate::{state::{config_w, reward_w, claim_status_w, user_total_claimed_w, total_claimed_w}, - handle::{try_update_config, try_add_tasks, try_complete_task, try_claim}, + handle::{try_update_config, try_add_tasks, try_complete_task, try_claim, try_decay}, query }; use shade_protocol::airdrop::RequiredTask; @@ -51,6 +51,7 @@ pub fn init( None => { env.message.sender.clone() } Some(admin) => { admin } }, + dump_address: msg.dump_address, airdrop_snip20: msg.airdrop_token.clone(), airdrop_total, task_claim, @@ -79,13 +80,16 @@ pub fn handle( ) -> StdResult { match msg { HandleMsg::UpdateConfig { - admin, start_date, end_date - } => try_update_config(deps, env, admin, start_date, end_date), + admin, dump_address, + start_date, end_date + } => try_update_config(deps, env, admin, dump_address, + start_date, end_date), HandleMsg::AddTasks { tasks } => try_add_tasks(deps, &env, tasks), HandleMsg::CompleteTask { address } => try_complete_task(deps, &env, address), HandleMsg::Claim { } => try_claim(deps, &env), + HandleMsg::Decay { } => try_decay(deps, &env), } } diff --git a/contracts/airdrop/src/handle.rs b/contracts/airdrop/src/handle.rs index 229116431..5212911eb 100644 --- a/contracts/airdrop/src/handle.rs +++ b/contracts/airdrop/src/handle.rs @@ -1,5 +1,5 @@ use cosmwasm_std::{to_binary, Api, Env, Extern, HandleResponse, Querier, StdError, StdResult, Storage, HumanAddr, Uint128}; -use crate::state::{config_r, config_w, reward_r, claim_status_w, claim_status_r, user_total_claimed_w, total_claimed_w}; +use crate::state::{config_r, config_w, reward_r, claim_status_w, claim_status_r, user_total_claimed_w, total_claimed_w, total_claimed_r}; use shade_protocol::airdrop::{HandleAnswer, RequiredTask}; use shade_protocol::generic_response::ResponseStatus; use secret_toolkit::snip20::send_msg; @@ -8,13 +8,14 @@ pub fn try_update_config( deps: &mut Extern, env: Env, admin: Option, + dump_address: Option, start_date: Option, end_date: Option, ) -> StdResult { let config = config_r(&deps.storage).load()?; // Check if admin if env.message.sender != config.admin { - return Err(StdError::Unauthorized { backtrace: None }); + return Err(StdError::unauthorized()); } // Save new info @@ -23,6 +24,9 @@ pub fn try_update_config( if let Some(admin) = admin { state.admin = admin; } + if let Some(dump_address)= dump_address { + state.dump_address = Some(dump_address); + } if let Some(start_date) = start_date { state.start_date = start_date; } @@ -50,7 +54,7 @@ pub fn try_add_tasks( let config = config_r(&deps.storage).load()?; // Check if admin if env.message.sender != config.admin { - return Err(StdError::Unauthorized { backtrace: None }); + return Err(StdError::unauthorized()); } config_w(&mut deps.storage).update(|mut config| { @@ -64,7 +68,7 @@ pub fn try_add_tasks( } if count > Uint128(100) { - return Err(StdError::GenericErr { msg: "tasks above 100%".to_string(), backtrace: None }) + return Err(StdError::generic_err("tasks above 100%")) } Ok(config) @@ -109,7 +113,7 @@ pub fn try_complete_task( } // if not found - Err(StdError::NotFound { kind: "task".to_string(), backtrace: None }) + Err(StdError::not_found("task")) } pub fn try_claim( @@ -120,11 +124,11 @@ pub fn try_claim( // Check if airdrop started if env.block.time < config.start_date { - return Err(StdError::Unauthorized { backtrace: None }) + return Err(StdError::unauthorized()) } if let Some(end_date) = config.end_date { if env.block.time > end_date { - return Err(StdError::Unauthorized { backtrace: None }) + return Err(StdError::unauthorized()) } } @@ -165,7 +169,7 @@ pub fn try_claim( })?; total_claimed_w(&mut deps.storage).update(|total_claimed| { - Ok(total_claimed + claimed_percentage) + Ok(total_claimed + redeem_amount) })?; // Redeem @@ -180,4 +184,33 @@ pub fn try_claim( data: Some( to_binary( &HandleAnswer::Claim { status: ResponseStatus::Success } )? ) }) +} + +pub fn try_decay( + deps: &mut Extern, + env: &Env, +) -> StdResult { + let config = config_r(&deps.storage).load()?; + + // Check if airdrop ended + if let Some(end_date) = config.end_date { + if let Some(dump_address) = config.dump_address { + if env.block.time > end_date { + let send_total = (config.airdrop_total - total_claimed_r(&deps.storage).load()?)?; + let messages = vec![send_msg( + dump_address, send_total, None, None, + 1, config.airdrop_snip20.code_hash, + config.airdrop_snip20.address)?]; + + return Ok(HandleResponse { + messages, + log: vec![], + data: Some( to_binary( &HandleAnswer::Decay { + status: ResponseStatus::Success } )? ) + }) + } + } + } + + Err(StdError::unauthorized()) } \ No newline at end of file diff --git a/contracts/airdrop/src/query.rs b/contracts/airdrop/src/query.rs index 0c15d2ce1..10320e17a 100644 --- a/contracts/airdrop/src/query.rs +++ b/contracts/airdrop/src/query.rs @@ -25,7 +25,7 @@ pub fn airdrop_amount let eligible_amount = reward_r(&deps.storage).load(key.as_bytes())?.amount; let mut finished_tasks = vec![]; - let mut claimed = user_total_claimed_r(&deps.storage).load(key.as_bytes())?; + let claimed = user_total_claimed_r(&deps.storage).load(key.as_bytes())?; let mut unclaimed = Uint128::zero(); let config = config_r(&deps.storage).load()?; diff --git a/packages/network_integration/Cargo.toml b/packages/network_integration/Cargo.toml index c4c231cf5..071b80990 100644 --- a/packages/network_integration/Cargo.toml +++ b/packages/network_integration/Cargo.toml @@ -12,6 +12,7 @@ default = [] [dependencies] colored = "2.0.0" +chrono = "0.4.19" shade-protocol = { version = "0.1.0", path = "../shade_protocol" } secretcli = { version = "0.1.0", path = "../secretcli" } serde = { version = "1.0.103", default-features = false, features = ["derive"] } diff --git a/packages/network_integration/tests/testnet_integration.rs b/packages/network_integration/tests/testnet_integration.rs index 93a4855ae..4a31909ce 100644 --- a/packages/network_integration/tests/testnet_integration.rs +++ b/packages/network_integration/tests/testnet_integration.rs @@ -16,14 +16,17 @@ use network_integration::{utils::{print_header, print_warning, generate_label, p minter::{initialize_minter, setup_minters, get_balance}, stake::setup_staker}}; use std::{thread, time}; +use chrono; use shade_protocol::snip20::InitialBalance; #[test] fn run_airdrop() -> Result<()> { let account = account_address(ACCOUNT_KEY)?; + let secondary_account = account_address("b")?; let half_airdrop = Uint128(500000); let full_airdrop = Uint128(1000000); + let all_airdrop = Uint128(2000000); /// Initialize dummy snip20 print_header("\nInitializing snip20"); @@ -35,7 +38,7 @@ fn run_airdrop() -> Result<()> { decimals: 6, initial_balances: Some(vec![InitialBalance{ address: HumanAddr::from(account.clone()), - amount: full_airdrop }]), + amount: all_airdrop }]), prng_seed: Default::default(), config: Some(InitConfig { public_total_supply: Some(true), @@ -53,7 +56,9 @@ fn run_airdrop() -> Result<()> { print_contract(&snip); { - let msg = snip20::HandleMsg::SetViewingKey { key: String::from(VIEW_KEY), padding: None }; + let msg = snip20::HandleMsg::SetViewingKey { + key: String::from(VIEW_KEY), + padding: None }; test_contract_handle(&msg, &snip, ACCOUNT_KEY, Some(GAS), Some("test"), None)?; @@ -61,17 +66,24 @@ fn run_airdrop() -> Result<()> { print_header("Initializing airdrop"); + let now = chrono::offset::Utc::now().timestamp() as u64; + let duration = 180; + let airdrop_init_msg = airdrop::InitMsg { admin: None, + dump_address: Some(HumanAddr::from(account.clone())), airdrop_token: Contract { address: HumanAddr::from(snip.address.clone()), code_hash: snip.code_hash.clone() }, start_time: None, - end_time: None, + end_time: Some(now + duration), rewards: vec![Reward { address: HumanAddr::from(account.clone()), amount: full_airdrop + }, Reward { + address: HumanAddr::from(secondary_account), + amount: full_airdrop }], default_claim: Uint128(50), task_claim: vec![RequiredTask { @@ -88,7 +100,7 @@ fn run_airdrop() -> Result<()> { { test_contract_handle(&snip20::HandleMsg::Send { recipient: HumanAddr::from(airdrop.address.clone()), - amount: full_airdrop, + amount: all_airdrop, msg: None, memo: None, padding: None @@ -113,12 +125,6 @@ fn run_airdrop() -> Result<()> { } } - // /// Register airdrop as allowed minter - // test_contract_handle(&snip20::HandleMsg::SetMinters { - // minters: vec![HumanAddr::from(airdrop.address.clone())], padding: None }, - // &snip, ACCOUNT_KEY, Some(GAS), - // Some("test"), None)?; - print_warning("Claiming half of the airdrop"); /// Claim airdrop test_contract_handle(&airdrop::HandleMsg::Claim {}, @@ -178,6 +184,16 @@ fn run_airdrop() -> Result<()> { } } + /// Try to claim expired tokens + print_warning("Claiming expired tokens"); + thread::sleep(time::Duration::from_secs(duration)); + + test_contract_handle(&airdrop::HandleMsg::Decay {}, + &airdrop, ACCOUNT_KEY, Some(GAS), + Some("test"), None)?; + + assert_eq!(all_airdrop, get_balance(&snip, account.clone())); + Ok(()) } diff --git a/packages/shade_protocol/src/airdrop.rs b/packages/shade_protocol/src/airdrop.rs index 2ce1f0893..49f9d5301 100644 --- a/packages/shade_protocol/src/airdrop.rs +++ b/packages/shade_protocol/src/airdrop.rs @@ -21,6 +21,8 @@ pub struct Reward { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct Config { pub admin: HumanAddr, + // Where the decayed tokens will be dumped, if none then nothing happens + pub dump_address: Option, // The snip20 to be minted pub airdrop_snip20: Contract, // Total claimable amount @@ -35,6 +37,8 @@ pub struct Config { #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] pub struct InitMsg { pub admin: Option, + // Where the decayed tokens will be dumped, if none then nothing happens + pub dump_address: Option, pub airdrop_token: Contract, // The airdrop time limit pub start_time: Option, @@ -57,6 +61,7 @@ impl InitCallback for InitMsg { pub enum HandleMsg { UpdateConfig { admin: Option, + dump_address: Option, start_date: Option, end_date: Option, }, @@ -66,7 +71,8 @@ pub enum HandleMsg { CompleteTask { address: HumanAddr }, - Claim {} + Claim {}, + Decay {}, } impl HandleCallback for HandleMsg { @@ -80,7 +86,8 @@ pub enum HandleAnswer { UpdateConfig { status: ResponseStatus }, AddTask { status: ResponseStatus }, CompleteTask { status: ResponseStatus }, - Claim { status: ResponseStatus } + Claim { status: ResponseStatus }, + Decay { status: ResponseStatus }, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] From 76ff6761dd7371a0a6232ee6870f7a91bbd98cc4 Mon Sep 17 00:00:00 2001 From: Guy Date: Mon, 22 Nov 2021 22:14:06 -0400 Subject: [PATCH 6/6] moved send --- contracts/airdrop/src/handle.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/contracts/airdrop/src/handle.rs b/contracts/airdrop/src/handle.rs index 5212911eb..1dceeeebd 100644 --- a/contracts/airdrop/src/handle.rs +++ b/contracts/airdrop/src/handle.rs @@ -172,14 +172,11 @@ pub fn try_claim( Ok(total_claimed + redeem_amount) })?; - // Redeem - let messages = vec![send_msg(user, redeem_amount, - None, None, 0, - config.airdrop_snip20.code_hash, - config.airdrop_snip20.address)?]; - Ok(HandleResponse { - messages, + messages: vec![send_msg(user, redeem_amount, + None, None, 0, + config.airdrop_snip20.code_hash, + config.airdrop_snip20.address)?], log: vec![], data: Some( to_binary( &HandleAnswer::Claim { status: ResponseStatus::Success } )? )