> For the complete documentation index, see [llms.txt](https://docs.xrpl-commons.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.xrpl-commons.org/core-dev-bootcamp/module07/homeworks/hints.md).

# Hints

## Helpful Guidance for the GetAccountBalance Implementation

[← Back to Workshop](/core-dev-bootcamp/module08/homeworks.md)

***

## General Approach

If you're stuck, try this step-by-step approach:

1. **Start small** — Get a minimal handler working first
2. **Add validation incrementally** — One check at a time
3. **Test frequently** — Verify each addition works
4. **Study existing handlers** — Copy proven patterns
5. **Use helper functions** — Don't reinvent the wheel

***

## Hint 1: File Structure

Your handler file should follow this structure:

```cpp
// Copyright and license header
// Include necessary headers
// namespace ripple {
//   Helper functions (if needed)
//   Main handler function
// } // namespace ripple
```

**Essential includes**:

* `<xrpld/rpc/Context.h>` — For JsonContext
* `<xrpld/rpc/detail/RPCHelpers.h>` — For helper functions
* `<xrpl/protocol/ErrorCodes.h>` — For error codes
* `<xrpl/protocol/jss.h>` — For JSON field names

***

## Hint 2: Handler Signature

Remember the standard signature:

```cpp
Json::Value doGetAccountBalance(RPC::JsonContext& context)
{
    Json::Value result;

    // Your implementation here

    return result;
}
```

**Important**: Function name should start with `do` followed by the command name in PascalCase.

***

## Hint 3: Account Validation

Use the `parseBase58` template function:

```cpp
auto const account = parseBase58<AccountID>(
    context.params[jss::account].asString()
);

if (!account) {
    return rpcError(rpcACT_MALFORMED, "Invalid account address");
}

// Use *account to get the AccountID value
```

***

## Hint 4: Ledger Lookup

The easiest way to get a ledger:

```cpp
std::shared_ptr<ReadView const> ledger;
auto const result = RPC::lookupLedger(ledger, context);

if (!ledger) {
    return result;  // This already contains the error
}

// Now 'ledger' is safe to use
```

This helper automatically:

* Parses `ledger_index` or `ledger_hash` from params
* Handles "current", "validated", "closed"
* Returns proper error if not found

***

## Hint 5: Reading Account Data

Use the keylet system:

```cpp
auto const sleAccount = ledger->read(keylet::account(*account));

if (!sleAccount) {
    return rpcError(rpcACT_NOT_FOUND, "Account not found");
}

// sleAccount is a std::shared_ptr<SLE const>
```

***

## Hint 6: Extracting Field Values

Access account fields using type-safe getters:

```cpp
// Get XRP balance
STAmount balance = sleAccount->getFieldAmount(sfBalance);

// Get sequence number
std::uint32_t sequence = sleAccount->getFieldU32(sfSequence);

// Get owner count
std::uint32_t ownerCount = sleAccount->getFieldU32(sfOwnerCount);
```

***

## Hint 7: Reserve Calculations

Get fee information from the ledger:

```cpp
auto const& fees = ledger->fees();

// Base reserve (for the account itself)
XRPAmount baseReserve = fees.accountReserve(0);

// Owner reserve (base reserve × owner count)
XRPAmount ownerReserve = fees.accountReserve(ownerCount) - baseReserve;

// Total reserve
XRPAmount totalReserve = baseReserve + ownerReserve;

// Available balance
XRPAmount available = balance.xrp() - totalReserve;
```

***

## Hint 8: Building JSON Responses

Use the jss namespace for standard field names:

```cpp
result[jss::account] = to_string(*account);
result[jss::ledger_index] = ledger->info().seq;
result[jss::validated] = ledger->isImmutable();

// Custom fields use string literals
result["balance"] = to_string(balance);
result["available_balance"] = to_string(available);
```

***

## Hint 9: Converting Amounts to Strings

Use `to_string()` for all amount types:

```cpp
// For STAmount
result["balance"] = to_string(balance);

// For XRPAmount
result["reserve"] = to_string(baseReserve);

// For AccountID
result[jss::account] = to_string(*account);
```

***

## Hint 10: Handler Registration

In `Handlers.cpp`, add this entry:

```cpp
{
    "get_account_balance",
    {
        &doGetAccountBalance,
        Role::USER,              // Minimum role required
        RPC::NEEDS_CURRENT_LEDGER  // Conditions
    }
}
```

**Why USER role?** This is a read operation, so authenticated users should have access.

**Why NEEDS\_CURRENT\_LEDGER?** We need ledger access to query account state.

***

## Hint 11: Error Handling Pattern

Follow this pattern for all validations:

```cpp
// 1. Check if field exists
if (!context.params.isMember(jss::account)) {
    return rpcError(rpcINVALID_PARAMS, "Missing 'account' field");
}

// 2. Check field type
if (!context.params[jss::account].isString()) {
    return rpcError(rpcINVALID_PARAMS, "'account' must be a string");
}

// 3. Validate field value
auto const account = parseBase58<AccountID>(
    context.params[jss::account].asString()
);

if (!account) {
    return rpcError(rpcACT_MALFORMED, "Invalid account address");
}

// 4. Continue processing
```

***

## Hint 12: Testing Setup

Your test file should start like this:

```cpp
#include <xrpld/rpc/handlers/GetAccountBalance.h>
#include <xrpl/protocol/jss.h>
#include <test/jtx.h>
#include <test/jtx/Env.h>

namespace ripple {
namespace test {

class GetAccountBalance_test : public beast::unit_test::suite
{
public:
    void testValidRequest()
    {
        using namespace jtx;

        Env env(*this);

        // Create account
        Account alice("alice");
        env.fund(XRP(1000), alice);
        env.close();

        // Build request
        Json::Value params;
        params[jss::account] = alice.human();

        // Call handler
        Json::Value result = doGetAccountBalance(/* context */);

        // Verify result
        BEAST_EXPECT(result.isMember("balance"));
        BEAST_EXPECT(result["balance"].asString() == "1000000000");
    }

    void run() override
    {
        testValidRequest();
    }
};

BEAST_DEFINE_TESTSUITE(GetAccountBalance, rpc, ripple);

} // namespace test
} // namespace ripple
```

***

## Hint 13: Common Pitfalls to Avoid

❌ **Don't** directly access params without checking existence first ✅ **Do** always check `isMember()` before accessing

❌ **Don't** assume ledger is always available ✅ **Do** use `lookupLedger()` and check the result

❌ **Don't** forget to dereference optional values ✅ **Do** use `*account` after parsing with `parseBase58`

❌ **Don't** return raw exception messages ✅ **Do** catch exceptions and return proper RPC errors

***

## Hint 14: Debugging Tips

If your handler doesn't work:

1. **Check compilation errors first**
   * Missing includes?
   * Typos in function names?
   * Wrong namespace?
2. **Add logging**

   ```cpp
   JLOG(context.j.debug()) << "Processing account: " << accountStr;
   ```
3. **Test with rippled command line**

   ```bash
   ./rippled get_account_balance '{"account":"rN7n7..."}'
   ```
4. **Check the handler table**
   * Is your handler registered?
   * Is the command name correct (lowercase with underscores)?

***

## Hint 15: Reference Code Locations

Look at these files for examples:

**Account validation**:

* `src/xrpld/rpc/handlers/AccountInfo.cpp:44-52`

**Ledger lookup**:

* `src/xrpld/rpc/handlers/AccountInfo.cpp:54-60`

**Reserve calculation**:

* `src/xrpld/rpc/handlers/AccountInfo.cpp:106-110`

**Response building**:

* `src/xrpld/rpc/handlers/AccountInfo.cpp:112-145`

***

## Still Stuck?

If these hints aren't enough:

1. **Compare with AccountInfo** — Your handler is very similar
2. **Check the test cases** — They show expected behavior
3. **Ask for help** — Use the feedback form

Remember: The goal is to learn, not to finish quickly. Take your time!

***

[← Back to Workshop](/core-dev-bootcamp/module08/homeworks.md) | [View Code Snippets →](/core-dev-bootcamp/module07/homeworks/snippets.md)
