Migration Guide#

v0.3.0 introduces a major restructuring of the standard library. The genlayer.gl and genlayer.py intermediate packages are removed. All public API is now accessible directly under the genlayer namespace.

Import Pattern#

The recommended import pattern has changed:

# v0.2.x
from genlayer import *

# v0.3.0
import genlayer as gl
from genlayer.types import *

The from genlayer import * star-import previously brought gl (a lazy proxy to genlayer.gl), all types, and storage names into scope. Now import genlayer as gl gives you direct access to submodules (gl.contract, gl.vm, gl.message, gl.chain, gl.storage, etc.) and decorators (gl.public, gl.private). The from genlayer.types import * import brings the type aliases (u8..``u256``, i8..``i256``, Address, bigint, etc.) into local scope.

Module Path Changes#

All genlayer.py.* and genlayer.gl.* paths are removed. Here is the mapping:

v0.2.x

v0.3.0

genlayer.py.types

genlayer.types

genlayer.py.keccak

genlayer.types.keccak

genlayer.py.calldata

genlayer.calldata

genlayer.py.storage

genlayer.storage

genlayer.py.evm

genlayer.evm

genlayer.gl.vm

genlayer.vm

genlayer.gl.nondet

genlayer.nondet

genlayer.gl.eq_principle

genlayer.eq_principle

genlayer.gl.genvm_contracts

genlayer.contract

genlayer.gl.annotations

genlayer._internal.annotations

genlayer.gl.advanced

removed (see below)

Note

Type aliases (u8..``u256``, i8..``i256``) are no longer typing.NewType instances; they are now typing.Annotated[int, ...]. They still work as type annotations, but they are no longer callable, so the u256(0) wrapping is gone — pass a plain int instead:

# v0.2.x
gl.contract.deploy(code=source, value=u256(1000), salt_nonce=u256(42))

# v0.3.0
gl.contract.deploy(code=source, value=1000, salt_nonce=42)

Contract Declaration#

gl.Contract is now gl.contract.Contract:

# v0.2.x
class MyToken(gl.Contract):
    ...

# v0.3.0
class MyToken(gl.contract.Contract):
    ...

Contract Interaction#

Functions for interacting with other contracts have been renamed and moved into gl.contract:

# v0.2.x
contract = gl.get_contract_at(address)
gl.deploy_contract(code=source, args=[...])

@gl.contract_interface
class IToken:
    class View:
        def balance_of(self, owner: Address) -> u256: ...

# v0.3.0
contract = gl.contract.get_at(address)
gl.contract.deploy(code=source, args=[...])

@gl.contract.interface
class IToken:
    class View:
        def balance_of(self, owner: Address) -> u256: ...

The ContractProxy type is renamed to gl.contract.Proxy.

Contract proxies, Contract itself, and the new gl.chain.Account all implement the gl.chain.IAccount protocol, exposing address, balance, and emit_transfer(value, *, on=...).

Value Transfers and on= Parameter#

Write-call and deploy APIs now take an on keyword controlling when the message is applied, with values 'accepted' or 'finalized' (default 'finalized'):

contract.emit(value=100, on='finalized').transfer(to, amount)
gl.contract.deploy(code=source, value=1000, on='finalized', salt_nonce=42)

A new emit_transfer helper sends a plain value transfer without a method call. The receiver may catch it via Contract.__receive__ (must be @gl.public.write.payable):

contract.emit_transfer(1000, on='finalized')

Accounts#

gl.chain.Account is a new lightweight wrapper around an Address that allows querying the balance of, or emitting a transfer to, any on-chain account (contract or EoA):

acc = gl.chain.Account(some_address)
bal = acc.balance
acc.emit_transfer(100)

Message Context#

The gl.message object was a NamedTuple. It is now a module (genlayer.message) with the same fields as module-level attributes:

# v0.2.x
sender = gl.message.sender_address
value = gl.message.value

# v0.3.0 (identical usage, but gl.message is a module now)
sender = gl.message.sender_address
value = gl.message.value

gl.message_raw is now gl.message.raw. The chain ID is exposed as gl.message.chain_id (and as gl.chain.id).

Events#

The Event class has moved from genlayer.gl to genlayer.chain:

# v0.2.x
class Transfer(gl.Event):
    def __init__(self, sender: Address, to: Address, /): ...

# v0.3.0
class Transfer(gl.chain.Event):
    def __init__(self, sender: Address, to: Address, /): ...

gl.advanced.emit_raw_event(topics, blob) is now gl.chain.Event.emit_raw(topics, blob).

Advanced / Error Handling#

The genlayer.gl.advanced module is removed. Its functionality has been relocated:

# v0.2.x
gl.advanced.user_error_immediate("reason")
gl.advanced.emit_raw_event(topics, blob)

