diff --git a/src/airdrop.rs b/src/airdrop.rs index 9baaadb0..5f21269b 100644 --- a/src/airdrop.rs +++ b/src/airdrop.rs @@ -1,10 +1,10 @@ -use orga::coins::Decimal; -use orga::coins::{Address, Amount}; +use orga::coins::Address; +#[cfg(feature = "full")] +use orga::coins::{Amount, Decimal}; use orga::collections::{ChildMut, Map}; use orga::context::GetContext; use orga::migrate::MigrateFrom; use orga::orga; -use orga::plugins::MIN_FEE; use orga::plugins::{Paid, Signer}; use orga::{Error, Result}; #[cfg(feature = "full")] @@ -64,7 +64,7 @@ impl Airdrop { self.accounts .get_mut(signer)? - .ok_or_else(|| Error::Coins("No airdrop account for signer".into())) + .ok_or_else(|| Error::App("No airdrop account for signer".into())) } fn pay_as_funding(&mut self, amount: u64) -> Result<()> { @@ -84,45 +84,19 @@ impl Airdrop { } #[call] - pub fn claim_btc_deposit(&mut self) -> Result<()> { - let mut acct = self.signer_acct_mut()?; - let amount = acct.btc_deposit.claim()?; - self.pay_as_funding(amount)?; - Ok(()) - } - - #[call] - pub fn claim_btc_withdraw(&mut self) -> Result<()> { + pub fn claim_airdrop2(&mut self) -> Result<()> { let mut acct = self.signer_acct_mut()?; - let amount = acct.btc_withdraw.claim()?; + let amount = acct.airdrop2.claim()?; self.pay_as_funding(amount)?; Ok(()) } - #[call] - pub fn claim_ibc_transfer(&mut self) -> Result<()> { + pub fn join_accounts(&mut self, dest_addr: Address) -> Result<()> { let mut acct = self.signer_acct_mut()?; - let amount = acct.ibc_transfer.claim()?; - self.pay_as_funding(amount)?; - Ok(()) - } - #[call] - pub fn claim_testnet_participation(&mut self) -> Result<()> { - #[cfg(not(feature = "testnet"))] - { - let mut acct = self.signer_acct_mut()?; - let amount = acct.testnet_participation.claim()?; - self.pay_as_funding(amount)?; + if acct.joined { + return Err(Error::App("Account already joined".to_string())); } - Ok(()) - } - - #[call] - pub fn join_accounts(&mut self, dest_addr: Address) -> Result<()> { - self.pay_as_funding(MIN_FEE)?; - - let mut acct = self.signer_acct_mut()?; if acct.is_empty() { return Err(Error::App("Account has no airdrop balance".to_string())); } @@ -143,12 +117,9 @@ impl Airdrop { }; add_part(&mut dest.airdrop1, src.airdrop1); - add_part(&mut dest.btc_deposit, src.btc_deposit); - add_part(&mut dest.ibc_transfer, src.ibc_transfer); - add_part(&mut dest.btc_withdraw, src.btc_withdraw); + add_part(&mut dest.airdrop2, src.airdrop2); - #[cfg(not(feature = "testnet"))] - add_part(&mut dest.testnet_participation, src.testnet_participation); + dest.joined = true; Ok(()) } @@ -233,18 +204,14 @@ impl Airdrop { #[cfg(feature = "testnet")] { - acct.btc_deposit.locked = unom / 3; - acct.btc_withdraw.locked = unom / 3; - acct.ibc_transfer.locked = unom / 3; + acct.airdrop2.claimable = unom; Ok((0, 0)) } #[cfg(not(feature = "testnet"))] { - acct.btc_deposit.locked = unom / 4; - acct.btc_withdraw.locked = unom / 4; - acct.ibc_transfer.locked = unom / 4; + acct.airdrop2.claimable = unom; assert!(testnet_completions <= 3); acct.testnet_participation.locked = unom / 12 * (3 - testnet_completions); acct.testnet_participation.claimable = unom / 12 * testnet_completions; @@ -354,7 +321,7 @@ impl Airdrop { // } #[cfg(not(feature = "testnet"))] -#[orga(version = 1)] +#[orga(version = 2)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct Account { #[orga(version(V0))] @@ -370,16 +337,30 @@ pub struct Account { pub ibc_transfer: Part, #[orga(version(V1))] pub testnet_participation: Part, + + #[orga(version(V2))] + pub airdrop2: Part, + #[orga(version(V2))] + pub joined: bool, } #[cfg(feature = "testnet")] -#[orga(version = 1)] +#[orga(version = 2)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct Account { pub airdrop1: Part, + + #[orga(version(V0, V1))] pub btc_deposit: Part, + #[orga(version(V0, V1))] pub btc_withdraw: Part, + #[orga(version(V0, V1))] pub ibc_transfer: Part, + + #[orga(version(V2))] + pub airdrop2: Part, + #[orga(version(V2))] + pub joined: bool, } impl Account { @@ -410,6 +391,27 @@ impl MigrateFrom for AccountV1 { } } +impl MigrateFrom for AccountV2 { + fn migrate_from(value: AccountV1) -> Result { + let add_part = |dest: &mut Part, src: Part| { + dest.claimable += src.claimable + src.locked; + dest.claimed += src.claimed; + }; + + let mut airdrop2 = Part::default(); + + add_part(&mut airdrop2, value.btc_deposit); + add_part(&mut airdrop2, value.btc_withdraw); + add_part(&mut airdrop2, value.ibc_transfer); + + Ok(Self { + airdrop1: value.airdrop1, + airdrop2, + joined: false, + }) + } +} + #[orga] #[derive(Clone, Debug, PartialEq, Eq)] pub struct Part { @@ -477,9 +479,7 @@ nomic100000aeu2lh0jrrnmn2npc88typ25u7t3aa64x,1,1,1,1,1,1,1,1,1,1,true,true,true" .get_mut(Address::from_str("nomic100000aeu2lh0jrrnmn2npc88typ25u7t3aa64x").unwrap()) .unwrap() .unwrap(); - let airdrop2_total = account.btc_deposit.total() - + account.btc_withdraw.total() - + account.ibc_transfer.total(); + let airdrop2_total = account.airdrop2.total(); assert_approx_eq(airdrop2_total, AIRDROP_II_TOTAL); } diff --git a/src/app.rs b/src/app.rs index 4cb09ff3..7ece9fdc 100644 --- a/src/app.rs +++ b/src/app.rs @@ -33,7 +33,7 @@ use orga::macros::build_call; use orga::migrate::Migrate; use orga::orga; use orga::plugins::sdk_compat::{sdk, sdk::Tx as SdkTx, ConvertSdkTx}; -use orga::plugins::{DefaultPlugins, PaidCall, Signer, Time, MIN_FEE}; +use orga::plugins::{disable_fee, DefaultPlugins, Paid, PaidCall, Signer, Time, MIN_FEE}; use orga::prelude::*; use orga::upgrade::Version; use orga::upgrade::{Upgrade, UpgradeV0}; @@ -159,9 +159,6 @@ impl InnerApp { let signer = self.signer()?; let mut coins = self.bitcoin.accounts.withdraw(signer, amount)?; - if let Some(mut acct) = self.airdrop.get_mut(signer)? { - acct.ibc_transfer.unlock(); - } let fee = ibc_fee(amount)?; let fee = coins.take(fee)?; @@ -233,9 +230,6 @@ impl InnerApp { )?; match dest { DepositCommitment::Address(addr) => { - if let Some(mut acct) = self.airdrop.get_mut(addr)? { - acct.btc_deposit.unlock(); - } self.bitcoin.accounts.deposit(addr, nbtc.into())? } DepositCommitment::Ibc(dest) => { @@ -286,14 +280,19 @@ impl InnerApp { script_pubkey: Adapter, amount: Amount, ) -> Result<()> { - let signer = self.signer()?; - if let Some(mut acct) = self.airdrop.get_mut(signer)? { - acct.btc_withdraw.unlock(); - } - Ok(self.bitcoin.withdraw(script_pubkey, amount)?) } + #[call] + fn join_accounts(&mut self, dest_addr: Address) -> Result<()> { + disable_fee(); + + self.airdrop.join_accounts(dest_addr)?; + self.incentives.join_accounts(dest_addr)?; + + Ok(()) + } + fn signer(&mut self) -> Result
{ self.context::() .ok_or_else(|| Error::Signer("No Signer context available".into()))? @@ -695,51 +694,6 @@ impl ConvertSdkTx for InnerApp { Ok(PaidCall { payer, paid }) } - "nomic/MsgClaimBtcDepositAirdrop" => { - let msg = msg - .value - .as_object() - .ok_or_else(|| Error::App("Invalid message value".to_string()))?; - if !msg.is_empty() { - return Err(Error::App("Message should be empty".to_string())); - } - - let payer = build_call!(self.airdrop.claim_btc_deposit()); - let paid = build_call!(self.accounts.give_from_funding_all()); - - Ok(PaidCall { payer, paid }) - } - - "nomic/MsgClaimBtcWithdrawAirdrop" => { - let msg = msg - .value - .as_object() - .ok_or_else(|| Error::App("Invalid message value".to_string()))?; - if !msg.is_empty() { - return Err(Error::App("Message should be empty".to_string())); - } - - let payer = build_call!(self.airdrop.claim_btc_withdraw()); - let paid = build_call!(self.accounts.give_from_funding_all()); - - Ok(PaidCall { payer, paid }) - } - - "nomic/MsgClaimIbcTransferAirdrop" => { - let msg = msg - .value - .as_object() - .ok_or_else(|| Error::App("Invalid message value".to_string()))?; - if !msg.is_empty() { - return Err(Error::App("Message should be empty".to_string())); - } - - let payer = build_call!(self.airdrop.claim_ibc_transfer()); - let paid = build_call!(self.accounts.give_from_funding_all()); - - Ok(PaidCall { payer, paid }) - } - #[cfg(feature = "stakenet")] "nomic/MsgClaimTestnetParticipationAirdrop" => { let msg = msg @@ -862,7 +816,7 @@ impl ConvertSdkTx for InnerApp { Ok(PaidCall { payer, paid }) } - "nomic/MsgJoinAirdropAccounts" => { + "nomic/MsgJoinRewardAccounts" => { let msg = msg .value .as_object() @@ -874,7 +828,7 @@ impl ConvertSdkTx for InnerApp { .parse() .map_err(|_| Error::App("Invalid destination address".to_string()))?; - let payer = build_call!(self.airdrop.join_accounts(dest_addr)); + let payer = build_call!(self.join_accounts(dest_addr)); let paid = build_call!(self.app_noop()); Ok(PaidCall { payer, paid }) diff --git a/src/bin/nomic.rs b/src/bin/nomic.rs index 114fdc75..8be19164 100644 --- a/src/bin/nomic.rs +++ b/src/bin/nomic.rs @@ -996,45 +996,14 @@ impl ClaimAirdropCmd { claimed = true; } - if acct.btc_deposit.claimable > 0 { + if acct.airdrop2.claimable > 0 { app_client() .call( - |app| build_call!(app.airdrop.claim_btc_deposit()), + |app| build_call!(app.airdrop.claim_airdrop2()), |app| build_call!(app.accounts.give_from_funding_all()), ) .await?; - println!( - "Claimed BTC deposit airdrop ({} uNOM)", - acct.btc_deposit.claimable - ); - claimed = true; - } - - if acct.btc_withdraw.claimable > 0 { - app_client() - .call( - |app| build_call!(app.airdrop.claim_btc_withdraw()), - |app| build_call!(app.accounts.give_from_funding_all()), - ) - .await?; - println!( - "Claimed BTC withdraw airdrop ({} uNOM)", - acct.btc_withdraw.claimable - ); - claimed = true; - } - - if acct.ibc_transfer.claimable > 0 { - app_client() - .call( - |app| build_call!(app.airdrop.claim_ibc_transfer()), - |app| build_call!(app.accounts.give_from_funding_all()), - ) - .await?; - println!( - "Claimed IBC transfer airdrop ({} uNOM)", - acct.ibc_transfer.claimable - ); + println!("Claimed airdrop 2 ({} uNOM)", acct.airdrop2.claimable); claimed = true; } diff --git a/src/incentives.rs b/src/incentives.rs index 541639e3..b5a83017 100644 --- a/src/incentives.rs +++ b/src/incentives.rs @@ -27,6 +27,14 @@ pub struct Account { testnet_participation: Part, } +impl Clone for AccountV1 { + fn clone(&self) -> Self { + Self { + testnet_participation: self.testnet_participation.clone(), + } + } +} + impl MigrateFrom for AccountV1 { fn migrate_from(old: AccountV0) -> orga::Result { Ok(AccountV1 { @@ -39,6 +47,13 @@ impl MigrateFrom for AccountV1 { } } +impl Account { + #[orga(version(V1))] + pub fn is_empty(&self) -> bool { + self.testnet_participation.is_empty() + } +} + #[orga] impl Incentives { pub fn from_csv(data: &[u8], mut funds: Coin) -> Result { @@ -85,6 +100,11 @@ impl Incentives { Ok(Incentives { accounts }) } + #[query] + pub fn get(&self, address: Address) -> Result> { + Ok(self.accounts.get(address)?.map(|a| a.clone())) + } + pub fn signer_acct_mut(&mut self) -> OrgaResult> { let signer = self .context::() @@ -94,7 +114,7 @@ impl Incentives { self.accounts .get_mut(signer)? - .ok_or_else(|| OrgaError::Coins("No airdrop account for signer".into())) + .ok_or_else(|| OrgaError::App("No incentive account for signer".into())) } fn pay_as_funding(&mut self, amount: u64) -> Result<()> { @@ -112,4 +132,39 @@ impl Incentives { self.pay_as_funding(amount)?; Ok(()) } + + #[orga(version(V1))] + pub fn join_accounts(&mut self, dest_addr: Address) -> OrgaResult { + let mut acct = match self.signer_acct_mut() { + Ok(acct) => acct, + Err(OrgaError::App(_)) => return Ok(0), + Err(e) => return Err(e), + }; + + if acct.is_empty() { + return Ok(0); + } + + let src = acct.clone(); + *acct = Account::default(); + + let mut dest = self.accounts.entry(dest_addr)?.or_default()?; + + let add_part = |dest: &mut Part, src: Part| { + if dest.claimable > 0 || dest.claimed > 0 { + dest.claimable += src.locked; + } else { + dest.locked += src.locked; + } + dest.claimable += src.claimable; + dest.claimed += src.claimed; + + src.total() + }; + + let testnet_participation = + add_part(&mut dest.testnet_participation, src.testnet_participation); + + Ok(testnet_participation) + } } diff --git a/wasm/src/lib.rs b/wasm/src/lib.rs index f12c6068..8641aadb 100644 --- a/wasm/src/lib.rs +++ b/wasm/src/lib.rs @@ -183,8 +183,8 @@ pub async fn claim(address: String) -> Result { .await } -#[wasm_bindgen(js_name = claimAirdrop)] -pub async fn claim_airdrop(address: String) -> Result { +#[wasm_bindgen(js_name = claimAirdrop1)] +pub async fn claim_airdrop1(address: String) -> Result { let address = address .parse() .map_err(|e| Error::Wasm(format!("{:?}", e)))?; @@ -198,45 +198,15 @@ pub async fn claim_airdrop(address: String) -> Result { .await } -#[wasm_bindgen(js_name = claimBtcDepositAirdrop)] -pub async fn claim_btc_deposit_airdrop(address: String) -> Result { +#[wasm_bindgen(js_name = claimAirdrop2)] +pub async fn claim_airdrop2(address: String) -> Result { let address = address .parse() .map_err(|e| Error::Wasm(format!("{:?}", e)))?; gen_call_bytes( address, sdk::Msg { - type_: "nomic/MsgClaimBtcDepositAirdrop".to_string(), - value: serde_json::Map::new().into(), - }, - ) - .await -} - -#[wasm_bindgen(js_name = claimBtcWithdrawAirdrop)] -pub async fn claim_btc_withdraw_airdrop(address: String) -> Result { - let address = address - .parse() - .map_err(|e| Error::Wasm(format!("{:?}", e)))?; - gen_call_bytes( - address, - sdk::Msg { - type_: "nomic/MsgClaimBtcWithdrawAirdrop".to_string(), - value: serde_json::Map::new().into(), - }, - ) - .await -} - -#[wasm_bindgen(js_name = claimIbcTransferAirdrop)] -pub async fn claim_ibc_transfer_airdrop(address: String) -> Result { - let address = address - .parse() - .map_err(|e| Error::Wasm(format!("{:?}", e)))?; - gen_call_bytes( - address, - sdk::Msg { - type_: "nomic/MsgClaimIbcTransferAirdrop".to_string(), + type_: "nomic/MsgClaimAirdrop2".to_string(), value: serde_json::Map::new().into(), }, ) @@ -399,8 +369,8 @@ pub async fn redelegate( .await } -fn parse_part(part: nomic::airdrop::Part) -> AirdropDetails { - AirdropDetails { +fn parse_part(part: nomic::airdrop::Part) -> RewardDetails { + RewardDetails { locked: part.locked, claimed: part.claimed, claimable: part.claimable, @@ -424,6 +394,22 @@ pub async fn airdrop_balances(addr: String) -> Result { } } +#[wasm_bindgen(js_name = incentiveBalances)] +pub async fn incentive_balances(addr: String) -> Result { + let address = addr.parse().map_err(|e| Error::Wasm(format!("{:?}", e)))?; + + if let Some(account) = app_client() + .query(|app| Ok(app.incentives.get(address)?)) + .await? + { + Ok(Incentives { + testnet_participation: parse_part(account.testnet_participation), + }) + } else { + Ok(Incentives::default()) + } +} + #[wasm_bindgen] pub async fn nonce(addr: String) -> Result { let address = addr.parse().map_err(|e| Error::Wasm(format!("{:?}", e)))?; @@ -594,8 +580,8 @@ pub async fn withdraw(address: String, dest_addr: String, amount: u64) -> Result .await } -#[wasm_bindgen(js_name = joinAirdropAccounts)] -pub async fn join_airdrop_accounts( +#[wasm_bindgen(js_name = joinRewardAccounts)] +pub async fn join_reward_accounts( source_address: String, destination_address: String, ) -> Result { @@ -612,7 +598,7 @@ pub async fn join_airdrop_accounts( gen_call_bytes( address.to_string(), sdk::Msg { - type_: "nomic/MsgJoinAirdropAccounts".to_string(), + type_: "nomic/MsgJoinRewardAccounts".to_string(), value: value.into(), }, ) diff --git a/wasm/src/types.rs b/wasm/src/types.rs index 3ccf2c9b..84b6a90c 100644 --- a/wasm/src/types.rs +++ b/wasm/src/types.rs @@ -43,78 +43,58 @@ pub struct Coin { #[derive(Clone, Default)] #[wasm_bindgen] -pub struct AirdropDetails { +pub struct RewardDetails { pub locked: u64, pub claimed: u64, pub claimable: u64, pub amount: u64, } -#[cfg(feature = "testnet")] #[derive(Clone, Default)] #[wasm_bindgen(getter_with_clone)] pub struct Airdrop { - pub airdrop1: AirdropDetails, + pub airdrop1: RewardDetails, #[wasm_bindgen(js_name = btcDeposit)] - pub btc_deposit: AirdropDetails, + pub btc_deposit: RewardDetails, #[wasm_bindgen(js_name = btcWithdraw)] - pub btc_withdraw: AirdropDetails, + pub btc_withdraw: RewardDetails, #[wasm_bindgen(js_name = ibcTransfer)] - pub ibc_transfer: AirdropDetails, - // #[wasm_bindgen(js_name = testnetParticipation)] - // pub testnet_participation: AirdropDetails, -} - -#[cfg(not(feature = "testnet"))] -#[derive(Clone, Default)] -#[wasm_bindgen(getter_with_clone)] -pub struct Airdrop { - pub airdrop1: AirdropDetails, - #[wasm_bindgen(js_name = btcDeposit)] - pub btc_deposit: AirdropDetails, - #[wasm_bindgen(js_name = btcWithdraw)] - pub btc_withdraw: AirdropDetails, - #[wasm_bindgen(js_name = ibcTransfer)] - pub ibc_transfer: AirdropDetails, + pub ibc_transfer: RewardDetails, } #[wasm_bindgen] impl Airdrop { - #[cfg(feature = "testnet")] - #[wasm_bindgen(js_name = airdropTotal)] - pub fn airdrop_total(&self) -> u64 { - self.airdrop1.amount - + self.btc_deposit.amount - + self.btc_withdraw.amount - + self.ibc_transfer.amount - // + self.testnet_participation.amount - } - - #[cfg(not(feature = "testnet"))] - #[wasm_bindgen(js_name = airdropTotal)] - pub fn airdrop_total(&self) -> u64 { + pub fn total(&self) -> u64 { self.airdrop1.amount + self.btc_deposit.amount + self.btc_withdraw.amount + self.ibc_transfer.amount } - #[cfg(feature = "testnet")] #[wasm_bindgen(js_name = claimedTotal)] pub fn claimed_total(&self) -> u64 { self.airdrop1.claimed + self.btc_deposit.claimed + self.btc_withdraw.claimed + self.ibc_transfer.claimed - // + self.testnet_participation.claimed + } +} + +#[derive(Clone, Default)] +#[wasm_bindgen(getter_with_clone)] +pub struct Incentives { + #[wasm_bindgen(js_name = testnetParticipation)] + pub testnet_participation: RewardDetails, +} + +#[wasm_bindgen] +impl Incentives { + pub fn total(&self) -> u64 { + self.testnet_participation.amount } - #[cfg(not(feature = "testnet"))] #[wasm_bindgen(js_name = claimedTotal)] pub fn claimed_total(&self) -> u64 { - self.airdrop1.claimed - + self.btc_deposit.claimed - + self.btc_withdraw.claimed - + self.ibc_transfer.claimed + self.testnet_participation.claimed } }