# 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)


---

# 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/module07/homeworks/hints.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.
