Deployment
All major Grunt contracts are deployed through factory contracts that implement the beacon proxy pattern. This guide covers how factories work, how to deploy new instances, and how to manage upgrades.
Factory Architecture
Section titled “Factory Architecture”Each factory owns an UpgradeableBeacon that points to a single implementation contract. When the factory creates a new instance, it deploys a lightweight BeaconProxy that delegates all calls to the implementation address stored in the beacon.
graph TD
F[Factory] --> B[UpgradeableBeacon]
B --> I["Implementation (shared)"]
I -.->|delegates| P1["BeaconProxy (instance 1)"]
I -.->|delegates| P2["BeaconProxy (instance 2)"]
I -.->|delegates| PN["BeaconProxy (instance N)"]
This architecture provides two key properties:
- Gas-efficient deployment – each new instance only deploys a minimal proxy, not the full implementation bytecode.
- Atomic upgrades – updating the beacon’s implementation pointer upgrades every proxy in a single transaction.
Factories
Section titled “Factories”| Factory | Creates | Key Parameters |
|---|---|---|
RequestFactory |
Request + PT vault + YT vault | asset, funding/repayment deadlines, puller |
PositionManagerFactory |
PositionManager instances | collateral asset, debt asset, owner |
MorphoBorrowPositionFactory |
MorphoBorrowPosition instances | Morpho contract, market ID, position manager, custom LLTV |
USCCFundFactory |
USCCFund wrappers | USCC token, recipient, owner |
TransferGuardFactory |
TransferGuard instances | owner |
Deploying Instances
Section titled “Deploying Instances”Borrow Position
Section titled “Borrow Position”Deploy a new Morpho Blue borrow position with a custom LLTV:
import {MorphoBorrowPositionFactory} from "src/borrow/MorphoBorrowPositionFactory.sol";import {IMorpho} from "src/interfaces/IMorpho.sol";
// Factory is already deployed and owns its beaconMorphoBorrowPositionFactory factory = MorphoBorrowPositionFactory(factoryAddress);
// Create a new borrow position with 72% custom LLTVaddress bp = factory.createBorrowPosition( morpho, // IMorpho -- Morpho Blue protocol contract marketId, // Id -- Morpho market ID positionManager, // address -- owner controlling this position 0.72e18 // uint256 -- custom LLTV (must be <= market LLTV));The factory emits a BorrowPositionCreated event with the new proxy address and initialization parameters.
Position Manager
Section titled “Position Manager”Deploy a new Position Manager for a collateral/debt pair:
import {PositionManagerFactory} from "src/manager/PositionManagerFactory.sol";
PositionManagerFactory factory = PositionManagerFactory(factoryAddress);
address pm = factory.createPositionManager( collateralAsset, // address -- e.g., wUSCC debtAsset, // address -- e.g., USDC owner // address -- admin who can configure the PM);After deployment, the owner must configure the Position Manager by adding borrow modules and setting queues:
PositionManager pm = PositionManager(pmAddress);
// Add a borrow position as a modulepm.addModule(borrowPositionAddress);
// Set the supply queue (order matters for deposit routing)PositionManager.SupplyQueueEntry[] memory supplyQueue = new PositionManager.SupplyQueueEntry[](1);supplyQueue[0] = PositionManager.SupplyQueueEntry({ position: borrowPositionAddress, maxBorrow: 5_000_000e6 // max borrow per deposit});pm.setSupplyQueue(supplyQueue);
// Set the withdrawal queueaddress[] memory withdrawalQueue = new address[](1);withdrawalQueue[0] = borrowPositionAddress;pm.setWithdrawalQueue(withdrawalQueue);Request
Section titled “Request”Deploy a Request with its associated PT and YT vaults:
import {RequestFactory} from "src/request/RequestFactory.sol";
RequestFactory factory = RequestFactory(factoryAddress);
address request = factory.createRequest( asset, // address -- the funding asset (e.g., USDC) fundingDeadline, // uint256 -- timestamp after which funding closes repaymentDeadline, // uint256 -- timestamp after which redemptions open puller // address -- the Facility address authorized to pull funds);The factory deploys three contracts atomically: the Request itself, a PT vault, and a YT vault.
Transfer Guard
Section titled “Transfer Guard”Deploy a compliance guard:
import {TransferGuardFactory} from "src/guard/TransferGuardFactory.sol";
TransferGuardFactory factory = TransferGuardFactory(factoryAddress);
address guard = factory.createTransferGuard( owner // address -- admin who configures token modes and address statuses);USCC Fund
Section titled “USCC Fund”Deploy a fund wrapper for Bitwise USCC:
import {USCCFundFactory} from "src/funds/USCCFundFactory.sol";
USCCFundFactory factory = USCCFundFactory(factoryAddress);
address fund = factory.createUSCCFund( uscc, // address -- USCC token contract recipient, // address -- Bitwise deposit recipient owner // address -- admin);Upgrading Implementations
Section titled “Upgrading Implementations”The beacon owner can upgrade all proxies created by a factory by updating the beacon’s implementation pointer:
import {UpgradeableBeacon} from "solady/utils/UpgradeableBeacon.sol";
// Get the beacon address from the factoryaddress beacon = factory.beacon();
// Deploy a new implementationaddress newImplementation = address(new MorphoBorrowPosition());
// Upgrade all proxies at once (only callable by beacon owner)UpgradeableBeacon(beacon).upgradeTo(newImplementation);After this call, every BeaconProxy created by the factory delegates to the new implementation. Existing storage is preserved – the proxy’s storage layout is independent of the implementation bytecode.
Important considerations when upgrading:
- New implementations must be storage-compatible with the previous version (append-only to ERC-7201 storage structs)
- The beacon owner should be a multisig or timelock for production deployments
- Test the new implementation thoroughly against existing proxy state before upgrading
Deployment Scripts
Section titled “Deployment Scripts”The repository includes Foundry deployment scripts in the script/ directory. The primary deployment script is script/Request.s.sol, which demonstrates deploying the full Request stack.
Run a deployment script:
forge script script/Request.s.sol --rpc-url $RPC_URL --broadcastFor local development, use Anvil:
# Start a local nodeanvil
# Deploy to local nodeforge script script/Request.s.sol --rpc-url http://localhost:8545 --broadcastVerifying Contracts
Section titled “Verifying Contracts”Since proxies delegate to shared implementations, you only need to verify the implementation contracts and the factories. The proxy bytecode is standard BeaconProxy from Solady.
forge verify-contract \ $IMPLEMENTATION_ADDRESS \ src/borrow/MorphoBorrowPosition.sol:MorphoBorrowPosition \ --chain-id 1 \ --etherscan-api-key $ETHERSCAN_KEY