Settlement
Settlement in Grunt is the process by which bridge facilitator offers become funded bridge loans on-chain. This page explains who calls what, how the Request contract moves through funding and repayment, and how signed offers are verified.
For API-facing onboarding, address setup, and status tracking, start with Onboarding.
Request Contract
Section titled “Request Contract”Every bridge loan is settled through a Request contract. The Facility deploys one Request per intent, and it serves as the settlement intermediary between bridge facilitators and the protocol.
The Request contract handles four concerns:
- Funding — accepting capital from bridge facilitators in exchange for PT/YT tokens
- Disbursement — allowing the Facility (the designated puller) to draw down the bridge loan
- Repayment — receiving repayment from the Facility once the underlying strategy settles
- Redemption — allowing PT/YT holders to redeem their tokens for the underlying asset
Lifecycle
Section titled “Lifecycle”graph LR
D[Deploy] --> F[Funding Phase] --> U[Fund Utilization] --> R[Repayment] --> RE[Redemption]
- Deploy — The RequestFactory creates a new Request along with its PT and YT vault contracts. The Request is initialized with the asset token, puller address, and repayment deadline.
- Funding phase — Bridge facilitators fund the Request through signed offer consumption or authorized push funding. PT and YT are minted as receipts.
- Fund utilization — The Facility calls
pullFunds()to transfer bridge capital into the strategy. - Repayment — The Facility returns funds through
repay(), and the owner later marks the Request repaid viasetRepaid(). - Redemption — PT and YT holders redeem against the assets now held by the Request.
Verifying a Request On-Chain
Section titled “Verifying a Request On-Chain”Each bridge loan settles through a freshly-deployed Request contract, so the address you interact with is not predictable in advance. To let bridge facilitators confirm on-chain that a given address is a legitimate 3F-issued Request (and not a contract spoofed by a third party), 3F maintains an on-chain attestation registry: the Request Whitelist. See the Request Whitelist page for the full design rationale, governance model, and security properties; this section is the integration recipe.
Deployment
Section titled “Deployment”| Network | Address |
|---|---|
| Ethereum mainnet | 0x3FCD87948CBF46605d6dEd0eD56d3Daccd9DAF9e |
How to integrate
Section titled “How to integrate”Before approving funds to a Request, signing an offer for it, or accepting a consume() call from one, gate strictly on isWhitelisted(address) == Whitelisted. The registry returns one of four states; only Whitelisted is safe to authorise, every other value (including the two Paused* variants) means “do not proceed”.
You can integrate against the enum or directly against the underlying uint8.
With the enum:
enum WhitelistStatus { NotWhitelisted, Whitelisted, PausedNotWhitelisted, PausedWhitelisted}
interface IWhitelist { function isWhitelisted(address addr) external view returns (WhitelistStatus);}
require(REQUEST_WHITELIST.isWhitelisted(request) == WhitelistStatus.Whitelisted);Without the enum:
interface IWhitelist { function isWhitelisted(address addr) external view returns (uint8);}
require(REQUEST_WHITELIST.isWhitelisted(request) == 1);The same call can be used from off-chain code (an eth_call against the registry) before signing an offer or sending an approval. Treat the registry’s output as the single source of truth — the registry’s pause state is reflected directly in the return value, so you do not need to read pause state separately.
Actor Matrix
Section titled “Actor Matrix”Most operational confusion comes from role boundaries. The following matrix shows who acts at each stage:
| Actor | Controlled by | Main responsibility | Typical on-chain calls |
|---|---|---|---|
| BF identity | You | Account-level approval and integration identity | No required settlement calls |
| BF operational funding address | You | Provides capital, receives PT/YT, and later redeems | mint() in push-funded flows, token approvals, redemption |
| 3F authorizer / consumer | 3F | Reflects accepted offers on-chain | authorizeMinting(), consume() |
| Facility puller | 3F | Draws bridge capital into the strategy and later repays it | pullFunds(), repay() |
| Request owner | 3F | Final state transitions and request-level administration | setRepaid(), owner-gated admin actions |
In the simplest setup, your BF identity and operational funding address are the same. In more advanced setups, the operational funding address can be a contract or multisig, as long as it can approve funds, receive PT/YT, and complete redemption later.
Accepted Offer to Redemption
Section titled “Accepted Offer to Redemption”Once an offer is accepted, settlement follows a strict sequence:
graph LR
A[Offer Accepted] --> B[consume or authorizeMinting]
B --> C[Funds Enter Request]
C --> D[PT/YT Minted]
D --> E[pullFunds]
E --> F[repay]
F --> G[setRepaid]
G --> H[PT/YT Redeemed]
- Request deployed — the Facility creates a Request for a specific strategy or intent.
- Offer accepted off-chain — the auction engine chooses a bridge facilitator offer.
- Funding path begins:
- Signed pull path — a 3F operator calls
consume()with the signed offer. The Request verifies the signature and pulls funds from the BF funding address. - Authorized push path — a 3F operator calls
authorizeMinting(). The BF funding address then approves the asset and callsmint().
- Signed pull path — a 3F operator calls
- PT/YT minted — the BF funding address receives PT and YT that represent principal and upside claims.
- Facility drawdown — the Facility calls
pullFunds()to move the bridge capital into the strategy. - Repayment — the Facility returns capital through
repay()once the strategy settles. - Redemption enabled — the Request owner calls
setRepaid(). - BF redemption — the same BF funding address that received PT/YT redeems them for the underlying asset.
The Offer Struct
Section titled “The Offer Struct”When a bridge facilitator signs an offer, it follows this on-chain structure:
struct Offer { address maker; // Funding address that will receive PT/YT uint256 amount; // Maximum principal the bridge facilitator will provide uint256 expectedReturn; // Yield required on that principal uint256 nonce; // Sequential nonce, must exceed stored minimum uint256 expiration; // Unix timestamp after which the offer is void bool useCallback; // Whether to call onRequestConsumed before pulling}The offer is signed as EIP-712 typed data against the specific Request contract’s domain. This binds the offer to a single Request and prevents replay across contracts or chains.
When consumed, the Request may fill the offer partially or fully. For a partial fill of ptAmount tokens, the corresponding YT amount is calculated proportionally:
ytAmount = offer.expectedReturn * ptAmount / offer.amountSigned Field Semantics
Section titled “Signed Field Semantics”| Field or concept | Meaning |
|---|---|
maker |
The on-chain funding address that is tracked for nonce state and receives PT/YT. |
deadline |
Endpoint-level authentication expiry for API typed-data auth. It is separate from settlement logic and should be short-lived. |
expiration |
Offer-level validity inside the signed Offer message. |
| Request binding | The EIP-712 domain verifyingContract and chainId tie the signature to one Request on one chain. |
nonce |
Replay protection and cancellation state. An offer is valid only if its nonce is strictly greater than the stored minimum nonce for that address. |
useCallback |
Commits the offer to just-in-time capital sourcing behavior before the Request pulls funds. |
Auction Engine
Section titled “Auction Engine”The auction engine is the off-chain component that coordinates offer collection and selection. It is not a smart contract; it is a service operated by 3F that manages the RFQ process.
What the auction engine does
Section titled “What the auction engine does”- Collects offers — receives signed offers and API-managed offers from bridge facilitators.
- Evaluates pricing — selects the offers that provide the lowest cost of capital for the Facility.
- Executes settlement — either consumes signed offers or authorizes push-funded minting.
- Reports status — updates lifecycle status so bridge facilitators can track positions through the API.
Trust model
Section titled “Trust model”The auction engine is a coordination layer. It does not hold bridge facilitator funds or signing authority over capital. The on-chain guarantees remain:
- the Request contract verifies every signature independently
- funds move through standard token transfer and approval rules
- PT/YT minting is deterministic based on the accepted offer parameters
- the auction engine cannot modify a signed offer after it has been produced
Signer Management
Section titled “Signer Management”Funding address vs. signing key
Section titled “Funding address vs. signing key”In many setups, the address that ultimately provides capital is not the same key that signs operational messages. For example:
- a multisig treasury may control the funding address while a delegated operator signs requests
- a smart contract vault may implement its own authorization logic for signing or callbacks
The Request contract supports this through EIP-1271 signature validation. When consuming an offer, the contract checks:
- If the
makeris an EOA, it validates the signature usingecrecover. - If the
makeris a contract, it callsisValidSignature()on that contract.
This allows a bridge facilitator to use multisigs, vaults, or custom authorization contracts without changing the settlement model.
Authorization for push-funded offers
Section titled “Authorization for push-funded offers”For API-managed push-funded offers, the flow is more explicit. A 3F operator calls authorizeMinting(funder, principalAmount, yieldAmount) for a specific BF funding address, and that funding address later calls mint() to transfer funds into the Request.
After mint(), the same funding address holds the PT/YT position and later performs redemption once the Request has been marked repaid.
Nonce Management
Section titled “Nonce Management”Every signed offer includes a nonce field. Nonces serve two purposes: replay protection and offer cancellation.
How nonces work
Section titled “How nonces work”- The Request contract stores a minimum nonce per funding address.
- An offer’s nonce must be strictly greater than the stored minimum.
- Nonce values start at 1; nonce 0 is always invalid.
- Nonces do not need to be sequential; gaps are allowed.
Soft cancel
Section titled “Soft cancel”To cancel an outstanding offer without an on-chain transaction, replace or withdraw it through the off-chain auction flow before it is consumed. This is operationally convenient, but it does not invalidate an already-signed payload on-chain by itself.
Hard cancel
Section titled “Hard cancel”To irrevocably invalidate all offers at or below a given nonce, call setMinNonce(newMinNonce) on the Request contract. This updates the stored minimum nonce for your funding address.
After calling setMinNonce(5):
- all offers with nonce 1, 2, 3, 4, or 5 are permanently invalid
- future offers must use nonce 6 or higher
Use hard cancellation when you need certainty that an old signature cannot be replayed, regardless of off-chain coordination state.
Choosing a strategy
Section titled “Choosing a strategy”| Scenario | Recommended approach |
|---|---|
| Replace an offer with better terms | Soft update through the auction flow |
| Withdraw from an auction entirely | Hard cancel with setMinNonce() |
| Routine offer refreshes | Reissue offers with a deliberate nonce policy |
| Emergency: revoke all outstanding offers | Hard cancel with a high nonce value |
Illustrative Integration Test Flow
Section titled “Illustrative Integration Test Flow”You can validate most BF integration logic on Sepolia or another test environment without recreating the full off-chain matching engine.
- Create or deploy a Request with a mock puller and mock owner you control.
- If you are testing the push-funded path, call
authorizeMinting()from the mock owner or consumer role. - From the BF funding address, approve the asset and call
mint(). - From the mock puller, call
pullFunds()to move capital into the simulated strategy. - From the mock puller, call
repay()with principal plus return. - From the mock owner, call
setRepaid(). - Redeem PT/YT from the BF funding address.
This is enough to verify approvals, PT/YT issuance, repayment gating, and redemption sequencing without running the full auction service.
On-Chain Lenders
Section titled “On-Chain Lenders”The current system sources bridge loan capital exclusively from off-chain bridge facilitators who submit offers through the auction engine. Future versions of Grunt may also support on-chain lending protocols as capital sources, including:
- CAP — programmatic lending vaults
- Wildcat — permissioned credit markets
- 3Jane — structured credit
These integrations would allow the Request contract to draw capital directly from on-chain lending pools alongside or instead of bridge facilitator offers. The PT/YT settlement mechanics remain the same; the difference is in how capital enters the Request.