# How to Deploy a WASM Smart Contract on Amadeus Protocol: A Complete Guide

> Deploy a WASM smart contract on Amadeus Protocol. Learn how to compile and submit your .wasm binary using Testnet.deploy/2 or JSON-RPC for node validation and storage.

- Repository: [Amadeus Protocol/node](https://github.com/amadeusprotocol/node)
- Tags: how-to-guide
- Published: 2026-08-20

---

**Deploy a WASM smart contract on Amadeus Protocol by reading the compiled `.wasm` binary, submitting it via `Testnet.deploy/2` or the JSON-RPC API, and letting the node validate and store the bytecode under your account.**

Amadeus Protocol executes smart contracts compiled to WebAssembly (WASM), providing deterministic gas metering and fast startup through the Wasmer runtime. This guide walks through the deployment flow, validation rules, and interaction patterns based on the official `amadeusprotocol/node` source code.

## Prerequisites: Compiling Your Contract

Before deployment, compile your contract to a `.wasm` binary. The repository includes AssemblyScript examples for reference:

```bash

# Build the sample counter contract

./contract_samples/assemblyscript/build_and_validate.sh

```

This produces `counter.wasm`, which you will deploy in the next step. The source for this example lives at [[`contract_samples/assemblyscript/0_counter.ts`](https://github.com/amadeusprotocol/node/blob/main/contract_samples/assemblyscript/0_counter.ts)](https://github.com/amadeusprotocol/node/blob/main/contract_samples/assemblyscript/0_counter.ts) in the repository.

## Deploying via Elixir: The `Testnet.deploy/2` Method

The simplest approach uses the Elixir helper module. In [`ex/lib/misc/testnet.ex#L16-L24`](https://github.com/amadeusprotocol/node/blob/main/ex/lib/misc/testnet.ex#L16-L24), the `Testnet.deploy/2` function handles file I/O and transaction construction:

```elixir

# Load your trainer credentials from application config

iex> pk = Application.fetch_env!(:ama, :trainer_pk)
iex> sk = Application.fetch_env!(:ama, :trainer_sk)

# Deploy the WASM contract

iex> Testnet.deploy("/path/to/counter.wasm")
:ok

```

Under the hood, this function:
1. Reads the WASM file with `File.read!/1`
2. Constructs a `"Contract.deploy"` transaction
3. Broadcasts it to the Amadeus network

## Deploying via JSON-RPC: Direct API Access

For non-Elixir clients, use the JSON-RPC endpoint with a base64-encoded payload:

```json
{
  "method": "contract_deploy",
  "params": {
    "wasm": "<base64-encoded .wasm file>",
    "init_func": null
  },
  "jsonrpc": "2.0",
  "id": 1
}

```

The node responds with a transaction hash. Once confirmed, the contract becomes addressable by your account's public key.

## Node-Side Validation: How Amadeus Secures WASM Contracts

Every deployment triggers `contract_validate`, implemented across two layers for security and performance.

### Rust NIF Entry Point

In [`ex/native/rdb/src/lib.rs#L819-L823`](https://github.com/amadeusprotocol/node/blob/main/ex/native/rdb/src/lib.rs#L819-L823), the `contract_validate` function receives the bytecode and forwards it to the consensus validator:

```rust
// Simplified flow: Elixir NIF -> Rust validation -> Wasmer checks
contract_validate(bytecode) -> consensus_apply::contract_validate(bytecode)

```

### Module Limits and DoS Prevention

The `check_module_limits` function in [`ex/native/rdb/src/consensus/bic/wasm.rs#L648-L710`](https://github.com/amadeusprotocol/node/blob/main/ex/native/rdb/src/consensus/bic/wasm.rs#L648-L710) enforces strict boundaries:

- **Binary size**: `WASM_MAX_BINARY_SIZE` hard limit
- **Function count**: Maximum exported and internal functions
- **Global/Export/Import counts**: Prevents resource exhaustion
- **Data section rules**: Restricts memory initialization patterns

Validation aborts the entire transaction if any limit is breached, preventing malformed or malicious modules from reaching storage.

### Gas Metering Configuration

Execution costs are predictable. The constant `COST_PER_OP_WASM` in [`ex/native/rdb/src/consensus/bic/protocol.rs#L50`](https://github.com/amadeusprotocol/node/blob/main/ex/native/rdb/src/consensus/bic/protocol.rs#L50) defines gas per WASM operation, consumed by the metering middleware during runtime.

## Calling Deployed Contracts

After deployment, interact with your contract using `Testnet.call/4`:

```elixir

# Call the "increment" function with argument "2"

iex> Testnet.call(sk, pk, "increment", ["2"])
{:ok, result}  # Returns the updated counter value

```

The runtime flow in [`ex/native/rdb/src/consensus/bic/wasm.rs#L931-L959`](https://github.com/amadeusprotocol/node/blob/main/ex/native/rdb/src/consensus/bic/wasm.rs#L931-L959) handles:
1. Retrieving stored bytecode from RocksDB (key: `"account:<pk>:attribute:bytecode"`)
2. Instantiating a Wasmer module with the singlepass compiler
3. Injecting arguments and executing the requested exported function
4. Returning results with gas consumption

## Wasmer Runtime Architecture

Amadeus Protocol builds on **Wasmer 7.1.0** with two critical middleware layers:

- **Singlepass compiler**: Fast compilation without optimizations, ensuring deterministic execution time
- **Metering middleware**: Injects gas accounting operations into every WASM instruction

This combination provides both performance and predictable resource limits for smart contract execution.

## Summary

- **Compile** your contract to WASM using AssemblyScript, Rust, or any WASM-targeting language
- **Deploy** via `Testnet.deploy/2` in Elixir or the `contract_deploy` JSON-RPC method
- **Validate** automatically through `contract_validate` with strict module limits
- **Store** bytecode under your account key in RocksDB after passing validation
- **Call** functions through `Testnet.call/4` or equivalent RPC, with Wasmer handling execution and gas metering

## Frequently Asked Questions

### What WASM compilers are compatible with Amadeus Protocol?

Any compiler producing valid WebAssembly 1.0+ output works, including AssemblyScript, Rust (`cargo-contract`), TinyGo, and C/C++ via Emscripten. The critical requirement is passing the `check_module_limits` validation in [[`wasm.rs`](https://github.com/amadeusprotocol/node/blob/main/wasm.rs)](https://github.com/amadeusprotocol/node/blob/main/ex/native/rdb/src/consensus/bic/wasm.rs), which restricts features like multi-value returns and bulk memory operations if they exceed configured bounds.

### Why did my deployment transaction fail with a validation error?

Amadeus rejects WASM modules exceeding size limits, function counts, global counts, or import/export limits defined in [`wasm.rs#L648-710`](https://github.com/amadeusprotocol/node/blob/main/ex/native/rdb/src/consensus/bic/wasm.rs#L648-L710). Run the [`build_and_validate.sh`](https://github.com/amadeusprotocol/node/blob/main/build_and_validate.sh) script from the contract samples to check your binary against these constraints before deployment.

### How is gas calculated for WASM contract execution?

Each WASM instruction consumes `COST_PER_OP_WASM` gas units as defined in [`protocol.rs#L50`](https://github.com/amadeusprotocol/node/blob/main/ex/native/rdb/src/consensus/bic/protocol.rs#L50). The metering middleware instruments your bytecode at load time, injecting accounting operations that track consumption during `call_contract` execution in [`wasm.rs#L931-959`](https://github.com/amadeusprotocol/node/blob/main/ex/native/rdb/src/consensus/bic/wasm.rs#L931-L959).

### Can I upgrade a contract after deployment?

The source analysis reveals no native upgrade mechanism in the validation or storage layers. Contracts are immutable once stored under `"account:<pk>:attribute:bytecode"`. To deploy updated logic, submit a new `Contract.deploy` transaction from the same account—this overwrites the stored bytecode with your new version.