Canonical page: <https://www.guvenkaya.co/reports/sweat-jars-refactor>

[Work & reports](https://www.guvenkaya.co/work) Report

# Sweat Economy Jars Refactor Security Review

[View PDF on GitHub](https://github.com/Guvenkaya/public-reports/blob/master/The-Sweat-Foundation-Ltd-Jars-Refactor-Rust-Smart-Contract-Security-Assessment.pdf)

![Sweat Economy logo](https://www.guvenkaya.co/v2/clients/sweat.svg)

- All findings

  6

- Critical

  0

- High

  1

- Medium

  2

- Low

  1

- Informational

  2

Date of engagement: 9th December 2024 - 18th December 2024

![Timur Güvenkaya](https://www.guvenkaya.co/v2/portraits/timur-guvenkaya.png)

Security review by

## Timur Guvenkaya

[Meet the team](https://www.guvenkaya.co/about#team)

## About Us

Guvenkaya is a security research firm specializing in Rust security, Web3 security of Rust-based protocols, and Web2 security. With our expertise, we provide both security auditing services and custom security solutions

## About The Sweat

Foundation The Sweat Foundation is an organization behind Sweat Economy, an innovative project at the intersection of fitness and crypto. It motivates users to stay active by converting their steps into SWEAT Token. This approach promotes health and fitness and works as an entry point to crypto for many users.

## Audit Results

Guvenkaya conducted a security assessment of the Sweat Jars smart contract refactor. During this engagement, 3 findings were reported. 1 was High ans 2 Medium severity . The Sweat Foundation team has fixed all the issues.

## Project Scope

| Files                  | Link                                                                                                              |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------- |
| Lib                    | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/contract/src/lib.rs>          |
| Internal               | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/contract/src/internal.rs>     |
| FT Receiver            | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/contract/src/ft_receiver.rs>  |
| FT Interface           | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/contract/src/ft_interface.rs> |
| Event                  | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/contract/src/event.rs>        |
| Assert                 | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/contract/src/assert.rs>       |
| Claim Functionality    | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/contract/src/claim>           |
| Withdraw Functionality | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/contract/src/withdraw>        |
| Restake Functionality  | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/contract/src/restake>         |

| Files                   | Link                                                                                                      |
| ----------------------- | --------------------------------------------------------------------------------------------------------- |
| Score Functionality     | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/contract/src/score>   |
| Product Functionality   | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/contract/src/product> |
| Penalty Functionality   | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/contract/src/product> |
| Migration Functionality | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/contract/src/product> |
| Jar Functionality       | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/contract/src/product> |
| Fee Functionality       | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/contract/src/product> |
| Common Functionality    | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/contract/src/product> |
| Model                   | <https://github.com/sweatco/sweat-jar/tree/ba110ce01f9a104e7d533b24bafe57956976f2a9/model/src>            |

## Out of Scope

The audit will include, but is not limited to, reviewing the code for security vulnerabilities, coding practices, and architecture. The audit does not include a review of the dependencies.

## Timeline

1. Start of the audit

   9th December 2024

2. Draft report

   19th December 2024

3. Final report

   10th January 2024

## Methodology

- RESEARCH INTO PROJECT ARCHITECTURE
- PREPARING ATTACK VECTORS
- SETTING UP AN ENVIRONMENT
- MANUAL CODE REVIEW OF THE CODE
- ASSESSMENT OF RUST SECURITY ISSUES
- ASSESSMENT OF NEAR SECURITY ISSUES
- ASSESSMENT OF ARITHMETIC ISSUES
- BUSINESS LOGIC VULNERABILITY ASSESSMENT
- ONCHAIN TESTING USING NEAR WORKSPACES
- BEST PRACTICES AND CODE QUALITY
- CHECKING FOR CODE REFACTORING/SIMPLIFICATION POSSIBILITIES
- ARCHITECTURE IMPROVEMENT SUGGESTIONS
- PREPARING POCS AND/OR TESTS FOR EACH CRITICAL/HIGH/MEDIUM ISSUES

## Severity Breakdown

### Likelihood Ratings

- Likely

  The vulnerability is easily discoverable and not overly complex to exploit.

- Possible

  The vulnerability presents some challenges either in discovery or in the complexity of the attack.

- Rare

  The vulnerability is either very difficult to discover or complex to exploit, or both. This matrix provides a nuanced view, taking into account both the ease of discovering a vulnerability and the complexity involved in exploiting it.

### Impact

- Severe

  The vulnerability is easily discoverable and not overly complex to exploit.

- Moderate

  The vulnerability presents some challenges either in discovery or in the complexity of the attack.

- Negligible

  The vulnerability is either very difficult to discover or complex to exploit, or both.

### Severity Ratings

- Critical

  Assigned to vulnerabilities with severe impact and a likely likelihood of exploitation.

- High

  For vulnerabilities with either severe impact but only a possible likelihood, or moderate impact with a likely likelihood.

- Medium

  Used for vulnerabilities with severe impact but a rare likelihood, moderate impact with a possible likelihood, or negligible impact with a likely likelihood.

- Low

  For vulnerabilities with moderate impact and rare likelihood, or negligible impact with a possible likelihood.

- Informational

  The lowest severity rating, typically for vulnerabilities with negligible impact and a rare likelihood of exploitation.

### Likelihood Matrix

| Attack Complexity / Discovery Ease | Obvious  | Concealed | Hidden   |
| ---------------------------------- | -------- | --------- | -------- |
| Complex                            | Possible | Rare      | Rare     |
| Moderate                           | Likely   | Possible  | Rare     |
| Straightforward                    | Likely   | Possible  | Possible |

### Likelihood/Impact Matrix

| Likelihood / Impact | Severe   | Moderate | Negligible    |
| ------------------- | -------- | -------- | ------------- |
| Likely              | Critical | High     | Medium        |
| Possible            | High     | Medium   | Low           |
| Rare                | Medium   | Low      | Informational |

## Findings Summary

### Remediation Complexity

This measures how difficult it is to fix the vulnerability once it has been identified.

- Simple

  Patches or fixes are readily available and easily implemented.

- Moderate

  Requires some time and resources to remediate, but well within the capabilities of most organizations.

- Difficult

  Remediation requires significant resources, specialized skills, or substantial changes to systems or architecture.

### Status

This measures how difficult it is to fix the vulnerability once it has been identified.

- Not Fixed

  Indicates that the vulnerability has been identified but no remedial action has been taken yet. This status is crucial for newly discovered vulnerabilities or those awaiting prioritization.

- Fixed

  This status is applied when the vulnerability has been successfully remediated. It implies that appropriate measures (like patching, configuration changes, or architectural modifications) have been implemented to resolve the issue.

- Acknowledged

  This status is used for vulnerabilities that have been recognized, but for various reasons (such as risk acceptance, cost, or other business decisions), have not been fixed. It indicates that the risk posed by the vulnerability is known and has been consciously accepted.

| Finding                                                                | Impact     | Likelihood | Severity      | Remediation complexity | Remediation status |
| ---------------------------------------------------------------------- | ---------- | ---------- | ------------- | ---------------------- | ------------------ |
| [GUV-1: Potential DoS of Batch Operations](#guv-1)                     | Severe     | Possible   | High          | Moderate               | Fixed              |
| [GUV-2: Missing Race Condition Lock](#guv-2)                           | Moderate   | Possible   | Medium        | Simple                 | Fixed              |
| [GUV-3: Missing Score Product Key Enforcement During Creation](#guv-3) | Moderate   | Possible   | Medium        | Simple                 | Fixed              |
| [GUV-4: Suboptimal Rounding Direction](#guv-4)                         | Negligible | Possible   | Low           | Simple                 | Fixed              |
| [GUV-5: Outdated Gas Measurements](#guv-5)                             | Negligible | Rare       | Informational | Simple                 | Fixed              |
| [GUV-6: Redundant State Access](#guv-6)                                | Negligible | Rare       | Informational | Simple                 | Fixed              |

## Findings Details

### GUV-1: Potential DoS of Batch Operations

High

Before a user can create a deposit to a jar, the amount is validated against minimum and maximum caps.

jars:assert\_cap:contract/src/product/model/v1.rs

```
     pub(crate) fn assert_cap(&self, amount: TokenAmount) {
             if self.cap.min > amount || amount > self.cap.max {
                env::panic_str(&format!(
                  "Total amount is out of product bounds: [{}..{}]",
                  self.cap.min, self.cap.max
                 ));
              }
           }
```

After discussion with the team, we discovered that the minimum cap in production is 1 SWEAT. This minimum deposit amount is too low, allowing an attacker to create numerous deposits for any user. This can trigger a gas limit failure in methods like withdraw\_all, claim\_total, and restake\_all, preventing users from performing these operations.

The attack requires approximately $30 in SWEAT and 4,500 deposits. The vulnerability affects all batch operations because the system iterates over all products—even if a user has other products with higher minimum deposits, operations like restake\_all, claim\_total, and withdraw\_all will fail due to the product with excessive deposits.

#### Recommendation

Consider raising the minimum deposit amount to make the attack economically unviable.

#### Remediation - Fixed

The Sweat Foundation team has fixed the issue by throttling requests on the backend side, setting keys for all products, add monitoring for jars' creation and stop issuing tickets for suspicious users

[View this finding in the original PDF](https://www.guvenkaya.co/reports/pdfs/sweat-jars-refactor.pdf#page=11)

### GUV-2: Missing Race Condition Lock

Medium

The restake method is missing an is\_pending\_withdraw lock on jars. This lock prevents race conditions before the callback arrives after a cross-contract call. When a jar has is\_pending\_withdraw set to true, the operation is either skipped or the smart contract returns an error.

jars:assert\_not\_locked:contract/src/assert.rs

```
     pub(crate) fn assert_not_locked(jar: &Jar) {
             require!(!jar.is_pending_withdraw, "Another operation on this Jar is in
     progress");
           }
```

This issue could lead to a scenario where a user's deposit is recorded with a timestamp from the past inside of the cache instead of the actual update time. As a result, interest may begin accumulating earlier than intended.

Possible Scenario:

User deregisters their account on the token contract Calls claim\_total, which sets the cache to current timestamp When the callback arrives, it fails due to the unregistered account and rolls back the cache to the previous timestamp while retaining the new deposit.

#### Recommendation

Consider implementing the is\_pending\_withdraw check in the restake method.

#### Remediation - Fixed

The Sweat Foundation team has fixed the issue by adding the race condition lock in this commit: 1b0c87287cd553c7c210acafbfc3c1d3bd63e131

[View this finding in the original PDF](https://www.guvenkaya.co/reports/pdfs/sweat-jars-refactor.pdf#page=13)

### GUV-3: Missing Score Product Key Enforcement During Creation

Medium

Score-based products are premium products that require a signature. However, during product creation through the register\_product method, the public key requirement is not enforced. This allows users to register score-based products without a public key, enabling deposits without any signature verification.

jars:register\_product:contract/src/product/api.rs

```
      fn register_product(&mut self, command: ProductDto) {
              self.assert_manager();
              assert_one_yocto();
```

assert!(self.products.get(\&command.id).is\_none(), "Product already exists");

```
                let product: Product = command.into();
```

product.assert\_fee\_amount();

self.products.insert(\&product.id, \&product);

```
                emit(EventKind::RegisterProduct(product));
            }
```

#### Recommendation

Consider enforcing public key validation in register\_product when registering score-based products.

#### Remediation - Fixed

The Sweat Foundation team has fixed the issue by asseting the public key existence on score based products in this commit:

ba110ce01f9a104e7d533b24bafe57956976f2a9

[View this finding in the original PDF](https://www.guvenkaya.co/reports/pdfs/sweat-jars-refactor.pdf#page=14)

### GUV-4: Suboptimal Rounding Direction

Low

In the restake\_all method's withdrawal fee calculation, the withdrawal fee is rounded down. This rounding behavior results in a cumulative loss of withdrawal fees for the protocol, though the impact is minimal.

jars:restake\_all:contract/src/restake/api/api.rs

```
      if withdrawal_amount > 0 {
                 let withdrawal_fee = total_fee * withdrawal_amount /
     total_mature_balance;
                   request.withdrawal = WithdrawalDto {
                      amount: withdrawal_amount,
                      fee: withdrawal_fee,
                   };
```

self.transfer\_remainder(request, event)

#### Recommendation

To prevent cumulative losses and potential system exploitation, fees should always be rounded against the user. This can be achieved by using div\_ceil to round up the withdrawal\_fee.

#### Remediation - Fixed

The Sweat Foundation team has fixed the issue by utilizing div\_ceil in this commit: 9bd95fc264d2dd286082f7d704270accef54dcca

[View this finding in the original PDF](https://www.guvenkaya.co/reports/pdfs/sweat-jars-refactor.pdf#page=15)

### GUV-5: Outdated Gas Measurements

Informational

After a recent refactor, the gas measurements for the restake\_all operation have become outdated. This could lead to incorrect assumptions about gas consumption.

jars:measure\_restake\_all:integration-tests/src/measure/restake\_all.rs

async fn measure\_restake\_all() -> Result<()> { set\_integration\_logs\_enabled(false);

```
              let product = RegisterProductCommand::Locked5Minutes60000Percents;
              let mut context = prepare_contract(None, [product]).await?;
              let alice = context.alice().await?;
```

```
              for _ in 0..200 {
                add_jar(&context, &alice, product, 10_000).await?;
              }
```

context.fast\_forward\_minutes(6).await?;

context.sweat\_jar().claim\_total(None).with\_user(\&alice).await?;

```
              let gas = context
                .sweat_jar()
                .restake_all(product.get().id, None)
                .with_user(&alice)
                .result()
                .await?
                .total_gas_burnt;
              dbg!(pretty_gas_string(gas));
```

```
             // 1 jar - 6 TGas 225 GGas total: 6225437862976
             // 100 jars - 50 TGas 709 GGas total: 50709431315947
             // 200 jars - 86 TGas 607 GGas total: 86607517267105...}
```

#### Recommendation

Consider re-running the measurements and updating comments

#### Remediation - Fixed

The Sweat Foundation team has fixed the issue by re-running the measurements.

[View this finding in the original PDF](https://www.guvenkaya.co/reports/pdfs/sweat-jars-refactor.pdf#page=16)

### GUV-6: Redundant State Access

Informational

The code contains a redundant call to get\_or\_create\_account\_mut.

jars:measure\_restake\_all:integration-tests/src/measure/restake\_all.rs

```
      let account = self.get_or_create_account_mut(&account_id);
```

```
                if signature.is_some() {
                   account.nonce += 1;
                }
```

```
                if matches!(product.terms, Terms::ScoreBased(_)) {
                   account.try_set_timezone(ticket.timezone);
                }
```

```
               let account = self.get_or_create_account_mut(&account_id);
```

account.deposit(product\_id, amount, None);

emit(Deposit((product\_id.clone(), amount.into())));

#### Recommendation

Remove the second get\_or\_create\_account\_mut call since the account reference is already available.

#### Remediation - Fixed

The Sweat Foundation team has fixed the issue by removing the redundant call in this commit: d409a301b5900353245d68f113aac27925b52a07

[View this finding in the original PDF](https://www.guvenkaya.co/reports/pdfs/sweat-jars-refactor.pdf#page=18)

Source: [published GitHub report](https://github.com/Guvenkaya/public-reports/blob/master/The-Sweat-Foundation-Ltd-Jars-Refactor-Rust-Smart-Contract-Security-Assessment.pdf) · 18 pages. The original PDF includes the source formatting, figures, and linked references.

## Table of Contents

- [Overview](#overview)
- [About Us](#about-us)
- [About The Sweat](#about-the-sweat)
- [Audit Results](#audit-results)
- [Project Scope](#project-scope)
- [Out of Scope](#out-of-scope)
- [Timeline](#timeline)
- [Methodology](#methodology)
- [Severity Breakdown](#severity-breakdown)
- [Findings Summary](#findings-summary)
- [Findings Details](#findings-details)

## Share

[Share on X](https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.guvenkaya.co%2Freports%2Fsweat-jars-refactor\&text=Sweat%20Economy%3A%20Jars%20Refactor%20Security%20Review) [Share on LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fwww.guvenkaya.co%2Freports%2Fsweat-jars-refactor) [Share on Threads](https://www.threads.net/intent/post?text=Sweat%20Economy%3A%20Jars%20Refactor%20Security%20Review%20https%3A%2F%2Fwww.guvenkaya.co%2Freports%2Fsweat-jars-refactor)

[Discuss a security review](https://www.guvenkaya.co/contact?source=report) [View PDF on GitHub](https://github.com/Guvenkaya/public-reports/blob/master/The-Sweat-Foundation-Ltd-Jars-Refactor-Rust-Smart-Contract-Security-Assessment.pdf)

## Tell us what you need to secure.

Describe your system, main concern, and deadline. We will reply with scoping questions and a proposed next step.

[Discuss your scope](https://www.guvenkaya.co/contact?source=report)
