Skip to content

Commit

Permalink
Add integration tests for Products (#27)
Browse files Browse the repository at this point in the history
* Add test interface for all contract methods

* Return from create_jar

* Add error handling

* Add tests for alterating products

* Add tests for premium product

* Clippy

* Fix tests

* Fixes

* Add tests for restake

* Update docs
  • Loading branch information
vasyafromrussia authored Sep 25, 2023
1 parent 73378f4 commit e72dbc4
Show file tree
Hide file tree
Showing 19 changed files with 670 additions and 44 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ rand = "0.8.5"
futures = "0.3.28"
near-sdk = "4.1.1"
itertools = "0.11.0"
ed25519-dalek = { version = "2.0.0", features = ["rand_core"] }
base64 = "0.21.3"
sha256 = "1.3.0"
6 changes: 3 additions & 3 deletions contract/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ crate-type = ["cdylib"]
near-sdk = { workspace = true }

near-contract-standards = "4.1.1"
ed25519-dalek = { version = "2.0.0", features = ["rand_core"] }
ed25519-dalek = { workspace = true }
near-self-update = { git = "https://github.com/sweatco/near-self-update.git", rev = "7064db3cdd924efc7fa7c00664920a2b482e7bcf" }

[dev-dependencies]
fake = { workspace = true }
rand = { workspace = true }
sha256 = "1.3.0"
sha256 = { workspace = true }
crypto-hash = "0.3"
base64 = "0.21.3"
base64 = { workspace = true }
12 changes: 6 additions & 6 deletions contract/src/claim/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ use std::cmp;
use near_sdk::{env, ext_contract, is_promise_success, json_types::U128, near_bindgen, AccountId, PromiseOrValue};

use crate::{
common::{TokenAmount, GAS_FOR_AFTER_CLAIM},
common::{u32::U32, TokenAmount, GAS_FOR_AFTER_CLAIM},
event::{emit, ClaimEventItem, EventKind},
ft_interface::FungibleTokenInterface,
jar::model::{Jar, JarId},
jar::{model::Jar, view::JarIdView},
Contract, ContractExt, Promise,
};

Expand Down Expand Up @@ -34,7 +34,7 @@ pub trait ClaimApi {
///
/// A `PromiseOrValue<TokenAmount>` representing the amount of tokens claimed. If the total available interest
/// across the specified jars is zero or the provided `amount` is zero, the returned value will also be zero.
fn claim_jars(&mut self, jar_ids: Vec<JarId>, amount: Option<U128>) -> PromiseOrValue<U128>;
fn claim_jars(&mut self, jar_ids: Vec<JarIdView>, amount: Option<U128>) -> PromiseOrValue<U128>;
}

#[ext_contract(ext_self)]
Expand All @@ -46,18 +46,18 @@ pub trait ClaimCallbacks {
impl ClaimApi for Contract {
fn claim_total(&mut self) -> PromiseOrValue<U128> {
let account_id = env::predecessor_account_id();
let jar_ids = self.account_jars(&account_id).iter().map(|a| a.id).collect();
let jar_ids = self.account_jars(&account_id).iter().map(|a| U32(a.id)).collect();
self.claim_jars(jar_ids, None)
}

fn claim_jars(&mut self, jar_ids: Vec<JarId>, amount: Option<U128>) -> PromiseOrValue<U128> {
fn claim_jars(&mut self, jar_ids: Vec<JarIdView>, amount: Option<U128>) -> PromiseOrValue<U128> {
let account_id = env::predecessor_account_id();
let now = env::block_timestamp_ms();

let unlocked_jars: Vec<Jar> = self
.account_jars(&account_id)
.iter()
.filter(|jar| !jar.is_pending_withdraw && jar_ids.contains(&jar.id))
.filter(|jar| !jar.is_pending_withdraw && jar_ids.contains(&U32(jar.id)))
.cloned()
.collect();

Expand Down
8 changes: 4 additions & 4 deletions contract/src/claim/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fn claim_partially_when_having_tokens_to_claim() {
context.set_block_timestamp_in_days(365);

context.switch_account(&alice);
let PromiseOrValue::Value(claimed) = context.contract.claim_jars(vec![jar.id], Some(U128(100))) else {
let PromiseOrValue::Value(claimed) = context.contract.claim_jars(vec![U32(jar.id)], Some(U128(100))) else {
panic!()
};

Expand All @@ -63,7 +63,7 @@ fn dont_delete_jar_on_all_interest_claim() {
context.set_block_timestamp_in_days(365);

context.switch_account(&alice);
context.contract.claim_jars(vec![jar.id], Some(U128(200_000)));
context.contract.claim_jars(vec![U32(jar.id)], Some(U128(200_000)));

let jar = context.contract.get_jar_internal(&alice, jar.id);
assert_eq!(200_000, jar.claimed_balance);
Expand Down Expand Up @@ -92,7 +92,7 @@ fn claim_all_withdraw_all_and_delete_jar() {
context.set_block_timestamp_in_ms(product.get_lockup_term().unwrap() + 1);

context.switch_account(&alice);
context.contract.claim_jars(vec![jar_id], Some(U128(200_000)));
context.contract.claim_jars(vec![U32(jar_id)], Some(U128(200_000)));

let jar = context.contract.get_jar_internal(&alice, jar_id);
assert_eq!(200_000, jar.claimed_balance);
Expand Down Expand Up @@ -140,7 +140,7 @@ fn withdraw_all_claim_all_and_delete_jar() {

let jar = context.contract.get_jar_internal(&alice, jar_id);

let PromiseOrValue::Value(claimed) = context.contract.claim_jars(vec![jar_id], Some(U128(200_000))) else {
let PromiseOrValue::Value(claimed) = context.contract.claim_jars(vec![U32(jar_id)], Some(U128(200_000))) else {
panic!();
};

Expand Down
7 changes: 4 additions & 3 deletions contract/src/penalty/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use near_sdk::{env, near_bindgen, AccountId};

use crate::{
event::{emit, EventKind::ApplyPenalty, PenaltyData},
jar::model::JarId,
jar::view::JarIdView,
product::model::Apy,
Contract, ContractExt,
};
Expand All @@ -23,14 +23,15 @@ pub trait PenaltyApi {
/// # Panics
///
/// This method will panic if the jar's associated product has a constant APY rather than a downgradable APY.
fn set_penalty(&mut self, account_id: AccountId, jar_id: JarId, value: bool);
fn set_penalty(&mut self, account_id: AccountId, jar_id: JarIdView, value: bool);
}

#[near_bindgen]
impl PenaltyApi for Contract {
fn set_penalty(&mut self, account_id: AccountId, jar_id: JarId, value: bool) {
fn set_penalty(&mut self, account_id: AccountId, jar_id: JarIdView, value: bool) {
self.assert_manager();

let jar_id = jar_id.0;
let jar = self.get_jar_internal(&account_id, jar_id);
let product = self.get_product(&jar.product_id);

Expand Down
2 changes: 0 additions & 2 deletions contract/src/product/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ impl ProductApi for Contract {
emit(EventKind::RegisterProduct(product));
}

// TODO: add integration tests
#[payable]
fn set_enabled(&mut self, product_id: ProductId, is_enabled: bool) {
self.assert_manager();
Expand All @@ -90,7 +89,6 @@ impl ProductApi for Contract {
}));
}

// TODO: add integration tests
#[payable]
fn set_public_key(&mut self, product_id: ProductId, public_key: Base64VecU8) {
self.assert_manager();
Expand Down
4 changes: 2 additions & 2 deletions contract/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ fn penalty_is_not_applicable_for_constant_apy() {
.with_jars(&[reference_jar]);

context.switch_account(&admin);
context.contract.set_penalty(alice, 0, true);
context.contract.set_penalty(alice, U32(0), true);
}

#[test]
Expand Down Expand Up @@ -251,7 +251,7 @@ fn get_total_interest_for_premium_with_penalty_after_half_term() {
assert_eq!(interest, 9_972_602);

context.switch_account(&admin);
context.contract.set_penalty(alice.clone(), 0, true);
context.contract.set_penalty(alice.clone(), U32(0), true);

context.set_block_timestamp_in_days(365);

Expand Down
11 changes: 7 additions & 4 deletions docs/requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,14 +165,17 @@ The `ft_interface.rs` file contains helpers to facilitate interaction with the r

The code in `ft_receiver.rs` handles incoming Token transfers. This mechanism is used for Jar creation, top-ups, and migration.

#### 3.1.4. 🧪 Integration tests
#### 3.1.4. 🌡️ Integration tests

The `./integration-tests` directory contains integration tests for the smart contract.
These tests work with both FT and DeFi Jars contracts, covering the following scenarios:

- **happy_flow.rs:** This scenario represents the successful registration of a product, the creation of a Jar, and the accrual of interest.
- **migration.rs:** These tests focus on the batched migration of CeFi $SWEAT Jars to the contract.
- **withdraw_fee.rs:** These tests deal with withdrawing Jars with fees, checking for the correct transfer of principal to a user account and fees account.
- **[happy_flow.rs](..%2Fintegration-tests%2Fsrc%2Fhappy_flow.rs):** This scenario represents the successful registration of a product, the creation of a Jar, and the accrual of interest.
- **[migration.rs](..%2Fintegration-tests%2Fsrc%2Fmigration.rs):** These tests focus on the batched migration of CeFi $SWEAT Jars to the contract.
- **[premium_product.rs](..%2Fintegration-tests%2Fsrc%2Fpremium_product.rs):** These tests cover mechanics related to Premium Products, including creation with a signature and applying penalties.
- **[product_actions.rs](..%2Fintegration-tests%2Fsrc%2Fproduct_actions.rs):** These tests confirm operations on Products that can be performed by an Admin.
- **[restake.rs](..%2Fintegration-tests%2Fsrc%2Frestake.rs):** These tests examine the creation of a new Jar from another Jar and the cleanup process after claiming from the original Jar.
- **[withdraw_fee.rs](..%2Fintegration-tests%2Fsrc%2Fwithdraw_fee.rs):** These tests deal with withdrawing Jars with fees, checking for the correct transfer of principal to a user account and fees account.

In addition to these files, it also contains utilities and testing data, with the most significant being:

Expand Down
2 changes: 2 additions & 0 deletions integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ rand = { workspace = true }
futures = { workspace = true }
near-sdk = { workspace = true }
itertools = { workspace = true }
ed25519-dalek = { workspace = true }
base64 = { workspace = true }

anyhow = "1.0"
borsh = "0.10.3"
Expand Down
21 changes: 21 additions & 0 deletions integration-tests/src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use std::{
sync::atomic::{AtomicBool, Ordering},
};

use ed25519_dalek::{SigningKey, VerifyingKey};
use rand::rngs::OsRng;
use serde_json::Value;
use workspaces::Account;

Expand All @@ -11,6 +13,7 @@ use crate::{context::Context, product::RegisterProductCommand};
pub trait ValueGetters {
fn get_u128(&self, key: &str) -> u128;
fn get_interest(&self) -> u128;
fn get_jar_id(&self) -> String;
}

impl ValueGetters for Value {
Expand All @@ -29,6 +32,16 @@ impl ValueGetters for Value {
fn get_interest(&self) -> u128 {
self.as_object().unwrap().get("amount").unwrap().get_u128("total")
}

fn get_jar_id(&self) -> String {
self.as_object()
.unwrap()
.get("id")
.unwrap()
.as_str()
.unwrap()
.to_string()
}
}

static CONTRACT_READY: AtomicBool = AtomicBool::new(false);
Expand Down Expand Up @@ -93,3 +106,11 @@ pub(crate) async fn prepare_contract(
fee_account,
})
}

pub(crate) fn generate_keypair() -> (SigningKey, VerifyingKey) {
let mut csprng = OsRng;
let signing_key: SigningKey = SigningKey::generate(&mut csprng);
let verifying_key: VerifyingKey = VerifyingKey::from(&signing_key);

(signing_key, verifying_key)
}
19 changes: 19 additions & 0 deletions integration-tests/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,23 @@ impl Context {

Ok(())
}

pub(crate) fn get_signature_material(
&self,
receiver_id: &Account,
product_id: &String,
valid_until: u64,
amount: u128,
last_jar_id: Option<String>,
) -> String {
format!(
"{},{},{},{},{},{}",
self.jar_contract.account().id(),
receiver_id.id(),
product_id,
amount,
last_jar_id.map_or_else(String::new, |value| value,),
valid_until,
)
}
}
Loading

0 comments on commit e72dbc4

Please sign in to comment.