Skip to main content

Managing the Business Logic Resolver

This guide covers how to register new facets (Business Logics) and create or update configurations in the Business Logic Resolver (BLR) — the central component that powers the Diamond Pattern in ATS. For the concepts behind it, see Core concepts and Architecture.

Table of Contents​

Overview​

The Business Logic Registry (BLR) is the on-chain registry that glues together the Diamond Pattern in ATS. It holds two responsibilities:

  1. Business Logic registry — Keeps a versioned mapping of resolver keys to facet implementation addresses. All facets share a single global version counter: registering or updating any facet increments the shared latest version by 1, so a given version is a consistent snapshot across every registered key.
  2. Configuration management — Maintains named sets of facets (configurations) that define the full interface of a token type (Equity, Bond, Loan, etc.). Each configuration has its own independent version history.

Together, these two responsibilities allow the system to upgrade facets and configurations without redeploying existing tokens.

┌─────────────────────────────────────────────────────────┐
│ Business Logic Registry (BLR) │
│ │
│ Business Logics (Facets) Configurations │
│ ┌──────────────────────────┐ ┌───────────────────┐ │
│ │ AccessControlFacet v1 │ │ Equity Config v1 │ │
│ │ AccessControlFacet v2 │ │ └─ facet set │ │
│ │ BondFacet v1 │ │ Equity Config v2 │ │
│ │ BondFacet v2 │ │ └─ facet set │ │
│ │ EquityFacet v1 │ │ Bond Config v1 │ │
│ │ ... │ │ └─ facet set │ │
│ └──────────────────────────┘ └───────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
│
▼ resolves
┌────────────────────────┐
│ ResolverProxy (token) │ ──► routes calls to correct facet
└────────────────────────┘

Key Concepts​

Business Logics (Facets)​

In the BLR, facets are referred to as Business Logics. Each one is identified by a bytes32 key (the businessLogicKey, the resolver key), a keccak256 hash that remains constant across versions. There is a single shared version counter for the whole registry: every registerBusinessLogics call increments it by 1, and the keys included in that call take the new version. Keys not included keep their previous version. This guarantees that everything registered up to a given version is mutually compatible.

Configurations​

A configuration is a named set of facets that defines the complete interface of a token type. For example, the Equity configuration includes all the facets an equity token needs (access control, compliance, KYC, lock, etc.).

Each configuration is identified by a bytes32 configurationId and maintains its own independent version history. A new configuration version is created every time createConfiguration or createBatchConfiguration is called for an existing configurationId.

Deploying Facets​

Register new facets​

Facets are registered in batch by calling registerBusinessLogics on the BLR, which accepts a list of BusinessLogicRegistryData entries — one per facet.

struct BusinessLogicRegistryData {
bytes32 businessLogicKey; // Unique identifier for this facet (constant across versions)
address businessLogicAddress; // Address of the deployed facet contract
}

function registerBusinessLogics(BusinessLogicRegistryData[] calldata _businessLogics) external;

Requirements:

  • Caller must have DEFAULT_ADMIN_ROLE on the BLR.
  • businessLogicKey must match the resolver key returned by the facet contract itself (getStaticResolverKey()) — the BLR validates this and reverts with BusinessLogicKeyMismatch otherwise.
  • Registering increments the BLR's shared latest version by 1; the facets in the call take that new version.
  • Facet implementations must already be deployed at the addresses given in businessLogicAddress.

Upgrade a facet to a new version​

Upgrading a facet follows the exact same process as registering a new one. Simply provide the new contract address for the facet you want to upgrade.

The BLR detects that the businessLogicKey already has a registered version and increments the version for that specific facet accordingly.

Managing Configurations​

Create a new configuration​

Use createConfiguration to deploy a full configuration in a single transaction. This is the simplest approach and is suitable when the configuration fits within a single block's gas limit.

struct FacetConfiguration {
bytes32 id; // The facet's businessLogicKey
uint256 version; // The specific facet version to include
}

function createConfiguration(
bytes32 _configurationId,
FacetConfiguration[] calldata _facetConfigurations,
bytes calldata _data
) external;

Requirements:

  • Caller must hold ROLE_CREATE_CONFIGURATION and be the owner of _configurationId (the first account to create a configuration becomes its owner via onlyOwner — only that owner can add versions).
  • _configurationId must not be bytes32(0).
  • Every id in _facetConfigurations must reference a registered businessLogicKey.
  • No duplicate facet IDs within the same configuration.

Example:

const EQUITY_CONFIG_ID = ethers.zeroPadValue(ethers.toBeHex(1), 32);

const facetConfigurations = [
{ id: RESOLVER_KEY_ACCESS_CONTROL, version: 1 },
{ id: RESOLVER_KEY_EQUITY, version: 1 },
{ id: RESOLVER_KEY_COMPLIANCE, version: 1 },
// ... all facets for the Equity token type
];

