Skip to main content

Creating Custom Calldata with BinaryWriter

The BinaryWriter class from @btc-vision/transaction is an essential tool for constructing calldata on OP_NET. It offers precise control over encoding and ensures compatibility with smart contract interactions.

Key Features of BinaryWriter
  • Encodes primitive types (U8, U256, Boolean, etc.) and complex structures (arrays, maps, tuples).
  • Designed to integrate seamlessly with OP_NET's ABI coding.
  • Optimized for gas efficiency when used correctly.

Using BinaryWriter

Instantiation

const writer = new BinaryWriter(length?);
  • length (optional): Pre-allocates the buffer to a specific size.

    Note

    Pre-allocating a buffer can improve performance when constructing large calldata.

    e.g., new BinaryWriter(1024) allocates a buffer of 1024 bytes.


Creating Function Selectors

To create a selector, use the ABICoder:

import { ABICoder } from "@btc-vision/transaction";

// Initialize ABI coder
const abiCoder = new ABICoder();

// Generate the selector for a function (e.g., "mint")
const mintSelector = Number(`0x${abiCoder.encodeSelector("mint")}`);
console.log("Mint Selector:", mintSelector);

This selector represents the unique identifier for the contract's mint function.

tip

Learn more about ABICoder for generating function selectors, encoding input parameters, and decoding returned data


Common Methods in BinaryWriter

Primitive Data Types

writer.writeU8(value: number); // Writes an unsigned 8-bit integer.
writer.writeU256(value: bigint); // Writes an unsigned 256-bit integer.
writer.writeBoolean(value: boolean); // Writes a boolean (1 byte).

Complex Structures

writer.writeAddress(value: Address); // Writes an Address object.
writer.writeTuple(values: bigint[]); // Writes a tuple of integers.
writer.writeString(value: string); // Writes a string.
writer.writeAddressValueTupleMap(map: AddressMap<bigint>); // Writes an AddressMap.

Buffer Operations

writer.getBuffer(clear: boolean = true): Uint8Array; // Returns the constructed buffer.
writer.reset(); // Resets the writer to its initial state.
writer.setOffset(offset: number); // Adjusts the current write offset.

Example: Constructing Calldata

Here’s an example of creating calldata for a mint function:

import {
ABICoder,
Address,
AddressMap,
BinaryWriter,
} from "@btc-vision/transaction";

const abiCoder = new ABICoder();
const mintSelector = Number(`0x${abiCoder.encodeSelector("mint")}`); // Generate selector

const recipient = new Address(Buffer.from("...")); // Replace with valid Address
const amount = 1000n; // Amount to mint
const decimals = 18n; // Token decimals

const calldata = new BinaryWriter(); // Create a new BinaryWriter
calldata.writeSelector(mintSelector); // Function selector
calldata.writeAddress(recipient); // Recipient's address
calldata.writeU256(BigInt(amount * 10n ** decimals)); // Amount scaled to token decimals
calldata.writeAddressValueTupleMap(new AddressMap()); // Example: Empty map
calldata.writeU256(0n); // Placeholder for additional parameters

console.log(calldata.getBuffer());

Best Practices

  • Specify an appropriate buffer size during instantiation for better performance.
  • Write compact types wherever possible, such as writeU8 for small integers.
  • Always test calldata with simulations before broadcasting to ensure correctness.
  • Use the ABICoder to generate selectors dynamically.

What’s Next?

Continue exploring contract interaction techniques: