Contract Architecture
Amish deploys a hierarchy of smart contracts that execute loans, manage collateral, and issue debt tokens. The architecture uses a factory pattern with deterministic addressing to coordinate across chains without message passing. This design enables true cross-chain lending where collateral lives on one chain and principal on another.
Contract hierarchy

Core contracts
The protocol consists of four primary contracts:
AmishHub - The factory and coordination point on each chain. It deploys market contracts, routes transfers, and manages permissions. Users approve the hub once, and all market operations flow through it.
CollateralManager - Holds collateral for a specific market and executes collateral-related transfers. One CollateralManager exists per market per chain where that market accepts collateral.
DebtIssuer - Issues debt tokens for matched loans and executes principal transfers. One DebtIssuer exists per market per chain where that market disburses principal.
IssuedDebt - An ERC-20 token representing a single loan position. Each loan creates a new IssuedDebt contract. The token is transferable, allowing lenders to sell their positions on secondary markets.
Supporting infrastructure
RootsManager - An abstract contract providing epoch-based Merkle root management. Both CollateralManager and DebtIssuer inherit from it.
MerkleTransferExecutor - An abstract contract providing logic for executing transfers verified by Merkle proofs. Both CollateralManager and DebtIssuer inherit from it.
FactsRegistry - An external contract storing verified facts about remote chain state. Used for cross-chain coordination and state transition verification.
AmishHub: the factory
AmishHub serves as the central entry point on each chain. All market deployments and token transfers route through it.
Transfer routing

Users approve the hub to spend their tokens once. When they interact with any market - depositing collateral, transferring principal, repaying loans - the actual transfer happens through the hub:
Only approved contracts (CollateralManager, DebtIssuer, IssuedDebt) can trigger transfers through the hub. This architecture simplifies user approvals while maintaining security.
Market deployment
When a new market is needed, the hub deploys both a CollateralManager and DebtIssuer. The market ID is computed from the market parameters:
Both contracts deploy as minimal proxies (EIP-1167) using the market ID as the salt. This guarantees identical addresses for the same market across all chains.
Deterministic addressing
The protocol uses CREATE2 deployment to achieve deterministic contract addresses. The address is computed from:
The deployer address (AmishHub, which has the same address on all chains)
A salt (the market ID)
The bytecode hash (identical for all instances of a contract type)
Because all inputs are identical across chains, the same market deploys to the same addresses everywhere. The WETH/USDC market on Ethereum has the same CollateralManager address as the WETH/USDC market on Arbitrum.
This property enables cross-chain coordination without bridges. A contract on Arbitrum knows exactly where to find its counterpart on Ethereum - the address is mathematically determined from shared parameters.
Minimal proxy pattern
Market contracts deploy as EIP-1167 minimal proxies. A proxy is a tiny contract (45 bytes) that delegates all calls to a shared implementation.
The benefits are substantial. Deploying a full contract costs roughly 2,000,000 gas. Deploying a proxy costs around 100,000 gas. With potentially thousands of markets, this saves significant deployment costs.
Each proxy maintains its own storage. The implementation provides the logic; the proxy provides isolated state. EVM semantics guarantee that storage collisions between proxies are impossible.
CollateralManager
CollateralManager holds collateral for borrowers in a specific market. It inherits from RootsManager and MerkleTransferExecutor.
Transfer execution
Collateral deposits execute via Merkle proofs. The backend constructs a Merkle tree containing all pending transfers for an epoch. After committing the root on-chain, users submit their transfers with inclusion proofs:
The contract verifies each proof against the stored root, executes the transfer if valid, and nullifies the transfer to prevent replay.
Collateral top-up
Borrowers can add collateral to existing positions without going through the Merkle process:
Top-ups are immediate. The event and counter allow the backend to track collateral additions and update health factors accordingly.
DebtIssuer
DebtIssuer handles the principal side of loans. It issues debt tokens and manages principal transfers.
Debt token issuance
When a loan activates, the DebtIssuer creates an IssuedDebt token. Issuance requires a Merkle proof demonstrating the debt record exists in the epoch's market state:
The debt token address is deterministic - it derives from the debt record content via CREATE2. If someone tries to issue the same debt twice, the second deployment fails because the address is already occupied.
Hub delegation
IssuedDebt contracts need to move tokens for repayments and redemptions, but they are not directly approved transfer executors. Instead, they delegate through the DebtIssuer:
Only the specific IssuedDebt contract for that debt ID can trigger transfers. This maintains a clean permission model.
IssuedDebt: the debt token
Each loan has its own IssuedDebt ERC-20 token. The token represents the lender's claim on principal plus interest.
Token mechanics
When a loan issues, the original lender receives tokens equal to the original principal amount. As the borrower makes repayments, funds accumulate in the IssuedDebt contract:
Token holders can redeem their tokens for a proportional share of accrued funds:
Transferability
Debt tokens are standard ERC-20 tokens. Lenders can transfer them to other addresses, sell them on DEXs, or use them as collateral in other protocols. This provides liquidity before the loan matures.
Price calculation
The price per token reflects accrued funds divided by outstanding supply:
As repayments come in, the price increases. Token holders can redeem at any time at the current price.
Epoch-based state management
The protocol organizes state changes into epochs. Each epoch has three Merkle roots:
executableTransfersRoot - Pending transfer intents (deposits, disbursements)
marketStateRoot - Debt records and market state
liquidationsRoot - Liquidation records
The backend advances epochs by posting new roots:
Epochs must advance sequentially. The backend cannot skip epochs or post roots out of order.
State transition verification
Each epoch advancement verifies the state transition via the FactsRegistry:
The FactsRegistry contains cryptographic proofs that the transition from one epoch to the next is valid. This prevents the backend from posting arbitrary or malicious state.
Merkle transfer execution
Transfers execute by providing inclusion proofs against the epoch's transfer root. The process:
Verify the Merkle proof
Check the transfer has not been nullified
Nullify the transfer (prevent replay)
Execute the main transfer
Execute the fee transfer to the transaction submitter
The nullification key combines epoch ID and transfer ID. A transfer can only execute once per epoch. Even if the same transfer appears in multiple epochs, each instance has a unique nullification key.
Execution fees
Each transfer includes an optional execution fee:
The fee goes to whoever submits the transaction. This incentivizes third parties to execute transfers on behalf of users, enabling gasless UX where users set fees and others pay gas.
Cross-chain operation
For cross-chain loans, contracts coordinate through deterministic addressing and storage proofs:
CollateralManager on Chain A holds the borrower's collateral
DebtIssuer on Chain B disburses principal and issues debt tokens
Both have identical addresses derived from the same market ID
The FactsRegistry on Chain B verifies collateral exists on Chain A via storage proofs
The collateralizedOnChainId field in ImmutableDebtRecord tells the DebtIssuer which chain to verify collateral on. Cross-chain state synchronization happens through Merkle proofs committed via epoch advancement.
Access control
The protocol uses role-based access control:
DEFAULT_ADMIN_ROLE - Manages other roles, granted to the hub
POST_NEW_ROOTS_ROLE - Required to advance epochs
The hub owner assigns the POST_NEW_ROOTS_ROLE to backend operators. Only addresses with this role can post new epoch roots.
Pause mechanism
All contracts inherit pause functionality from the hub. When the hub pauses, all child contracts stop accepting operations:
This provides a global emergency stop. A single pause on the hub halts all deposits, transfers, redemptions, and liquidations across all markets.
Last updated