- Solidity compiler:
0.8.35 - Build/test runner: Foundry (
forge) - Libraries:
- OpenZeppelin Contracts
@openzeppelin/contracts
- OpenZeppelin Contracts
pnpm --dir contracts build
pnpm --dir contracts test
pnpm --dir contracts format
pnpm --dir contracts configure:agentsforge must be installed locally for contract commands. The package scripts use a Node wrapper that works on Ubuntu, WSL, and Windows PowerShell by checking the normal Foundry install path and PATH.
RiskGuardInheritanceRegistry is the active inheritance contract path. It does not custody user funds. Instead, it stores one active inheritance plan per smart account:
- beneficiary addresses and basis-point shares
- heartbeat interval
- optional grace period
- optional beneficiary timelock
- last heartbeat/check-in time
- active/cancelled plan state
This keeps assets in the user's Somnia/Thirdweb smart account so the user can continue day-to-day native-token and ERC-20 usage. The registry must first be installed as an ERC-7579 executor module on that smart account (installModule(2, registry)) before it can transfer assets after missed heartbeat conditions; the dashboard does this in the same signed batch as createPlan.
The previous standalone locked vault contract has been removed from the active code path because it required users to deposit funds they could no longer use normally.
Current Somnia Testnet deployment:
RiskGuardInheritanceRegistry:0x355D81e993Bc423C81b8fe348fEEe659E738710E- deployer:
0x64769A00fB002b7ED192834443C9c819565Ab702 - transaction:
0xd39b35d744d0c3415e156620372760953a44bb63d0f0c5e2c82338f5405481a6 - deployed:
2026-06-07 - Somnia LLM Inference agent configured:
12847293847561029384 - Somnia agent configuration transaction:
0x985e8f804ae96c323b9b48efd683d646363a75da8b16f92838e245e3bb707569 - Somnia agent reward per call:
0.1 STT - Somnia agent reward configuration transaction:
0x431c007f863c4c924a61ef6cb8840f227c62c5078455a2f6a584a30bf3fef4e9 - Reactivity funding transaction:
0x829d1dc13c0e134bcf4297822892ca411a9a0b8c6bb56ff1b91f2515123ce848(1 STT)
RiskGuard hook approval gate deployment on Somnia Testnet:
RiskGuardApprovalStore:0xaCdBb69cb283Cb0feDA1a0D1a3a657D74c35e1e3RiskGuardHookModule:0x296Dc049b35447Aaf9Ea8996667eFB289F257289RiskGuardValidator:0xAc80322a018c4e3c310eB1a01d0F952E94679b4aTelegramCheckInValidator:0x83d3404aa108134FE6C1cFa0316509CC4E24d27F- deployer:
0x64769A00fB002b7ED192834443C9c819565Ab702 RiskGuardApprovalStoretransaction:0xbbfd38bbbea451e59769df617d235a24e2d57ef4ec37bebe8b50c07c02c64f0dRiskGuardHookModuletransaction:0x1c98522de5cf3741d05330df2be6f721cac3577de95bbdeca91950e9cd2255d2RiskGuardValidatortransaction:0x3f2c551f9c12c797feaa0cd00cecfa0e7b9b616449b46cabccfa3c6dc016b38bTelegramCheckInValidatortransaction:0xc7fe9384681891ad4135ea4b9516cb6db4fe21e3e0aacd28c37588416ef6da66- deployed:
2026-06-02 - Somnia LLM Inference agent configured:
12847293847561029384 - Somnia agent configuration transaction:
0x0dbd4e3b0359f650122ee6df3f2666ebcd26c90d73103e6fb3ee2e2b18e6541a - Somnia agent reward per call:
0.1 STT - Somnia agent reward configuration transaction:
0x1bf8a4e36f12e96a7de1fcb34eb9681aac3dae42e47e9b0597cd9c559e322d37 - note:
RiskGuardValidatorallows the configuredRiskGuardInheritanceRegistry.checkIn()call without agent review so heartbeat renewal can pass through RiskGuard policy. - note:
TelegramCheckInValidatoris the production Telegram check-in path. It is installed once per smart account withabi.encode(checkInSigner)and validates only zero-valueRiskGuardInheritanceRegistry.checkIn()UserOps from that signer. - note:
RiskGuardValidatoris the active guard path for Thirdweb ERC-7579 accounts. It validates owner/admin signatures and gates ERC-7579execute(bytes32,bytes)UserOps duringvalidateUserOp, including native transfers.RiskGuardHookModuleis kept for hook experiments, but Thirdweb's publishedModularAccountdoes not run hooks on its primaryexecute(...)path. - note: no Thirdweb contract is forked. The account factory and account implementation stay Thirdweb-published; RiskGuard is installed as a separate ERC-7579 validator module.
For a smart account test install, register the approval route after installing the validator:
RiskGuardApprovalStore(0xaCdBb69cb283Cb0feDA1a0D1a3a657D74c35e1e3).registerAgentAndHook(
agentAddress,
0xAc80322a018c4e3c310eB1a01d0F952E94679b4a
);The frontend and agent use Thirdweb's ERC-7579 beta smart account preset:
smartWallet(Config.erc7579({
chain,
sponsorGas: true,
factoryAddress,
validatorAddress: riskGuardValidatorModule,
}));Telegram check-in uses the same Thirdweb ERC-7579 account preset but with validatorAddress: riskGuardCheckInValidatorModule and without Thirdweb sessionKey permissions. That avoids the Thirdweb IAccountPermissions API, which this deployed account implementation does not support.
RiskGuard hybrid approval flow:
RiskGuardValidator.validateUserOp(...)is the enforcement point. It blocks risky native transfers, contract calls, approvals, and batches by revertingPendingApprovalRequired(smartAccount, txHash, signer, riskContext).- For agent-native review, the owner/admin calls
RiskGuardValidator.requestAgentReview(smartAccount, callData). The validator pays the Somnia Agent request fromagentBudgetOf[smartAccount], callsIAgentRequester.createRequest(...), and records the pending request. - The configured Risk Assessment Agent is Somnia's LLM Inference base agent. It receives an
inferString(prompt, system, false, [])payload with decoded transaction context and should return a concise line beginning withAPPROVE:orREJECT:followed by a reason. - Somnia's agent platform calls
handleRiskAssessmentResponse(...). If the LLM response begins withAPPROVE:, the validator stores a one-timeagentApprovals[smartAccount][txHash]entry. The user can then resubmit the original transaction; the validator consumes the agent approval.REJECT:, failed requests, or malformed responses leave the transaction blocked and emit the reason for Telegram/status reporting. The agent runtime sends a manual Telegram Approve/Decline fallback when the Somnia agent does not approve. - If an agent request is pending for the same transaction,
validateUserOp(...)revertsAgentReviewPending(smartAccount, txHash, requestId). - For high-risk or manual-review paths, the existing off-chain Telegram flow remains available: a RiskGuard-aware frontend, wallet, bundler, or RPC proxy forwards decoded
PendingApprovalRequireddata toPOST /api/riskguard/pending-approval; on Approve, the agent wallet callsRiskGuardApprovalStore.submitApproval(smartAccount, txHash). - On Decline, the agent submits nothing, so the original transaction remains blocked.
The validator intentionally does not call createRequest from inside the reverting validation path, because state changes and external calls in a reverted UserOp validation would be rolled back. requestAgentReview(...) is the explicit on-chain agent invocation step for the agent-first demo flow.
Agent environment for Telegram approvals:
RISK_GUARD_APPROVAL_STORE_ADDRESSSESSION_KEY_ENCRYPTION_KEY- Supabase session-key storage configured for the
session_keystable - Telegram bot settings (
TELEGRAM_BOT_TOKEN,TELEGRAM_BOT_USERNAME, optionalTELEGRAM_WEBHOOK_SECRET)
RiskGuard does not use a global RISK_GUARD_AGENT_ADDRESS. During guard setup, the agent API creates/reuses a per-smart-account riskguard-approval session key, returns its address to the frontend, and the smart account registers that address in ApprovalStore.registerAgentAndHook(...). The encrypted private key stays in backend session-key storage and is used only when the user taps Approve in Telegram. Because this key submits the approval as an EOA transaction, it needs a small STT gas balance; the frontend funds it from the user's smart account during RiskGuard setup.
For smart-account transactions sent through the RiskGuard smart-account helper, the frontend also stores a signed pending UserOperation with the agent API when RiskGuard requests Somnia Agent review. Telegram Approve only submits the one-time ApprovalStore approval; after that succeeds, the backend relays the already-signed UserOperation to the bundler. The agent does not create a new transfer or gain generic execution authority.
Important limitation: "any dApp / any wallet" needs the transaction path to use the RiskGuard validator and a RiskGuard-aware sender surface, wallet provider, bundler, or RPC proxy that forwards failed simulation/revert data to /api/riskguard/pending-approval. A purely on-chain subscription to a normal reverted UserOp is not possible with standard EVM logs because reverted logs are discarded.
Thirdweb ERC-7579 modular account deployment on Somnia Testnet:
DefaultValidator:0x60A206E8927d8e2e02c48e4CdD499fCe66eB82a5ModularAccountFactory:0xE5B5897f84AfE449B2Afc962AD8164425C89AD8D- deployer/owner:
0x64769A00fB002b7ED192834443C9c819565Ab702 - publisher:
0xdd99b75f095d0c4d5112aCe938e4e6ed962fb024 DefaultValidatortransaction:0x9f1968cc5731968ac87d0dfa62e392294acad17ad8069d47654b2f67b2dfb2ffModularAccountimplementation/reference transaction:0x2fda481ade86f7732d1949a4c71884b355e010a6ecb7c67117829e8341e7d71dModularAccountFactorytransaction:0x713284e24b32fd681502c4d24fbd3d89a96bb1e6ee8015ebefe88a43b8cb5f33- deployed:
2026-05-31 - note: deploy uses Thirdweb's CREATE2 factory
0x4e59b44847b379578588920ca78fbf26c0b4956c, socontractAddressis null in transaction receipts; the Thirdweb SDK returns the resolved deployed contract addresses above.
Deploy or redeploy the Thirdweb ERC-7579 contracts:
pnpm --dir frontend exec node scripts/deploy-thirdweb-modular.mjsRequired environment variables:
WALLET_DEPLOYER_PRIVATE_KEYTHIRDWEB_SECRET_KEYorTHIRDWEB_CLIENT_ID- RPC/chain metadata comes from
config/public-chains.json
After deployment, set:
config/public-chains.jsonunderchains.somnia-testnet.contracts.riskGuardModularAccountFactoryconfig/public-chains.jsonunderchains.somnia-testnet.contracts.riskGuardDefaultValidator- optionally
.envasRISK_GUARD_MODULAR_ACCOUNT_FACTORY_ADDRESS - optionally
.envasRISK_GUARD_DEFAULT_VALIDATOR_ADDRESS
Configure Somnia Agent IDs after deploying or redeploying the RiskGuard/Inheritance contracts:
pnpm --dir contracts configure:agentsRequired environment variables:
WALLET_DEPLOYER_PRIVATE_KEYRISK_GUARD_RISK_ASSESSMENT_AGENT_IDINHERITANCE_HEARTBEAT_AGENT_IDINHERITANCE_DISTRIBUTION_AGENT_ID- optional
SOMNIA_AGENT_REQUESTER_ADDRESS; defaults to the known Somnia AgentRequester for chain5031or50312
agentId values and the AgentRequester address are public on-chain configuration. Do not commit a real WALLET_DEPLOYER_PRIVATE_KEY.
Deploy the registry with Foundry:
forge create src/InheritanceRegistry.sol:RiskGuardInheritanceRegistry \
--rpc-url https://dream-rpc.somnia.network \
--private-key $WALLET_DEPLOYER_PRIVATE_KEY \
--broadcastAfter deployment, set the address in:
config/public-chains.jsonunderchains.somnia-testnet.contracts.inheritanceRegistry.envasINHERITANCE_REGISTRY_CONTRACT_ADDRESS
The registry itself does not move funds until each user's smart account has installed the registry as an ERC-7579 executor module (installModule(2, registry)). Users should keep day-to-day native tokens and ERC-20s in the smart account, not in this registry.
- A smart account calls
createPlan(...)with beneficiaries, protected assets, heartbeat, grace, and timelock. From the dashboard this is a single signed ERC-7579 batch (one UserOp) that also installs the registry as the account's executor module (installModule(2, registry)) and funds the per-account agent budget — so the registry can later transfer assets viaexecuteFromExecutor(...). (Authorization usesinstallModule, not SoladygrantRoles:grantRolesisonlyOwnerand revertsUnauthorized()on a self-call inside a batch, whereasinstallModuleisonlyEntryPointOrSelf.) - The registry rejects normal EOAs because they cannot execute
executeFromExecutor(...)/executeBatch(...)later. - The smart account keeps holding and using native tokens/ERC-20s normally.
- The user refreshes liveness through
checkIn()or a successful agent heartbeat callback before grace ends. - After
lastHeartbeatAt + heartbeatInterval + gracePeriod + timelockPeriod, the Reactivity schedule callsonEvent(...). onEvent(...)does not move funds directly. It creates a Somnia distribution-agent request throughIAgentRequester.createRequest(...), using the smart account's funded agent budget.- On successful agent callback,
handleDistributionResponse(...)snapshots each protected asset balance once and asks the smart account to execute transfers by beneficiary share. triggerDistributionAgent(...)remains a manual fallback for keepers or demos, but the normal automatic path is Reactivity -> Somnia Agent -> callback -> transfer.- Failed transfers are skipped in agent mode, leaving that share in the smart account for retries. The manual
executeInheritancepath fails closed on transfer failure.
contracts/test/InheritanceRegistry.t.sol covers:
- creating one active plan per smart account
- rejecting a second active plan until cancellation
- updating beneficiaries and timing rules
- allowing
heartbeat = 10 minutes,grace = 0, andtimelock = 0for the fast testnet demo - cancelling plans and clearing beneficiaries
- rejecting bad shares, self-beneficiaries, and invalid duration bounds
- refreshing heartbeat deadlines with
checkIn() - Somnia agent heartbeat and distribution request tracking
- per-smart-account agent budgets
- native-token and ERC-20 distribution from an authorized smart account
- skip-on-fail distribution behavior
contracts/test/RiskGuardValidator.t.sol covers the agent review request →
approval → allowed-UserOp path.
contracts/test/ApprovalRiskScanner.t.sol covers the revoke.cash-style
ApprovalRiskScanner (src/riskguard/ApprovalRiskScanner.sol):
quoteScanbatch deposit math (3 × _agentDepositfor any non-zero item count)requestScanstoring up to 50 approvals, firing one JSON-API batch call and one Parse-Website batch call, then one LLM-Inference batch call on fan-in- failed/timed-out batch callbacks still reaching a fail-safe inference score
- duplicate/replayed callbacks (no-op +
UnknownAgentRequest) - insufficient deposit and
OnlyAgentPlatformguards
Post-deploy, configure all three agent integrations (RiskGuard, Inheritance,
ApprovalRiskScanner) with pnpm --dir contracts configure:agents. The scanner is
read-only (no revoke action) and runs on Somnia; approval discovery is done
off-chain by the agent backend via each chain's Blockscout-compatible explorer
API (module=logs&action=getLogs), which supports full-range queries. Raw RPC is
used only for live allowance() / isApprovedForAll() verification (where Somnia
eth_getLogs is capped at 1000 blocks, which is why discovery uses the explorer
API instead).
Current ApprovalRiskScanner deployment:
ApprovalRiskScanner:0xC35634383b0489aC8A3DD0DD396AF5373231a446- deploy transaction:
0xa4f8da92523cff1036cf487761e4237d0fdded9988191db1b568233a00c97acd - Somnia agent reward per call:
0.01 STT - Somnia agent configuration transaction:
0x465b28149bf7e146834b0401b606a4ab77ba44aea383f824ae9b192d279d8ea3 - Somnia agent reward configuration transaction:
0x5dd3eee780cfadc111914c96692d081ac22b641cbd34975668a9bd2ff215497f - batch quote after deploy:
0.18 STTfor any non-zero batch up to 50 approvals