# Appendix: Helper Functions

## Common Utility Functions for RPC Handler Development

[← Back to Building and Integrating Custom RPC Handlers](/core-dev-bootcamp/module07.md)

***

## Introduction

The rippled RPC module provides a rich set of helper functions that handle common operations in handlers. Rather than reimplementing these utilities, your handlers should leverage existing functions for consistency, reliability, and maintainability.

This reference guide catalogs the most frequently-used helper functions with signatures, usage examples, and common patterns.

**Source Location**: `src/xrpld/rpc/detail/RPCHelpers.h` and `src/xrpld/rpc/detail/RPCHelpers.cpp`

***

## Account and Address Functions

### RPC::accountFromString()

**Purpose**: Parse account identifier from string, supporting multiple formats

**Signature**:

```cpp
namespace RPC {
    boost::optional<AccountID>
    accountFromString(std::string const& strAccount);
}
```

**Parameters**:

* `strAccount`: Account identifier as string (Base58Check address or account ID)

**Returns**:

* `boost::optional<AccountID>` - Contains parsed AccountID if successful, empty if invalid

**Error Conditions**:

* Invalid Base58Check format
* Wrong checksum
* Incorrect length

**Example Usage**:

```cpp
std::string const accountStr = context.params[jss::account].asString();

auto const account = RPC::accountFromString(accountStr);
if (!account) {
    return rpcError(rpcACT_MALFORMED, "Invalid account format");
}

// Use account value
AccountID const accountID = *account;
auto const sle = ledger->read(keylet::account(accountID));
```

**When to Use**:

* Parsing account addresses from RPC parameters
* Validating account identifiers in handlers
* Converting string representations to AccountID objects

**Notes**:

* Wrapper around `parseBase58<AccountID>()`
* Handles both standard and non-standard account formats
* Preferred over manual Base58 decoding

***

### parseBase58()

**Purpose**: Generic Base58Check decoder template

**Signature**:

```cpp
template<class T>
boost::optional<T>
parseBase58(std::string const& encoded);
```

**Template Parameters**:

* `T`: Target type (usually AccountID, Blob, etc.)

**Parameters**:

* `encoded`: Base58Check encoded string

**Returns**:

* `boost::optional<T>` - Decoded value if valid, empty if invalid

**Example Usage - Account**:

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

if (!account) {
    return rpcError(rpcACT_MALFORMED);
}
```

**Example Usage - Generic Blob**:

```cpp
auto const hash = parseBase58<uint256>(
    context.params[jss::tx_hash].asString());

if (!hash) {
    return rpcError(rpcINVALID_PARAMS, "Invalid hash format");
}
```

**Example Usage - Seed Decoding**:

```cpp
auto const seedPair = parseBase58<std::pair<uint16_t, Blob>>(
    seedStr);

if (!seedPair) {
    return rpcError(rpcBAD_SEED, "Invalid seed format");
}
```

**When to Use**:

* Decoding Base58Check encoded data
* Parsing binary identifiers and hashes
* Converting external formats to internal representations

**Notes**:

* Template specialization required for custom types
* Performs checksum validation automatically
* More general than accountFromString()

***

## Ledger Access Functions

### RPC::lookupLedger()

**Purpose**: Retrieve ledger by specification, handling current/validated/indexed requests

**Signature**:

```cpp
namespace RPC {
    Json::Value
    lookupLedger(
        std::shared_ptr<ReadView const>& ledger,
        RPC::JsonContext& context);
}
```

**Parameters**:

* `ledger`: Output reference (populated if successful)
* `context`: RPC request context with parameters and app reference

**Returns**:

* `Json::Value`: Empty on success, error response if lookup failed

**Supported Parameters**:

* `ledger_hash`: Specific ledger hash
* `ledger_index`: Numeric index or "current"/"validated"
* `ledger_min_index` / `ledger_max_index`: Range lookup (less common)

**Example Usage - Default to Current**:

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

if (!ledger) {
    return result;  // Return error response
}

// Now use ledger safely
auto const sle = ledger->read(keylet::account(*account));
```

**Example Usage - With Default Ledger**:

```cpp
std::shared_ptr<ReadView const> ledger;

// If no ledger specified, defaults to validated
if (context.params.isMember(jss::ledger_index)) {
    auto const result = RPC::lookupLedger(ledger, context);
    if (!ledger) return result;
} else {
    ledger = context.ledgerMaster.getValidatedLedger();
}
```

**Error Responses**:

* `rpcLGR_NOT_FOUND`: Specified ledger doesn't exist
* `rpcNO_CURRENT`: No current ledger available
* `rpcNO_CLOSED`: No validated ledger available
* `rpcLGR_INDEX_BOUNDS`: Index out of valid range

**When to Use**:

* Retrieving ledgers for queries
* Handling user-specified ledger parameters
* Validating ledger availability before operations

**Notes**:

* Single most important helper for handlers
* Always use instead of manual ledger lookup
* Properly handles all ledger specification modes

***

### RPC::getLedgerRange()

**Purpose**: Determine valid ledger index range available on this node

**Signature**:

