What It Takes to Operate a Contract as a REST API
A token contract already has functions for reading balances and sending tokens. If we expose both as REST APIs that a business server can call, is the integration finished?
The balance screen may work while the final digits of a large amount change along the way. A transfer request may receive a normal HTTP response before the token movement the customer expects has completed. Moving function names into URLs is not enough to explain either difference.
BXB's API Service Factory reads a contract interface, constructs HTTP APIs, and connects requests to reads and transaction execution.
Following the same token's balanceOf and transfer functions, we can ask:
what information must survive the entire path for a generated API to become an integration we can operate?
Read a balance and send 10 tokens
Suppose token T is deployed on one EVM network: a network with an Ethereum-compatible execution environment. A holds 100 tokens and B holds none. A business server wants to read A's balance and then send 10 tokens to B. After a successful transfer, A will hold 90 and B will hold 10.
To keep the calculation simple, T has zero decimal places. The integer 10 passed to the contract means 10 tokens.
There is no minting, burning, token transfer fee, or concurrent transaction, and native coins for network fees are available separately.
These are illustrative assumptions, not a record of an actual customer transaction.
The example ABI contains two functions. We use account, to, and amount as their input names.
balanceOf(address account)
returns (uint256)
transfer(
address to, uint256 amount
) returns (bool)
This is an abbreviated view of the calls. The actual ABI also identifies balanceOf as a state-reading view function
and transfer as a nonpayable function used to change state.
The Solidity ABI describes function names, input and output types,
and execution characteristics in a form programs can read.
Both functions belong to the same contract, but the results a business server receives are different. A balance query reads a value. A transfer requires a signed transaction and subsequent confirmation of its result.
Connect the function list to an execution destination
An ABI alone does not identify which token on which network to call. Tokens on several networks may use the same ABI, and their contract addresses can differ even within one network.
BXB manages contract resources and deployed addresses, connecting the contract address an API server will call to its network. Here, contract T is the call destination. It is different from B, the address receiving the tokens.
It helps to separate registration into three outputs.
| Output | What it contains |
|---|---|
| API specification | HTTP paths and methods, input locations and types, response formats |
| Execution configuration | The network, contract address, and function mapping |
| Request handlers | The path from an HTTP request to a read or transaction execution |
The API specification uses OpenAPI. It can be displayed as documentation, but BXB's API server also uses it to configure the routes and handlers that receive requests. After registration, each request uses the prepared mapping; it does not register the ABI again.
The default path includes the contract address, function name, and function selector. A selector is a short identifier derived from the function name and input types. Functions with the same name but different input types must be distinguished when making a call. The path preserves both a readable name and the identity of the function to execute.
Move HTTP inputs into contract arguments
The two example functions use the following default mappings.
| Function | Default mapping |
|---|---|
balanceOf | GET: account in the query |
transfer | POST: to and amount in a JSON body |
A's address goes into account for the balance query, and B's address goes into to for the transfer.
These names follow the ABI. If another contract uses names such as _owner or value, callers must check those names in the generated specification.
Both requests also use the x-user-key header on this default path.
If customer-a is the alias connected to A's wallet, that is the value to send.
The read request selects a wallet for the call, but the read itself is not signed as a blockchain transaction.
account identifies the address whose balance is read; x-user-key is used to find the wallet for the call.
The business server must authenticate A and authorize transactions separately from this header. The article on customer accounts and signing boundaries explains how the alias connects a customer to a wallet.
GET and POST alone do not tell us everything about execution.
BXB can map read functions with inputs that are difficult to express as query parameters, such as structs or multidimensional arrays,
to POST with a request body. How inputs travel over HTTP and whether a call changes ledger state are separate decisions.
The example balanceOf takes one address, so the default GET path is sufficient.
Large integers reveal a problem small amounts hide
When sending 10 tokens, writing amount as a number or a string may appear to make no difference.
But uint256 covers a much larger range than JavaScript's ordinary Number can represent exactly as integers.
Consider 9007199254740993 as a separate example of an amount in base units.
It is not an amount we intend to send from the earlier balance of 100. It isolates the point where the representation changes.
const input =
'{"amount":9007199254740993}';
const data = JSON.parse(input);
String(data.amount);
// "9007199254740992"
The JSON syntax is valid, but the amount read by JavaScript is already smaller by one.
JavaScript's maximum safe integer
is 9007199254740991. It cannot distinguish every integer above that value exactly.
Converting to a larger integer type after the value reaches the server cannot recover a digit already lost on the client.
The specifications BXB generates therefore represent integers such as uint256 as decimal strings.
The relevant part of the schema for amount looks like this:
amount:
type: string
format: uint256
description: uint256
pattern: '^[0-9]+$'
The HTTP interface exchanges a string while retaining the original contract type, uint256.
That lets the value be interpreted as an integer argument rather than arbitrary text.
Declaring a string format does not, by itself, check a business limit or token balance.
Keep the string contract on both input and output
Writing string in a specification does not preserve precision on its own.
The request handler must interpret that string, and the response code must keep the same representation.
BXB's ABI conversion path reads a decimal string into BigInteger, an arbitrary-precision integer type, to construct the contract argument.
When decoding a query result, it writes integers back as decimal strings.
For the large example value, the representation must survive this path:
HTTP decimal string
"9007199254740993"
↓
Read as a large integer
↓
Encode as ABI uint256
↓
Decode the same ABI value
↓
HTTP decimal string
"9007199254740993"
This flow shows the representation changes when the same integer is supplied and read back.
It does not mean that transfer returns the amount sent.
Functions accepting integer inputs and queries returning integers must each honor the same representation contract.
In the balance query, the ABI's return value has no name, so BXB assigns it result0.
A's balance of 100 is represented as {"result0":"100"}. If the ABI names the return value, that name is used instead.
A client displaying or calculating with this string must also choose a type that preserves precision.
Converting token decimal places is a separate concern.
If T had six decimal places, 10 tokens would correspond to 10000000 base units.
The API input would be "10000000", not "10".
The string representation preserves digits; the unit conversion determines the amount to send. Both must be correct.
Separate the transfer response from completed token movement
Return now to the original token with zero decimal places.
The business server supplies B's address, the amount string "10", and A's wallet alias to the transfer endpoint.
BXB encodes the inputs according to the ABI and constructs a transaction calling contract T on the configured network.
It uses the signing path associated with A's wallet and sends the transaction.
The response on the ordinary asynchronous submission path includes transactionHash.
This is different from a balance query that directly returns a business value.
A transaction hash is an identifier for tracking what happens next. An HTTP response or a hash alone cannot establish node acceptance, successful execution in a block, and the completion state shown to a customer. The receipt—the blockchain execution result—and the contract-specific outcome require subsequent checks.
ERC-20's transfer returns a bool, but that return value does not appear directly
in an ordinary transaction receipt or this HTTP response.
The service must check outcomes that match the contract's semantics, such as transfer events and balances, and apply its required confirmation conditions.
The details of retries and waiting for confirmation belong to the broader transaction lifecycle.
Once success has been confirmed in this example, A holds 90 tokens and B holds 10.
Later balance queries return the integer strings "90" and "10".
The customer's request to “send 10” is complete only when the path reaches this result, with the total still equal to 100.
What operating a generated API involves
Registering an API creates routes and a specification. Operating it also means managing its execution destination, the representation of input and output values, and the criteria used to interpret transaction state. Passing one small number through a documentation screen does not verify all those connections.
For example, retaining "100" as a string in a balance response, preserving the last digit of a large integer in both directions,
and using a returned transaction hash to find the execution result are distinct checks.
A test that only matches function names to URLs can miss them.
The design history associated with API Service Factory includes Korean registered patent No. 10-2887854, “System and method for providing APIs through smart contract format conversion” (a descriptive translation of the title). The retrospective on the path from Polsto to BXB describes how this integration problem developed into a product.
The automation reduces interface glue code that would otherwise be repeated for every contract. The business system decides what to execute and under whose authority; BXB connects the request's types to its execution destination. Along that path, an amount of 10 must remain 10, and a transaction identifier must lead to a confirmed result.