# Smart Contract Errors (Python)

Python-focused fixes for common Algorand smart contract errors.

## Table of Contents

- [Logic Eval Errors](#logic-eval-errors)
  - [Assert Failed](#assert-failed)
  - [Opcode Budget Exceeded](#opcode-budget-exceeded)
  - [Invalid Program](#invalid-program)
  - [Stack Underflow](#stack-underflow)
  - [Byte/Int Type Mismatch](#byteint-type-mismatch)
- [ABI Errors](#abi-errors)
  - [Method Not Found](#method-not-found)
  - [ABI Encoding Error](#abi-encoding-error)
  - [Return Value Decoding Error](#return-value-decoding-error)
- [State Errors](#state-errors)
  - [Global State Full](#global-state-full)
  - [Local State Not Opted In](#local-state-not-opted-in)
  - [Box Not Found](#box-not-found)
  - [Box MBR Not Met](#box-mbr-not-met)
- [Inner Transaction Errors](#inner-transaction-errors)
- [Debugging Tips](#debugging-tips)

## Logic Eval Errors

### Assert Failed

```
logic eval error: assert failed pc=123
```

**Cause:** An `assert` statement evaluated to false.

**Debug with source maps (LogicError):**

```python
from algokit_utils import LogicError

try:
    app_client.send.my_method(value=0)
except LogicError as e:
    print(e)       # Shows: assert failed at contracts/my_contract.py:45
    print(e.pc)    # Program counter: 123
    print(e.line)  # Source line number
```

**Common causes:**
- Input validation failed (e.g., `assert amount > 0`)
- Authorization check failed (e.g., `assert Txn.sender == self.owner`)
- State precondition not met (e.g., `assert self.is_initialized`)

**Fix:** Check the assertion condition and ensure inputs satisfy it.

### Opcode Budget Exceeded

```
logic eval error: dynamic cost budget exceeded
```

**Cause:** Contract exceeded the 700 opcode budget per app call.

**Budget limits:**
| Context | Budget |
|---------|--------|
| Single app call | 700 opcodes |
| Max pooled (16 app calls) | 11,200 opcodes |
| Logic signature | 20,000 opcodes |

**Fix - Pool budget with extra app calls:**
```python
# Add dummy app calls to increase budget
algorand.new_group()
    .add_app_call_method_call(actual_call_params)
    .add_app_call(AppCallParams(
        sender=sender,
        app_id=app_id,
        on_complete=OnComplete.NoOp,
        args=[b"noop"]  # Dummy call for budget
    ))
    .send()
```

**Optimize expensive operations:**
```python
# EXPENSIVE - iteration over large data
for i in range(100):
    process(data[i])

# CHEAPER - batch operations or use Box storage
box_data = Box(Bytes, key=b"data")
```

**Split across multiple calls:**
```python
# Instead of one large operation, split into phases
@arc4.abimethod
def process_phase1(self) -> None: ...

@arc4.abimethod
def process_phase2(self) -> None: ...
```

### Invalid Program

```
logic eval error: invalid program
```

**Cause:** The TEAL program is malformed or uses unsupported opcodes.

**Common causes:**
- Compiling for wrong AVM version
- Using opcodes not supported on target network
- Corrupted approval/clear program bytes

**Fix:** Ensure compilation targets the correct AVM version.

### Stack Underflow

```
logic eval error: stack underflow
```

**Cause:** Operation tried to pop from empty stack.

**In Algorand Python:** This usually indicates a bug in low-level operations. Check any `op.*` calls.

### Byte/Int Type Mismatch

```
logic eval error: assert failed: wanted type uint64 but got []byte
```

**Cause:** Wrong type passed to an operation.

**Common in Algorand Python:**
```python
# INCORRECT - String where UInt64 expected
assert payment.amount == "1000000"

# CORRECT - Use proper types
assert payment.amount == UInt64(1_000_000)
```

## ABI Errors

### Method Not Found

```
error: method "foo(uint64)void" not found
```

**Cause:** Calling a method that doesn't exist in the contract ABI.

**Fix:**
1. Regenerate the typed client after contract changes
2. Check the method signature matches exactly
3. Verify the contract was deployed with the latest code

### ABI Encoding Error

```
ABIEncodingError: value out of range for uint64
```

**Cause:** Value doesn't fit the ABI type.

**Examples:**
```python
# INCORRECT - Negative value for uint64
arc4.UInt64(-1)

# INCORRECT - Value too large
arc4.UInt8(256)  # Max is 255

# CORRECT - Use appropriate type
arc4.UInt64(0)
arc4.UInt16(256)
```

### Return Value Decoding Error

```
error: could not decode return value
```

**Cause:** Method returned unexpected data format.

**Common causes:**
- Contract didn't log the return value properly
- Wrong return type in client
- Transaction failed before return

**Fix:** Check contract method has correct return annotation.

## State Errors

### Global State Full

```
logic eval error: store global state: failed
```

**Cause:** Exceeded declared global state schema.

**Fix:** Increase schema in contract deployment:
```python
class MyContract(ARC4Contract):
    # Declare more state slots in schema
    @arc4.abimethod(create=True)
    def create(self) -> None:
        pass
```

### Local State Not Opted In

```
logic eval error: application APPID not opted in
```

**Cause:** Account hasn't opted into the application.

**Fix:** Opt in before accessing local state:
```python
algorand.send.app_call(AppCallParams(
    sender=user_address,
    app_id=app_id,
    on_complete=OnComplete.OptIn,
))
```

### Box Not Found

```
logic eval error: box not found
```

**Cause:** Accessing a box that doesn't exist.

**Fix:** Create box before access or check existence:
```python
# In contract - check if box exists
if self.my_box.exists:
    value = self.my_box.value
else:
    self.my_box.value = default_value
```

### Box MBR Not Met

```
logic eval error: box create with insufficient funds
```

**Cause:** App account lacks funds for box minimum balance requirement.

**MBR formula:** `2500 + (400 * (key_length + value_length))` microAlgos per box

**Fix:** Fund the app account:
```python
algorand.send.payment(PaymentParams(
    sender=funder.address,
    receiver=app_client.app_address,
    amount=AlgoAmount(algo=1),  # Cover box MBR
))
```

## Inner Transaction Errors

### Insufficient Balance for Inner Txn

```
logic eval error: insufficient balance
```

**Cause:** App account lacks funds for inner transaction amount.

**Fix:** Fund the app account before inner transactions:
```python
# Fund app before calling method with inner transactions
algorand.send.payment(PaymentParams(
    sender=deployer.address,
    receiver=app_client.app_address,
    amount=AlgoAmount(algo=5),
))
```

### Inner Transaction Limit

```
logic eval error: too many inner transactions
```

**Cause:** Exceeded 256 inner transactions per group.

**Fix:** Split operations across multiple outer transactions.

### App Not Opted Into Asset

```
logic eval error: asset ASSET_ID not opted in
```

**Cause:** Contract account isn't opted into the asset.

**Fix:** Add opt-in method to contract:
```python
@arc4.abimethod
def opt_in_to_asset(self, asset: Asset) -> None:
    itxn.AssetTransfer(
        xfer_asset=asset,
        asset_receiver=Global.current_application_address,
        asset_amount=0,
        fee=0
    ).submit()
```

## Debugging Tips

### Enable Debug Logging

```python
import logging
logging.getLogger("algokit").setLevel(logging.DEBUG)
```

### Catch LogicError with Source Map

```python
from algokit_utils import LogicError

try:
    app_client.send.my_method(value=0)
except LogicError as e:
    print(e)       # Full error with source mapping
    print(e.pc)    # Program counter
    print(e.line)  # Source line number
```

### Check Program Counter Location

When you see `pc=123`, use algokit to find the source:

```bash
algokit compile contracts/my_contract.py --output-sourcemap
```

Then map the PC to source using the generated `.map` file.

## References

- [Debugging Smart Contracts](https://dev.algorand.co/concepts/smart-contracts/debugging/)
- [AVM Opcodes Reference](https://dev.algorand.co/reference/teal/opcodes/)
- [Error Handling in AlgoKit](https://dev.algorand.co/algokit/utils/typescript/debugging/)
