From 894cd05f33d2a530f3e3699f7721953c2213dbf3 Mon Sep 17 00:00:00 2001 From: Vladas Zakrevskis <146100@gmail.com> Date: Tue, 5 Sep 2023 14:53:19 +0300 Subject: [PATCH] Jar coverage (#6) --- .github/workflows/push.yml | 6 +-- .github/workflows/test.yml | 6 +-- Makefile | 24 +++++++++ contract/src/jar/api.rs | 2 +- contract/src/jar/model.rs | 5 +- contract/src/jar/view.rs | 8 ++- contract/src/lib.rs | 47 ++++++++++++++---- contract/src/migration/model.rs | 3 +- contract/src/product/command.rs | 12 ++--- contract/src/product/mod.rs | 20 +++++++- contract/src/withdraw/api.rs | 27 +++++++++- contract/src/withdraw/view.rs | 29 ++++++++++- res/sweat_jar.wasm | Bin 569817 -> 569817 bytes .../build-in-docker.sh | 2 +- build.sh => scripts/build.sh | 0 scripts/coverage.sh | 3 ++ deploy.sh => scripts/deploy.sh | 0 17 files changed, 154 insertions(+), 40 deletions(-) create mode 100644 Makefile rename build-in-docker.sh => scripts/build-in-docker.sh (85%) rename build.sh => scripts/build.sh (100%) create mode 100755 scripts/coverage.sh rename deploy.sh => scripts/deploy.sh (100%) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 24b3f320..8d149acf 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -16,7 +16,7 @@ jobs: uses: actions/checkout@v3 - name: Build - run: ./build-in-docker.sh + run: make build - name: Upload binary uses: actions/upload-artifact@v3 @@ -31,7 +31,7 @@ jobs: - uses: actions/checkout@v3 - name: Unit tests - run: cargo test --all + run: make test integration-tests: needs: [ build ] @@ -47,7 +47,7 @@ jobs: path: res/ - name: Integration tests - run: cargo run -p integration-tests + run: make integration push: needs: [ unit-tests, integration-tests ] diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3067ef82..dab83e4a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,7 @@ jobs: uses: actions/checkout@v3 - name: Build - run: ./build-in-docker.sh + run: make build-in-docker - name: Upload binary uses: actions/upload-artifact@v3 @@ -31,7 +31,7 @@ jobs: - uses: actions/checkout@v3 - name: Unit tests - run: cargo test --all + run: make test integration-tests: needs: [ build ] @@ -47,4 +47,4 @@ jobs: path: res/ - name: Integration tests - run: cargo run -p integration-tests + run: make integration diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..1bd1e505 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +build: + ./scripts/build.sh + +build-in-docker: + ./scripts/build-in-docker.sh + +dock: build-in-docker + +deploy: + ./scripts/deploy.sh + +cov: + ./scripts/coverage.sh + +test: + cargo test --all + +integration: + cargo run -p integration-tests + +int: integration + +fmt: + cargo +nightly fmt --all diff --git a/contract/src/jar/api.rs b/contract/src/jar/api.rs index b9e7ccd1..04c9ead8 100644 --- a/contract/src/jar/api.rs +++ b/contract/src/jar/api.rs @@ -258,8 +258,8 @@ mod signature_tests { get_register_restakable_product_command, }, }, + withdraw::api::WithdrawApi, }; - use crate::withdraw::api::WithdrawApi; // Signature for structure (value -> utf8 bytes): // contract_id: "owner" -> [111, 119, 110, 101, 114] diff --git a/contract/src/jar/model.rs b/contract/src/jar/model.rs index 040e79bc..23e66350 100644 --- a/contract/src/jar/model.rs +++ b/contract/src/jar/model.rs @@ -27,9 +27,8 @@ pub type JarIndex = u32; /// and the ID of the last jar created for the recipient. The concatenation of this data /// forms a message that is then hashed using the SHA-256 algorithm. This resulting hash is used /// to verify the authenticity of the data against an Ed25519 signature provided in the ft_transfer_call data. -#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug)] #[serde(crate = "near_sdk::serde")] -#[cfg_attr(not(target_arch = "wasm32"), derive(PartialEq))] pub struct JarTicket { /// The unique identifier of the product for which the jar is intended to be created. /// This product_id links the request to the specific terms and conditions of the product that will govern the behavior of the jar. @@ -45,7 +44,6 @@ pub struct JarTicket { /// The `Jar` struct represents a deposit jar within the smart contract. #[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Clone, Debug)] #[serde(crate = "near_sdk::serde", rename_all = "snake_case")] -#[cfg_attr(not(target_arch = "wasm32"), derive(PartialEq))] pub struct Jar { /// The index of the jar in the `Contracts.jars` vector. Also serves as the unique identifier for the jar. pub index: JarIndex, @@ -85,7 +83,6 @@ pub struct Jar { /// allowing for efficient interest calculations between state changes. #[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Clone, Debug)] #[serde(crate = "near_sdk::serde")] -#[cfg_attr(not(target_arch = "wasm32"), derive(PartialEq))] pub struct JarCache { pub updated_at: Timestamp, pub interest: TokenAmount, diff --git a/contract/src/jar/view.rs b/contract/src/jar/view.rs index 534c0195..65806335 100644 --- a/contract/src/jar/view.rs +++ b/contract/src/jar/view.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, fmt::Debug}; use near_sdk::{ json_types::{U128, U64}, @@ -10,9 +10,8 @@ use crate::{common::U32, product::model::ProductId, *}; pub type JarIndexView = U32; -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(crate = "near_sdk::serde")] -#[cfg_attr(not(target_arch = "wasm32"), derive(PartialEq))] pub struct JarView { pub index: JarIndexView, pub account_id: AccountId, @@ -37,9 +36,8 @@ impl From for JarView { } } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(crate = "near_sdk::serde")] -#[cfg_attr(not(target_arch = "wasm32"), derive(PartialEq))] pub struct AggregatedTokenAmountView { pub detailed: HashMap, pub total: U128, diff --git a/contract/src/lib.rs b/contract/src/lib.rs index 04770d0f..d082cdba 100644 --- a/contract/src/lib.rs +++ b/contract/src/lib.rs @@ -80,6 +80,8 @@ impl Contract { #[cfg(test)] mod tests { + use std::collections::HashMap; + use common::tests::Context; use near_sdk::{ json_types::{U128, U64}, @@ -90,7 +92,11 @@ mod tests { use crate::{ claim::api::ClaimApi, common::U32, - jar::{api::JarApi, model::JarTicket}, + jar::{ + api::JarApi, + model::JarTicket, + view::{AggregatedTokenAmountView, JarView}, + }, penalty::api::PenaltyApi, product::{ api::*, @@ -218,7 +224,9 @@ mod tests { let context = Context::new(admin); let interest = context.contract.get_total_interest(alice); + assert_eq!(interest.total.0, 0); + assert_eq!(interest.detailed, HashMap::new()); } #[test] @@ -234,7 +242,7 @@ mod tests { }); context.switch_account_to_owner(); - context.contract.create_jar( + let jar = context.contract.create_jar( alice.clone(), JarTicket { product_id: get_product().id, @@ -244,10 +252,15 @@ mod tests { None, ); + let contract_jar = JarView::from(context.contract.jars.get(0).unwrap().clone()); + assert_eq!(jar, contract_jar); + context.set_block_timestamp_in_minutes(30); - let interest = context.contract.get_total_interest(alice).total.0; - assert_eq!(interest, 684); + let interest = context.contract.get_total_interest(alice); + + assert_eq!(interest.total.0, 684); + assert_eq!(interest.detailed, HashMap::from([(U32(0), U128(684))])) } #[test] @@ -263,7 +276,7 @@ mod tests { }); context.switch_account_to_owner(); - context.contract.create_jar( + let jar = context.contract.create_jar( alice.clone(), JarTicket { product_id: get_product().id, @@ -273,10 +286,20 @@ mod tests { None, ); + let contract_jar = JarView::from(context.contract.jars.get(0).unwrap().clone()); + assert_eq!(jar, contract_jar); + context.set_block_timestamp_in_days(365); - let interest = context.contract.get_total_interest(alice).total.0; - assert_eq!(interest, 12_000_000); + let interest = context.contract.get_total_interest(alice); + + assert_eq!( + interest, + AggregatedTokenAmountView { + detailed: [(U32(0), U128(12_000_000))].into(), + total: U128(12_000_000) + } + ) } #[test] @@ -292,7 +315,7 @@ mod tests { }); context.switch_account_to_owner(); - context.contract.create_jar( + let jar = context.contract.create_jar( alice.clone(), JarTicket { product_id: get_product().id, @@ -302,6 +325,9 @@ mod tests { None, ); + let contract_jar = JarView::from(context.contract.jars.get(0).unwrap().clone()); + assert_eq!(jar, contract_jar); + context.set_block_timestamp_in_days(400); let interest = context.contract.get_total_interest(alice).total.0; @@ -321,7 +347,7 @@ mod tests { }); context.switch_account_to_owner(); - context.contract.create_jar( + let jar = context.contract.create_jar( alice.clone(), JarTicket { product_id: get_product().id, @@ -331,6 +357,9 @@ mod tests { None, ); + let contract_jar = JarView::from(context.contract.jars.get(0).unwrap().clone()); + assert_eq!(jar, contract_jar); + context.set_block_timestamp_in_days(182); let mut interest = context.contract.get_total_interest(alice.clone()).total.0; diff --git a/contract/src/migration/model.rs b/contract/src/migration/model.rs index 84fcfe79..6fc99916 100644 --- a/contract/src/migration/model.rs +++ b/contract/src/migration/model.rs @@ -7,9 +7,8 @@ use near_sdk::{ use crate::product::model::ProductId; -#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Clone)] +#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Debug, PartialEq, Clone)] #[serde(crate = "near_sdk::serde")] -#[cfg_attr(not(target_arch = "wasm32"), derive(Debug, PartialEq))] pub struct CeFiJar { pub id: String, pub account_id: AccountId, diff --git a/contract/src/product/command.rs b/contract/src/product/command.rs index afc6776c..faa6e2bd 100644 --- a/contract/src/product/command.rs +++ b/contract/src/product/command.rs @@ -9,9 +9,8 @@ use crate::{ product::model::{Apy, Cap, DowngradableApy, FixedProductTerms, Product, ProductId, Terms, WithdrawalFee}, }; -#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Clone, Debug)] +#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, PartialEq, Clone, Debug)] #[serde(crate = "near_sdk::serde")] -#[cfg_attr(not(target_arch = "wasm32"), derive(PartialEq))] pub struct RegisterProductCommand { pub id: ProductId, pub apy_default: (U128, u32), @@ -56,17 +55,15 @@ impl From for Product { } } -#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Clone, Debug)] +#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, PartialEq, Clone, Debug)] #[serde(crate = "near_sdk::serde", tag = "type", content = "data", rename_all = "snake_case")] -#[cfg_attr(not(target_arch = "wasm32"), derive(PartialEq))] pub enum TermsDto { Fixed(FixedProductTermsDto), Flexible, } -#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Clone, Debug)] +#[derive(BorshDeserialize, BorshSerialize, Serialize, PartialEq, Deserialize, Clone, Debug)] #[serde(crate = "near_sdk::serde")] -#[cfg_attr(not(target_arch = "wasm32"), derive(PartialEq))] pub struct FixedProductTermsDto { pub lockup_term: U64, pub allows_top_up: bool, @@ -86,9 +83,8 @@ impl From for Terms { } } -#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Clone, Debug)] +#[derive(BorshDeserialize, BorshSerialize, Serialize, Deserialize, Clone, PartialEq, Debug)] #[serde(crate = "near_sdk::serde", tag = "type", content = "data", rename_all = "snake_case")] -#[cfg_attr(not(target_arch = "wasm32"), derive(PartialEq))] pub enum WithdrawalFeeDto { /// Fixed amount of tokens which a user will pay on tokens withdraw Fix(U128), diff --git a/contract/src/product/mod.rs b/contract/src/product/mod.rs index aedf580a..e614a128 100644 --- a/contract/src/product/mod.rs +++ b/contract/src/product/mod.rs @@ -14,7 +14,7 @@ pub(crate) mod tests { common::{tests::Context, UDecimal}, product::{ api::ProductApi, - command::{FixedProductTermsDto, RegisterProductCommand, TermsDto}, + command::{FixedProductTermsDto, RegisterProductCommand, TermsDto, WithdrawalFeeDto}, model::{Apy, Cap, DowngradableApy, FixedProductTerms, Product, Terms}, }, }; @@ -45,6 +45,24 @@ pub(crate) mod tests { } } + pub(crate) fn get_fee_product_command(fee: WithdrawalFeeDto) -> RegisterProductCommand { + RegisterProductCommand { + id: "product_with_fee".to_string(), + apy_default: (U128(12), 2), + apy_fallback: None, + cap_min: U128(100), + cap_max: U128(100_000_000_000), + terms: TermsDto::Fixed(FixedProductTermsDto { + lockup_term: U64(365 * 24 * 60 * 60 * 1000), + allows_restaking: false, + allows_top_up: false, + }), + withdrawal_fee: Some(fee), + public_key: None, + is_enabled: true, + } + } + pub(crate) fn get_register_product_command() -> RegisterProductCommand { RegisterProductCommand { id: "product".to_string(), diff --git a/contract/src/withdraw/api.rs b/contract/src/withdraw/api.rs index 9813a720..59663140 100644 --- a/contract/src/withdraw/api.rs +++ b/contract/src/withdraw/api.rs @@ -183,8 +183,9 @@ mod tests { jar::{api::JarApi, model::JarTicket}, product::{ api::ProductApi, + command::WithdrawalFeeDto, model::Product, - tests::{get_register_flexible_product_command, get_register_product_command}, + tests::{get_fee_product_command, get_register_flexible_product_command, get_register_product_command}, }, withdraw::api::WithdrawApi, }; @@ -403,4 +404,28 @@ mod tests { context.contract.withdraw(U32(0), Some(U128(2_000_000))); } + + #[test] + fn product_with_fee() { + // let alice = accounts(0); + // let admin = accounts(1); + // let mut context = Context::new(admin.clone()); + // + // let product: Product = get_fee_product_command(WithdrawalFeeDto::Fix(U128(10))).into(); + // context.switch_account(&admin); + // context.with_deposit_yocto(1, |context| { + // context + // .contract + // .register_product(get_register_flexible_product_command()) + // }); + // + // let ticket = JarTicket { + // product_id: product.id, + // valid_until: U64(0), + // }; + // + // context + // .contract + // .create_jar(alice.clone(), ticket, U128(1_000_000), None); + } } diff --git a/contract/src/withdraw/view.rs b/contract/src/withdraw/view.rs index 5492230f..eaddcef3 100644 --- a/contract/src/withdraw/view.rs +++ b/contract/src/withdraw/view.rs @@ -6,9 +6,8 @@ use near_sdk::{ use crate::{common::TokenAmount, ft_interface::Fee}; /// The `WithdrawView` struct represents the result of a deposit jar withdrawal operation. -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Debug, PartialEq)] #[serde(crate = "near_sdk::serde")] -#[cfg_attr(not(target_arch = "wasm32"), derive(PartialEq))] pub struct WithdrawView { /// The amount of tokens that has been transferred to the user's account as part of the withdrawal. withdrawn_amount: U128, @@ -27,3 +26,29 @@ impl WithdrawView { } } } + +#[cfg(test)] +mod test { + use near_sdk::{json_types::U128, AccountId}; + + use crate::{ft_interface::Fee, withdraw::view::WithdrawView}; + + #[test] + fn withdrawal_view() { + let fee = WithdrawView::new( + 1_000_000, + Some(Fee { + beneficiary_id: AccountId::new_unchecked("account_id".to_string()), + amount: 100, + }), + ); + + assert_eq!( + fee, + WithdrawView { + withdrawn_amount: U128(1_000_000 - 100), + fee: U128(100), + } + ); + } +} diff --git a/res/sweat_jar.wasm b/res/sweat_jar.wasm index c8d4306438a2b798686981fc0abe12c0f7e5693b..864dd6cad693730739e5aca4712ec580dff4e5ca 100755 GIT binary patch delta 31 ncmcclSn1|trG^&97N!>F7M2#)7Pc+ynQs|Iw`aX&&p!eH)+h|w delta 31 ncmcclSn1|trG^&97N!>F7M2#)7Pc+ynQs}zwr9O%&p!eH)-Md& diff --git a/build-in-docker.sh b/scripts/build-in-docker.sh similarity index 85% rename from build-in-docker.sh rename to scripts/build-in-docker.sh index 92880c52..050051b9 100755 --- a/build-in-docker.sh +++ b/scripts/build-in-docker.sh @@ -7,4 +7,4 @@ docker run \ --mount type=bind,source=$HOST_DIR,target=/host \ --cap-add=SYS_PTRACE --security-opt seccomp=unconfined \ -t nearprotocol/contract-builder:latest-amd64 \ - /bin/bash -c "cd /host && ./build.sh" + /bin/bash -c "cd /host && make build" diff --git a/build.sh b/scripts/build.sh similarity index 100% rename from build.sh rename to scripts/build.sh diff --git a/scripts/coverage.sh b/scripts/coverage.sh new file mode 100755 index 00000000..b141ce85 --- /dev/null +++ b/scripts/coverage.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +cargo llvm-cov --hide-instantiations --open diff --git a/deploy.sh b/scripts/deploy.sh similarity index 100% rename from deploy.sh rename to scripts/deploy.sh