> For the complete documentation index, see [llms.txt](https://docs.bloxchain.app/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.bloxchain.app/developers/sdk-setup/configure-programmatically.md).

# Configure programmatically

Set up a smart account's actions, roles, permissions, gates and allowlists from code with `@bloxchain/sdk`, instead of clicking through the Bloxchain Platform.

**Where:** your own scripts or service, against the account contract · **You need:** [SDK setup](/developers/sdk-setup.md) complete, a wallet holding the **Owner** role, a **Broadcaster** address to submit, Sepolia test ETH

## When to script configuration

Use the Platform UI for a single account you set up once. Script it when:

* **Many accounts.** You are rolling out the same policy to ten treasuries, or one account per customer.
* **Repeatable environments.** Staging and production should differ only by address.
* **Continuous integration (CI).** A pipeline deploys an account and configures it before the tests that depend on that policy run.
* **Migrating a policy.** You read one account's configuration and reproduce it on another.

For the other routes, including presets and signed files, see [Ways to configure an account](/guides/ways-to-configure.md).

## The building blocks

Configuration is five distinct things. Each maps to a contract concept and to a page in the Platform.

| You want to                                  | Contract concept                                                             | Batch action                                       | Platform page          |
| -------------------------------------------- | ---------------------------------------------------------------------------- | -------------------------------------------------- | ---------------------- |
| Register a function as an action             | Function schema on the guard, with its supported `TxAction` list             | `GuardConfigActionType.REGISTER_FUNCTION`          | **Actions**            |
| Create a role                                | Named role with a wallet limit                                               | `RoleConfigActionType.CREATE_ROLE`                 | **Roles**              |
| Put a signer in a role                       | Wallet grant on a role                                                       | `RoleConfigActionType.ADD_WALLET`, `REVOKE_WALLET` | **Signers**            |
| Assign a role to a step of an execution path | Function permission: a `grantedActionsBitmap` over `TxAction` values         | `RoleConfigActionType.ADD_FUNCTION_TO_ROLE`        | **Manage permissions** |
| Grant the gate                               | The same role's permission on the **handler** selector that carries the step | `RoleConfigActionType.ADD_FUNCTION_TO_ROLE`        | **Gates**              |
| Allow a destination                          | Per-selector list of permitted targets                                       | `GuardConfigActionType.ADD_TARGET_TO_WHITELIST`    | **Allowlists**         |

Two details are worth stating plainly.

**Gates are a second grant, not a different mechanism.** A step runs only when the caller's role has the action on the function's own selector *and* on the handler selector used to submit it; the engine checks `hasActionPermission` for both. That is why the Platform can show a row with access on **Actions** but no gate on **Gates**. Both grants travel in the same role config batch. See the handler versus execution selector notes in [Runtime RBAC](/protocol-reference/api-reference/runtime-rbac.md).

**Allowlists are keyed by function selector.** There is one list per selector, not one list per account. **Allowed contracts** are the targets permitted under the action's own execution selector, **Allowed recipients** are the targets under the native transfer selector, and **Allowed payees** and payout token contracts are validated under `ATTACHED_PAYMENT_RECIPIENT_SELECTOR` and `ERC20_TRANSFER_SELECTOR`. Read a current list with `GuardController.getFunctionWhitelistTargets(selector)`. See [Guard controller](/protocol-reference/api-reference/guard-controller.md).

## How a change is submitted

Configuration changes are **batched**, then **signed**, then **submitted**. There are two batch families, one per component:

| Family                                                                                         | Class             | Submit method                                        |
| ---------------------------------------------------------------------------------------------- | ----------------- | ---------------------------------------------------- |
| Guard config: register or unregister functions, add or remove allowed targets                  | `GuardController` | `guardConfigBatchRequestAndApprove(metaTx, options)` |
| Role config: create or remove roles, add or revoke wallets, add or remove function permissions | `RuntimeRBAC`     | `roleConfigBatchRequestAndApprove(metaTx, options)`  |

The shape is the same for both:

```typescript
// 1. Build the action list, then encode it with the deployed definitions contract
const executionParams = await guardConfigBatchExecutionParams(publicClient, guardDefs, actions);

// 2. Bind the payload to the handler you will actually submit through
const metaTxParams = await guard.createMetaTxParams(
  accountAddress,
  GUARD_CONTROLLER_FUNCTION_SELECTORS.GUARD_CONFIG_BATCH_META_SELECTOR,
  TxAction.SIGN_META_REQUEST_AND_APPROVE,
  3600n,          // deadline in seconds
  0n,             // no max gas price
  ownerAddress,
);
const metaTx = await guard.generateUnsignedMetaTransactionForNew(
  ownerAddress, accountAddress, 0n, 1_000_000n,
  GUARD_CONTROLLER_OPERATION_TYPES.CONTROLLER_CONFIG_BATCH,
  GUARD_CONTROLLER_FUNCTION_SELECTORS.GUARD_CONFIG_BATCH_EXECUTE_SELECTOR,
  executionParams, metaTxParams,
);

// 3. Sign the raw digest the contract produced. No EIP-191 prefix: the contract
//    recovers with a plain ecrecover, so viem's signMessage would not verify.
const signer = new MetaTransactionSigner(publicClient, walletClient, accountAddress, sepolia);
const signed = await signer.signMetaTransactionWithWallet(metaTx);

// 4. A Broadcaster submits and pays gas
await guard.guardConfigBatchRequestAndApprove(signed, { from: broadcasterAddress });
```

The definitions contract addresses (`GuardControllerDefinitions`, `RuntimeRBACDefinitions`) come from the published address catalog, `official-deployed-addresses.json` in `@bloxchain/contracts`, on a catalog network. On a chain that is not in the catalog, such as a local chain, use the `deployed-addresses.json` from your own deploy. See [Official addresses](/developers/deployment.md#official-addresses). Full argument lists: [Guard controller](/protocol-reference/api-reference/guard-controller.md) and the [API reference](/protocol-reference/api-reference.md).

## Worked example: one action end to end

Set up an ERC-20 transfer that a requester starts and an approver clears.

1. **Register the action.** One guard config batch with a `REGISTER_FUNCTION` entry carrying the signature `transfer(address,uint256)`, an operation name, and the `TxAction` values the action should support.
2. **Allow the destination.** In the same batch, add an `ADD_TARGET_TO_WHITELIST` entry pairing the transfer selector with the token contract. Batching both means the action is never registered without its allowlist.

   ```typescript
   const actions = [
     { actionType: GuardConfigActionType.REGISTER_FUNCTION,      data: registerData },
     { actionType: GuardConfigActionType.ADD_TARGET_TO_WHITELIST, data: whitelistData },
   ];
   ```
3. **Create the roles.** A role config batch with `CREATE_ROLE` for `TREASURY_REQUESTER` and `TREASURY_APPROVER`, each with a **Max wallets** limit and its initial function permissions.
4. **Assign the steps.** `ADD_FUNCTION_TO_ROLE` grants each role the `TxAction` values for the steps it performs: the request step for the requester, the approve and cancel steps for the approver.
5. **Grant the gate.** A further `ADD_FUNCTION_TO_ROLE` entry gives the same role the action on the handler selector for that step.
6. **Add the signers.** `ADD_WALLET` entries put real addresses in each role.
7. **Verify.** `getFunctionSchema(selector)`, `getActiveRolePermissions(roleHash)`, `getAuthorizedWallets(roleHash)` and `hasActionPermission(address, selector, action)` should all agree with your intent before anyone sends value.

Changing a grant is a removal followed by an addition. Re-adding a selector already on a role reverts, so put `REMOVE_FUNCTION_FROM_ROLE` and `ADD_FUNCTION_TO_ROLE` in the same batch.

## Idempotency and safety

* **Read before you write.** Call `getSupportedFunctions()`, `getSupportedRoles()` and `getFunctionWhitelistTargets()` first, and queue only the entries that are missing. A script that re-runs cleanly is worth more than one that runs once.
* **Never hard-code a private key.** Read it from the environment or a secret store and fail fast when it is absent.
* **Dry-run against a test account.** Deploy a throwaway account on Sepolia, run the whole script, then compare the resulting state with the account you intend to configure.
* **Permanent grants cannot be revoked.** When a schema is not grant-revocable, removing the permission reverts. Plan the role map before you grant, and remember that deleting a custom role is the only way to clear its gates.
* **Mind pending requests.** Removing an allowed target or unregistering a function while requests still reference it makes those requests fail to cancel or complete. Clear the queue first.
* **Use short deadlines.** Meta-transaction nonces are per signer and must be submitted in order. See [Meta-transactions](/protocol-reference/api-reference/meta-transactions.md).

## How the result appears in the Platform

A scripted account is an ordinary account, because the policy lives on chain either way. Open it in the Platform and the registered function appears on **Policies → Actions**, its per-step roles on **Manage permissions**, the handler grants on **Policies → Gates**, the destinations under **Allowed recipients**, **Allowed payees** and **Allowed contracts**, and the role with its signers on **Roles** and **Signers**. An action registered without permissions or an allowlist shows **Flow incomplete** until you finish it.

That makes the hybrid pattern practical: script the setup once, then let the team request, approve and execute in bloxchain.app.

## Related

* [Actions](/platform-guide/policies/actions.md) and [Manage permissions](/platform-guide/policies/manage-permissions.md)
* [SDK core patterns](/developers/sdk-setup/sdk-core-patterns.md)
* [Design roles and permissions](/guides/roles-and-permissions.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.bloxchain.app/developers/sdk-setup/configure-programmatically.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
