Skip to content
Available on private testnetTestnet: yesMainnet: Not availableSoftware: Core bad4a06ea · protocol pqwerai-v8-testnet/1Updated

Language center

Each tab is the same contract — increment() (emits Incremented(uint256) where the language supports events), get() returns (uint256) — so you can compare languages on identical behaviour. Selecting a tab switches every tab group on the site.

// SPDX-License-Identifier: MIT
pragma solidity 0.8.26;

/// Starter counter (Solidity 0.8.26). Unaudited example.
contract Counter {
    uint256 private count;
    address public immutable creator;

    event Incremented(uint256 value);

    constructor() {
        creator = msg.sender;
    }

    function increment() external {
        count += 1;
        emit Incremented(count);
    }

    function get() external view returns (uint256) {
        return count;
    }
}
# pragma version 0.4.3
# Starter counter (Vyper 0.4.3). Unaudited example.

event Incremented:
    value: uint256

CREATOR: public(immutable(address))
count: uint256

@deploy
def __init__():
    CREATOR = msg.sender

@external
def increment():
    self.count += 1
    log Incremented(value=self.count)

@view
@external
def get() -> uint256:
    return self.count
// Starter counter (Yul, compiled by solc 0.8.26). Unaudited example.
// The ABI is declared here because Yul has none:
// @abi function increment() nonpayable returns ()
// @abi function get() view returns (uint256)
// @abi event Incremented(uint256 value)
object "Counter" {
  code {
    datacopy(0, dataoffset("runtime"), datasize("runtime"))
    return(0, datasize("runtime"))
  }
  object "runtime" {
    code {
      if callvalue() { revert(0, 0) }
      switch shr(224, calldataload(0))
      case 0xd09de08a /* increment() */ {
        let v := add(sload(0), 1)
        sstore(0, v)
        mstore(0, v)
        // keccak256("Incremented(uint256)")
        log1(0, 32, 0x20d8a6f5a693f9d1d627a598e8820f7a55ee74c183aa8f1a30e8d4e8dd9a8d84)
        return(0, 0)
      }
      case 0x6d4ce63c /* get() */ {
        mstore(0, sload(0))
        return(0, 32)
      }
      default { revert(0, 0) }
    }
  }
}
/* Starter counter (Huff, compiled by Huff Neo hnc 1.7.1). Unaudited example. */
#define function increment() nonpayable returns ()
#define function get() view returns (uint256)
#define event Incremented(uint256 value)

#define constant COUNT_SLOT = FREE_STORAGE_POINTER()

#define macro INCREMENT() = takes (0) returns (0) {
    [COUNT_SLOT] sload 0x01 add      // [v]
    dup1 [COUNT_SLOT] sstore          // [v]
    0x00 mstore                       // []
    __EVENT_HASH(Incremented) 0x20 0x00 log1
    stop
}

#define macro GET() = takes (0) returns (0) {
    [COUNT_SLOT] sload 0x00 mstore
    0x20 0x00 return
}

#define macro MAIN() = takes (0) returns (0) {
    callvalue iszero novalue jumpi
    0x00 0x00 revert
    novalue:
    0x00 calldataload 0xE0 shr
    dup1 __FUNC_SIG(increment) eq inc jumpi
    dup1 __FUNC_SIG(get) eq get jumpi
    0x00 0x00 revert
    inc: INCREMENT()
    get: GET()
}
// Starter counter (Fe 26.5.1, EXPERIMENTAL). Unaudited example.
use std::abi::sol

msg CounterMsg {
    #[selector = sol("increment()")]
    Increment,
    #[selector = sol("get()")]
    Get -> u256,
}

struct CounterStore {
    value: u256,
}

pub contract Counter {
    mut store: CounterStore

    init() uses (mut store) {
        store.value = 0
    }

    recv CounterMsg {
        Increment uses (mut store) {
            store.value = store.value + 1
        }

        Get -> u256 uses (store) {
            store.value
        }
    }
}

Measured on chain 77240

All five were compiled with the pinned toolchains (target cancun), deployed, incremented once and read back by the script below on a disposable local testnet.

Language Runtime bytes Deployment gas increment() gas get() after one increment
Solidity 0.8.26 (optimizer 200, no metadata hash) 288 115,466 44,587 1
Vyper 0.4.3 207 98,394 44,431 1
Yul (solc 0.8.26 --strict-assembly --optimize) 93 73,278 44,248 1
Huff Neo 1.7.1 98 74,312 44,252 1
Fe 26.5.1 (experimental) 99 77,010 43,304 1

increment() cost is dominated by the cold SSTORE from zero to one (~22,100 gas) plus the 21,000 transaction base; the Solidity and Vyper versions also store an immutable creator at deployment. Fe's counter emits no event, which is why its call is cheaper — compare behaviour, not just numbers.

languages/build-and-deploy-all.sh
# Compile the same Counter (increment(), get()) in five languages, deploy each one, call it, and
# print the measured gas. Compilers: SOLC (solc 0.8.26), VYPER (0.4.3), HNC (Huff Neo 1.7.1), FE (Fe 26.5.1).
SOLC=${SOLC:-solc}; VYPER=${VYPER:-vyper}; HNC=${HNC:-hnc}; FE=${FE:-fe}
W="--keystore $KEYSTORE --password-file $PASSWORD_FILE --rpc-url $RPC"
mkdir -p out
"$SOLC" --evm-version cancun --optimize --metadata-hash none --bin Counter.sol -o out/sol --overwrite >/dev/null
cp out/sol/Counter.bin out/solidity.bin
"$VYPER" --evm-version cancun -f bytecode Counter.vy > out/vyper.bin
"$SOLC" --strict-assembly --evm-version cancun --optimize --bin Counter.yul | sed -n '/Binary representation:/{n;p;}' > out/yul.bin
"$HNC" Counter.huff -b -e cancun | tail -n 1 > out/huff.bin
"$FE" build --standalone Counter.fe >/dev/null && cp out/Counter.bin out/fe.bin
printf '%-9s %8s %10s %10s %s\n' language runtimeB deployGas incrGas "get() after 1 increment"
for L in solidity vyper yul huff fe; do
  BIN=0x$(tr -d ' \r\n' < out/$L.bin | sed 's/^0x//')
  TX=$(cast send --async $W --create "$BIN")
  ADDR=$(cast receipt "$TX" contractAddress --rpc-url "$RPC")
  DGAS=$(cast receipt "$TX" gasUsed --rpc-url "$RPC")
  RT=$(cast code "$ADDR" --rpc-url "$RPC")
  TX2=$(cast send "$ADDR" "increment()" --async $W)
  IGAS=$(cast receipt "$TX2" gasUsed --rpc-url "$RPC")
  printf '%-9s %8s %10s %10s %s\n' "$L" $(( (${#RT} - 2) / 2 )) "$DGAS" "$IGAS" "$(cast call "$ADDR" 'get()(uint256)' --rpc-url "$RPC")"
done

Choosing a language

Need Choose
OpenZeppelin, audits, largest ecosystem Solidity
Fewer footguns, readable code, no inline assembly Vyper
Tight control in a few hot paths Yul (inline in Solidity or standalone)
Maximal control over every opcode Huff — only with expert review
Research on new language designs Fe — experimental, never value-bearing

JavaScript, TypeScript and Python are application languages on PQWERAI: they build transactions and read state, they do not run on chain (details).


Found a problem? Report a documentation issue · Build testnet-rc1+72057fe3ec