For the complete documentation index, see llms.txt. This page is also available as Markdown.

Builder Integration

A Wealth builder is an address that receives a fee from a user's vault redemption or RFQ sell. Any address can be a builder. There is no registration and no minimum balance. This page is not about ValueChain block production.

Branch on fundType before attaching a builder fee.

fundType

Where it trades

Builder fee

escrow_vault

Vault contracts and TigrisRFQ on ValueChain

This page. The fee is one packed bytes32 on the ValueChain call.

clob

The orderbook

Builder Codes in Trading. The fee is an order field.

A clob fund is a SoDEX RWA token, such as vQQQ or vVOO. Its buys and sells are orderbook trades. Use the trading builder flow for those fills, and do not pack builderFeeData onto a CLOB order. The rest of this page is the escrow_vault path.

The user sends the transaction from their own wallet and pays ValueChain gas in SOSO. approveBuilder takes no signature. requestDepositOnBehalf, requestRedeemOnBehalf, buyOnBehalf, and sellOnBehalf have no builderFeeData argument, so the Transaction Relayer cannot attach a fee.

Deposits and RFQ buys can update the user's cap for that builder. They do not transfer a token. The fee is taken from the user's proceeds when a redeem or sell claim succeeds, in vault.getAsset(), and sent to the builder. The builder does not submit a second claim.

Fee units and limits

feeRate and maxFeeRate are integers. Precision is 100_000. The protocol maximum is 2000.

fee = amount * rate / 100_000
rate
Basis points
Percent of proceeds

10

1 bp

0.01%

100

10 bp

0.1%

500

50 bp

0.5%

2000

200 bp

2%

The charged fee is rounded down. The user receives amount - fee. A fee that rounds down to 0 stays with the user, and PostChargeBuilderFee is not emitted.

approveBuilder accepts maxFeeRate from 1 to 2000. The rate stored on a redeem or sell is min(feeRate, cap).

Integration flow

  1. Choose the builder address that receives the fee.

  2. Load the vault from Fund Information for an escrow_vault fundId. Per-fund addresses change with each deployment.

  3. The user sets a cap with approveBuilder, or passes a non-zero maxFeeRate in builderFeeData on the same transaction.

  4. Read getMaxFeeRate(user, builder). A result of 0 means this builder is not approved. An unapproved builder with maxFeeRate of 0 still lets the redeem or sell succeed, stores nothing, and charges nothing.

  5. Encode builderFeeData. Encode referral separately (Referrals); use bytes32(0) when no code applies. The user submits requestRedeem or RFQ sell.

  6. Index RedeemBuilderFeeDataLocked or SellBuilderFeeDataLocked on that receipt. The stored rate cannot be read back later. On claim, match PostChargeBuilderFee to that request.

Contracts

ValueChain mainnet, chain id 286623. RPC https://mainnet.valuechain.xyz.

Contract
Address

BuilderRegistry

0x638c784760FD1eE465F1bDaF721268ACA24D5A22

TigrisRFQ

0x4Aa5E39956aCADd68bE0B6720a030Be3C14a6239

Load each vault from Wealth Contracts. The vault must be registered with VaultFactory before it can call approveBuilderFor. Mainnet TigrisRFQ holds APPROVE_BUILDER_ROLE.

Packing builderFeeData

Callers pass one bytes32:

Bits
Field
Meaning

0–159

builder

Address that receives the fee

160–175

feeRate

Rate requested on this redeem or sell

176–191

maxFeeRate

Cap written to BuilderRegistry when non-zero

maxFeeRate of 0 leaves the existing cap unchanged. A non-zero max replaces that user's cap for this builder, including when the new cap is lower. To charge one order without changing the cap, leave max at 0 and call approveBuilder first.

feeRate is ignored on deposit, so a deposit only registers the cap. On redeem or sell the applied rate is min(feeRate, the cap just read). The cap write runs first, so a non-zero maxFeeRate in this call is the cap used here. Stored builder fee data keeps the builder and that applied rate, and drops maxFeeRate. Decode a stored value with decodeBuilderFee: the low 160 bits are the address, and shifting right by 160 then masking with 0xffff yields the rate.

A zero builder or an applied rate of 0 stores nothing. RedeemBuilderFeeDataLocked or SellBuilderFeeDataLocked is not emitted.

Approval

Each user has a cap per builder. getMaxFeeRate(user, builder) == 0 means the builder is not approved.

approveBuilder reverts with ErrInvalidFeeRate when maxFeeRate is outside 1 to 2000. Later redeems and sells can charge up to this cap without sending a new max.

A separate approval transaction is optional. A non-zero maxFeeRate in builderFeeData makes the vault or TigrisRFQ call approveBuilderFor in the same transaction the user sends. The cap that is checked is the caller's.

revokeBuilder(builder) applies to later requests. A redeem or sell that already stored builder fee data still charges that rate.

