Cradle Protocol — Technical Whitepaper
On-chain real-world assets for Africa. The token standard, compliance layer, settlement model, lending protocol, launchpad mechanics, and wallet infrastructure that make the Cradle protocol work.
Abstract
Cradle is a regulated on-chain protocol for real-world asset tokenization and trading, built for African capital markets. It provides a shared compliance and settlement rail across three product surfaces — an Issuer Console for asset originators, a Marketplace for investors, and a Launchpad for token offerings — with every transaction governed by the same on-chain identity registry and compliance layer.
The protocol is built on ERC-3643 (T-REX), the permissioned token standard for regulated securities. All asset tokens carry programmable compliance rules directly in the contract, transfers settle atomically on-chain, and fee distribution happens at the smart-contract level on every fill. No off-chain reconciliation. No fragmented liquidity across siloed apps.
This document describes the technical architecture of the Cradle protocol: the token standard, the compliance and identity system, the trading and settlement model, the lending protocol, the token offering and vesting mechanics, and the wallet infrastructure that makes non-custodial participation accessible.
1. Protocol Architecture
The Cradle protocol is composed of six contract subsystems that share a single identity layer:
┌─────────────────────────────────────────────────────────────┐
│ Applications │
│ Issuer Console │ Marketplace │ Launchpad │
└─────────────┬────────┴────────┬──────────┴──────┬───────────┘
│ │ │
┌─────────────▼─────────────────▼─────────────────▼───────────┐
│ Protocol Subsystems │
│ │
│ ┌──────────────┐ ┌─────────────┐ ┌──────────────────┐ │
│ │ Tokens │ │ Trading │ │ Launchpad │ │
│ │ AssetToken │ │ Settlement │ │ ICO + Vesting │ │
│ │ Factory │ │FeeDistribut.│ │ │ │
│ └──────┬───────┘ └──────┬──────┘ └──────────────────┘ │
│ │ │ │
│ ┌──────▼──────────────────▼───────────────────────────────┐ │
│ │ Compliance + Identity Layer │ │
│ │ CradleIdentityRegistry · CradleComplianceModule │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────┐ ┌──────────────────────────┐ │
│ │ Lending Protocol │ │ Wallet Infrastructure │ │
│ │ LendingPool │ │ WalletAccount (CREATE2)│ │
│ │ InterestRateModel │ │ WalletFactory │ │
│ │ PoolFactory │ │ │ │
│ └───────────────────────┘ └──────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘The identity registry and compliance module are the load-bearing layer of the entire protocol. Every asset token calls the compliance module before executing a transfer. The compliance module queries the identity registry to verify both parties. If either check fails, the transfer reverts — at the protocol level, not as an application-layer rule.
2. Asset Tokenization — ERC-3643
2.1 Why ERC-3643
Standard ERC-20 tokens have no awareness of who holds them. Any address can receive any transfer. For securities — where regulators require KYC, jurisdictional restrictions, and investor type gating — this is unacceptable. ERC-3643 (the T-REX standard) extends ERC-20 with a compliance hook that runs on every transfer, consulting an on-chain identity registry and a pluggable compliance module before allowing the operation to proceed.
The result is a token that enforces securities law at the contract level. An unverified wallet cannot receive tokens. A transfer to a blocked jurisdiction reverts automatically. A lockup period cannot be bypassed. These rules are not application logic — they are the token.
2.2 CradleAssetToken
Every tokenized asset on Cradle is an instance of CradleAssetToken. The contract is an ERC-20 with the following additions:
Compliance hook on every transfer. Before transfer or transferFrom executes, _requireCompliance calls complianceModule.canTransfer(from, to, amount). If the module returns false, the transaction reverts with TransferNotCompliant. Minting (from address(0)) and burning (to address(0)) bypass this check by design — mint and burn are agent-controlled operations that occur under issuer authority.
Agent-controlled mint and burn. Only addresses in the agents mapping may call mint and burn. Agents are set by the owner and represent the issuer's authorised custody agents. Minting happens when a custodian confirms that the underlying asset is held. Burning happens when the asset is redeemed. This creates the on-chain/off-chain correspondence that proof-of-reserve audits verify.
Owner-controlled pause. The owner can call pause() at any time, which blocks all transfers (via the whenNotPaused modifier on both transfer functions and on mint/burn). This is the emergency circuit breaker — used when the underlying asset is suspended from trading, when a compliance investigation is open, or when an oracle feed becomes stale.
References to both the identity registry and the compliance module. The token holds addresses for both contracts. The compliance module is the decision engine. The identity registry is the data source the compliance module queries. These two dependencies are immutable post-deployment (set in the constructor) to prevent owner upgrades that could silently relax compliance rules.
2.3 CradleAssetTokenFactory
New asset tokens are deployed via CradleAssetTokenFactory.deployAssetToken(...). The factory takes a name, symbol, identity registry address, compliance module address, and an initial agents array — producing a fully configured CradleAssetToken and recording its address. Only the factory owner (the Cradle platform) can deploy new tokens, ensuring every asset on the marketplace has passed the issuer onboarding process before it has an on-chain address.
3. Identity and Compliance
3.1 CradleIdentityRegistry
The identity registry is the single source of truth for investor KYC state on Cradle. It maps wallet addresses to structured identity records:
struct Identity {
uint16 countryCode; // ISO-3166-1 numeric country code
uint8 investorType; // 0 = retail, 1 = accredited, 2 = institutional
bool exists;
}Registrar agents are the only addresses permitted to write to the registry (onlyAgent modifier). The owner manages the agent list — adding or removing addresses that represent KYC providers or the Cradle backend. In practice, the Cradle platform operator is the registrar for investor onboarding; a third-party KYC provider can also be granted agent status to write verified claims directly.
isVerified(wallet) returns a simple boolean — whether a wallet has a registered identity. This is the fast path called by the compliance module's identity check. getIdentity(wallet) returns the full (countryCode, investorType) tuple and reverts with IdentityNotFound if no record exists — the try/catch pattern in the compliance module uses this to safely handle the revert.
Removing an identity (removeIdentity) immediately blocks that wallet from receiving any compliant transfer. A revoked investor cannot accumulate new positions without re-verification.
3.2 CradleComplianceModule
The compliance module is the rule engine. It holds a set of independently-activatable restriction types, each identified by a bytes32 constant:
| Constant | Restriction |
|---|---|
IDENTITY_CHECK | Both from and to must have verified identities in the registry |
COUNTRY_RESTRICTION | The receiver must not be in a blocked country code |
MAX_HOLDER_COUNT | Total distinct holders may not exceed the configured cap |
REDEMPTION_LOCK | All transfers are blocked until block.timestamp >= lockUntil |
Restrictions are activated by the module owner via addRestriction(restrictionType, data), where data is an ABI-encoded parameter payload. For COUNTRY_RESTRICTION, data decodes to a uint16[] of blocked ISO country codes, which are unpacked into the restrictedCountries mapping. For MAX_HOLDER_COUNT, it decodes to a uint256 cap. For REDEMPTION_LOCK, it decodes to a uint256 Unix timestamp.
canTransfer(from, to, amount) is called by every CradleAssetToken transfer and evaluates active restrictions in order:
- Minting and burning (zero address) bypass all checks — these are issuer-controlled events.
- Zero-amount transfers pass — they're used for approval checks.
- IDENTITY_CHECK — both
fromandtomust returntruefromidentityRegistry.isVerified. A single unverified party causes an immediatereturn false. - COUNTRY_RESTRICTION — calls
identityRegistry.getIdentity(to)in atry/catch. If the identity lookup reverts (no record) or the returned country code is inrestrictedCountries, returnsfalse. The receiver's country, not the sender's, is what is checked — consistent with securities distribution rules. - MAX_HOLDER_COUNT — if the pool is full, the transfer is blocked unless the recipient already holds tokens (and is therefore already counted in the holder total). The existing-holder check queries
token.balanceOf(to) > 0. - REDEMPTION_LOCK — if
block.timestamp < lockUntil, all transfers are blocked regardless of other checks.
Multiple restrictions can be active simultaneously. All active restrictions must pass. Deactivating a restriction via removeRestriction cleans up the associated state — country codes are removed from the mapping, caps are reset to zero.
4. Trading and Settlement
Cradle Markets uses a hybrid model: order matching happens off-chain for performance; settlement executes on-chain for finality.
4.1 The Hybrid Model
An off-chain matching engine maintains the order book for each listed asset. When a buyer's limit order matches a seller's, the engine produces a matched trade record. A trusted relayer — a Cradle-operated server process — submits this matched trade to CradleSettlement.settle(...) on-chain. The settlement contract executes an atomic three-step sequence:
- Transfer
baseAmountofbaseTokenfrom the seller's wallet to the buyer's wallet. - Transfer
quoteAmountofquoteTokenfrom the buyer's wallet to the seller's wallet. - If
feeAmount > 0, transferfeeAmountofquoteTokenfrom the buyer's wallet to the fee distributor, then callfeeDistributor.recordFee(...)to allocate the custodian/platform split.
All three steps execute in one transaction. If any step fails, the entire transaction reverts. There is no partial settlement — either the full trade completes or nothing changes.
4.2 Replay Protection
Each trade carries a bytes32 nonce. The settlement contract tracks used nonces in usedNonces. Any attempt to resubmit a previously settled trade reverts with NonceAlreadyUsed. The relayer is the only address permitted to call settle — validated by the onlyRelayer modifier. If the relayer private key is rotated, the owner updates the relayer address via setRelayer.
4.3 CradleFeeDistributor
Trading fees are split at the smart-contract level between the asset's issuer (custodian) and the platform. The split ratio is configured via custodianSplitBps — at current configuration, issuers receive 70% of every trade fee (7000 basis points), the platform receives 30%.
recordFee is the main settlement-path entry point. It is onlySettlement — callable only by the CradleSettlement contract after it has already transferred the fee tokens to the distributor. The function updates the accounting mappings without executing another token transfer:
custodianShare = (amount * custodianSplitBps) / 10_000
platformShare = amount - custodianShare
custodianBalances[custodian][token] += custodianShare
platformBalances[token] += platformShareIssuers call claimFees(token) to withdraw their accumulated share at any time. The platform owner calls claimPlatformFees(token). The two-function design means fee accumulation is not subject to gas on every trade — claims happen in batches at the issuer's discretion.
A separate receiveFee entry point exists for direct integrations and test environments, accepting a transferFrom from the caller rather than relying on the settlement contract having already pushed tokens in.
5. Lending Protocol
5.1 CradleLendingPool
Cradle's lending protocol is an ERC-4626-style vault. Depositors supply the underlying asset (a stablecoin or liquid token) and receive vault shares representing their proportional claim on pool assets including accrued interest. Borrowers post collateral against the pool to receive loans.
Deposits and withdrawals follow the share/asset conversion model:
shares = assets * totalShares / totalManagedAssets (on deposit)
assets = shares * totalManagedAssets / totalShares (on withdrawal)On first deposit, the ratio is 1:1. As interest accrues — increasing totalManagedAssets without increasing totalShares — each share becomes redeemable for a larger quantity of the underlying asset. This is how depositor yield is delivered.
Borrowing requires the caller to post collateral. The current collateral factor is 75% LTV (collateralFactorBps = 7500), meaning a borrower must deposit at least 133% of the borrow amount in collateral value. Only one active loan per address is permitted at a time (ActiveLoanExists error if a position already exists).
requiredCollateral = (borrowAmount * 10_000) / collateralFactorBps
= borrowAmount * 10_000 / 7500
= borrowAmount * 1.333...Repayment is full or partial. If the remaining borrowedAmount reaches zero after repayment, the full collateral is returned to the borrower and the position is deleted.
Liquidation. A position becomes liquidatable when its health factor drops below 1e18:
healthFactor = (collateralAmount * collateralFactorBps / 10_000) * 1e18 / borrowedAmountA health factor below 1 means the collateral is no longer sufficient to cover the loan at the required LTV. Any external address can call liquidate(borrower), repaying the full debt and receiving the collateral plus a liquidation bonus (liquidationBonusBps = 500 → 5%). Any collateral remaining after the bonus is returned to the borrower.
5.2 CradleInterestRateModel
Interest rates are determined by a utilisation-based model with a kink (jump) mechanism — the same design used by Compound V2 and Aave.
Utilisation is defined as:
utilization = totalBorrowed / totalManagedAssetsBelow the kink utilisation threshold:
borrowRate = baseRate + (utilization * multiplier) / 1e18Above the kink:
borrowRate = baseRate + (kink * multiplier) / 1e18
+ ((utilization - kink) * jumpMultiplier) / 1e18The jumpMultiplier is significantly higher than multiplier, creating a steep rate increase when utilisation exceeds the kink. This discourages over-utilisation and protects depositor liquidity — if rates rise sharply, borrowers are incentivised to repay quickly.
Supply rate (what depositors earn) is:
supplyRate = borrowRate * utilization / 1e18At low utilisation, depositors earn little because few borrowers are paying interest. At high utilisation, both borrowers and depositors see elevated rates.
All parameters (baseRate, multiplier, jumpMultiplier, kink) are immutable — fixed at deployment. A new rate model is deployed and pointed to by the pool if parameters need to change.
5.3 CradleLendingPoolFactory
Pools are deployed via CradleLendingPoolFactory.createPool(asset, interestRateModel). The factory enforces one pool per asset (PoolAlreadyExists if a pool for the given asset already exists), maintains the list of all deployed pools, and provides an asset-to-pool lookup. Only the factory owner can create pools.
6. Token Offerings and Vesting
6.1 CradleICO
The Launchpad product is powered by CradleICO — a token sale contract supporting hard/soft caps, per-wallet allocation limits, multiple accepted payment tokens, and automatic refunds on failure.
Lifecycle states:
Pending → Active → Completed
↘ FailedgetState() derives the current state from block.timestamp, startTime, endTime, totalRaised, hardCap, and softCap:
- Before
startTime:Pending - Between
startTimeandendTimewithtotalRaised < hardCap:Active totalRaised >= hardCapat any point:Completed(hard cap hit mid-sale terminates it immediately)- After
endTimewithtotalRaised >= softCap:Completed - After
endTimewithtotalRaised < softCap:Failed
Participation. During the Active state, investors call participate(paymentToken, amount). The contract checks: the payment token is in acceptedPaymentTokens; the new cumulative contribution is within [minAllocation, maxAllocation]; the total raised after this contribution does not exceed hardCap. Funds are pulled via transferFrom.
Claiming. Once Completed, participants call claimTokens(). Token amount is calculated as:
tokenAmount = (contribution * 1e18) / priceRefunds. If the sale reaches Failed, investors call refund() to recover all their contributions. The contract iterates the participant's Participation array, transferring each payment token back and zeroing the amounts to prevent double-refund.
Fund withdrawal. After a successful sale, the issuer (owner) calls withdrawFunds(paymentToken, amount) to extract raised capital.
6.2 CradleVesting
Token allocations from ICOs with vesting requirements are managed by CradleVesting. Each beneficiary has at most one VestingSchedule:
struct VestingSchedule {
uint256 totalAmount;
uint256 claimedAmount;
uint256 startTime;
uint256 cliffDuration;
uint256 vestingDuration;
}The vested amount at any given block.timestamp is:
0if beforestartTime + cliffDuration(cliff has not passed)totalAmountifblock.timestamp >= startTime + vestingDuration(fully vested)- Linear interpolation between cliff end and vest end:
totalAmount * elapsed / vestingDuration
vestedAmount = totalAmount * (block.timestamp - startTime) / vestingDurationNote: linear interpolation uses time elapsed from startTime, not from the cliff. This means the cliff "unlocks" the linearly-accrued portion from start to cliff-end in one step, then continues accruing linearly thereafter.
claim() transfers getClaimableAmount(beneficiary) to msg.sender, which is vestedAmount - claimedAmount. The claimedAmount cursor is updated atomically with the transfer.
Schedules can only be created by the authorised icoContract address. This prevents arbitrary schedules from being registered — only the ICO contract that distributes tokens can attach vesting to those distributions.
7. Wallet Infrastructure
7.1 CradleWalletAccount
Each user on the Cradle platform has a dedicated smart contract wallet — a CradleWalletAccount — rather than interacting directly with an EOA. The wallet is controlled by the Cradle executor (a backend relayer that submits transactions on behalf of users), which means users are not required to hold ETH for gas or manage signing keys directly.
The wallet exposes:
execute(target, value, data)— single arbitrary callexecuteBatch(targets, values, datas)— atomic multi-call in one transactionapproveToken(token, spender, amount)— convenience ERC-20 approvalsetGuardian(newGuardian)— guardian assignment for recoveryrecoverOwnership(newOwner)— ownership rotation, callable by either owner or guardian
Guardian model. The guardian is a recovery address that can rotate the _owner via recoverOwnership. This provides a path for users to recover their wallet if the Cradle executor rotates keys or if an individual user wants to transition to self-custody by setting themselves as guardian and recovering ownership to their own EOA.
7.2 CradleWalletFactory
Wallets are deployed deterministically using CREATE2 via CradleWalletFactory. The salt is a bytes32 userIdHash — the keccak256 of the user's unique identifier. Because CREATE2 addresses are derived from the deployer address, bytecode, and salt, the wallet address for any user is predictable before deployment:
predicted = keccak256(0xff ++ factory ++ userIdHash ++ keccak256(bytecode))
truncated to addresscomputeWalletAddress(userIdHash) returns this prediction without deploying. The practical consequence is that incoming transfers can be sent to a user's wallet address before the wallet is deployed — the wallet is pre-funded at its future address, and the first deposit or trade triggers deployment.
Batch deployment via createWalletBatch(userIdHashes[]) deploys multiple wallets in a single transaction, reducing onboarding overhead for institutional participants or bulk user imports.
8. Product Surfaces
The protocol is accessed through three applications, each targeting a different participant type:
Issuer Console (issuer.cradlemarkets.com)
Asset originators use the Issuer Console to move through a 7-step onboarding wizard: entity verification, beneficial ownership declaration, KYB document submission, compliance attestation, asset declaration, custody confirmation, and token mint request. Once approved, issuers can monitor their on-chain KPIs (total assets, pending audits, live listings), and claim accumulated trading fees — the 70% custodian share of every fill on assets they originated.
Marketplace (app.cradlemarkets.com)
The investor-facing exchange. Verified investors browse listed assets across four classes — equities, bonds, local-currency stablecoins, and commodities — and trade on a live order book with limit and market orders, price-time priority matching, and WebSocket-streamed fills. The same application hosts the lending interface (deposit, borrow, repay, monitor health factor) and portfolio analytics.
Launchpad (launchpad.cradlemarkets.com)
The capital-raising surface. Companies run compliant token offerings — IPOs, corporate bond issuances, infrastructure raises — with the 7-step offering wizard generating the CradleICO contract configuration. Vesting schedules, hard caps, soft caps, investor management, and on-chain distribution are handled by the protocol. The dashboard aggregates raise progress across all offerings a company has open.
9. Asset Classes
| Class | Description | Settlement token |
|---|---|---|
| Equities | Fractional ownership of listed African corporations (NSE, NGX, JSE) | USDC / local stablecoin |
| Bonds | Sovereign and corporate debt with transparent yield curves and on-chain redemption schedules | USDC / local stablecoin |
| Local-Currency Stablecoins | NGN, KES, GHS tokens redeemable 1:1 against fiat reserves | Native |
| Commodities | Gold, oil, and agricultural commodities backed by physical reserves | USDC |
Local-currency stablecoins are a first-class asset class on Cradle, not just a settlement mechanism. Naira and Shilling stablecoins keep capital on-shore, eliminate FX leakage on domestic transactions, and enable on-chain yields denominated in local currency — meaningful for retail investors whose income and obligations are in local currency.
10. Security Properties
Compliance is protocol-level. Transfer restrictions are not application rules that can be bypassed by calling the contract directly — they are enforced inside CradleAssetToken.transfer and CradleAssetToken.transferFrom. A transfer to an unverified wallet reverts at the EVM level regardless of what application layer initiated it.
Settlement is atomic. CradleSettlement.settle either executes all three token movements in one transaction or reverts entirely. There is no state where a buyer has paid but not received tokens, or vice versa.
Nonce protection prevents replay. Each settled trade carries a unique bytes32 nonce tracked in usedNonces. A submitted trade cannot be re-executed.
Mint and burn are agent-gated. Only addresses explicitly authorised by the token owner can mint new supply or burn existing supply. Unauthorised inflation of token supply is not possible.
Pause capability. Any asset token can be paused by its owner, blocking all transfers, mints, and burns. This is the emergency halt for situations such as a trading suspension on the underlying exchange or a compliance hold.
Liquidation is permissionless. Any external address can trigger liquidation of an unhealthy position. This removes the dependency on Cradle operating a proprietary liquidation bot — market participants can observe on-chain health factors and liquidate for the bonus, keeping the lending pool solvent.
Wallet recovery path exists. The guardian model on CradleWalletAccount ensures that a key rotation on the Cradle executor does not permanently lock users out. Recovery can be initiated by the guardian, which can be the user's own EOA.
11. Contract Index
| Contract | Purpose |
|---|---|
CradleAssetToken | ERC-20 security token with compliance hooks and agent-gated mint/burn |
CradleAssetTokenFactory | Deploys and registers new asset token instances |
CradleIdentityRegistry | On-chain KYC identity store with agent-based registration |
CradleComplianceModule | Pluggable transfer-rule engine (identity, country, holder count, lockup) |
CradleSettlement | Atomic trade settlement via trusted relayer with nonce replay protection |
CradleFeeDistributor | Custodian/platform fee split with claimable balances |
CradleLendingPool | ERC-4626-style deposit/borrow/liquidate vault |
CradleInterestRateModel | Utilisation-based kink interest rate model |
CradleLendingPoolFactory | One pool per asset deployment and registry |
CradleICO | Token sale with caps, per-wallet limits, and refund-on-failure |
CradleVesting | Linear cliff vesting for ICO token distributions |
CradleWalletAccount | Per-user smart contract wallet with batch execution and guardian recovery |
CradleWalletFactory | CREATE2 deterministic wallet deployment |
Cradle · cradlemarkets.com
© 2026 Cradle Labs Limited. All rights reserved.