This guide introduces the core components of Brahma Builder Kit through a practical example, demonstrating how different participants interact to create secure, automated operations.
BRAHMA BUILDER KIT:Brahma Builder Kit consists of services that provide programmatic access to sub-accounts and the policy engine. These services facilitate automated interactions with sub accounts through a simplified API, enabling developers to build and deploy automated operations while maintaining security through policy constraints.
EXECUTORS: Executors are third-party services responsible for running and executing automations on top of the Brahma infrastructure. They act as trusted intermediaries that interact with sub-accounts according to predefined permissions and policies. Each executor must register with the Brahma system and can only perform actions within the bounds of user-authorized policies.
SUB-ACCOUNT: A sub-account is an isolated Smart Contract wallet (Safe) owned by the user's account that shares specific access permissions with authorized executors. While executors can execute transactions and access funds within the sub-account, all operations are protected by the policy validator. This validator ensures each execution strictly complies with user-defined policies, providing a secure framework for automated operations while maintaining user control over their assets.
A Simple Auto-Transfer BOT
Let's build an automated USDC transfer bot that executes transfers from your subaccount to whitelisted addresses at 10-minute intervals
The Policy
To build an automation on your account, we first need to register an executor and define policies that govern its actions. These policies act as safeguards for user assets in case an executor becomes malicious. Each policy has three parameters
inputTokens:
Defines which tokens the executor can access
Acts as a whitelist for token movements and prevents executor from accessing any other tokens in sub account
In our case: Only USDC is permitted
hopAddresses:
Specifies allowed destination addresses for transfers and creates a whitelist of approved recipients
In our case: Single approved destination address which will be receiver of the token
limitPerExecution:
Controls how user-defined transfer limits are enforced in combination with duration settings
false: Limits are time-bound and cumulative over a specified duration
Example: If a user sets a limit of 10 USDC with a 4-day duration, the executor can transfer up to 10 USDC total within any 4-day period
Any transfers exceeding 10 USDC within the 4-day window will be rejected
true: Limits are applied per individual execution, independent of time
Example: If a user sets a limit of 10 USDC, each individual transfer can move up to 10 USDC, regardless of frequency
Duration is not considered (set to 0) as each execution is evaluated independently
This parameter helps users:
Control the total exposure of funds over time (false)
Set fixed caps on individual transfers (true)
Choose between time-based or per-transaction risk management
For our USDC transfer bot, we'll define a straightforward policy:
{// Only USDC on Base can be accessed by the executor// Any attempts to move other tokens will be blocked by policy"inputTokens": ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"// Base USDC address ],// Executor can only transfer USDC to this specific address// Any transfers to other addresses will be rejected"hopAddresses": ["0x687f4304Df62449dBc6C95FE9A8cb1153d40D42e"// Our whitelisted destination ],// Since we want a fixed amount every 10 minutes// we set this to false to enforce overall limits// rather than per-execution limits"limitPerExecution": false}
Creating an Executor
After defining policies, we need to create an executor entity on Brahma account that users can subscribe to and grant sub account access. This requires signing an EIP-712 message with the executor's private key (EIP-1271 signatures are also supported) and registering with the Builder Kit. Upon successful registration, you'll receive a unique registryID for your executor.
Here's the core implementation focusing on the essential components:
{// Chain ID for Base network"chainId":8453,// Executor configuration defining permissions and constraints"config": {// No fees charged for transfers"feeInBPS":0,"feeReceiver":"0x0000000000000000000000000000000000000000","feeToken":"0x0000000000000000000000000000000000000000",// Single whitelisted address for USDC transfers"hopAddresses": ["0x687f4304Df62449dBc6C95FE9A8cb1153d40D42e" ],// Only USDC token is allowed"inputTokens": ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" ],// Time-based limits instead of per-execution"limitPerExecution":false },// Address of the executor"executor":"0x123...789",// This will be your executor's address// Metadata for the automation service"executorMetadata": {"id":"simple-automated-token-transfer","logo":"https://your-logo-url.com/logo.png","metadata": {},"name":"automated-token-transfer" },// Unique identifier for this executor registration"id":"a1b2c3d4-5e6f-7g8h-9i0j-k1l2m3n4o5p6",// EIP-712 signature from executor"signature":"0x123...abc",// This will be the actual signature generated// Active status"status":1,// Unix timestamp of registration"timestamp":69}
Now that our executor is registered, it can execute transactions on the subaccounts whenever users subscribe through the Console UI, provided they comply with the defined policies. The next step is setting up automated scheduling. Let's explore how to use the Automation API to run our bot every 10 minutes while ensuring all transactions adhere to our defined policies
Registering your executor with the Brahma Builder Kit
The scheduler API which is part of Brahma Builder Kit enables task scheduling for registered executors, allowing you to focus on building core automation logic. Once registered, Console's automation services will call your hosted server to generate transaction data and notify you of executions. The service handles all scheduling and transaction relaying automatically.
constautomationConfig= {"registryID":"your-registry-uuid",// Your unique registry ID"source": {"type":"INTERVAL","value":10*60// 10 minutes interval },"destination": {"baseURL":"https://your-server-url.com"// Your server's public URL with HTTPS },"verifyPayload":false,// Since we omitted HMAC verification"relayerCount":1// Number of relayer addresses to generate}
After registering our automation, we'll create a server that handles Console's automation requests. This server will:
Respond to execution triggers
Generate and sign transaction data
Send it back to the Scheduler API for execution
handle the post execution hooks to log outputTransactionHash
Below is a simple Express server implementation that handles these interactions. Once a user subscribes, the Console Automation API will automatically trigger our execution hooks at 10-minute intervals, creating an end-to-end automated transfer system.
require('dotenv').config();constexpress=require('express');const { ethers } =require('ethers');constapp=express();// Environment variablesconstPORT=process.env.PORT||3000;constEXECUTOR_PRIVATE_KEY=process.env.EXECUTOR_PRIVATE_KEY;constTOKEN_CONTRACT=process.env.TOKEN_CONTRACT;constRECIPIENT=process.env.RECIPIENT;constTRANSFER_AMOUNT=process.env.TRANSFER_AMOUNT;// ERC20 ABI for transfer functionconstERC20_ABI= ["function transfer(address to, uint256 amount) returns (bool)"];/** * Generates executor signature for the transaction * Full implementation omitted for brevity * * @param{string} to - Target contract address * @param{string} data - Encoded call data * @param{string} value - ETH value for the transaction * @returns{Promise<string>} - Executor signature */constgenerateExecutorSignature=async (to, data, value) => {};app.use(express.json());app.post('/kernel/trigger',async (req, res) => {try {const { requestID,params } =req.body;consttokenInterface=newethers.Interface(ERC20_ABI);// Encode transfer function callconstcallData=tokenInterface.encodeFunctionData("transfer", [RECIPIENT,ethers.parseUnits(TRANSFER_AMOUNT,18) ]);// Get executor signature (simplified)constexecutorSignature=awaitgenerateExecutorSignature(TOKEN_CONTRACT, callData,'0' );constresponsePayload= { skip:false, requestID: requestID, task: { subaccount:params.subAccountAddress, executor:params.executorAddress, executorSignature, executable: { callType:'CALL', to:TOKEN_CONTRACT, value:'0', data: callData } } };console.log('Processing transfer request:', { timestamp:newDate().toISOString(), recipient:RECIPIENT, amount:TRANSFER_AMOUNT });res.json(responsePayload); } catch (error) {console.error('Error processing trigger:', error);res.status(500).json({ error:'Internal server error' }); }});// once executed the post execution hook is called, which contains the // transactions hash and other infoapp.post('/kernel/executed', (req, res) => {const { successful,error,outputTransactionHash } =req.body;constexecutionLog= { timestamp:newDate().toISOString(), successful, error: error ||null, txHash: outputTransactionHash };console.log('Execution result:', executionLog);res.json({ log: executionLog });});app.listen(PORT, () => {console.log(`Server running on port ${PORT}`);console.log(`Configured to transfer ${TRANSFER_AMOUNT} tokens to ${RECIPIENT}`);});
Summary
Through this example of building a simple USDC transfer bot, we got a practical introduction to Brahma Builder Kit. We saw how the API offered by the Brahma Builder Kit work alongside policies and core infrastructure components to enable secure, automated operations. This example demonstrated the basic interaction between executors, policies, and the scheduler system.
In the next document, we'll dive deeper into the Brahma Builder Kit's architecture, exploring its core components and how they work together to enable more complex automation scenarios