Operate
Synsema Network (L1)
Synsema Network is an Avalanche L1 whose first job is to be a public registry of agents: for each agent, the hash of its program, the address that controls it, the hash of the capabilities it declares, the attestation measurement of the machine it runs on, and the Merkle roots of its audit log. Checking an agent means reading its record, not trusting whoever runs it. On top, an open EVM: payments, tokens, contracts. Gas is paid in the native token, SYN.
The chain does not run agents; agents run anywhere (your machine, a server, the platform, a TEE) and use the chain through the EVM builtins of Blockchain.
Today it is a devnet, at devnet.synsema.io: the state can be reset and SYN on it has no value. Everything on this page runs against it.
| Chain id | 7960 (0x1f18) |
| Native token | SYN, 18 decimals |
| RPC | https://devnet.synsema.io/<token>/rpc |
| Agent registry | 0x9124Ac7F5Ff9bA48aed29c78198b59a1Ba6732eF |
| Explorer | devnet.synsema.io/explorer |
| EVM | up to Cancun (compile with evm_version = "cancun") |
Access: a token, a key, some SYN§
The RPC wants a token in its path. Get one — no sign-up, 10 per address per hour, 600 requests per minute each:
curl -s -X POST https://devnet.synsema.io/token
The answer carries the token, the RPC URL and the line for your .env. Then a key of your own — any 32 random bytes — and its address:
openssl rand -hex 32
# .env, next to your programs (Synsema reads it and never prints a secret)
L1_RPC=https://devnet.synsema.io/<token>/rpc
MY_KEY=<the 64 hex characters>
-- address.syn
require secret("MY_KEY")
print(eth_address(secret("MY_KEY")))
The faucet sends 100 SYN to an address, once an hour per address:
curl -s -X POST https://devnet.synsema.io/<token>/faucet \
-H 'content-type: application/json' -d '{"address": "0x…"}'
A wallet (MetaMask, Rabby) takes the same values: RPC URL with your token, chain id 7960, symbol SYN. The home page has a button that does the three steps and adds the network to your wallet.
Register an agent§
agent.syn is the program being registered (any Synsema program). Every value that moves money is written out: nothing is signed with a default you did not see.
-- register.syn: synsema run register.syn
require env("L1_RPC")
require secret("MY_KEY")
require sign("MY_KEY")
require net("devnet.synsema.io")
require file.read("agent.syn")
let url be env("L1_RPC")
let registry be "0x9124Ac7F5Ff9bA48aed29c78198b59a1Ba6732eF"
let k be secret("MY_KEY")
let me be eth_address(k)
-- the record: program, capabilities, environment, agent card
let program_hash be sha256(read_file("agent.syn"))
let caps_hash be sha256(canonical_json(["net"]))
let measurement be int_to_bytes(0, 32)
let uri be "https://example.org/.well-known/agent-card.json"
let data be abi_encode("register(bytes32,bytes32,bytes32,string)", [program_hash, caps_hash, measurement, uri])
-- build, sign, send, wait
let fees be eth_fee_history(url)
let tx be tx_eip1559({"chain_id": 7960, "nonce": eth_nonce(url, me), "to": registry, "value": 0,
"gas": eth_estimate_gas(url, {"from": me, "to": registry, "data": data}) * 2,
"max_fee": fees["base_fee"] * 2 + fees["priority"], "max_priority": fees["priority"], "data": data})
let hash be eth_send_raw(url, tx_eip1559_raw(tx, secp256k1_sign(tx["digest"], k)))
let receipt be eth_wait_receipt(url, hash, 1, 60)
assert(receipt != nothing and receipt["status"] == 1, "the registration did not go through")
let n be abi_decode("uint256", eth_call(url, {"to": registry, "data": abi_encode("count()", [])}))[0]
print(`registered as agent {n - 1}, tx {hash}`)
What goes in each field:
| Field | Value |
|---|---|
programHash | sha256 of the program: the .syn, or the WASM module that embeds it, or any artifact of an agent written in another language |
capsHash | sha256(canonical_json(<the require lines>)) — the ceiling of what the agent may do. 0x00…00 if the agent declares nothing |
measurement | The attestation measurement of the enclave it runs in (Attestation); zero without a TEE |
agentURI | Where the agent describes itself (its Agent Card, Agent identity). The chain does not check it |
The explorer marks every agent with what it declares and proves: capabilities declared / not declared, attested / not attested.
Read the registry§
Reading needs no key and no SYN:
require env("L1_RPC")
require net("devnet.synsema.io")
let url be env("L1_RPC")
let registry be "0x9124Ac7F5Ff9bA48aed29c78198b59a1Ba6732eF"
task call(sig, args)
give eth_call(url, {"to": registry, "data": abi_encode(sig, args)})
let n be abi_decode("uint256", call("count()", []))[0]
print(`{n} agents on chain {eth_chain_id(url)}`)
let a be abi_decode("(address,bytes32,bytes32,bytes32,string,uint64)", call("get(uint256)", [0]))
print(`agent 0: controller {a[0]}, card {a[4]}`)
Without a token, the explorer's JSON API answers the same questions: GET /api/agents, /api/agent/<id>, /api/tx/<hash>, /api/address/<0x…>, /api/blocks, /api/tokens, described in /openapi.json.
Verify a payment without trusting anyone§
A 200 from an app does not prove a payment. The receipt on the chain does. This program answers "did this hash pay exactly this amount to this address, and is it final?":
-- paid.syn: synsema run paid.syn -- <hash> <payee>
require env("L1_RPC")
require net("devnet.synsema.io")
task hex_int(s)
let h be slice(s, 2)
when length(h) % 2 == 1
set h to "0" + h
give bytes_to_int(bytes(h, "hex"))
task paid(hash, payee, amount_wei)
let r be eth_receipt(env("L1_RPC"), hash)
when r == nothing or r["status"] != 1
give false
let t be eth_rpc(env("L1_RPC"), "eth_getTransactionByHash", [hash])
give lower(t["to"]) == lower(payee) and hex_int(t["value"]) == amount_wei
print(paid(args()[0], args()[1], 100 * 1000000000000000000))
On Avalanche an accepted block is final — there are no reorganizations — so "included" means "cannot be undone". For people, the explorer's verify page does the same and has a button that asks the node directly and compares.
The AgentRegistry contract§
| Function | Who |
|---|---|
register(bytes32 programHash, bytes32 capsHash, bytes32 measurement, string agentURI) → uint256 id | anyone |
anchor(uint256 id, uint64 seq, bytes32 root) | the controller; seq must be the last one + 1 (no gaps, no rewrites) |
count() → uint256, get(uint256 id), programHashOf(uint256 id) | read |
lastSeq(uint256 id), lastRoot(uint256 id) | read |
Events: Registered(uint256 indexed id, address indexed owner, bytes32 programHash, string agentURI) and Anchored(uint256 indexed id, uint64 seq, bytes32 root).
Tokens and contracts§
Anyone with SYN can deploy contracts; the explorer finds ERC-20 tokens by their Transfer events. A Synsema program cannot create a contract yet (tx_eip1559 requires to): deploy with Foundry and call it from Synsema.
forge create --broadcast --rpc-url "$L1_RPC" --private-key "$MY_KEY" src/MyToken.sol:MyToken
Gotchas§
- Fees: the base fee is 25 gwei and the suggested tip follows recent transactions, so it can be
larger than twice the base. Use max_fee = base_fee 2 + priority; with base_fee 2 alone, tx_eip1559 refuses (max_priority is greater than max_fee).
- Blocks only with transactions. A height that does not move means the chain is idle.
- Recent state only. The node prunes:
eth_callagainst an old block answers
missing trie node. History is complete in the explorer.
- Big integers from text:
number()returns a float and loses digits past 2⁵³ — build wei
amounts with integer arithmetic (100 * 1000000000000000000), not from a parsed string.
- Hex:
bytes(s, "hex")wants no0x;eth_rpcreturns everything with it —slice(s, 2).