Docs Técnicas
Ethereum Adapter
The Ethereum adapter lets any dApp built with ethers.js, viem, wagmi, or web3.js talk to a TREA contract without knowing it's TREA underneath. It speaks the same ABI language that Ethereum established as the industry standard.
The Ethereum adapter lets any dApp built with ethers.js, viem, wagmi, or web3.js talk to a TREA contract without knowing it's TREA underneath. It speaks the same ABI language that Ethereum established as the industry standard.
dApp (ethers.js) AtlasDB node
──────────────── ─────────────────────────────────────
transfer(addr, 1000) → POST /api/eth/call/:contract_id
calldata (hex) → decode calldata → ContractArg[]
→ ContractInstance::execute(@view)
← encode return → ABI bytes (hex)
result (uint256) ← { "result": "0000…03e8", "decoded": ["1000"] }Write calls (@tx) still require a signed ContractCall transaction via POST /api/transaction — the adapter handles read-only calls only. This is by design: signatures in AtlasDB are Ed25519, not Ethereum ECDSA, so write authorization stays inside the AtlasDB trust model.
Endpoints
`GET /api/eth/abi/:artifact_id`
Returns the Ethereum JSON ABI for a published contract artifact.
curl http://localhost:3001/api/eth/abi/basic-token-v1[
{ "type": "function", "name": "balanceOf",
"inputs": [{ "name": "owner", "type": "address" }],
"outputs": [{ "name": "", "type": "uint256" }],
"stateMutability": "view" },
{ "type": "function", "name": "transfer",
"inputs": [{ "name": "to", "type": "address" }, { "name": "amount", "type": "uint256" }],
"outputs": [{ "name": "out0", "type": "bool" }],
"stateMutability": "nonpayable" },
...
]`POST /api/eth/call/:contract_id`
Executes a @view function using raw Ethereum calldata.
Request body:
{
"calldata": "70a08231000000000000000000000000ab...cd",
"caller": "wallet:alice"
}| Field | Required | Description | |-------|----------|-------------| | calldata | yes | Hex-encoded Ethereum calldata (4-byte selector + ABI params) | | caller | no | Address injected as ctx.caller in the execution context |
Response (success):
{
"status": "ok",
"function": "balanceOf",
"result": "0000000000000000000000000000000000000000000000000000000000002710",
"decoded": ["10000"]
}Response (write call rejected):
{
"status": "rejected: function `transfer` is a @tx — write calls must be submitted as a signed ContractCall transaction via POST /api/transaction",
"error": "..."
}Using with ethers.js
import { ethers } from "ethers";
// 1. Fetch the ABI from the node
const res = await fetch("http://localhost:3001/api/eth/abi/basic-token-v1");
const abi = await res.json();
// 2. Encode calldata with ethers.js (no provider needed for encoding)
const iface = new ethers.Interface(abi);
const calldata = iface.encodeFunctionData("balanceOf", [
"0xab...cd", // 20-byte address
]);
// 3. Call the adapter
const callRes = await fetch("http://localhost:3001/api/eth/call/my-token-instance", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ calldata, caller: "wallet:alice" }),
});
const { result, decoded } = await callRes.json();
// 4. Decode the return value
const [balance] = iface.decodeFunctionResult("balanceOf", "0x" + result);
console.log("Balance:", balance.toString()); // "10000"Using with viem
import { encodeFunctionData, decodeFunctionResult } from "viem";
const calldata = encodeFunctionData({
abi,
functionName: "totalSupply",
});
const { result } = await fetch(`/api/eth/call/${contractId}`, {
method: "POST",
body: JSON.stringify({ calldata }),
}).then(r => r.json());
const [supply] = decodeFunctionResult({ abi, functionName: "totalSupply", data: `0x${result}` });Supported ABI types
| Ethereum type | TREA type | |---------------|-----------| | address | Address | | bool | bool | | uint8–uint256 | u8–u256 | | int64, int128 | i64, i128 | | string | String / AssetId | | bytes32 | Hash256 |
Types not in this table (arrays, tuples, bytes) are not supported and will return a rejected: error. Map and Vector storage fields are internal and not exportable via the Ethereum ABI.
Selector compatibility
The adapter uses keccak256("functionName(type1,type2,...)")[:4] — exactly the same algorithm as Ethereum. The canonical selectors for ERC-20 are:
| Function | Selector | |----------|----------| | transfer(address,uint256) | 0xa9059cbb | | transferFrom(address,address,uint256) | 0x23b872dd | | balanceOf(address) | 0x70a08231 | | approve(address,uint256) | 0x095ea7b3 | | totalSupply() | 0x18160ddd |
Any SDK that generates calldata for ERC-20 functions will automatically match TREA contracts that implement those functions.
Design notes
- Read-only by design. The adapter only executes
@viewfunctions. Write calls
remain inside the AtlasDB signing model (Ed25519 + ContractCall transaction). This is an explicit trust boundary: AtlasDB uses Ed25519, not Ethereum ECDSA. Translating ECDSA-signed writes to Ed25519 is the responsibility of an external bridge or wallet integration — not this adapter.
- Write calls do produce TREA receipts — via
POST /api/transactionwith a
signed ContractCall. Any dApp that needs write authorization must go through that path. The adapter's read endpoints are a convenience layer on top of the same TREA runtime, not a bypass of it.
- Zero-value calls.
@viewfunctions do not move value, so zero is a valid
argument (e.g. balanceOf does not take an amount). @tx functions are rejected by the adapter before reaching the runtime. If a write does reach the TREA runtime with a zero amount, it is rejected natively with invalid_amount. No additional zero-value guard is needed in the adapter.
- No proxy / no EVM. The adapter is a thin translation layer. The execution
happens in the TREA runtime, not in an EVM. All accounting semantics and audit receipts are preserved.
- Integer range.
uint256andint256values are capped atu128/i128
range. Values outside that range return an overflow error. This is intentional — TREA uses u128 as the canonical accounting integer and does not support true 256-bit arithmetic.