@btc-vision/opnet-transform v0.1.5
AssemblyScript Smart Contract Transform
This package is an AssemblyScript transform that automatically scans your smart-contract source code for:
- Methods decorated with
@method,@returns, and@emit - Event classes (declared via
@eventor classes extendingNetEvent) - Constructor parameters for event classes
- ABI definitions and type definitions for each contract class
It then:
- Generates ABI files (
.abi.json) in anabis/folder for each contract class. - Generates
.d.tsdefinitions for each ABI. - Injects an
execute(...)method that dispatches calls based on a 4-byte selector.
This is useful for writing contracts in AssemblyScript while automatically producing typed ABIs and event definitions.
Table of Contents
- Configuration
- How It Works
- Example Contract
- Generated ABI Artifacts
- Typical Usage with asc
- FAQ / Tips
- License
Configuration
To use this transform, you should configure your AssemblyScript project with the following files:
tsconfig.json:
{
"extends": "@btc-vision/opnet-transform/std/assembly.json",
"include": [
"./**/*.ts",
"../contracts/**/*.ts"
]
}- Extends a basic AssemblyScript/TypeScript config from
@btc-vision/opnet-transform. - In
include, list all the.tspaths to be compiled.
asconfig.json:
{
"targets": {
"release": {
"outFile": "build/MyToken.wasm",
"textFile": "build/MyToken.wat",
"sourceMap": false,
"optimizeLevel": 3,
"shrinkLevel": 2,
"converge": true,
"noAssert": true,
"disable": [
"mutable-globals",
"sign-extension",
"nontrapping-f2i",
"bulk-memory"
],
"runtime": "stub",
"memoryBase": 0,
"initialMemory": 1,
"maximumMemory": 512,
"bindings": "esm",
"exportStart": "start",
"use": [
"abort=src/index/abort"
]
}
},
"options": {
"transform": "@btc-vision/opnet-transform"
}
}In particular:
"transform": "@btc-vision/opnet-transform"instructs AssemblyScript to run this transform on your code.- The
"targets.release"block is where you specify how to compile your.wasm, how aggressively to optimize, memory limits, etc.
With both tsconfig.json and asconfig.json in place, simply run your build script (e.g. asc --config asconfig.json)
to compile your AssemblyScript code through the transform.
How It Works
Decorators
@method(...): Marks a method as callable; this is added to the contract’s ABI.@returns(...): Defines return values for a method, which the transform adds to the ABI.@emit(...): Declares that the method will emit certain event names.
Event Classes
Event classes can be declared in two ways:
Extending
NetEvent:export class Deposit extends NetEvent { constructor(user: Address, poolId: u64, amount: u256, to: Address) { const eventData = new BytesWriter(...); eventData.writeAddress(user); ... super("Deposit", eventData); } }- Automatically recognized as an event, even without
@event.
- Automatically recognized as an event, even without
Using the
@eventDecorator:@event("Deposit") export class Deposit { user: Address; amount: u256; ... }
Methods & Returns
Each callable method can have a @method(...) decorator to:
- Override the method name for ABI purposes.
- Provide parameter definitions (either as strings or
{ name, type }objects).
A @returns(...) decorator can define the output parameters. For example:
@method("mint", { name: "to", type: ABIDataTypes.ADDRESS }, { name: "amount", type: ABIDataTypes.UINT256 })
@returns({ name: "success", type: ABIDataTypes.BOOL })Emit Decorator
Annotate a method with @emit("EventNameA", "EventNameB") if it triggers events. The transform will:
- Assign those events to the contract’s ABI.
- Generate typed event definitions (like
EventNameAEventin the.d.ts). - Warn if you reference an event that isn’t declared.
Example Contract
import { u256 } from '@btc-vision/as-bignum/assembly';
import {
Address,
AddressMap,
Blockchain,
BytesWriter,
Calldata,
DeployableOP_20,
OP20InitParameters,
BOOLEAN_BYTE_LENGTH,
} from '@btc-vision/btc-runtime/runtime';
@final
export class MyToken extends DeployableOP_20 {
public constructor() {
super();
// ...
}
public override onDeployment(_calldata: Calldata): void {
const maxSupply: u256 = u256.fromString('100000000000000000000000000000000000');
const decimals: u8 = 18;
const name: string = 'BobTheNoob';
const symbol: string = 'BOB';
this.instantiate(new OP20InitParameters(maxSupply, decimals, name, symbol));
}
@method(
{ name: 'address', type: ABIDataTypes.ADDRESS },
{ name: 'amount', type: ABIDataTypes.UINT256 },
)
@returns({ name: 'success', type: ABIDataTypes.BOOL })
public mint(calldata: Calldata): BytesWriter {
this.onlyDeployer(Blockchain.tx.sender);
const response = new BytesWriter(BOOLEAN_BYTE_LENGTH);
const resp = this._mint(calldata.readAddress(), calldata.readU256());
response.writeBoolean(resp);
return response;
}
// ...
}Missing @emit?
If you define a method that emits an event class, you can mark it:
@method()
@emit("TransferEvent")
public
transfer(calldata
:
Calldata
):
BytesWriter
{
// ...
// triggers event 'TransferEvent'
return new BytesWriter(0);
}This ensures the event gets added to the contract’s ABI.
Generated ABI Artifacts
After compiling, you’ll find a directory named abis/ containing:
<ClassName>.abi.json
Describes the methods (and their input/output types) plus any events the class uses.<ClassName>.d.ts
A TypeScript declaration file for typed usage in client code, e.g.:import { IMyToken } from "./abis/MyToken";
Each .d.ts includes:
- Event interfaces:
<EventName>Event. - CallResult types: describing the method outputs and the events it emits.
Typical Usage with asc
Use the AssemblyScript compiler, referencing asconfig.json:
npx asc -c asconfig.json- The transform scans your code for
@method,@returns,@emit, and event classes. - It automatically creates an
abis/folder next to your compiler output.
FAQ / Tips
Can I rename a method for ABI without changing its actual function name?
Yes, you can do@method("someOtherName", "uint256").How do I define multiple returns?
Use@returns("uint256", "bool")or multiple named objects:@returns({ name: "foo", type: "uint256" }, { name: "bar", type: "bool" }).Where do events go if I never reference them with @emit?
The transform logs a warning that an event was declared but never used. Unused events won’t appear in any contract’s final ABI.How do I reference events across multiple files?
As long as the event class (extendingNetEvent) is imported into the same compilation scope, the transform sees it.@emit("EventName")just has to match the event’s class name or override name (if you used@event("SomeName")).