Skip to content
3F Docs

Onboarding

This guide is the operator-facing entry point for bridge facilitators integrating with Grunt. It focuses on how to set up your addresses, authenticate, discover auctions, submit offers, track status, and complete the bridge loan lifecycle.

Environment Chains Base URL
Developement Ethereum Sepolia https://bf.dev.gcp.3f.xyz
Production Ethereum Mainnet https://bf.3f.xyz

Before you begin, ensure you have:

  1. A funded wallet on the target network — you need gas for signatures, approvals, and any on-chain funding or redemption steps.
  2. Access to the required asset — your operational setup must be able to source the asset used by the Request contracts you intend to fund (for example, USDC).
  3. An approved bridge facilitator identity — 3F must approve the address your organization will use for onboarding and integration policy.
  4. An operational funding setup — your treasury, vault, or contract flow must support either signed pull offers or authorized push funding.

Most BF integrations use two logical addresses:

  • Bridge facilitator identity — your approved 3F identity, used for onboarding, authentication, and account-level policy.
  • Operational funding address — the address that actually provides capital, receives PT/YT, and performs operational on-chain actions.

A simple setup can use the same address for both roles. More advanced setups often separate them so a multisig, vault, or contract-controlled address handles capital while another system manages day-to-day API access.

When to use separate addresses:

  • Multisig or custody-controlled capital — your funding address is a cold storage or multisig wallet; a hot wallet manages API operations
  • Contract-based vaults — your funding address is a smart contract that implements custom authorization or callback logic
  • Operational resilience — you want the ability to rotate operator credentials without changing the on-chain funding address

When a single address suffices:

  • Simplified operations — one address controls both identity and capital, reducing coordination overhead
  • Direct control — your operational model does not require separate custody or approval layers
Role Controlled by Purpose Typical actions
BF identity You Approved integration identity Onboarding, API key generation, signed admin/auth payloads
Operational funding address You Capital source and PT/YT holder Submit or fund offers, approve assets, call mint(), redeem PT/YT
3F authorizer 3F Reflects accepted push-funded offers on-chain authorizeMinting()
Facility puller 3F Draws bridge capital into the strategy and repays it pullFunds(), repay()
Request owner 3F Final lifecycle transitions setRepaid()

If you use a separate operational funding address, keep it stable. That is the address that must be ready to approve funds, receive PT/YT, and later redeem them after repayment.

This address can be set via POST /v1/facilitator/offer-address.

Step 1: Register Your Bridge Facilitator Identity

Section titled “Step 1: Register Your Bridge Facilitator Identity”

Contact 3F and share the address that should be approved as your bridge facilitator identity. 3F uses this identity to:

  • allow your organization to participate in bridge facilitator flows
  • issue API credentials
  • map a dedicated operational funding address if you use one
  • coordinate wallet rotation or emergency access changes

Once approved, decide whether your BF identity and operational funding address will be the same or separate.

Step 2: Choose Your Auth and Funding Model

Section titled “Step 2: Choose Your Auth and Funding Model”

Grunt supports two operator-facing authentication models:

  • EIP-712 signatures — used for onboarding and administrative auth. Signed offers and signed offer queries also use typed data.
  • API key authentication — used for day-to-day offer operations when you want a persistent credential in your systems.

Administrative actions use short-lived signatures. Offer operations can use signatures or API keys, depending on the flow you choose.

There are two ways your capital reaches a Request:

  • Signed pull offers — you sign an offer off-chain and the Request later pulls funds from your operational funding address if the offer is selected.
  • Authorized push funding — you authenticate with the API, 3F authorizes the accepted amount on-chain, and your operational funding address pushes funds by calling mint().

Use signed pull offers when you want the Request to consume a typed offer directly. Use authorized push funding when you want an explicit accept-then-mint operational step.

To generate an API key, sign a short-lived EIP-712 message and submit it through the API key generation flow.

This step maps to POST /v1/facilitator/generate-key. The path currently retains legacy naming, but this is the bridge facilitator API-key generation endpoint.

Typed-data shape:

