UNPKG

@btc-vision/transaction

Version:

OPNet transaction library allows you to create and sign transactions for the OPNet network.

441 lines (324 loc) 14.7 kB
# ABICoder ABI encoding and decoding for OPNet smart contracts. Provides method selector generation, calldata encoding, and response decoding using a binary serialization format. > **Key difference from Ethereum:** OPNet uses **SHA256** for method selectors (first 4 bytes), **not** Keccak256. This is a fundamental distinction -- Ethereum-style ABI tools will produce incorrect selectors for OPNet contracts. **Source:** [`src/abi/ABICoder.ts`](../../src/abi/ABICoder.ts) --- ## Table of Contents - [Overview](#overview) - [ABIDataTypes Enum](#abidatatypes-enum) - [Type Mapping Table](#type-mapping-table) - [API Reference](#api-reference) - [encodeSelector](#encodeselectorselectoridentifier-string-string) - [numericSelectorToHex](#numericselectortohexselector-number-string) - [decodeData](#decodedatadata-uint8array-types-abitype-unknown) - [decodeSingleValue](#decodesinglevaluereader-binaryreader-type-abitype-unknown) - [ABI Type System](#abi-type-system) - [Simple Types](#simple-types) - [Tuple Types](#tuple-types) - [Struct Types](#struct-types) - [Examples](#examples) - [Encoding a Method Selector](#encoding-a-method-selector) - [Decoding Contract Response Data](#decoding-contract-response-data) - [Decoding Tuples and Structs](#decoding-tuples-and-structs) - [Building Calldata for Contract Calls](#building-calldata-for-contract-calls) - [Navigation](#navigation) --- ## Overview The `ABICoder` class is the bridge between TypeScript values and the binary format used by OPNet smart contracts. It handles two core tasks: 1. **Selector generation** -- Computing 4-byte method selectors from function signature strings using SHA256. 2. **Data decoding** -- Reading binary response data from contracts and converting it into typed TypeScript values. ```mermaid flowchart LR subgraph Encoding SIG["Function Signature<br/>'transfer(address,uint256)'"] --> SHA["SHA256 Hash"] SHA --> SEL["First 4 bytes<br/>Method Selector"] end subgraph Decoding BIN["Binary Response<br/>(Uint8Array)"] --> BR["BinaryReader"] BR --> DEC["ABICoder.decodeData()"] TYPES["ABI Type Array<br/>[ADDRESS, UINT256]"] --> DEC DEC --> VALUES["TypeScript Values<br/>[Address, bigint]"] end ``` --- ## ABIDataTypes Enum The `ABIDataTypes` enum defines all supported primitive and composite types for ABI encoding/decoding. ```typescript import { ABIDataTypes } from '@btc-vision/transaction'; ``` ### Unsigned Integers | Enum Value | Description | |-----------|-------------| | `ABIDataTypes.UINT8` | 8-bit unsigned integer (0--255) | | `ABIDataTypes.UINT16` | 16-bit unsigned integer | | `ABIDataTypes.UINT32` | 32-bit unsigned integer | | `ABIDataTypes.UINT64` | 64-bit unsigned integer | | `ABIDataTypes.UINT128` | 128-bit unsigned integer | | `ABIDataTypes.UINT256` | 256-bit unsigned integer | ### Signed Integers | Enum Value | Description | |-----------|-------------| | `ABIDataTypes.INT8` | 8-bit signed integer (-128 to 127) | | `ABIDataTypes.INT16` | 16-bit signed integer | | `ABIDataTypes.INT32` | 32-bit signed integer | | `ABIDataTypes.INT64` | 64-bit signed integer | | `ABIDataTypes.INT128` | 128-bit signed integer | ### Basic Types | Enum Value | Description | |-----------|-------------| | `ABIDataTypes.BOOL` | Boolean (true/false) | | `ABIDataTypes.ADDRESS` | OPNet address (compact) | | `ABIDataTypes.EXTENDED_ADDRESS` | Extended OPNet address (full public key) | | `ABIDataTypes.STRING` | Length-prefixed UTF-8 string | | `ABIDataTypes.BYTES` | Length-prefixed arbitrary bytes | | `ABIDataTypes.BYTES4` | Fixed 4-byte value | | `ABIDataTypes.BYTES32` | Fixed 32-byte value | ### Tuple / Map Types | Enum Value | Description | |-----------|-------------| | `ABIDataTypes.ADDRESS_UINT256_TUPLE` | Map of Address to uint256 | | `ABIDataTypes.EXTENDED_ADDRESS_UINT256_TUPLE` | Map of ExtendedAddress to uint256 | ### Signature Types | Enum Value | Description | |-----------|-------------| | `ABIDataTypes.SCHNORR_SIGNATURE` | Schnorr signature (64 bytes) | ### Array Types | Enum Value | Description | |-----------|-------------| | `ABIDataTypes.ARRAY_OF_ADDRESSES` | Array of Address values | | `ABIDataTypes.ARRAY_OF_EXTENDED_ADDRESSES` | Array of ExtendedAddress values | | `ABIDataTypes.ARRAY_OF_UINT256` | Array of uint256 values | | `ABIDataTypes.ARRAY_OF_UINT128` | Array of uint128 values | | `ABIDataTypes.ARRAY_OF_UINT64` | Array of uint64 values | | `ABIDataTypes.ARRAY_OF_UINT32` | Array of uint32 values | | `ABIDataTypes.ARRAY_OF_UINT16` | Array of uint16 values | | `ABIDataTypes.ARRAY_OF_UINT8` | Array of uint8 values | | `ABIDataTypes.ARRAY_OF_STRING` | Array of strings | | `ABIDataTypes.ARRAY_OF_BYTES` | Array of byte arrays | | `ABIDataTypes.ARRAY_OF_BUFFERS` | Array of raw buffers | --- ## Type Mapping Table Each `ABIDataTypes` value maps to a specific TypeScript type at runtime: | ABIDataTypes | TypeScript Type | Size / Notes | |-------------|----------------|-------------| | `UINT8` | `number` | 1 byte | | `UINT16` | `number` | 2 bytes | | `UINT32` | `number` | 4 bytes | | `UINT64` | `bigint` | 8 bytes | | `UINT128` | `bigint` | 16 bytes | | `UINT256` | `bigint` | 32 bytes | | `INT8` | `number` | 1 byte, signed | | `INT16` | `number` | 2 bytes, signed | | `INT32` | `number` | 4 bytes, signed | | `INT64` | `bigint` | 8 bytes, signed | | `INT128` | `bigint` | 16 bytes, signed | | `BOOL` | `boolean` | 1 byte | | `ADDRESS` | `Address` | Compact address | | `EXTENDED_ADDRESS` | `Address` | Full public key address | | `STRING` | `string` | u32 length prefix + UTF-8 | | `BYTES` | `Uint8Array` | u32 length prefix + data | | `BYTES4` | `Uint8Array` | Fixed 4 bytes | | `BYTES32` | `Uint8Array` | Fixed 32 bytes | | `SCHNORR_SIGNATURE` | `SchnorrSignature` | 128 bytes (64-byte extended address + 64-byte signature) | | `ADDRESS_UINT256_TUPLE` | `AddressMap<bigint>` | Address-keyed map | | `EXTENDED_ADDRESS_UINT256_TUPLE` | `ExtendedAddressMap<bigint>` | Extended address-keyed map | | `ARRAY_OF_ADDRESSES` | `Address[]` | u16 count prefix | | `ARRAY_OF_EXTENDED_ADDRESSES` | `Address[]` | u16 count prefix | | `ARRAY_OF_UINT256` | `bigint[]` | u16 count prefix | | `ARRAY_OF_UINT128` | `bigint[]` | u16 count prefix | | `ARRAY_OF_UINT64` | `bigint[]` | u16 count prefix | | `ARRAY_OF_UINT32` | `number[]` | u16 count prefix | | `ARRAY_OF_UINT16` | `number[]` | u16 count prefix | | `ARRAY_OF_UINT8` | `number[]` | u16 count prefix | | `ARRAY_OF_STRING` | `string[]` | u16 count prefix | | `ARRAY_OF_BYTES` | `Uint8Array[]` | u16 count prefix | | `ARRAY_OF_BUFFERS` | `Uint8Array[]` | u16 count prefix | --- ## API Reference ### `encodeSelector(selectorIdentifier: string): string` Computes the method selector for a function signature using SHA256. Returns the first 4 bytes of the SHA256 hash as a hex string. **Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `selectorIdentifier` | `string` | Function signature string (e.g., `'transfer(address,uint256)'`) | **Returns:** `string` -- 8-character hex string representing the 4-byte selector. ```typescript const coder = new ABICoder(); // SHA256-based selector (NOT Keccak256) const selector = coder.encodeSelector('transfer(address,uint256)'); console.log(selector); // e.g., "a9059cbb" (will differ from Ethereum's selector) const balanceOf = coder.encodeSelector('balanceOf(address)'); console.log(balanceOf); // 4-byte hex selector ``` --- ### `numericSelectorToHex(selector: number): string` Converts a numeric selector to its hex string representation. **Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `selector` | `number` | Numeric selector value | **Returns:** `string` -- Hex representation of the selector. ```typescript const coder = new ABICoder(); const hex = coder.numericSelectorToHex(0xa9059cbb); console.log(hex); // "a9059cbb" ``` --- ### `decodeData(data: Uint8Array, types: AbiType[]): unknown[]` Decodes binary response data from a contract call according to the specified ABI types. Reads values sequentially from the data using a `BinaryReader`. **Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `data` | `Uint8Array` | Raw binary response data from a contract | | `types` | `AbiType[]` | Ordered array of expected ABI types | **Returns:** `unknown[]` -- Array of decoded values, one per type entry. ```typescript const coder = new ABICoder(); // Decode a response containing an address and a uint256 balance const result = coder.decodeData(responseBytes, [ ABIDataTypes.ADDRESS, ABIDataTypes.UINT256, ]); const address = result[0] as Address; const balance = result[1] as bigint; ``` --- ### `decodeSingleValue(reader: BinaryReader, type: AbiType): unknown` Decodes a single value from a `BinaryReader` based on the ABI type. Supports all `ABIDataTypes` enum values, tuple arrays, and struct objects. **Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `reader` | `BinaryReader` | Active binary reader positioned at the value | | `type` | `AbiType` | The ABI type to decode | **Returns:** `unknown` -- The decoded value. The type parameter accepts three forms: - **Simple type:** An `ABIDataTypes` enum value (e.g., `ABIDataTypes.UINT256`) - **Tuple:** An array of `AbiType` values (e.g., `[ABIDataTypes.ADDRESS, ABIDataTypes.UINT256]`) - **Struct:** An object mapping field names to `AbiType` values (e.g., `{ owner: ABIDataTypes.ADDRESS, balance: ABIDataTypes.UINT256 }`) --- ## ABI Type System The `AbiType` type is a recursive union that supports three forms: ```typescript type AbiType = ABIDataTypes | AbiType[] | { [field: string]: AbiType }; ``` ### Simple Types Use `ABIDataTypes` enum values directly for primitive types: ```typescript const types: AbiType[] = [ABIDataTypes.ADDRESS, ABIDataTypes.UINT256, ABIDataTypes.BOOL]; ``` ### Tuple Types An array of `AbiType` values represents a tuple. How it decodes depends on the element count: - **Single-element tuple** `[ABIDataTypes.UINT256]` -- Decodes as a flat array of `bigint` values (u16 count prefix, then values). - **Multi-element tuple** `[ABIDataTypes.ADDRESS, ABIDataTypes.UINT256]` -- Decodes as an array of tuple entries, where each entry is an array of decoded values (u16 count prefix, then entries). ```typescript // Single-element tuple: returns bigint[] const balances = coder.decodeData(data, [[ABIDataTypes.UINT256]]); // Multi-element tuple: returns [Address, bigint][] const pairs = coder.decodeData(data, [ [ABIDataTypes.ADDRESS, ABIDataTypes.UINT256], ]); ``` ### Struct Types An object mapping field names to `AbiType` values represents a struct. Structs are decoded inline (no count prefix) into a `Record<string, unknown>`: ```typescript // Struct type: returns { owner: Address, balance: bigint, active: boolean } const result = coder.decodeData(data, [ { owner: ABIDataTypes.ADDRESS, balance: ABIDataTypes.UINT256, active: ABIDataTypes.BOOL, }, ]); const info = result[0] as { owner: Address; balance: bigint; active: boolean }; ``` --- ## Examples ### Encoding a Method Selector ```typescript import { ABICoder } from '@btc-vision/transaction'; const coder = new ABICoder(); // Generate selectors for common ERC-20-like methods const transferSelector = coder.encodeSelector('transfer(address,uint256)'); const approveSelector = coder.encodeSelector('approve(address,uint256)'); const balanceOfSelector = coder.encodeSelector('balanceOf(address)'); console.log('transfer:', transferSelector); console.log('approve:', approveSelector); console.log('balanceOf:', balanceOfSelector); ``` ### Decoding Contract Response Data ```typescript import { ABICoder, ABIDataTypes } from '@btc-vision/transaction'; import type { Address } from '@btc-vision/transaction'; const coder = new ABICoder(); // Decode a simple uint256 balance response const balanceResult = coder.decodeData(responseBytes, [ABIDataTypes.UINT256]); const balance = balanceResult[0] as bigint; console.log('Balance:', balance); // Decode a multi-value response (e.g., token info) const tokenInfo = coder.decodeData(responseBytes, [ ABIDataTypes.STRING, // name ABIDataTypes.STRING, // symbol ABIDataTypes.UINT8, // decimals ABIDataTypes.UINT256, // totalSupply ]); const name = tokenInfo[0] as string; const symbol = tokenInfo[1] as string; const decimals = tokenInfo[2] as number; const totalSupply = tokenInfo[3] as bigint; console.log(`${name} (${symbol}): ${totalSupply} (${decimals} decimals)`); ``` ### Decoding Tuples and Structs ```typescript import { ABICoder, ABIDataTypes } from '@btc-vision/transaction'; const coder = new ABICoder(); // Decode an array of addresses (single-element tuple) const holders = coder.decodeData(data, [[ABIDataTypes.ADDRESS]]); const addressList = holders[0] as Address[]; // Decode an array of (address, balance) pairs (multi-element tuple) const balances = coder.decodeData(data, [ [ABIDataTypes.ADDRESS, ABIDataTypes.UINT256], ]); const pairs = balances[0] as [Address, bigint][]; for (const [addr, bal] of pairs) { console.log(`${addr}: ${bal}`); } // Decode a struct const config = coder.decodeData(data, [ { admin: ABIDataTypes.ADDRESS, maxSupply: ABIDataTypes.UINT256, paused: ABIDataTypes.BOOL, }, ]); const configObj = config[0] as { admin: Address; maxSupply: bigint; paused: boolean }; ``` ### Building Calldata for Contract Calls When calling a contract, you encode the method selector and write the parameters using `BinaryWriter`: ```typescript import { ABICoder, BinaryWriter, ABIDataTypes } from '@btc-vision/transaction'; const coder = new ABICoder(); // 1. Get the method selector const selector = coder.encodeSelector('transfer(address,uint256)'); // 2. Build the calldata with BinaryWriter const writer = new BinaryWriter(); // Write the 4-byte selector const selectorBytes = new Uint8Array( selector.match(/.{2}/g)!.map((b) => parseInt(b, 16)), ); writer.writeBytes(selectorBytes); // Write the parameters writer.writeAddress(recipientAddress); // ADDRESS parameter writer.writeU256(amount); // UINT256 parameter const calldata = writer.getBuffer(); // 3. Use calldata in an interaction transaction const interactionParams = { to: contractAddress, calldata, // ... other params }; ``` --- ## Navigation - **Previous:** [BinaryReader](../binary/binary-reader.md) - **Next:** [Address Types](../addresses/address-types.md) - **Up:** [README](../README.md) -- Table of Contents