How to Interact with Amadeus Protocol Smart Contracts: Complete Developer Guide

Amadeus Protocol smart contracts are deployed and called through the Elixir node's REPL or JSON-RPC API, using AssemblyScript-compiled WASM binaries signed by a trainer key pair.

The Amadeus Protocol node (amadeusprotocol/node) provides a unified interface for smart contract interaction. Contracts are written in AssemblyScript, compiled to WebAssembly (.wasm), and executed by the node's WebAssembly runtime. Whether you prefer the interactive Elixir REPL or standard HTTP JSON-RPC calls, the underlying transaction flow remains identical: construct a signed transaction, submit it to the transaction pool, and await block inclusion.

Deploying Smart Contracts to the Testnet

The Testnet module in ex/lib/misc/testnet.ex provides the primary interface for contract deployment during development and testing.

Prerequisites: Trainer Key Configuration

Before deploying, your node must have a trainer key pair configured in the application environment:


# These are automatically set when running a local testnet node

pk = Application.fetch_env!(:ama, :trainer_pk)  # public key

sk = Application.fetch_env!(:ama, :trainer_sk)  # private key (signing key)

The trainer key serves as the default account for gas fees and contract ownership on local testnets.

Deploying a Compiled WASM Contract

Use Testnet.deploy/1 to upload a compiled .wasm file:


# Absolute or relative path to the compiled contract

contract_path = "/path/to/node/contract_samples/assemblyscript/counter.wasm"
{:ok, contract_address} = Testnet.deploy(contract_path)

The deploy function:

Sample contracts are located at contract_samples/assemblyscript/ (e.g., counter.wasm), providing ready-to-deploy examples for testing.

Calling Contract Methods

Once deployed, interact with contracts via Testnet.call/4 or the JSON-RPC equivalent.

REPL-Based Contract Calls

The four-parameter call/4 function in ex/lib/misc/testnet.ex follows the signature:

Testnet.call(private_key, public_key, function_name, args_list)

Example: interacting with the counter contract


# Read initial state

{:ok, value} = Testnet.call(sk, pk, "get", [])
IO.puts("Current counter: #{value}")

# => "0"

# Execute state-changing call (increment by 3)

{:ok, tx_hash} = Testnet.call(sk, pk, "increment", ["3"])

# Verify the change

{:ok, new_value} = Testnet.call(sk, pk, "get", [])
IO.puts("New counter: #{new_value}")

# => "3"

Key implementation details from the source:

  • Transaction construction: Testnet.call/4 builds a Tx struct as defined in ex/lib/consensus/models/tx.ex
  • Signing: Uses the provided private_key to cryptographically sign the transaction
  • Submission: Pushes to the mempool via ex/lib/node/txpool.ex
  • Result decoding: Automatically decodes the contract's return value from the execution trace

HTTP JSON-RPC Contract Calls

For external clients, the ApiContract handler in ex/lib/api/api_contract.ex exposes identical functionality:

curl -X POST https://nodes.amadeus.bot/rpc \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "contract.call",
    "params": {
      "signer": "0x<private_key_hex>",
      "contract": "0x<contract_address>",
      "function": "increment",
      "args": ["5"]
    },
    "id": 1
  }'

Response format:

{
  "jsonrpc": "2.0",
  "result": {
    "tx_hash": "0x...",
    "return_value": "8"
  },
  "id": 1
}

The RPC handler constructs the same internal transaction structure as the REPL, ensuring behavioral parity between interfaces.

Transaction Lifecycle and Architecture

Understanding the flow helps debug failed calls and optimize integration timing.

Step 1: Transaction Structure (ex/lib/consensus/models/tx.ex)

A contract call transaction contains:

  • From: caller's public key
  • To: contract address (zero address for deployments)
  • Data: ABI-encoded function selector and arguments
  • Nonce: sequence number preventing replay attacks
  • Signature: ECDSA signature over all fields