{
"types": {
"EIP712Domain": [
{ "name": "name", "type": "string" },
{ "name": "version", "type": "string" },
{ "name": "chainId", "type": "uint256" }
],
"GenerateFacilitatorApiKey": [
{ "name": "facilitator", "type": "address" },
{ "name": "deadline", "type": "uint256" }
]
},
"primaryType": "GenerateFacilitatorApiKey",
"domain": {
"name": "grunt-api",
"version": "1",
"chainId": 1
},
"message": {
"facilitator": "0xYourBridgeFacilitatorIdentity",
"deadline": 1735689600
}
}

Key rules:

  • deadline is the authentication expiry for this specific request.
  • deadline must be Unix seconds, not milliseconds.
  • deadline should be short-lived to reduce replay risk.
  • deadline is distinct from offer expiration, which controls how long an offer remains valid for matching.

Store the resulting API key securely. Use it only for offer-management flows, and rotate it whenever your operating environment changes.

Use the auction listing flow to find active Request contracts that need bridge capital.

In most integrations, this discovery step maps to GET /v1/auction.

Operationally, there are now three useful discovery modes:

  • GET /v1/auction — the default lightweight view for polling active opportunities.
  • GET /v1/auction?scope=all — include terminal and historical auctions for reconciliation.
  • GET /v1/auction?domain=true — include request-domain metadata needed for signed pull offers.

When domain=true is present, each auction may include:

{
"requestId": "0xRequestContractAddress",
"eip712Domain": {
"name": "Request Name",
"version": "0.0.1",
"chainId": 1
}
}

This response is meant to bridge discovery and signing:

  • requestId is the Request contract you will ultimately bind the offer to.
  • eip712Domain.name, version, and chainId are the Request-domain values you should use in the EIP-712 payload.
  • the verifyingContract is still the Request address itself, so use requestId for that field.

In practice, many BF operators keep the default GET /v1/auction polling loop for monitoring and only request domain=true when an operator or signing service is preparing an actual signed pull offer.

The API field is a convenience, not a new source of truth. If you need to cross-check or troubleshoot a signature mismatch, query eip712Domain() on the Request contract directly and treat the contract result as authoritative.

When reviewing an auction, confirm:

  • the Request contract address
  • the asset you must fund
  • the total amount requested and the amount already filled
  • the funding deadline
  • the auction status

In practice, most BF operators keep two views:

  • Active auctions — opportunities that can still be funded
  • All auctions — historical and terminal requests for reconciliation or audit

Auction statuses describe the off-chain RFQ and settlement workflow around a Request. They are API-facing operational states, not Request contract states.

graph LR
    O[OPEN] --> SV[SOLVABLE]
    SV --> SIP[SOLVING_IN_PROGRESS]
    SIP --> DR[DEPOSIT_READY]
    SIP --> SV
    DR --> P[PROCESSING]
    P --> PS[PARTIAL_SOLVED]
    PS --> SV
    P --> SUC[SUCCEEDED]
    P -.-> F[FAILED]
    SUC --> R[REPAID]
    PS --> R
Status Meaning
OPEN Auction was created by the engine from staged request metadata and is waiting until its deposit deadline.
SOLVABLE Auction is ready for the solver to evaluate offers. Also used after insufficient liquidity, backoff, or recovery paths return work to the solver.
SOLVING_IN_PROGRESS Solver has claimed the auction and is selecting offers. This is a leased in-progress state.
DEPOSIT_READY Solver selected one or more offers and created accepted transaction rows. The relayer can now process them.
PROCESSING Relayer has claimed the auction and is broadcasting or monitoring accepted transactions.
PARTIAL_SOLVED Some execution completed, but the auction is not fully filled. The auction remains eligible for future solver passes.
SUCCEEDED Auction target is filled, or the reduced target reached zero. Remaining submitted offers are marked NOT_ACCEPTED.
FAILED Defined execution-complete failure state. Current production failure paths normally return the auction to PARTIAL_SOLVED so remaining work can be retried.
REPAID Monitor marks a SUCCEEDED or PARTIAL_SOLVED auction repaid once the linked Request has is_repaid = true.