```cpp
namespace RPC {
    bool
    getLedgerRange(
        uint32_t& minIndex,
        uint32_t& maxIndex,
        RPC::JsonContext const& context);
}
```

**Parameters**:

* `minIndex`: Output - oldest available ledger index
* `maxIndex`: Output - newest available ledger index
* `context`: RPC request context

**Returns**:

* `bool`: True if range valid, false if no ledgers available

**Example Usage**:

```cpp
uint32_t minLedger, maxLedger;
if (!RPC::getLedgerRange(minLedger, maxLedger, context)) {
    return rpcError(rpcNO_CURRENT, "No ledgers available");
}

// Validate user's index is in range
uint32_t const requestedIndex =
    context.params[jss::ledger_index].asUInt();

if (requestedIndex < minLedger || requestedIndex > maxLedger) {
    return rpcError(rpcLGR_INDEX_BOUNDS,
        "Ledger index " + to_string(requestedIndex) +
        " outside available range [" + to_string(minLedger) +
        ", " + to_string(maxLedger) + "]");
}
```

**When to Use**:

* Validating user-supplied ledger indices
* Determining available data range
* Implementing range-based queries
* Error messages about available history

**Notes**:

* Range depends on node's history mode
* Archival nodes have larger ranges
* Always validate user indices against this range

***

## Amount and Value Functions

### amountFromJsonNoThrow()

**Purpose**: Safely parse XRP or token amount from JSON without throwing exceptions

**Signature**:

```cpp
boost::optional<Amount>
amountFromJsonNoThrow(Json::Value const& value);
```

**Parameters**:

* `value`: JSON value containing amount specification

**Returns**:

* `boost::optional<Amount>`: Parsed amount if valid, empty if invalid

**Supported Formats**:

```json
"1000000"           // XRP in drops (string)
1000000             // XRP in drops (number)
{
  "currency": "USD",
  "issuer": "rN7n7otQDd6FczFgLdlqtyMVrnPQGEDE8",
  "value": "100.50"
}
```

**Example Usage - XRP Amount**:

```cpp
auto const amount = amountFromJsonNoThrow(
    context.params[jss::amount]);

if (!amount) {
    return rpcError(rpcINVALID_PARAMS, "Invalid amount format");
}

// Check for minimum
if (*amount < XRPAmount{1000}) {
    return rpcError(rpcINVALID_PARAMS, "Amount too small");
}
```

**Example Usage - Token Amount**:

```cpp
auto const amount = amountFromJsonNoThrow(
    context.params[jss::send_max]);

if (!amount || !amount->isIOU()) {
    return rpcError(rpcINVALID_PARAMS,
        "send_max must be token amount");
}

const auto& currency = amount->getCurrency();
const auto& issuer = amount->getIssuer();
```

**When to Use**:

* Parsing transaction amounts
* Validating amount parameters
* Handling both XRP and token amounts
* Any situation dealing with ledger values

**Notes**:

* Preferred over throwing version for handlers
* Handles format variations automatically
* Returns optional for clear error handling

***

## JSON and Type Conversion Functions

### ripple::to\_string()

**Purpose**: Convert various types to string representation

**Common Overloads**:

```cpp
// Hashes and cryptographic types
std::string to_string(uint256 const& hash);
std::string to_string(AccountID const& account);
std::string to_string(Blob const& blob);

// Ledger state
std::string to_string(LedgerEntryType type);
std::string to_string(TransactionType type);

// Network/protocol values
std::string to_string(NetworkOPs::OperatingMode mode);
std::string to_string(ProtocolVersion version);
```

**Example Usage**:

```cpp
// Convert hash to hex string
uint256 const txHash = tx->getID();
result[jss::tx_hash] = to_string(txHash);

// Convert account to address string
AccountID const account = ...;
result[jss::account] = to_string(account);

// Convert transaction type
TransactionType const txType = ...;
result[jss::transaction_type] = to_string(txType);
```

**When to Use**:

* Converting internal types to JSON-serializable format
* Building human-readable output
* Creating hash/address strings for responses

**Notes**:

* Overloaded for many ripple types
* Preferred over manual formatting
* Handles endianness and encoding automatically

***

## Permission and State Functions

### context.role Functions

**Purpose**: Check request caller's authorization level

**Available Methods**:

```cpp
bool isAdmin() const;          // ADMIN role
bool isIdentified() const;     // IDENTIFIED, ADMIN, or PROXY
bool isUnlimited() const;      // Admin with no limits
bool isUser() const;           // USER role
```

**Example Usage**:

```cpp
// Restrict to admin
if (!context.role.isAdmin()) {
    return rpcError(rpcNO_PERMISSION, "Admin only");
}

// Allow identified users
if (!context.role.isIdentified()) {
    return rpcError(rpcFORBIDDEN, "Must be identified");
}

// Check for rate limiting
if (!context.role.isUnlimited() && rateLimited) {
    return rpcError(rpcSLOW_DOWN, "Rate limited");
}
```

**Role Hierarchy**:

