BUILDER_FEE. Revenue-share credits use kind REVENUE_SHARE.
Attribution
Attribution follows the application that submitted the taker order. The matching engine stamps each resulting trade with that authenticated application’s id and looks up its current BuilderCodes enrollment when the fill settles.- A maker application’s enrollment does not earn BuilderCodes revenue for that fill.
- An unenrolled taker application, or one without a usable payout destination, receives no BuilderCodes credit.
- Liquidation slices do not charge or credit the ordinary builder fee. Fills with no positive Monaco protocol take receive no revenue-share credit.
- An enrolled application’s ordinary builder fee is a separate revenue stream.
It is excluded from the revenue waterfall and credited exactly to the payout
wallet enrolled when the taker command is sequenced. Other application-fee
paths, including copy-trading fees, do not create
BUILDER_FEEpayouts.
Revenue waterfall
The waterfall runs independently for every fill, using deterministic integer math in the quote token’s RAW atomic units. For one fill, letgross be Monaco’s non-negative protocol take:
gross; a
positive maker fee increases it. The application’s additional taker fee is not
part of gross.
The sequenced deduction order is:
- Monaco floor. Reserve
floor(gross × monaco_floor_bps / 10000)for the treasury, capped atgross. This is a retention guard, not another transfer. - BuilderCodes. While the revenue share is on, target
floor(gross × buildercodes_budget_bps / 10000), capped at the amount left after the Monaco floor. While it is off, this step takes nothing. The realized rate can therefore be lower than the configured budget when the floor leaves too little; each payout row reports that realized whole-bps value asappliedBps. - Gas reserve. Convert
gas_reserve_fixedfrom a decimal quote-token amount to RAW units, then take at most the amount still available. A small fill can pay this only partially. - TraderCodes. Use the remaining amount as the base for PitPass TraderCodes rates. Any part not paid as a TraderCodes reward remains with the treasury.
Rollout configuration
These are the compiled defaults when a feature-flag document omits the keys:
An active sequenced treasury account is also required for revenue-share and gas
payouts. Ordinary builder-fee credits do not depend on this account. On initial
warmup, an unset or unresolved
treasury_multisig_address leaves no treasury
account and therefore produces no revenue-share or gas payout. After an account
has been installed, a configured-but-unresolved refresh retains that last
identity, whereas explicitly removing the address through the admin treasury
endpoint stops Monaco protocol-take attribution once the matching engine applies
the removal. To shut down BuilderCodes revenue sharing, turn off
buildercodes_enabled rather than removing the treasury address. Configuration
and account changes are sequenced into matching-engine state so verification
replay uses the same values as live settlement.
When buildercodes_enabled is turned off, the revenue-share credit stops before
any balance mutation or audit record is emitted, and that amount stays in the
TraderCodes base. The Monaco floor and the gas reserve still apply. Ordinary
builder-fee credits continue whenever builder_fee_enabled and enrollment make
the fee chargeable. Existing BuilderCodes balances and payout history are kept,
and frontend reads and claims keep working.
Frontend endpoints
All seven endpoints require an authenticated, non-delegated wallet session. No program switch gates reads or claims. Setting the builder fee needs the revenue share or builder fees switched on; while both are off it is refused with403
on REST and PERMISSION_DENIED on gRPC.
Program config, builder-fee controls and change history, payout history, and
summary are application-scoped. The application comes from the session, and
the authenticated wallet must match its current payout address. The request
cannot select another application or wallet.
The stored builder fee may be set while builder fees are disabled, but a real
change must not exceed the live cap (never above 100 bps). A frontend may make two real
changes in a rolling 24 hours; administrator changes do not count. Setting the
current value is a successful no-op that consumes no change, even when the
allowance is exhausted or the live cap has fallen below that stored value.
A newly stored fee reaches order requests within seconds. Sequencer ordering is
authoritative for the builder-fee gate and enrollment: an order processed before
one of those sequenced changes uses the earlier state, and a later order uses
the new state. The charged fee is capped at the live cap and is zero while
builder fees are disabled or payout enrollment is incomplete. Builder fees are
charged on taker fills only; the remainder of an order that rests on the book
pays no builder fee when it later fills as maker. Each positive charged fee is
credited to the application’s payout balance as a
BUILDER_FEE payout. The change log is
newest first and includes both frontend wallet changes and administrator
changes; an administrator row exposes the operator’s email in actor.
setBuilderFee requires an explicit bps (0 clears it). Invalid or
above-cap changes return 400 / INVALID_ARGUMENT. A missing or nonmatching
payout address fails the ownership gate first with 403 /
PERMISSION_DENIED; incomplete enrollment reached after ownership passes,
or an exhausted rolling allowance, returns 409 / FAILED_PRECONDITION.
The change-limit response carries details.reason: "builder_fee_change_limit"
and details.nextChangeAllowedAt; gRPC includes the next allowed time in its
message. Change history is cursor-paginated with pageSize 1–100 (default 20)
and pageToken; an empty nextPageToken ends the walk, and invalid
pagination returns 400 / INVALID_ARGUMENT.
Both operations require the session application to be a frontend. Missing authentication returns 401 /
UNAUTHENTICATED; a failed ownership check, or setting the fee while both fee switches are off, returns 403 /
PERMISSION_DENIED; a missing application returns 404 / NOT_FOUND;
internal failures return 500 / INTERNAL. Change-history reads draw the
account’s read budget (429 / RESOURCE_EXHAUSTED when enforced).
Maintenance can refuse the write before it runs with 503 /
UNAVAILABLE.
The no-op exception also applies when interpreting the generated REST
reference’s live-cap descriptions: repeating the stored value is accepted
even when that value exceeds a subsequently lowered cap.
Reward balance and claim are wallet-scoped. The server derives the reserved
bucket from the authenticated wallet, and current enrollment is not required.
This distinction lets an old payout wallet inspect and claim its retained
balance after an address rotation, even though it can no longer read the
application’s current config or payout history.
Multisig payout wallets
A payout address that is a smart-contract wallet — a Safe or another multisig — authenticates with a contract signature (EIP-1271) rather than apersonal_sign from a private key. The login flow is otherwise the sequence
every wallet follows: request a challenge, have the wallet authorize the
returned message, and submit the result to POST /api/v1/auth/verify. Monaco
asks the wallet contract’s isValidSignature whether it accepts the challenge
and issues a session when the contract says yes.
Because collecting a threshold of owner approvals is a coordination round
between people rather than a single click, challenges stay valid for 30 minutes
instead of the usual 5 whenever contract-wallet login is enabled. The longer
window applies to every address, not only eligible ones: /auth/challenge
is unauthenticated and returns expiresAt, so an eligibility-specific lifetime
would let anyone probe which addresses hold a payout bucket by comparing
lifetimes. The nonce is still single-use and the signed message still authorizes
one specific session public key, so the longer window widens only how long the
wallet has to answer, never what the challenge can authorize.
Contract-wallet login is scoped to addresses that hold a reserved payout
bucket, which is what makes a multisig able to claim and withdraw what it
earned. It is not general smart-contract-wallet support for trading accounts.
Four conditions must all hold, and any of them failing leaves the address on the
ordinary signature path:
- the deployment has a chain RPC endpoint configured (
SEI_RPC_URL). Without it the contract-wallet path is never constructed, so the flag below has nothing to switch on; contract_wallet_auth_enabledis on;- the address holds a reserved BuilderCodes payout bucket — including one retained after a payout-address rotation;
- the address has contract code deployed on chain.
The challenge itself looks the same either way — the server issues one message
and decides how to verify it at
/auth/verify — so which path a wallet took
is visible in the verification outcome, not in the challenge. A wallet that is
silently falling back will fail verification rather than receive a different
challenge.contract_wallet_auth_enabled defaults to false. While it is off, every
login is verified by key recovery and a multisig payout wallet cannot
authenticate.
Enrollment still accepts any nonzero address, so a payout address pointed at a
contract wallet keeps accruing its share in the meantime — that balance is
retained, not lost, and becomes claimable once the conditions above are met.RAW-unit fields
BuilderCodes payout amounts and balances use RAW atomic units, represented as base-10 integer strings. Convert them with the token’s configured decimals:1000000 is 1 token when that token has six decimals. Do
not add amounts for different tokens.
RAW-unit fields include:
- payout history
amount - summary
byToken[].totalAmountandbyTokenAndKind[].totalAmount - reward balance
available - claim request
amount - claim response
payoutBalanceandtradingBalance
gas_reserve_fixed is the exception: it is a decimal amount in ordinary quote
units. The matching engine scales it to the traded pair’s quote-token decimals
and truncates sub-atomic precision.
Payout-address rotation
Changing a payout address affects future credits after the new enrollment view is sequenced. It does not move or merge balances already earned:- future fills accrue to the new wallet’s reserved bucket;
- earlier credits remain in the old wallet’s bucket;
- the old wallet can still call reward balance and claim;
- current application config, history, and summary belong to the new payout wallet because those reads require the current address.
Claim and withdraw
Read the wallet’s reserved balances, then claim a positive RAW amount for one token:409 / gRPC
FAILED_PRECONDITION; it is never partially filled. A missing or empty bucket
uses the same response because both mean there is no balance to move.
Unknown outcomes: do not retry blindly
An admitted claim can still be queued when a balance read runs. Wait until the matching engine is healthy and reconcile the payout and trading balances before issuing another claim. The TypeScript SDK marks ambiguous claim failuresretryable: false so generic retry middleware does not claim twice.
Two tagged 503 / UNAVAILABLE cases are decided before sequencer admission
and are safe to retry with backoff:
- maintenance refusal: REST
error: "OPERATIONS_BLOCKED"; gRPC metadatax-maintenance-blocked: true; - sequencer overload: REST
code: "OVERLOADED"; the same code appears in the gRPC status details.
429 / gRPC RESOURCE_EXHAUSTED
before it reaches the sequencer: nothing committed, so retry after the
interval in Retry-After.
