@casper-ecosystem/odra-js-client
v1.0.0
Published
JavaScript/TypeScript utilities for interacting with Odra smart contracts on the Casper Network
Readme
@casper-ecosystem/odra-js-client
JavaScript/TypeScript utilities for interacting with Odra smart contracts on the Casper Network.
Built on top of casper-js-sdk.
Installation
npm install @casper-ecosystem/odra-js-client casper-js-sdkQuick start
import { OdraClient } from '@casper-ecosystem/odra-js-client';
import { Args, CLValue } from 'casper-js-sdk';
import { readFileSync } from 'fs';
const client = new OdraClient(
'http://<Node Address>:7777/rpc',
'casper-test'
);
client.setContractHash(
'834d3e29...', // bare hex, "contract-hash-...", or "hash-..." all accepted
'f1127fd1...', // contract package hash (required for write operations)
);
// Read a Var<T> field
const name = await client.getVar('name');
console.log(name?.toString()); // e.g. "MyToken"
// Read a Mapping<K, V> entry
const balance = await client.getMappingItem('balances', 'account-hash-abc...');
// Call a non-payable entry point
await client.call(
'transfer',
Args.fromMap({
recipient: CLValue.newCLString('account-hash-...'),
amount: CLValue.newCLUInt256('1000000000'),
}),
{
sender: publicKey,
paymentAmount: '2500000000',
signingKeys: [privateKey],
},
);
// Call a payable entry point via the Odra proxy WASM
const proxyWasm = new Uint8Array(readFileSync('proxy_caller.wasm'));
await client.callPayable(
'stake',
Args.fromMap({}),
'2000000000000', // amount in motes
proxyWasm,
{
sender: publicKey,
paymentAmount: '5000000000',
signingKeys: [privateKey],
},
);API
OdraClient
High-level client. Wraps all utilities into a single class.
| Method | Description |
| ---------------------------------------------------------- | -------------------------------------------------------------------------------- |
| setContractHash(hash, packageHash?) | Configure target contract. Accepts bare hex or prefixed strings. Returns this. |
| resolveContractHash() | Auto-resolve and cache the latest contract hash from the package hash. |
| getNamedKeys() | List all named keys on the contract. |
| getVar(name) | Read an Odra Var<T> field by named key name. |
| getMappingItem(name, key) | Read one entry from an Odra Mapping<K,V> dictionary. |
| getState(index, key?, blockHeight?) | Read raw bytes from Odra's internal state dictionary by field index. |
| call(entryPoint, args, params) | Submit a non-payable contract call transaction. |
| callPayable(entryPoint, innerArgs, amount, wasm, params) | Submit a payable call via the Odra proxy WASM pattern. |
OdraStateDecoder
Sequential byte-stream decoder for packed Odra struct state.
Odra stores every Var<T> / Mapping<K,V> value as Casper-serialised bytes inside a List<U8> CLValue. The decoder skips the 4-byte count prefix automatically.
Fields must be read in the same order they are declared in the Rust struct. Reading out of order will silently produce incorrect values.
import { getOdraState, OdraStateDecoder } from '@casper-ecosystem/odra-js-client';
import { RpcClient, HttpHandler } from 'casper-js-sdk';
const rpc = new RpcClient(new HttpHandler('http://<Node Address>:7777/rpc'));
const raw = await getOdraState(rpc, contractHash, BONDING_CURVE_INDEX, tokenKey);
const dec = new OdraStateDecoder(raw);
const csprReserve = dec.readU256();
const initialPrice = dec.readU256();
const graduated = dec.readBool();
const graduatedAt = dec.readOptionU64(); // bigint | null| Method | CL type | Return |
| -------------------- | -------------------- | ----------------------- |
| readBool() | Bool | boolean |
| readU8() | U8 | number |
| readU32() | U32 | number |
| readU64() | U64 | bigint |
| readU128() | U128 | bigint |
| readU256() | U256 | bigint |
| readU512() | U512 | bigint |
| readString() | String | string |
| readKey() | Key | string (prefixed) |
| readKeyBytes() | Key | Uint8Array (33 bytes) |
| readOption(fn) | Option<T> | T \| null |
| readOptionU64() | Option<U64> | bigint \| null |
| readOptionU256() | Option<U256> | bigint \| null |
| readOptionString() | Option<String> | string \| null |
| readFixedBytes(n) | ByteArray(N) | Uint8Array |
| readBytes() | Bytes / List<U8> | Uint8Array |
Proxy call utilities
import { isOdraProxyCall, parseOdraProxyCallArgs, buildOdraProxyCallArgs } from '@casper-ecosystem/odra-js-client';
// Detect an Odra proxy call in a transaction scanner
if (isOdraProxyCall(tx.payload.fields.args)) {
const { packageHash, entryPoint, innerArgs, amount } = parseOdraProxyCallArgs(
tx.payload.fields.args,
);
}
// Build outer args for a proxy WASM transaction
const outerArgs = buildOdraProxyCallArgs(
packageHashHex,
'stake',
innerArgs,
'2000000000000', // omit for non-payable
);Low-level state helpers
import {
getOdraContractNamedKeys,
getOdraContractVar,
getOdraContractMappingItem,
getOdraState,
deriveOdraStateKey,
getContractHashFromPackage,
} from '@casper-ecosystem/odra-js-client';Payable entry points - how it works
Odra payable entry points require a Session transaction (inline WASM) instead of a standard contract call. This is because StoredVersionedContractByPackageHash cannot transfer CSPR from the caller's main purse to the contract's purse.
The proxy WASM accepts:
| Arg | CL type | Description |
| ---------------- | --------------- | ---------------------------------------------------------- |
| package_hash | ByteArray(32) | Target contract package hash |
| entry_point | String | Entry point name |
| args | List<U8> | Casper-serialised inner Args |
| attached_value | U512 | Amount the contract reads via env().attached_value() |
| amount | U512 | Amount the Casper node uses to perform the actual transfer |
amountandattached_valuecarry the same mote value but serve different purposes.
Development
npm install
npm test # run unit tests
npm run build # compile to dist/