```
FORBID
  |
  PROXY
    |
    IDENTIFIED
      |
      ADMIN (unlimited)
  |
USER
```

**When to Use**:

* Early permission checks
* Restricting sensitive operations
* Varying response detail by role
* See [Authentication and Authorization](https://github.com/XRPL-Commons/xrpl-trainings/blob/main/core-dev-bootcamp/module07/authentication-authorization.md)

**Notes**:

* Check early, before expensive operations
* Roles defined in Config.h
* Proxy role is special case

***

## Specialized Query Functions

### ledger->read()

**Purpose**: Read ledger entry by keylet

**Signature**:

```cpp
std::shared_ptr<SLE const>
read(Keylet const& keylet) const;
```

**Common Keylets**:

```cpp
keylet::account(AccountID const& id)
keylet::ledger()                      // Ledger header
keylet::fees()                        // Fee schedule
keylet::signingPubKey(PublicKey)
keylet::offer(AccountID, uint32_t seq)
keylet::escrow(AccountID, uint32_t seq)
```

**Example Usage**:

```cpp
// Read account state
auto const sle = ledger->read(keylet::account(*account));
if (!sle) {
    return rpcError(rpcACT_NOT_FOUND);
}

// Access fields
uint32_t flags = sle->getFieldU32(sfFlags);
XRPAmount balance = sle->getFieldAmount(sfBalance);
std::string domain = sle->getFieldString(sfDomain);
```

**When to Use**:

* Querying ledger state
* Accessing account information
* Reading any ledger object

**Notes**:

* Type-safe key construction
* Null return indicates not found
* See [Implementing Custom Handlers](/core-dev-bootcamp/module07/implementing-custom-handlers.md)

***

## Error Response Functions

### rpcError()

**Purpose**: Create standardized RPC error responses

**Overloads**:

```cpp
Json::Value rpcError(RippleErrorCode code);
Json::Value rpcError(RippleErrorCode code, std::string const& msg);
```

**Example Usage**:

```cpp
// Simple error
return rpcError(rpcINVALID_PARAMS);

// With descriptive message
return rpcError(rpcACT_NOT_FOUND,
    "Account " + accountStr + " not found");

// Error with value context
return rpcError(rpcINVALID_PARAMS,
    "Fee " + to_string(fee) + " below minimum");
```

**When to Use**:

* Building all error responses
* Consistent error formatting
* Always prefer to manual error JSON

**Notes**:

* Handles HTTP status code mapping
* Formats as standard JSON-RPC error
* See [Error Handling and Validation](https://github.com/XRPL-Commons/xrpl-trainings/blob/main/core-dev-bootcamp/module07/error-handling-validation.md)

***

## Function Selection Guide

| Task                  | Function(s)               |
| --------------------- | ------------------------- |
| Parse account address | `accountFromString()`     |
| Parse any Base58Check | `parseBase58<T>()`        |
| Get ledger to query   | `lookupLedger()`          |
| Validate ledger index | `getLedgerRange()`        |
| Parse amount          | `amountFromJsonNoThrow()` |
| Convert to string     | `to_string()`             |
| Check permissions     | `context.role.is*()`      |
| Read ledger object    | `ledger->read()`          |
| Create error          | `rpcError()`              |

***

## Usage Pattern Summary

```cpp
Json::Value doMyHandler(RPC::JsonContext& context)
{
    // 1. Check permissions
    if (!context.role.isIdentified()) {
        return rpcError(rpcNO_PERMISSION);
    }

    // 2. Extract and validate parameters
    if (!context.params.isMember(jss::account)) {
        return rpcError(rpcINVALID_PARAMS);
    }

    auto const account = RPC::accountFromString(
        context.params[jss::account].asString());
    if (!account) {
        return rpcError(rpcACT_MALFORMED);
    }

    // 3. Get ledger
    std::shared_ptr<ReadView const> ledger;
    auto const result = RPC::lookupLedger(ledger, context);
    if (!ledger) {
        return result;
    }

    // 4. Query ledger
    auto const sle = ledger->read(keylet::account(*account));
    if (!sle) {
        return rpcError(rpcACT_NOT_FOUND);
    }

    // 5. Build response
    Json::Value response;
    response[jss::account] = to_string(*account);
    response[jss::balance] = to_string(
        sle->getFieldAmount(sfBalance));

    return response;
}
```

***

## Navigation

[← Back to Appendices](/core-dev-bootcamp/module07/appendices.md) | [Handler Examples](/core-dev-bootcamp/module07/appendices/handler-examples.md) | [Debugging Guide →](/core-dev-bootcamp/module07/appendices/debugging-guide.md)

***

**Related Module Sections**:

* [Implementing Custom Handlers](/core-dev-bootcamp/module07/implementing-custom-handlers.md)
* [Authentication and Authorization](https://github.com/XRPL-Commons/xrpl-trainings/blob/main/core-dev-bootcamp/module07/authentication-authorization.md)
* [Error Handling and Validation](https://github.com/XRPL-Commons/xrpl-trainings/blob/main/core-dev-bootcamp/module07/error-handling-validation.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/appendices/helper-functions.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.
