diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index e00b9f89..1b574d98 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -8,6 +8,11 @@ env: CARGO_TERM_COLOR: always jobs: + typos: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: crate-ci/typos@v1.21.0 lint: runs-on: ubuntu-latest @@ -80,7 +85,7 @@ jobs: path: measured.txt push: - needs: [ build, lint, unit-tests, integration-tests, measure, mutation-tests ] + needs: [ build, typos, lint, unit-tests, integration-tests, measure, mutation-tests ] runs-on: ubuntu-latest steps: - name: Checkout diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 05cc318d..7d6d4f7e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,6 +8,11 @@ env: CARGO_TERM_COLOR: always jobs: + typos: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: crate-ci/typos@v1.21.0 lint: runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 0cf6ec4b..f44caf7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3797,7 +3797,6 @@ name = "sweat-jar-model" version = "1.0.1" dependencies = [ "anyhow", - "async-trait", "near-sdk 5.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "near-workspaces", "nitka 0.4.0", diff --git a/Cargo.toml b/Cargo.toml index 892f1d43..e8fa3149 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,6 @@ members = ["model", "contract", "integration-tests"] [workspace.dependencies] anyhow = "1.0.75" -async-trait = "0.1.74" fake = "2.8.0" rand = "0.8.5" futures = "0.3.28" @@ -16,8 +15,8 @@ ed25519-dalek = { version = "2.0.0", features = ["rand_core"] } base64 = "0.22.0" sha256 = "1.3.0" mutants = "0.0.3" -serde = "1.0" sha2 = "0.10" +serde = "1.0" nitka = "0.4.0" nitka-proc = "0.4.0" diff --git a/contract/src/integration_test/integration_test.rs b/contract/src/integration_test/integration_test.rs index 03040305..f15192ec 100644 --- a/contract/src/integration_test/integration_test.rs +++ b/contract/src/integration_test/integration_test.rs @@ -1,14 +1,50 @@ #![cfg(feature = "integration-test")] -use near_sdk::{env, near_bindgen, Timestamp}; -use sweat_jar_model::api::IntegrationTestMethods; +use near_sdk::{env, near_bindgen, AccountId, Timestamp}; +use sweat_jar_model::{api::IntegrationTestMethods, jar::JarView, ProductId}; -use crate::{Contract, ContractExt}; +use crate::{jar::model::Jar, Contract, ContractExt}; +#[mutants::skip] #[near_bindgen] impl IntegrationTestMethods for Contract { - #[mutants::skip] fn block_timestamp_ms(&self) -> Timestamp { env::block_timestamp_ms() } + + fn bulk_create_jars( + &mut self, + account_id: AccountId, + product_id: ProductId, + principal: u128, + number_of_jars: u16, + ) -> Vec { + self.assert_manager(); + (0..number_of_jars) + .map(|_| self.create_jar_for_integration_tests(&account_id, &product_id, principal)) + .collect() + } +} + +#[mutants::skip] +impl Contract { + fn create_jar_for_integration_tests( + &mut self, + account_id: &AccountId, + product_id: &ProductId, + amount: u128, + ) -> JarView { + let product = self.get_product(&product_id); + + product.assert_enabled(); + product.assert_cap(amount); + + let id = self.increment_and_get_last_jar_id(); + let now = env::block_timestamp_ms(); + let jar = Jar::create(id, account_id.clone(), product_id.clone(), amount, now); + + self.add_new_jar(account_id, jar.clone()); + + jar.into() + } } diff --git a/contract/src/internal.rs b/contract/src/internal.rs index 8ea7d577..7b4c2cee 100644 --- a/contract/src/internal.rs +++ b/contract/src/internal.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, fmt::Display}; use near_sdk::require; use sweat_jar_model::{ @@ -74,10 +74,30 @@ impl Contract { } } +pub(crate) fn assert_gas(gas_needed: u64, error: impl FnOnce() -> Message) { + let gas_left = env::prepaid_gas().as_gas() - env::used_gas().as_gas(); + + if gas_left < gas_needed { + let error = error(); + + env::panic_str(&format!( + r#"Not enough gas left. Consider attaching more gas to the transaction. + {error} + Gas left: {gas_left} Needed: {gas_needed}. Need additional {} gas"#, + gas_needed - gas_left + )); + } +} + #[cfg(test)] mod test { + use near_sdk::env; - use crate::{common::tests::Context, test_utils::admin}; + use crate::{ + common::tests::Context, + internal::assert_gas, + test_utils::{admin, expect_panic}, + }; #[test] #[should_panic(expected = r#"Can be performed only by admin"#)] @@ -86,4 +106,33 @@ mod test { let context = Context::new(admin); context.contract().update_contract(vec![], None); } + + #[test] + fn test_assert_gas() { + const GAS_FOR_ASSERT_CALL: u64 = 529536222; + + expect_panic( + &(), + "Not enough gas left. Consider attaching more gas to the transaction.", + || { + assert_gas(u64::MAX, || "Error message"); + }, + ); + + let gas_left = env::prepaid_gas().as_gas() - env::used_gas().as_gas(); + expect_panic(&(), &format!("Need additional {GAS_FOR_ASSERT_CALL} gas"), || { + assert_gas(gas_left, || "Error message"); + }); + + let gas_left = env::prepaid_gas().as_gas() - env::used_gas().as_gas(); + expect_panic(&(), "Need additional 1 gas", || { + assert_gas(gas_left - GAS_FOR_ASSERT_CALL + 1, || "Error message"); + }); + + let gas_left = env::prepaid_gas().as_gas() - env::used_gas().as_gas(); + assert_gas(gas_left - GAS_FOR_ASSERT_CALL, || "Error message"); + + let gas_left = env::prepaid_gas().as_gas() - env::used_gas().as_gas(); + assert_gas(gas_left - GAS_FOR_ASSERT_CALL - 1, || "Error message"); + } } diff --git a/contract/src/jar/api.rs b/contract/src/jar/api.rs index ee6be114..272c9727 100644 --- a/contract/src/jar/api.rs +++ b/contract/src/jar/api.rs @@ -14,7 +14,7 @@ use crate::{ }; impl Contract { - fn can_be_restacked(&self, jar: &Jar, now: u64) -> bool { + fn can_be_restaked(&self, jar: &Jar, now: u64) -> bool { let product = self.get_product(&jar.product_id); !jar.is_empty() && product.is_enabled && product.allows_restaking() && jar.is_liquidable(&product, now) } @@ -170,7 +170,7 @@ impl JarApi for Contract { }) .jars .iter() - .filter(|j| self.can_be_restacked(j, now)) + .filter(|j| self.can_be_restaked(j, now)) .cloned() .collect(); diff --git a/contract/src/jar/tests/restake_all.rs b/contract/src/jar/tests/restake_all.rs index 1b51a3f4..44f8a828 100644 --- a/contract/src/jar/tests/restake_all.rs +++ b/contract/src/jar/tests/restake_all.rs @@ -13,47 +13,46 @@ fn restake_all() { let alice = alice(); let admin = admin(); - let restackable_product = generate_product("restakable_product") + let restakable_product = generate_product("restakable_product") .with_allows_restaking(true) .lockup_term(MS_IN_YEAR); - let disabled_restackable_product = generate_product("disabled_restackable_product") + let disabled_restakable_product = generate_product("disabled_restakable_product") .with_allows_restaking(true) .enabled(false) .lockup_term(MS_IN_YEAR); - let non_restackable_product = generate_product("non_restakable_product") + let non_restakable_product = generate_product("non_restakable_product") .with_allows_restaking(false) .lockup_term(MS_IN_YEAR); - let long_term_restackable_product = generate_product("long_term_restackable_product") + let long_term_restakable_product = generate_product("long_term_restakable_product") .with_allows_restaking(true) .lockup_term(MS_IN_YEAR * 2); - let restackable_jar_1 = Jar::generate(JAR_ID_RANGE.fake(), &alice, &restackable_product.id).principal(PRINCIPAL); - let restackable_jar_2 = Jar::generate(JAR_ID_RANGE.fake(), &alice, &restackable_product.id).principal(PRINCIPAL); + let restakable_jar_1 = Jar::generate(JAR_ID_RANGE.fake(), &alice, &restakable_product.id).principal(PRINCIPAL); + let restakable_jar_2 = Jar::generate(JAR_ID_RANGE.fake(), &alice, &restakable_product.id).principal(PRINCIPAL); - let disabled_jar = - Jar::generate(JAR_ID_RANGE.fake(), &alice, &disabled_restackable_product.id).principal(PRINCIPAL); + let disabled_jar = Jar::generate(JAR_ID_RANGE.fake(), &alice, &disabled_restakable_product.id).principal(PRINCIPAL); - let non_restackable_jar = - Jar::generate(JAR_ID_RANGE.fake(), &alice, &non_restackable_product.id).principal(PRINCIPAL); + let non_restakable_jar = + Jar::generate(JAR_ID_RANGE.fake(), &alice, &non_restakable_product.id).principal(PRINCIPAL); let long_term_jar = - Jar::generate(JAR_ID_RANGE.fake(), &alice, &long_term_restackable_product.id).principal(PRINCIPAL); + Jar::generate(JAR_ID_RANGE.fake(), &alice, &long_term_restakable_product.id).principal(PRINCIPAL); let mut context = Context::new(admin) .with_products(&[ - restackable_product, - disabled_restackable_product, - non_restackable_product, - long_term_restackable_product, + restakable_product, + disabled_restakable_product, + non_restakable_product, + long_term_restakable_product, ]) .with_jars(&[ - restackable_jar_1.clone(), - restackable_jar_2.clone(), + restakable_jar_1.clone(), + restakable_jar_2.clone(), disabled_jar.clone(), - non_restackable_jar.clone(), + non_restakable_jar.clone(), long_term_jar.clone(), ]); @@ -61,20 +60,20 @@ fn restake_all() { context.switch_account(&alice); - let restacked_jars = context.contract().restake_all(); + let restaked_jars = context.contract().restake_all(); - assert_eq!(restacked_jars.len(), 2); - assert_eq!(restacked_jars.iter().map(|j| j.id.0).collect::>(), vec![1, 2]); + assert_eq!(restaked_jars.len(), 2); + assert_eq!(restaked_jars.iter().map(|j| j.id.0).collect::>(), vec![1, 2]); let all_jars = context.contract().get_jars_for_account(alice); assert_eq!( all_jars.iter().map(|j| j.id.0).collect::>(), [ - restackable_jar_1.id, - restackable_jar_2.id, + restakable_jar_1.id, + restakable_jar_2.id, disabled_jar.id, - non_restackable_jar.id, + non_restakable_jar.id, long_term_jar.id, 1, 2, diff --git a/contract/src/test_utils.rs b/contract/src/test_utils.rs index d80d6bcd..a12a8a50 100644 --- a/contract/src/test_utils.rs +++ b/contract/src/test_utils.rs @@ -79,6 +79,10 @@ pub trait AfterCatchUnwind { fn after_catch_unwind(&self); } +impl AfterCatchUnwind for () { + fn after_catch_unwind(&self) {} +} + pub fn expect_panic(ctx: &impl AfterCatchUnwind, msg: &str, action: impl FnOnce() + UnwindSafe) { let res = catch_unwind(move || action()); diff --git a/contract/src/withdraw/api.rs b/contract/src/withdraw/api.rs index 1cca4da1..52de5786 100644 --- a/contract/src/withdraw/api.rs +++ b/contract/src/withdraw/api.rs @@ -1,5 +1,4 @@ use near_sdk::{ - env::panic_str, ext_contract, is_promise_success, json_types::U128, near_bindgen, @@ -13,7 +12,7 @@ use sweat_jar_model::{ TokenAmount, }; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] #[serde(crate = "near_sdk::serde")] pub struct JarWithdraw { pub jar: Jar, @@ -86,12 +85,11 @@ impl WithdrawApi for Contract { self.migrate_account_jars_if_needed(account_id.clone()); let now = env::block_timestamp_ms(); - let jars: Vec = self - .account_jars - .get(&account_id) - .unwrap_or_else(|| { - panic_str(&format!("Jars for account '{account_id}' don't exist")); - }) + let Some(account_jars) = self.account_jars.get(&account_id) else { + return PromiseOrValue::Value(BulkWithdrawView::default()); + }; + + let jars: Vec = account_jars .jars .clone() .into_iter() @@ -104,6 +102,10 @@ impl WithdrawApi for Contract { let amount = jar.principal; + if amount == 0 { + return None; + } + let mut withdrawn_jar = jar.withdrawn(&product, amount, now); let should_be_closed = withdrawn_jar.should_be_closed(&product, now); @@ -120,6 +122,10 @@ impl WithdrawApi for Contract { }) .collect(); + if jars.is_empty() { + return PromiseOrValue::Value(BulkWithdrawView::default()); + } + self.transfer_bulk_withdraw(&account_id, jars) } } @@ -270,14 +276,11 @@ impl Contract { let total_amount = jars.iter().map(|j| j.amount).sum(); - let gas_left = crate::env::prepaid_gas().as_gas() - crate::env::used_gas().as_gas(); - - if gas_left - < crate::common::gas_data::GAS_FOR_FT_TRANSFER.as_gas() - + crate::common::gas_data::GAS_FOR_BULK_AFTER_WITHDRAW.as_gas() - { - panic_str("Not enough gas left to complete transfer_bulk_withdraw."); - } + crate::internal::assert_gas( + crate::common::gas_data::GAS_FOR_FT_TRANSFER.as_gas() + + crate::common::gas_data::GAS_FOR_BULK_AFTER_WITHDRAW.as_gas(), + || format!("transfer_bulk_withdraw. Number of jars: {}", jars.len()), + ); self.ft_contract() .ft_transfer(account_id, total_amount, "bulk_withdraw", &total_fee) @@ -344,6 +347,7 @@ impl Contract { } #[near_bindgen] +#[mutants::skip] // Covered by integration tests impl WithdrawCallbacks for Contract { #[private] fn after_withdraw( diff --git a/contract/src/withdraw/tests.rs b/contract/src/withdraw/tests.rs index 74a1051f..3998192d 100644 --- a/contract/src/withdraw/tests.rs +++ b/contract/src/withdraw/tests.rs @@ -48,9 +48,7 @@ fn withdraw_locked_jar_before_maturity_by_not_owner() { context.contract().withdraw(U32(0), None); }); - expect_panic(&context, "Jars for account 'owner' don't exist", || { - context.contract().withdraw_all(); - }); + assert_eq!(context.contract().withdraw_all().unwrap().total_amount.0, 0); } #[test] @@ -79,9 +77,7 @@ fn withdraw_locked_jar_after_maturity_by_not_owner() { context.contract().withdraw(U32(jar.id), None); }); - expect_panic(&context, "Jars for account 'owner' don't exist", || { - context.contract().withdraw_all(); - }); + assert_eq!(context.contract().withdraw_all().unwrap().total_amount.0, 0); } #[test] diff --git a/integration-tests/src/context.rs b/integration-tests/src/context.rs index 3aa23e87..06647113 100644 --- a/integration-tests/src/context.rs +++ b/integration-tests/src/context.rs @@ -2,10 +2,14 @@ use anyhow::Result; use near_workspaces::Account; use nitka::{misc::ToNear, near_sdk::json_types::U128}; use sweat_jar_model::{ - api::{InitApiIntegration, JarApiIntegration, ProductApiIntegration, SweatJarContract}, + api::{ + InitApiIntegration, IntegrationTestMethodsIntegration, JarApiIntegration, ProductApiIntegration, + SweatJarContract, + }, jar::JarView, + ProductId, }; -use sweat_model::{StorageManagementIntegration, SweatApiIntegration, SweatContract}; +use sweat_model::{FungibleTokenCoreIntegration, StorageManagementIntegration, SweatApiIntegration, SweatContract}; use crate::product::RegisterProductCommand; @@ -20,7 +24,6 @@ pub trait IntegrationContext { async fn fee(&mut self) -> Result; fn sweat_jar(&self) -> SweatJarContract; fn ft_contract(&self) -> SweatContract; - async fn last_jar_for(&self, account: &Account) -> Result; } impl IntegrationContext for Context { @@ -47,16 +50,6 @@ impl IntegrationContext for Context { contract: &self.contracts[FT_CONTRACT], } } - - async fn last_jar_for(&self, account: &Account) -> Result { - Ok(self - .sweat_jar() - .get_jars_for_account(account.to_near()) - .await? - .into_iter() - .last() - .unwrap()) - } } pub(crate) async fn prepare_contract( @@ -136,3 +129,71 @@ pub(crate) async fn prepare_contract( Ok(context) } + +pub trait ContextHelpers { + async fn last_jar_for(&self, account: &Account) -> Result; + async fn bulk_create_jars( + &mut self, + account: &Account, + product_id: &ProductId, + principal: u128, + number_of_jars: u16, + ) -> Result>; + async fn account_balance(&self, account: &Account) -> Result; +} + +impl ContextHelpers for Context { + async fn last_jar_for(&self, account: &Account) -> Result { + Ok(self + .sweat_jar() + .get_jars_for_account(account.to_near()) + .await? + .into_iter() + .last() + .unwrap()) + } + + async fn bulk_create_jars( + &mut self, + account: &Account, + product_id: &ProductId, + principal: u128, + number_of_jars: u16, + ) -> Result> { + let total_amount = principal * number_of_jars as u128; + + self.ft_contract() + .tge_mint(&account.to_near(), U128(100_000_000_000)) + .await?; + + let account_balance = self.account_balance(account).await?; + assert!( + account_balance > total_amount, + r#" + Account doesn't have enough $SWEAT to create {number_of_jars} jars with {principal} principal. + Required: {total_amount} has: {account_balance} + "#, + ); + + self.ft_contract() + .ft_transfer( + self.sweat_jar().contract.as_account().id().clone(), + total_amount.into(), + None, + ) + .with_user(account) + .await?; + + let manager = self.manager().await?; + + self.sweat_jar() + .bulk_create_jars(account.to_near(), product_id.clone(), principal, number_of_jars) + .with_user(&manager) + .await + } + + async fn account_balance(&self, account: &Account) -> Result { + let balance = self.ft_contract().ft_balance_of(account.to_near()).await?.0; + Ok(balance) + } +} diff --git a/integration-tests/src/restake.rs b/integration-tests/src/restake.rs index ff016b9e..da813fc9 100644 --- a/integration-tests/src/restake.rs +++ b/integration-tests/src/restake.rs @@ -3,7 +3,7 @@ use nitka::{misc::ToNear, set_integration_logs_enabled}; use sweat_jar_model::api::{ClaimApiIntegration, JarApiIntegration}; use crate::{ - context::{prepare_contract, IntegrationContext}, + context::{prepare_contract, ContextHelpers, IntegrationContext}, jar_contract_extensions::JarContractExtensions, product::RegisterProductCommand, }; @@ -68,6 +68,7 @@ async fn restake() -> Result<()> { #[mutants::skip] async fn restake_all() -> Result<()> { const PRINCIPAL: u128 = 1_000_000; + const JARS_COUNT: u16 = 210; println!("👷🏽 Run test for restake all"); @@ -103,6 +104,10 @@ async fn restake_all() -> Result<()> { let jar_10_min = context.last_jar_for(&alice).await?; assert_eq!(jar_10_min.principal.0, PRINCIPAL + 3); + context + .bulk_create_jars(&alice, &product_5_min.id(), PRINCIPAL, JARS_COUNT) + .await?; + let claimed = context.sweat_jar().claim_total(None).await?; assert_eq!(claimed.get_total().0, 0); @@ -110,20 +115,24 @@ async fn restake_all() -> Result<()> { context.sweat_jar().claim_total(None).with_user(&alice).await?; - let restacked = context.sweat_jar().restake_all().with_user(&alice).await?; + let restaked = context.sweat_jar().restake_all().with_user(&alice).await?; // 212 jars: ⛽ 91 TGas 566 GGas total: 91566686658202. 1 jar: ⛽ 6 TGas 410 GGas total: 6410903482276 + + assert_eq!(restaked.len(), 212); assert_eq!( - restacked.into_iter().map(|j| j.principal).collect::>(), + restaked.into_iter().map(|j| j.principal).collect::>()[..2], vec![jar_5_min_1.principal, jar_5_min_2.principal] ); let jars = context.sweat_jar().get_jars_for_account(alice.to_near()).await?; - assert_eq!(jars.iter().map(|j| j.id.0).collect::>(), vec![3, 4, 5]); + let principals = jars.iter().map(|j| j.principal.0).collect::>(); - assert_eq!( - jars.iter().map(|j| j.principal.0).collect::>(), - vec![PRINCIPAL + 3, PRINCIPAL + 1, PRINCIPAL + 2] + assert!( + [PRINCIPAL + 3, PRINCIPAL + 1, PRINCIPAL + 2] + .iter() + .all(|p| principals.contains(p)), + "Can't find all expected principals in {principals:?}" ); Ok(()) diff --git a/integration-tests/src/withdraw_all.rs b/integration-tests/src/withdraw_all.rs index f61c144a..48bf4f25 100644 --- a/integration-tests/src/withdraw_all.rs +++ b/integration-tests/src/withdraw_all.rs @@ -1,11 +1,10 @@ use anyhow::Result; -use near_workspaces::types::Gas; use nitka::{misc::ToNear, set_integration_logs_enabled}; use sweat_jar_model::api::{ClaimApiIntegration, JarApiIntegration, WithdrawApiIntegration}; use sweat_model::FungibleTokenCoreIntegration; use crate::{ - context::{prepare_contract, IntegrationContext}, + context::{prepare_contract, ContextHelpers, IntegrationContext}, jar_contract_extensions::JarContractExtensions, product::RegisterProductCommand, }; @@ -14,6 +13,8 @@ use crate::{ #[mutants::skip] async fn withdraw_all() -> Result<()> { const PRINCIPAL: u128 = 1_000_000; + const JARS_COUNT: u16 = 210; + const BULK_PRINCIPAL: u128 = PRINCIPAL * JARS_COUNT as u128; println!("👷🏽 Run test for withdraw all"); @@ -42,6 +43,10 @@ async fn withdraw_all() -> Result<()> { let jar_5_min_2 = context.last_jar_for(&alice).await?; assert_eq!(jar_5_min_2.principal.0, PRINCIPAL + 2); + context + .bulk_create_jars(&alice, &product_5_min.id(), PRINCIPAL, JARS_COUNT) + .await?; + context .sweat_jar() .create_jar(&alice, product_10_min.id(), PRINCIPAL + 3, &context.ft_contract()) @@ -62,14 +67,6 @@ async fn withdraw_all() -> Result<()> { .ft_balance_of(context.sweat_jar().contract.as_account().to_near()) .await?; - context - .sweat_jar() - .withdraw_all() - .with_user(&alice) - .gas(Gas::from_tgas(5)) - .expect_error("Not enough gas left to complete transfer_bulk_withdraw") - .await?; - let withdrawn = context.sweat_jar().withdraw_all().with_user(&alice).await?; let alice_balance_after = context.ft_contract().ft_balance_of(alice.to_near()).await?; @@ -78,13 +75,13 @@ async fn withdraw_all() -> Result<()> { .ft_balance_of(context.sweat_jar().contract.as_account().to_near()) .await?; - assert_eq!(alice_balance_after.0 - alice_balance.0, 2000003); - assert_eq!(jar_balance.0 - jar_balance_after.0, 2000003); + assert_eq!(alice_balance_after.0 - alice_balance.0, BULK_PRINCIPAL + 2000003); + assert_eq!(jar_balance.0 - jar_balance_after.0, BULK_PRINCIPAL + 2000003); - assert_eq!(withdrawn.total_amount.0, 2000003); + assert_eq!(withdrawn.total_amount.0, BULK_PRINCIPAL + 2000003); assert_eq!( - withdrawn.jars.iter().map(|j| j.withdrawn_amount).collect::>(), + withdrawn.jars.iter().map(|j| j.withdrawn_amount).collect::>()[..2], vec![jar_5_min_1.principal, jar_5_min_2.principal] ); diff --git a/model/Cargo.toml b/model/Cargo.toml index 24606c9f..6d7ee6e7 100644 --- a/model/Cargo.toml +++ b/model/Cargo.toml @@ -19,7 +19,6 @@ release-api = [] [dependencies] anyhow = { workspace = true } near-sdk = { workspace = true } -async-trait = { workspace = true } nitka-proc = { workspace = true } nitka = { workspace = true, optional = true } diff --git a/model/src/api.rs b/model/src/api.rs index 66469970..7c79cf23 100644 --- a/model/src/api.rs +++ b/model/src/api.rs @@ -292,4 +292,11 @@ pub trait WithdrawApi { #[make_integration_version] pub trait IntegrationTestMethods { fn block_timestamp_ms(&self) -> near_sdk::Timestamp; + fn bulk_create_jars( + &mut self, + account_id: AccountId, + product_id: ProductId, + principal: u128, + number_of_jars: u16, + ) -> Vec; } diff --git a/model/src/withdraw.rs b/model/src/withdraw.rs index 1143b8df..f5ca2e22 100644 --- a/model/src/withdraw.rs +++ b/model/src/withdraw.rs @@ -18,7 +18,7 @@ pub struct WithdrawView { pub fee: U128, } -#[derive(Debug)] +#[derive(Debug, Default)] #[near(serializers=[borsh, json])] pub struct BulkWithdrawView { pub total_amount: U128, diff --git a/res/sweat_jar.wasm b/res/sweat_jar.wasm index 1951361f..ae32ab3f 100755 Binary files a/res/sweat_jar.wasm and b/res/sweat_jar.wasm differ diff --git a/typos.toml b/typos.toml new file mode 100644 index 00000000..0f2b4de9 --- /dev/null +++ b/typos.toml @@ -0,0 +1,3 @@ +[default.extend-words] +# TGE - Token Generation Event +tge = "tge"