const tx = await blr.createConfiguration(EQUITY_CONFIG_ID, facetConfigurations, "0x");
await tx.wait();

const version = await blr.getLatestVersionByConfiguration(EQUITY_CONFIG_ID);
console.log("Equity configuration version:", version.toString()); // 1

Create a configuration in batches​

ATS configurations typically include 40+ facets, which may exceed a single transaction's gas limit. Use createBatchConfiguration to split the deployment across multiple transactions.

function createBatchConfiguration(
bytes32 _configurationId,
FacetConfiguration[] calldata _facetConfigurations,
bool _isLastBatch,
bytes calldata _data
) external;
  • Set _isLastBatch = false for every intermediate batch.
  • Set _isLastBatch = true on the final batch to finalize and activate the configuration.
  • The configuration is not active until the last batch is committed.

Example:

const FACETS_PER_BATCH = 15;
const allFacets = [...]; // full list of FacetConfiguration entries

for (let i = 0; i < allFacets.length; i += FACETS_PER_BATCH) {
const batch = allFacets.slice(i, i + FACETS_PER_BATCH);
const isLastBatch = i + FACETS_PER_BATCH >= allFacets.length;

const tx = await blr.createBatchConfiguration(EQUITY_CONFIG_ID, batch, isLastBatch, "0x");
await tx.wait();

console.log(`Batch ${Math.floor(i / FACETS_PER_BATCH) + 1} submitted. Last: ${isLastBatch}`);
}

Cancel an in-progress batch​

If a batch deployment needs to be aborted before the final batch is submitted, call cancelBatchConfiguration. This discards all accumulated state for that configuration ID and allows you to start fresh.

Note: A configuration deployed in a single createConfiguration call cannot be cancelled — it is committed atomically.

function cancelBatchConfiguration(bytes32 _configurationId) external;
// Abort an in-progress batch deployment
const tx = await blr.cancelBatchConfiguration(EQUITY_CONFIG_ID);
await tx.wait();
console.log("Batch configuration cancelled.");

Upgrade a configuration to a new version​

Upgrading a configuration follows the same process as creating one. When createConfiguration or createBatchConfiguration is called with a _configurationId that already has registered versions, the BLR creates a new version rather than overwriting the existing one.

Only the configuration's owner can upgrade it. DiamondCutManager gates these calls with onlyOwner(configurationId) + onlyRole(ROLE_CREATE_CONFIGURATION): the account that first created the configuration owns it, and only that owner (holding ROLE_CREATE_CONFIGURATION) can add new versions.

// Existing Equity config is at version 1.
// Calling createConfiguration again creates version 2.
const updatedFacets = [
{ id: RESOLVER_KEY_ACCESS_CONTROL, version: 2 }, // upgraded facet
{ id: RESOLVER_KEY_EQUITY, version: 1 }, // unchanged
// ...
];

const tx = await blr.createConfiguration(EQUITY_CONFIG_ID, updatedFacets, "0x");
await tx.wait();

const newVersion = await blr.getLatestVersionByConfiguration(EQUITY_CONFIG_ID);
console.log("Equity configuration upgraded to version:", newVersion.toString()); // 2

Querying the BLR​

The BLR exposes a rich set of read-only functions for inspecting the registry state.

Resolve facet addresses​

// Returns the address registered under the latest facet version
function resolveLatestBusinessLogic(bytes32 _businessLogicKey) external view returns (address);

// Returns the address registered at a specific facet version
function resolveBusinessLogicByVersion(bytes32 _businessLogicKey, uint256 _version) external view returns (address);

List registered facets (paginated)​

function getBusinessLogicCount() external view returns (uint256);
function getBusinessLogicKeys(uint256 _pageIndex, uint256 _pageLength) external view returns (bytes32[] memory);

Configuration queries​

function getConfigurationsLength() external view returns (uint256);
function getConfigurations(uint256 _pageIndex, uint256 _pageLength) external view returns (bytes32[] memory);
function getLatestVersionByConfiguration(bytes32 _configurationId) external view returns (uint256);
function getFacetsByConfigurationIdAndVersion(
bytes32 _configurationId,
uint256 _version,
uint256 _pageIndex,
uint256 _pageLength
) external view returns (IDiamondLoupe.Facet[] memory);
function getFacetAddressesByConfigurationIdAndVersion(
bytes32 _configurationId,
uint256 _version,
uint256 _pageIndex,
uint256 _pageLength
) external view returns (address[] memory);
function isResolverProxyConfigurationRegistered(
bytes32 _configurationId,
uint256 _version
) external view returns (bool);

