Built-In Macros

Built-in macros are implemented by the assembler, and provide additional features beyond basic instructions, constants, or labels.

Instruction Macros

%import("...")

The %import macro expands to the instructions read from another file as if they had been typed here. The path is resolved relative to the current file.

Source: main.etk

push1 some_label
jump

%import("other.etk")

Source: other.etk

some_label:
    jumpdest
    stop

After Expansion

push1 0x03
jump

jumpdest
stop

%include("...")

The %include macro expands to the instructions read from another file, but unlike %import, the included file is assembled independently from the current file:

  • Labels from the included file are not available in the including file, and vise versa.
  • The address of the first instruction in the included file will be zero.

The path is resolved relative to the current file.

Source: main.etk

some_label:                 # <- Not visible in `other.etk`.
    push1 some_label        # <- Pushes a zero on the stack.

%include("other.etk")

Source: other.etk

different_label:            # <- Not visible in `main.etk`.
    push1 different_label   # <- ALSO pushes a zero on the stack.

After Expansion

push1 0x00
push1 0x00

%include_hex("...")

The %include_hex macro functions exactly like %include, except instead of assembling the given path, it includes the raw hexadecimal bytes.

%push(...)

The %push macro will expand to a reasonably sized push instruction for the given argument.

For example:


#![allow(unused)]
fn main() {
extern crate etk_asm;
let src = r#"
%push(hello)

hello:
    jumpdest
"#;
let mut output = Vec::new();
let mut ingest = etk_asm::ingest::Ingest::new(&mut output);
ingest.ingest(file!(), src).unwrap();
assert_eq!(output, &[0x60, 0x02, 0x5b]);
}

Will look something like the following after expansion:

push1 0x02
jumpdest

Expression Macros

selector("...")

The selector macro is useful when writing contracts that adhere to the Solidity ABI. Specifically, the selector macro expands to the four byte selector of the given function signature.

For example:


#![allow(unused)]
fn main() {
extern crate etk_asm;
let src = r#"
push4 selector("transfer(address,uint256)")    # <- expands to 0x63a9059cbb
"#;
let mut output = Vec::new();
let mut ingest = etk_asm::ingest::Ingest::new(&mut output);
ingest.ingest(file!(), src).unwrap();
assert_eq!(output, &[0x63, 0xa9, 0x05, 0x9c, 0xbb]);
}

The fully expanded source would look like:

push4 0xa9059cbb

topic("...")

The topic macro is operates similiarly to selector, except it returns the entire 32 byte hash digest. This is useful for the log opcodes.

For example:


#![allow(unused)]
fn main() {
extern crate etk_asm;
let src = r#"
push32 topic("transfer(address,uint256)")
"#;
let mut output = Vec::new();
let mut ingest = etk_asm::ingest::Ingest::new(&mut output);
ingest.ingest(file!(), src).unwrap();
assert_eq!(output, &[0x7f, 169, 5, 156, 187, 42, 176, 158, 178, 25, 88, 63, 74, 89, 165, 208, 98, 58, 222, 52, 109, 150, 43, 205, 78, 70, 177, 29, 160, 71, 201, 4, 155]);
}

The fully expanded source would look like:

push32 0xa9059cbb2ab09eb219583f4a59a5d0623ade346d962bcd4e46b11da047c9049b