ClaimAllHelper (bundle + delegated Collect)
ClaimAllHelper is a stateless orchestration helper that bundles common Collect actions (Barons ETH + King ETH fallback) into a single transaction and provides delegation-gated wrappers for bots. It does not change economics or custody funds. In the CLAIM stream, this is the bundling layer for collecting earned ETH in one call.
TL;DR:
claimAll(mode, targetTokenId, durationSeconds, createAutoMax, minVeOut)bundles Baron Collect + King-bucket withdraw for the caller.claimAllForUser(...)runs the same on behalf of an opted-in user via a DelegationHub session. The helper holds no funds and adds no new reward logic.
Public API
| Function | Caller | Purpose |
|---|---|---|
claimAll(mode, targetTokenId, durationSeconds, createAutoMax, minVeOut) | user | Bundle Baron Collect (mode=0/1) + best-effort King-bucket withdraw. |
claimAllForUser(user, mode, targetTokenId, durationSeconds, createAutoMax, minVeOut) | delegate | Same, gated by the relevant DelegationHub permission bits. |
withdrawKingBalanceForUser(user) | delegate | Pull King bucket only; needs P_WITHDRAW_KING_BUCKET_FOR. |
claimShareholderForUser(user, mode, ...) | delegate | Pull Baron ETH only (ETH lands on user); needs the corresponding Collect / Compound permission bits. |
claimShareholderToCallerForUser(user) | delegate | Pull Baron ETH for user and forward it to the caller (looping bot); needs P_CLAIM_SHAREHOLDER_FOR | P_ROUTE_SHAREHOLDER_ETH_TO_CALLER. |
Why a helper exists
- bundle common Collect actions into a single transaction
- provide delegation-gated wrappers for bots (via DelegationHub)
It does not:
- change economics
- custody funds
- introduce new reward logic
Source of truth: src/ClaimAllHelper.sol.
Helper-only entrypoints
Two protocol contracts expose helper-only entrypoints:
ShareholderRoyalties.claimShareholderFor(user, ...)isonlyClaimAllHelperMineCore.withdrawKingBalanceFor(user)isonlyClaimAllHelper
These entrypoints let an EOA (or bot) call ClaimAllHelper, while the helper calls into those contracts in a way that preserves protocol invariants.
Public surface
Bundle for the caller
claimAll(mode, targetTokenId, durationSeconds, createAutoMax, minVeOut)
What it does:
- Collects Barons ETH for the caller via
ShareholderRoyalties.claimShareholderFor(msg.sender, ...) - Withdraws the caller’s King ETH fallback bucket via
MineCore.withdrawKingBalanceFor(msg.sender)
Mode values match ShareholderRoyalties for the caller bundle only:
mode = 0ETH (Collect ETH)mode = 1LOCK_FURNACE (Collect & Lock)
The delegation-gated wrappers (claimShareholderForUser, claimAllFor) accept the same arity for ABI parity but enforce mode == 0 only. Any non-zero mode reverts NotAuthorized once the delegation gate clears, so a delegate can never spend the user’s shareholder ETH on a fresh veCLAIM lock through these paths.
Important behavior:
- The helper is nonpayable.
- If the Barons Collect leg reverts (example: locking paused in mode=1), the whole bundle reverts and the King bucket is not withdrawn.
- The King ETH withdraw leg is best-effort but selective, not a blanket try/catch. The helper invokes
MineCore.withdrawKingBalanceFor(user)via a low-level call and inspects the 4-byte revert selector:- Selector matches
Errors.EthTransferFailed(the recipient rejected ETH): the helper emitsEvents.KingWithdrawalFailed(user, reason)and the bundle still succeeds with the Barons payout. The first 128 bytes of revert data are preserved inreasonfor indexers. - Any other revert (
WiringMismatch,ZeroAddress,nonReentrantre-entry, an unrelated downstream revert, etc.): the helper rethrows the original revert verbatim and the whole bundle reverts — Barons payout is also rolled back by the EVM. - Zero King balance is a silent no-op inside MineCore (
kingEthBalance[user] == 0returns early without reverting), not a failure path.
- Selector matches
- The two legs are not symmetric: Barons failure is always fatal; King failure is best-effort only for the specific ETH-rejection case.
Tested-control note (recovery semantic):
- The
EthTransferFailed-only swallow + recoverable-balance shape is asserted bytestClaimAllPartialFailureLeavesKingBalanceRecoverable(caller bundle) andtestClaimAllForDelegatedPartialFailureLeavesKingBalanceRecoverable(delegated bundle). Both run a first call with the King ETH transfer rejected, assert the Barons leg lands and zero successful King withdrawals were recorded, then run a second call once the ETH transfer path is restored and assert the King leg lands for the original target. - The unconditional bubble-up of any non-
EthTransferFailedrevert is asserted bytestClaimAllBubblesUnexpectedMineCoreRevert_NoPartialClaims(caller bundle) andtestClaimAllForDelegatedBubblesUnexpectedMineCoreRevert_NoPartialClaims(delegated bundle). Both assert the Barons leg is rolled back when the King leg reverts with anything other thanErrors.EthTransferFailed.
Delegation-gated wrappers
These methods are for bots acting on behalf of user.
| Method | What it does | Required permission | Notes |
|---|---|---|---|
claimShareholderForUser(user, ...) | Barons Collect for user (ETH lands on user) | P_CLAIM_SHAREHOLDER_FOR | ETH-only (mode == 0); any non-zero mode reverts NotAuthorized. |
claimShareholderToCallerForUser(user) | Barons Collect for user, ETH forwarded to the caller | P_CLAIM_SHAREHOLDER_FOR | P_ROUTE_SHAREHOLDER_ETH_TO_CALLER | ETH-only by construction (no mode); routes to msg.sender (caller-only). |
withdrawKingBalanceForUser(user) | Withdraw King ETH bucket for user | P_WITHDRAW_KING_BUCKET_FOR | — |
claimAllFor(user, ...) | Bundle: Barons Collect + King bucket withdraw | P_CLAIM_ALL_FOR | ETH-only (mode == 0); any non-zero mode reverts NotAuthorized. |
All delegated wrappers reject self-calls: if (user == msg.sender) revert NotAuthorized(). A delegate cannot use these wrappers to act on themselves — call the non-delegated claimShareholder / withdrawKingBalance / claimAll (caller-bundle) paths instead.
claimShareholderToCallerForUser is the looping-bot rail: it Collects user’s Baron ETH and forwards it to the caller (msg.sender) in one call. The recipient is the caller only — there is no arbitrary-recipient variant, so a delegate can loop the payout to its own address but cannot redirect it to a third party. It requires the value-redirect bit P_ROUTE_SHAREHOLDER_ETH_TO_CALLER on top of P_CLAIM_SHAREHOLDER_FOR; a session holding only the Collect bit cannot move the ETH off the user.
Why ETH-only on delegated paths:
- A
DelegationHubpermission bit grants a bot the right to settle the user’s earned shareholder ETH; it does not grant the right to lock that ETH into a fresh veCLAIM position on the user’s behalf. Routing the payout into Furnace would convert a Collect into a position-creating operation the delegate was never authorized to perform. - LOCK_FURNACE Collect remains user-direct via
ShareholderRoyalties.claimShareholder(mode = 1, ...). The delegated rail covers ETH payouts only.
Authorization model:
- ClaimAllHelper has no standalone hub pointer. Delegated wrappers resolve authority from the live
MineCore/ShareholderRoyalties/ sharedFurnacebundle. - For every delegated helper path, the helper first requires:
MineCore.claimAllHelper() == address(this)MineCore.royalties() == royaltiesShareholderRoyalties.claimAllHelper() == address(this)ShareholderRoyalties.mineCore() == mineCoreMineCore.furnace() == ShareholderRoyalties.furnace()Furnace.mineCore() == mineCoreFurnace.shareholderRoyalties() == royaltiesFurnace.delegationHub() == MineCore.delegationHub()
withdrawKingBalanceForUser(...)fails closed on any MineCore/Furnace hub drift; it does not trust a rawMineCore.delegationHub()read in isolation.claimShareholderForUser(...),claimShareholderToCallerForUser(...), andclaimAllFor(...)use that same canonical-hub preflight, so split-brainMineCore.furnace()vsShareholderRoyalties.furnace()wiring is rejected before authorization.
On success, delegated wrappers emit:
Events.DelegationSessionUsed(user, delegate, actionTypeId, permsUsed, refId=0, timestamp)
actionTypeId values (v1.0.0):
- 10 =
CLAIM_SHAREHOLDER_FOR - 11 =
WITHDRAW_KING_BUCKET_FOR - 12 =
CLAIM_ALL_FOR
Integrator notes
Computing minVeOut (mode = LOCK_FURNACE)
ClaimAllHelper does not compute slippage bounds for you.
For Collect & Lock:
- Quote via FurnaceQuoter (resolve address from
furnace.furnaceQuoter()):furnaceQuoter.quoteEnterWithEth(user, ethIn, targetTokenId, durationSeconds, quoteCreateAutoMax)
- Convert quote to min-out:
minVeOut = floor(veOut * (10_000 - slippageBps) / 10_000)veOuthere covers only the newly locked amount at the lock’s remaining duration; entry into an existing lock does not change its duration.- If
veOut > 0but floor-rounding would produceminVeOut == 0, clamp to1before calling a Furnace entry path.
Use the same approach whether you call:
ShareholderRoyalties.claimShareholder(...)directly, or- ClaimAllHelper bundle methods.
Locking pause behavior
If Furnace.lockingPaused == true:
- any path that uses mode
LOCK_FURNACEwill revert - mode
ETH(Collect ETH) still works
UX recommendations
- Keep the UI verb Collect for ETH payouts.
- Only expose ClaimAllHelper if you actually need bundling or delegated wrappers.
- Many apps can call
claimShareholderandwithdrawKingBalanceseparately.
- Many apps can call
Operator notes
The remaining section covers deploy-time wiring. Integrators can skip unless instrumenting governance flows.
Wiring
ClaimAllHelper is deployed as its own contract.
It is constructed with immutable pointers to:
ShareholderRoyalties(Barons ETH)MineCore(King ETH fallback bucket)
Deployment hardening + runtime safety:
- both constructor addresses must be nonzero contract addresses
- every public path first verifies the canonical back-links it depends on:
MineCore.claimAllHelper() == address(this)MineCore.royalties() == royaltiesShareholderRoyalties.claimAllHelper() == address(this)ShareholderRoyalties.mineCore() == mineCore
- helper wiring changes use helper redeployment and updates to both core contracts via the timelocked owner
setClaimAllHelperis gated bywhenNotFrozenon both MineCore and ShareholderRoyalties — after the freeze-and-burn ceremony, the helper pointer is permanently locked and redeployment is no longer possible (Security page status: Redeploy → No)
Addresses come from deployments/<network>.json.
See also
- ShareholderRoyalties (Barons) — royalty distribution mechanics
- Bot Sessions (DelegationHub) — delegated collect permissions
- Furnace — lock entry for Collect & Lock mode
- Tutorial: Collect Barons ETH or Collect & Lock
- User manual: Overview