# v0.3.0
gl.vm.UserError.immediate("reason")
gl.chain.Event.emit_raw(topics, blob)

UserError now carries an arbitrary calldata-encodable payload instead of a string. The payload is accessed via .data (previously .message), and UserError.immediate accepts any calldata.Encodable:

raise gl.vm.UserError({'kind': 'InsufficientBalance', 'have': have, 'need': need})

try:
    ...
except gl.vm.UserError as e:
    payload = e.data

The error-message handler hook (__on_errored_message__) has been removed.

VM Tracing#

Trace functions have moved from genlayer.gl to genlayer.vm:

# v0.2.x
gl.trace("debug message")
gl.trace_time_micro()

# v0.3.0
gl.vm.trace("debug message")
gl.vm.trace_time_micro()

Non-deterministic Execution (gl.vm)#

Warning

gl.vm.run_nondet changed meaning. The two run_nondet functions were renamed:

  • the old run_nondet_unsafe (no validator sandbox) is now run_nondet;

  • the old run_nondet (safe, validator runs in a sandbox) is now run_nondet_default.

Code that called gl.vm.run_nondet still compiles but now silently uses the unsafe variant. To keep the previous behavior, switch to gl.vm.run_nondet_default:

# v0.2.x (safe)            # v0.3.0 (same behavior)
gl.vm.run_nondet(...)      gl.vm.run_nondet_default(...)

# v0.2.x (unsafe)          # v0.3.0 (same behavior)
gl.vm.run_nondet_unsafe(...)   gl.vm.run_nondet(...)

The high-level equivalence principles (gl.eq_principle.*) are unaffected — they were updated internally.

gl.vm.spawn_sandbox replaced the single allow_write_ops flag with three granular permissions, each effective only if the current VM holds it:

# v0.2.x
gl.vm.spawn_sandbox(fn, allow_write_ops=True)

# v0.3.0
gl.vm.spawn_sandbox(
    fn,
    allow_write_storage=True,
    allow_send_messages=True,
    allow_register_runners=True,
)

Two runtime helpers were added: gl.vm.register_runner(code) registers a runner archive and returns its custom:<hash> id, and gl.vm.map_file(runner, path_in_runner, path_in_vfs) maps a file from a runner into the VM filesystem.

Storage#

Storage types (DynArray, Array, TreeMap) and the allow decorator are accessible via gl.storage:

# v0.2.x
x: gl.DynArray[str]
m: gl.TreeMap[str, u32]
@gl.allow
class MyRecord: ...

# v0.3.0
x: gl.storage.DynArray[str]
m: gl.storage.TreeMap[str, u32]
@gl.storage.allow
class MyRecord: ...

allow_storage was renamed to allow (accessible as gl.storage.allow). A new gl.storage.Pickled[T] helper is available for storing arbitrary picklable objects.

Decorators#

gl.public and gl.private are still available directly on gl:

# Both v0.2.x and v0.3.0
@gl.public.write
def transfer(self, to: Address, amount: u256): ...

@gl.public.view
def balance_of(self, owner: Address) -> u256: ...

Non-deterministic Operations#

gl.nondet now raises a dedicated gl.nondet.NondetException (with causes and ctx) on errors instead of a bare exception. Web request helpers (gl.nondet.web.get/post/put/…) accept a sign: bool keyword to sign outbound requests with the contract’s identity.

Environment Detection#

A new top-level gl.IS_IN_VM boolean indicates whether code is running inside the GenVM, which is useful for code that is shared between contracts and off-chain tooling. The raw WASI module is available as gl.wasi. A gl.gvm32 module provides Crockford Base32 encode/decode helpers (mirroring the Rust genlayer_sdk::gvm32 implementation).

Summary of Renames#

v0.2.x

v0.3.0

gl.Contract

gl.contract.Contract

gl.contract_interface

gl.contract.interface

gl.deploy_contract(...)

gl.contract.deploy(...)

gl.get_contract_at(addr)

gl.contract.get_at(addr)

gl.ContractProxy

gl.contract.Proxy

gl.Event

gl.chain.Event

gl.advanced.user_error_immediate(...)

gl.vm.UserError.immediate(...)

gl.advanced.emit_raw_event(...)

gl.chain.Event.emit_raw(...)

gl.trace(...)

gl.vm.trace(...)

gl.trace_time_micro()

gl.vm.trace_time_micro()

gl.vm.run_nondet_unsafe(...)

gl.vm.run_nondet(...)

gl.vm.run_nondet(...)

gl.vm.run_nondet_default(...)

gl.message_raw

gl.message.raw

gl.storage.allow_storage

gl.storage.allow

gl.allow

gl.storage.allow

gl.DynArray

gl.storage.DynArray

gl.Array

gl.storage.Array

gl.TreeMap

gl.storage.TreeMap

UserError(msg: str).message

UserError(data: Encodable).data