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 synsema.network: 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://synsema.network/<token>/rpc |
| Agent registry | 0x9124Ac7F5Ff9bA48aed29c78198b59a1Ba6732eF |
| Explorer | synsema.network/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://synsema.network/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://synsema.network/<token>/rpc
MY_KEY=<the 64 hex characters>
-- address.syn
require secret("MY_KEY")
print(evm_address(secret("MY_KEY")))
The faucet sends 100 SYN to an address, once an hour per address:
curl -s -X POST https://synsema.network/<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("synsema.network")
require file.read("agent.syn")
let url be env("L1_RPC")
let registry be "0x9124Ac7F5Ff9bA48aed29c78198b59a1Ba6732eF"
let k be secret("MY_KEY")
let me be evm_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 evm_fee_history(url)
let tx be evm_tx({"chain_id": 7960, "nonce": evm_nonce(url, me), "to": registry, "value": 0,
"gas": evm_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 evm_send(url, evm_tx_raw(tx, secp256k1_sign(tx.digest, k)))
let receipt be evm_wait(url, hash, 1, 60)
assert(receipt != nothing and receipt["status"] == 1, "the registration did not go through")
-- the id is in the Registered event of the receipt
let registered be {"name": "Registered", "inputs": [
{"name": "id", "type": "uint256", "indexed": true},
{"name": "owner", "type": "address", "indexed": true},
{"name": "programHash", "type": "bytes32", "indexed": false},
{"name": "agentURI", "type": "string", "indexed": false}]}
each lg in receipt["logs"]
let ev be abi_decode_log(registered, lg, nothing)
when ev != nothing
print(`registered as agent {ev.id}, 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("synsema.network")
let url be env("L1_RPC")
let registry be "0x9124Ac7F5Ff9bA48aed29c78198b59a1Ba6732eF"
task call(sig, args)
give evm_call(url, {"to": registry, "data": abi_encode(sig, args)})
let n be abi_decode("uint256", call("count()", []))[0]
print(`{n} agents on chain {evm_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("synsema.network")
task paid(hash, payee, amount_wei)
let r be evm_receipt(env("L1_RPC"), hash)
when r == nothing or r["status"] != 1
give false
-- a raw JSON-RPC answer: the method keeps the node's name, the value is a "0x…" quantity
let t be evm_rpc(env("L1_RPC"), "eth_getTransactionByHash", [hash])
give lower(t["to"]) == lower(payee) and int(t["value"]) == amount_wei
print(paid(args()[0], args()[1], 100 * 10**18))
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. Compile with Foundry (forge build, evm_version = "cancun") and deploy from Synsema with evm_tx_create, which tells you the contract's address before you sign (Blockchain):
-- deploy.syn: synsema run deploy.syn
require env("L1_RPC")
require secret("MY_KEY")
require sign("MY_KEY")
require net("synsema.network")
require file.read("out/MyToken.sol/MyToken.json")
let url be env("L1_RPC")
let k be secret("MY_KEY")
let me be evm_address(k)
-- init code = the compiled bytecode ‖ the constructor arguments (ABI, no selector)
let artifact be json_decode(read_file("out/MyToken.sol/MyToken.json")) -- from `forge build`
let bytecode be bytes(artifact["bytecode"]["object"], "hex")
let init be bytecode + abi_encode("(string,string,uint256)", ["My Token", "MTK", 10**24])
let fees be evm_fee_history(url)
let tx be evm_tx_create({"chain_id": 7960, "nonce": evm_nonce(url, me), "from": me, "value": 0,
"gas": evm_estimate_gas(url, {"from": me, "data": init}) * 2,
"max_fee": fees["base_fee"] * 2 + fees["priority"], "max_priority": fees["priority"], "data": init})
print(`deploying to {tx.contract_address}`) -- known before signing
let hash be evm_send(url, evm_tx_raw(tx, secp256k1_sign(tx.digest, k)))
let receipt be evm_wait(url, hash, 1, 60)
assert(receipt != nothing and receipt["status"] == 1, "the deployment did not go through")
forge create does the same from the shell, if you prefer:
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, evm_tx 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:
evm_callagainst an old block answers
missing trie node. History is complete in the explorer.
- Big integers: wei amounts are exact integers —
100 10*18, never1e20(a float) nor
number(text), which refuses a value past 2⁵³ instead of losing digits. From text, int(text).
- Hex:
evm_rpcreturns the node's raw answer, with0xeverywhere. A quantity ("0x56bc…",
any length) is int(s); data is bytes(s, "hex"), which accepts the 0x and wants an even number of digits. The other way, hex(n) gives the node's quantity form.