Operación
Synsema Network (L1)
Synsema Network es una L1 de Avalanche cuyo primer trabajo es ser un registro público de agentes: para cada agente, el hash de su programa, la dirección que lo controla, el hash de las capacidades que declara, la medida de atestación de la máquina donde corre y las raíces de Merkle de su auditoría. Comprobar un agente es leer su registro, no confiar en quien lo opera. Encima, una EVM abierta: pagos, tokens, contratos. El gas se paga con el token nativo, SYN.
La cadena no ejecuta agentes; los agentes corren en cualquier lado (tu máquina, un servidor, la plataforma, un TEE) y usan la cadena con los builtins EVM de Blockchain.
Hoy es una devnet, en devnet.synsema.io: el estado se puede reiniciar y el SYN ahí no tiene valor. Todo lo de esta página corre contra ella.
| Chain id | 7960 (0x1f18) |
| Token nativo | SYN, 18 decimales |
| RPC | https://devnet.synsema.io/<token>/rpc |
| Registro de agentes | 0x9124Ac7F5Ff9bA48aed29c78198b59a1Ba6732eF |
| Explorador | devnet.synsema.io/explorer |
| EVM | hasta Cancun (compilá con evm_version = "cancun") |
Acceso: un token, una clave, algo de SYN§
El RPC pide un token en la ruta. Sacá uno — sin registro, 10 por dirección IP por hora, 600 pedidos por minuto cada uno:
curl -s -X POST https://devnet.synsema.io/token
La respuesta trae el token, la URL del RPC y la línea para tu .env. Después, una clave propia — cualquier 32 bytes aleatorios — y su dirección:
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")))
El grifo manda 100 SYN a una dirección, una vez por hora por dirección:
curl -s -X POST https://devnet.synsema.io/<token>/faucet \
-H 'content-type: application/json' -d '{"address": "0x…"}'
Una billetera (MetaMask, Rabby) toma los mismos valores: URL del RPC con tu token, chain id 7960, símbolo SYN. La portada tiene un botón que hace los tres pasos y agrega la red a tu billetera.
Registrar un agente§
agent.syn es el programa que se registra (cualquier programa Synsema). Cada valor que mueve plata está escrito: nada se firma con un valor por defecto que no viste.
-- 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}`)
Qué va en cada campo:
| Campo | Valor |
|---|---|
programHash | sha256 del programa: el .syn, o el módulo WASM que lo embebe, o cualquier artefacto de un agente escrito en otro lenguaje |
capsHash | sha256(canonical_json(<las líneas require>)) — el techo de lo que el agente puede hacer. 0x00…00 si el agente no declara nada |
measurement | La medida de atestación del enclave donde corre (Atestación); cero sin TEE |
agentURI | Donde el agente se describe (su Agent Card, Identidad de agentes). La cadena no lo comprueba |
El explorador marca cada agente con lo que declara y lo que prueba: capacidades declaradas / no declaradas, atestado / no atestado.
Leer el registro§
Leer no necesita clave ni 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]}`)
Sin token, la API JSON del explorador responde lo mismo: GET /api/agents, /api/agent/<id>, /api/tx/<hash>, /api/address/<0x…>, /api/blocks, /api/tokens, descriptas en /openapi.json.
Verificar un pago sin confiar en nadie§
Un 200 de una app no prueba un pago. Lo prueba el recibo en la cadena. Este programa responde "¿este hash pagó exactamente este monto a esta dirección, y es 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))
En Avalanche un bloque aceptado es final — no hay reorganizaciones —, así que "incluida" significa "no se puede deshacer". Para personas, la página de verificación del explorador hace lo mismo y tiene un botón que le pregunta directamente al nodo y compara.
El contrato AgentRegistry§
| Función | Quién |
|---|---|
register(bytes32 programHash, bytes32 capsHash, bytes32 measurement, string agentURI) → uint256 id | cualquiera |
anchor(uint256 id, uint64 seq, bytes32 root) | el controlador; seq tiene que ser el último + 1 (sin huecos, sin reescrituras) |
count() → uint256, get(uint256 id), programHashOf(uint256 id) | lectura |
lastSeq(uint256 id), lastRoot(uint256 id) | lectura |
Eventos: Registered(uint256 indexed id, address indexed owner, bytes32 programHash, string agentURI) y Anchored(uint256 indexed id, uint64 seq, bytes32 root).
Tokens y contratos§
Cualquiera con SYN puede desplegar contratos; el explorador encuentra los tokens ERC-20 por sus eventos Transfer. Un programa Synsema todavía no puede crear un contrato (tx_eip1559 exige to): desplegá con Foundry y llamalo desde Synsema.
forge create --broadcast --rpc-url "$L1_RPC" --private-key "$MY_KEY" src/MyToken.sol:MyToken
Trampas§
- Fees: la base es de 25 gwei y la propina sugerida sigue a las transacciones recientes, así que
puede superar dos veces la base. Usá max_fee = base_fee 2 + priority; con base_fee 2 solo, tx_eip1559 rechaza (max_priority is greater than max_fee).
- Bloques sólo con transacciones. Una altura que no se mueve significa que la cadena está quieta.
- Sólo el estado reciente. El nodo poda:
eth_callcontra un bloque viejo responde
missing trie node. La historia está completa en el explorador.
- Enteros grandes desde texto:
number()devuelve un float y pierde dígitos pasado 2⁵³ — armá
los montos en wei con aritmética entera (100 * 1000000000000000000), no desde un texto parseado.
- Hex:
bytes(s, "hex")no quiere el0x;eth_rpcdevuelve todo con él —slice(s, 2).