diff --git a/contracts/liquidity_hub/bonding-manager/schema/bonding-manager.json b/contracts/liquidity_hub/bonding-manager/schema/bonding-manager.json index 8fe857a7..7951283d 100644 --- a/contracts/liquidity_hub/bonding-manager/schema/bonding-manager.json +++ b/contracts/liquidity_hub/bonding-manager/schema/bonding-manager.json @@ -592,21 +592,12 @@ ], "properties": { "bonded_assets": { - "description": "The total amount of bonded assets by the address.", + "description": "The assets that are bonded by the address.", "type": "array", "items": { "$ref": "#/definitions/Coin" } }, - "first_bonded_epoch_id": { - "description": "If Some, the epoch id at which the user/address bonded first time. None is used when this Response is used to check the bonded assets in the contract.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, "total_bonded": { "description": "The total amount of bonded tokens by the address. Bear in mind the bonded assets are considered to be equal for this purpose.", "allOf": [ @@ -642,6 +633,7 @@ "claimable": { "$schema": "http://json-schema.org/draft-07/schema#", "title": "ClaimableRewardBucketsResponse", + "description": "Response for the Claimable query", "type": "object", "required": [ "reward_buckets" @@ -1089,12 +1081,18 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Bond": { "type": "object", "required": [ "asset", "created_at_epoch", + "id", "last_updated", + "receiver", "weight" ], "properties": { @@ -1112,12 +1110,26 @@ "format": "uint64", "minimum": 0.0 }, + "id": { + "description": "The id of the bond.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, "last_updated": { "description": "The epoch id at which the bond was last time updated.", "type": "integer", "format": "uint64", "minimum": 0.0 }, + "receiver": { + "description": "The owner of the bond.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, "unbonded_at": { "description": "The time at which the Bond was unbonded.", "type": [ diff --git a/contracts/liquidity_hub/bonding-manager/schema/raw/response_to_bonded.json b/contracts/liquidity_hub/bonding-manager/schema/raw/response_to_bonded.json index 6deb74cb..233b61a6 100644 --- a/contracts/liquidity_hub/bonding-manager/schema/raw/response_to_bonded.json +++ b/contracts/liquidity_hub/bonding-manager/schema/raw/response_to_bonded.json @@ -9,21 +9,12 @@ ], "properties": { "bonded_assets": { - "description": "The total amount of bonded assets by the address.", + "description": "The assets that are bonded by the address.", "type": "array", "items": { "$ref": "#/definitions/Coin" } }, - "first_bonded_epoch_id": { - "description": "If Some, the epoch id at which the user/address bonded first time. None is used when this Response is used to check the bonded assets in the contract.", - "type": [ - "integer", - "null" - ], - "format": "uint64", - "minimum": 0.0 - }, "total_bonded": { "description": "The total amount of bonded tokens by the address. Bear in mind the bonded assets are considered to be equal for this purpose.", "allOf": [ diff --git a/contracts/liquidity_hub/bonding-manager/schema/raw/response_to_claimable.json b/contracts/liquidity_hub/bonding-manager/schema/raw/response_to_claimable.json index 788b95ed..c46decf7 100644 --- a/contracts/liquidity_hub/bonding-manager/schema/raw/response_to_claimable.json +++ b/contracts/liquidity_hub/bonding-manager/schema/raw/response_to_claimable.json @@ -1,6 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "title": "ClaimableRewardBucketsResponse", + "description": "Response for the Claimable query", "type": "object", "required": [ "reward_buckets" diff --git a/contracts/liquidity_hub/bonding-manager/schema/raw/response_to_unbonding.json b/contracts/liquidity_hub/bonding-manager/schema/raw/response_to_unbonding.json index 5181a615..a72e62fb 100644 --- a/contracts/liquidity_hub/bonding-manager/schema/raw/response_to_unbonding.json +++ b/contracts/liquidity_hub/bonding-manager/schema/raw/response_to_unbonding.json @@ -26,12 +26,18 @@ }, "additionalProperties": false, "definitions": { + "Addr": { + "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Really? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", + "type": "string" + }, "Bond": { "type": "object", "required": [ "asset", "created_at_epoch", + "id", "last_updated", + "receiver", "weight" ], "properties": { @@ -49,12 +55,26 @@ "format": "uint64", "minimum": 0.0 }, + "id": { + "description": "The id of the bond.", + "type": "integer", + "format": "uint64", + "minimum": 0.0 + }, "last_updated": { "description": "The epoch id at which the bond was last time updated.", "type": "integer", "format": "uint64", "minimum": 0.0 }, + "receiver": { + "description": "The owner of the bond.", + "allOf": [ + { + "$ref": "#/definitions/Addr" + } + ] + }, "unbonded_at": { "description": "The time at which the Bond was unbonded.", "type": [ diff --git a/contracts/liquidity_hub/bonding-manager/src/bonding/commands.rs b/contracts/liquidity_hub/bonding-manager/src/bonding/commands.rs index 62bed1e3..c93c7e1a 100644 --- a/contracts/liquidity_hub/bonding-manager/src/bonding/commands.rs +++ b/contracts/liquidity_hub/bonding-manager/src/bonding/commands.rs @@ -30,7 +30,7 @@ pub(crate) fn bond( &white_whale_std::epoch_manager::epoch_manager::QueryMsg::CurrentEpoch {}, )?; - let mut bonds_by_receiver = get_bonds_by_receiver( + let bonds_by_receiver = get_bonds_by_receiver( deps.storage, info.sender.to_string(), Some(true), @@ -40,7 +40,8 @@ pub(crate) fn bond( )?; let mut bond = if bonds_by_receiver.is_empty() { - // create bond id + // the user doesn't have any bonds of the given asset + let bond_id = BOND_COUNTER.update::<_, StdError>(deps.storage, |current_id| Ok(current_id + 1u64))?; @@ -56,16 +57,13 @@ pub(crate) fn bond( ..Bond::default() } } else { + // sanity check ensure!( bonds_by_receiver.len() == 1usize, - //todo change this error - ContractError::NothingToUnbond + ContractError::AssetMismatch ); - //todo change this error - bonds_by_receiver - .pop() - .ok_or(ContractError::NothingToUnbond)? + bonds_by_receiver[0].clone() }; // update bond values @@ -110,7 +108,7 @@ pub(crate) fn unbond( helpers::validate_claimed(&deps, &info)?; helpers::validate_bonding_for_current_epoch(&deps)?; - let mut bonds_by_receiver = get_bonds_by_receiver( + let bonds_by_receiver = get_bonds_by_receiver( deps.storage, info.sender.to_string(), Some(true), @@ -121,17 +119,19 @@ pub(crate) fn unbond( ensure!( bonds_by_receiver.len() <= 1usize, - //todo change this error - ContractError::NothingToUnbond + ContractError::AssetMismatch ); if bonds_by_receiver.is_empty() { Err(ContractError::NothingToUnbond) } else { - //todo change this error - let mut unbond: Bond = bonds_by_receiver - .pop() - .ok_or(ContractError::NothingToUnbond)?; + // sanity check + ensure!( + bonds_by_receiver.len() == 1usize, + ContractError::AssetMismatch + ); + + let mut unbond = bonds_by_receiver[0].clone(); // check if the address has enough bond ensure!( diff --git a/contracts/liquidity_hub/bonding-manager/src/contract.rs b/contracts/liquidity_hub/bonding-manager/src/contract.rs index f2b9a379..c282a13c 100644 --- a/contracts/liquidity_hub/bonding-manager/src/contract.rs +++ b/contracts/liquidity_hub/bonding-manager/src/contract.rs @@ -43,7 +43,7 @@ pub fn instantiate( CONFIG.save(deps.storage, &config)?; cw_ownable::initialize_owner(deps.storage, deps.api, Some(info.sender.as_str()))?; - // Initialize the upcoming reward bucket + // Initialize the upcoming reward bucket and bond counter UPCOMING_REWARD_BUCKET.save(deps.storage, &UpcomingRewardBucket::default())?; BOND_COUNTER.save(deps.storage, &0)?; diff --git a/contracts/liquidity_hub/bonding-manager/src/queries.rs b/contracts/liquidity_hub/bonding-manager/src/queries.rs index 5b490bf2..c8926db0 100644 --- a/contracts/liquidity_hub/bonding-manager/src/queries.rs +++ b/contracts/liquidity_hub/bonding-manager/src/queries.rs @@ -166,7 +166,6 @@ pub fn query_claimable( claimable_reward_buckets.retain(|bucket| !bucket.available.is_empty()); } - println!("here: {:?}", claimable_reward_buckets); Ok(ClaimableRewardBucketsResponse { reward_buckets: claimable_reward_buckets, }) diff --git a/contracts/liquidity_hub/bonding-manager/src/state.rs b/contracts/liquidity_hub/bonding-manager/src/state.rs index e9e16ee4..738de7c5 100644 --- a/contracts/liquidity_hub/bonding-manager/src/state.rs +++ b/contracts/liquidity_hub/bonding-manager/src/state.rs @@ -16,22 +16,16 @@ pub const BONDS: IndexedMap = IndexedMap::new( "bonds", BondIndexes { receiver: MultiIndex::new(|_pk, b| b.receiver.to_string(), "bonds", "bonds__receiver"), - asset_denom: MultiIndex::new( - |_pk, b| b.asset.denom.to_string(), - "bonds", - "bonds__asset_denom", - ), }, ); pub struct BondIndexes<'a> { pub receiver: MultiIndex<'a, String, Bond, String>, - pub asset_denom: MultiIndex<'a, String, Bond, String>, } impl<'a> IndexList for BondIndexes<'a> { fn get_indexes(&'_ self) -> Box> + '_> { - let v: Vec<&dyn Index> = vec![&self.receiver, &self.asset_denom]; + let v: Vec<&dyn Index> = vec![&self.receiver]; Box::new(v.into_iter()) } } @@ -137,8 +131,6 @@ pub fn get_bonds_by_receiver( }) .collect::>>()?; - println!("bonds_by_receiver: {:?}", bonds_by_receiver); - if let Some(is_bonding) = is_bonding { bonds_by_receiver.retain(|bond| bond.unbonded_at.is_none() == is_bonding); } diff --git a/contracts/liquidity_hub/bonding-manager/src/tests/bond.rs b/contracts/liquidity_hub/bonding-manager/src/tests/bond.rs index 7d882e5c..11e825dd 100644 --- a/contracts/liquidity_hub/bonding-manager/src/tests/bond.rs +++ b/contracts/liquidity_hub/bonding-manager/src/tests/bond.rs @@ -1,6 +1,7 @@ +use cosmwasm_std::{coin, coins}; + use crate::tests::suite::TestingSuite; use crate::ContractError; -use cosmwasm_std::{coin, coins}; #[test] fn test_bond_unsuccessful() { @@ -48,3 +49,34 @@ fn test_bond_unsuccessful() { }, ); } + +#[test] +fn test_same_bond_multiple_times() { + let mut suite = TestingSuite::default(); + let creator = suite.senders[0].clone(); + + suite + .instantiate_default() + .add_one_day() + .create_new_epoch() + .bond( + creator.clone(), + &vec![coin(1_000u128, "bWHALE")], + |result| { + result.unwrap(); + }, + ) + .bond( + creator.clone(), + &vec![coin(2_000u128, "bWHALE")], + |result| { + result.unwrap(); + }, + ) + .query_bonded(Some(creator.clone().to_string()), |res| { + assert_eq!( + res.unwrap().1.bonded_assets, + vec![coin(3_000u128, "bWHALE")] + ); + }); +}