Use this flow when you want Grunt to consume a typed offer directly from your operational funding address.

Both signed pull offers and authorized push offers start with an offer submission call, POST /v1/offer.

  1. Read the current minimum nonce for your funding address from the Request contract.
  2. Build an offer typed-data payload bound to that specific Request. In most API-led integrations, this means reading the auction row, taking requestId as the verifyingContract, and using GET /v1/auction?domain=true to populate the Request-domain name, version, and chainId.
  3. Sign the offer from the address or contract authorization path that controls the funding address.
  4. Approve the Request contract as the asset spender for the amount you are willing to fund before the funding deadline.
  5. If selected, the Request consumes the offer, pulls funds, and mints PT/YT to your funding address.

Typed-data shape:

{
"types": {
"EIP712Domain": [
{ "name": "name", "type": "string" },
{ "name": "version", "type": "string" },
{ "name": "chainId", "type": "uint256" },
{ "name": "verifyingContract", "type": "address" }
],
"Offer": [
{ "name": "maker", "type": "address" },
{ "name": "amount", "type": "uint256" },
{ "name": "expectedReturn", "type": "uint256" },
{ "name": "nonce", "type": "uint256" },
{ "name": "expiration", "type": "uint256" },
{ "name": "useCallback", "type": "bool" }
]
},
"primaryType": "Offer",
"domain": {
"name": "Request Name",
"version": "0.0.1",
"chainId": 1,
"verifyingContract": "0xRequestContractAddress"
},
"message": {
"maker": "0xYourOperationalFundingAddress",
"amount": "1000000000000",
"expectedReturn": "100000000000",
"nonce": 7,
"expiration": 1735689600,
"useCallback": false
}
}

Field semantics:

  • nonce — replay protection and cancellation state. An offer is valid only if its nonce exceeds the Request’s stored minimum nonce for your funding address.
  • expiration — Unix timestamp after which the offer is no longer valid for matching.
  • expectedReturn — expected yield, denominated in the same asset as the principal. For example, if you lend 1,000,000 USDC and require a 1% return, set expectedReturn to 10,000. Total repayment is amount + expectedReturn, or 1,010,000 in that example.
  • useCallback — when set to true, the Request calls onRequestConsumed() on your funding address before pulling funds. This enables contract-based funding addresses to move capital from internal vaults, set allowances, or execute preparatory logic atomically. Set to false for EOA-based funding where funds are already approved and available.
  • The Request-domain verifyingContract and chainId bind the signature to a single Request contract on a specific chain.

Use this flow when you want to submit an offer through the API and only move capital after 3F authorizes the accepted amount on-chain.

  1. Submit the offer using API authentication.
  2. Monitor status until the offer becomes AWAITING_MINT.
  3. From your operational funding address, approve the Request contract as the asset spender for the accepted amount.
  4. Call mint() to transfer funds into the Request and receive PT/YT.
  5. Keep the same operational funding address available for later redemption once the Request is repaid.

Illustrative API-managed offer shape:

{
"bridgeFacilitatorAddress": "0xYourBridgeFacilitatorIdentity",
"fundingAddress": "0xYourOperationalFundingAddress",
"amount": "1000000000000",
"expectedReturn": "100000000000",
"auctionId": "auction_01HXK...",
"chainId": 1
}

Once offers are live, your operating loop is straightforward:

  1. List or poll your offers by BF identity or funding address.
  2. Watch the offer status until it is accepted, awaiting mint, completed, or rejected.
  3. Update or cancel offers when your pricing or capacity changes.
  4. If a push-funded offer reaches AWAITING_MINT, fund it promptly from the operational funding address.
  5. After repayment is marked complete, redeem PT/YT from the same operational funding address that received them.

Offer listing maps to GET /v1/offer, and explicit offer withdrawal maps to POST /v1/offer/cancel.