Managing Selector Blacklists​

The BLR supports per-configuration selector blacklists. A blacklisted selector will be rejected by any ResolverProxy using that configuration, effectively deprecating a function without redeploying.

function addSelectorsToBlacklist(bytes32 _configurationId, bytes4[] calldata _selectors) external;
function removeSelectorsFromBlacklist(bytes32 _configurationId, bytes4[] calldata _selectors) external;
function getSelectorsBlacklist(
bytes32 _configurationId,
uint256 _pageIndex,
uint256 _pageLength
) external view returns (bytes4[] memory);

Example:

// Deprecate a function from all Equity tokens
const selector = ethers.id("deprecatedFunction(uint256)").slice(0, 10) as `0x${string}`;

const tx = await blr.addSelectorsToBlacklist(EQUITY_CONFIG_ID, [selector]);
await tx.wait();
console.log("Selector blacklisted:", selector);

Events Reference​

BusinessLogicsRegistered​

Emitted when registerBusinessLogics completes successfully.

event BusinessLogicsRegistered(BusinessLogicRegistryData[] businessLogics, uint256[] newLatestVersions);

DiamondConfigurationCreated​

Emitted when a full configuration is created or a new version is committed via createConfiguration.

event DiamondConfigurationCreated(
bytes32 configurationId,
FacetConfiguration[] facetConfigurations,
uint256 version,
bytes data
);

DiamondBatchConfigurationCreated​

Emitted after each createBatchConfiguration call, including intermediate batches.

event DiamondBatchConfigurationCreated(
bytes32 configurationId,
FacetConfiguration[] facetConfigurations,
bool isLastBatch,
uint256 version,
bytes data
);

DiamondBatchConfigurationCanceled​

Emitted when an in-progress batch deployment is aborted.

event DiamondBatchConfigurationCanceled(bytes32 indexed configurationId, uint256 version);

Errors Reference​

Business Logic errors​

ErrorDescription
BusinessLogicVersionDoesNotExist(uint256 version)The requested version does not exist in the registry.
BusinessLogicKeyDuplicated(bytes32 key)The same businessLogicKey appears more than once in a single registerBusinessLogics call.
BusinessLogicKeyMismatch(address impl, bytes32 actual, bytes32 expected)The resolverKey() returned by the facet contract does not match the provided businessLogicKey.
ZeroKeyNotValidForBusinessLogic()bytes32(0) is not a valid businessLogicKey.

Configuration errors​

ErrorDescription
DefaultValueForConfigurationIdNotPermitted()bytes32(0) is not a valid configurationId.
FacetIdNotRegistered(bytes32 configurationId, bytes32 facetId)A facet referenced in the configuration has not been registered in the BLR.
DuplicatedFacetInConfiguration(bytes32 facetId)The same facet appears more than once in a single configuration.
ResolverProxyConfigurationNoRegistered(bytes32 resolverProxyConfigurationId, uint256 version)The requested configuration version does not exist.
SelectorBlacklisted(bytes4 selector)A function selector in the facet is on the blacklist for this configuration.
SelectorAlreadyRegistered(bytes32 configurationId, uint256 version, bytes32 facetId, bytes4 selector)A function selector is already mapped to a different facet in the same configuration version.

Script Examples​

The deployment and configuration operations are encapsulated in helper scripts under packages/ats/contracts/scripts/:

ScriptPurpose
scripts/infrastructure/operations/blrDeployment.tsDeploy BLR proxy and implementation
scripts/infrastructure/operations/registerFacets.tsRegister facets in the BLR
scripts/infrastructure/operations/blrConfigurations.tsGeneric batch configuration helper
scripts/domain/equity/createConfiguration.tsCreate or upgrade the Equity configuration
scripts/domain/bond/createConfiguration.tsCreate or upgrade the Bond configuration
scripts/domain/loan/createConfiguration.tsCreate or upgrade the Loan configuration
scripts/domain/loanPortfolio/createConfiguration.tsCreate or upgrade the Loan Portfolio configuration

Full deployment flow​

The following summarizes the complete sequence executed during a fresh deployment:

1. Deploy ProxyAdmin
2. Deploy BLR (proxy + implementation)
3. Initialize BLR → grants DEFAULT_ADMIN_ROLE to deployer
4. Deploy all facets
5. registerBusinessLogics(all facets) → shared latestVersion = 1
6. createBatchConfiguration(EQUITY_CONFIG_ID, ...) → Equity v1
7. createBatchConfiguration(BOND_CONFIG_ID, ...) → Bond v1
8. createBatchConfiguration(LOAN_CONFIG_ID, ...) → Loan v1
... (other configurations)
9. Deploy Factory (proxy + implementation)
10. Write deployment output JSON (addresses, keys, versions)