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
@event
or 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.ts
definitions 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.
- Configuration
- How It Works
- Example Contract
- Generated ABI Artifacts
- Typical Usage with asc
- FAQ / Tips
- License
To use this transform, you should configure your AssemblyScript project with the following files:
tsconfig.json
:
- Extends a basic AssemblyScript/TypeScript config from
@btc-vision/opnet-transform
. - In
include
, list all the.ts
paths 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.
@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 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
@event
Decorator:@event("Deposit") export class Deposit { user: Address; amount: u256; ... }
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 })
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
EventNameAEvent
in the.d.ts
). - Warn if you reference an event that isn’t declared.
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;
}
// ...
}
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.
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.
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.
-
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")
).