graph LR
    S[SUBMITTED] --> A[ACCEPTED]
    S --> C[CANCELLED]
    S --> N[NOT_ACCEPTED]
    A -->|Push funding| M[AWAITING_MINT]
    M --> SUC[SUCCEEDED]
    A -->|Signed pull funding| SUC
    A --> F[FAILED]
    M --> F
Status Meaning
SUBMITTED Initial/default offer state. Only SUBMITTED and unexpired offers are selectable by the solver.
ACCEPTED Solver selected the offer and created an accepted transaction for it.
AWAITING_MINT Unsigned offer authorization confirmed on-chain; the system is waiting for the maker to mint.
CANCELLED Offer was canceled and is no longer selectable.
SUCCEEDED Offer execution completed successfully, either through signed offer consumption or maker mint after authorization.
FAILED Offer execution failed, timed out, or failed validation or broadcast handling.
NOT_ACCEPTED Offer remained submitted but was not selected before the auction closed, succeeded, or was repaid.

Updating and cancelling outstanding offers

Section titled “Updating and cancelling outstanding offers”
  • Soft update — submit a new offer with better pricing or terms to replace your existing offer in the off-chain auction. No gas required; the new offer simply supersedes the old one for matching purposes.
  • Soft cancel — withdraw your offer from consideration through the off-chain auction by submitting a cancellation request. No gas required, but valid only before the offer is selected.
  • Hard cancel — increment your on-chain minimum nonce by sending a transaction to the Request contract. This invalidates all prior signed offers with certainty. Requires gas but provides finality independent of off-chain state.

Use soft updates and cancels for active position management. Reserve hard cancels for emergency revocation or when you need cryptographic certainty that old offers cannot be consumed.

Off-chain acceptance is not final until funds arrive. If an accepted offer is not funded within the required window, 3F may fail the offer and select another bridge facilitator. Treat AWAITING_MINT as a prompt to fund, not as a guaranteed terminal state.

Failure Typical cause Practical fix
Signature is required A signed flow was submitted without the required EIP-712 signature. Provide a valid signature or switch to the API-key flow where supported.
Authentication signature expired deadline was already in the past when the request was verified. Regenerate the payload with a fresh short-lived deadline.
Invalid request or chain binding The typed data was signed for the wrong chain or Request contract. Rebuild the payload from the correct Request domain and sign again.
Signature verification failed The domain, message, or signer did not match what the system expected. Recreate the canonical payload and re-sign from the correct authorization path.
Funding address mismatch Your API credentials and operational funding address did not resolve to the same BF configuration. Use the correct credentials or update the mapped operational funding address with 3F.
Funding transaction failed Balance or allowance was insufficient when the Request attempted to transfer funds. Recheck asset balance, approval, and the exact amount being funded.

The following illustrates a complete bridge facilitator cycle for a structured asset deployment.


Step Action Detail
1 Strategy opens deposits 7-day window for a USDC-denominated money market fund (e.g., Bitwise)
2 Deposits close, auctions begin Leverage points are auctioned; you discover opportunities via GET /v1/auction
3 You evaluate an opportunity Request seeks 1,000,000 USDC at 10% annualized return
Step Action Detail
4 You submit an offer POST /v1/offer with amount: 1000000, expectedReturn: 10000 (~1% yield for the term; total repayment target 1,010,000)
5 Offer accepted Status transitions to AWAITING_MINT for push funding, or proceeds through signed pull funding after acceptance
6 Funds transferred Request calls pullFunds() or you call mint(); PT/YT minted to your funding address
Step Action Detail
7 Strategy deploys capital Funds deployed into target asset; begins accruing yield
8 Settlement completes Timing depends on NAV cycle: standard delay if settled within deposit window, +1 day if after
9 Repayment marked Request owner calls setRepaid() with principal + yield
10 You redeem PT/YT Burn tokens to recover 1,010,000 USDC (principal + return)

If settlement exceeds the expected delay, repayment includes additional yield proportional to the extra time held. Your return reflects actual duration, preserving bridge facilitation economics.


For on-chain sequencing and role-level settlement detail, continue to Settlement. For PT/YT payoff mechanics, see PT/YT Tokens.