Step 2: Mempool Admission (ex/lib/node/txpool.ex)

Submitted transactions enter the node's transaction pool where they are:

  • Validated for signature correctness
  • Checked for sufficient balance (gas fees)
  • Ordered by nonce and fee priority

Step 3: Block Inclusion and Execution

Validators include pooled transactions in new blocks. The WASM runtime loads the contract's bytecode, executes the specified function with provided arguments, and persists state changes.

Step 4: Return Value Propagation

For read-only calls, the result returns immediately. For state-changing calls, the response includes the transaction hash; polling or WebSocket subscription confirms finality.

Key Source Files for Contract Interaction

File Purpose Critical Functions
ex/lib/misc/testnet.ex REPL helper for deployment and calls deploy/1, call/4
ex/lib/consensus/models/tx.ex Transaction data structure Tx struct, signing logic
ex/lib/node/txpool.ex Transaction mempool management add_transaction/1
ex/lib/api/api_contract.ex JSON-RPC contract endpoint contract.call handler
contract_samples/assemblyscript/ Reference WASM contracts counter.wasm, source .ts files

Common Integration Patterns

Pattern 1: Automated Deployment Scripts


# script/deploy.exs

defmodule Deployer do
  def run(contract_path) do
    sk = Application.fetch_env!(:ama, :trainer_sk)
    pk = Application.fetch_env!(:ama, :trainer_pk)
    
    {:ok, addr} = Testnet.deploy(contract_path)
    IO.puts("Deployed to #{addr}")
    
    # Initialize with constructor call if needed

    Testnet.call(sk, pk, "init", ["param1", "param2"])
  end
end

Deployer.run(System.argv() |> List.first())

Run with: mix run script/deploy.exs path/to/contract.wasm

Pattern 2: Multi-Client RPC Integration


# Python example using identical RPC interface

import requests, json

def call_contract(sk_hex, contract_addr, fn_name, args):
    payload = {
        "jsonrpc": "2.0",
        "method": "contract.call",
        "params": {
            "signer": sk_hex,
            "contract": contract_addr,
            "function": fn_name,
            "args": args
        },
        "id": 1
    }
    r = requests.post("https://nodes.amadeus.bot/rpc", json=payload)
    return r.json()["result"]

Summary

  • Amadeus Protocol smart contract interaction centers on the Testnet module for REPL workflows and ApiContract for HTTP JSON-RPC clients
  • AssemblyScript → WASM compilation produces deployable binaries stored in contract_samples/assemblyscript/
  • Testnet.deploy/1 handles contract creation; Testnet.call/4 handles method invocation with automatic signing
  • Transaction flow traverses tx.ex (structure) → txpool.ex (admission) → block inclusion → WASM execution
  • Identical semantics across REPL and RPC interfaces enable flexible integration from any programming environment

Frequently Asked Questions

How do I obtain a trainer key pair for testnet deployment?

The trainer keys are automatically generated when starting a local testnet node. They are stored in the application environment under :trainer_pk and :trainer_sk. For persistent deployments, export these values from your node's configuration or environment variables before starting the application.

What format should contract arguments use in Testnet.call?

Arguments are passed as strings in a list, regardless of the target type. The contract's AssemblyScript wrapper handles type conversion. For example, pass ["123"] for a u64 parameter or ["0xabc..."] for address types. The WASM runtime deserializes these according to the function's expected signature.

Can I interact with contracts without running a full node?

Yes. Public RPC endpoints like https://nodes.amadeus.bot/rpc expose the same contract.call and contract.deploy methods. You only need a valid trainer key (or any account with testnet tokens) to sign transactions; no local Elixir node is required for interaction.

Where are deployed contract addresses stored?

Contract addresses are returned by Testnet.deploy/1 and should be persisted by your application. The node does not maintain a registry of your deployments. Track the hex address in your deployment scripts, environment variables, or database for subsequent call operations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →