For the complete documentation index, see llms.txt. This page is also available as Markdown.

Guard controller

The GuardController class provides type-safe access to Bloxchain GuardController contracts for execution control, target whitelisting, and function schema management.

🎯 Overview

GuardController extends BaseStateMachine with execution control features:

  • Generic contract delegation with full EngineBlox workflows

  • Target whitelist management via batch configuration

  • Function schema registration for runtime function management

  • Time-locked execution with approval workflows

  • Meta-transaction support for all operations

  • Native token transfer support

🚀 Quick Start

import { GuardController } from '@bloxchain/sdk/typescript'
import { createPublicClient, createWalletClient, http } from 'viem'
import { mainnet } from 'viem/chains'

// Initialize clients
const publicClient = createPublicClient({
  chain: mainnet,
  transport: http()
})

const walletClient = createWalletClient({
  account: privateKeyToAccount('0x...'),
  chain: mainnet,
  transport: http()
})

// Create GuardController instance
const guardController = new GuardController(
  publicClient,
  walletClient,
  '0x...', // contract address
  mainnet
)

📖 Core Features

1. Execution Functions

Request Time-Locked Execution

Approve Time-Locked Transaction

Request and Approve in One Step (Meta-Transaction)

2. Guard Configuration Batch

GuardController uses batch configuration for whitelist management and function schema registration.

Guard Configuration Actions

Register Function Schema

Add Target to Whitelist

Batch Multiple Operations

3. Whitelist Management

Get Function Whitelist Targets

Note: The whitelist is per-function-selector. Multiple functions can have different whitelists.

Pending transactions and delisting: The core engine re-validates the stored execution target against the whitelist when cancelling or completing a pending tx. If you REMOVE_TARGET_FROM_WHITELIST while matching txs are still PENDING, cancel and finalize paths revert until that target is whitelisted again. Operational mitigation: (1) clear or cancel pendings before delisting, or (2) temporarily ADD_TARGET_TO_WHITELIST the same target, cancel or run the approve/execute path to completion, then remove the target again.

Pending transactions and UNREGISTER_FUNCTION: Unregistering removes the execution selector from the supported-function set. Pending txs that still reference that selector then fail _validateTargetWhitelist with ResourceNotFound on cancel/complete until the function is registered again (registerFunction) and whitelist policy is restored as needed. Prefer no pending txs for that selector before unregister, or use re-register → cancel/complete → unregister.

4. Function Schema Management

Function schema registration is now handled by GuardController (moved from RuntimeRBAC).

Get Function Schema

🔄 Complete Workflow Example

Setup: Register Function and Whitelist Target

Execute: Call Whitelisted Function

📡 Event Monitoring

Listen for Guard Configuration Events

Contracts emit ComponentEvent(bytes4 functionSelector, bytes data). For guard config, filter by the executeGuardConfigBatch selector and decode data as (GuardConfigActionType, bytes4 functionSelector, address target). See generated contract API.

🛡️ Security Features

1. Whitelist Validation

For external contract targets, the address must be on the per-function-selector whitelist (unless the engine rule below applies):

target == address(this) (engine vs GuardController):

  • EngineBlox._validateTargetWhitelist: Once the execution selector is registered, TxParams.target == address(this) is always allowed and does not require address(this) to appear in functionTargetWhitelist[executionSelector]. That supports internal / composed execution (macros, vault logic on the same contract) without bloating every whitelist with the state machine’s own address. RBAC (who may request/approve/execute) still applies; the whitelist is not the only control plane.

  • Attached payouts: Recipient and ERC20 token contracts are still validated under ATTACHED_PAYMENT_RECIPIENT_SELECTOR / ERC20_TRANSFER_SELECTOR when amounts are non-zero.

  • GuardController public entrypoints: In addition to the engine, _validateNotInternalFunction blocks target == address(this) unless the execution selector is an allowed system macro selector (e.g. native transfer–class flows). So arbitrary “call any internal selector on self” is not exposed through the standard GuardController execute paths, while the engine’s whitelist rule remains as documented above for other integrations.

Attached payments (executeWithPayment): payout policy uses two extra whitelist keys (registered when GuardController definitions load—same ADD_TARGET_TO_WHITELIST batch flow). Base-only state machines that use attached payments without GuardController must register these schemas separately (see PaymentTestHelper in tests). The core engine reverts (ResourceNotFound) if whitelist validation runs for a selector that is not yet in the supported-function set—there is no silent skip.

Selector (see EngineBlox SDK / Solidity)

What to whitelist

ATTACHED_PAYMENT_RECIPIENT_SELECTOR

payment.recipient for native and ERC20 attached payouts

ERC20_TRANSFER_SELECTOR (transfer(address,uint256))

payment.erc20TokenAddress — token contracts the vault may pay out via safeTransfer

ERC20 token model (attached payouts): The engine transfers the nominal erc20TokenAmount via safeTransfer and does not verify recipient credits against balance deltas. Fee-on-transfer, deflationary, rebasing, or otherwise non-standard ERC20 tokens are not supported for attached payments—use conventional tokens only, or you risk accounting mismatch and disputes.

Primary execution still uses TxParams.target whitelisted under the execution function selector (e.g. mint, NATIVE_TRANSFER_SELECTOR).

2. Function Schema Protection

Protected function schemas cannot be unregistered:

3. Role-Based Permissions

Combine with RuntimeRBAC for role-based access control:

🔧 Advanced Usage

Native Token Transfers

GuardController supports native token (ETH) transfers:

Design note: NATIVE_TRANSFER_SELECTOR is a convenience path for ETH-only transfers (empty params). Non-native selectors may still intentionally forward ETH to payable targets for edge-case workflows; default practice is to keep value = 0 for standard function calls.

Time-Locked Operations

For operations requiring additional security:

🧪 Testing

Unit Testing

Integration Testing

🚨 Common Issues

Issue: "Target not whitelisted"

Solution: Add the target to the whitelist using ADD_TARGET_TO_WHITELIST action in a guard config batch.

Issue: "Function schema not found"

Solution: Register the function schema using REGISTER_FUNCTION action via GuardController (not RuntimeRBAC).

Issue: "Transaction execution failed"

Solution: Ensure:

  1. Function schema is registered

  2. Target is whitelisted for the function

  3. Caller has appropriate role permissions (via RuntimeRBAC)

  4. Gas limit is sufficient (a gasLimit of 0 in TxParams means "forward all remaining gas" — equivalent to gasleft(). Set a positive value for a strict upper bound.)

Issue: "Handler selector mismatch"

Solution: Ensure the function selector in the execution params matches the registered function schema.

Issue: OWNER cannot call executeWithPayment

Solution: Default GuardControllerDefinitions registers the schema for executeWithPayment (EXECUTE_WITH_PAYMENT_SELECTOR in contracts/core/execution/lib/definitions/GuardControllerDefinitions.sol) but intentionally does not grant an OWNER_ROLE FunctionPermission for it—the permission surface is kept minimal out of the box.

You cannot fix this with guard-config: getGuardConfigActionSpecs() / GuardConfigActionType only cover whitelist add/remove, REGISTER_FUNCTION, and UNREGISTER_FUNCTION—there is no guard-config action to attach a selector to a role.

Add the grant via RBAC (role-config) after init: use roleConfigBatchRequestAndApprove → executeRoleConfigBatch with RoleConfigActionType.ADD_FUNCTION_TO_ROLE, following the encoders and batch ordering constraints in contracts/core/access/lib/definitions/RuntimeRBACDefinitions.sol, and satisfy schema / handler rules from GuardControllerDefinitions.sol (e.g. handlerForSelectors for executeWithPayment must match what addFunctionToRole enforces against that schema).

Issue: NATIVE_TRANSFER_SELECTOR not found / native ETH flows revert

Solution: GuardControllerDefinitions already registers the NATIVE_TRANSFER_SELECTOR pseudo-schema (plus ATTACHED_PAYMENT_RECIPIENT_SELECTOR and ERC20_TRANSFER_SELECTOR). If your contract inherits from a base-only state machine without loading GuardControllerDefinitions, you must register these schemas manually via a guard-config or definition bundle.

Issue: Unregistered function still has whitelist entries / hooks

Solution: EngineBlox.unregisterFunction removes the schema and supported-function membership but does not sweep functionTargetWhitelist or functionTargetHooks for that selector. Stale rows are inert while the selector is absent (whitelist checks revert ResourceNotFound), but they reappear if the selector is re-registered. Clear them explicitly with REMOVE_TARGET_FROM_WHITELIST / clearHook before or after unregistering.

Issue: Payment failure rolls back the entire execution

Solution: This is intentional all-or-nothing atomicity. If the main call succeeds but executeAttachedPayment reverts (e.g. insufficient balance, whitelist mismatch), the whole approval/execute transaction reverts including the main effect. Ensure payment prerequisites (balance, whitelist) are met before submitting.


Last updated

Was this helpful?