From 1cb9ba01fc7b3ec98fc832fb673ecc0f30b55290 Mon Sep 17 00:00:00 2001 From: swelf Date: Thu, 17 Aug 2023 15:14:03 +0300 Subject: [PATCH] added update_reserve method. withdraw_all is permissioned --- .../schema/cw20-merkle-airdrop.json | 20 ++++ .../schema/raw/execute.json | 20 ++++ contracts/cw20-merkle-airdrop/src/contract.rs | 25 ++++- contracts/cw20-merkle-airdrop/src/msg.rs | 3 + contracts/cw20-merkle-airdrop/src/tests.rs | 102 +++++++++++++++++- 5 files changed, 166 insertions(+), 4 deletions(-) diff --git a/contracts/cw20-merkle-airdrop/schema/cw20-merkle-airdrop.json b/contracts/cw20-merkle-airdrop/schema/cw20-merkle-airdrop.json index 40047dc9..85dddee4 100644 --- a/contracts/cw20-merkle-airdrop/schema/cw20-merkle-airdrop.json +++ b/contracts/cw20-merkle-airdrop/schema/cw20-merkle-airdrop.json @@ -137,6 +137,26 @@ } }, "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_reserve" + ], + "properties": { + "update_reserve": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + } + } + }, + "additionalProperties": false } ], "definitions": { diff --git a/contracts/cw20-merkle-airdrop/schema/raw/execute.json b/contracts/cw20-merkle-airdrop/schema/raw/execute.json index 699e75e2..e688dd6c 100644 --- a/contracts/cw20-merkle-airdrop/schema/raw/execute.json +++ b/contracts/cw20-merkle-airdrop/schema/raw/execute.json @@ -67,6 +67,26 @@ } }, "additionalProperties": false + }, + { + "type": "object", + "required": [ + "update_reserve" + ], + "properties": { + "update_reserve": { + "type": "object", + "required": [ + "address" + ], + "properties": { + "address": { + "type": "string" + } + } + } + }, + "additionalProperties": false } ], "definitions": { diff --git a/contracts/cw20-merkle-airdrop/src/contract.rs b/contracts/cw20-merkle-airdrop/src/contract.rs index 15f99f10..8fe98f28 100644 --- a/contracts/cw20-merkle-airdrop/src/contract.rs +++ b/contracts/cw20-merkle-airdrop/src/contract.rs @@ -92,6 +92,7 @@ pub fn execute( ExecuteMsg::WithdrawAll {} => execute_withdraw_all(deps, env, info), ExecuteMsg::Pause {} => execute_pause(deps, env, info), ExecuteMsg::Resume {} => execute_resume(deps, env, info), + ExecuteMsg::UpdateReserve { address } => execute_update_reserve(deps, env, info, address), } } @@ -197,6 +198,10 @@ pub fn execute_withdraw_all( env: Env, info: MessageInfo, ) -> Result { + let cfg = CONFIG.load(deps.storage)?; + if info.sender != cfg.reserve_address { + return Err(ContractError::Unauthorized {}); + } let vesting_start = VESTING_START.load(deps.storage)?; let vesting_duration = VESTING_DURATION.load(deps.storage)?; let expiration = vesting_start + vesting_duration; @@ -218,7 +223,6 @@ pub fn execute_withdraw_all( // Get the current total balance for the contract and burn it all. // By burning, we exchange them for NTRN tokens - let cfg = CONFIG.load(deps.storage)?; let amount_to_withdraw = deps .querier .query_wasm_smart::( @@ -245,13 +249,30 @@ pub fn execute_withdraw_all( .add_messages([burn_message, send_message]) .add_attributes(vec![ attr("action", "withdraw_all"), - attr("address", info.sender), attr("amount", amount_to_withdraw), attr("recipient", cfg.reserve_address), ]); Ok(res) } +fn execute_update_reserve( + deps: DepsMut, + _: Env, + info: MessageInfo, + address: String, +) -> Result { + let mut cfg = CONFIG.load(deps.storage)?; + if cfg.owner != info.sender && cfg.reserve_address != info.sender { + return Err(ContractError::Unauthorized {}); + } + cfg.reserve_address = deps.api.addr_validate(&address)?; + CONFIG.save(deps.storage, &cfg)?; + Ok(Response::new().add_attributes(vec![ + attr("action", "update_reserve"), + attr("address", address), + ])) +} + pub fn execute_pause( deps: DepsMut, env: Env, diff --git a/contracts/cw20-merkle-airdrop/src/msg.rs b/contracts/cw20-merkle-airdrop/src/msg.rs index 0e4dc96a..ffbe2e88 100644 --- a/contracts/cw20-merkle-airdrop/src/msg.rs +++ b/contracts/cw20-merkle-airdrop/src/msg.rs @@ -42,6 +42,9 @@ pub enum ExecuteMsg { WithdrawAll {}, Pause {}, Resume {}, + UpdateReserve { + address: String, + }, } #[cw_serde] diff --git a/contracts/cw20-merkle-airdrop/src/tests.rs b/contracts/cw20-merkle-airdrop/src/tests.rs index 09485853..99865ab8 100644 --- a/contracts/cw20-merkle-airdrop/src/tests.rs +++ b/contracts/cw20-merkle-airdrop/src/tests.rs @@ -337,6 +337,91 @@ fn expiration() { ) } +#[test] +fn update_reserve_address() { + let mut deps = mock_dependencies(); + let env = mock_env(); + let airdrop_start = env.block.time.minus_seconds(5_000).seconds(); + let vesting_start = env.block.time.plus_seconds(10_000).seconds(); + let vesting_duration_seconds = 20_000; + let info = mock_info("owner0000", &[]); + + let msg = InstantiateMsg { + credits_address: "credits0000".to_string(), + reserve_address: "reserve0000".to_string(), + merkle_root: "5d4f48f147cb6cb742b376dce5626b2a036f69faec10cd73631c791780e150fc".to_string(), + airdrop_start, + vesting_start, + vesting_duration_seconds, + total_amount: None, + hrp: None, + }; + let _res = instantiate(deps.as_mut(), env.clone(), info, msg).unwrap(); + + // can't claim expired + let msg = ExecuteMsg::UpdateReserve { + address: "reserve0001".to_string(), + }; + + let res = execute( + deps.as_mut(), + env.clone(), + mock_info("regularaddress", &[]), + msg.clone(), + ) + .unwrap_err(); + assert_eq!(res, ContractError::Unauthorized {}); + + let res = execute( + deps.as_mut(), + env.clone(), + mock_info("owner0000", &[]), + msg.clone(), + ) + .unwrap(); + assert_eq!( + res.attributes, + vec![ + attr("action", "update_reserve"), + attr("address", "reserve0001"), + ] + ); + + // old reserve is unauthorized now + let res = execute( + deps.as_mut(), + env.clone(), + mock_info("reserve0000", &[]), + msg, + ) + .unwrap_err(); + assert_eq!(res, ContractError::Unauthorized {}); + + let res = execute( + deps.as_mut(), + env.clone(), + mock_info("reserve0001", &[]), + ExecuteMsg::UpdateReserve { + address: "reserve0002".to_string(), + }, + ) + .unwrap(); + assert_eq!( + res.attributes, + vec![ + attr("action", "update_reserve"), + attr("address", "reserve0002"), + ] + ); + + assert_eq!( + from_binary::(&query(deps.as_ref(), env, QueryMsg::Config {}).unwrap()) + .unwrap() + .reserve_address, + "reserve0002" + ); +} + #[test] fn withdraw_all() { let test_data: Encoded = from_slice(TEST_DATA_1).unwrap(); @@ -449,8 +534,9 @@ fn withdraw_all() { ) .unwrap(); assert_eq!(Uint128::new(10000), response.balance); - //withdraw before expiration + let withdraw_msg = ExecuteMsg::WithdrawAll {}; + //unauthorized let err = router .execute_contract( Addr::unchecked("owner0000".to_string()), @@ -461,6 +547,18 @@ fn withdraw_all() { .unwrap_err() .downcast::() .unwrap(); + assert_eq!(err, ContractError::Unauthorized {}); + //withdraw before expiration + let err = router + .execute_contract( + Addr::unchecked("reserve0000".to_string()), + merkle_airdrop_addr.clone(), + &withdraw_msg, + &[], + ) + .unwrap_err() + .downcast::() + .unwrap(); assert_eq!( err, ContractError::WithdrawAllUnavailable { @@ -480,7 +578,7 @@ fn withdraw_all() { let withdraw_all_msg = ExecuteMsg::WithdrawAll {}; router .execute_contract( - Addr::unchecked("owner0000".to_string()), + Addr::unchecked("reserve0000".to_string()), merkle_airdrop_addr.clone(), &withdraw_all_msg, &[],