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:
- Loads the WASM binary from disk
- Constructs a deploy transaction (defined in
ex/lib/consensus/models/tx.ex) - Signs it with the trainer private key
- Submits to
ex/lib/node/txpool.exfor block inclusion - Returns the contract address for subsequent calls
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/4builds aTxstruct as defined inex/lib/consensus/models/tx.ex - Signing: Uses the provided
private_keyto 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
Testnetmodule for REPL workflows andApiContractfor HTTP JSON-RPC clients - AssemblyScript → WASM compilation produces deployable binaries stored in
contract_samples/assemblyscript/ Testnet.deploy/1handles contract creation;Testnet.call/4handles 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →