This document is the engineering scope for the 30 June 2026 Sepolia testnet milestone. It defines what we need delivered for a working demo: the borrower / credit story end-to-end (deposit ERC-3643 collateral, onboard via ONCHAINID, borrow stablecoins, see the effects in the admin app), plus the admin and operator controls that govern it. The June-30 scope is firm: it is the committed delivery target for this milestone, not a starting point for expansion.
The June-30 build moves Ascend from a demonstrative model toward a compliant, securities-only institutional credit platform. The committed scope for this milestone is organized around three themes.
Adopt the ERC-3643 (T-REX) stack — ONCHAINID, the Identity Registry and its ancillary registries, and a modular Compliance contract with rule modules — wired into the Credit Facility.
Borrowers are gated by the facility's ONCHAINID / Identity Registry and compliance on the borrower-lifecycle path. The complementary lender-gated Liquidity Module (KYC + Qualified-Lender on deposit/withdraw) is captured as forward direction in the Beyond June-30 section, where we invite OZ's proposed approach.
Collateral deposit-pause and safe de-listing, plus an Admin/Operator role model with per-account pause, user and token freeze, and global pause/shutdown — surfaced through a curated platform slice (securities-only data, fixed navigation and real client state, populated DDF and Reserve pages, a compliance-rules Wizard catalog, and the supporting taxonomy and stablecoin data).
Two further themes — rehypothecation via a vToken escrow, and DeFi exercise destinations — are architectural direction only and are captured in the Beyond June-30 section; they are not in this milestone's build.
| Deliverable | Detail |
|---|---|
| ONCHAINID, Identity Registry, and Compliance integrated into the Credit Facility | via initialize / hooks |
| Borrow-side gating on the borrower-lifecycle path (identity + compliance via the facility) | lender-gated Liquidity Module is forward direction, not committed |
| Collateral deposit-pause and safe de-listing as the first Operator controls | plus the Admin/Operator role model |
| The borrower flow demonstrable end-to-end on Sepolia testnet | with effects surfacing in the admin app |
| A prioritized slice of platform UI | navigation and real state, healthy-majority accounts (one distressed retained for the auction demo), DDF and Reserve data, compliance Wizard catalog |
The document is layered so you can stop at the depth you need.
| Layer | What it covers |
|---|---|
| Scope at a glance | This executive summary plus the June-30 milestone section (acceptance table, cadence, cut-lines). |
| Build detail | The Part A (contracts) and Part B (platform) item blocks — each gives a one-line “what” and a short “why it matters / acceptance.” |
| Cross-cutting | The engineering-correction and security notes that apply across items. |
| Beyond June-30 | Forward-looking direction, clearly separated from the committed scope. |
Each committed item is written as a requirement with an acceptance criterion. Identity and KYC are presented at the requirements level against the commodity ERC-3643 / ONCHAINID stack OZ already knows, not as deep implementation.
Requirements are written against the code as it stands today. The ERC-3643 and ONCHAINID dependencies
are already present in ascend-contracts (@erc3643org/erc-3643,
@onchain-id/solidity), and AuthorityWithIdentityRegistry already integrates
ERC-3643 by forwarding canCall to a primary authority with a per-(target, selector)
Identity Registry fallback — the natural home for a single registry instance. The platform side has an
indexing pipeline (subgraph → ingester → sink → event-relay → platform-service
→ projections → API → frontend) and React frontends (apps/admin,
apps/client) where a Wizard, DDF, Reserve, margin-account, and credit-facility views already
exist. The dependencies are already installed, so the gap is deployment, wiring, and the UI slice, not
new dependencies.
| Capability | Today | June-30 gap to close |
|---|---|---|
| ERC-3643 / ONCHAINID deps | Installed | Deploy and wire (via initialize / hooks, not constructor) |
| Identity Registry | Fallback path in AuthorityWithIdentityRegistry | One registry instance + KYC / Qualified-Lender claims model |
| Compliance modules | Implemented as configurable compliance hooks (pre/post-action checks); base rule hooks (rate-limit, sanctioned-address) present in ascend-contracts, not yet wired into the facility | Integrate ModularCompliance + rule modules (Trading Hours, Max/Min Size, Frequency; catalog also covers Rate-limit, Sanctions-blacklist, Credit-draw, Jurisdiction) and bind them to the Credit Facility via the hook framework |
| Lender gating | None on deposit / withdraw | Direction only — beyond June-30 (OZ to propose approach) |
| Borrow-side gating | Identity-registry fallback only | Borrower-lifecycle ops gated by identity + compliance |
| Collateral pause / safe de-list | add / removeCollateralToken only | Deposit-pause + pledged-guard de-list |
| Margin-account controls | Global pause + FreezerModule | Admin/Operator roles, per-account pause, user/token freeze |
| Mock collateral assets | Generic ERC-20-style mocks | Four identity-gated ERC-3643 mock assets |
| Platform UI (nav, DDF/Reserve, Wizard) | Largely mock; pages render blank | Real client state, populated pages, compliance Wizard catalog |
| vToken / escrow / rehypothecation | None | Direction only — beyond June-30 |
Part A covers the onchain protocol work. The June-30 contract scope centers on four items: identity and compliance wired into the Credit Facility (A1, A2, A3) — which gates the borrower-lifecycle path — four ERC-3643 mock assets for end-to-end validation (A4), collateral deposit-pause controls (A7), and a focused subset of admin/operator controls (A8 subset). The lender-gated Liquidity Module (A5) and the remaining items (A6, A9, A10, and the rest of A8) are forward-looking direction, collected in the Beyond June-30 section; for A5 we invite OZ's proposed approach. All June-30 work targets the Sepolia testnet.
Identity, IdFactory,
ImplementationAuthority, ClaimIssuer) from the already-imported
@onchain-id/solidity package — these are installed dependencies, so the work is
deploy-and-wire, not author-from-scratch. Each participant gets an ONCHAINID carrying signed claims; the
facility resolves identities through the Identity Registry (A2), not by storing ONCHAINIDs itself.
initialize /
module config; ONCHAINIDs are resolved via the registry at call time, never the constructor.
Accept when a participant with a valid ONCHAINID and required claims is accepted, one
lacking them is rejected at deposit/borrow, and unit tests cover both paths.
IdentityRegistry plus IdentityRegistryStorage,
ClaimTopicsRegistry, and TrustedIssuersRegistry from
@erc3643org/erc-3643. Make this the single identity source of truth; pass its address into
facility initialize.
AuthorityWithIdentityRegistry so exactly one registry is referenced
platform-wide — repoint its existing fallback wiring to the new IdentityRegistry instance
(migrate, not parallel), so there is one verification path. Accept when the registry is
deployed and bound, isVerified(address) reflects KYC + Qualified-Lender claims, the
AuthorityWithIdentityRegistry fallback points at this instance, and both the facility and
the liquidity module read from it. Depends on A1.
ModularCompliance and bind the rule-module catalog. Committed for June-30:
Trading Hours, Max/Min Size, and Frequency
(time-windowed limits). The fuller catalog the engine should be able to bind — confirmed at the
architecture session — also includes Rate-limit (anti-structuring per-window caps),
Sanctions / blacklist (transfer-time deny-list, distinct from the KYC claim),
Credit-draw (borrow-side draw limits), Credit Rating (gates eligibility
and limits by borrower / collateral rating), and Jurisdiction, which is the
existing Country module (jurisdiction / residency) and remains the single named cut-line
that can defer if the schedule tightens. Pass the compliance address into facility initialize.
withHook, statusBefore/After) and FreezerModule.canTransact —
these overlap with transfer-time gating and should be coordinated, not duplicated
(avoid double-enforcement between FreezerModule and Compliance). The decided mechanism is to expose
compliance as an installable Credit Facility hook, enforced on margin-account operations through the
existing installHook / withHook framework, so a single hook can carry the bound
rule set across the borrower-lifecycle selectors. For June-30 the rule set is a fixed, hard-coded set
bound at install time — not a fully dynamic, runtime-reconfigurable compliance engine; the configurable
catalog is an offchain Wizard concern (B11), not a v1 onchain requirement. Ship safe defaults plus an
admin override so a misconfigured rule cannot brick transfers or liquidation. Accept when each rule module independently
blocks/permits per spec, a transfer/borrow passing all bound modules succeeds, and the liquidation path
remains executable under compliance. Depends on A1, A2. The platform compliance Wizard (B11) is the
offchain analog.
ValuationModule price feed; provide Ignition seeding and matching platform seed constants so
the same four assets appear consistently onchain and in the UI (B9). Keep transfers obeying compliance in
tests — no silently-unrestricted mocks. Accept when all four identity-gated, priced
tokens are usable as collateral through a full borrow flow in tests. Depends on A2, A3,
ValuationModule.
LiquidityModuleCore that requires every lender to hold an
ONCHAINID with KYC and Qualified-Lender claims (verified via the Identity Registry) to
deposit or withdraw. Keep borrow-side gating symmetric so both sides are permissioned.
LiquidityModuleCore
is an ERC-6909 multi-token vault, so a permissioned variant would extend the core and cover the multi-token
deposit/withdraw and lender-facing IOU paths — gating across token ids, not a single asset — keyed on the
same Identity Registry the facility reads (isVerified plus the KYC and Qualified-Lender topics),
with a safe withdrawal path / admin remediation so claim expiry cannot trap a legitimate lender. We have
deliberately left the design open: rather than prescribe the mechanism, we would like OZ to propose its
approach for permissioning the liquidity side — how it extends LiquidityModuleCore, where
verification is enforced, and how it stays clone-safe and consistent with the borrow-side gating.
Borrow-side gating (borrowers gated by the facility's identity and compliance, via A1/A2/A3) remains
committed for June-30; only the lender-gated Liquidity Module moves to direction. Relates to A1, A2
(claims); A3 optional.
pauseCollateralDeposits(token) / unpause, consulted inside depositCollateral.
Harden removeCollateralToken(token) to revert unless a maintained
pledgedCount[token] == 0, incrementing/decrementing that counter in every collateral
mutation path (deposit, withdraw, transfer, liquidate). Gate to Admin + Operator; emit events for the
indexer. Crucially, keep withdrawals of a paused token allowed so users are never trapped.
Accept when deposits of a paused token revert while withdrawals still succeed, de-listing
reverts when any account holds the token and succeeds when none do, and events are indexed.
Depends on the Operator role.
AccessManager and ships the protective, reversible controls: per-account pause,
freeze user, freeze tokens in an account, and the existing
global pause / shutdown.
FreezerModule already freezes users/accounts and gates via canTransact —
consulted on both controller operations and NFT (account) transfers, the same gate that governs the
liquidation _update(liquidator, id, 0) path. Extend it with token-level freeze and a
per-account pause flag consulted by the operation modifiers, taking care that compliance/freeze
coordination never blocks the liquidation transfer. The facility is already globally
Pausable. Role-gate every control and event-log it. Accept when each
control is exercisable only by Admin/Operator, emits events, and negative tests cover unauthorized
callers. Depends on the role model; FreezerModule extension. The heavier A8 powers — account migrate
and cross-account clawback — are forward-looking and sit in the Beyond June-30 section.
flowchart LR
subgraph Current["Current — ascend-contracts"]
H1["Hub"] --> CF1["CreditFacility (clone)"]
CF1 --> M1["Modules: Liquidity, Valuation,
Risk, Fee, Metadata, Freezer"]
CF1 --> DA1["DistressedAssetsAuction"]
AWI1["AuthorityWithIdentityRegistry
(ERC-3643 fallback path)"] -.-> CF1
end
subgraph Target["Target — June-30 additions"]
H2["Hub"] --> CF2["CreditFacility (clone)"]
CF2 --> M2["Modules (existing)"]
CF2 --> DA2["DistressedAssetsAuction"]
CF2 -. "initialize / hooks" .-> IR["A2: Identity Registry
+ claim topics (KYC,
Qualified Lender)"]
CF2 -. "installHook" .-> CMP["A3: ModularCompliance
+ rule modules"]
CF2 -. "direction" .-> PLM["A5: Permissioned Liquidity
Module (beyond June-30)"]
IR --> OID["A1: ONCHAINID suite"]
CMP --> IR
PLM --> IR
A4T["A4: 4 ERC-3643
mock assets"] --> IR
A4T --> CMP
AWI2["AuthorityWithIdentityRegistry
(single registry — reconciled)"] -.-> IR
end
Current ==>|"A1, A2, A3, A4 + A7/A8 controls"| Target
classDef add stroke-width:2px
class OID,IR,CMP,A4T add
The target keeps the existing Hub → CreditFacility → modules + DistressedAssetsAuction shape and
adds the identity, compliance, and permissioned-liquidity layer on top. Consumers (the facility, the
liquidity module, the mock assets) hold pointers to the Identity Registry and Compliance, and the registry
resolves the ONCHAINID. The single-registry reconciliation (one IdentityRegistry, not two) is
the connective change.
Stated as requirements and acceptance against the commodity ERC-3643 / ONCHAINID stack.
| Requirement | Mechanism | Accept when |
|---|---|---|
| Per-participant onchain identity | ONCHAINID carrying signed claims (A1) | Identity deployed and resolvable via the registry |
| Single identity source of truth | ERC-3643 IdentityRegistry + storage (A2) | isVerified(address) returns the correct claim state; one registry platform-wide |
| KYC required of all participants | KYC claim topic | Unverified wallet rejected at deposit/borrow |
| Lender eligibility | Qualified-Lender claim topic | Lender without the claim rejected in the permissioned Liquidity Module |
| Jurisdiction control | Country rule module (A3; cut-line) | Country rule blocks/permits per allow-list when enabled |
| Transfer/borrow-time rules | ModularCompliance + the bound rule-module catalog: Trading Hours / Max-Min Size / Frequency (committed), plus Rate-limit / Sanctions-blacklist / Credit-draw / Credit Rating / Jurisdiction (catalog) (A3) | Each bound module independently gates; liquidation stays executable |
Claim topics in scope for June-30: KYC (identity verified, AML/sanctions cleared — required of all participants including every lender) and Qualified Lender (qualified/accredited or institutional status — the Qualified-Lender / accredited claim model is seeded for June-30, while lender-side enforcement via the Permissioned Liquidity Module (A5) is forward direction). Country (jurisdiction/residency, enforced via the A3 Country rule module) is the deferrable cut-line.
Part B covers the platform's offchain services and UI: the indexing pipeline (subgraph to projections to API) and the two frontend apps (admin and client). For June-30, the focus is a curated UI slice that lets the end-to-end borrower / credit story render convincingly on Sepolia testnet, with the admin / operator controls surfaced, and the reserve and distressed-asset views populated. The forward-looking items (vToken, rehypothecation, the “Exercise” DeFi flows) are summarized in the Beyond June-30 section, as direction rather than committed scope.
These items are architectural direction, not committed June-30 scope. They are recorded here so the June-30 build leaves clean extension points. None should be implemented or production-flagged without separate sign-off and external audit.
_update(liquidator, id, 0)), and per-account isolation namespaces collateral/debt by
tokenId. Worth capturing as an ADR.
| Item | Direction |
|---|---|
B5 — Escrow / vToken mock | A UI mock for the vToken escrow: collateral tokenized into vTokens, held in escrow, and allocated to accounts. Mirrors the contract-side vToken substrate (A10). |
B6 — Margin-account controls mock | A full admin controls panel per margin account for the broader account-control set (pause account, freeze user, freeze tokens, migrate, clawback), role-gated with an audit-log display. The June-30 admin subset (per-account pause, freeze) is already covered in Part A; this item is the full mocked panel. |
B7 — Collateral controls mock | A UI mock for the collateral controls: toggle deposit-pause per token and attempt de-list with a guard message when the token is pledged. The June-30 deposit-pause is covered in Part A; this is the surfacing layer for the full control. |
B8 — Rehypothecation mock | A UI mock that pledges one collateral token to multiple margin accounts within explicit utilization caps, with aggregate-utilization and double-pledge indicators so the UI never implies unlimited reuse. |
B12 — “Exercise” post-borrow flow + DeFi destinations | Rename the post-borrow flow to “Exercise” and add a destination picker that routes drawn stablecoin (or vToken) to DeFi destinations grouped by action type (stake / yield, swap, lend / borrow, bridge), including a Chainlink CCIP bridge option. Mock integrations, clearly labeled. Exact protocol names and destination chains remain to be confirmed and are out of June-30 scope. |
This is the committed scope for the 30 June 2026 demo on Sepolia testnet. It is a ceiling, not a starting point: the borrower / credit story end-to-end (deposit ERC-3643 collateral → onboard via ONCHAINID → borrow stablecoins → effects surfaced in the admin app), plus the admin/operator controls and the supporting platform pages.
Four workstreams map to the contract and platform items above. The acceptance column is the heart of “demo-ready” — it is what we will check against on 30 June.
| Workstream | Maps to | Acceptance (demo-ready, on Sepolia) |
|---|---|---|
| Identity + Compliance into the Credit Facility | A1, A2, A3 |
A registered, KYC'd identity can transact; an unregistered one is blocked at deposit/borrow; at least one compliance rule module is enforced on a margin-account operation — all wired via initialize / hooks, never the constructor. |
| Borrow-side gating | A1, A2, A3 |
Borrowers are gated by the facility's identity and compliance on the borrower-lifecycle path; an unverified or non-compliant borrower is blocked. The lender-gated Liquidity Module (A5) is forward direction (OZ to propose approach) and is not part of the June-30 acceptance. |
| Collateral deposit-pause (first Operator control) | A7 (+ Operator role) |
An Operator pauses deposits of a collateral token; deposits revert while withdrawals still succeed; the event is indexed by the platform. |
| Platform UI slice | B2, B3, B4, B11 (+ B9/B10 data) |
Navigation returns to the originating Credit Facility after an action; margin accounts show a healthy-majority distribution with one distressed account retained for the auction/liquidation demo; DDF and Reserve pages are populated and internally consistent; the compliance Wizard catalog is usable. |
A light sequencing suggestion, not a fixed contract — sequence to your own dependency read.
| Phase | Work |
|---|---|
| Phase 1 — registries + first controls | Deploy the identity/compliance registries and ONCHAINID factory (A1/A2); define the Operator role; ship collateral deposit-pause (A7); start platform nav/state (B2) and the account re-balance (B3). |
| Phase 2 — compliance binding | Bind ModularCompliance + rule modules as a facility hook (A3); DDF/Reserve data (B4). |
| Phase 3 — mock assets + Wizard + integration | Borrow-side parity + integration tests; the four ERC-3643 mock assets for end-to-end runs (A4); compliance Wizard catalog (B11); B9/B10 data. |
| Final hardening | Negative tests, demo polish, seed and deployment-address wiring updates, and a go/no-go. |
These apply across the contract items and are worth fixing once, centrally.
The Credit Facility is deployed as a deterministic clone via the Hub and
configured through initialize(name, symbol, authority, modules[]); its constructor only calls
_disableInitializers(). Identity Registry, Compliance, and ONCHAINID resolution must therefore
be wired through initialize / module config / hooks — never the constructor. Several
requirements read as “register X in the constructor”; for a clone that is not possible, so we
call out the clone-safe wiring per affected item (A1, A2, A3, A5).
Define both in the AccessManager and make it the single authority
every gated call resolves through — the same authority (AuthorityWithIdentityRegistry) that
already lets an identity-registry-verified principal satisfy a gate as an alternative to holding a role.
Admin and Operator are the gate for the deposit-pause (A7) and the broader account controls
(A8 subset); per-selector gating (setTargetFunctionRole) is the mechanism, with a clear
role-admin hierarchy so onboarding and operator powers are granted and revoked through defined admins
rather than ad-hoc. The more powerful forward-looking powers (migrate, clawback) should additionally be
timelocked or multisig-gated and always event-logged. Keep the role set coherent: the same Admin/Operator
semantics referenced in A7 and the A8 subset, not divergent definitions per item. The confirmed
roles-and-permissions list (the grants per selector and the role-admin assignments) is owed to OZ as the
single source that finalizes these definitions.
ERC-3643 enforces at transfer time, and the facility's existing code
already warns about hooks/restrictions interfering with liquidate().
FreezerModule.canTransact is already consulted on both controller operations and NFT (account)
transfers — the same gate that governs the liquidation _update(liquidator, id, 0) path — so
compliance/freeze coordination must never block that transfer. Ship safe defaults plus an admin override so
a misconfigured compliance rule can never block the liquidation path. Reentrancy is already annotated in
borrow/repay/liquidate; keep that posture.
After any protocol redeploy, update the platform's deployment-address wiring and replay fork pin — otherwise the indexed stack points at stale contracts and projections drift.
Unit + integration coverage (Hardhat Solidity + Node.js) for every gating path — accepted and rejected. Keep platform mocks behind a flag so demo data never leaks into real flows.
AuthorityWithIdentityRegistry already integrates an ERC-3643
fallback (forwards canCall to the primary IAuthority, falls back to a
per-(target, selector) IIdentityRegistry). Migrate that fallback wiring to point at the new
IdentityRegistry so there is exactly one registry instance platform-wide — one source of truth,
not two divergent verification paths.
These are the build-relevant points we would like to settle together before grooming tickets. None blocks starting the deploy/wiring work; all affect the exact shape of what ships.
| # | Question |
|---|---|
| 1 | Country rule module — in or out for 30 June? ModularCompliance is in scope with Trading Hours, Max/Min Size, and Frequency. Country is the single named cut-line if time is tight (the first thing to drop in the floor above). Confirm whether Country is committed for the demo or deferred. |
| 2 | Qualified-Lender claim topic + trusted issuer. Confirm the claim-topic definition for Qualified Lender and which trusted issuer signs it. This drives the TrustedIssuersRegistry seeding and the A5 gating logic. |
| 3 | DDF page — intended contents. Confirm what “DDF” denotes (we read it as a distressed-debt view tied to distressed accounts, auction status, and recoveries) before B4 is populated. |
| 4 | Stablecoin denominations. Confirm which currency denominations are in scope (USD only, or USD + EUR + …) and which coins per denomination, so the borrow-side picker and seeds are correct. |
| 5 | Collateral taxonomy. Confirm the asset-class taxonomy for the securities-only set — in particular whether “commercial paper” and “corporate bonds” are one class or two — so enums/fixtures across contracts, seeds, and frontend agree. |
| Component | Role in Ascend |
|---|---|
| ONCHAINID (Identity) | Per-participant onchain identity holding signed claims (KYC, Qualified Lender, Country). |
| ClaimTopicsRegistry | The set of claim topics a token/facility requires (e.g., KYC, Qualified Lender). |
| TrustedIssuersRegistry | Which issuers may sign each claim topic. |
| IdentityRegistry | Maps wallet → ONCHAINID and answers isVerified; the single identity source of truth. |
| IdentityRegistryStorage | Backing storage for the registry, shared across tokens. |
| ModularCompliance | Binds rule modules; evaluated at transfer / borrow time. |
| Rule modules | Trading Hours, Max/Min Size, Frequency, Country (jurisdiction), Rate-limit, Sanctions / blacklist, Credit-draw, Credit Rating — composable transfer/borrow-time rules bound by ModularCompliance. |
| Chainlink ACE | Offchain Automated Compliance Engine feeding/enforcing rules; surfaced as a Wizard module on the platform. |
Identity verified and AML/sanctions cleared. Required of all participants, including every lender.
Attests qualified / accredited or institutional status. Required to deposit or withdraw in the Permissioned Liquidity Module (A5).
Jurisdiction / residency. Enforced via the Country compliance rule module (A3 — see Open Questions for its 30 June scope).
For the engineering team, the existing code touchpoints each workstream builds on.
| Path | Touchpoint |
|---|---|
ascend-contracts/contracts/CreditFacility.sol | initialize, hooks, transferFrom / _update, liquidate, borrow/repay. |
ascend-contracts/contracts/Hub.sol | cloneDeterministic + initialize. |
ascend-contracts/contracts/access/AuthorityWithIdentityRegistry.sol | The existing ERC-3643 fallback (reconcile to a single registry). |
ascend-contracts/contracts/modules/ | LiquidityModuleCore, FreezerModule, ValuationModule, RiskModule, FeeModule, MetadataModule. |
ascend-contracts/package.json | @erc3643org/erc-3643, @onchain-id/solidity, @chainlink/contracts (identity/compliance dependencies already present). |
ascend-platform/backend/scripts/replay-events/src/{constants,seed-constants}.ts | Seeds. |
ascend-platform/frontend/apps/{admin,client} | Wizard, DDF, Reserve, and margin-account views. |
The current ascend-contracts shape grows toward the target by adding the identity and compliance
layer. The additions (ONCHAINID + Identity Registry, and ModularCompliance + rule modules) are the 30 June
contract scope — A1/A2 and A3. The permissioned-liquidity layer (A5) is shown as forward direction, not
committed June-30 scope.
flowchart TB
subgraph current["Current — ascend-contracts"]
Hub1["Hub
(cloneDeterministic + initialize)"]
CF1["CreditFacility
(ERC-721 margin accounts)"]
MOD1["Modules:
Liquidity, Valuation, Risk,
Fee, Metadata, Freezer"]
DAA1["DistressedAssetsAuction"]
AUTH1["AuthorityWithIdentityRegistry
(ERC-3643 fallback)"]
Hub1 --> CF1
CF1 --> MOD1
CF1 --> DAA1
CF1 -.-> AUTH1
end
subgraph target["Target — 30 June additions (highlighted)"]
Hub2["Hub"]
CF2["CreditFacility
(identity + compliance wired via initialize / hooks)"]
MOD2["Modules
(existing)"]
DAA2["DistressedAssetsAuction"]
OID["ONCHAINID
(per-participant claims)"]
IR["Identity Registry
(+ ClaimTopics, TrustedIssuers, Storage)"]
MC["ModularCompliance
(rule-module catalog:
Trading Hours, Size, Frequency,
Country, Rate-limit, Sanctions,
Credit-draw, Credit Rating)"]
PLM["Permissioned Liquidity Module
(direction — beyond June-30)"]
Hub2 --> CF2
CF2 --> MOD2
CF2 --> DAA2
CF2 --> PLM
CF2 --> MC
CF2 --> IR
IR --> OID
MC --> IR
PLM --> IR
end
current ==>|"A1/A2 · A3"| target
classDef add stroke-width:2px
class OID,IR,MC add
The target keeps the existing Hub → CreditFacility → modules + DistressedAssetsAuction shape and
adds the identity, compliance, and permissioned-liquidity layer on top. Consumers hold pointers to the
Identity Registry and Compliance, and the registry resolves the ONCHAINID. The single-registry reconciliation
(one IdentityRegistry, not two) is the connective change.
This is the confirmed roles-and-permissions reference noted as pending against the abstract
AccessManager implementation. It is written as a specification for the
setTargetFunctionRole wiring and the role-admin graph in ascend-contracts — the
roles the protocol needs and how they nest — not an implementation dump. Roles tied to the permissioned
Liquidity Module (A5) are marked forward direction, consistent with the
body descope.
Ascend is an onchain tri-party agent: the neutral custodian-bank role of a classic tri-party arrangement is disintermediated into the Hub and its modules, while a licensed Clearing & Settlement Broker-Dealer carries the regulatory wrapper. Three economic parties — Borrower, Lender (cash), and the Agent / Operator (the broker-dealer running the Hub) — interact through one access authority.
Each verified party holds an ONCHAINID in the Identity Registry, checked on every ERC-3643 transfer. Claims
are issued offchain by a Trusted Issuer (ClaimIssuer). Claim topics for the product set:
KYC · Qualified Lender · Country. Credit Rating is
enforced as a Compliance rule module (A3), not an identity claim.
| Party | Required claims | Gate |
|---|---|---|
| Borrower | KYC (identity + AML / sanctions), Country (jurisdiction); collateral-eligibility and credit / risk limits enforced via Compliance. | Borrower-lifecycle selectors — identity path, or the Investor role. |
| Lender (cash) | KYC, Accredited / Qualified Lender, Country; plus the lender liquidity claim for the permissioned pool Forward · A5 | Lender deposit / withdraw eligibility, enforced in the liquidity module. |
The product model carries the fuller claim set above. A demo harness may stand in a coarser eligibility check for borrower verification and a single lender liquidity claim topic; the demo proves the mechanism, not the final claim schema.
The agent functions a tri-party custodian traditionally performs, encoded as roles in
ascend-contracts. June-30 core unless marked forward. Granularity is per-selector
(setTargetFunctionRole), but the role set is deliberately coarse — Operator absorbs collateral,
pause, hooks, fees, freeze, and risk params rather than splitting into many granular roles.
| Role | Agent function | Gates (illustrative) | Role admin |
|---|---|---|---|
| Admin | Root of trust + governance. | Configure the access graph (setTargetFunctionRole, grantRole, setRoleAdmin); reserve-tier + compliance registration. |
Self (under OWNERS — E.4). |
| Operator | Day-to-day agent operations. | Facility pause / unpause; collateral add / remove + pledge-pause; installHook / uninstallHook; fee + risk / valuation params; freeze (via FreezerModule); setAuthorizedRegistry. |
Admin |
| Onboarding Officer | KYC / compliance onboarding. | Role-admin of Investor + Liquidator; no direct facility powers. | Operator |
| Investor = Borrower | KYC'd account holder. | createMarginAccount, withdrawCollateral, borrowLiquidity, transferCollateral, transfer, closeMarginAccount. Interchangeable with the identity path. |
Onboarding Officer |
| Liquidator | Default management — seizure. | liquidate(...); also held by the default and auction venues. |
Onboarding Officer |
| Hub Admin | Provision a new tri-party book. | Hub.createCreditFacility(...). |
Admin |
| Auction Manager | Default management — auction venue config. | Auction duration, bid params, repayer / backstop / fee-collector, pause. | Admin |
| Verified Buyer | KYC'd auction bidder. | Auction bid(...). |
Auction Manager |
| DDF Operator | Default management — recovery. | Distressed-facility liquidate / recover / releaseTo. |
Admin |
| Reserve-Cover | Reserve-waterfall draw authority. | cover(...) on the reserve registry. |
Admin |
| PUBLIC | Intentionally permissionless. | depositCollateral and repayment paths. |
— |
FreezerModule override |
Kill-switch (not a role). | Operator-gated freeze of user / account; overrides all positive paths. | (Operator) |
Margin-account ops gate on the NFT owner, not the raw caller — an approved operator acts only while the owner still qualifies under role-or-identity.
Cross-cutting configuration is not a single unilateral key. The model is OpenZeppelin
AccessManager with role separation, execution delays / timelocks, and two-owner OWNERS
change control on cross-cutting config, with a full audit trail of configuration changes (the enforceable
governance admin requirement). There is no single unilateral upgrade path — this is the answer to
“who holds the keys / upgrade-rug risk.”
Implementation wiring, not operator grants. The Hub itself is the facility factory.
| Role | Held by | Why |
|---|---|---|
| Liquidity Broker | The CreditFacility contract |
Only the facility may move pool funds on a borrower's behalf. |
| Pool-Draw | The ReserveRegistry contract |
Only the registry orchestrates per-tier reserve draws. |
These gate the permissioned Liquidity Module, which is forward direction, not June-30 committed scope (see A5 — OZ to propose its approach). Listed so the access graph is coherent end-to-end.
Pool config (setToken, IOU-ratio caps, pause). Role admin: Admin. Forward direction — gates the permissioned variant of LiquidityModuleCore (A5).
Pool borrow / repay, held by the facility (see E.5). The borrow-side machine wiring is committed; the lender-side permissioning it serves is forward.
ONCHAINID eligibility enforced inside the module: registered identity + a valid liquidity claim from a trusted issuer + compliance canTransfer. Forward direction (A5).
For a clean spec boundary, the following are deploy-time test scaffolding, not roles to model in the product access graph:
| Item | Why it is not a product role |
|---|---|
| Latent enum entries | An entry declared in the role enum but bound to no selector and granted to no one is latent / dead — not part of the permission model. |
| Test-fixture personas | Additional deploy-time test accounts (a second borrower, a keeper, and similar) are fixtures for end-to-end runs, not a permission model. |
| Coarse eligibility stand-ins | A coarse boolean eligibility check is a harness stand-in for the full ERC-3643 claim verification specified in E.2. |
| Mapping point | Detail |
|---|---|
| Roles to scope items | Admin = the scope doc's Admin; Operator = the scope doc's Operator; Onboarding Officer = the KYC / compliance officer implied by the claim model (A2). |
| The A8 control slice | Per-account pause, freeze user, freeze tokens, global pause / shutdown (A8) is Operator-gated via FreezerModule + the facility's Pausable. |
| Coarse-by-design role set | The structurally separate operational roles are the default-management set (Liquidator / Auction Manager / DDF Operator / Reserve-Cover) and, forward, Liquidity Manager (A5). |