Bot sessions (DelegationHub)
DelegationHub is an onchain session registry for opt-in bot delegation.
A session is:
user(the identity being represented)delegate(the bot / executor)perms(permission bitmask)expiry(unix seconds)
Sessions are verified by protocol contracts onchain via isAuthorized(...).
Contract + wiring
Source:
src/DelegationHub.solsrc/interfaces/IDelegationHub.solsrc/lib/DelegationPermissions.sol(canonical bit positions)
Wiring (v1.0.0+):
- MineCore stores
delegationHuband uses it for delegated takeovers + reign routing. - Furnace stores
delegationHuband uses it for delegated entry (...Forfunctions). - ClaimAllHelper verifies sessions via
MineCore.delegationHub(). - VeClaimNFT verifies sessions via
Furnace.delegationHub()for safe lock maintenance wrappers. - ShareholderRoyalties verifies sessions via
Furnace.delegationHub()for safe config wrappers. - LpStakingVault7D verifies sessions via
Furnace.delegationHub()for safe config wrappers. - MarketRouter does not use DelegationHub in v1.0.0 (no Market permission bits; market actions are intentionally not delegated; use standard approvals).
Freeze rule: set the hub address before config freeze.
Read surface
| Method | Notes |
|---|---|
getSession(user, delegate) -> (perms, expiry) | Raw stored session data |
isAuthorized(user, delegate, requiredPerms) -> bool | Checks expiry and bitmask |
nonces(user) -> uint256 | EIP-712 nonce for gasless set-by-sig |
Write surface
| Method | Caller | Notes |
|---|---|---|
setSession(delegate, perms, expiry) | user | Stores session; overwrites prior |
revokeSession(delegate) | user | Clears perms + expiry |
setSessionBySig(user, delegate, perms, expiry, nonce, deadline, sig) | anyone | Gasless; validates signature + nonce + deadline |
Signature model:
- EIP-712 domain:
- name:
ClaimRush DelegationHub - version:
1
- name:
- Uses OpenZeppelin
SignatureChecker(supports EOAs + EIP-1271 smart wallets).
Consumers in protocol contracts
MineCore: delegated takeover
MineCore.takeoverFor(newKing):
msg.senderpays ETH and becomes the executornewKingbecomes the King identity- requires
DelegationHub.isAuthorized(newKing, msg.sender, P_TAKEOVER_FOR)
Default routing for the new reign:
ethRecipient = msg.sender(bot)claimRecipient = newKing(user)
Optional:
- if the session also grants
P_ROUTE_REIGN_CLAIM_TO_CALLER, MineCore sets:claimRecipient = msg.sender
Mid-reign routing update:
MineCore.setCurrentReignRecipients(ethRecipient, claimRecipient)- callable by the active king identity
- or an authorized delegate with:
- for
ethRecipient:P_SET_REIGN_ETH_RECIPIENTorP_SET_REIGN_ETH_RECIPIENT_TO_CALLER_ONLY - for
claimRecipient:P_SET_REIGN_CLAIM_RECIPIENTorP_SET_REIGN_CLAIM_RECIPIENT_TO_USER_ONLY
- for
ClaimAllHelper: delegated harvest + withdraw
ClaimAllHelper provides delegation-gated wrappers:
claimShareholderForUser(user, ...)requiresP_CLAIM_SHAREHOLDER_FORwithdrawKingBalanceForUser(user)requiresP_WITHDRAW_KING_BUCKET_FORclaimAllFor(user, ...)requiresP_CLAIM_ALL_FOR
Furnace: delegated entry (bot pays, user receives lock)
Delegated entry points:
enterWithEthFor(user, ...)requiresP_FURNACE_ENTER_ETH_FORenterWithClaimFromCallerFor(user, ...)requiresP_FURNACE_ENTER_CLAIM_FORenterWithTokenFromCallerFor(user, ...)requiresP_FURNACE_ENTER_TOKEN_FOR
VeClaimNFT: delegated lock maintenance (safe, non-custodial)
Delegated lock maintenance entry points:
extendLockForUser(user, tokenId, additionalDuration)requiresP_VE_EXTEND_LOCK_FORmergeLocksForUser(user, fromTokenId, intoTokenId)requiresP_VE_MERGE_LOCKS_FORunlockExpiredForUser(user, tokenId)requiresP_VE_UNLOCK_EXPIRED_FOR
Non-custodial rules enforced:
- no ERC20 spend from the user
- unlock returns CLAIM to
user(never to the delegate) - merge/extend require the lock(s) are owned by
user
Settings/config: delegated config setters (safe, non-custodial)
Delegated config entry points:
MineCore.setKingAutoLockConfigForUser(user, ...)requiresP_SET_KING_AUTO_LOCK_CONFIG_FORShareholderRoyalties.setAutoCompoundConfigForUser(user, ...)requiresP_SET_SHAREHOLDER_AUTOCOMPOUND_CONFIG_FORLpStakingVault7D.setAutoCompoundConfigForUser(user, ...)requiresP_SET_LP_AUTOCOMPOUND_CONFIG_FOR
Non-custodial rules enforced:
- config-only (no value transfer)
- tokenId parameters must be veNFTs owned by
user
Permissions map
Canonical bits are defined in src/lib/DelegationPermissions.sol.
| Group | Bit | Permission | Enables |
|---|---|---|---|
| Crown | 0 | P_TAKEOVER_FOR | MineCore.takeoverFor(newKing) |
| Crown | 1 | P_ROUTE_REIGN_CLAIM_TO_CALLER | Route King-stream mined CLAIM to bot during delegated takeover |
| Crown | 2 | P_SET_REIGN_ETH_RECIPIENT | Allow delegate to set ethRecipient mid-reign to any address |
| Crown | 3 | P_SET_REIGN_ETH_RECIPIENT_TO_CALLER_ONLY | Allow delegate to set ethRecipient = msg.sender mid-reign |
| Crown | 4 | P_SET_REIGN_CLAIM_RECIPIENT | Allow delegate to set claimRecipient mid-reign to any address |
| Crown | 5 | P_SET_REIGN_CLAIM_RECIPIENT_TO_USER_ONLY | Allow delegate to set claimRecipient = king mid-reign |
| Harvest | 6 | P_WITHDRAW_KING_BUCKET_FOR | ClaimAllHelper.withdrawKingBalanceForUser(user) |
| Harvest | 7 | P_CLAIM_SHAREHOLDER_FOR | ClaimAllHelper.claimShareholderForUser(user, ...) |
| Harvest | 8 | P_CLAIM_ALL_FOR | ClaimAllHelper.claimAllFor(user, ...) |
| Furnace | 9 | P_FURNACE_ENTER_ETH_FOR | Furnace.enterWithEthFor(user, ...) |
| Furnace | 10 | P_FURNACE_ENTER_CLAIM_FOR | Furnace.enterWithClaimFromCallerFor(user, ...) |
| Furnace | 11 | P_FURNACE_ENTER_TOKEN_FOR | Furnace.enterWithTokenFromCallerFor(user, ...) |
| VeLock | 12 | P_VE_EXTEND_LOCK_FOR | VeClaimNFT.extendLockForUser(user, tokenId, ...) |
| VeLock | 13 | P_VE_MERGE_LOCKS_FOR | VeClaimNFT.mergeLocksForUser(user, fromTokenId, intoTokenId) |
| VeLock | 14 | P_VE_UNLOCK_EXPIRED_FOR | VeClaimNFT.unlockExpiredForUser(user, tokenId) |
| Config | 15 | P_SET_KING_AUTO_LOCK_CONFIG_FOR | MineCore.setKingAutoLockConfigForUser(user, ...) |
| Config | 16 | P_SET_SHAREHOLDER_AUTOCOMPOUND_CONFIG_FOR | ShareholderRoyalties.setAutoCompoundConfigForUser(user, ...) |
| Config | 17 | P_SET_LP_AUTOCOMPOUND_CONFIG_FOR | LpStakingVault7D.setAutoCompoundConfigForUser(user, ...) |
Integration guidance
- Keep sessions short-lived (hours/days), refresh as needed.
- Use minimal perms for the bot’s job.
- Treat recipient routing perms as high risk:
P_SET_REIGN_ETH_RECIPIENTcan redirect the dethroned-King ETH payout for the active reign.P_SET_REIGN_CLAIM_RECIPIENTcan redirect mined CLAIM mid-reign.- Prefer constrained variants (
...TO_CALLER_ONLY,...TO_USER_ONLY) where possible.
- Treat delegation as security-sensitive UX (like approvals):
- show the delegate address
- show expiry
- show selected permissions
- provide a one-click revoke
Agent SDK
This repo’s TypeScript SDK (agents/sdk/) includes helpers for DelegationHub:
- canonical permission bits (
agents/sdk/src/delegation/permissions.ts) - EIP-712 typed data builder (
buildSetSessionTypedData) - sign + submit
setSessionBySig(signSetSession,submitSetSessionBySig)
Examples:
# Local demo (user signs with derived actor0; delegate submits with derived actor1)
RPC_URL=http://127.0.0.1:8545 npm -C agents/sdk run example:delegation
# Production-style: user wallet signs typed data; delegate submits signature
RPC_URL=http://127.0.0.1:8545 \
npm -C agents/sdk run example:session -- --cmd build \
--user 0xUserAddress \
--delegate 0xDelegateAddress \
--perms TAKEOVER_FOR,CLAIM_ALL_FOR \
--out /tmp/session.json \
--pretty
# user signs /tmp/session.json via eth_signTypedData_v4
RPC_URL=http://127.0.0.1:8545 PRIVATE_KEYS=0x<delegatePrivateKey> \
npm -C agents/sdk run example:session -- --cmd submit --typed-data /tmp/session.json --sig 0x...
# Revoke (gasless)
RPC_URL=http://127.0.0.1:8545 \
npm -C agents/sdk run example:session -- --cmd build --revoke --user 0xUserAddress --delegate 0xDelegateAddress --out /tmp/revoke.json --pretty
RPC_URL=http://127.0.0.1:8545 PRIVATE_KEYS=0x<delegatePrivateKey> \
npm -C agents/sdk run example:session -- --cmd submit --typed-data /tmp/revoke.json --sig 0x...
# Run a delegated agent loop
RPC_URL=http://127.0.0.1:8545 npm -C agents/sdk run example:agent -- --actor-index 1 --acting-for 0xUserAddress --once
# Delegated safe maintenance (ve upkeep + optional config sync)
RPC_URL=http://127.0.0.1:8545 \
ENABLE_SAFE_MAINTENANCE=1 \
VE_EXTEND_IF_REMAINING_DAYS=7 \
VE_EXTEND_BY_DAYS=30 \
npm -C agents/sdk run example:agent -- --actor-index 1 --acting-for 0xUserAddress --once
Optional config sync env vars:
- `KING_AUTO_LOCK_*` (MineCore king auto-lock config)
- `ROYALTIES_AUTOCOMPOUND_*` (ShareholderRoyalties auto-compound config)
- `LP_AUTOCOMPOUND_*` (LP vault auto-compound config)Observability and indexing
DelegationHub already emits SessionSet(...) events when a user grants, updates, or revokes a session.
In addition, the protocol emits Events.DelegationSessionUsed(...) for delegated entrypoints that have a canonical actionType id (takeover, claim, furnace enter, lock maintenance, settings/config):
actionTypeis one of:TAKEOVER,CLAIM,FURNACE_ENTER,VE_LOCK,CONFIG(seesrc/lib/DelegationActionTypes.sol)permsUsedis the permission bitmask actually consumed for the actionrefIdis a small reference for auditors/indexers:- takeover:
reignId - furnace enter:
tokenId - ve lock maintenance:
tokenId(merge usesintoTokenId/ destination lock) - config setters:
tokenIdwhen applicable, otherwise0 - claim helpers:
0
- takeover:
The subgraph in this repo indexes these into:
DelegationSession(rolling session state, including last used + last action)DelegationSessionUse(immutable activity feed with tx hashes)DelegationSessionSetEvent(immutable session change feed with tx hashes)
These entities power:
- Security → Bot access (sessions list + activity feed + one-click revoke)
- Radar Inbox alerts (bot session used, session granted/updated/revoked, recipients changed mid-reign)