Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dYdX Airdrop updates #652

Merged
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
aec674d
Add name mint discount whitelist address
MightOfOaks Jan 30, 2024
dce9d17
Update tests to include name discount wl address
MightOfOaks Jan 30, 2024
33d0943
Merge branch 'humanalgorithm/dydx-main-branch-v3.x' into serkan/dydx-…
MightOfOaks Mar 7, 2024
85a3e51
Update instantiation to accommodate a whitelist-immutable-flex
MightOfOaks Mar 10, 2024
9152288
Use build_messages and validation as common crates for registration &…
MightOfOaks Mar 10, 2024
83b1147
Add mint_count query impl for whitelist-immutable-flex
MightOfOaks Mar 10, 2024
df428aa
Implement registration logic
MightOfOaks Mar 11, 2024
4362ad7
Set flag for the registered address
MightOfOaks Mar 12, 2024
88ad59a
Include name collection address among instantiation parameters
MightOfOaks Mar 12, 2024
aa6f9ca
Add collection token mint validation
MightOfOaks Mar 13, 2024
868ae49
Add validation for name mint & association
MightOfOaks Mar 13, 2024
7ffa284
Check for previous claims
MightOfOaks Mar 13, 2024
48aacbd
Add claim_airdrop()
MightOfOaks Mar 13, 2024
fff2a30
Check for total airdrop count for reward distribution
MightOfOaks Mar 13, 2024
f048121
Address clippy issues
MightOfOaks Mar 13, 2024
d1df392
Update test-suite/common_setup to include whitelist-flex
MightOfOaks Mar 14, 2024
868355b
Add TS interface & types
MightOfOaks Mar 15, 2024
ceb1aa8
Update airdrop & whitelist immutable flex contract instantiation para…
MightOfOaks Mar 18, 2024
b57450c
Address issues in test code (tests failing pre-update)
MightOfOaks Mar 18, 2024
576e8b7
Merge branch 'humanalgorithm/dydx-main-branch-v3.x' into serkan/dydx-…
MightOfOaks Mar 19, 2024
5ee3aff
Address issues induced by the merge
MightOfOaks Mar 19, 2024
32a527a
Update error for invalid eth signature
MightOfOaks Mar 20, 2024
ee608b9
Update Member query response for whitelist-immutable-flex
MightOfOaks Mar 20, 2024
c733a78
Add queries for registration, claim and airdrop count
MightOfOaks Mar 20, 2024
db8aa26
Update schema
MightOfOaks Mar 20, 2024
28179ab
Update TS interface & types
MightOfOaks Mar 20, 2024
2b8102d
Add names/flat-rate-whitelist branch as a dependency
MightOfOaks Mar 22, 2024
e8a020d
Update dependency for whitelist-updatable-flatrate
MightOfOaks Mar 27, 2024
613978f
eth-verify recovery param
jhernandezb Mar 26, 2024
06bfc80
whitelist updates
jhernandezb Mar 27, 2024
5f1ed3e
Merge pull request #673 from public-awesome/jhernadnezb/dydx-airdrop-…
jhernandezb Mar 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
322 changes: 269 additions & 53 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ open-edition-factory = { version = "3.5.0", path = "contracts/factories/open-edi
open-edition-minter = { version = "3.5.0", path = "contracts/minters/open-edition-minter" }
whitelist-immutable = { version = "3.5.0", path = "contracts/whitelists/whitelist-immutable" }
whitelist-immutable-flex = { version = "3.5.0", path = "contracts/whitelists/whitelist-immutable-flex" }
whitelist-updatable = "0.12.0"
sg-whitelist-flex = { version = "3.5.0", path = "contracts/whitelists/whitelist-flex" }
sg721-name = "1.2.5"
ethereum-verify = { version = "3.5.0", path = "packages/ethereum-verify" }
rekt-airdrop = { version = "3.5.0", path = "contracts/rekt-airdrop"}
dydx-airdrop = { version = "3.5.0", path = "contracts/dydx-airdrop"}
Expand Down
9 changes: 6 additions & 3 deletions contracts/dydx-airdrop/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ cw2 = { workspace = true }
ethereum-verify = { workspace = true }
hex = "0.4"
rust_decimal = { version = "1.14.3" }
sg-whitelist = { workspace = true }
cw721 = { workspace = true }
sg721-base = { workspace = true, features = ["library"] }
sg721-name = { workspace = true, features = ["library"] }
sg-whitelist-flex = { workspace = true, features = ["library"] }
sg1 = { workspace = true }
sha2 = { workspace = true }
sha3 = "0.10"
Expand All @@ -43,7 +46,8 @@ serde = { workspace = true }
sg-std = { workspace = true }
thiserror = { workspace = true }
vending-minter = { workspace = true, features = ["library"] }
whitelist-immutable = { workspace = true, features = ["library"] }
whitelist-immutable-flex = { workspace = true, features = ["library"] }
whitelist-updatable = { workspace = true, features = ["library"] }

[dev-dependencies]
async-std = "1.12.0"
Expand All @@ -53,7 +57,6 @@ eyre = "0.6"
rlp = "0.5"
sg2 = { workspace = true }
vending-factory = { workspace = true }
sg721-base = { workspace = true }
cw-multi-test = { workspace = true }
sg-multi-test = { workspace = true }
anyhow = "1.0.57"
140 changes: 140 additions & 0 deletions contracts/dydx-airdrop/src/build_messages.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
use cosmwasm_std::{
coins, to_json_binary, Addr, BankMsg, Deps, DepsMut, Env, MessageInfo, StdResult, WasmMsg,
};
use sg_std::{CosmosMsg, Response, StargazeMsgWrapper, SubMsg, NATIVE_DENOM};
use whitelist_updatable::msg::ExecuteMsg;
// TODO: Replace with whitelist_updatable_flatrate
use crate::msg::InstantiateMsg;
use crate::query::{query_collection_whitelist, query_mint_count};
use crate::state::{Config, CONFIG};
use crate::ContractError;
use sg_whitelist_flex::helpers::interface::CollectionWhitelistContract;
use sg_whitelist_flex::msg::AddMembersMsg;
use sg_whitelist_flex::msg::{ExecuteMsg as CollectionWhitelistExecuteMsg, Member};
use whitelist_immutable_flex::msg::InstantiateMsg as WIFInstantiateMsg;
use whitelist_updatable::msg::ExecuteMsg::AddAddresses;
pub const IMMUTABLE_WHITELIST_LABEL: &str = "Whitelist Immutable Flex for Airdrop";
pub const INIT_WHITELIST_REPLY_ID: u64 = 1;

pub fn whitelist_instantiate(
env: Env,
msg: InstantiateMsg,
) -> Result<cosmwasm_std::SubMsg<StargazeMsgWrapper>, ContractError> {
let whitelist_instantiate_msg = WIFInstantiateMsg {
members: msg.members,
mint_discount_bps: Some(0),
};
let wasm_msg = WasmMsg::Instantiate {
code_id: msg.whitelist_code_id,
admin: Some(env.contract.address.to_string()),
funds: vec![],
label: IMMUTABLE_WHITELIST_LABEL.to_string(),
msg: to_json_binary(&whitelist_instantiate_msg)?,
};
Ok(SubMsg::reply_on_success(wasm_msg, INIT_WHITELIST_REPLY_ID))
}

pub fn state_config(
deps: Deps,
info: MessageInfo,
msg: InstantiateMsg,
) -> Result<Config, ContractError> {
Ok(Config {
admin: info.sender,
claim_msg_plaintext: msg.clone().claim_msg_plaintext,
airdrop_amount: crate::validation::validate_airdrop_amount(msg.airdrop_amount)?,
whitelist_address: None,
minter_address: deps.api.addr_validate(msg.minter_address.as_ref())?,
name_discount_wl_address: deps
.api
.addr_validate(msg.name_discount_wl_address.as_ref())?,
name_collection_address: deps
.api
.addr_validate(msg.name_collection_address.as_ref())?,
airdrop_count_limit: msg.airdrop_count_limit,
})
}

pub fn claim_reward(info: MessageInfo, airdrop_amount: u128) -> Result<Response, ContractError> {
let mut res = Response::new();
let bank_msg = SubMsg::new(BankMsg::Send {
to_address: info.sender.to_string(),
amount: coins(airdrop_amount, NATIVE_DENOM),
});
res = res.add_submessage(bank_msg);

Ok(res)
}

pub fn dust_and_whitelist_add(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can just be whitelist_add and call the dust( ) function from the calling function

deps: &DepsMut,
info: MessageInfo,
eth_address: String,
) -> Result<Response, ContractError> {
let res = add_member_to_whitelists(deps, info.clone(), eth_address)?;
let res = res.add_message(dust_member_wallet(info.clone())?);
Ok(res)
}
pub fn add_member_to_whitelists(
deps: &DepsMut,
info: MessageInfo,
eth_address: String,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
let collection_whitelist = query_collection_whitelist(deps)?;
let mint_count = query_mint_count(deps, eth_address.clone())?;
let names_discount_whitelist = config.name_discount_wl_address;

let res = Response::new();
let res = res.add_message(add_member_to_collection_whitelist(
deps,
info.sender.clone(),
collection_whitelist,
mint_count,
)?);
let res = res.add_message(add_member_to_names_discount_whitelist(
info.sender.clone(),
names_discount_whitelist.clone().to_string(),
)?);
Ok(res)
}

fn add_member_to_collection_whitelist(
deps: &DepsMut,
wallet_address: Addr,
collection_whitelist: String,
mint_count: u32,
) -> StdResult<CosmosMsg> {
let inner_msg = AddMembersMsg {
to_add: vec![Member {
address: wallet_address.to_string(),
mint_count,
}],
};
let execute_msg = CollectionWhitelistExecuteMsg::AddMembers(inner_msg);
CollectionWhitelistContract(deps.api.addr_validate(&collection_whitelist)?).call(execute_msg)
}

fn add_member_to_names_discount_whitelist(
wallet_address: Addr,
name_discount_wl: String,
) -> StdResult<CosmosMsg> {
let execute_msg: ExecuteMsg = AddAddresses {
addresses: vec![wallet_address.to_string()],
};
let msg = to_json_binary(&execute_msg)?;
Ok(WasmMsg::Execute {
contract_addr: name_discount_wl,
msg,
funds: vec![],
}
.into())
}

pub fn dust_member_wallet(info: MessageInfo) -> StdResult<CosmosMsg> {
let inner_msg = BankMsg::Send {
to_address: info.sender.to_string(),
amount: coins(1000000, NATIVE_DENOM),
};
Ok(CosmosMsg::Bank(inner_msg))
}
167 changes: 13 additions & 154 deletions contracts/dydx-airdrop/src/claim_airdrop.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
use crate::query::query_collection_whitelist;
use crate::state::ADDRS_TO_MINT_COUNT;
use crate::build_messages::claim_reward;
use crate::state::{AIRDROP_COUNT, HAS_CLAIMED};
use crate::{state::CONFIG, ContractError};
use build_messages::claim_and_whitelist_add;
use cosmwasm_std::DepsMut;
use cosmwasm_std::{coins, Addr, BankMsg, StdResult};
use cosmwasm_std::{Env, MessageInfo};
use sg_std::Response;
use sg_std::{CosmosMsg, SubMsg};
use sg_whitelist::msg::ExecuteMsg as CollectionWhitelistExecuteMsg;
use sg_whitelist::{helpers::interface::CollectionWhitelistContract, msg::AddMembersMsg};
use validation::validate_claim;

use crate::validation::validate_claim;

pub fn claim_airdrop(
deps: DepsMut,
Expand All @@ -19,158 +15,21 @@ pub fn claim_airdrop(
eth_sig: String,
) -> Result<Response, ContractError> {
let config = CONFIG.load(deps.storage)?;
let airdrop_count_limit = config.airdrop_count_limit;
if AIRDROP_COUNT.load(deps.storage)? >= airdrop_count_limit {
return Err(ContractError::AirdropCountLimitExceeded {});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be a one line assert check

}
validate_claim(
&deps,
info.clone(),
eth_address.clone(),
eth_sig,
config.clone(),
)?;
let res = claim_and_whitelist_add(&deps, info, config.airdrop_amount)?;
increment_local_mint_count_for_address(deps, eth_address)?;

let res = claim_reward(info, config.airdrop_amount)?;
AIRDROP_COUNT.update(deps.storage, |count: u32| -> Result<u32, ContractError> {
Ok(count + 1)
})?;
HAS_CLAIMED.save(deps.storage, &eth_address, &true)?;
Ok(res.add_attribute("claimed_amount", config.airdrop_amount.to_string()))
}

pub fn increment_local_mint_count_for_address(
deps: DepsMut,
eth_address: String,
) -> Result<Response, ContractError> {
let mint_count_for_address = ADDRS_TO_MINT_COUNT
.load(deps.storage, &eth_address)
.unwrap_or(0);
ADDRS_TO_MINT_COUNT.save(deps.storage, &eth_address, &(mint_count_for_address + 1))?;

Ok(Response::new())
}

mod build_messages {
use super::*;
use sg_std::NATIVE_DENOM;

pub fn claim_and_whitelist_add(
deps: &DepsMut,
info: MessageInfo,
airdrop_amount: u128,
) -> Result<Response, ContractError> {
let mut res = Response::new();
let bank_msg = SubMsg::new(BankMsg::Send {
to_address: info.sender.to_string(),
amount: coins(airdrop_amount, NATIVE_DENOM),
});
res = res.add_submessage(bank_msg);
let collection_whitelist = query_collection_whitelist(deps)?;
let res = res.add_message(add_member_to_collection_whitelist(
deps,
info.sender,
collection_whitelist,
)?);
Ok(res)
}

fn add_member_to_collection_whitelist(
deps: &DepsMut,
wallet_address: Addr,
collection_whitelist: String,
) -> StdResult<CosmosMsg> {
let inner_msg = AddMembersMsg {
to_add: vec![wallet_address.to_string()],
};
let execute_msg = CollectionWhitelistExecuteMsg::AddMembers(inner_msg);
CollectionWhitelistContract(deps.api.addr_validate(&collection_whitelist)?)
.call(execute_msg)
}
}

mod validation {
use super::*;
use cosmwasm_std::StdError;
use ethereum_verify::verify_ethereum_text;

use crate::{
query::{query_airdrop_is_eligible, query_per_address_limit},
state::Config,
};

pub fn compute_plaintext_msg(config: &Config, info: MessageInfo) -> String {
str::replace(
&config.claim_msg_plaintext,
"{wallet}",
info.sender.as_ref(),
)
}

pub fn validate_claim(
deps: &DepsMut,
info: MessageInfo,
eth_address: String,
eth_sig: String,
config: Config,
) -> Result<(), ContractError> {
validate_is_eligible(deps, eth_address.clone())?;
validate_eth_sig(deps, info, eth_address.clone(), eth_sig, config)?;
validate_mints_remaining(deps, &eth_address)?;
Ok(())
}

fn validate_is_eligible(deps: &DepsMut, eth_address: String) -> Result<(), ContractError> {
let eligible = query_airdrop_is_eligible(deps.as_ref(), eth_address.clone())?;
match eligible {
true => Ok(()),
false => Err(ContractError::AddressNotEligible {
address: eth_address,
}),
}
}

fn validate_eth_sig(
deps: &DepsMut,
info: MessageInfo,
eth_address: String,
eth_sig: String,
config: Config,
) -> Result<(), ContractError> {
let valid_eth_sig =
validate_ethereum_text(deps, info, &config, eth_sig, eth_address.clone())?;
match valid_eth_sig {
true => Ok(()),
false => Err(ContractError::AddressNotEligible {
address: eth_address,
}),
}
}

pub fn validate_mints_remaining(
deps: &DepsMut,
eth_address: &str,
) -> Result<(), ContractError> {
let mint_count = ADDRS_TO_MINT_COUNT.load(deps.storage, eth_address);
let mint_count = mint_count.unwrap_or(0);
let per_address_limit = query_per_address_limit(&deps.as_ref())?;
if mint_count < per_address_limit {
Ok(())
} else {
Err(ContractError::MintCountReached {
address: eth_address.to_string(),
})
}
}

pub fn validate_ethereum_text(
deps: &DepsMut,
info: MessageInfo,
config: &Config,
eth_sig: String,
eth_address: String,
) -> StdResult<bool> {
let plaintext_msg = compute_plaintext_msg(config, info);
match hex::decode(eth_sig.clone()) {
Ok(eth_sig_hex) => {
verify_ethereum_text(deps.as_ref(), &plaintext_msg, &eth_sig_hex, &eth_address)
}
Err(_) => Err(StdError::InvalidHex {
msg: format!("Could not decode {eth_sig}"),
}),
}
}
}
Loading
Loading