Architecture
How the ATS contracts fit together at runtime: the Diamond pattern, the Business Logic Resolver, the per-token proxies, and the factory that ties them together. If you only read one architecture page, read this one.
Big picture
Every token is an EIP-2535 Diamond — a thin proxy that holds the state and delegates each function call to a facet (a feature module). Rather than each token storing its own facet map, all tokens share a central Business Logic Resolver (BLR) that maps feature keys to versioned facet addresses. A Factory deploys new tokens and wires them to the BLR. Two infrastructure contracts — the BLR and the Factory — sit behind upgradeable OpenZeppelin proxies managed by a ProxyAdmin.
Reading the diagram: a call to a token hits its ResolverProxy, which asks the BLR which facet serves that function (for the token's configuration + version), then runs the facet's code against the token's own storage via
delegatecall. The BLR and Factory are upgraded by the ProxyAdmin; tokens are "upgraded" by pointing them at a newer configuration — never a redeploy.
Two proxy patterns
ATS deliberately uses two different proxy mechanisms for two different jobs. Why two? The infrastructure (BLR, Factory) is a small, stable codebase that upgrades as a whole — the simple TUP fits it. A token, by contrast, accrues many features that must be versioned and upgraded independently across thousands of tokens — exactly what the Diamond gives you. Confusing the two is the most common source of upgrade mistakes, so keep the distinction clear:
| TransparentUpgradeableProxy (TUP) | ResolverProxy (Diamond) | |
|---|---|---|
| Used for | BLR, Factory (infrastructure) | Equity / Bond / Loan / … tokens |
| What it points at | A single implementation contract | A configuration of many facets |
| How you upgrade it | Swap the implementation via ProxyAdmin | Point it at a newer configuration version |
| Who controls it | ProxyAdmin owner | The token's DEFAULT_ADMIN_ROLE |
| Guide | Upgrading infrastructure | Upgrading configurations |
- TUP is the standard OpenZeppelin pattern: one proxy, one implementation, upgraded by the admin.
- ResolverProxy is the Diamond pattern: one proxy, many facets, resolved per function call through the BLR. "Upgrading" a token never changes its proxy bytecode — it changes which configuration version the proxy resolves against.
The Business Logic Resolver (BLR)
The BLR — infrastructure/diamond/BusinessLogicResolver.sol —
is the registry at the heart of the system. It stores two things:
- Business logics (facets). A mapping from a
bytes32resolver key to versioned facet addresses. Registering or updating any facet bumps a single, shared latest version so that a chosen version is guaranteed consistent across every key (registerBusinessLogics,resolveLatestBusinessLogic,resolveBusinessLogicByVersion). - Configurations. Via its parent
IDiamondCutManager, the BLR also stores configurations: named, independently-versioned sets of facets that together define an asset type (Equity, Bond, …). Each configuration is identified by abytes32configuration ID and answers "for this asset type at this version, which facet serves this selector?".
A configuration can also maintain a selector blacklist — selectors that are disabled for that asset type even if a facet would otherwise serve them.
See Core concepts for resolver keys, configuration IDs, and versioning, and Managing the BLR for the operations.
How a function call resolves
A token (ResolverProxy,
infrastructure/proxy/ResolverProxy.sol)
stores its resolver address plus its resolverProxyConfiguration (configuration ID + version). On
any call whose selector it doesn't implement directly, its fallback asks the BLR which facet serves
that selector for its configuration, then delegatecalls the facet so the code runs against the
token's own storage.
Each facet declares the selectors it serves and its resolver key through
IStaticFunctionSelectors,
which is how configurations are assembled and validated.
The Factory
The Factory (factory/Factory.sol,
exposed as a facet via FactoryFacet) deploys new tokens. It offers typed entry points per asset
class plus a generic one:
deployEquity(EquityData, FactoryRegulationData)deployBond(BondData, FactoryRegulationData)deployDepositToken(DepositTokenData, FactoryRegulationData)deployProxy(resolver, configKey, version, rbacs, data)— the low-level primitive: deploys a bare, uninitialised ResolverProxy for any configuration (the caller then initialises the facets). See Deploying a token.
The typed methods are the normal way to create a token: each deploys the proxy and runs all of
its facet initialisers, applies regulation data, and marks the token operational. deployProxy does
none of that — it just creates the bare proxy.
There is no separate deployBondFixedRate / deployBondKpiLinkedRate function. The bond variant
is selected by the configuration ID in SecurityData.resolverProxyConfiguration.key and deployed
through deployBond, not a dedicated method.
Each deploy call creates a ResolverProxy, wires it to the BLR with the chosen configuration ID and
version, seeds the initial roles (Rbac[]), and initialises the relevant facets.
Lifecycle flows
Create a token — Factory.deployEquity(...) → new ResolverProxy is created, pinned to the
Equity configuration's current version, roles seeded, facets initialised → token is live.
Upgrade a token's logic — deploy improved facet(s) → registerBusinessLogics in the BLR (bumps
the shared version) → create a new configuration version referencing the new facets → point
tokens at the new version (pinned) or let auto-update tokens pick it up. No token redeployment.
See Upgrading configurations.
Upgrade the infrastructure — deploy a new BLR or Factory implementation → ProxyAdmin swaps the
implementation behind the TUP. Token contracts are unaffected.
See Upgrading infrastructure.
Logical layers
The contracts are organised by responsibility, not by deep folder nesting. Conceptually there are four logical layers — storage wrappers, core standards, domain features, and jurisdiction-specific behaviour — but on disk the facets sit in flat folders organised by feature. The next page, Repository structure, shows exactly where everything lives.