├── contracts ├── anchor_airdrop_registry │ ├── src │ │ ├── testing │ │ │ └── mod.rs │ │ ├── lib.rs │ │ └── state.rs │ ├── .cargo │ │ └── config │ ├── schema │ │ ├── instantiate_msg.json │ │ ├── query_msg.json │ │ ├── m_i_r_airdrop_handle_msg.json │ │ └── execute_msg.json │ ├── README.md │ ├── examples │ │ └── schema.rs │ └── Cargo.toml ├── anchor_basset_hub │ ├── src │ │ ├── testing │ │ │ └── mod.rs │ │ ├── lib.rs │ │ ├── math.rs │ │ ├── bond.rs │ │ └── config.rs │ ├── .cargo │ │ └── config │ ├── schema │ │ ├── whitelisted_validators_response.json │ │ ├── withdrawable_unbonded_response.json │ │ ├── current_batch_response.json │ │ ├── parameters.json │ │ ├── instantiate_msg.json │ │ ├── config.json │ │ ├── unbond_requests_response.json │ │ ├── state.json │ │ ├── state_response.json │ │ ├── all_history_response.json │ │ ├── query_msg.json │ │ └── execute_msg.json │ ├── README.md │ ├── examples │ │ └── schema.rs │ └── Cargo.toml ├── anchor_basset_reward │ ├── src │ │ ├── testing │ │ │ ├── mod.rs │ │ │ └── mock_querier.rs │ │ ├── lib.rs │ │ ├── querier.rs │ │ ├── math.rs │ │ ├── contract.rs │ │ ├── global.rs │ │ ├── state.rs │ │ └── user.rs │ ├── .cargo │ │ └── config │ ├── schema │ │ ├── config_response.json │ │ ├── instantiate_msg.json │ │ ├── accrued_rewards_response.json │ │ ├── state_response.json │ │ ├── holder_response.json │ │ ├── holders_response.json │ │ ├── query_msg.json │ │ └── execute_msg.json │ ├── rustfmt.toml │ ├── NOTICE │ ├── README.md │ ├── examples │ │ └── schema.rs │ └── Cargo.toml └── anchor_basset_token │ ├── src │ ├── testing │ │ ├── mod.rs │ │ └── mock_querier.rs │ ├── lib.rs │ ├── msg.rs │ ├── querier.rs │ ├── state.rs │ ├── contract.rs │ └── handler.rs │ ├── .cargo │ └── config │ ├── schema │ ├── all_accounts_response.json │ ├── balance_response.json │ ├── token_info_response.json │ ├── token_init_msg.json │ ├── allowance_response.json │ ├── all_allowances_response.json │ └── query_msg.json │ ├── NOTICE │ ├── examples │ └── schema.rs │ ├── Cargo.toml │ └── README.md ├── packages ├── signed_integers │ ├── src │ │ ├── lib.rs │ │ └── signed_integer.rs │ └── Cargo.toml └── basset │ ├── src │ ├── lib.rs │ ├── contract_error.rs │ ├── testing.rs │ ├── tax_querier.rs │ ├── reward.rs │ ├── airdrop.rs │ ├── hub.rs │ └── mock_querier.rs │ └── Cargo.toml ├── Cargo.toml ├── .editorconfig ├── .gitignore ├── CHANGELOG.md ├── rustfmt.toml ├── .github ├── actions-rs │ └── grcov.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── pull_request_template.md └── workflows │ ├── main.yml │ ├── release-artifacts.yml │ └── tests.yml ├── NOTICE ├── codecov.yml ├── README.md ├── FIRST_DEMO.md └── LICENSE /contracts/anchor_airdrop_registry/src/testing/mod.rs: -------------------------------------------------------------------------------- 1 | mod tests; 2 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/src/testing/mod.rs: -------------------------------------------------------------------------------- 1 | mod mock_querier; 2 | mod tests; 3 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/src/testing/mod.rs: -------------------------------------------------------------------------------- 1 | mod mock_querier; 2 | mod tests; 3 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/src/testing/mod.rs: -------------------------------------------------------------------------------- 1 | mod mock_querier; 2 | mod tests; 3 | -------------------------------------------------------------------------------- /packages/signed_integers/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod signed_integer; 2 | 3 | pub use crate::signed_integer::SignedInt; 4 | -------------------------------------------------------------------------------- /contracts/anchor_airdrop_registry/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | pub mod state; 3 | 4 | #[cfg(test)] 5 | mod testing; 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["contracts/*", "packages/*"] 3 | 4 | [profile.release] 5 | rpath = false 6 | lto = true 7 | overflow-checks = true -------------------------------------------------------------------------------- /contracts/anchor_basset_token/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | pub mod msg; 3 | pub mod state; 4 | 5 | mod handler; 6 | mod querier; 7 | 8 | #[cfg(test)] 9 | mod testing; 10 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | pub mod state; 3 | 4 | mod bond; 5 | mod config; 6 | mod math; 7 | mod unbond; 8 | 9 | #[cfg(test)] 10 | mod testing; 11 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | pub mod state; 3 | 4 | mod global; 5 | mod math; 6 | mod querier; 7 | mod user; 8 | 9 | #[cfg(test)] 10 | mod testing; 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.rs] 11 | indent_size = 4 12 | -------------------------------------------------------------------------------- /packages/basset/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod tax_querier; 2 | 3 | pub use tax_querier::deduct_tax; 4 | pub mod airdrop; 5 | pub mod contract_error; 6 | pub mod hub; 7 | pub mod reward; 8 | 9 | #[cfg(test)] 10 | mod mock_querier; 11 | 12 | #[cfg(test)] 13 | mod testing; 14 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/.cargo/config: -------------------------------------------------------------------------------- 1 | [alias] 2 | wasm = "build --release --target wasm32-unknown-unknown" 3 | wasm-debug = "build --target wasm32-unknown-unknown" 4 | unit-test = "test --lib" 5 | integration-test = "test --test integration" 6 | schema = "run --example schema" 7 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/.cargo/config: -------------------------------------------------------------------------------- 1 | [alias] 2 | wasm = "build --release --target wasm32-unknown-unknown" 3 | wasm-debug = "build --target wasm32-unknown-unknown" 4 | unit-test = "test --lib" 5 | integration-test = "test --test integration" 6 | schema = "run --example schema" 7 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/.cargo/config: -------------------------------------------------------------------------------- 1 | [alias] 2 | wasm = "build --release --target wasm32-unknown-unknown" 3 | wasm-debug = "build --target wasm32-unknown-unknown" 4 | unit-test = "test --lib" 5 | integration-test = "test --test integration" 6 | schema = "run --example schema" 7 | -------------------------------------------------------------------------------- /contracts/anchor_airdrop_registry/.cargo/config: -------------------------------------------------------------------------------- 1 | [alias] 2 | wasm = "build --release --target wasm32-unknown-unknown" 3 | wasm-debug = "build --target wasm32-unknown-unknown" 4 | unit-test = "test --lib" 5 | integration-test = "test --test integration" 6 | schema = "run --example schema" 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build results 2 | /target 3 | 4 | # Cargo+Git helper file (https://github.com/rust-lang/cargo/blob/0.44.1/src/cargo/sources/git/utils.rs#L320-L327) 5 | .cargo-ok 6 | 7 | # Text file backups 8 | **/*.rs.bk 9 | 10 | # macOS 11 | .DS_Store 12 | 13 | # IDEs 14 | *.iml 15 | .idea 16 | 17 | artifacts -------------------------------------------------------------------------------- /contracts/anchor_basset_token/schema/all_accounts_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "AllAccountsResponse", 4 | "type": "object", 5 | "required": [ 6 | "accounts" 7 | ], 8 | "properties": { 9 | "accounts": { 10 | "type": "array", 11 | "items": { 12 | "type": "string" 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.2.1 2 | Bug fix: `update_global_index` failed if there were tokens with unknown exchange rates on the `anchor_basset_reward` contract balance. The solution is to handle only the tokens with known exchange rates. 3 | 4 | # 0.2.0 5 | Columbus-5 update 6 | 7 | * Bump CosmWasm to [v0.16.0](https://github.com/CosmWasm/cosmwasm/releases/v0.16.0) 8 | 9 | # 0.1.0 10 | 11 | Initial Release 12 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/schema/whitelisted_validators_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "WhitelistedValidatorsResponse", 4 | "type": "object", 5 | "required": [ 6 | "validators" 7 | ], 8 | "properties": { 9 | "validators": { 10 | "type": "array", 11 | "items": { 12 | "type": "string" 13 | } 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/schema/config_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "ConfigResponse", 4 | "type": "object", 5 | "required": [ 6 | "hub_contract", 7 | "reward_denom" 8 | ], 9 | "properties": { 10 | "hub_contract": { 11 | "type": "string" 12 | }, 13 | "reward_denom": { 14 | "type": "string" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/schema/instantiate_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "InstantiateMsg", 4 | "type": "object", 5 | "required": [ 6 | "hub_contract", 7 | "reward_denom" 8 | ], 9 | "properties": { 10 | "hub_contract": { 11 | "type": "string" 12 | }, 13 | "reward_denom": { 14 | "type": "string" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/anchor_airdrop_registry/schema/instantiate_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "InstantiateMsg", 4 | "type": "object", 5 | "required": [ 6 | "hub_contract", 7 | "reward_contract" 8 | ], 9 | "properties": { 10 | "hub_contract": { 11 | "type": "string" 12 | }, 13 | "reward_contract": { 14 | "type": "string" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/src/msg.rs: -------------------------------------------------------------------------------- 1 | use cw20::{Cw20Coin, MinterResponse}; 2 | use schemars::JsonSchema; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Serialize, Deserialize, JsonSchema)] 6 | pub struct TokenInitMsg { 7 | pub name: String, 8 | pub symbol: String, 9 | pub decimals: u8, 10 | pub initial_balances: Vec, 11 | pub mint: Option, 12 | pub hub_contract: String, 13 | } 14 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # stable 2 | newline_style = "unix" 3 | hard_tabs = false 4 | tab_spaces = 4 5 | 6 | # unstable... should we require `rustup run nightly cargo fmt` ? 7 | # or just update the style guide when they are stable? 8 | #fn_single_line = true 9 | #format_code_in_doc_comments = true 10 | #overflow_delimited_expr = true 11 | #reorder_impl_items = true 12 | #struct_field_align_threshold = 20 13 | #struct_lit_single_line = true 14 | #report_todo = "Always" 15 | 16 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/rustfmt.toml: -------------------------------------------------------------------------------- 1 | # stable 2 | newline_style = "unix" 3 | hard_tabs = false 4 | tab_spaces = 4 5 | 6 | # unstable... should we require `rustup run nightly cargo fmt` ? 7 | # or just update the style guide when they are stable? 8 | #fn_single_line = true 9 | #format_code_in_doc_comments = true 10 | #overflow_delimited_expr = true 11 | #reorder_impl_items = true 12 | #struct_field_align_threshold = 20 13 | #struct_lit_single_line = true 14 | #report_todo = "Always" 15 | 16 | -------------------------------------------------------------------------------- /.github/actions-rs/grcov.yml: -------------------------------------------------------------------------------- 1 | source-dir: ./contracts 2 | binary-path: ./target/debug/ 3 | branch: true 4 | ignore-not-existing: true 5 | llvm: true 6 | filter: covered 7 | output-type: lcov 8 | output-path: ./lcov.info 9 | prefix-dir: /home/user/build/ 10 | excl-start: '#[test]' 11 | excl-br-start: '#[test]' 12 | ignore: 13 | - "./packages/**" 14 | - "./**/lib.rs" 15 | - "./**/examples/schema.rs" 16 | - "./**/testing/**/*" 17 | - "./**/tests.rs" 18 | - "./**/querier.rs" 19 | - "./**/mock_querier.rs" 20 | -------------------------------------------------------------------------------- /contracts/anchor_airdrop_registry/README.md: -------------------------------------------------------------------------------- 1 | # Anchor bAsset Airdrop Registry 2 | 3 | **NOTE**: Reference documentation for this contract is available [here](https://app.gitbook.com/@anchor-protocol/s/anchor-2/smart-contracts/bluna/airdrop-registry). 4 | 5 | The Airdrop Registry contract manages the fabrication of messages relevant to claiming and swapping tokens airdropped to Luna delegators. Airdropped tokens to the bLuna Hub contract is swapped for Terra USD and distributed as bLuna rewards. 6 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/README.md: -------------------------------------------------------------------------------- 1 | # Anchor bAsset Hub 2 | 3 | **NOTE**: Reference documentation for this contract is available [here](https://anchor-protocol.gitbook.io/anchor/bluna/hub). 4 | 5 | The Hub contract acts as the central hub for all minted bLuna. Native Luna tokens received from users are delegated from here, and undelegations from bLuna unbond requests are also handled from this contract. Rewards generated from delegations are withdrawn to the Reward contract, later distributed to bLuna holders. 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE] title-of-feature " 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Problem definition** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Feature specification** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report to help us improve 4 | title: "[BUG] title_of_bug" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Describe the steps to reproduce the behavior. 15 | 16 | **Context & versions** 17 | Delineate the correct versions of all the components involved in reproducing the bug. 18 | 19 | **(if applicable) suggested solution** 20 | If you know the solution to the bug, let us know! 21 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Summary of changes 2 | 3 | 4 | 5 | ## Report of required housekeeping 6 | 7 | - [ ] Github issue OR spec proposal link 8 | - [ ] Wrote tests 9 | - [ ] Added a relevant changelog entry to CHANGELOG.md 10 | 11 | ---- 12 | 13 | ## (FOR ADMIN) Before merging 14 | 15 | - [ ] Added appropriate labels to PR 16 | - [ ] Squashed all commits, uses message "Merge pull request #XYZ: [title]" (coding standards) 17 | - [ ] Confirm added tests are consistent with the intended behavior of changes 18 | - [ ] Ensure all tests pass 19 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Anchor Protocol 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | require_ci_to_pass: yes 3 | 4 | coverage: 5 | precision: 2 6 | round: down 7 | range: "70...100" 8 | 9 | parsers: 10 | gcov: 11 | branch_detection: 12 | conditional: yes 13 | loop: yes 14 | method: no 15 | macro: no 16 | 17 | comment: 18 | layout: "reach,diff,flags,files,footer" 19 | behavior: default 20 | require_changes: no 21 | 22 | ignore: 23 | - "./packages/**" 24 | - "./**/lib.rs" 25 | - "./**/examples/schema.rs" 26 | - "./**/testing/**/*" 27 | - "./**/tests.rs" 28 | - "./**/querier.rs" 29 | - "./**/mock_querier.rs" 30 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2020 MSNTCS 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /packages/basset/src/contract_error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{OverflowError, StdError}; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug, PartialEq)] 5 | pub enum ContractError { 6 | #[error("{0}")] 7 | Std(#[from] StdError), 8 | 9 | #[error("{0}")] 10 | OverflowError(#[from] OverflowError), 11 | 12 | #[error("Unauthorized")] 13 | Unauthorized {}, 14 | 15 | #[error("Invalid zero amount")] 16 | InvalidZeroAmount {}, 17 | 18 | #[error("Max spread assertion")] 19 | MaxSpreadAssertion {}, 20 | 21 | #[error("Max slippage assertion")] 22 | MaxSlippageAssertion {}, 23 | 24 | #[error("Asset mismatch")] 25 | AssetMismatch {}, 26 | } 27 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/src/math.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Decimal, Uint128}; 2 | 3 | const DECIMAL_FRACTIONAL: Uint128 = Uint128::new(1_000_000_000u128); 4 | 5 | /// return a / b 6 | pub fn decimal_division(a: Uint128, b: Decimal) -> Uint128 { 7 | let decimal = Decimal::from_ratio(a, b * DECIMAL_FRACTIONAL); 8 | decimal * DECIMAL_FRACTIONAL 9 | } 10 | 11 | #[cfg(test)] 12 | mod tests { 13 | use super::*; 14 | 15 | #[test] 16 | fn test_decimal_division() { 17 | let a = Uint128::new(100); 18 | let b = Decimal::from_ratio(Uint128::new(10), Uint128::new(50)); 19 | let res = decimal_division(a, b); 20 | assert_eq!(res, Uint128::new(500)); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/src/querier.rs: -------------------------------------------------------------------------------- 1 | use basset::hub::Config; 2 | use cosmwasm_std::{Addr, Binary, CanonicalAddr, Deps, QueryRequest, StdResult, WasmQuery}; 3 | use cosmwasm_storage::to_length_prefixed; 4 | 5 | pub fn query_token_contract(deps: Deps, contract_addr: Addr) -> StdResult { 6 | let conf: Config = deps 7 | .querier 8 | .query(&QueryRequest::Wasm(WasmQuery::Raw { 9 | contract_addr: contract_addr.to_string(), 10 | key: Binary::from(to_length_prefixed(b"config")), 11 | })) 12 | .unwrap(); 13 | 14 | Ok(conf 15 | .token_contract 16 | .expect("the token contract must have been registered")) 17 | } 18 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/NOTICE: -------------------------------------------------------------------------------- 1 | CW20-Base: A reference implementation for fungible token on CosmWasm 2 | Copyright (C) 2020 Confio OÜ 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | -------------------------------------------------------------------------------- /contracts/anchor_airdrop_registry/examples/schema.rs: -------------------------------------------------------------------------------- 1 | use std::env::current_dir; 2 | use std::fs::create_dir_all; 3 | 4 | use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; 5 | 6 | use basset::airdrop::{ExecuteMsg, InstantiateMsg, MIRAirdropHandleMsg, QueryMsg}; 7 | 8 | fn main() { 9 | let mut out_dir = current_dir().unwrap(); 10 | out_dir.push("schema"); 11 | create_dir_all(&out_dir).unwrap(); 12 | remove_schemas(&out_dir).unwrap(); 13 | 14 | export_schema(&schema_for!(InstantiateMsg), &out_dir); 15 | export_schema(&schema_for!(ExecuteMsg), &out_dir); 16 | export_schema(&schema_for!(QueryMsg), &out_dir); 17 | export_schema(&schema_for!(MIRAirdropHandleMsg), &out_dir); 18 | } 19 | -------------------------------------------------------------------------------- /packages/signed_integers/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "signed_integer" 3 | version = "0.1.0" 4 | authors = ["MSNTCS "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | [profile.release] 9 | opt-level = 3 10 | debug = false 11 | rpath = false 12 | lto = true 13 | debug-assertions = false 14 | codegen-units = 1 15 | panic = 'abort' 16 | incremental = false 17 | overflow-checks = true 18 | 19 | [dependencies] 20 | cw20 = { version = "0.8.0" } 21 | cosmwasm-storage = { version = "0.16.0" } 22 | terra-cosmwasm = { version = "2.2.0" } 23 | cosmwasm-std = { version = "0.16.0" } 24 | schemars = "0.8.1" 25 | serde = { version = "1.0.103", default-features = false, features = ["derive"] } -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/README.md: -------------------------------------------------------------------------------- 1 | # Anchor bAsset Reward 2 | 3 | **NOTE**: Reference documentation for this contract is available [here](https://anchor-protocol.gitbook.io/anchor/bluna/reward). 4 | 5 | The Reward contract contains logic for distributing Luna delegation rewards to holders of bLuna. After the Hub contract withdraws Luna delegation rewards to the Reward contract, the Hub contract can request all reward to a single denomination (Terra USD), which can then be distributed to bLuna holders. Holders of bLuna can then send a request to this contract to claim their accrued rewards. 6 | The Reward contract also stores the balance and reward index values for all bLuna holders, which is used to calculate the amount of bLuna rewards that a specific holder has accrued. 7 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/schema/balance_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "BalanceResponse", 4 | "type": "object", 5 | "required": [ 6 | "balance" 7 | ], 8 | "properties": { 9 | "balance": { 10 | "$ref": "#/definitions/Uint128" 11 | } 12 | }, 13 | "definitions": { 14 | "Uint128": { 15 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 16 | "type": "string" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/basset/src/testing.rs: -------------------------------------------------------------------------------- 1 | use crate::deduct_tax; 2 | use crate::mock_querier::mock_dependencies; 3 | use cosmwasm_std::{Coin, Decimal, Uint128}; 4 | 5 | #[test] 6 | fn test_deduct_tax() { 7 | let mut deps = mock_dependencies(&[]); 8 | 9 | deps.querier.with_tax( 10 | Decimal::percent(1), 11 | &[(&"uusd".to_string(), &Uint128::from(1000000u128))], 12 | ); 13 | 14 | // cap to 1000000 15 | assert_eq!( 16 | deduct_tax(&deps.as_mut().querier, Coin::new(10000000000u128, "uusd")).unwrap(), 17 | Coin { 18 | denom: "uusd".to_string(), 19 | amount: Uint128::new(9999000000u128) 20 | } 21 | ); 22 | 23 | // normal tax 24 | assert_eq!( 25 | deduct_tax(&deps.as_mut().querier, Coin::new(50000000u128, "uusd")).unwrap(), 26 | Coin { 27 | denom: "uusd".to_string(), 28 | amount: Uint128::new(49504950u128) 29 | } 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/schema/accrued_rewards_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "AccruedRewardsResponse", 4 | "type": "object", 5 | "required": [ 6 | "rewards" 7 | ], 8 | "properties": { 9 | "rewards": { 10 | "$ref": "#/definitions/Uint128" 11 | } 12 | }, 13 | "definitions": { 14 | "Uint128": { 15 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 16 | "type": "string" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/src/querier.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Addr, Binary, DepsMut, QueryRequest, StdResult, WasmQuery}; 2 | use cosmwasm_storage::to_length_prefixed; 3 | 4 | use crate::state::read_hub_contract; 5 | use basset::hub::Config; 6 | 7 | pub fn query_reward_contract(deps: &DepsMut) -> StdResult { 8 | let hub_address = deps 9 | .api 10 | .addr_humanize(&read_hub_contract(deps.storage).unwrap()) 11 | .unwrap(); 12 | 13 | let config: Config = deps.querier.query(&QueryRequest::Wasm(WasmQuery::Raw { 14 | contract_addr: hub_address.to_string(), 15 | key: Binary::from(to_length_prefixed(b"config")), 16 | }))?; 17 | 18 | let address = deps 19 | .api 20 | .addr_humanize( 21 | &config 22 | .reward_contract 23 | .expect("the reward contract must have been registered"), 24 | ) 25 | .unwrap(); 26 | Ok(address) 27 | } 28 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/schema/withdrawable_unbonded_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "WithdrawableUnbondedResponse", 4 | "type": "object", 5 | "required": [ 6 | "withdrawable" 7 | ], 8 | "properties": { 9 | "withdrawable": { 10 | "$ref": "#/definitions/Uint128" 11 | } 12 | }, 13 | "definitions": { 14 | "Uint128": { 15 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 16 | "type": "string" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/examples/schema.rs: -------------------------------------------------------------------------------- 1 | use std::env::current_dir; 2 | use std::fs::create_dir_all; 3 | 4 | use basset::reward::{ 5 | AccruedRewardsResponse, ConfigResponse, ExecuteMsg, HolderResponse, HoldersResponse, 6 | InstantiateMsg, QueryMsg, StateResponse, 7 | }; 8 | use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; 9 | 10 | fn main() { 11 | let mut out_dir = current_dir().unwrap(); 12 | out_dir.push("schema"); 13 | create_dir_all(&out_dir).unwrap(); 14 | remove_schemas(&out_dir).unwrap(); 15 | 16 | export_schema(&schema_for!(InstantiateMsg), &out_dir); 17 | export_schema(&schema_for!(ExecuteMsg), &out_dir); 18 | export_schema(&schema_for!(QueryMsg), &out_dir); 19 | export_schema(&schema_for!(ConfigResponse), &out_dir); 20 | export_schema(&schema_for!(StateResponse), &out_dir); 21 | export_schema(&schema_for!(AccruedRewardsResponse), &out_dir); 22 | export_schema(&schema_for!(HolderResponse), &out_dir); 23 | export_schema(&schema_for!(HoldersResponse), &out_dir); 24 | } 25 | -------------------------------------------------------------------------------- /packages/basset/src/tax_querier.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Coin, Decimal, QuerierWrapper, StdResult, Uint128}; 2 | 3 | use terra_cosmwasm::TerraQuerier; 4 | 5 | static DECIMAL_FRACTION: Uint128 = Uint128::new(1_000_000_000_000_000_000u128); 6 | 7 | pub fn compute_tax(querier: &QuerierWrapper, coin: &Coin) -> StdResult { 8 | let terra_querier = TerraQuerier::new(querier); 9 | let tax_rate: Decimal = (terra_querier.query_tax_rate()?).rate; 10 | let tax_cap: Uint128 = (terra_querier.query_tax_cap(coin.denom.to_string())?).cap; 11 | Ok(std::cmp::min( 12 | (coin.amount.checked_sub(coin.amount.multiply_ratio( 13 | DECIMAL_FRACTION, 14 | DECIMAL_FRACTION * tax_rate + DECIMAL_FRACTION, 15 | )))?, 16 | tax_cap, 17 | )) 18 | } 19 | 20 | pub fn deduct_tax(querier: &QuerierWrapper, coin: Coin) -> StdResult { 21 | let tax_amount = compute_tax(querier, &coin)?; 22 | Ok(Coin { 23 | denom: coin.denom, 24 | amount: (coin.amount.checked_sub(tax_amount))?, 25 | }) 26 | } 27 | -------------------------------------------------------------------------------- /packages/basset/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basset" 3 | version = "0.1.0" 4 | authors = ["MSNTCS "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [profile.release] 10 | opt-level = 3 11 | debug = false 12 | rpath = false 13 | lto = true 14 | debug-assertions = false 15 | codegen-units = 1 16 | panic = 'abort' 17 | incremental = false 18 | overflow-checks = true 19 | 20 | [features] 21 | # for quicker tests, cargo test --lib 22 | # for more explicit tests, cargo test --features=backtraces 23 | backtraces = ["cosmwasm-std/backtraces"] 24 | 25 | [dependencies] 26 | cw20 = { version = "0.8.0" } 27 | cosmwasm-storage = { version = "0.16.0"} 28 | terra-cosmwasm = { version = "2.2.0" } 29 | cosmwasm-std = { version = "0.16.0" } 30 | schemars = "0.8.1" 31 | thiserror = { version = "1.0.20" } 32 | serde = { version = "1.0.103", default-features = false, features = ["derive"] } 33 | 34 | [dev-dependencies] 35 | cosmwasm-vm = { version = "0.16.0", default-features = false } 36 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/schema/current_batch_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "CurrentBatchResponse", 4 | "type": "object", 5 | "required": [ 6 | "id", 7 | "requested_with_fee" 8 | ], 9 | "properties": { 10 | "id": { 11 | "type": "integer", 12 | "format": "uint64", 13 | "minimum": 0.0 14 | }, 15 | "requested_with_fee": { 16 | "$ref": "#/definitions/Uint128" 17 | } 18 | }, 19 | "definitions": { 20 | "Uint128": { 21 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 22 | "type": "string" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/examples/schema.rs: -------------------------------------------------------------------------------- 1 | use std::env::current_dir; 2 | use std::fs::create_dir_all; 3 | 4 | use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; 5 | 6 | use anchor_basset_token::msg::TokenInitMsg; 7 | use cw20::{ 8 | AllAccountsResponse, AllAllowancesResponse, AllowanceResponse, BalanceResponse, 9 | TokenInfoResponse, 10 | }; 11 | use cw20_legacy::msg::{ExecuteMsg, QueryMsg}; 12 | 13 | fn main() { 14 | let mut out_dir = current_dir().unwrap(); 15 | out_dir.push("schema"); 16 | create_dir_all(&out_dir).unwrap(); 17 | remove_schemas(&out_dir).unwrap(); 18 | 19 | export_schema(&schema_for!(TokenInitMsg), &out_dir); 20 | export_schema(&schema_for!(ExecuteMsg), &out_dir); 21 | export_schema(&schema_for!(QueryMsg), &out_dir); 22 | export_schema(&schema_for!(AllowanceResponse), &out_dir); 23 | export_schema(&schema_for!(BalanceResponse), &out_dir); 24 | export_schema(&schema_for!(TokenInfoResponse), &out_dir); 25 | export_schema(&schema_for!(AllAllowancesResponse), &out_dir); 26 | export_schema(&schema_for!(AllAccountsResponse), &out_dir); 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | on: [push] 2 | 3 | name: Code Coverage 4 | 5 | jobs: 6 | lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v1 10 | - uses: actions-rs/toolchain@v1 11 | with: 12 | toolchain: nightly 13 | override: true 14 | - uses: actions-rs/cargo@v1 15 | with: 16 | command: clean 17 | - uses: actions-rs/cargo@v1 18 | with: 19 | command: test 20 | env: 21 | CARGO_INCREMENTAL: '0' 22 | RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests' 23 | RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests' 24 | - id: coverage 25 | uses: actions-rs/grcov@v0.1 26 | with: 27 | config: actions-rs/grcov.yml 28 | - name: Upload coverage to Codecov 29 | uses: codecov/codecov-action@v2 30 | with: 31 | token: ${{ secrets.CODECOV_TOKEN }} 32 | files: ${{ steps.coverage.outputs.report }} 33 | path_to_write_report: ./coverage/codecov_report.txt 34 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/schema/token_info_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "TokenInfoResponse", 4 | "type": "object", 5 | "required": [ 6 | "decimals", 7 | "name", 8 | "symbol", 9 | "total_supply" 10 | ], 11 | "properties": { 12 | "decimals": { 13 | "type": "integer", 14 | "format": "uint8", 15 | "minimum": 0.0 16 | }, 17 | "name": { 18 | "type": "string" 19 | }, 20 | "symbol": { 21 | "type": "string" 22 | }, 23 | "total_supply": { 24 | "$ref": "#/definitions/Uint128" 25 | } 26 | }, 27 | "definitions": { 28 | "Uint128": { 29 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 30 | "type": "string" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/schema/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "Parameters", 4 | "type": "object", 5 | "required": [ 6 | "epoch_period", 7 | "er_threshold", 8 | "peg_recovery_fee", 9 | "reward_denom", 10 | "unbonding_period", 11 | "underlying_coin_denom" 12 | ], 13 | "properties": { 14 | "epoch_period": { 15 | "type": "integer", 16 | "format": "uint64", 17 | "minimum": 0.0 18 | }, 19 | "er_threshold": { 20 | "$ref": "#/definitions/Decimal" 21 | }, 22 | "peg_recovery_fee": { 23 | "$ref": "#/definitions/Decimal" 24 | }, 25 | "reward_denom": { 26 | "type": "string" 27 | }, 28 | "unbonding_period": { 29 | "type": "integer", 30 | "format": "uint64", 31 | "minimum": 0.0 32 | }, 33 | "underlying_coin_denom": { 34 | "type": "string" 35 | } 36 | }, 37 | "definitions": { 38 | "Decimal": { 39 | "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", 40 | "type": "string" 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /contracts/anchor_airdrop_registry/schema/query_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "QueryMsg", 4 | "anyOf": [ 5 | { 6 | "type": "object", 7 | "required": [ 8 | "config" 9 | ], 10 | "properties": { 11 | "config": { 12 | "type": "object" 13 | } 14 | }, 15 | "additionalProperties": false 16 | }, 17 | { 18 | "type": "object", 19 | "required": [ 20 | "airdrop_info" 21 | ], 22 | "properties": { 23 | "airdrop_info": { 24 | "type": "object", 25 | "properties": { 26 | "airdrop_token": { 27 | "type": [ 28 | "string", 29 | "null" 30 | ] 31 | }, 32 | "limit": { 33 | "type": [ 34 | "integer", 35 | "null" 36 | ], 37 | "format": "uint32", 38 | "minimum": 0.0 39 | }, 40 | "start_after": { 41 | "type": [ 42 | "string", 43 | "null" 44 | ] 45 | } 46 | } 47 | } 48 | }, 49 | "additionalProperties": false 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /.github/workflows/release-artifacts.yml: -------------------------------------------------------------------------------- 1 | name: Release Artifacts 2 | on: 3 | push: 4 | tags: 5 | - "v[0-9]+.[0-9]+.[0-9]+" # Push events to matching v*, i.e. v1.0, v20.15.10 6 | - "v[0-9]+.[0-9]+.[0-9]+-rc*" # Push events to matching v*, i.e. v1.0-rc1, v20.15.10-rc5 7 | 8 | jobs: 9 | release-artifacts: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Install latest stable 14 | uses: actions-rs/toolchain@v1 15 | with: 16 | toolchain: stable 17 | override: true 18 | components: rustfmt, clippy 19 | - name: Setup Docker Buildx 20 | uses: docker/setup-buildx-action@v1 21 | - name: Generate Cargo.lock 22 | run: | 23 | cargo fetch --verbose 24 | - name: Build Artifacts 25 | run: | 26 | docker run --rm -v "$(pwd)":/code \ 27 | --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ 28 | --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ 29 | cosmwasm/rust-optimizer:0.11.5 30 | tar -zcvf cosmwasm-artifacts.tar.gz artifacts 31 | - name: Create Release 32 | uses: softprops/action-gh-release@v1 33 | with: 34 | files: cosmwasm-artifacts.tar.gz 35 | body_path: CHANGELOG.md 36 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/schema/instantiate_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "InstantiateMsg", 4 | "type": "object", 5 | "required": [ 6 | "epoch_period", 7 | "er_threshold", 8 | "peg_recovery_fee", 9 | "reward_denom", 10 | "unbonding_period", 11 | "underlying_coin_denom", 12 | "validator" 13 | ], 14 | "properties": { 15 | "epoch_period": { 16 | "type": "integer", 17 | "format": "uint64", 18 | "minimum": 0.0 19 | }, 20 | "er_threshold": { 21 | "$ref": "#/definitions/Decimal" 22 | }, 23 | "peg_recovery_fee": { 24 | "$ref": "#/definitions/Decimal" 25 | }, 26 | "reward_denom": { 27 | "type": "string" 28 | }, 29 | "unbonding_period": { 30 | "type": "integer", 31 | "format": "uint64", 32 | "minimum": 0.0 33 | }, 34 | "underlying_coin_denom": { 35 | "type": "string" 36 | }, 37 | "validator": { 38 | "type": "string" 39 | } 40 | }, 41 | "definitions": { 42 | "Decimal": { 43 | "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", 44 | "type": "string" 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/schema/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "Config", 4 | "type": "object", 5 | "required": [ 6 | "creator" 7 | ], 8 | "properties": { 9 | "airdrop_registry_contract": { 10 | "anyOf": [ 11 | { 12 | "$ref": "#/definitions/CanonicalAddr" 13 | }, 14 | { 15 | "type": "null" 16 | } 17 | ] 18 | }, 19 | "creator": { 20 | "$ref": "#/definitions/CanonicalAddr" 21 | }, 22 | "reward_contract": { 23 | "anyOf": [ 24 | { 25 | "$ref": "#/definitions/CanonicalAddr" 26 | }, 27 | { 28 | "type": "null" 29 | } 30 | ] 31 | }, 32 | "token_contract": { 33 | "anyOf": [ 34 | { 35 | "$ref": "#/definitions/CanonicalAddr" 36 | }, 37 | { 38 | "type": "null" 39 | } 40 | ] 41 | } 42 | }, 43 | "definitions": { 44 | "Binary": { 45 | "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec", 46 | "type": "string" 47 | }, 48 | "CanonicalAddr": { 49 | "$ref": "#/definitions/Binary" 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/schema/unbond_requests_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "UnbondRequestsResponse", 4 | "type": "object", 5 | "required": [ 6 | "address", 7 | "requests" 8 | ], 9 | "properties": { 10 | "address": { 11 | "type": "string" 12 | }, 13 | "requests": { 14 | "type": "array", 15 | "items": { 16 | "type": "array", 17 | "items": [ 18 | { 19 | "type": "integer", 20 | "format": "uint64", 21 | "minimum": 0.0 22 | }, 23 | { 24 | "$ref": "#/definitions/Uint128" 25 | } 26 | ], 27 | "maxItems": 2, 28 | "minItems": 2 29 | } 30 | } 31 | }, 32 | "definitions": { 33 | "Uint128": { 34 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 35 | "type": "string" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anchor_basset_token" 3 | version = "1.0.0" 4 | authors = ["MSNTCS "] 5 | edition = "2018" 6 | 7 | [lib] 8 | crate-type = ["cdylib", "rlib"] 9 | 10 | [profile.release] 11 | opt-level = 3 12 | debug = false 13 | rpath = false 14 | lto = true 15 | debug-assertions = false 16 | codegen-units = 1 17 | panic = 'abort' 18 | incremental = false 19 | overflow-checks = true 20 | 21 | [features] 22 | backtraces = ["cosmwasm-std/backtraces"] 23 | # use library feature to disable all init/handle/query exports 24 | library = [] 25 | 26 | [dependencies] 27 | cw2 = { version = "0.8.0" } 28 | cw20 = { version = "0.8.0" } 29 | cosmwasm-std = { version = "0.16.0", features = ["staking"] } 30 | cosmwasm-storage = { version = "0.16.0", features = ["iterator"] } 31 | cw-storage-plus = { version = "0.8.0", features = ["iterator"]} 32 | schemars = "0.8.1" 33 | cw20-legacy = { version = "0.2.0", features = ["library"]} 34 | serde = { version = "1.0.103", default-features = false, features = ["derive"] } 35 | snafu = { version = "0.6.3" } 36 | basset = { path = "../../packages/basset", default-features = false, version = "0.1.0"} 37 | 38 | [dev-dependencies] 39 | # we only need to enable this if we use integration tests 40 | #cosmwasm-vm = { version = "0.15.0", default-features = false } 41 | cosmwasm-schema = { version = "0.16.0", default-features = false } 42 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/examples/schema.rs: -------------------------------------------------------------------------------- 1 | use std::env::current_dir; 2 | use std::fs::create_dir_all; 3 | 4 | use cosmwasm_schema::{export_schema, remove_schemas, schema_for}; 5 | 6 | use anchor_basset_hub::state::Parameters; 7 | use basset::hub::{ 8 | AllHistoryResponse, CurrentBatchResponse, InstantiateMsg, QueryMsg, StateResponse, 9 | UnbondRequestsResponse, WhitelistedValidatorsResponse, WithdrawableUnbondedResponse, 10 | }; 11 | use basset::hub::{Config, ExecuteMsg, State}; 12 | 13 | fn main() { 14 | let mut out_dir = current_dir().unwrap(); 15 | out_dir.push("schema"); 16 | create_dir_all(&out_dir).unwrap(); 17 | remove_schemas(&out_dir).unwrap(); 18 | 19 | export_schema(&schema_for!(InstantiateMsg), &out_dir); 20 | export_schema(&schema_for!(ExecuteMsg), &out_dir); 21 | export_schema(&schema_for!(QueryMsg), &out_dir); 22 | export_schema(&schema_for!(State), &out_dir); 23 | export_schema(&schema_for!(Config), &out_dir); 24 | export_schema(&schema_for!(Parameters), &out_dir); 25 | export_schema(&schema_for!(StateResponse), &out_dir); 26 | export_schema(&schema_for!(WhitelistedValidatorsResponse), &out_dir); 27 | export_schema(&schema_for!(WithdrawableUnbondedResponse), &out_dir); 28 | export_schema(&schema_for!(UnbondRequestsResponse), &out_dir); 29 | export_schema(&schema_for!(CurrentBatchResponse), &out_dir); 30 | export_schema(&schema_for!(AllHistoryResponse), &out_dir); 31 | } 32 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/schema/state_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "StateResponse", 4 | "type": "object", 5 | "required": [ 6 | "global_index", 7 | "prev_reward_balance", 8 | "total_balance" 9 | ], 10 | "properties": { 11 | "global_index": { 12 | "$ref": "#/definitions/Decimal" 13 | }, 14 | "prev_reward_balance": { 15 | "$ref": "#/definitions/Uint128" 16 | }, 17 | "total_balance": { 18 | "$ref": "#/definitions/Uint128" 19 | } 20 | }, 21 | "definitions": { 22 | "Decimal": { 23 | "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", 24 | "type": "string" 25 | }, 26 | "Uint128": { 27 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 28 | "type": "string" 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/schema/holder_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "HolderResponse", 4 | "type": "object", 5 | "required": [ 6 | "address", 7 | "balance", 8 | "index", 9 | "pending_rewards" 10 | ], 11 | "properties": { 12 | "address": { 13 | "type": "string" 14 | }, 15 | "balance": { 16 | "$ref": "#/definitions/Uint128" 17 | }, 18 | "index": { 19 | "$ref": "#/definitions/Decimal" 20 | }, 21 | "pending_rewards": { 22 | "$ref": "#/definitions/Decimal" 23 | } 24 | }, 25 | "definitions": { 26 | "Decimal": { 27 | "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", 28 | "type": "string" 29 | }, 30 | "Uint128": { 31 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 32 | "type": "string" 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anchor_basset_reward" 3 | version = "1.0.0" 4 | authors = ["MSNTCS "] 5 | edition = "2018" 6 | 7 | exclude = [ 8 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 9 | "contract.wasm", 10 | "hash.txt", 11 | ] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [lib] 16 | crate-type = ["cdylib", "rlib"] 17 | 18 | [profile.release] 19 | opt-level = 3 20 | debug = false 21 | rpath = false 22 | lto = true 23 | debug-assertions = false 24 | codegen-units = 1 25 | panic = 'abort' 26 | incremental = false 27 | overflow-checks = true 28 | 29 | [features] 30 | # for more explicit tests, cargo test --features=backtraces 31 | backtraces = ["cosmwasm-std/backtraces"] 32 | 33 | [dependencies] 34 | cw20 = { version = "0.8.0" } 35 | cosmwasm-std = { version = "0.16.0", features = ["iterator"] } 36 | cosmwasm-storage = { version = "0.16.0", features = ["iterator"] } 37 | cw-storage-plus = { version = "0.8.0", features = ["iterator"]} 38 | schemars = "0.8.1" 39 | serde = { version = "1.0.103", default-features = false, features = ["derive"] } 40 | thiserror = { version = "1.0.21" } 41 | terra-cosmwasm = { version = "2.2.0" } 42 | basset = { path = "../../packages/basset", default-features = false, version = "0.1.0"} 43 | cosmwasm-bignumber = "2.2.0" 44 | 45 | [dev-dependencies] 46 | cosmwasm-schema = { version = "0.16.0", default-features = false } 47 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Formatting Check & Test 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | - master 9 | - "release/*" 10 | 11 | jobs: 12 | clippy: 13 | name: Actions - clippy 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | with: 18 | fetch-depth: 1 19 | - uses: actions-rs/toolchain@v1 20 | with: 21 | toolchain: stable 22 | components: clippy 23 | profile: minimal 24 | override: true 25 | - run: cargo fetch --verbose 26 | - run: cargo clippy --all --all-targets -- -D warnings 27 | 28 | rustfmt: 29 | name: Actions - rustfmt 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v2 33 | with: 34 | fetch-depth: 1 35 | - uses: actions-rs/toolchain@v1 36 | with: 37 | toolchain: stable 38 | components: rustfmt 39 | profile: minimal 40 | override: true 41 | - run: cargo fmt -- --check 42 | 43 | unit-test: 44 | name: Actions - unit test 45 | runs-on: ${{ matrix.os }} 46 | strategy: 47 | matrix: 48 | os: [ubuntu-latest] 49 | steps: 50 | - uses: actions/checkout@v2 51 | with: 52 | fetch-depth: 1 53 | - uses: actions-rs/toolchain@v1 54 | with: 55 | toolchain: stable 56 | profile: minimal 57 | - run: cargo fetch --verbose 58 | - run: cargo build 59 | - run: cargo test --verbose --all 60 | env: 61 | RUST_BACKTRACE: 1 -------------------------------------------------------------------------------- /contracts/anchor_airdrop_registry/schema/m_i_r_airdrop_handle_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "MIRAirdropHandleMsg", 4 | "anyOf": [ 5 | { 6 | "type": "object", 7 | "required": [ 8 | "claim" 9 | ], 10 | "properties": { 11 | "claim": { 12 | "type": "object", 13 | "required": [ 14 | "amount", 15 | "proof", 16 | "stage" 17 | ], 18 | "properties": { 19 | "amount": { 20 | "$ref": "#/definitions/Uint128" 21 | }, 22 | "proof": { 23 | "type": "array", 24 | "items": { 25 | "type": "string" 26 | } 27 | }, 28 | "stage": { 29 | "type": "integer", 30 | "format": "uint8", 31 | "minimum": 0.0 32 | } 33 | } 34 | } 35 | }, 36 | "additionalProperties": false 37 | } 38 | ], 39 | "definitions": { 40 | "Uint128": { 41 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 42 | "type": "string" 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/src/state.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{CanonicalAddr, StdResult, Storage}; 2 | //use cosmwasm_storage::{singleton, singleton_read}; 3 | use cw_storage_plus::Item; 4 | 5 | pub const HUB_CONTRACT_KEY: Item = Item::new("\u{0}\u{c}hub_contract"); 6 | 7 | // meta is the token definition as well as the total_supply 8 | pub fn read_hub_contract(storage: &dyn Storage) -> StdResult { 9 | HUB_CONTRACT_KEY.load(storage) 10 | } 11 | 12 | pub fn store_hub_contract( 13 | storage: &mut dyn Storage, 14 | hub_contract: &CanonicalAddr, 15 | ) -> StdResult<()> { 16 | HUB_CONTRACT_KEY.save(storage, hub_contract) 17 | } 18 | 19 | #[cfg(test)] 20 | mod test { 21 | use super::*; 22 | 23 | use cosmwasm_std::testing::mock_dependencies; 24 | use cosmwasm_std::{Api, StdResult, Storage}; 25 | use cosmwasm_storage::{singleton, singleton_read}; 26 | 27 | pub static HUB_CONTRACT: &[u8] = b"hub_contract"; 28 | 29 | pub fn store_hub(storage: &mut dyn Storage, params: &CanonicalAddr) -> StdResult<()> { 30 | singleton(storage, HUB_CONTRACT).save(params) 31 | } 32 | pub fn read_hub(storage: &dyn Storage) -> StdResult { 33 | singleton_read(storage, HUB_CONTRACT).load() 34 | } 35 | 36 | #[test] 37 | fn hub_legacy_compatibility() { 38 | let mut deps = mock_dependencies(&[]); 39 | store_hub( 40 | &mut deps.storage, 41 | &deps.api.addr_canonicalize("hub").unwrap(), 42 | ) 43 | .unwrap(); 44 | 45 | assert_eq!( 46 | HUB_CONTRACT_KEY.load(&deps.storage).unwrap(), 47 | read_hub(&deps.storage).unwrap() 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/signed_integers/src/signed_integer.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::Uint128; 2 | use schemars::JsonSchema; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | /// boolean is set for specifying the negativity. 6 | /// false means the value is positive. 7 | #[derive( 8 | Serialize, Deserialize, Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, JsonSchema, 9 | )] 10 | pub struct SignedInt(#[schemars(with = "String")] pub Uint128, pub bool); 11 | 12 | impl SignedInt { 13 | pub fn from_subtraction, B: Into>( 14 | minuend: A, 15 | subtrahend: B, 16 | ) -> SignedInt { 17 | let minuend: Uint128 = minuend.into(); 18 | let subtrahend: Uint128 = subtrahend.into(); 19 | let subtraction = minuend.checked_sub(subtrahend); 20 | if subtraction.is_err() { 21 | return SignedInt((subtrahend.checked_sub(minuend)).unwrap(), true); 22 | } 23 | SignedInt(subtraction.unwrap(), false) 24 | } 25 | } 26 | 27 | #[cfg(test)] 28 | mod test { 29 | use super::*; 30 | use cosmwasm_std::Uint128; 31 | 32 | #[test] 33 | fn from_subtraction() { 34 | let min = Uint128::new(1000010); 35 | let sub = Uint128::new(1000000); 36 | let signed_integer = SignedInt::from_subtraction(min, sub); 37 | assert_eq!(signed_integer.0, Uint128::new(10)); 38 | assert!(!signed_integer.1); 39 | 40 | //check negative values 41 | let min = Uint128::new(1000000); 42 | let sub = Uint128::new(1100000); 43 | let signed_integer = SignedInt::from_subtraction(min, sub); 44 | assert_eq!(signed_integer.0, Uint128::new(100000)); 45 | assert!(signed_integer.1); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /contracts/anchor_airdrop_registry/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anchor_airdrop_registry" 3 | version = "1.0.0" 4 | authors = ["Terraform Labs, PTE."] 5 | edition = "2018" 6 | 7 | exclude = [ 8 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 9 | "contract.wasm", 10 | "hash.txt", 11 | ] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [lib] 16 | crate-type = ["cdylib", "rlib"] 17 | 18 | [profile.release] 19 | opt-level = 3 20 | debug = false 21 | rpath = false 22 | lto = true 23 | debug-assertions = false 24 | codegen-units = 1 25 | panic = 'abort' 26 | incremental = false 27 | overflow-checks = true 28 | 29 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 30 | [features] 31 | # for quicker tests, cargo test --lib 32 | # for more explicit tests, cargo test --features=backtraces 33 | backtraces = ["cosmwasm-std/backtraces"] 34 | 35 | [dependencies] 36 | cw2 = { version = "0.8.0" } 37 | cw20 = { version = "0.8.0" } 38 | cw20-base = { version = "0.8.0", features = ["library"] } 39 | cosmwasm-std = { version = "0.16.0", features = ["iterator"] } 40 | cosmwasm-storage = { version = "0.16.0", features = ["iterator"] } 41 | schemars = "0.8.1" 42 | basset = { path = "../../packages/basset", default-features = false, version = "0.1.0"} 43 | serde = { version = "1.0.103", default-features = false, features = ["derive"] } 44 | cw-storage-plus = { version = "0.8.0"} 45 | 46 | [dev-dependencies] 47 | cosmwasm-schema = "0.16.0" 48 | cosmwasm-vm = { version = "0.16.0", default-features = false, features = ["iterator"] } -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/schema/holders_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "HoldersResponse", 4 | "type": "object", 5 | "required": [ 6 | "holders" 7 | ], 8 | "properties": { 9 | "holders": { 10 | "type": "array", 11 | "items": { 12 | "$ref": "#/definitions/HolderResponse" 13 | } 14 | } 15 | }, 16 | "definitions": { 17 | "Decimal": { 18 | "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", 19 | "type": "string" 20 | }, 21 | "HolderResponse": { 22 | "type": "object", 23 | "required": [ 24 | "address", 25 | "balance", 26 | "index", 27 | "pending_rewards" 28 | ], 29 | "properties": { 30 | "address": { 31 | "type": "string" 32 | }, 33 | "balance": { 34 | "$ref": "#/definitions/Uint128" 35 | }, 36 | "index": { 37 | "$ref": "#/definitions/Decimal" 38 | }, 39 | "pending_rewards": { 40 | "$ref": "#/definitions/Decimal" 41 | } 42 | } 43 | }, 44 | "Uint128": { 45 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 46 | "type": "string" 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "anchor_basset_hub" 3 | version = "1.0.0" 4 | authors = ["MSNTCS "] 5 | edition = "2018" 6 | 7 | exclude = [ 8 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 9 | "contract.wasm", 10 | "hash.txt", 11 | ] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [lib] 16 | crate-type = ["cdylib", "rlib"] 17 | 18 | [profile.release] 19 | opt-level = 3 20 | debug = false 21 | rpath = false 22 | lto = true 23 | debug-assertions = false 24 | codegen-units = 1 25 | panic = 'abort' 26 | incremental = false 27 | overflow-checks = true 28 | 29 | [features] 30 | # for quicker tests, cargo test --lib 31 | # for more explicit tests, cargo test --features=backtraces 32 | backtraces = ["cosmwasm-std/backtraces"] 33 | 34 | [dependencies] 35 | cw20 = { version = "0.8.0" } 36 | cw20-legacy = { version = "0.2.0" } 37 | cosmwasm-std = { version = "0.16.0", features = ["iterator"] } 38 | cosmwasm-storage = { version = "0.16.0", features = ["iterator"] } 39 | cw-storage-plus = { version = "0.8.0", features = ["iterator"]} 40 | terra-cosmwasm = { version = "2.2.0" } 41 | schemars = "0.8.1" 42 | serde = { version = "1.0.103", default-features = false, features = ["derive"] } 43 | snafu = { version = "0.6.3" } 44 | rand = "0.5.0" 45 | anchor_basset_token = {path = "../anchor_basset_token"} 46 | anchor_airdrop_registry = {path = "../anchor_airdrop_registry", default-features = false, version = "1.0.0"} 47 | basset = { path = "../../packages/basset", default-features = false, version = "0.1.0"} 48 | signed_integer = { path = "../../packages/signed_integers", default-features = false, version = "0.1.0"} 49 | 50 | [dev-dependencies] 51 | cosmwasm-vm = { version = "0.16.0", default-features = false, features = ["iterator"] } 52 | cosmwasm-schema = "0.16.0" -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/schema/state.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "State", 4 | "type": "object", 5 | "required": [ 6 | "actual_unbonded_amount", 7 | "exchange_rate", 8 | "last_index_modification", 9 | "last_processed_batch", 10 | "last_unbonded_time", 11 | "prev_hub_balance", 12 | "total_bond_amount" 13 | ], 14 | "properties": { 15 | "actual_unbonded_amount": { 16 | "$ref": "#/definitions/Uint128" 17 | }, 18 | "exchange_rate": { 19 | "$ref": "#/definitions/Decimal" 20 | }, 21 | "last_index_modification": { 22 | "type": "integer", 23 | "format": "uint64", 24 | "minimum": 0.0 25 | }, 26 | "last_processed_batch": { 27 | "type": "integer", 28 | "format": "uint64", 29 | "minimum": 0.0 30 | }, 31 | "last_unbonded_time": { 32 | "type": "integer", 33 | "format": "uint64", 34 | "minimum": 0.0 35 | }, 36 | "prev_hub_balance": { 37 | "$ref": "#/definitions/Uint128" 38 | }, 39 | "total_bond_amount": { 40 | "$ref": "#/definitions/Uint128" 41 | } 42 | }, 43 | "definitions": { 44 | "Decimal": { 45 | "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", 46 | "type": "string" 47 | }, 48 | "Uint128": { 49 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 50 | "type": "string" 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/schema/state_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "StateResponse", 4 | "type": "object", 5 | "required": [ 6 | "actual_unbonded_amount", 7 | "exchange_rate", 8 | "last_index_modification", 9 | "last_processed_batch", 10 | "last_unbonded_time", 11 | "prev_hub_balance", 12 | "total_bond_amount" 13 | ], 14 | "properties": { 15 | "actual_unbonded_amount": { 16 | "$ref": "#/definitions/Uint128" 17 | }, 18 | "exchange_rate": { 19 | "$ref": "#/definitions/Decimal" 20 | }, 21 | "last_index_modification": { 22 | "type": "integer", 23 | "format": "uint64", 24 | "minimum": 0.0 25 | }, 26 | "last_processed_batch": { 27 | "type": "integer", 28 | "format": "uint64", 29 | "minimum": 0.0 30 | }, 31 | "last_unbonded_time": { 32 | "type": "integer", 33 | "format": "uint64", 34 | "minimum": 0.0 35 | }, 36 | "prev_hub_balance": { 37 | "$ref": "#/definitions/Uint128" 38 | }, 39 | "total_bond_amount": { 40 | "$ref": "#/definitions/Uint128" 41 | } 42 | }, 43 | "definitions": { 44 | "Decimal": { 45 | "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", 46 | "type": "string" 47 | }, 48 | "Uint128": { 49 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 50 | "type": "string" 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/schema/all_history_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "AllHistoryResponse", 4 | "type": "object", 5 | "required": [ 6 | "history" 7 | ], 8 | "properties": { 9 | "history": { 10 | "type": "array", 11 | "items": { 12 | "$ref": "#/definitions/UnbondHistory" 13 | } 14 | } 15 | }, 16 | "definitions": { 17 | "Decimal": { 18 | "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", 19 | "type": "string" 20 | }, 21 | "Uint128": { 22 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 23 | "type": "string" 24 | }, 25 | "UnbondHistory": { 26 | "type": "object", 27 | "required": [ 28 | "amount", 29 | "applied_exchange_rate", 30 | "batch_id", 31 | "released", 32 | "time", 33 | "withdraw_rate" 34 | ], 35 | "properties": { 36 | "amount": { 37 | "$ref": "#/definitions/Uint128" 38 | }, 39 | "applied_exchange_rate": { 40 | "$ref": "#/definitions/Decimal" 41 | }, 42 | "batch_id": { 43 | "type": "integer", 44 | "format": "uint64", 45 | "minimum": 0.0 46 | }, 47 | "released": { 48 | "type": "boolean" 49 | }, 50 | "time": { 51 | "type": "integer", 52 | "format": "uint64", 53 | "minimum": 0.0 54 | }, 55 | "withdraw_rate": { 56 | "$ref": "#/definitions/Decimal" 57 | } 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/schema/query_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "QueryMsg", 4 | "anyOf": [ 5 | { 6 | "type": "object", 7 | "required": [ 8 | "config" 9 | ], 10 | "properties": { 11 | "config": { 12 | "type": "object" 13 | } 14 | }, 15 | "additionalProperties": false 16 | }, 17 | { 18 | "type": "object", 19 | "required": [ 20 | "state" 21 | ], 22 | "properties": { 23 | "state": { 24 | "type": "object" 25 | } 26 | }, 27 | "additionalProperties": false 28 | }, 29 | { 30 | "type": "object", 31 | "required": [ 32 | "accrued_rewards" 33 | ], 34 | "properties": { 35 | "accrued_rewards": { 36 | "type": "object", 37 | "required": [ 38 | "address" 39 | ], 40 | "properties": { 41 | "address": { 42 | "type": "string" 43 | } 44 | } 45 | } 46 | }, 47 | "additionalProperties": false 48 | }, 49 | { 50 | "type": "object", 51 | "required": [ 52 | "holder" 53 | ], 54 | "properties": { 55 | "holder": { 56 | "type": "object", 57 | "required": [ 58 | "address" 59 | ], 60 | "properties": { 61 | "address": { 62 | "type": "string" 63 | } 64 | } 65 | } 66 | }, 67 | "additionalProperties": false 68 | }, 69 | { 70 | "type": "object", 71 | "required": [ 72 | "holders" 73 | ], 74 | "properties": { 75 | "holders": { 76 | "type": "object", 77 | "properties": { 78 | "limit": { 79 | "type": [ 80 | "integer", 81 | "null" 82 | ], 83 | "format": "uint32", 84 | "minimum": 0.0 85 | }, 86 | "start_after": { 87 | "type": [ 88 | "string", 89 | "null" 90 | ] 91 | } 92 | } 93 | } 94 | }, 95 | "additionalProperties": false 96 | } 97 | ] 98 | } 99 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/schema/token_init_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "TokenInitMsg", 4 | "type": "object", 5 | "required": [ 6 | "decimals", 7 | "hub_contract", 8 | "initial_balances", 9 | "name", 10 | "symbol" 11 | ], 12 | "properties": { 13 | "decimals": { 14 | "type": "integer", 15 | "format": "uint8", 16 | "minimum": 0.0 17 | }, 18 | "hub_contract": { 19 | "type": "string" 20 | }, 21 | "initial_balances": { 22 | "type": "array", 23 | "items": { 24 | "$ref": "#/definitions/Cw20Coin" 25 | } 26 | }, 27 | "mint": { 28 | "anyOf": [ 29 | { 30 | "$ref": "#/definitions/MinterResponse" 31 | }, 32 | { 33 | "type": "null" 34 | } 35 | ] 36 | }, 37 | "name": { 38 | "type": "string" 39 | }, 40 | "symbol": { 41 | "type": "string" 42 | } 43 | }, 44 | "definitions": { 45 | "Cw20Coin": { 46 | "type": "object", 47 | "required": [ 48 | "address", 49 | "amount" 50 | ], 51 | "properties": { 52 | "address": { 53 | "type": "string" 54 | }, 55 | "amount": { 56 | "$ref": "#/definitions/Uint128" 57 | } 58 | } 59 | }, 60 | "MinterResponse": { 61 | "type": "object", 62 | "required": [ 63 | "minter" 64 | ], 65 | "properties": { 66 | "cap": { 67 | "description": "cap is how many more tokens can be issued by the minter", 68 | "anyOf": [ 69 | { 70 | "$ref": "#/definitions/Uint128" 71 | }, 72 | { 73 | "type": "null" 74 | } 75 | ] 76 | }, 77 | "minter": { 78 | "type": "string" 79 | } 80 | } 81 | }, 82 | "Uint128": { 83 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 84 | "type": "string" 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /packages/basset/src/reward.rs: -------------------------------------------------------------------------------- 1 | use schemars::JsonSchema; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use cosmwasm_std::{Decimal, Uint128}; 5 | 6 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 7 | pub struct InstantiateMsg { 8 | pub hub_contract: String, 9 | pub reward_denom: String, 10 | } 11 | 12 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 13 | #[serde(rename_all = "snake_case")] 14 | pub enum ExecuteMsg { 15 | //////////////////// 16 | /// Owner's operations 17 | /////////////////// 18 | 19 | /// Swap all of the balances to uusd. 20 | SwapToRewardDenom {}, 21 | 22 | /// Update the global index 23 | UpdateGlobalIndex {}, 24 | 25 | //////////////////// 26 | /// bAsset's operations 27 | /////////////////// 28 | 29 | /// Increase user staking balance 30 | /// Withdraw rewards to pending rewards 31 | /// Set current reward index to global index 32 | IncreaseBalance { address: String, amount: Uint128 }, 33 | /// Decrease user staking balance 34 | /// Withdraw rewards to pending rewards 35 | /// Set current reward index to global index 36 | DecreaseBalance { address: String, amount: Uint128 }, 37 | 38 | //////////////////// 39 | /// User's operations 40 | /////////////////// 41 | 42 | /// return the accrued reward in uusd to the user. 43 | ClaimRewards { recipient: Option }, 44 | } 45 | 46 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 47 | #[serde(rename_all = "snake_case")] 48 | pub enum QueryMsg { 49 | Config {}, 50 | State {}, 51 | AccruedRewards { 52 | address: String, 53 | }, 54 | Holder { 55 | address: String, 56 | }, 57 | Holders { 58 | start_after: Option, 59 | limit: Option, 60 | }, 61 | } 62 | 63 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 64 | pub struct ConfigResponse { 65 | pub hub_contract: String, 66 | pub reward_denom: String, 67 | } 68 | 69 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 70 | pub struct StateResponse { 71 | pub global_index: Decimal, 72 | pub total_balance: Uint128, 73 | pub prev_reward_balance: Uint128, 74 | } 75 | 76 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 77 | pub struct AccruedRewardsResponse { 78 | pub rewards: Uint128, 79 | } 80 | 81 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 82 | pub struct HolderResponse { 83 | pub address: String, 84 | pub balance: Uint128, 85 | pub index: Decimal, 86 | pub pending_rewards: Decimal, 87 | } 88 | 89 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 90 | pub struct HoldersResponse { 91 | pub holders: Vec, 92 | } 93 | 94 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 95 | pub struct MigrateMsg {} 96 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/src/math.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_bignumber::Decimal256; 2 | use cosmwasm_std::Decimal; 3 | 4 | /// return a * b 5 | pub fn decimal_multiplication_in_256(a: Decimal, b: Decimal) -> Decimal { 6 | let a_u256: Decimal256 = a.into(); 7 | let b_u256: Decimal256 = b.into(); 8 | let c_u256: Decimal = (b_u256 * a_u256).into(); 9 | c_u256 10 | } 11 | 12 | /// return a + b 13 | pub fn decimal_summation_in_256(a: Decimal, b: Decimal) -> Decimal { 14 | let a_u256: Decimal256 = a.into(); 15 | let b_u256: Decimal256 = b.into(); 16 | let c_u256: Decimal = (b_u256 + a_u256).into(); 17 | c_u256 18 | } 19 | 20 | /// return a - b 21 | pub fn decimal_subtraction_in_256(a: Decimal, b: Decimal) -> Decimal { 22 | let a_u256: Decimal256 = a.into(); 23 | let b_u256: Decimal256 = b.into(); 24 | let c_u256: Decimal = (a_u256 - b_u256).into(); 25 | c_u256 26 | } 27 | 28 | #[cfg(test)] 29 | mod tests { 30 | use super::*; 31 | use cosmwasm_std::{Decimal, Uint128}; 32 | 33 | #[test] 34 | fn test_decimal_multiplication() { 35 | let a = Uint128::new(100); 36 | let b = Decimal::from_ratio(Uint128::new(1111111), Uint128::new(10000000)); 37 | let multiplication = 38 | decimal_multiplication_in_256(Decimal::from_ratio(a, Uint128::new(1)), b); 39 | assert_eq!(multiplication.to_string(), "11.11111"); 40 | } 41 | 42 | #[test] 43 | fn test_decimal_sumation() { 44 | let a = Decimal::from_ratio(Uint128::new(20), Uint128::new(50)); 45 | let b = Decimal::from_ratio(Uint128::new(10), Uint128::new(50)); 46 | let res = decimal_summation_in_256(a, b); 47 | assert_eq!(res.to_string(), "0.6"); 48 | } 49 | 50 | #[test] 51 | fn test_decimal_subtraction() { 52 | let a = Decimal::from_ratio(Uint128::new(20), Uint128::new(50)); 53 | let b = Decimal::from_ratio(Uint128::new(10), Uint128::new(50)); 54 | let res = decimal_subtraction_in_256(a, b); 55 | assert_eq!(res.to_string(), "0.2"); 56 | } 57 | 58 | #[test] 59 | fn test_decimal_multiplication_in_256() { 60 | let a = Uint128::new(100); 61 | let b = Decimal::from_ratio(Uint128::new(1111111), Uint128::new(10000000)); 62 | let multiplication = 63 | decimal_multiplication_in_256(Decimal::from_ratio(a, Uint128::new(1)), b); 64 | assert_eq!(multiplication.to_string(), "11.11111"); 65 | } 66 | 67 | #[test] 68 | fn test_decimal_sumation_in_256() { 69 | let a = Decimal::from_ratio(Uint128::new(20), Uint128::new(50)); 70 | let b = Decimal::from_ratio(Uint128::new(10), Uint128::new(50)); 71 | let res = decimal_summation_in_256(a, b); 72 | assert_eq!(res.to_string(), "0.6"); 73 | } 74 | 75 | #[test] 76 | fn test_decimal_subtraction_in_256() { 77 | let a = Decimal::from_ratio(Uint128::new(20), Uint128::new(50)); 78 | let b = Decimal::from_ratio(Uint128::new(10), Uint128::new(50)); 79 | let res = decimal_subtraction_in_256(a, b); 80 | assert_eq!(res.to_string(), "0.2"); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /contracts/anchor_airdrop_registry/src/state.rs: -------------------------------------------------------------------------------- 1 | use schemars::JsonSchema; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | use basset::airdrop::{AirdropInfo, AirdropInfoElem}; 5 | use cosmwasm_std::{from_slice, to_vec, CanonicalAddr, Order, StdResult, Storage}; 6 | use cw_storage_plus::{Bound, Item, Map}; 7 | 8 | pub static KEY_CONFIG: &[u8] = b"config"; 9 | pub static PREFIX_AIRODROP_INFO: &[u8] = b"airdrop_info"; 10 | 11 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 12 | pub struct Config { 13 | pub owner: CanonicalAddr, 14 | pub hub_contract: String, 15 | pub reward_contract: String, 16 | pub airdrop_tokens: Vec, 17 | } 18 | 19 | pub const CONFIG: Item = Item::new("\u{0}\u{6}config"); 20 | pub const AIRDROP_INFO: Map<&[u8], AirdropInfo> = Map::new("airdrop_info"); 21 | 22 | pub fn store_config(storage: &mut dyn Storage, config: &Config) -> StdResult<()> { 23 | CONFIG.save(storage, config) 24 | } 25 | 26 | pub fn read_config(storage: &dyn Storage) -> StdResult { 27 | CONFIG.load(storage) 28 | } 29 | 30 | pub fn store_airdrop_info( 31 | storage: &mut dyn Storage, 32 | airdrop_token: String, 33 | airdrop_info: AirdropInfo, 34 | ) -> StdResult<()> { 35 | let key = to_vec(&airdrop_token)?; 36 | AIRDROP_INFO.save(storage, &key, &airdrop_info) 37 | } 38 | 39 | pub fn update_airdrop_info( 40 | storage: &mut dyn Storage, 41 | airdrop_token: String, 42 | airdrop_info: AirdropInfo, 43 | ) -> StdResult<()> { 44 | let key = to_vec(&airdrop_token)?; 45 | AIRDROP_INFO.update(storage, &key, |_| -> StdResult<_> { Ok(airdrop_info) })?; 46 | Ok(()) 47 | } 48 | 49 | pub fn remove_airdrop_info(storage: &mut dyn Storage, airdrop_token: String) -> StdResult<()> { 50 | let key = to_vec(&airdrop_token)?; 51 | AIRDROP_INFO.remove(storage, &key); 52 | Ok(()) 53 | } 54 | 55 | pub fn read_airdrop_info(storage: &dyn Storage, airdrop_token: String) -> StdResult { 56 | let key = to_vec(&airdrop_token).unwrap(); 57 | AIRDROP_INFO.load(storage, &key) 58 | } 59 | 60 | const MAX_LIMIT: u32 = 30; 61 | const DEFAULT_LIMIT: u32 = 10; 62 | pub fn read_all_airdrop_infos( 63 | storage: &dyn Storage, 64 | start_after: Option, 65 | limit: Option, 66 | ) -> StdResult> { 67 | let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; 68 | let start = calc_range_start(start_after).map(Bound::exclusive); 69 | 70 | let infos: Vec = AIRDROP_INFO 71 | .range(storage, start, None, Order::Ascending) 72 | .take(limit) 73 | .map(|item| { 74 | let (k, v) = item.unwrap(); 75 | let key: String = from_slice(&k).unwrap(); 76 | AirdropInfoElem { 77 | airdrop_token: key, 78 | info: v, 79 | } 80 | }) 81 | .collect(); 82 | 83 | Ok(infos) 84 | } 85 | 86 | fn calc_range_start(start_after: Option) -> Option> { 87 | start_after.map(|air| { 88 | let mut v = to_vec(&air).unwrap(); 89 | v.push(1); 90 | v 91 | }) 92 | } 93 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/src/contract.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "library"))] 2 | use cosmwasm_std::entry_point; 3 | 4 | use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; 5 | 6 | use cw20_legacy::allowances::{execute_decrease_allowance, execute_increase_allowance}; 7 | use cw20_legacy::contract::instantiate as cw20_init; 8 | use cw20_legacy::contract::query as cw20_query; 9 | use cw20_legacy::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; 10 | 11 | use crate::handler::*; 12 | use crate::msg::TokenInitMsg; 13 | use crate::state::store_hub_contract; 14 | use cw20::MinterResponse; 15 | use cw20_legacy::ContractError; 16 | 17 | #[cfg_attr(not(feature = "library"), entry_point)] 18 | pub fn instantiate( 19 | deps: DepsMut, 20 | env: Env, 21 | info: MessageInfo, 22 | msg: TokenInitMsg, 23 | ) -> StdResult { 24 | store_hub_contract( 25 | deps.storage, 26 | &deps.api.addr_canonicalize(&msg.hub_contract)?, 27 | )?; 28 | 29 | cw20_init( 30 | deps, 31 | env, 32 | info, 33 | InstantiateMsg { 34 | name: msg.name, 35 | symbol: msg.symbol, 36 | decimals: msg.decimals, 37 | initial_balances: msg.initial_balances, 38 | mint: Some(MinterResponse { 39 | minter: msg.hub_contract, 40 | cap: None, 41 | }), 42 | }, 43 | )?; 44 | 45 | Ok(Response::default()) 46 | } 47 | 48 | #[cfg_attr(not(feature = "library"), entry_point)] 49 | pub fn execute( 50 | deps: DepsMut, 51 | env: Env, 52 | info: MessageInfo, 53 | msg: ExecuteMsg, 54 | ) -> Result { 55 | match msg { 56 | ExecuteMsg::Transfer { recipient, amount } => { 57 | execute_transfer(deps, env, info, recipient, amount) 58 | } 59 | ExecuteMsg::Burn { amount } => execute_burn(deps, env, info, amount), 60 | ExecuteMsg::Send { 61 | contract, 62 | amount, 63 | msg, 64 | } => execute_send(deps, env, info, contract, amount, msg), 65 | ExecuteMsg::Mint { recipient, amount } => execute_mint(deps, env, info, recipient, amount), 66 | ExecuteMsg::IncreaseAllowance { 67 | spender, 68 | amount, 69 | expires, 70 | } => execute_increase_allowance(deps, env, info, spender, amount, expires), 71 | ExecuteMsg::DecreaseAllowance { 72 | spender, 73 | amount, 74 | expires, 75 | } => execute_decrease_allowance(deps, env, info, spender, amount, expires), 76 | ExecuteMsg::TransferFrom { 77 | owner, 78 | recipient, 79 | amount, 80 | } => execute_transfer_from(deps, env, info, owner, recipient, amount), 81 | ExecuteMsg::BurnFrom { owner, amount } => execute_burn_from(deps, env, info, owner, amount), 82 | ExecuteMsg::SendFrom { 83 | owner, 84 | contract, 85 | amount, 86 | msg, 87 | } => execute_send_from(deps, env, info, owner, contract, amount, msg), 88 | } 89 | } 90 | 91 | #[cfg_attr(not(feature = "library"), entry_point)] 92 | pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { 93 | cw20_query(deps, _env, msg) 94 | } 95 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/schema/query_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "QueryMsg", 4 | "anyOf": [ 5 | { 6 | "type": "object", 7 | "required": [ 8 | "config" 9 | ], 10 | "properties": { 11 | "config": { 12 | "type": "object" 13 | } 14 | }, 15 | "additionalProperties": false 16 | }, 17 | { 18 | "type": "object", 19 | "required": [ 20 | "state" 21 | ], 22 | "properties": { 23 | "state": { 24 | "type": "object" 25 | } 26 | }, 27 | "additionalProperties": false 28 | }, 29 | { 30 | "type": "object", 31 | "required": [ 32 | "whitelisted_validators" 33 | ], 34 | "properties": { 35 | "whitelisted_validators": { 36 | "type": "object" 37 | } 38 | }, 39 | "additionalProperties": false 40 | }, 41 | { 42 | "type": "object", 43 | "required": [ 44 | "current_batch" 45 | ], 46 | "properties": { 47 | "current_batch": { 48 | "type": "object" 49 | } 50 | }, 51 | "additionalProperties": false 52 | }, 53 | { 54 | "type": "object", 55 | "required": [ 56 | "withdrawable_unbonded" 57 | ], 58 | "properties": { 59 | "withdrawable_unbonded": { 60 | "type": "object", 61 | "required": [ 62 | "address" 63 | ], 64 | "properties": { 65 | "address": { 66 | "type": "string" 67 | } 68 | } 69 | } 70 | }, 71 | "additionalProperties": false 72 | }, 73 | { 74 | "type": "object", 75 | "required": [ 76 | "parameters" 77 | ], 78 | "properties": { 79 | "parameters": { 80 | "type": "object" 81 | } 82 | }, 83 | "additionalProperties": false 84 | }, 85 | { 86 | "type": "object", 87 | "required": [ 88 | "unbond_requests" 89 | ], 90 | "properties": { 91 | "unbond_requests": { 92 | "type": "object", 93 | "required": [ 94 | "address" 95 | ], 96 | "properties": { 97 | "address": { 98 | "type": "string" 99 | } 100 | } 101 | } 102 | }, 103 | "additionalProperties": false 104 | }, 105 | { 106 | "type": "object", 107 | "required": [ 108 | "all_history" 109 | ], 110 | "properties": { 111 | "all_history": { 112 | "type": "object", 113 | "properties": { 114 | "limit": { 115 | "type": [ 116 | "integer", 117 | "null" 118 | ], 119 | "format": "uint32", 120 | "minimum": 0.0 121 | }, 122 | "start_from": { 123 | "type": [ 124 | "integer", 125 | "null" 126 | ], 127 | "format": "uint64", 128 | "minimum": 0.0 129 | } 130 | } 131 | } 132 | }, 133 | "additionalProperties": false 134 | } 135 | ] 136 | } 137 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/schema/allowance_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "AllowanceResponse", 4 | "type": "object", 5 | "required": [ 6 | "allowance", 7 | "expires" 8 | ], 9 | "properties": { 10 | "allowance": { 11 | "$ref": "#/definitions/Uint128" 12 | }, 13 | "expires": { 14 | "$ref": "#/definitions/Expiration" 15 | } 16 | }, 17 | "definitions": { 18 | "Expiration": { 19 | "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", 20 | "anyOf": [ 21 | { 22 | "description": "AtHeight will expire when `env.block.height` >= height", 23 | "type": "object", 24 | "required": [ 25 | "at_height" 26 | ], 27 | "properties": { 28 | "at_height": { 29 | "type": "integer", 30 | "format": "uint64", 31 | "minimum": 0.0 32 | } 33 | }, 34 | "additionalProperties": false 35 | }, 36 | { 37 | "description": "AtTime will expire when `env.block.time` >= time", 38 | "type": "object", 39 | "required": [ 40 | "at_time" 41 | ], 42 | "properties": { 43 | "at_time": { 44 | "$ref": "#/definitions/Timestamp" 45 | } 46 | }, 47 | "additionalProperties": false 48 | }, 49 | { 50 | "description": "Never will never expire. Used to express the empty variant", 51 | "type": "object", 52 | "required": [ 53 | "never" 54 | ], 55 | "properties": { 56 | "never": { 57 | "type": "object" 58 | } 59 | }, 60 | "additionalProperties": false 61 | } 62 | ] 63 | }, 64 | "Timestamp": { 65 | "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.", 66 | "allOf": [ 67 | { 68 | "$ref": "#/definitions/Uint64" 69 | } 70 | ] 71 | }, 72 | "Uint128": { 73 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 74 | "type": "string" 75 | }, 76 | "Uint64": { 77 | "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", 78 | "type": "string" 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /packages/basset/src/airdrop.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Decimal, Uint128}; 2 | use schemars::JsonSchema; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 6 | pub struct InstantiateMsg { 7 | pub hub_contract: String, 8 | pub reward_contract: String, 9 | } 10 | 11 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 12 | #[serde(rename_all = "snake_case")] 13 | pub enum ExecuteMsg { 14 | FabricateMIRClaim { 15 | stage: u8, 16 | amount: Uint128, 17 | proof: Vec, 18 | }, 19 | FabricateANCClaim { 20 | stage: u8, 21 | amount: Uint128, 22 | proof: Vec, 23 | }, 24 | UpdateConfig { 25 | owner: Option, 26 | hub_contract: Option, 27 | reward_contract: Option, 28 | }, 29 | AddAirdropInfo { 30 | airdrop_token: String, 31 | airdrop_info: AirdropInfo, 32 | }, 33 | RemoveAirdropInfo { 34 | airdrop_token: String, 35 | }, 36 | UpdateAirdropInfo { 37 | airdrop_token: String, 38 | airdrop_info: AirdropInfo, 39 | }, 40 | } 41 | 42 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 43 | #[serde(rename_all = "snake_case")] 44 | pub enum QueryMsg { 45 | Config {}, 46 | AirdropInfo { 47 | airdrop_token: Option, 48 | start_after: Option, 49 | limit: Option, 50 | }, 51 | } 52 | 53 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 54 | #[serde(rename_all = "snake_case")] 55 | pub enum MIRAirdropHandleMsg { 56 | Claim { 57 | stage: u8, 58 | amount: Uint128, 59 | proof: Vec, 60 | }, 61 | } 62 | 63 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 64 | #[serde(rename_all = "snake_case")] 65 | pub enum ANCAirdropHandleMsg { 66 | Claim { 67 | stage: u8, 68 | amount: Uint128, 69 | proof: Vec, 70 | }, 71 | } 72 | 73 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 74 | #[serde(rename_all = "snake_case")] 75 | pub enum PairHandleMsg { 76 | Swap { 77 | belief_price: Option, 78 | max_spread: Option, 79 | to: Option, 80 | }, 81 | } 82 | 83 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 84 | pub struct AirdropInfo { 85 | pub airdrop_token_contract: String, 86 | pub airdrop_contract: String, 87 | pub airdrop_swap_contract: String, 88 | pub swap_belief_price: Option, 89 | pub swap_max_spread: Option, 90 | } 91 | 92 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 93 | pub struct ConfigResponse { 94 | pub owner: String, 95 | pub hub_contract: String, 96 | pub reward_contract: String, 97 | pub airdrop_tokens: Vec, 98 | } 99 | 100 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 101 | pub struct AirdropInfoElem { 102 | pub airdrop_token: String, 103 | pub info: AirdropInfo, 104 | } 105 | 106 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 107 | pub struct AirdropInfoResponse { 108 | pub airdrop_info: Vec, 109 | } 110 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/schema/execute_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "ExecuteMsg", 4 | "anyOf": [ 5 | { 6 | "description": "Owner's operations Swap all of the balances to uusd.", 7 | "type": "object", 8 | "required": [ 9 | "swap_to_reward_denom" 10 | ], 11 | "properties": { 12 | "swap_to_reward_denom": { 13 | "type": "object" 14 | } 15 | }, 16 | "additionalProperties": false 17 | }, 18 | { 19 | "description": "Update the global index", 20 | "type": "object", 21 | "required": [ 22 | "update_global_index" 23 | ], 24 | "properties": { 25 | "update_global_index": { 26 | "type": "object" 27 | } 28 | }, 29 | "additionalProperties": false 30 | }, 31 | { 32 | "description": "bAsset's operations Increase user staking balance Withdraw rewards to pending rewards Set current reward index to global index", 33 | "type": "object", 34 | "required": [ 35 | "increase_balance" 36 | ], 37 | "properties": { 38 | "increase_balance": { 39 | "type": "object", 40 | "required": [ 41 | "address", 42 | "amount" 43 | ], 44 | "properties": { 45 | "address": { 46 | "type": "string" 47 | }, 48 | "amount": { 49 | "$ref": "#/definitions/Uint128" 50 | } 51 | } 52 | } 53 | }, 54 | "additionalProperties": false 55 | }, 56 | { 57 | "description": "Decrease user staking balance Withdraw rewards to pending rewards Set current reward index to global index", 58 | "type": "object", 59 | "required": [ 60 | "decrease_balance" 61 | ], 62 | "properties": { 63 | "decrease_balance": { 64 | "type": "object", 65 | "required": [ 66 | "address", 67 | "amount" 68 | ], 69 | "properties": { 70 | "address": { 71 | "type": "string" 72 | }, 73 | "amount": { 74 | "$ref": "#/definitions/Uint128" 75 | } 76 | } 77 | } 78 | }, 79 | "additionalProperties": false 80 | }, 81 | { 82 | "description": "User's operations return the accrued reward in uusd to the user.", 83 | "type": "object", 84 | "required": [ 85 | "claim_rewards" 86 | ], 87 | "properties": { 88 | "claim_rewards": { 89 | "type": "object", 90 | "properties": { 91 | "recipient": { 92 | "type": [ 93 | "string", 94 | "null" 95 | ] 96 | } 97 | } 98 | } 99 | }, 100 | "additionalProperties": false 101 | } 102 | ], 103 | "definitions": { 104 | "Uint128": { 105 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 106 | "type": "string" 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/src/testing/mock_querier.rs: -------------------------------------------------------------------------------- 1 | use basset::hub::Config; 2 | use cosmwasm_std::testing::{MockApi, MockQuerier, MockStorage, MOCK_CONTRACT_ADDR}; 3 | use cosmwasm_std::{ 4 | from_slice, to_binary, Api, Coin, ContractResult, Empty, OwnedDeps, Querier, QuerierResult, 5 | QueryRequest, SystemError, SystemResult, WasmQuery, 6 | }; 7 | use cosmwasm_storage::to_length_prefixed; 8 | 9 | pub const MOCK_HUB_CONTRACT_ADDR: &str = "hub"; 10 | pub const MOCK_REWARD_CONTRACT_ADDR: &str = "reward"; 11 | pub const MOCK_TOKEN_CONTRACT_ADDR: &str = "token"; 12 | 13 | pub fn mock_dependencies( 14 | contract_balance: &[Coin], 15 | ) -> OwnedDeps { 16 | let contract_addr = String::from(MOCK_CONTRACT_ADDR); 17 | let custom_querier: WasmMockQuerier = 18 | WasmMockQuerier::new(MockQuerier::new(&[(&contract_addr, contract_balance)])); 19 | 20 | OwnedDeps { 21 | storage: MockStorage::default(), 22 | api: MockApi::default(), 23 | querier: custom_querier, 24 | } 25 | } 26 | 27 | pub struct WasmMockQuerier { 28 | base: MockQuerier, 29 | } 30 | 31 | impl Querier for WasmMockQuerier { 32 | fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { 33 | // MockQuerier doesn't support Custom, so we ignore it completely here 34 | let request: QueryRequest = match from_slice(bin_request) { 35 | Ok(v) => v, 36 | Err(e) => { 37 | return SystemResult::Err(SystemError::InvalidRequest { 38 | error: format!("Parsing query request: {}", e), 39 | request: bin_request.into(), 40 | }) 41 | } 42 | }; 43 | self.handle_query(&request) 44 | } 45 | } 46 | 47 | impl WasmMockQuerier { 48 | pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { 49 | match &request { 50 | QueryRequest::Wasm(WasmQuery::Raw { contract_addr, key }) => { 51 | if *contract_addr == MOCK_HUB_CONTRACT_ADDR { 52 | let prefix_config = to_length_prefixed(b"config").to_vec(); 53 | let api: MockApi = MockApi::default(); 54 | if key.as_slice().to_vec() == prefix_config { 55 | let config = Config { 56 | creator: api.addr_canonicalize(&String::from("owner1")).unwrap(), 57 | reward_contract: Some( 58 | api.addr_canonicalize(&String::from(MOCK_REWARD_CONTRACT_ADDR)) 59 | .unwrap(), 60 | ), 61 | token_contract: Some( 62 | api.addr_canonicalize(&String::from(MOCK_TOKEN_CONTRACT_ADDR)) 63 | .unwrap(), 64 | ), 65 | airdrop_registry_contract: Some( 66 | api.addr_canonicalize(&String::from("airdrop")).unwrap(), 67 | ), 68 | }; 69 | SystemResult::Ok(ContractResult::from(to_binary(&config))) 70 | } else { 71 | unimplemented!() 72 | } 73 | } else { 74 | unimplemented!() 75 | } 76 | } 77 | _ => self.base.handle_query(request), 78 | } 79 | } 80 | } 81 | 82 | impl WasmMockQuerier { 83 | pub fn new(base: MockQuerier) -> Self { 84 | WasmMockQuerier { base } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/src/contract.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "library"))] 2 | use cosmwasm_std::entry_point; 3 | 4 | use crate::global::{execute_swap, execute_update_global_index}; 5 | use crate::state::{read_config, read_state, store_config, store_state, Config, State}; 6 | use crate::user::{ 7 | execute_claim_rewards, execute_decrease_balance, execute_increase_balance, 8 | query_accrued_rewards, query_holder, query_holders, 9 | }; 10 | use cosmwasm_std::{ 11 | to_binary, Binary, Decimal, Deps, DepsMut, Env, MessageInfo, Response, StdResult, Uint128, 12 | }; 13 | 14 | use basset::reward::{ 15 | ConfigResponse, ExecuteMsg, InstantiateMsg, MigrateMsg, QueryMsg, StateResponse, 16 | }; 17 | use terra_cosmwasm::TerraMsgWrapper; 18 | 19 | #[cfg_attr(not(feature = "library"), entry_point)] 20 | pub fn instantiate( 21 | deps: DepsMut, 22 | _env: Env, 23 | _info: MessageInfo, 24 | msg: InstantiateMsg, 25 | ) -> StdResult { 26 | let conf = Config { 27 | hub_contract: deps.api.addr_canonicalize(&msg.hub_contract)?, 28 | reward_denom: msg.reward_denom, 29 | }; 30 | 31 | store_config(deps.storage, &conf)?; 32 | store_state( 33 | deps.storage, 34 | &State { 35 | global_index: Decimal::zero(), 36 | total_balance: Uint128::zero(), 37 | prev_reward_balance: Uint128::zero(), 38 | }, 39 | )?; 40 | 41 | Ok(Response::default()) 42 | } 43 | 44 | #[cfg_attr(not(feature = "library"), entry_point)] 45 | pub fn execute( 46 | deps: DepsMut, 47 | env: Env, 48 | info: MessageInfo, 49 | msg: ExecuteMsg, 50 | ) -> StdResult> { 51 | match msg { 52 | ExecuteMsg::ClaimRewards { recipient } => execute_claim_rewards(deps, env, info, recipient), 53 | ExecuteMsg::SwapToRewardDenom {} => execute_swap(deps, env, info), 54 | ExecuteMsg::UpdateGlobalIndex {} => execute_update_global_index(deps, env, info), 55 | ExecuteMsg::IncreaseBalance { address, amount } => { 56 | execute_increase_balance(deps, env, info, address, amount) 57 | } 58 | ExecuteMsg::DecreaseBalance { address, amount } => { 59 | execute_decrease_balance(deps, env, info, address, amount) 60 | } 61 | } 62 | } 63 | 64 | #[cfg_attr(not(feature = "library"), entry_point)] 65 | pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { 66 | match msg { 67 | QueryMsg::Config {} => to_binary(&query_config(deps)?), 68 | QueryMsg::State {} => to_binary(&query_state(deps)?), 69 | QueryMsg::AccruedRewards { address } => to_binary(&query_accrued_rewards(deps, address)?), 70 | QueryMsg::Holder { address } => to_binary(&query_holder(deps, address)?), 71 | QueryMsg::Holders { start_after, limit } => { 72 | to_binary(&query_holders(deps, start_after, limit)?) 73 | } 74 | } 75 | } 76 | 77 | fn query_config(deps: Deps) -> StdResult { 78 | let config: Config = read_config(deps.storage)?; 79 | Ok(ConfigResponse { 80 | hub_contract: deps.api.addr_humanize(&config.hub_contract)?.to_string(), 81 | reward_denom: config.reward_denom, 82 | }) 83 | } 84 | 85 | fn query_state(deps: Deps) -> StdResult { 86 | let state: State = read_state(deps.storage)?; 87 | Ok(StateResponse { 88 | global_index: state.global_index, 89 | total_balance: state.total_balance, 90 | prev_reward_balance: state.prev_reward_balance, 91 | }) 92 | } 93 | 94 | #[cfg_attr(not(feature = "library"), entry_point)] 95 | pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { 96 | Ok(Response::default()) 97 | } 98 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/schema/all_allowances_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "AllAllowancesResponse", 4 | "type": "object", 5 | "required": [ 6 | "allowances" 7 | ], 8 | "properties": { 9 | "allowances": { 10 | "type": "array", 11 | "items": { 12 | "$ref": "#/definitions/AllowanceInfo" 13 | } 14 | } 15 | }, 16 | "definitions": { 17 | "AllowanceInfo": { 18 | "type": "object", 19 | "required": [ 20 | "allowance", 21 | "expires", 22 | "spender" 23 | ], 24 | "properties": { 25 | "allowance": { 26 | "$ref": "#/definitions/Uint128" 27 | }, 28 | "expires": { 29 | "$ref": "#/definitions/Expiration" 30 | }, 31 | "spender": { 32 | "type": "string" 33 | } 34 | } 35 | }, 36 | "Expiration": { 37 | "description": "Expiration represents a point in time when some event happens. It can compare with a BlockInfo and will return is_expired() == true once the condition is hit (and for every block in the future)", 38 | "anyOf": [ 39 | { 40 | "description": "AtHeight will expire when `env.block.height` >= height", 41 | "type": "object", 42 | "required": [ 43 | "at_height" 44 | ], 45 | "properties": { 46 | "at_height": { 47 | "type": "integer", 48 | "format": "uint64", 49 | "minimum": 0.0 50 | } 51 | }, 52 | "additionalProperties": false 53 | }, 54 | { 55 | "description": "AtTime will expire when `env.block.time` >= time", 56 | "type": "object", 57 | "required": [ 58 | "at_time" 59 | ], 60 | "properties": { 61 | "at_time": { 62 | "$ref": "#/definitions/Timestamp" 63 | } 64 | }, 65 | "additionalProperties": false 66 | }, 67 | { 68 | "description": "Never will never expire. Used to express the empty variant", 69 | "type": "object", 70 | "required": [ 71 | "never" 72 | ], 73 | "properties": { 74 | "never": { 75 | "type": "object" 76 | } 77 | }, 78 | "additionalProperties": false 79 | } 80 | ] 81 | }, 82 | "Timestamp": { 83 | "description": "A point in time in nanosecond precision.\n\nThis type can represent times from 1970-01-01T00:00:00Z to 2554-07-21T23:34:33Z.", 84 | "allOf": [ 85 | { 86 | "$ref": "#/definitions/Uint64" 87 | } 88 | ] 89 | }, 90 | "Uint128": { 91 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 92 | "type": "string" 93 | }, 94 | "Uint64": { 95 | "description": "A thin wrapper around u64 that is using strings for JSON encoding/decoding, such that the full u64 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u64` to get the value out:\n\n``` # use cosmwasm_std::Uint64; let a = Uint64::from(42u64); assert_eq!(a.u64(), 42);\n\nlet b = Uint64::from(70u32); assert_eq!(b.u64(), 70); ```", 96 | "type": "string" 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![codecov](https://codecov.io/gh/Anchor-Protocol/anchor-bAsset-contracts/branch/master/graph/badge.svg?token=GSAL9XEWNH)](https://codecov.io/gh/Anchor-Protocol/anchor-bAsset-contracts) 2 | 3 | # Anchor bAsset Contracts 4 | 5 | This monorepository contains the source code for the smart contracts implementing bAsset Protocol on the [Terra](https://terra.money) blockchain. 6 | 7 | You can find information about the architecture, usage, and function of the smart contracts on the official Anchor documentation [site](https://anchorprotocol.com/). 8 | 9 | 10 | ## Contracts 11 | | Contract | Reference | Description | 12 | | --------------------------------------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | 13 | | [`anchor_basset_hub`](https://github.com/Anchor-Protocol/anchor-bAsset-contracts/tree/master/contracts/anchor_basset_hub)|[doc](https://docs.anchorprotocol.com/smart-contracts/bluna/hub-1)| Manages minted bLunas and bonded Lunas 14 | | [`anchor_basset_reward`](https://github.com/Anchor-Protocol/anchor-bAsset-contracts/tree/master/contracts/anchor_basset_reward)|[doc](https://docs.anchorprotocol.com/smart-contracts/bluna/reward)|Manages the distribution of delegation rewards 15 | | [`anchor_basset_token`](https://github.com/Anchor-Protocol/anchor-bAsset-contracts/tree/master/contracts/anchor_basset_token)| [doc](https://github.com/Anchor-Protocol/anchor-bAsset-contracts/tree/master/contracts/anchor_basset_token)|CW20 compliance 16 | | [`anchor_airdrop_registery`](https://github.com/Anchor-Protocol/anchor-bAsset-contracts/tree/master/contracts/anchor_airdrop_registry)| [doc](https://docs.anchorprotocol.com/smart-contracts/bluna/airdrop-registry)|Manages message fabricators for MIR and ANC airdrops 17 | ## Development 18 | 19 | ### Environment Setup 20 | 21 | - Rust v1.44.1+ 22 | - `wasm32-unknown-unknown` target 23 | - Docker 24 | 25 | 1. Install `rustup` via https://rustup.rs/ 26 | 27 | 2. Run the following: 28 | 29 | ```sh 30 | rustup default stable 31 | rustup target add wasm32-unknown-unknown 32 | ``` 33 | 34 | 3. Make sure [Docker](https://www.docker.com/) is installed 35 | 36 | ### Unit / Integration Tests 37 | 38 | Each contract contains Rust unit tests embedded within the contract source directories. You can run: 39 | 40 | ```sh 41 | cargo test unit-test 42 | cargo test integration-test 43 | ``` 44 | 45 | ### Compiling 46 | 47 | After making sure tests pass, you can compile each contract with the following: 48 | 49 | ```sh 50 | RUSTFLAGS='-C link-arg=-s' cargo wasm 51 | cp ../../target/wasm32-unknown-unknown/release/cw1_subkeys.wasm . 52 | ls -l cw1_subkeys.wasm 53 | sha256sum cw1_subkeys.wasm 54 | ``` 55 | 56 | #### Production 57 | 58 | For production builds, run the following: 59 | 60 | ```sh 61 | docker run --rm -v "$(pwd)":/code \ 62 | --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ 63 | --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ 64 | cosmwasm/workspace-optimizer:0.11.5 65 | ``` 66 | 67 | This performs several optimizations which can significantly reduce the final size of the contract binaries, which will be available inside the `artifacts/` directory. 68 | 69 | ## License 70 | 71 | Copyright 2021 Anchor Protocol 72 | 73 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 74 | 75 | See the License for the specific language governing permissions and limitations under the License. 76 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/src/global.rs: -------------------------------------------------------------------------------- 1 | use crate::state::{read_config, read_state, store_state, Config, State}; 2 | 3 | use crate::math::decimal_summation_in_256; 4 | 5 | use cosmwasm_std::{ 6 | attr, CosmosMsg, Decimal, DepsMut, Env, MessageInfo, Response, StdError, StdResult, 7 | }; 8 | use terra_cosmwasm::{create_swap_msg, ExchangeRatesResponse, TerraMsgWrapper, TerraQuerier}; 9 | /// Swap all native tokens to reward_denom 10 | /// Only hub_contract is allowed to execute 11 | #[allow(clippy::if_same_then_else)] 12 | #[allow(clippy::needless_collect)] 13 | pub fn execute_swap( 14 | deps: DepsMut, 15 | env: Env, 16 | info: MessageInfo, 17 | ) -> StdResult> { 18 | let config = read_config(deps.storage)?; 19 | let sender_raw = deps.api.addr_canonicalize(info.sender.as_str())?; 20 | 21 | if sender_raw != config.hub_contract { 22 | return Err(StdError::generic_err("unauthorized")); 23 | } 24 | 25 | let contr_addr = env.contract.address; 26 | let balance = deps.querier.query_all_balances(contr_addr)?; 27 | let mut messages: Vec> = Vec::new(); 28 | 29 | let reward_denom = config.reward_denom; 30 | 31 | let denoms: Vec = balance.iter().map(|item| item.denom.clone()).collect(); 32 | 33 | let exchange_rates = query_exchange_rates(&deps, reward_denom.clone(), denoms)?; 34 | let known_denoms: Vec = exchange_rates 35 | .exchange_rates 36 | .iter() 37 | .map(|item| item.quote_denom.clone()) 38 | .collect(); 39 | 40 | for coin in balance { 41 | if coin.denom == reward_denom.clone() || !known_denoms.contains(&coin.denom) { 42 | continue; 43 | } 44 | 45 | messages.push(create_swap_msg(coin, reward_denom.to_string())); 46 | } 47 | 48 | let res = Response::new() 49 | .add_messages(messages) 50 | .add_attributes(vec![attr("action", "swap")]); 51 | 52 | Ok(res) 53 | } 54 | 55 | /// Increase global_index according to claimed rewards amount 56 | /// Only hub_contract is allowed to execute 57 | pub fn execute_update_global_index( 58 | deps: DepsMut, 59 | env: Env, 60 | info: MessageInfo, 61 | ) -> StdResult> { 62 | let config: Config = read_config(deps.storage)?; 63 | let mut state: State = read_state(deps.storage)?; 64 | 65 | // Permission check 66 | if config.hub_contract != deps.api.addr_canonicalize(info.sender.as_str())? { 67 | return Err(StdError::generic_err("Unauthorized")); 68 | } 69 | 70 | // Zero staking balance check 71 | if state.total_balance.is_zero() { 72 | return Err(StdError::generic_err("No asset is bonded by Hub")); 73 | } 74 | 75 | let reward_denom = read_config(deps.storage)?.reward_denom; 76 | 77 | // Load the reward contract balance 78 | let balance = deps 79 | .querier 80 | .query_balance(env.contract.address, reward_denom.as_str())?; 81 | 82 | let previous_balance = state.prev_reward_balance; 83 | 84 | // claimed_rewards = current_balance - prev_balance; 85 | let claimed_rewards = balance.amount.checked_sub(previous_balance)?; 86 | 87 | state.prev_reward_balance = balance.amount; 88 | 89 | // global_index += claimed_rewards / total_balance; 90 | state.global_index = decimal_summation_in_256( 91 | state.global_index, 92 | Decimal::from_ratio(claimed_rewards, state.total_balance), 93 | ); 94 | store_state(deps.storage, &state)?; 95 | 96 | let attributes = vec![ 97 | attr("action", "update_global_index"), 98 | attr("claimed_rewards", claimed_rewards), 99 | ]; 100 | let res = Response::new().add_attributes(attributes); 101 | 102 | Ok(res) 103 | } 104 | 105 | pub fn query_exchange_rates( 106 | deps: &DepsMut, 107 | base_denom: String, 108 | quote_denoms: Vec, 109 | ) -> StdResult { 110 | let querier = TerraQuerier::new(&deps.querier); 111 | let res: ExchangeRatesResponse = querier.query_exchange_rates(base_denom, quote_denoms)?; 112 | Ok(res) 113 | } 114 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/schema/query_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "QueryMsg", 4 | "anyOf": [ 5 | { 6 | "description": "Returns the current balance of the given address, 0 if unset. Return type: BalanceResponse.", 7 | "type": "object", 8 | "required": [ 9 | "balance" 10 | ], 11 | "properties": { 12 | "balance": { 13 | "type": "object", 14 | "required": [ 15 | "address" 16 | ], 17 | "properties": { 18 | "address": { 19 | "type": "string" 20 | } 21 | } 22 | } 23 | }, 24 | "additionalProperties": false 25 | }, 26 | { 27 | "description": "Returns metadata on the contract - name, decimals, supply, etc. Return type: TokenInfoResponse.", 28 | "type": "object", 29 | "required": [ 30 | "token_info" 31 | ], 32 | "properties": { 33 | "token_info": { 34 | "type": "object" 35 | } 36 | }, 37 | "additionalProperties": false 38 | }, 39 | { 40 | "description": "Only with \"mintable\" extension. Returns who can mint and how much. Return type: MinterResponse.", 41 | "type": "object", 42 | "required": [ 43 | "minter" 44 | ], 45 | "properties": { 46 | "minter": { 47 | "type": "object" 48 | } 49 | }, 50 | "additionalProperties": false 51 | }, 52 | { 53 | "description": "Only with \"allowance\" extension. Returns how much spender can use from owner account, 0 if unset. Return type: AllowanceResponse.", 54 | "type": "object", 55 | "required": [ 56 | "allowance" 57 | ], 58 | "properties": { 59 | "allowance": { 60 | "type": "object", 61 | "required": [ 62 | "owner", 63 | "spender" 64 | ], 65 | "properties": { 66 | "owner": { 67 | "type": "string" 68 | }, 69 | "spender": { 70 | "type": "string" 71 | } 72 | } 73 | } 74 | }, 75 | "additionalProperties": false 76 | }, 77 | { 78 | "description": "Only with \"enumerable\" extension (and \"allowances\") Returns all allowances this owner has approved. Supports pagination. Return type: AllAllowancesResponse.", 79 | "type": "object", 80 | "required": [ 81 | "all_allowances" 82 | ], 83 | "properties": { 84 | "all_allowances": { 85 | "type": "object", 86 | "required": [ 87 | "owner" 88 | ], 89 | "properties": { 90 | "limit": { 91 | "type": [ 92 | "integer", 93 | "null" 94 | ], 95 | "format": "uint32", 96 | "minimum": 0.0 97 | }, 98 | "owner": { 99 | "type": "string" 100 | }, 101 | "start_after": { 102 | "type": [ 103 | "string", 104 | "null" 105 | ] 106 | } 107 | } 108 | } 109 | }, 110 | "additionalProperties": false 111 | }, 112 | { 113 | "description": "Only with \"enumerable\" extension Returns all accounts that have balances. Supports pagination. Return type: AllAccountsResponse.", 114 | "type": "object", 115 | "required": [ 116 | "all_accounts" 117 | ], 118 | "properties": { 119 | "all_accounts": { 120 | "type": "object", 121 | "properties": { 122 | "limit": { 123 | "type": [ 124 | "integer", 125 | "null" 126 | ], 127 | "format": "uint32", 128 | "minimum": 0.0 129 | }, 130 | "start_after": { 131 | "type": [ 132 | "string", 133 | "null" 134 | ] 135 | } 136 | } 137 | } 138 | }, 139 | "additionalProperties": false 140 | } 141 | ] 142 | } 143 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/src/bond.rs: -------------------------------------------------------------------------------- 1 | use crate::contract::{query_total_issued, slashing}; 2 | use crate::math::decimal_division; 3 | use crate::state::{is_valid_validator, CONFIG, CURRENT_BATCH, PARAMETERS, STATE}; 4 | use basset::hub::State; 5 | use cosmwasm_std::{ 6 | attr, to_binary, CosmosMsg, DepsMut, Env, MessageInfo, Response, StakingMsg, StdError, 7 | StdResult, Uint128, WasmMsg, 8 | }; 9 | use cw20::Cw20ExecuteMsg; 10 | 11 | pub fn execute_bond( 12 | mut deps: DepsMut, 13 | env: Env, 14 | info: MessageInfo, 15 | validator: String, 16 | ) -> StdResult { 17 | // validator must be whitelisted 18 | let is_valid = is_valid_validator(deps.storage, validator.clone())?; 19 | if !is_valid { 20 | return Err(StdError::generic_err( 21 | "The chosen validator is currently not supported", 22 | )); 23 | } 24 | 25 | let params = PARAMETERS.load(deps.storage)?; 26 | let coin_denom = params.underlying_coin_denom; 27 | let threshold = params.er_threshold; 28 | let recovery_fee = params.peg_recovery_fee; 29 | 30 | // current batch requested fee is need for accurate exchange rate computation. 31 | let current_batch = CURRENT_BATCH.load(deps.storage)?; 32 | let requested_with_fee = current_batch.requested_with_fee; 33 | 34 | // coin must have be sent along with transaction and it should be in underlying coin denom 35 | if info.funds.len() > 1usize { 36 | return Err(StdError::generic_err( 37 | "More than one coin is sent; only one asset is supported", 38 | )); 39 | } 40 | 41 | let payment = info 42 | .funds 43 | .iter() 44 | .find(|x| x.denom == coin_denom && x.amount > Uint128::zero()) 45 | .ok_or_else(|| { 46 | StdError::generic_err(format!("No {} assets are provided to bond", coin_denom)) 47 | })?; 48 | 49 | // check slashing 50 | slashing(&mut deps, env)?; 51 | 52 | let state = STATE.load(deps.storage)?; 53 | let sender = info.sender; 54 | 55 | // get the total supply 56 | let mut total_supply = query_total_issued(deps.as_ref()).unwrap_or_default(); 57 | 58 | // peg recovery fee should be considered 59 | let mint_amount = decimal_division(payment.amount, state.exchange_rate); 60 | let mut mint_amount_with_fee = mint_amount; 61 | if state.exchange_rate < threshold { 62 | let max_peg_fee = mint_amount * recovery_fee; 63 | let required_peg_fee = ((total_supply + mint_amount + current_batch.requested_with_fee) 64 | .checked_sub(state.total_bond_amount + payment.amount))?; 65 | let peg_fee = Uint128::min(max_peg_fee, required_peg_fee); 66 | mint_amount_with_fee = (mint_amount.checked_sub(peg_fee))?; 67 | } 68 | 69 | // total supply should be updated for exchange rate calculation. 70 | total_supply += mint_amount_with_fee; 71 | 72 | // exchange rate should be updated for future 73 | STATE.update(deps.storage, |mut prev_state| -> StdResult { 74 | prev_state.total_bond_amount += payment.amount; 75 | prev_state.update_exchange_rate(total_supply, requested_with_fee); 76 | Ok(prev_state) 77 | })?; 78 | 79 | let mut messages: Vec = vec![ 80 | // send the delegate message 81 | CosmosMsg::Staking(StakingMsg::Delegate { 82 | validator, 83 | amount: payment.clone(), 84 | }), 85 | ]; 86 | 87 | // issue the basset token for sender 88 | let mint_msg = Cw20ExecuteMsg::Mint { 89 | recipient: sender.to_string(), 90 | amount: mint_amount_with_fee, 91 | }; 92 | 93 | let config = CONFIG.load(deps.storage)?; 94 | let token_address = deps 95 | .api 96 | .addr_humanize( 97 | &config 98 | .token_contract 99 | .expect("the token contract must have been registered"), 100 | )? 101 | .to_string(); 102 | 103 | messages.push(CosmosMsg::Wasm(WasmMsg::Execute { 104 | contract_addr: token_address, 105 | msg: to_binary(&mint_msg)?, 106 | funds: vec![], 107 | })); 108 | 109 | Ok(Response::new().add_messages(messages).add_attributes(vec![ 110 | attr("action", "mint"), 111 | attr("from", sender), 112 | attr("bonded", payment.amount), 113 | attr("minted", mint_amount_with_fee), 114 | ])) 115 | } 116 | -------------------------------------------------------------------------------- /FIRST_DEMO.md: -------------------------------------------------------------------------------- 1 | # Anchor bAsset 2 | This contract is supposed to manage bAsset in Terra blockchain. The contract consist of four core features: 3 | 4 | * A user can _***mint***_ bLuna by sending Luna to the contract, which results in the delegation of Luna. 5 | * A user can _***burn***_ bLuna by sending a transaction to undelegate and redeem underlying Luna. 6 | * A user can _***claim rewards***_ for bLuna by sending a transaction claiming staking rewards of the underlying Luna delegation. 7 | * A user can *_**send**_* bLuna to an another account. 8 | 9 | ## Configs 10 | | Name | 11 | | ------------ | 12 | | name | 13 | | symbol | 14 | |decimals| 15 | 16 | ## State 17 | ### TokenInfo 18 | TokenInfo holds general information related to the contract. 19 | ```rust 20 | pub struct TokenInfo { 21 | /// name of the derivative token 22 | pub name: String, 23 | /// symbol / ticker of the derivative token 24 | pub symbol: String, 25 | /// decimal places of the derivative token (for UI) 26 | pub decimals: u8, 27 | /// total supply of the derivation token 28 | pub total_supply: Uint128, 29 | } 30 | ``` 31 | 32 | ### TokenState 33 | TokenState is supposed to keep information related to current state of the token. 34 | 35 | `EPOC`: is 6 hours period that the contract collects all burn messages. `EPOC` is hardcoded with regards to the block time. 36 | 37 | ```rust 38 | // Manages all burn requests per each `EPOC`. 39 | // `Undelegation` keeps all amounts in `undelegation.claim` variable. 40 | // The contract sends one `StakingMsg:: Undelegate` per each `EPOC`. 41 | // Besides, `Undelegation` has a map that keeps a record of each `InitBurn` request. 42 | // This will be used in the `FinishBurn` message. 43 | pub struct Undelegation { 44 | pub claim: Uint128, 45 | // maps address of the user and the amount of burn that they requests. 46 | pub undelegated_wait_list_map: HashMap, 47 | } 48 | ``` 49 | ```rust 50 | pub struct TokenState { 51 | // the contract gathers all burn requests in 6 hours and manage them all at once. 52 | // In order to do this, EpocId is designed. this variable keeps the current epoc of the contract. 53 | pub current_epoc: u64, 54 | // is used to help to calculate current epoc. 55 | pub current_block_time: u64, 56 | // maps address of validator address to amount that the contract has delegated to 57 | pub delegation_map: HashMap, 58 | // maps bLuna holdings and accrued rewards (index). 59 | pub holder_map: HashMap, 60 | //stores InitBurn requests per each Epoc. 61 | // Each epoc has an Identical EpocId. 62 | pub undelegated_wait_list: HashMap, 63 | } 64 | ``` 65 | 66 | ### PoolInfo 67 | PoolInfo manages reward distribution. It keeps the reward index as well as the total bonded amount to calculate the current reward index. Besides , PoolInfo Keeps the total supply of the contract to compute the exchange rate. 68 | 69 | ```rust 70 | pub struct PoolInfo { 71 | pub exchange_rate: Decimal, 72 | pub total_bond_amount: Uint128, 73 | pub total_issued: Uint128, 74 | pub claimed: Uint128, 75 | pub reward_index: Decimal, 76 | } 77 | ``` 78 | 79 | ## InitMsg 80 | ```rust 81 | pub struct InitMsg { 82 | pub name: String, 83 | pub symbol: String, 84 | pub decimals: u8, 85 | } 86 | ``` 87 | | Key | Type | 88 | | ------------------ | ---------- | 89 | | `name` | String | 90 | | `symbol` | String | 91 | | `deceimals` | u8 | 92 | 93 | ## HandleMsg 94 | ```rust 95 | pub enum HandleMsg { 96 | /// Mint is a message to work as follows: 97 | /// Receives `amount` Luna from sender. 98 | /// Delegate `amount` to a specific `validator`. 99 | /// Issue the same `amount` of bLuna to sender. 100 | Mint { 101 | validator: HumanAddr, 102 | amount: Uint128, 103 | }, 104 | /// ClaimRewards sends bluna rewards to sender. 105 | ClaimRewards {}, 106 | /// InitBurn is send an undelegate message after receiving all 107 | /// requests for an specific period of time. 108 | InitBurn { amount: Uint128 }, 109 | /// FinishBurn is suppose to ask for liquidated luna 110 | FinishBurn { amount: Uint128 }, 111 | /// Send is like a base message in CW20 to move bluna to another account 112 | Send { 113 | recipient: HumanAddr, 114 | amount: Uint128, 115 | }, 116 | } 117 | ``` 118 | ### Mint 119 | * Mint{*address* validator, *Uint128* amount} 120 | 121 | * Receives amount Luna from sender. 122 | * amount Luna is delegated to validator. 123 | * Updates `token_state.undelagation_map`. 124 | * Updates `token_state.holding_map`. 125 | * Mints amount/exchangeRate bLuna to sender. 126 | * If `pool_info.exchange_rate` < 1, a 0.1% fee is attached. 127 | 128 | 129 | ### ClaimRewards 130 | * ClaimRewards{} 131 | * Sends previously accrued bLuna rewards to sender. 132 | * Updates `token_state.holding_map`. 133 | * Updates previously recorded index values to the current rewardIndex. 134 | 135 | 136 | ### InitBurn 137 | * InitiateBurn{*Uint128* amount} 138 | * Invokes ClaimRewards{}. 139 | * Updates `token_state.holding_map`. 140 | * Burns amount bLuna from sender. 141 | * Updates `Token_state.undelegation_wait_list_Map`. 142 | * If EpochTime has passed since last Undelegate{} execution, invokes Undelegate{}. 143 | 144 | ### FinishBurn 145 | * FinishBurn{*Uint128* amount} 146 | * Checks whether the unbonding period is over. 147 | * Updates `token_state.undelegated_wait_list_map`. 148 | * Sends amount Luna to sender. 149 | 150 | 151 | ### Send 152 | Sending bLuna to a different account automatically credits previously accrued rewards to the sender. 153 | 154 | * Send{*address* recipient, *Uint128* amount} 155 | * Invokes ClaimRewards{}. 156 | * Updates `token_state.holding_map`. 157 | * Sends amount bLuna to recipient. 158 | 159 | ## QueryMsg 160 | This will be provided. -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/src/testing/mock_querier.rs: -------------------------------------------------------------------------------- 1 | use basset::hub::Config; 2 | use cosmwasm_std::testing::{MockApi, MockQuerier, MockStorage, MOCK_CONTRACT_ADDR}; 3 | use cosmwasm_std::{ 4 | from_slice, to_binary, Api, Coin, ContractResult, Decimal, OwnedDeps, Querier, QuerierResult, 5 | QueryRequest, SystemError, SystemResult, Uint128, WasmQuery, 6 | }; 7 | use cosmwasm_storage::to_length_prefixed; 8 | use std::str::FromStr; 9 | use terra_cosmwasm::{ 10 | ExchangeRateItem, ExchangeRatesResponse, TaxCapResponse, TaxRateResponse, TerraQuery, 11 | TerraQueryWrapper, TerraRoute, 12 | }; 13 | 14 | pub const MOCK_HUB_CONTRACT_ADDR: &str = "hub"; 15 | pub const MOCK_REWARD_CONTRACT_ADDR: &str = "reward"; 16 | pub const MOCK_TOKEN_CONTRACT_ADDR: &str = "token"; 17 | 18 | pub fn mock_dependencies( 19 | contract_balance: &[Coin], 20 | ) -> OwnedDeps { 21 | let contract_addr = String::from(MOCK_CONTRACT_ADDR); 22 | let custom_querier: WasmMockQuerier = 23 | WasmMockQuerier::new(MockQuerier::new(&[(&contract_addr, contract_balance)])); 24 | 25 | OwnedDeps { 26 | storage: MockStorage::default(), 27 | api: MockApi::default(), 28 | querier: custom_querier, 29 | } 30 | } 31 | 32 | pub struct WasmMockQuerier { 33 | base: MockQuerier, 34 | } 35 | 36 | impl Querier for WasmMockQuerier { 37 | fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { 38 | // MockQuerier doesn't support Custom, so we ignore it completely here 39 | let request: QueryRequest = match from_slice(bin_request) { 40 | Ok(v) => v, 41 | Err(e) => { 42 | return SystemResult::Err(SystemError::InvalidRequest { 43 | error: format!("Parsing query request: {}", e), 44 | request: bin_request.into(), 45 | }) 46 | } 47 | }; 48 | self.handle_query(&request) 49 | } 50 | } 51 | 52 | impl WasmMockQuerier { 53 | pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { 54 | match &request { 55 | QueryRequest::Custom(TerraQueryWrapper { route, query_data }) => { 56 | if &TerraRoute::Treasury == route { 57 | match query_data { 58 | TerraQuery::TaxRate {} => { 59 | let res = TaxRateResponse { 60 | rate: Decimal::percent(1), 61 | }; 62 | SystemResult::Ok(ContractResult::from(to_binary(&res))) 63 | } 64 | TerraQuery::TaxCap { denom: _ } => { 65 | let cap = Uint128::new(1000000u128); 66 | let res = TaxCapResponse { cap }; 67 | SystemResult::Ok(ContractResult::from(to_binary(&res))) 68 | } 69 | _ => panic!("DO NOT ENTER HERE"), 70 | } 71 | } else if &TerraRoute::Oracle == route { 72 | match query_data { 73 | TerraQuery::ExchangeRates { 74 | base_denom, 75 | quote_denoms, 76 | } => { 77 | let exchange_rates = quote_denoms 78 | .iter() 79 | .filter(|d| d != &"mnt") 80 | .map(|e| ExchangeRateItem { 81 | quote_denom: e.clone(), 82 | exchange_rate: Decimal::from_str("22.1").unwrap(), 83 | }) 84 | .collect(); 85 | 86 | SystemResult::Ok(ContractResult::from(to_binary( 87 | &ExchangeRatesResponse { 88 | base_denom: base_denom.to_string(), 89 | exchange_rates, 90 | }, 91 | ))) 92 | } 93 | _ => panic!("DO NOT ENTER HERE"), 94 | } 95 | } else { 96 | panic!("DO NOT ENTER HERE") 97 | } 98 | } 99 | QueryRequest::Wasm(WasmQuery::Raw { contract_addr, key }) => { 100 | if *contract_addr == MOCK_HUB_CONTRACT_ADDR { 101 | let prefix_config = to_length_prefixed(b"config").to_vec(); 102 | let api: MockApi = MockApi::default(); 103 | if key.as_slice().to_vec() == prefix_config { 104 | let config = Config { 105 | creator: api.addr_canonicalize(&String::from("owner1")).unwrap(), 106 | reward_contract: Some( 107 | api.addr_canonicalize(&String::from(MOCK_REWARD_CONTRACT_ADDR)) 108 | .unwrap(), 109 | ), 110 | token_contract: Some( 111 | api.addr_canonicalize(&String::from(MOCK_TOKEN_CONTRACT_ADDR)) 112 | .unwrap(), 113 | ), 114 | airdrop_registry_contract: Some( 115 | api.addr_canonicalize(&String::from("airdrop")).unwrap(), 116 | ), 117 | }; 118 | SystemResult::Ok(ContractResult::from(to_binary(&config))) 119 | } else { 120 | unimplemented!() 121 | } 122 | } else { 123 | unimplemented!() 124 | } 125 | } 126 | _ => self.base.handle_query(request), 127 | } 128 | } 129 | } 130 | 131 | impl WasmMockQuerier { 132 | pub fn new(base: MockQuerier) -> Self { 133 | WasmMockQuerier { base } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /contracts/anchor_airdrop_registry/schema/execute_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "ExecuteMsg", 4 | "anyOf": [ 5 | { 6 | "type": "object", 7 | "required": [ 8 | "fabricate_m_i_r_claim" 9 | ], 10 | "properties": { 11 | "fabricate_m_i_r_claim": { 12 | "type": "object", 13 | "required": [ 14 | "amount", 15 | "proof", 16 | "stage" 17 | ], 18 | "properties": { 19 | "amount": { 20 | "$ref": "#/definitions/Uint128" 21 | }, 22 | "proof": { 23 | "type": "array", 24 | "items": { 25 | "type": "string" 26 | } 27 | }, 28 | "stage": { 29 | "type": "integer", 30 | "format": "uint8", 31 | "minimum": 0.0 32 | } 33 | } 34 | } 35 | }, 36 | "additionalProperties": false 37 | }, 38 | { 39 | "type": "object", 40 | "required": [ 41 | "fabricate_a_n_c_claim" 42 | ], 43 | "properties": { 44 | "fabricate_a_n_c_claim": { 45 | "type": "object", 46 | "required": [ 47 | "amount", 48 | "proof", 49 | "stage" 50 | ], 51 | "properties": { 52 | "amount": { 53 | "$ref": "#/definitions/Uint128" 54 | }, 55 | "proof": { 56 | "type": "array", 57 | "items": { 58 | "type": "string" 59 | } 60 | }, 61 | "stage": { 62 | "type": "integer", 63 | "format": "uint8", 64 | "minimum": 0.0 65 | } 66 | } 67 | } 68 | }, 69 | "additionalProperties": false 70 | }, 71 | { 72 | "type": "object", 73 | "required": [ 74 | "update_config" 75 | ], 76 | "properties": { 77 | "update_config": { 78 | "type": "object", 79 | "properties": { 80 | "hub_contract": { 81 | "type": [ 82 | "string", 83 | "null" 84 | ] 85 | }, 86 | "owner": { 87 | "type": [ 88 | "string", 89 | "null" 90 | ] 91 | }, 92 | "reward_contract": { 93 | "type": [ 94 | "string", 95 | "null" 96 | ] 97 | } 98 | } 99 | } 100 | }, 101 | "additionalProperties": false 102 | }, 103 | { 104 | "type": "object", 105 | "required": [ 106 | "add_airdrop_info" 107 | ], 108 | "properties": { 109 | "add_airdrop_info": { 110 | "type": "object", 111 | "required": [ 112 | "airdrop_info", 113 | "airdrop_token" 114 | ], 115 | "properties": { 116 | "airdrop_info": { 117 | "$ref": "#/definitions/AirdropInfo" 118 | }, 119 | "airdrop_token": { 120 | "type": "string" 121 | } 122 | } 123 | } 124 | }, 125 | "additionalProperties": false 126 | }, 127 | { 128 | "type": "object", 129 | "required": [ 130 | "remove_airdrop_info" 131 | ], 132 | "properties": { 133 | "remove_airdrop_info": { 134 | "type": "object", 135 | "required": [ 136 | "airdrop_token" 137 | ], 138 | "properties": { 139 | "airdrop_token": { 140 | "type": "string" 141 | } 142 | } 143 | } 144 | }, 145 | "additionalProperties": false 146 | }, 147 | { 148 | "type": "object", 149 | "required": [ 150 | "update_airdrop_info" 151 | ], 152 | "properties": { 153 | "update_airdrop_info": { 154 | "type": "object", 155 | "required": [ 156 | "airdrop_info", 157 | "airdrop_token" 158 | ], 159 | "properties": { 160 | "airdrop_info": { 161 | "$ref": "#/definitions/AirdropInfo" 162 | }, 163 | "airdrop_token": { 164 | "type": "string" 165 | } 166 | } 167 | } 168 | }, 169 | "additionalProperties": false 170 | } 171 | ], 172 | "definitions": { 173 | "AirdropInfo": { 174 | "type": "object", 175 | "required": [ 176 | "airdrop_contract", 177 | "airdrop_swap_contract", 178 | "airdrop_token_contract" 179 | ], 180 | "properties": { 181 | "airdrop_contract": { 182 | "type": "string" 183 | }, 184 | "airdrop_swap_contract": { 185 | "type": "string" 186 | }, 187 | "airdrop_token_contract": { 188 | "type": "string" 189 | }, 190 | "swap_belief_price": { 191 | "anyOf": [ 192 | { 193 | "$ref": "#/definitions/Decimal" 194 | }, 195 | { 196 | "type": "null" 197 | } 198 | ] 199 | }, 200 | "swap_max_spread": { 201 | "anyOf": [ 202 | { 203 | "$ref": "#/definitions/Decimal" 204 | }, 205 | { 206 | "type": "null" 207 | } 208 | ] 209 | } 210 | } 211 | }, 212 | "Decimal": { 213 | "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", 214 | "type": "string" 215 | }, 216 | "Uint128": { 217 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 218 | "type": "string" 219 | } 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/src/state.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Addr, Api, CanonicalAddr, Decimal, Deps, Order, StdResult, Storage, Uint128}; 2 | 3 | use schemars::JsonSchema; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | use basset::reward::HolderResponse; 7 | use cw_storage_plus::{Bound, Item, Map}; 8 | 9 | pub const STATE: Item = Item::new("\u{0}\u{5}state"); 10 | pub const CONFIG: Item = Item::new("\u{0}\u{6}config"); 11 | pub const HOLDERS: Map<&[u8], Holder> = Map::new("holders"); 12 | 13 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 14 | pub struct Config { 15 | pub hub_contract: CanonicalAddr, 16 | pub reward_denom: String, 17 | } 18 | 19 | pub fn store_config(storage: &mut dyn Storage, config: &Config) -> StdResult<()> { 20 | CONFIG.save(storage, config) 21 | } 22 | 23 | pub fn read_config(storage: &dyn Storage) -> StdResult { 24 | CONFIG.load(storage) 25 | } 26 | 27 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 28 | pub struct State { 29 | pub global_index: Decimal, 30 | pub total_balance: Uint128, 31 | pub prev_reward_balance: Uint128, 32 | } 33 | 34 | pub fn store_state(storage: &mut dyn Storage, state: &State) -> StdResult<()> { 35 | STATE.save(storage, state) 36 | } 37 | 38 | pub fn read_state(storage: &dyn Storage) -> StdResult { 39 | STATE.load(storage) 40 | } 41 | 42 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 43 | pub struct Holder { 44 | pub balance: Uint128, 45 | pub index: Decimal, 46 | pub pending_rewards: Decimal, 47 | } 48 | 49 | // This is similar to HashMap 50 | pub fn store_holder( 51 | storage: &mut dyn Storage, 52 | holder_address: &CanonicalAddr, 53 | holder: &Holder, 54 | ) -> StdResult<()> { 55 | HOLDERS.save(storage, holder_address.as_slice(), holder) 56 | } 57 | 58 | pub fn read_holder(storage: &dyn Storage, holder_address: &CanonicalAddr) -> StdResult { 59 | let res = HOLDERS.may_load(storage, holder_address.as_slice())?; 60 | match res { 61 | Some(holder) => Ok(holder), 62 | None => Ok(Holder { 63 | balance: Uint128::zero(), 64 | index: Decimal::zero(), 65 | pending_rewards: Decimal::zero(), 66 | }), 67 | } 68 | } 69 | 70 | // settings for pagination 71 | const MAX_LIMIT: u32 = 30; 72 | const DEFAULT_LIMIT: u32 = 10; 73 | pub fn read_holders( 74 | deps: Deps, 75 | start_after: Option, 76 | limit: Option, 77 | ) -> StdResult> { 78 | let limit = limit.unwrap_or(DEFAULT_LIMIT).min(MAX_LIMIT) as usize; 79 | let start = calc_range_start(deps.api, start_after.map(Addr::unchecked))?.map(Bound::exclusive); 80 | 81 | HOLDERS 82 | .range(deps.storage, start, None, Order::Ascending) 83 | .take(limit) 84 | .map(|elem| { 85 | let (k, v) = elem?; 86 | let address: String = deps.api.addr_humanize(&CanonicalAddr::from(k))?.to_string(); 87 | Ok(HolderResponse { 88 | address, 89 | balance: v.balance, 90 | index: v.index, 91 | pending_rewards: v.pending_rewards, 92 | }) 93 | }) 94 | .collect() 95 | } 96 | 97 | // this will set the first key after the provided key, by appending a 1 byte 98 | fn calc_range_start(api: &dyn Api, start_after: Option) -> StdResult>> { 99 | match start_after { 100 | Some(human) => { 101 | let mut v: Vec = api.addr_canonicalize(human.as_ref())?.0.into(); 102 | v.push(0); 103 | Ok(Some(v)) 104 | } 105 | None => Ok(None), 106 | } 107 | } 108 | 109 | #[cfg(test)] 110 | mod test { 111 | use super::*; 112 | 113 | use cosmwasm_std::testing::mock_dependencies; 114 | use cosmwasm_std::{Api, StdResult, Storage}; 115 | use cosmwasm_storage::{ 116 | bucket, bucket_read, singleton, singleton_read, Bucket, ReadonlyBucket, 117 | }; 118 | 119 | pub static STATE_KEY: &[u8] = b"state"; 120 | pub static CONFIG_KEY: &[u8] = b"config"; 121 | pub static PREFIX_HOLDERS: &[u8] = b"holders"; 122 | 123 | pub fn store_state(storage: &mut dyn Storage, params: &State) -> StdResult<()> { 124 | singleton(storage, STATE_KEY).save(params) 125 | } 126 | pub fn read_state(storage: &dyn Storage) -> StdResult { 127 | singleton_read(storage, STATE_KEY).load() 128 | } 129 | 130 | pub fn store_legacy_config(storage: &mut dyn Storage, params: &Config) -> StdResult<()> { 131 | singleton(storage, CONFIG_KEY).save(params) 132 | } 133 | pub fn read_legacy_config(storage: &dyn Storage) -> StdResult { 134 | singleton_read(storage, CONFIG_KEY).load() 135 | } 136 | 137 | /// balances are state of the erc20 tokens 138 | pub fn legacy_holders(storage: &mut dyn Storage) -> Bucket { 139 | bucket(storage, PREFIX_HOLDERS) 140 | } 141 | 142 | /// balances are state of the erc20 tokens (read-only version for queries) 143 | pub fn legacy_holders_read(storage: &dyn Storage) -> ReadonlyBucket { 144 | bucket_read(storage, PREFIX_HOLDERS) 145 | } 146 | 147 | #[test] 148 | fn state_legacy_compatibility() { 149 | let mut deps = mock_dependencies(&[]); 150 | store_state( 151 | &mut deps.storage, 152 | &State { 153 | global_index: Default::default(), 154 | total_balance: Default::default(), 155 | prev_reward_balance: Default::default(), 156 | }, 157 | ) 158 | .unwrap(); 159 | 160 | assert_eq!( 161 | STATE.load(&deps.storage).unwrap(), 162 | read_state(&deps.storage).unwrap() 163 | ); 164 | } 165 | 166 | #[test] 167 | fn config_legacy_compatibility() { 168 | let mut deps = mock_dependencies(&[]); 169 | store_legacy_config( 170 | &mut deps.storage, 171 | &Config { 172 | hub_contract: deps.api.addr_canonicalize("hub").unwrap(), 173 | reward_denom: "".to_string(), 174 | }, 175 | ) 176 | .unwrap(); 177 | 178 | assert_eq!( 179 | CONFIG.load(&deps.storage).unwrap(), 180 | read_legacy_config(&deps.storage).unwrap() 181 | ); 182 | } 183 | 184 | #[test] 185 | fn holders_legacy_compatibility() { 186 | let mut deps = mock_dependencies(&[]); 187 | let mut balances = legacy_holders(&mut deps.storage); 188 | let addr1 = deps.api.addr_canonicalize("addr0000").unwrap(); 189 | let key1 = addr1.as_slice(); 190 | 191 | balances 192 | .save( 193 | key1, 194 | &Holder { 195 | balance: Uint128::from(200u128), 196 | index: Default::default(), 197 | pending_rewards: Default::default(), 198 | }, 199 | ) 200 | .unwrap(); 201 | 202 | let balances_read = legacy_holders_read(&deps.storage); 203 | assert_eq!( 204 | HOLDERS.load(&deps.storage, key1).unwrap(), 205 | balances_read.load(key1).unwrap() 206 | ); 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /packages/basset/src/hub.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{Binary, CanonicalAddr, Decimal, Uint128}; 2 | use cw20::Cw20ReceiveMsg; 3 | use schemars::JsonSchema; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | pub type UnbondRequest = Vec<(u64, Uint128)>; 7 | 8 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 9 | pub struct InstantiateMsg { 10 | pub epoch_period: u64, 11 | pub underlying_coin_denom: String, 12 | pub unbonding_period: u64, 13 | pub peg_recovery_fee: Decimal, 14 | pub er_threshold: Decimal, 15 | pub reward_denom: String, 16 | pub validator: String, 17 | } 18 | 19 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema, Default)] 20 | pub struct State { 21 | pub exchange_rate: Decimal, 22 | pub total_bond_amount: Uint128, 23 | pub last_index_modification: u64, 24 | pub prev_hub_balance: Uint128, 25 | pub actual_unbonded_amount: Uint128, 26 | pub last_unbonded_time: u64, 27 | pub last_processed_batch: u64, 28 | } 29 | 30 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 31 | pub struct Config { 32 | pub creator: CanonicalAddr, 33 | pub reward_contract: Option, 34 | pub token_contract: Option, 35 | pub airdrop_registry_contract: Option, 36 | } 37 | 38 | impl State { 39 | pub fn update_exchange_rate(&mut self, total_issued: Uint128, requested_with_fee: Uint128) { 40 | let actual_supply = total_issued + requested_with_fee; 41 | if self.total_bond_amount.is_zero() || actual_supply.is_zero() { 42 | self.exchange_rate = Decimal::one() 43 | } else { 44 | self.exchange_rate = Decimal::from_ratio(self.total_bond_amount, actual_supply); 45 | } 46 | } 47 | } 48 | 49 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 50 | #[serde(rename_all = "snake_case")] 51 | pub enum ExecuteMsg { 52 | //////////////////// 53 | /// Owner's operations 54 | //////////////////// 55 | 56 | /// Set the owener 57 | UpdateConfig { 58 | owner: Option, 59 | reward_contract: Option, 60 | token_contract: Option, 61 | airdrop_registry_contract: Option, 62 | }, 63 | 64 | /// Register receives the reward contract address 65 | RegisterValidator { 66 | validator: String, 67 | }, 68 | 69 | // Remove the validator from validators whitelist 70 | DeregisterValidator { 71 | validator: String, 72 | }, 73 | 74 | /// update the parameters that is needed for the contract 75 | UpdateParams { 76 | epoch_period: Option, 77 | unbonding_period: Option, 78 | peg_recovery_fee: Option, 79 | er_threshold: Option, 80 | }, 81 | 82 | //////////////////// 83 | /// User's operations 84 | //////////////////// 85 | 86 | /// Receives `amount` in underlying coin denom from sender. 87 | /// Delegate `amount` to a specific `validator`. 88 | /// Issue `amount` / exchange_rate for the user. 89 | Bond { 90 | validator: String, 91 | }, 92 | 93 | /// Update global index 94 | UpdateGlobalIndex { 95 | airdrop_hooks: Option>, 96 | }, 97 | 98 | /// Send back unbonded coin to the user 99 | WithdrawUnbonded {}, 100 | 101 | /// Check whether the slashing has happened or not 102 | CheckSlashing {}, 103 | 104 | //////////////////// 105 | /// bAsset's operations 106 | /////////////////// 107 | 108 | /// Receive interface for send token. 109 | /// Unbond the underlying coin denom. 110 | /// Burn the received basset token. 111 | Receive(Cw20ReceiveMsg), 112 | 113 | //////////////////// 114 | /// internal operations 115 | /////////////////// 116 | ClaimAirdrop { 117 | airdrop_token_contract: String, // Contract address of MIR Cw20 Token 118 | airdrop_contract: String, // Contract address of MIR Airdrop 119 | airdrop_swap_contract: String, // E.g. Contract address of MIR <> UST Terraswap Pair 120 | claim_msg: Binary, // Base64-encoded JSON of MIRAirdropHandleMsg::Claim 121 | swap_msg: Binary, // Base64-encoded string of JSON of PairHandleMsg::Swap 122 | }, 123 | 124 | /// Swaps claimed airdrop tokens to UST through Terraswap & sends resulting UST to bLuna Reward contract 125 | SwapHook { 126 | airdrop_token_contract: String, // E.g. contract address of MIR Token 127 | airdrop_swap_contract: String, // E.g. Contract address of MIR <> UST Terraswap Pair 128 | swap_msg: Binary, // E.g. Base64-encoded JSON of PairHandleMsg::Swap 129 | }, 130 | } 131 | 132 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 133 | #[serde(rename_all = "snake_case")] 134 | pub enum QueryMsg { 135 | Config {}, 136 | State {}, 137 | WhitelistedValidators {}, 138 | CurrentBatch {}, 139 | WithdrawableUnbonded { 140 | address: String, 141 | }, 142 | Parameters {}, 143 | UnbondRequests { 144 | address: String, 145 | }, 146 | AllHistory { 147 | start_from: Option, 148 | limit: Option, 149 | }, 150 | } 151 | 152 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 153 | #[serde(rename_all = "snake_case")] 154 | pub enum Cw20HookMsg { 155 | Unbond {}, 156 | } 157 | 158 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 159 | pub struct UnbondHistory { 160 | pub batch_id: u64, 161 | pub time: u64, 162 | pub amount: Uint128, 163 | pub applied_exchange_rate: Decimal, 164 | pub withdraw_rate: Decimal, 165 | pub released: bool, 166 | } 167 | 168 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 169 | pub struct StateResponse { 170 | pub exchange_rate: Decimal, 171 | pub total_bond_amount: Uint128, 172 | pub last_index_modification: u64, 173 | pub prev_hub_balance: Uint128, 174 | pub actual_unbonded_amount: Uint128, 175 | pub last_unbonded_time: u64, 176 | pub last_processed_batch: u64, 177 | } 178 | 179 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 180 | pub struct ConfigResponse { 181 | pub owner: String, 182 | pub reward_contract: Option, 183 | pub token_contract: Option, 184 | pub airdrop_registry_contract: Option, 185 | } 186 | 187 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 188 | pub struct WhitelistedValidatorsResponse { 189 | pub validators: Vec, 190 | } 191 | 192 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 193 | pub struct CurrentBatchResponse { 194 | pub id: u64, 195 | pub requested_with_fee: Uint128, 196 | } 197 | 198 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 199 | pub struct WithdrawableUnbondedResponse { 200 | pub withdrawable: Uint128, 201 | } 202 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 203 | pub struct UnbondRequestsResponse { 204 | pub address: String, 205 | pub requests: UnbondRequest, 206 | } 207 | 208 | #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] 209 | pub struct AllHistoryResponse { 210 | pub history: Vec, 211 | } 212 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/README.md: -------------------------------------------------------------------------------- 1 | # Anchor bAsset Token: CW20 Fungible Tokens 2 | 3 | CW20 is a specification for fungible tokens based on CosmWasm. 4 | The name and design is loosely based on Ethereum's ERC20 standard, 5 | but many changes have been made. The types in here can be imported by 6 | contracts that wish to implement this spec, or by contracts that call 7 | to any standard cw20 contract. 8 | 9 | The specification is split into multiple sections, a contract may only 10 | implement some of this functionality, but must implement the base. 11 | 12 | ## Base 13 | 14 | This handles balances and transfers. Note that all amounts are 15 | handled as `Uint128` (128 bit integers with JSON string representation). 16 | Handling decimals is left to the UI and not interpreted. 17 | 18 | ## Messages 19 | 20 | ### Transfer 21 | 22 | * Transfer{*recipient* HumanAddr, *amount* Uint128} 23 | - Sends `IncreaseBalance` to the reward contract for the recipient. 24 | - Sends `DecreaseBalance` to the reward contract for the sender. 25 | - Moves `amount` tokens from the `env.sender` account to the `recipient` account. 26 | - This is designed to 27 | send to an address controlled by a private key and *does not* trigger 28 | any actions on the recipient if it is a contract. 29 | - Reduces `env.sender`'s and increases `recipient`'s balance in the reward contract for reward calculation. 30 | 31 | ### Send 32 | * Send{contract, amount, msg} 33 | - Sends `IncreaseBalance` to the reward contract for the contract. 34 | - Sends `DecreaseBalance` to the reward contract for the sender. 35 | - Moves `amount` tokens from the `env.sender` account to the `contract`. 36 | -`contract` must be an address of a contract that implements the `Receiver` interface. 37 | - In order to burn, the `msg` must be `InitBurn` and will be passed to the recipient contract, along with the amount. 38 | - Reduces `env.sender`'s balance and increases the balance of the `contract` in the reward contract for reward calculation. 39 | 40 | ### Burn 41 | * Burn{amount} 42 | - Removes `amount` tokens from the balance of `env.sender` 43 | - Reduces `total_supply` by the same amount. 44 | - Decreases `env.sender`'s balance in the reward contract for reward calculation 45 | 46 | ## Queries 47 | 48 | ### Balance 49 | * Balance{address} 50 | - Returns the balance of the given address. 51 | - Returns "0" if the address is unknown to the contract. Return type 52 | is `BalanceResponse{balance}`. 53 | 54 | ### TokenInfo 55 | * TokenInfo{} 56 | - Returns the token info of the contract. Return type is 57 | `TokenInfoResponse{name, symbol, decimal, total_supply}`. 58 | 59 | ## Allowances 60 | 61 | A contract may allow actors to delegate some of their balance to other 62 | accounts. This is not as essential as with ERC20 as we use `Send`/`Receive` 63 | to send tokens to a contract, not `Approve`/`TransferFrom`. But it 64 | is still a nice use-case, and you can see how the Cosmos SDK wants to add 65 | payment allowances to native tokens. This is mainly designed to provide 66 | access to other public-key-based accounts. 67 | 68 | There was an issue with race conditions in the original ERC20 approval spec. 69 | If you had an approval of 50 and I then want to reduce it to 20, I submit a 70 | Tx to set the allowance to 20. If you see that and immediately submit a tx 71 | using the entire 50, you then get access to the other 20. Not only did you quickly 72 | spend the 50 before I could reduce it, you get another 20 for free. 73 | 74 | The solution discussed in the Ethereum community was an `IncreaseAllowance` 75 | and `DecreaseAllowance` operator (instead of `Approve`). To originally set 76 | an approval, use `IncreaseAllowance`, which works fine with no previous allowance. 77 | `DecreaseAllowance` is meant to be robust, that is if you decrease by more than 78 | the current allowance (eg. the user spent some in the middle), it will just round 79 | down to 0 and not make any underflow error. 80 | 81 | ## Messages 82 | 83 | ### IncreaseAllowance 84 | * IncreaseAllowance{spender, amount, expires} 85 | - Set or increase the allowance such that `spender` may access up to `amount + current_allowance` tokens 86 | from the `env.sender` account. 87 | - This may optionally come with an `Expiration` 88 | time, which if set limits when the approval can be used (by time or height). 89 | 90 | ### DecreaseAllowance 91 | * DecreaseAllowance{spender, amount, expires} 92 | - Decrease or clear the allowance such that `spender` may access up to `current_allowance - amount` tokens 93 | from the `env.sender` account. 94 | - This may optionally come with an `Expiration` 95 | time, which if set limits when the approval can be used (by time or height). 96 | If `amount >= current_allowance`, this will clear the allowance (delete it). 97 | 98 | ### TransferFrom 99 | * TransferFrom{owner, recipient, amount} 100 | - This makes use of an allowance 101 | and if there was a valid, un-expired pre-approval for the `env.sender`, 102 | then we move `amount` tokens from `owner` to `recipient` and deduct it 103 | from the available allowance. 104 | - Reduces `owner`'s balance and increases the `recipient` balance in the reward contract for reward calculation. 105 | 106 | ### SendFrom 107 | * SendFrom{owner, contract, amount, msg} 108 | - `SendFrom` is to `Send`, what 109 | `TransferFrom` is to `Transfer`. This allows a pre-approved account to 110 | not just transfer the tokens, but to send them to another contract 111 | to trigger a given action. 112 | - Reduces `owner`'s balance and increases the balance of the `contract` in the reward contract. 113 | - **Note** `SendFrom` will set the `Receive{sender}` 114 | to be the `env.sender` (the account that triggered the transfer) 115 | rather than the `owner` account (the account the money is coming from). 116 | This is an open question whether we should switch this? 117 | 118 | ### BurnFrom 119 | BurnFrom{owner, amount} 120 | - This works like `TransferFrom`, but burns 121 | the tokens instead of transfering them. This will reduce the owner's 122 | balance, `total_supply` and the caller's allowance. 123 | - Reduces `owner`'s balance in the reward contract for reward calculation. 124 | 125 | ## Queries 126 | ### Allowance 127 | * Allowance{owner, spender} 128 | - This returns the available allowance 129 | that `spender` can access from the `owner`'s account, along with the 130 | expiration info. Return type is `AllowanceResponse{balance, expiration}`. 131 | 132 | ## Mintable 133 | 134 | This allows another contract to mint new tokens, possibly with a cap. 135 | There is only one minter specified here, if you want more complex 136 | access management, please use a multisig or other contract as the 137 | minter address and handle updating the ACL there. 138 | 139 | ## Messages 140 | ### Mint 141 | * Mint{recipient, amount} 142 | - If the `env.sender` is the allowed minter, 143 | this will create `amount` new tokens (updating total supply) and 144 | add them to the balance of `recipient`. 145 | 146 | ## Queries 147 | ### Minter 148 | * Minter{} 149 | - Returns who and how much can be minted. Return type is 150 | `MinterResponse {minter, cap}`. Cap may be unset. 151 | 152 | ## Enumerable 153 | 154 | This should be enabled with all blockchains that have iterator support. 155 | It allows us to get lists of results with pagination. 156 | 157 | ## Queries 158 | ### AllAllowances 159 | * AllAllowances{owner, start_after, limit} 160 | - Returns the list of all non-expired allowances 161 | by the given owner. `start_after` and `limit` provide pagination. 162 | 163 | ### AllAccounts 164 | * AllAccounts{start_after, limit} 165 | - Returns the list of all accounts that have been created on 166 | the contract (just the addresses). `start_after` and `limit` provide pagination. 167 | 168 | 169 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/src/config.rs: -------------------------------------------------------------------------------- 1 | use crate::state::{ 2 | read_validators, remove_white_validators, store_white_validators, Parameters, CONFIG, 3 | PARAMETERS, 4 | }; 5 | use basset::hub::{Config, ExecuteMsg}; 6 | use cosmwasm_std::{ 7 | attr, to_binary, Addr, CosmosMsg, Decimal, DepsMut, DistributionMsg, Env, MessageInfo, 8 | Response, StakingMsg, StdError, StdResult, WasmMsg, 9 | }; 10 | 11 | use rand::{Rng, SeedableRng, XorShiftRng}; 12 | 13 | /// Update general parameters 14 | /// Only creator/owner is allowed to execute 15 | #[allow(clippy::too_many_arguments)] 16 | pub fn execute_update_params( 17 | deps: DepsMut, 18 | _env: Env, 19 | info: MessageInfo, 20 | epoch_period: Option, 21 | unbonding_period: Option, 22 | peg_recovery_fee: Option, 23 | er_threshold: Option, 24 | ) -> StdResult { 25 | // only owner can send this message. 26 | let config = CONFIG.load(deps.storage)?; 27 | let sender_raw = deps.api.addr_canonicalize(info.sender.as_str())?; 28 | if sender_raw != config.creator { 29 | return Err(StdError::generic_err("unauthorized")); 30 | } 31 | 32 | let params: Parameters = PARAMETERS.load(deps.storage)?; 33 | 34 | let new_params = Parameters { 35 | epoch_period: epoch_period.unwrap_or(params.epoch_period), 36 | underlying_coin_denom: params.underlying_coin_denom, 37 | unbonding_period: unbonding_period.unwrap_or(params.unbonding_period), 38 | peg_recovery_fee: peg_recovery_fee.unwrap_or(params.peg_recovery_fee), 39 | er_threshold: er_threshold.unwrap_or(params.er_threshold), 40 | reward_denom: params.reward_denom, 41 | }; 42 | 43 | PARAMETERS.save(deps.storage, &new_params)?; 44 | 45 | Ok(Response::new().add_attributes(vec![attr("action", "update_params")])) 46 | } 47 | 48 | /// Update the config. Update the owner, reward and token contracts. 49 | /// Only creator/owner is allowed to execute 50 | pub fn execute_update_config( 51 | deps: DepsMut, 52 | _env: Env, 53 | info: MessageInfo, 54 | owner: Option, 55 | reward_contract: Option, 56 | token_contract: Option, 57 | airdrop_registry_contract: Option, 58 | ) -> StdResult { 59 | // only owner must be able to send this message. 60 | let conf = CONFIG.load(deps.storage)?; 61 | let sender_raw = deps.api.addr_canonicalize(info.sender.as_str())?; 62 | if sender_raw != conf.creator { 63 | return Err(StdError::generic_err("unauthorized")); 64 | } 65 | 66 | let mut messages: Vec = vec![]; 67 | 68 | if let Some(o) = owner { 69 | let owner_raw = deps.api.addr_canonicalize(o.as_str())?; 70 | 71 | CONFIG.update(deps.storage, |mut last_config| -> StdResult { 72 | last_config.creator = owner_raw; 73 | Ok(last_config) 74 | })?; 75 | } 76 | if let Some(reward) = reward_contract { 77 | let reward_raw = deps.api.addr_canonicalize(reward.as_str())?; 78 | 79 | CONFIG.update(deps.storage, |mut last_config| -> StdResult { 80 | last_config.reward_contract = Some(reward_raw); 81 | Ok(last_config) 82 | })?; 83 | 84 | // register the reward contract for automate reward withdrawal. 85 | messages.push(CosmosMsg::Distribution( 86 | DistributionMsg::SetWithdrawAddress { address: reward }, 87 | )); 88 | } 89 | 90 | if let Some(token) = token_contract { 91 | let token_raw = deps.api.addr_canonicalize(token.as_str())?; 92 | 93 | CONFIG.update(deps.storage, |mut last_config| -> StdResult { 94 | last_config.token_contract = Some(token_raw); 95 | Ok(last_config) 96 | })?; 97 | } 98 | 99 | if let Some(airdrop) = airdrop_registry_contract { 100 | let airdrop_raw = deps.api.addr_canonicalize(airdrop.as_str())?; 101 | CONFIG.update(deps.storage, |mut last_config| -> StdResult { 102 | last_config.airdrop_registry_contract = Some(airdrop_raw); 103 | Ok(last_config) 104 | })?; 105 | } 106 | 107 | Ok(Response::new() 108 | .add_messages(messages) 109 | .add_attributes(vec![attr("action", "update_config")])) 110 | } 111 | 112 | /// Register a white listed validator. 113 | /// Only creator/owner is allowed to execute 114 | pub fn execute_register_validator( 115 | deps: DepsMut, 116 | env: Env, 117 | info: MessageInfo, 118 | validator: String, 119 | ) -> StdResult { 120 | let hub_conf = CONFIG.load(deps.storage)?; 121 | 122 | let sender_raw = deps.api.addr_canonicalize(info.sender.as_str())?; 123 | let contract_raw = deps.api.addr_canonicalize(env.contract.address.as_str())?; 124 | if hub_conf.creator != sender_raw && contract_raw != sender_raw { 125 | return Err(StdError::generic_err("unauthorized")); 126 | } 127 | 128 | // given validator must be first a validator in the system. 129 | let exists = deps 130 | .querier 131 | .query_all_validators()? 132 | .iter() 133 | .any(|val| val.address == validator); 134 | if !exists { 135 | return Err(StdError::generic_err( 136 | "The specified address is not a validator", 137 | )); 138 | } 139 | 140 | store_white_validators(deps.storage, validator.clone())?; 141 | 142 | Ok(Response::new().add_attributes(vec![ 143 | attr("action", "register_validator"), 144 | attr("validator", validator), 145 | ])) 146 | } 147 | 148 | /// Deregister a previously-whitelisted validator. 149 | /// Only creator/owner is allowed to execute 150 | pub fn execute_deregister_validator( 151 | deps: DepsMut, 152 | env: Env, 153 | info: MessageInfo, 154 | validator: String, 155 | ) -> StdResult { 156 | let token = CONFIG.load(deps.storage)?; 157 | 158 | let sender_raw = deps.api.addr_canonicalize(info.sender.as_str())?; 159 | if token.creator != sender_raw { 160 | return Err(StdError::generic_err("unauthorized")); 161 | } 162 | let validators_before_remove = read_validators(deps.storage)?; 163 | 164 | if validators_before_remove.len() == 1 { 165 | return Err(StdError::generic_err( 166 | "Cannot remove the last whitelisted validator", 167 | )); 168 | } 169 | 170 | remove_white_validators(deps.storage, validator.to_string())?; 171 | 172 | let query = deps 173 | .querier 174 | .query_delegation(env.contract.address.clone(), validator.clone()); 175 | 176 | let mut replaced_val = Addr::unchecked(""); 177 | let mut messages: Vec = vec![]; 178 | 179 | if let Ok(q) = query { 180 | let delegated_amount = q; 181 | let validators = read_validators(deps.storage)?; 182 | 183 | // redelegate the amount to a random validator. 184 | let block_height = env.block.height; 185 | let mut rng = XorShiftRng::seed_from_u64(block_height); 186 | let random_index = rng.gen_range(0, validators.len()); 187 | replaced_val = Addr::unchecked(validators.get(random_index).unwrap().as_str()); 188 | 189 | if let Some(delegation) = delegated_amount { 190 | messages.push(CosmosMsg::Staking(StakingMsg::Redelegate { 191 | src_validator: validator.to_string(), 192 | dst_validator: replaced_val.to_string(), 193 | amount: delegation.amount, 194 | })); 195 | 196 | let msg = ExecuteMsg::UpdateGlobalIndex { 197 | airdrop_hooks: None, 198 | }; 199 | messages.push(CosmosMsg::Wasm(WasmMsg::Execute { 200 | contract_addr: env.contract.address.to_string(), 201 | msg: to_binary(&msg)?, 202 | funds: vec![], 203 | })); 204 | } 205 | } 206 | 207 | Ok(Response::new().add_messages(messages).add_attributes(vec![ 208 | attr("action", "de_register_validator"), 209 | attr("validator", validator), 210 | attr("new-validator", replaced_val), 211 | ])) 212 | } 213 | -------------------------------------------------------------------------------- /packages/basset/src/mock_querier.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::testing::{MockApi, MockQuerier, MockStorage}; 2 | use cosmwasm_std::{ 3 | from_slice, to_binary, AllBalanceResponse, Api, BalanceResponse, BankQuery, CanonicalAddr, 4 | Coin, ContractResult, Decimal, OwnedDeps, Querier, QuerierResult, QueryRequest, SystemError, 5 | SystemResult, Uint128, WasmQuery, 6 | }; 7 | use cosmwasm_storage::to_length_prefixed; 8 | use std::collections::HashMap; 9 | 10 | use crate::hub::Config; 11 | use schemars::JsonSchema; 12 | use serde::{Deserialize, Serialize}; 13 | use terra_cosmwasm::{TaxCapResponse, TaxRateResponse, TerraQuery, TerraQueryWrapper, TerraRoute}; 14 | 15 | pub const MOCK_CONTRACT_ADDR: &str = "cosmos2contract"; 16 | 17 | pub fn mock_dependencies( 18 | contract_balance: &[Coin], 19 | ) -> OwnedDeps { 20 | let contract_addr = String::from(MOCK_CONTRACT_ADDR); 21 | let custom_querier: WasmMockQuerier = WasmMockQuerier::new( 22 | MockQuerier::new(&[(&contract_addr, contract_balance)]), 23 | MockApi::default(), 24 | ); 25 | 26 | OwnedDeps { 27 | storage: MockStorage::default(), 28 | api: MockApi::default(), 29 | querier: custom_querier, 30 | } 31 | } 32 | 33 | #[derive(Clone, Default)] 34 | pub struct TaxQuerier { 35 | rate: Decimal, 36 | caps: HashMap, 37 | } 38 | 39 | impl TaxQuerier { 40 | pub fn new(rate: Decimal, caps: &[(&String, &Uint128)]) -> Self { 41 | TaxQuerier { 42 | rate, 43 | caps: caps_to_map(caps), 44 | } 45 | } 46 | } 47 | 48 | pub(crate) fn caps_to_map(caps: &[(&String, &Uint128)]) -> HashMap { 49 | let mut owner_map: HashMap = HashMap::new(); 50 | for (denom, cap) in caps.iter() { 51 | owner_map.insert(denom.to_string(), **cap); 52 | } 53 | owner_map 54 | } 55 | 56 | pub struct WasmMockQuerier { 57 | base: MockQuerier, 58 | tax_querier: TaxQuerier, 59 | } 60 | 61 | impl Querier for WasmMockQuerier { 62 | fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { 63 | // MockQuerier doesn't support Custom, so we ignore it completely here 64 | let request: QueryRequest = match from_slice(bin_request) { 65 | Ok(v) => v, 66 | Err(e) => { 67 | return SystemResult::Err(SystemError::InvalidRequest { 68 | error: format!("Parsing query request: {}", e), 69 | request: bin_request.into(), 70 | }) 71 | } 72 | }; 73 | self.handle_query(&request) 74 | } 75 | } 76 | 77 | impl WasmMockQuerier { 78 | pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { 79 | match &request { 80 | QueryRequest::Custom(TerraQueryWrapper { route, query_data }) => { 81 | if &TerraRoute::Treasury == route { 82 | match query_data { 83 | TerraQuery::TaxRate {} => { 84 | let res = TaxRateResponse { 85 | rate: self.tax_querier.rate, 86 | }; 87 | SystemResult::Ok(ContractResult::from(to_binary(&res))) 88 | } 89 | TerraQuery::TaxCap { denom } => { 90 | let cap = self 91 | .tax_querier 92 | .caps 93 | .get(denom) 94 | .copied() 95 | .unwrap_or_default(); 96 | let res = TaxCapResponse { cap }; 97 | SystemResult::Ok(ContractResult::from(to_binary(&res))) 98 | } 99 | _ => panic!("DO NOT ENTER HERE"), 100 | } 101 | } else { 102 | panic!("DO NOT ENTER HERE") 103 | } 104 | } 105 | QueryRequest::Wasm(WasmQuery::Raw { 106 | contract_addr: _, 107 | key, 108 | }) => { 109 | let prefix_config = to_length_prefixed(b"config").to_vec(); 110 | let api: MockApi = MockApi::default(); 111 | 112 | if key.as_slice().to_vec() == prefix_config { 113 | let config = Config { 114 | creator: api.addr_canonicalize(&String::from("owner1")).unwrap(), 115 | reward_contract: Some( 116 | api.addr_canonicalize(&String::from("reward")).unwrap(), 117 | ), 118 | token_contract: Some( 119 | api.addr_canonicalize(&String::from("token")).unwrap(), 120 | ), 121 | airdrop_registry_contract: Some( 122 | api.addr_canonicalize(&String::from("airdrop")).unwrap(), 123 | ), 124 | }; 125 | SystemResult::Ok(ContractResult::from(to_binary(&config))) 126 | } else { 127 | unimplemented!() 128 | } 129 | } 130 | QueryRequest::Bank(BankQuery::AllBalances { address }) => { 131 | if address == &String::from("reward") { 132 | let mut coins: Vec = vec![]; 133 | let luna = Coin { 134 | denom: "uluna".to_string(), 135 | amount: Uint128::new(1000u128), 136 | }; 137 | coins.push(luna); 138 | let krt = Coin { 139 | denom: "ukrt".to_string(), 140 | amount: Uint128::new(1000u128), 141 | }; 142 | coins.push(krt); 143 | let all_balances = AllBalanceResponse { amount: coins }; 144 | SystemResult::Ok(ContractResult::from(to_binary(&all_balances))) 145 | } else { 146 | unimplemented!() 147 | } 148 | } 149 | QueryRequest::Bank(BankQuery::Balance { address, denom }) => { 150 | if address == &String::from("reward") && denom == "uusd" { 151 | let bank_res = BalanceResponse { 152 | amount: Coin { 153 | amount: Uint128::new(2000u128), 154 | denom: denom.to_string(), 155 | }, 156 | }; 157 | SystemResult::Ok(ContractResult::from(to_binary(&bank_res))) 158 | } else { 159 | unimplemented!() 160 | } 161 | } 162 | _ => self.base.handle_query(request), 163 | } 164 | } 165 | } 166 | 167 | #[derive(Clone, Default)] 168 | pub struct TokenQuerier { 169 | balances: HashMap>, 170 | } 171 | 172 | impl WasmMockQuerier { 173 | pub fn new(base: MockQuerier, _api: A) -> Self { 174 | WasmMockQuerier { 175 | base, 176 | tax_querier: TaxQuerier::default(), 177 | } 178 | } 179 | 180 | // configure the tax mock querier 181 | pub fn with_tax(&mut self, rate: Decimal, caps: &[(&String, &Uint128)]) { 182 | self.tax_querier = TaxQuerier::new(rate, caps); 183 | } 184 | } 185 | 186 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 187 | #[serde(rename_all = "snake_case")] 188 | pub struct TokenInfo { 189 | pub name: String, 190 | pub symbol: String, 191 | pub decimals: u8, 192 | pub total_supply: Uint128, 193 | pub mint: Option, 194 | pub owner: CanonicalAddr, 195 | } 196 | 197 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 198 | pub struct MinterData { 199 | pub minter: CanonicalAddr, 200 | /// cap is how many more tokens can be issued by the minter 201 | pub cap: Option, 202 | } 203 | -------------------------------------------------------------------------------- /contracts/anchor_basset_token/src/handler.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{ 2 | to_binary, Binary, CosmosMsg, DepsMut, Env, MessageInfo, Response, SubMsg, Uint128, WasmMsg, 3 | }; 4 | 5 | use crate::querier::query_reward_contract; 6 | use basset::reward::ExecuteMsg::{DecreaseBalance, IncreaseBalance}; 7 | use cw20_legacy::allowances::{ 8 | execute_burn_from as cw20_burn_from, execute_send_from as cw20_send_from, 9 | execute_transfer_from as cw20_transfer_from, 10 | }; 11 | use cw20_legacy::contract::{ 12 | execute_burn as cw20_burn, execute_mint as cw20_mint, execute_send as cw20_send, 13 | execute_transfer as cw20_transfer, 14 | }; 15 | use cw20_legacy::ContractError; 16 | 17 | pub fn execute_transfer( 18 | deps: DepsMut, 19 | env: Env, 20 | info: MessageInfo, 21 | recipient: String, 22 | amount: Uint128, 23 | ) -> Result { 24 | let sender = info.sender.clone(); 25 | let reward_contract = query_reward_contract(&deps)?; 26 | 27 | let rcpt_addr = deps.api.addr_validate(&recipient)?; 28 | 29 | let res: Response = cw20_transfer(deps, env, info, recipient, amount)?; 30 | let messages = vec![ 31 | CosmosMsg::Wasm(WasmMsg::Execute { 32 | contract_addr: reward_contract.to_string(), 33 | msg: to_binary(&DecreaseBalance { 34 | address: sender.to_string(), 35 | amount, 36 | }) 37 | .unwrap(), 38 | funds: vec![], 39 | }), 40 | CosmosMsg::Wasm(WasmMsg::Execute { 41 | contract_addr: reward_contract.to_string(), 42 | msg: to_binary(&IncreaseBalance { 43 | address: rcpt_addr.to_string(), 44 | amount, 45 | }) 46 | .unwrap(), 47 | funds: vec![], 48 | }), 49 | ]; 50 | Ok(Response::new() 51 | .add_messages(messages) 52 | .add_attributes(res.attributes)) 53 | } 54 | 55 | pub fn execute_burn( 56 | deps: DepsMut, 57 | env: Env, 58 | info: MessageInfo, 59 | amount: Uint128, 60 | ) -> Result { 61 | let sender = info.sender.clone(); 62 | let reward_contract = query_reward_contract(&deps)?; 63 | 64 | let res: Response = cw20_burn(deps, env, info, amount)?; 65 | let messages = vec![CosmosMsg::Wasm(WasmMsg::Execute { 66 | contract_addr: reward_contract.to_string(), 67 | msg: to_binary(&DecreaseBalance { 68 | address: sender.to_string(), 69 | amount, 70 | }) 71 | .unwrap(), 72 | funds: vec![], 73 | })]; 74 | Ok(Response::new() 75 | .add_messages(messages) 76 | .add_attributes(res.attributes)) 77 | } 78 | 79 | pub fn execute_mint( 80 | deps: DepsMut, 81 | env: Env, 82 | info: MessageInfo, 83 | recipient: String, 84 | amount: Uint128, 85 | ) -> Result { 86 | let reward_contract = query_reward_contract(&deps)?; 87 | 88 | let res: Response = cw20_mint(deps, env, info, recipient.clone(), amount)?; 89 | Ok(Response::new() 90 | .add_messages(vec![CosmosMsg::Wasm(WasmMsg::Execute { 91 | contract_addr: reward_contract.to_string(), 92 | msg: to_binary(&IncreaseBalance { 93 | address: recipient, 94 | amount, 95 | }) 96 | .unwrap(), 97 | funds: vec![], 98 | })]) 99 | .add_attributes(res.attributes)) 100 | } 101 | 102 | pub fn execute_send( 103 | deps: DepsMut, 104 | env: Env, 105 | info: MessageInfo, 106 | contract: String, 107 | amount: Uint128, 108 | msg: Binary, 109 | ) -> Result { 110 | let sender = info.sender.clone(); 111 | let reward_contract = query_reward_contract(&deps)?; 112 | 113 | let res: Response = cw20_send(deps, env, info, contract.clone(), amount, msg)?; 114 | let messages = vec![ 115 | vec![ 116 | SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { 117 | contract_addr: reward_contract.to_string(), 118 | msg: to_binary(&DecreaseBalance { 119 | address: sender.to_string(), 120 | amount, 121 | }) 122 | .unwrap(), 123 | funds: vec![], 124 | })), 125 | SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { 126 | contract_addr: reward_contract.to_string(), 127 | msg: to_binary(&IncreaseBalance { 128 | address: contract, 129 | amount, 130 | }) 131 | .unwrap(), 132 | funds: vec![], 133 | })), 134 | ], 135 | res.messages, 136 | ] 137 | .concat(); 138 | 139 | Ok(Response::new() 140 | .add_submessages(messages) 141 | .add_attributes(res.attributes)) 142 | } 143 | 144 | pub fn execute_transfer_from( 145 | deps: DepsMut, 146 | env: Env, 147 | info: MessageInfo, 148 | owner: String, 149 | recipient: String, 150 | amount: Uint128, 151 | ) -> Result { 152 | let reward_contract = query_reward_contract(&deps)?; 153 | 154 | let valid_owner = deps.api.addr_validate(owner.as_str())?; 155 | 156 | let res: Response = cw20_transfer_from(deps, env, info, owner, recipient.clone(), amount)?; 157 | let messages = vec![ 158 | CosmosMsg::Wasm(WasmMsg::Execute { 159 | contract_addr: reward_contract.to_string(), 160 | msg: to_binary(&DecreaseBalance { 161 | address: valid_owner.to_string(), 162 | amount, 163 | }) 164 | .unwrap(), 165 | funds: vec![], 166 | }), 167 | CosmosMsg::Wasm(WasmMsg::Execute { 168 | contract_addr: reward_contract.to_string(), 169 | msg: to_binary(&IncreaseBalance { 170 | address: recipient, 171 | amount, 172 | }) 173 | .unwrap(), 174 | funds: vec![], 175 | }), 176 | ]; 177 | Ok(Response::new() 178 | .add_messages(messages) 179 | .add_attributes(res.attributes)) 180 | } 181 | 182 | pub fn execute_burn_from( 183 | deps: DepsMut, 184 | env: Env, 185 | info: MessageInfo, 186 | owner: String, 187 | amount: Uint128, 188 | ) -> Result { 189 | let reward_contract = query_reward_contract(&deps)?; 190 | 191 | let valid_owner = deps.api.addr_validate(owner.as_str())?; 192 | 193 | let res: Response = cw20_burn_from(deps, env, info, owner, amount)?; 194 | let messages = vec![CosmosMsg::Wasm(WasmMsg::Execute { 195 | contract_addr: reward_contract.to_string(), 196 | msg: to_binary(&DecreaseBalance { 197 | address: valid_owner.to_string(), 198 | amount, 199 | }) 200 | .unwrap(), 201 | funds: vec![], 202 | })]; 203 | Ok(Response::new() 204 | .add_messages(messages) 205 | .add_attributes(res.attributes)) 206 | } 207 | 208 | pub fn execute_send_from( 209 | deps: DepsMut, 210 | env: Env, 211 | info: MessageInfo, 212 | owner: String, 213 | contract: String, 214 | amount: Uint128, 215 | msg: Binary, 216 | ) -> Result { 217 | let reward_contract = query_reward_contract(&deps)?; 218 | 219 | let valid_owner = deps.api.addr_validate(owner.as_str())?; 220 | 221 | let res: Response = cw20_send_from(deps, env, info, owner, contract.clone(), amount, msg)?; 222 | let messages = vec![ 223 | vec![ 224 | SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { 225 | contract_addr: reward_contract.to_string(), 226 | msg: to_binary(&DecreaseBalance { 227 | address: valid_owner.to_string(), 228 | amount, 229 | }) 230 | .unwrap(), 231 | funds: vec![], 232 | })), 233 | SubMsg::new(CosmosMsg::Wasm(WasmMsg::Execute { 234 | contract_addr: reward_contract.to_string(), 235 | msg: to_binary(&IncreaseBalance { 236 | address: contract, 237 | amount, 238 | }) 239 | .unwrap(), 240 | funds: vec![], 241 | })), 242 | ], 243 | res.messages, 244 | ] 245 | .concat(); 246 | 247 | Ok(Response::new() 248 | .add_submessages(messages) 249 | .add_attributes(res.attributes)) 250 | } 251 | -------------------------------------------------------------------------------- /contracts/anchor_basset_reward/src/user.rs: -------------------------------------------------------------------------------- 1 | use crate::querier::query_token_contract; 2 | use crate::state::{ 3 | read_config, read_holder, read_holders, read_state, store_holder, store_state, Config, Holder, 4 | State, 5 | }; 6 | use basset::reward::{AccruedRewardsResponse, HolderResponse, HoldersResponse}; 7 | 8 | use cosmwasm_std::{ 9 | attr, BankMsg, Coin, CosmosMsg, Decimal, Deps, DepsMut, Env, MessageInfo, Response, StdError, 10 | StdResult, Uint128, 11 | }; 12 | 13 | use crate::math::{ 14 | decimal_multiplication_in_256, decimal_subtraction_in_256, decimal_summation_in_256, 15 | }; 16 | use basset::deduct_tax; 17 | use std::str::FromStr; 18 | use terra_cosmwasm::TerraMsgWrapper; 19 | 20 | pub fn execute_claim_rewards( 21 | deps: DepsMut, 22 | _env: Env, 23 | info: MessageInfo, 24 | recipient: Option, 25 | ) -> StdResult> { 26 | let holder_addr = info.sender; 27 | let holder_addr_raw = deps.api.addr_canonicalize(holder_addr.as_str())?; 28 | let recipient = match recipient { 29 | Some(value) => deps.api.addr_validate(value.as_str()).unwrap(), 30 | None => holder_addr.clone(), 31 | }; 32 | 33 | let mut holder: Holder = read_holder(deps.storage, &holder_addr_raw)?; 34 | let mut state: State = read_state(deps.storage)?; 35 | let config: Config = read_config(deps.storage)?; 36 | 37 | let reward_with_decimals = 38 | calculate_decimal_rewards(state.global_index, holder.index, holder.balance)?; 39 | 40 | let all_reward_with_decimals = 41 | decimal_summation_in_256(reward_with_decimals, holder.pending_rewards); 42 | let decimals = get_decimals(all_reward_with_decimals)?; 43 | 44 | let rewards = all_reward_with_decimals * Uint128::new(1); 45 | 46 | if rewards.is_zero() { 47 | return Err(StdError::generic_err("No rewards have accrued yet")); 48 | } 49 | 50 | let new_balance = (state.prev_reward_balance.checked_sub(rewards))?; 51 | state.prev_reward_balance = new_balance; 52 | store_state(deps.storage, &state)?; 53 | 54 | holder.pending_rewards = decimals; 55 | holder.index = state.global_index; 56 | store_holder(deps.storage, &holder_addr_raw, &holder)?; 57 | 58 | let bank_msg: CosmosMsg = CosmosMsg::Bank(BankMsg::Send { 59 | to_address: recipient.to_string(), 60 | amount: vec![deduct_tax( 61 | &deps.querier, 62 | Coin { 63 | denom: config.reward_denom, 64 | amount: rewards, 65 | }, 66 | )?], 67 | }); 68 | 69 | let res = Response::new() 70 | .add_attributes(vec![ 71 | attr("action", "claim_reward"), 72 | attr("holder_address", holder_addr), 73 | attr("rewards", rewards), 74 | ]) 75 | .add_message(bank_msg); 76 | 77 | Ok(res) 78 | } 79 | 80 | pub fn execute_increase_balance( 81 | deps: DepsMut, 82 | _env: Env, 83 | info: MessageInfo, 84 | address: String, 85 | amount: Uint128, 86 | ) -> StdResult> { 87 | let config = read_config(deps.storage)?; 88 | let owner_human = deps.api.addr_humanize(&config.hub_contract)?; 89 | let address_raw = deps.api.addr_canonicalize(&address)?; 90 | let sender = info.sender; 91 | 92 | let token_address = deps 93 | .api 94 | .addr_humanize(&query_token_contract(deps.as_ref(), owner_human)?)?; 95 | 96 | // Check sender is token contract 97 | if sender != token_address { 98 | return Err(StdError::generic_err("unauthorized")); 99 | } 100 | 101 | let mut state: State = read_state(deps.storage)?; 102 | let mut holder: Holder = read_holder(deps.storage, &address_raw)?; 103 | 104 | // get decimals 105 | let rewards = calculate_decimal_rewards(state.global_index, holder.index, holder.balance)?; 106 | 107 | holder.index = state.global_index; 108 | holder.pending_rewards = decimal_summation_in_256(rewards, holder.pending_rewards); 109 | holder.balance += amount; 110 | state.total_balance += amount; 111 | 112 | store_holder(deps.storage, &address_raw, &holder)?; 113 | store_state(deps.storage, &state)?; 114 | 115 | let attributes = vec![ 116 | attr("action", "increase_balance"), 117 | attr("holder_address", address), 118 | attr("amount", amount), 119 | ]; 120 | 121 | let res = Response::new().add_attributes(attributes); 122 | Ok(res) 123 | } 124 | 125 | pub fn execute_decrease_balance( 126 | deps: DepsMut, 127 | _env: Env, 128 | info: MessageInfo, 129 | address: String, 130 | amount: Uint128, 131 | ) -> StdResult> { 132 | let config = read_config(deps.storage)?; 133 | let hub_contract = deps.api.addr_humanize(&config.hub_contract)?; 134 | let address_raw = deps.api.addr_canonicalize(&address)?; 135 | 136 | // Check sender is token contract 137 | if query_token_contract(deps.as_ref(), hub_contract)? 138 | != deps.api.addr_canonicalize(info.sender.as_str())? 139 | { 140 | return Err(StdError::generic_err("unauthorized")); 141 | } 142 | 143 | let mut state: State = read_state(deps.storage)?; 144 | let mut holder: Holder = read_holder(deps.storage, &address_raw)?; 145 | if holder.balance < amount { 146 | return Err(StdError::generic_err(format!( 147 | "Decrease amount cannot exceed user balance: {}", 148 | holder.balance 149 | ))); 150 | } 151 | 152 | let rewards = calculate_decimal_rewards(state.global_index, holder.index, holder.balance)?; 153 | 154 | holder.index = state.global_index; 155 | holder.pending_rewards = decimal_summation_in_256(rewards, holder.pending_rewards); 156 | holder.balance = (holder.balance.checked_sub(amount))?; 157 | state.total_balance = (state.total_balance.checked_sub(amount))?; 158 | 159 | store_holder(deps.storage, &address_raw, &holder)?; 160 | store_state(deps.storage, &state)?; 161 | 162 | let attributes = vec![ 163 | attr("action", "decrease_balance"), 164 | attr("holder_address", address), 165 | attr("amount", amount), 166 | ]; 167 | 168 | let res = Response::new().add_attributes(attributes); 169 | 170 | Ok(res) 171 | } 172 | 173 | pub fn query_accrued_rewards(deps: Deps, address: String) -> StdResult { 174 | let global_index = read_state(deps.storage)?.global_index; 175 | 176 | let holder: Holder = read_holder(deps.storage, &deps.api.addr_canonicalize(&address)?)?; 177 | let reward_with_decimals = 178 | calculate_decimal_rewards(global_index, holder.index, holder.balance)?; 179 | let all_reward_with_decimals = 180 | decimal_summation_in_256(reward_with_decimals, holder.pending_rewards); 181 | 182 | let rewards = all_reward_with_decimals * Uint128::new(1); 183 | 184 | Ok(AccruedRewardsResponse { rewards }) 185 | } 186 | 187 | pub fn query_holder(deps: Deps, address: String) -> StdResult { 188 | let holder: Holder = read_holder(deps.storage, &deps.api.addr_canonicalize(&address)?)?; 189 | Ok(HolderResponse { 190 | address, 191 | balance: holder.balance, 192 | index: holder.index, 193 | pending_rewards: holder.pending_rewards, 194 | }) 195 | } 196 | 197 | pub fn query_holders( 198 | deps: Deps, 199 | start_after: Option, 200 | limit: Option, 201 | ) -> StdResult { 202 | let start_after = if let Some(start_after) = start_after { 203 | Some(deps.api.addr_validate(&start_after)?) 204 | } else { 205 | None 206 | }; 207 | 208 | let holders: Vec = read_holders(deps, start_after, limit)?; 209 | 210 | Ok(HoldersResponse { holders }) 211 | } 212 | 213 | // calculate the reward based on the sender's index and the global index. 214 | fn calculate_decimal_rewards( 215 | global_index: Decimal, 216 | user_index: Decimal, 217 | user_balance: Uint128, 218 | ) -> StdResult { 219 | let decimal_balance = Decimal::from_ratio(user_balance, Uint128::new(1)); 220 | Ok(decimal_multiplication_in_256( 221 | decimal_subtraction_in_256(global_index, user_index), 222 | decimal_balance, 223 | )) 224 | } 225 | 226 | // calculate the reward with decimal 227 | fn get_decimals(value: Decimal) -> StdResult { 228 | let stringed: &str = &*value.to_string(); 229 | let parts: &[&str] = &*stringed.split('.').collect::>(); 230 | match parts.len() { 231 | 1 => Ok(Decimal::zero()), 232 | 2 => { 233 | let decimals = Decimal::from_str(&*("0.".to_owned() + parts[1]))?; 234 | Ok(decimals) 235 | } 236 | _ => Err(StdError::generic_err("Unexpected number of dots")), 237 | } 238 | } 239 | 240 | #[cfg(test)] 241 | mod tests { 242 | use super::*; 243 | 244 | #[test] 245 | pub fn proper_calculate_rewards() { 246 | let global_index = Decimal::from_ratio(Uint128::new(9), Uint128::new(100)); 247 | let user_index = Decimal::zero(); 248 | let user_balance = Uint128::new(1000); 249 | let reward = calculate_decimal_rewards(global_index, user_index, user_balance).unwrap(); 250 | assert_eq!(reward.to_string(), "90"); 251 | } 252 | 253 | #[test] 254 | pub fn proper_get_decimals() { 255 | let global_index = Decimal::from_ratio(Uint128::new(9999999), Uint128::new(100000000)); 256 | let user_index = Decimal::zero(); 257 | let user_balance = Uint128::new(10); 258 | let reward = get_decimals( 259 | calculate_decimal_rewards(global_index, user_index, user_balance).unwrap(), 260 | ) 261 | .unwrap(); 262 | assert_eq!(reward.to_string(), "0.9999999"); 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /contracts/anchor_basset_hub/schema/execute_msg.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "ExecuteMsg", 4 | "anyOf": [ 5 | { 6 | "description": "Owner's operations Set the owener", 7 | "type": "object", 8 | "required": [ 9 | "update_config" 10 | ], 11 | "properties": { 12 | "update_config": { 13 | "type": "object", 14 | "properties": { 15 | "airdrop_registry_contract": { 16 | "type": [ 17 | "string", 18 | "null" 19 | ] 20 | }, 21 | "owner": { 22 | "type": [ 23 | "string", 24 | "null" 25 | ] 26 | }, 27 | "reward_contract": { 28 | "type": [ 29 | "string", 30 | "null" 31 | ] 32 | }, 33 | "token_contract": { 34 | "type": [ 35 | "string", 36 | "null" 37 | ] 38 | } 39 | } 40 | } 41 | }, 42 | "additionalProperties": false 43 | }, 44 | { 45 | "description": "Register receives the reward contract address", 46 | "type": "object", 47 | "required": [ 48 | "register_validator" 49 | ], 50 | "properties": { 51 | "register_validator": { 52 | "type": "object", 53 | "required": [ 54 | "validator" 55 | ], 56 | "properties": { 57 | "validator": { 58 | "type": "string" 59 | } 60 | } 61 | } 62 | }, 63 | "additionalProperties": false 64 | }, 65 | { 66 | "type": "object", 67 | "required": [ 68 | "deregister_validator" 69 | ], 70 | "properties": { 71 | "deregister_validator": { 72 | "type": "object", 73 | "required": [ 74 | "validator" 75 | ], 76 | "properties": { 77 | "validator": { 78 | "type": "string" 79 | } 80 | } 81 | } 82 | }, 83 | "additionalProperties": false 84 | }, 85 | { 86 | "description": "update the parameters that is needed for the contract", 87 | "type": "object", 88 | "required": [ 89 | "update_params" 90 | ], 91 | "properties": { 92 | "update_params": { 93 | "type": "object", 94 | "properties": { 95 | "epoch_period": { 96 | "type": [ 97 | "integer", 98 | "null" 99 | ], 100 | "format": "uint64", 101 | "minimum": 0.0 102 | }, 103 | "er_threshold": { 104 | "anyOf": [ 105 | { 106 | "$ref": "#/definitions/Decimal" 107 | }, 108 | { 109 | "type": "null" 110 | } 111 | ] 112 | }, 113 | "peg_recovery_fee": { 114 | "anyOf": [ 115 | { 116 | "$ref": "#/definitions/Decimal" 117 | }, 118 | { 119 | "type": "null" 120 | } 121 | ] 122 | }, 123 | "unbonding_period": { 124 | "type": [ 125 | "integer", 126 | "null" 127 | ], 128 | "format": "uint64", 129 | "minimum": 0.0 130 | } 131 | } 132 | } 133 | }, 134 | "additionalProperties": false 135 | }, 136 | { 137 | "description": "User's operations Receives `amount` in underlying coin denom from sender. Delegate `amount` to a specific `validator`. Issue `amount` / exchange_rate for the user.", 138 | "type": "object", 139 | "required": [ 140 | "bond" 141 | ], 142 | "properties": { 143 | "bond": { 144 | "type": "object", 145 | "required": [ 146 | "validator" 147 | ], 148 | "properties": { 149 | "validator": { 150 | "type": "string" 151 | } 152 | } 153 | } 154 | }, 155 | "additionalProperties": false 156 | }, 157 | { 158 | "description": "Update global index", 159 | "type": "object", 160 | "required": [ 161 | "update_global_index" 162 | ], 163 | "properties": { 164 | "update_global_index": { 165 | "type": "object", 166 | "properties": { 167 | "airdrop_hooks": { 168 | "type": [ 169 | "array", 170 | "null" 171 | ], 172 | "items": { 173 | "$ref": "#/definitions/Binary" 174 | } 175 | } 176 | } 177 | } 178 | }, 179 | "additionalProperties": false 180 | }, 181 | { 182 | "description": "Send back unbonded coin to the user", 183 | "type": "object", 184 | "required": [ 185 | "withdraw_unbonded" 186 | ], 187 | "properties": { 188 | "withdraw_unbonded": { 189 | "type": "object" 190 | } 191 | }, 192 | "additionalProperties": false 193 | }, 194 | { 195 | "description": "Check whether the slashing has happened or not", 196 | "type": "object", 197 | "required": [ 198 | "check_slashing" 199 | ], 200 | "properties": { 201 | "check_slashing": { 202 | "type": "object" 203 | } 204 | }, 205 | "additionalProperties": false 206 | }, 207 | { 208 | "description": "bAsset's operations Receive interface for send token. Unbond the underlying coin denom. Burn the received basset token.", 209 | "type": "object", 210 | "required": [ 211 | "receive" 212 | ], 213 | "properties": { 214 | "receive": { 215 | "$ref": "#/definitions/Cw20ReceiveMsg" 216 | } 217 | }, 218 | "additionalProperties": false 219 | }, 220 | { 221 | "description": "internal operations", 222 | "type": "object", 223 | "required": [ 224 | "claim_airdrop" 225 | ], 226 | "properties": { 227 | "claim_airdrop": { 228 | "type": "object", 229 | "required": [ 230 | "airdrop_contract", 231 | "airdrop_swap_contract", 232 | "airdrop_token_contract", 233 | "claim_msg", 234 | "swap_msg" 235 | ], 236 | "properties": { 237 | "airdrop_contract": { 238 | "type": "string" 239 | }, 240 | "airdrop_swap_contract": { 241 | "type": "string" 242 | }, 243 | "airdrop_token_contract": { 244 | "type": "string" 245 | }, 246 | "claim_msg": { 247 | "$ref": "#/definitions/Binary" 248 | }, 249 | "swap_msg": { 250 | "$ref": "#/definitions/Binary" 251 | } 252 | } 253 | } 254 | }, 255 | "additionalProperties": false 256 | }, 257 | { 258 | "description": "Swaps claimed airdrop tokens to UST through Terraswap & sends resulting UST to bLuna Reward contract", 259 | "type": "object", 260 | "required": [ 261 | "swap_hook" 262 | ], 263 | "properties": { 264 | "swap_hook": { 265 | "type": "object", 266 | "required": [ 267 | "airdrop_swap_contract", 268 | "airdrop_token_contract", 269 | "swap_msg" 270 | ], 271 | "properties": { 272 | "airdrop_swap_contract": { 273 | "type": "string" 274 | }, 275 | "airdrop_token_contract": { 276 | "type": "string" 277 | }, 278 | "swap_msg": { 279 | "$ref": "#/definitions/Binary" 280 | } 281 | } 282 | } 283 | }, 284 | "additionalProperties": false 285 | } 286 | ], 287 | "definitions": { 288 | "Binary": { 289 | "description": "Binary is a wrapper around Vec to add base64 de/serialization with serde. It also adds some helper methods to help encode inline.\n\nThis is only needed as serde-json-{core,wasm} has a horrible encoding for Vec", 290 | "type": "string" 291 | }, 292 | "Cw20ReceiveMsg": { 293 | "description": "Cw20ReceiveMsg should be de/serialized under `Receive()` variant in a ExecuteMsg", 294 | "type": "object", 295 | "required": [ 296 | "amount", 297 | "msg", 298 | "sender" 299 | ], 300 | "properties": { 301 | "amount": { 302 | "$ref": "#/definitions/Uint128" 303 | }, 304 | "msg": { 305 | "$ref": "#/definitions/Binary" 306 | }, 307 | "sender": { 308 | "type": "string" 309 | } 310 | } 311 | }, 312 | "Decimal": { 313 | "description": "A fixed-point decimal value with 18 fractional digits, i.e. Decimal(1_000_000_000_000_000_000) == 1.0\n\nThe greatest possible value that can be represented is 340282366920938463463.374607431768211455 (which is (2^128 - 1) / 10^18)", 314 | "type": "string" 315 | }, 316 | "Uint128": { 317 | "description": "A thin wrapper around u128 that is using strings for JSON encoding/decoding, such that the full u128 range can be used for clients that convert JSON numbers to floats, like JavaScript and jq.\n\n# Examples\n\nUse `from` to create instances of this and `u128` to get the value out:\n\n``` # use cosmwasm_std::Uint128; let a = Uint128::from(123u128); assert_eq!(a.u128(), 123);\n\nlet b = Uint128::from(42u64); assert_eq!(b.u128(), 42);\n\nlet c = Uint128::from(70u32); assert_eq!(c.u128(), 70); ```", 318 | "type": "string" 319 | } 320 | } 321 | } 322 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS --------------------------------------------------------------------------------