All packages
agentrail_mandates

agentrail_mandates

Saurabh-0312
v0.1.0/0 downloads/Repository

Package ref

agentrail-mandates@v0.1.0

Run package

CLI

Run graph_out from the command line.

substreams run agentrail-mandates@v0.1.0 graph_out -e solana-devnet
Authenticate by running substreams auth or directly on thegraph.market (see docs).

README

agentrail_mandates — agent mandate activity on Solana (Substreams)

Indexes every transaction that touches the AgentRail program (GcYqRmrRko3WbKNuGarDTbmRF1GcdeHzc3eV37gtM4Bj on devnet): mandates issued and revoked, permissions added, and every execute_payment / verify attempt, including the ones the mandate refused. A refused attempt is a landed, failed transaction. This package keeps it and maps the Anchor error number to a block_reason:

ErrorNameblock_reason
6006InstructionNotAllowedNOT_PERMITTED
6007PerTxLimitExceededOVER_BUDGET
6008SpendLimitExceededOVER_BUDGET
6001ExpiredEXPIRED
6000NotActiveREVOKED
otherFAILED (kept, never dropped)

The program emits no events on purpose: reverted transactions cannot emit, and the reverts are the product. Everything is decoded from instruction data (8-byte Anchor discriminator + Borsh args, per target/idl/agentrail.json) and transaction meta (err, log_messages).

Modules

sf.solana.type.v1.Block ──▶ map_instructions ──▶ store_mandates (pda -> ens_node)
                                   │                     │
                                   └──────▶ map_activity ◀┘ ──▶ graph_out

There is deliberately no blockFilter: solana-common's program_ids_without_votes index is built from successful transactions, so a block whose only AgentRail transaction reverted would be skipped and the refusal lost (verified live). Raw blocks cost more to process and keep every revert.

ModuleOutputWhat
map_instructionsagentrail.v1.Activityevery AgentRail instruction in the block, successful or reverted
store_mandatesstore stringmandate PDA → ENS node, learned from create_mandate (the node sits at offset 80 of the account, unreadable from a block stream; the create argument carries it)
map_activityagentrail.v1.Activitythe shared-schema rows (indexer/schema.graphql), every action and permission joined to its ens_node
graph_outsf.substreams.sink.entity.v1.EntityChangesthe same rows as entity changes for a Substreams-powered subgraph

ens_node is the join key shared with the Sepolia and Base Sepolia subgraphs: one query over Mandate.ensNode spans all three chains.

Reuse

  • map_instructions params: program:<base58> — decode another deployment of the program (also change the blockFilter query, which is a static string).
  • map_activity params: mandate:<pda>=<ens_node>;… — seed the join for mandates created before the streamed range. The node is public on the ENS name, so this is data the operator already has.
  • Fork src/decode.rs for any Anchor program with a mandate-shaped instruction set; the error parsing in src/errors.rs is program-independent.

Run

substreams auth                                   # Graph Market token -> .substreams.env
. ./.substreams.env
cargo build --target wasm32-unknown-unknown --release && substreams pack substreams.yaml
substreams run -e devnet.sol.streamingfast.io:443 substreams.yaml map_activity -s 495587900 -t +12100
substreams gui -e devnet.sol.streamingfast.io:443 substreams.yaml map_activity -s 495587900

src/pb is generated by substreams protogen and committed, trimmed to the two protobuf modules this crate uses (substreams build regenerates it with the manifest's exclude list; the Solana block types come from the substreams-solana crate, never from local bindings).

cargo test runs the decoder against real devnet transactions in tests/fixtures (a full demo run: create, permission, three payments of which two were refused, two verify of which one was refused).

Modules

Execution graph

4 modules
mapMain

graph_out

from #495587900

Output

sf.substreams.sink.entity.v1.EntityChanges

store

store_mandates

from #495587900

Store value

string

Update policy

set