{"record":{"id":"e4efc47078d45a40","repo":"affaan-m/ECC","slug":"min-amount-out-is-required-before-send","errorCode":null,"errorMessage":"min_amount_out is required before send","messagePattern":"min_amount_out is required before send","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/llm-trading-agent-security/SKILL.md","lineNumber":82,"sourceCode":"\n        daily = self._get_24h_spend()\n        if daily + usd_amount > MAX_DAILY_SPEND_USD:\n            raise SpendLimitError(f\"Daily limit: ${daily} + ${usd_amount} > ${MAX_DAILY_SPEND_USD}\")\n\n        self._record_spend(usd_amount)\n```\n\n### Simulate before sending\n\n```python\nclass SlippageError(Exception):\n    pass\n\nasync def safe_execute(self, tx: dict, expected_min_out: int | None = None) -> str:\n    sim_result = await self.w3.eth.call(tx)\n\n    if expected_min_out is None:\n        raise ValueError(\"min_amount_out is required before send\")\n\n    actual_out = decode_uint256(sim_result)\n    if actual_out < expected_min_out:\n        raise SlippageError(f\"Simulation: {actual_out} < {expected_min_out}\")\n\n    signed = self.account.sign_transaction(tx)\n    return await self.w3.eth.send_raw_transaction(signed.raw_transaction)\n```\n\n### Circuit breaker\n\n```python\nclass TradingCircuitBreaker:\n    MAX_CONSECUTIVE_LOSSES = 3\n    MAX_HOURLY_LOSS_PCT = 0.05\n\n    def check(self, portfolio_value: float) -> None:\n        if self.consecutive_losses >= self.MAX_CONSECUTIVE_LOSSES:","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/skills/llm-trading-agent-security/SKILL.md#L64-L100","documentation":"In `safe_execute`, after `self.w3.eth.call(tx)` simulates the transaction, the function requires an `expected_min_out` to enforce slippage. If it is `None`, the function raises `ValueError(\"min_amount_out is required before send\")` and never signs. This is a guard against broadcasting a swap with no slippage protection.","triggerScenarios":"Caller invokes `safe_execute(tx)` positionally, omitting `expected_min_out`. The route/agent computed the call data but never resolved a minimum output (e.g. forgot to call `quote` or set `amount_out_min`).","commonSituations":"Refactor that added the `expected_min_out` parameter but did not update all call sites. Dev environment passed `None` as a 'skip check' flag that the production code now refuses. Off-by-one in `decode_uint256` upstream left the value unset.","solutions":["Compute `expected_min_out` from a fresh quote minus slippage tolerance and pass it explicitly.","If you genuinely want to skip the check in a sandbox, do not — instead set `expected_min_out=0` and accept that you have no slippage protection.","Make the parameter required (drop the `= None` default) so the error becomes a `TypeError` at call time, not a runtime `ValueError`.","Add a unit test that `safe_execute(tx)` without `expected_min_out` raises before any RPC call."],"exampleFix":"# before\nasync def safe_execute(self, tx: dict, expected_min_out: int | None = None) -> str:\n    sim_result = await self.w3.eth.call(tx)\n    if expected_min_out is None:\n        raise ValueError(\"min_amount_out is required before send\")\n\n# after — required param, compute slippage-bounded minimum at the call site\nasync def safe_execute(self, tx: dict, expected_min_out: int) -> str:\n    sim_result = await self.w3.eth.call(tx)\n    actual_out = decode_uint256(sim_result)\n    if actual_out < expected_min_out:\n        raise SlippageError(...)","handlingStrategy":"validation","validationCode":"def has_min_out(x) -> bool:\n    return isinstance(x, int) and x >= 0 and x is not None","typeGuard":"null","tryCatchPattern":"try:\n    tx_hash = await agent.safe_execute(tx, expected_min_out=min_out)\nexcept ValueError as e:\n    if 'min_amount_out' in str(e):\n        min_out = compute_min_out(quote, slippage_bps)\n        tx_hash = await agent.safe_execute(tx, expected_min_out=min_out)\n    else:\n        raise","preventionTips":["Make `expected_min_out` a required positional argument so misuse is a TypeError at call time.","Always compute `amount_out_min` from a fresh quote before constructing the tx.","Add a unit test that calling `safe_execute` without the minimum raises before any RPC."],"tags":["web3","trading","slippage","validation","agents"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}