diff --git a/Cargo.lock b/Cargo.lock index 9396e8a..5d219d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,6 +37,22 @@ dependencies = [ "memchr", ] +[[package]] +name = "airdrop" +version = "1.0.0" +dependencies = [ + "create_type_spec_derive", + "pbc_contract_codegen", + "pbc_contract_common", + "pbc_lib", + "pbc_traits", + "read_write_rpc_derive", + "read_write_state_derive", + "rpc-msg-derive", + "serde_json", + "utils", +] + [[package]] name = "anes" version = "0.1.6" @@ -1029,9 +1045,10 @@ checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "meta-names-contract" -version = "2.3.1" +version = "2.4.0" dependencies = [ "access-control", + "airdrop", "contract-version-base", "create_type_spec_derive", "criterion", diff --git a/Cargo.toml b/Cargo.toml index 642ac15..328e5e4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ resolver = "2" members = [ + "airdrop", "contract", "contract-proxy", "contract-version-base", diff --git a/airdrop/Cargo.toml b/airdrop/Cargo.toml new file mode 100644 index 0000000..7cef78e --- /dev/null +++ b/airdrop/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "airdrop" +version = "1.0.0" +authors = ["Yeboster"] +edition = "2021" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +pbc_contract_common = { workspace = true } +pbc_contract_codegen = { workspace = true } +pbc_traits = { workspace = true } +pbc_lib = { workspace = true } +read_write_rpc_derive = { workspace = true } +read_write_state_derive = { workspace = true } +create_type_spec_derive = { workspace = true } + +utils = { path = "../utils" } +rpc-msg-derive = { path = "../rpc-msg-derive" } + +serde_json = { workspace = true } + +[features] +abi = [ + "pbc_contract_common/abi", + "pbc_contract_codegen/abi", + "pbc_traits/abi", + "create_type_spec_derive/abi", +] diff --git a/airdrop/src/actions.rs b/airdrop/src/actions.rs new file mode 100644 index 0000000..9e04343 --- /dev/null +++ b/airdrop/src/actions.rs @@ -0,0 +1,19 @@ +use pbc_contract_common::{address::Address, avl_tree_map::AvlTreeMap}; + +use crate::state::AirdropState; + +pub fn execute_init() -> AirdropState { + AirdropState { + inventory: AvlTreeMap::new(), + } +} + +pub fn execute_airdrop(state: &mut AirdropState, address: &Address) { + assert!(state.has_airdrop(address), "No airdrop for the address"); + + state._use_airdrop(address); +} + +pub fn execute_add_airdrop(state: &mut AirdropState, address: &Address) { + state._add_airdrop(address); +} diff --git a/airdrop/src/lib.rs b/airdrop/src/lib.rs new file mode 100644 index 0000000..3496451 --- /dev/null +++ b/airdrop/src/lib.rs @@ -0,0 +1,5 @@ +pub mod actions; +pub mod state; + +#[cfg(test)] +mod tests; diff --git a/airdrop/src/state.rs b/airdrop/src/state.rs new file mode 100644 index 0000000..d4544af --- /dev/null +++ b/airdrop/src/state.rs @@ -0,0 +1,34 @@ +use create_type_spec_derive::CreateTypeSpec; +use pbc_contract_common::{address::Address, avl_tree_map::AvlTreeMap}; +use read_write_state_derive::ReadWriteState; + +#[repr(C)] +#[derive(ReadWriteState, CreateTypeSpec, Default, Debug)] +pub struct AirdropState { + pub inventory: AvlTreeMap
, +} + +impl AirdropState { + /// Check if the address has an airdrop + pub fn has_airdrop(&self, address: &Address) -> bool { + self.inventory.contains_key(address) + } + + /// Use airdrop from the address + pub fn _use_airdrop(&mut self, address: &Address) { + if let Some(airdrop) = self.inventory.get(address) { + let remaining = airdrop - 1; + if remaining == 0 { + self.inventory.remove(address); + } else { + self.inventory.insert(*address, remaining); + } + } + } + + /// Add airdrop to the address + pub fn _add_airdrop(&mut self, address: &Address) { + let airdrop = self.inventory.get(address).unwrap_or(0); + self.inventory.insert(*address, airdrop + 1); + } +} diff --git a/airdrop/src/tests.rs b/airdrop/src/tests.rs new file mode 100644 index 0000000..0fcdb8a --- /dev/null +++ b/airdrop/src/tests.rs @@ -0,0 +1,49 @@ +// Setup tests + +use utils::tests::mock_address; + +use crate::actions::{execute_add_airdrop, execute_airdrop, execute_init}; + +#[test] +fn proper_has_airdrop() { + let address = mock_address(0); + let mut state = execute_init(); + + assert_eq!(state.has_airdrop(&address), false); + + execute_add_airdrop(&mut state, &address); + + assert_eq!(state.has_airdrop(&address), true); +} + +#[test] +fn proper_execute_airdrop() { + let address = mock_address(0); + let mut state = execute_init(); + + execute_add_airdrop(&mut state, &address); + execute_airdrop(&mut state, &address); + + assert_eq!(state.has_airdrop(&address), false); +} + +#[test] +fn proper_execute_add_airdrop() { + let address = mock_address(0); + let mut state = execute_init(); + + execute_add_airdrop(&mut state, &address); + execute_add_airdrop(&mut state, &address); + + assert_eq!(state.has_airdrop(&address), true); + assert_eq!(state.inventory.get(&address).unwrap(), 2); +} + +#[test] +#[should_panic(expected = "No airdrop for the address")] +fn proper_execute_airdrop_no_airdrop() { + let address = mock_address(0); + let mut state = execute_init(); + + execute_airdrop(&mut state, &address); +} diff --git a/contract/Cargo.toml b/contract/Cargo.toml index f85cb26..8bfc996 100644 --- a/contract/Cargo.toml +++ b/contract/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "meta-names-contract" -version = "2.3.1" +version = "2.4.0" authors = ["Yeboster"] edition = "2021" @@ -17,6 +17,7 @@ read_write_state_derive = { workspace = true } create_type_spec_derive = { workspace = true } access-control = { path = "../access-control" } +airdrop = { path = "../airdrop" } contract-version-base = { path = "../contract-version-base" } partisia-name-system = { path = "../partisia-name-system" } nft = { path = "../nft" } diff --git a/contract/src/contract.rs b/contract/src/contract.rs index a0b4905..b6c4c7a 100644 --- a/contract/src/contract.rs +++ b/contract/src/contract.rs @@ -17,6 +17,7 @@ use pbc_contract_common::{ use nft::{actions as nft_actions, msg as nft_msg}; use access_control::{actions as ac_actions, msg as ac_msg}; +use airdrop::actions::{self as airdrop_actions, execute_airdrop}; use partisia_name_system::{actions as pns_actions, msg as pns_msg, state::RecordClass}; use utils::events::assert_callback_success; @@ -58,11 +59,13 @@ pub fn initialize(ctx: ContractContext, msg: InitMsg) -> (ContractState, Vec