An unapproved builder with maxFeeRate of 0 does not revert. The call succeeds, the stored fee is bytes32(0), no lock event is emitted, and the claim pays the full amount. A zero builder with maxFeeRate of 0, or a feeRate of 0, ends the same way.

maxFeeRate above 2000 reverts the whole call with ErrInvalidFeeRate, including a deposit. A zero builder with a non-zero maxFeeRate reverts with ErrInvalidAddress.

When the fee is charged

Call
Behavior

requestDeposit, RFQ buy

May update the approval. No token is charged and no builder fee data is stored.

requestRedeem, RFQ sell

Stores the builder and min(feeRate, cap) after this call's cap update.

claimRedeem, claimDelayedRedeem

Charges the stored rate on that slice of assets.

claimSell, claimDelayedSell

Charges only the filled underlying proceeds.

Unfilled refunds, rejected sells, buy claims

No fee.

The immediate and delayed amounts are each charged at the rate stored for that request. revokeBuilder after the request does not change it.

Redeem, sell, and claim

requestDeposit and requestRedeem take builderFeeData and then referral. Pass bytes32(0) for either value when it does not apply. Direct calls from the user's wallet are on Direct Deposits and Direct Redemptions.

Install viem and tsx. Set PRIVATE_KEY, VAULT_ADDRESS, RECEIVER, and RAW_AMOUNT (share base units). Set BUILDER to the fee recipient and FEE_RATE to the rate for this redeem. Set MAX_FEE_RATE to register a cap in this transaction; omit it to keep the existing cap. Set REFERRAL to the bytes32 from Referrals, or omit it.

Save as request-redeem-builder.mts and run npx tsx request-redeem-builder.mts.

To set the cap in its own transaction, send approveBuilder with the same wallet client before a redeem that leaves maxFeeRate at 0:

A deposit uses the same builderFeeData position and does not charge a token:

Claim calls do not take builderFeeData. The fee was stored on the request. Settlement timing is on Settlement and Claims.

TigrisRFQ

The quote signature covers payAmount and receiveAmount only. The user attaches builderFeeData beside it. buy and sell return no id. Read orderId from BuySubmitted or SellSubmitted on the receipt. On SellSubmitted, vault, owner, and orderId are indexed. SellBuilderFeeDataLocked is on the same receipt when the stored fee is non-zero.

A buy can carry builderFeeData to update the approval. Claiming a buy does not charge a builder fee. After a sell fills, the user receives the quoted underlying minus the builder fee. A partial fill charges only the filled underlying. A rejected sell charges nothing.

Accounting

RedeemBuilderFeeDataLocked is emitted by the vault proxy. SellBuilderFeeDataLocked, and PostChargeBuilderFee on a sell claim, are emitted by the TigrisRFQ proxy. PostChargeBuilderFee on a vault claim is emitted by the vault proxy.

PostChargeBuilderFee.builder, owner, and token are not indexed. Filter logs by the proxy address, then match in the client. token is the vault underlying, vault.getAsset(). On a vault claim, owner is the redeem receiver. On a sell claim, owner is the order owner. RequestedRedeem.caller is the share owner whose cap was checked.

ClaimedRedeem, ClaimedDelayedRedeem, Claimed, and ClaimedDelayed report the amount before the fee. The user receives that amount minus PostChargeBuilderFee.amount.

  1. Read RedeemBuilderFeeDataLocked or SellBuilderFeeDataLocked from the request receipt. Match it to RequestedRedeem or SellSubmitted by requestId or orderId. No lock event means the claim pays the full amount from the claim event.

  2. Decode lockedFeeData. Estimate claimableAssets * feeRate / 100_000 for that slice. Use the claimable proceeds, not the approval cap.

  3. On the claim receipt, subtract PostChargeBuilderFee.amount from the claim-event amount. A fee that rounds down to zero has no PostChargeBuilderFee, and the user receives the full claim-event amount.

  4. A positive token balance on the builder address still includes transfers other than builder fees. Treat a credit as builder income when a PostChargeBuilderFee log names that builder and token.

Troubleshooting

Symptom
Check

Call succeeds and the builder receives nothing

The builder was not approved and maxFeeRate was 0, feeRate was 0, or the fee rounded down to 0. No lock event means nothing was stored.

User receives less than the claim event amount

ClaimedRedeem, ClaimedDelayedRedeem, Claimed, and ClaimedDelayed report the pre-fee amount. Subtract PostChargeBuilderFee.amount.

The whole transaction reverts, including a deposit

maxFeeRate above 2000 (ErrInvalidFeeRate), or a zero builder with a non-zero maxFeeRate (ErrInvalidAddress). The vault must be registered with VaultFactory.

Charged rate is below the rate in builderFeeData

The stored rate is min(feeRate, cap). A non-zero maxFeeRate in this call replaces the previous cap, including when it is lower.

Relayer or OnBehalf call cannot attach a fee

Those methods have no builderFeeData argument. approveBuilder has no signature; the user sends it.

Builder Codes in TradingWealth ContractsReferralsDirect RedemptionsSettlement and Claims

Last updated