# Homework 2: Implement a Custom Validation Check

[← Back to Transactors: Understanding the Lifecycle of a Transaction](/core-dev-bootcamp/module03bis.md)

***

### Objective

Add a custom preflight or preclaim check to an existing transactor and verify it works correctly with unit tests.

***

### Background

For this exercise, you'll add a new validation rule to the CheckCreate transactor. The goal is to understand how validation logic is added and tested.

***

### Scenario

Imagine a new business rule: **Checks cannot be created for amounts less than 1 XRP (1,000,000 drops)**.

This is a hypothetical requirement for learning purposes—it doesn't exist in the actual protocol.

***

### Tasks

#### Part 1: Identify Where to Add the Check

1. Open `src/xrpld/app/tx/detail/CreateCheck.cpp`
2. Determine whether this validation belongs in:
   * `preflight()` - stateless validation
   * `preclaim()` - ledger-state validation
3. **Question**: Which phase is appropriate and why?

***

#### Part 2: Implement the Validation

Add the following check to the appropriate phase:

```cpp
// Minimum check amount: 1 XRP
STAmount const sendMax{ctx.tx.getFieldAmount(sfSendMax)};

if (sendMax.native())
{
    // Only enforce for XRP
    if (sendMax < STAmount{1000000})
    {
        JLOG(ctx.j.warn()) << "Check amount below minimum (1 XRP)";
        return temBAD_AMOUNT;  // or appropriate code
    }
}
```

**Questions to consider:**

* Should this apply to non-XRP amounts?
* What result code is most appropriate?
* Should this be amendment-gated?

***

#### Part 3: Write a Unit Test

Create a test in `src/test/app/Check_test.cpp` (or a new test file):

```cpp
void testMinimumAmount()
{
    testcase("Minimum check amount");

    using namespace jtx;
    Env env(*this);

    Account const alice{"alice"};
    Account const bob{"bob"};

    // Fund accounts
    env.fund(XRP(10000), alice, bob);
    env.close();

    // Test: Check below minimum should fail
    {
        auto const seq = env.seq(alice);

        env(check::create(alice, bob, drops(999999)),
            ter(temBAD_AMOUNT));  // Expected failure
    }

    // Test: Check at minimum should succeed
    {
        auto const seq = env.seq(alice);

        env(check::create(alice, bob, drops(1000000)),
            ter(tesSUCCESS));  // Expected success
    }

    // Test: Check above minimum should succeed
    {
        auto const seq = env.seq(alice);

        env(check::create(alice, bob, XRP(100)),
            ter(tesSUCCESS));  // Expected success
    }
}
```

***

#### Part 4: Compile and Run Tests

1. Rebuild rippled:

```bash
cd build
cmake --build . --target rippled
```

2. Run the Check tests:

```bash
./rippled --unittest Check_test
```

3. Verify your new test passes.

***

#### Part 5: Edge Cases

Add tests for edge cases:

1. **Exactly 1 XRP**: Should this succeed or fail?
2. **Non-XRP amounts**: Should the minimum apply?
3. **Negative amounts**: How is this handled? (Already validated elsewhere?)

***

### Deliverables

1. **Modified code**: The validation check you added
2. **Test code**: Unit tests for the new validation
3. **Test output**: Results showing tests pass
4. **Analysis document** answering:
   * Why you chose preflight vs preclaim
   * Why you chose a particular result code
   * What edge cases you considered

***

### Discussion Questions

1. If this were a real feature, should it be amendment-gated? Why?
2. What would happen if you returned `tecBAD_AMOUNT` instead of `temBAD_AMOUNT`?
3. Could this check ever pass in preflight but fail in doApply? Under what circumstances?
4. How would you test this check in a live (non-standalone) environment?

***

### Bonus Challenge

Make the minimum amount configurable:

1. Add a new amendment flag
2. Only enforce the minimum when the amendment is enabled
3. Write tests for both enabled and disabled states

***

### Cleanup

**Important**: After completing this exercise, revert your changes to avoid affecting the actual codebase:

```bash
git checkout -- src/xrpld/app/tx/detail/CreateCheck.cpp
git checkout -- src/test/app/Check_test.cpp
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.xrpl-commons.org/core-dev-bootcamp/module03bis/homework2.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
