This repository contains the core Solidity contracts for the Perimeter Protocol, an audited, open-source protocol that anyone can freely use to build unique credit applications using USDC.
A project by Circle Research, Perimeter offers several important features, such as the ability to delegate loan management and monitoring, integration with open identity standards, and the flexibility to accommodate different types of credit instruments. Perimeter stands out for not relying on a protocol token and its adaptability to various scenarios.
Please read more about Perimeter in the whitepaper.
Perimeter consists of a number of smart contracts including lending pools, loan constructs, and permissioning helpers.
-
Protocol participants include protocol administrators, Pool Admins, Borrowers, and Lenders.
-
Global protocol administrative roles (
Admin
,Pauser
,Deployer
,Operator
) are defined through the singletonServiceConfiguration
contract. -
PoolAdmins create ERC-4626 compliant lending pools through a
PoolFactory
. Pools manage the accounting related to deposits, withdrawals, loan funding, loan payments, and defaults. -
Borrowers create loans through a
LoanFactory
, which are tied to a pool. Each loan is self-contained, with its own lifecycle and maturation process according to its unique schedule and terms. Both open and fixed-term loans are available. -
Lenders deposit into pools to receive pool tokens at an exchange rate determined by the Pool and based on the Pool's current Net Asset Value (NAV). The NAV is computed based on the available liquidity, liquidity outstanding (deployed to loans), and expected interest at that given block.
The Perimeter architecture was designed with modularity in mind, allowing new PoolFactory
and LoanFactory
instances to be attached to the protocol through the global ServiceConfiguration
, potentially creating Pools and Loans with new features.
Perimeter allows upgrades to be performed unilaterally by the global Deployer
role. The following contracts are upgradeable by the Deployer
:
-
"Singleton" contracts like the
ServiceConfiguration
,PoolAdminAccessControl
, andToSAcceptance
can be individually upgraded in-place through the UUPS pattern. -
The "1-to-N" contracts emitted by factories (
Pool
,Loan
,WithdrawController
,Vault
,PoolController
) are implemented as Beacon proxies, allowing a single transaction to update an implementation across all proxies simultaneously.
As noted in the diagram, there are several layers of permissioning, which utilize a combination of Verite-based, privacy-preserving, decentralized identity credentials and standard allowlists, allowing Pool Admins to determine the appropriate permissioning strategy for their pools.
Specifically, for handling identity credentials, Perimeter follows a verifier pattern, in which trusted verifiers are registered with the protocol and can attest to verifying the privacy-preserving credentials off-chain. This verification result is then recorded on-chain, granting access for a period of time.
Perimeter applies permissioning in several places:
-
Permissioning which accounts can create new Pools. This is gated through the
PoolAdminAccessControl
, a singleton contract which maintains a registry of Verite credential attestations for Pool Admins. The specific allowed credential schemas and trusted verifiers can be configured through the protocolOperator
. Additionally, consenting to the protocol-wide Terms of Service is required, managed through theToSAcceptanceRegistry
. -
Permissioning lender access to Pools: a flexible
PoolAccessControl
contract allows individual Pool Admins to specify either a credential-based approach to permissioning or an allow list-based approach (or both) for their given Pool. Prior to enabling access, lenders must first mark their consent to theToSAcceptanceRegistry
.
Perimeter is written as a basic Hardhat project. To get started, install the dependencies:
npm install
Copy .env.example and update with appropriate values:
cp .env.example .env
For local dev, it's helpful to test scripts against a network running locally.
npx hardhat node
npx hardhat run scripts/deploy.ts
Documentation is generated by solidity-docgen
npm run docs
# or
npx hardhat docgen
npm test
# or
npx hardhat test
npm run test:gas
# or
REPORT_GAS=true npx hardhat test
npm run test:coverage
# or
npx hardhat coverage
There are several hardhat tasks to allow for easy modification of the contracts.
Updating the ServiceConfiguration:
- setPaused
- setLiquidityAsset
- setLoanFactory
- setTosAcceptanceRegistry
You can find complete instructions for how to run each command by running the following:
npx hardhat setPaused --help
Here is an example command to pause a hypothetical contract:
npx hardhat setPaused --address 0x869076ca72531B5474F9182d735a3e3F2e365fc6 --paused true
Update the config in scripts/verite-verify.ts
with the appropriate values, then run
npx hardhat run scripts/verite-verify.ts
This will print out a verification result that can be send to the verify()
method on the given contract.
Contract source can be uploaded and verified to Etherscan. Update .env
to include your Etherscan API key.
For each deployed contract, run the following:
npx hardhat verify --network goerli CONTRACT_ADDRESS
There are three contracts that require constructor arguments:
- WithdrawControllerFactory
- PoolControllerFactory
- PoolFactory
npx hardhat verify --network goerli CONTRACT_ADDRESS arg1 arg2 arg3
There are several deployment scripts available (see scripts/deploy.ts
). These require a number of values to be set in an .env
file in the root of the repository.
We welcome contributions, bug fixes, and feature suggestions to Perimeter. Please open an issue or a pull request with your ideas!