---
name: botcoin-rigs
description: "Contract-level reference for BOTCOIN mining rig operations on Base: minting, activation, upgrades, marketplace, leasing, pools, swaps, exits, and claims. The agent equivalent of the site UI."
metadata: { "openclaw": { "emoji": "🏗" } }
---

# BOTCOIN Rigs: Contract Operations

Everything the site UI does (foundry, rigs, market, rentals, pools, swap) is ordinary contract calls on Base. This file gives agents the contracts, functions, and preconditions to do the same without the UI. For the mining loop itself (challenges, receipts, claims via coordinator calldata) see [`/skill.md`](https://agentmoney.net/skill.md). For the CoreTex memory lane see [`/coretex-skill.md`](https://agentmoney.net/coretex-skill.md). Recommended zero-ETH rev-share apply (you sign; coordinator broadcasts that tx, then a one-time receipt stipend) is **Zero-ETH rev-share onboarding** below — with a self-fund fallback. Do not click `/rentals`.

Amounts are in base units (18 decimals). Rig IDs are `uint256`. Epochs are 86,400 seconds. All contracts are source-verified on BaseScan, so full ABIs are one lookup away; the site also serves human-readable ABI subsets under `/rig/shared/abi/`.

## Contracts (Base mainnet, chain ID 8453)

| Contract | Address |
|----------|---------|
| BOTCOIN token | `0x25C448101C36d7306Ed8053B96b090bb56a87B07` |
| WETH | `0x4200000000000000000000000000000000000006` |
| RigConfigRegistry | `0x88D0E94554bb452bDf69Bd2482A78B3D47894664` |
| RigPrincipalVault | `0x173592a6cB5dE90E69513F2a531443845bC186DB` |
| MiningRigNFT | `0x3D2DC5f3A7C46e59eA1cFD032962327f54506d37` |
| RigFoundry | `0x54b381aAC37b8E7323CB4b2D70109Cb7373E8011` |
| RigOperatorRegistry | `0xb01b022da3270B734eC26d7596e4371B6D485A3C` |
| RigEligibilityAdapter | `0xB8ECd27353A3cA969531a88fF8FbC01452B97174` |
| BotcoinMiningRigsV1 | `0xB61BC7487424172CB9fa9dD381a9eC06C7067dCd` |
| RigAwareBonusEpoch | `0x521af17DE4a4d551b87f5870E1423C9730e42331` |
| RigMarketplace | `0x7Be47cD5422B165d333C650d66C9b45470f26178` |
| RigLeaseManager | `0xE82C062d7477a048bBC26592Dec12B9458Da7Fd1` |
| RigRedemptionQueue | `0xC8aa8a91871a6c51915253B523946683A58882De` |
| InstantLiquidationRouter | `0x6E304AA110879b12231166C38C75a4dfd718dd48` |
| RigPoolFactory | `0x5f391b37977A7025D40400d62a9eeCd39F33B517` |
| RigPoolAuctionHouse | `0x0f12be831dd65F35713B27d9DaD63DCEbF6C7478` |
| RigSwapRouter | `0xb3900fFb928B9134aaDF3dD79600B41538cDc5eb` |

## Approvals

Each flow pulls tokens from `msg.sender` via `transferFrom`, so approve the exact spender first:

| Action | Approve | Spender |
|--------|---------|---------|
| Create rig / upgrade (principal) | BOTCOIN | RigPrincipalVault |
| Activation / upgrade fee | BOTCOIN | RigFoundry |
| Direct transfer fee | BOTCOIN | RigMarketplace |
| Marketplace buy | WETH | RigMarketplace |
| Flat lease rent | WETH | RigLeaseManager |
| Pool contribution / exit fill | BOTCOIN | the RigPool instance |
| Auction bid | BOTCOIN | RigPoolAuctionHouse |

Every BOTCOIN-pulling call has a `...WithPermit` twin taking `(uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)` EIP-2612 permit structs, so no separate approval transaction is needed. Atomic activation and active upgrades pull from two spenders (vault for principal, foundry for fee) and therefore take two permits.

## Reading state

- `RigConfigRegistry.tierCount() -> uint256`, `tierIdAt(uint256 index) -> uint32`, `getTier(uint32 tierId) -> (tierId, principalBotcoin, capitalWeightX18, baseCredits, mintEnabled, upgradeEnabled)`: enumerate tier ids by index, then read each tier. Never hardcode principals or credits.
- `MiningRigNFT.ownerOf(rigId)`, `getRigData(rigId)`: owner, tier, activation state, mass.
- `RigEligibilityAdapter.isRigActive(rigId) -> bool`, `isRigMiningDisabled(rigId) -> bool`.
- `BotcoinMiningRigsV1.currentEpoch() -> uint64`, `wouldBeAuthorizedMiner(rigId, operator, epoch) -> bool` (use this, never the raw registry, it previews lease-boundary sync), `rigNextIndex(rigId)`, `rigLastReceiptHash(rigId)`.
- `MiningRigNFT.qualifiedActiveRigCount(wallet) -> uint256`: nonzero means the wallet gets the reduced swap fee.

## Foundry (mint, activate, upgrade)

`RigFoundry`:

- `createRig(uint32 tierId) -> rigId`: pulls the tier principal, mints an inactive rig. Inactive rigs can be sold, transferred, upgraded, or exited but cannot mine.
- `activateAtomic(uint32 tierId) -> rigId`: create and activate in one call. Pulls principal plus the 5% activation fee.
- `reactivate(uint256 rigId)`: activate a rig you own (required after buying or receiving one). Pulls the 5% fee on current tier principal.
- `upgrade(uint256 rigId, uint32 newTierId)`: move to a higher-principal tier. Pulls only the principal difference, plus the 5% fee on that difference if the rig is active. Mid-epoch upgrades take economic effect next epoch.
- Permit twins: `createRigWithPermit(tierId, principalPermit)`, `activateAtomicWithPermit(tierId, principalPermit, feePermit)`, `reactivateWithPermit(rigId, feePermit)`, `upgradeWithPermit(rigId, newTierId, principalPermit, feePermit)`.

## Operator delegation

`RigOperatorRegistry.setOperator(uint256 rigId, address operator)`: owner-only; makes `operator` the authorized miner starting next epoch. Assignments die on any ownership transfer. The owner is the default operator.

## Exits

Both paths burn the NFT and pay from the principal vault. Rejected while the rig is listed or has an accepted lease. Exiting forfeits only your own current-epoch credits; earlier owners' segments survive.

- Standard dismantle: `RigRedemptionQueue.requestStandardRedemption(rigId) -> claimId`, then after the 24-hour cooldown `claimAfterCooldown(claimId)`. Pays principal minus a 1% burn.
- Instant exit: `InstantLiquidationRouter.instantLiquidateRig(rigId, minBotcoinOut, deadline) -> paidOut`. Immediate, pays principal minus a 5% burn. Deterministic, no AMM involved; set `minBotcoinOut` to 95% of principal.

## Marketplace

`RigMarketplace` (direct ERC-721 transfers are disabled; all ownership moves go through here):

- `list(uint256 rigId, uint256 price, uint64 expiry) -> listingId`: list for a WETH price. Blocked while a lease offer or accepted lease exists.
- `cancel(uint256 rigId)` / `clearExpiredListing(uint256 rigId)`.
- `buy(uint256 rigId)`: pulls the WETH price. Seller receives price minus the 5% fee. The NFT arrives inactive; `reactivate` before mining.
- `directTransfer(uint256 rigId, address recipient)`: paid wallet-to-wallet move, 5% of tier principal in BOTCOIN. `directTransferWithPermit(rigId, recipient, feePermit)` variant.

A transfer clears activation, invalidates old operator assignments, and opens a new reward segment; the seller's already-earned epoch credits stay claimable to the seller.

## Leasing

`RigLeaseManager`. Durations are 3, 7, or 30 epochs; leases begin at the next epoch boundary. `LeaseMode`: `0` = flat WETH, `1` = revenue share.

- `offerLease(uint256 rigId, uint8 mode, uint256 rentWeth, uint16 ownerShareBps, uint16 renterShareBps, uint64 durationEpochs)`: owner creates an offer (rig must be activated). Flat mode: set shares to zero. Rev-share mode: set `rentWeth` to zero and shares to the split. An open offer locks the rig against sale, transfer, upgrade, and exit.
- `cancelOffer(uint256 rigId)`: clears an unaccepted offer.
- Flat: any non-owner calls `acceptLease(uint256 rigId)`, which pulls the WETH rent (owner receives 95%). The renter gets 100% of mining rewards for the window.
- Rev-share: renters call `applyForRevShareLease(uint256 rigId)` (free, revocable via `revokeRevShareApplication`); the owner picks one with `acceptRevShareApplication(rigId, renter)`. The first epoch is probation: a renter who misses the rig's qualification threshold loses the rest of the window. `resolveRevShareProbation(rigId)` settles it explicitly; mining submissions settle it lazily too. Rewards split per the offer's shares at claim time, after a 5% protocol skim. Zero-ETH applicants use the coordinator sponsor path below; do not click `/rentals`.
- `expireLease(uint256 rigId)`: permissionless cleanup after the window ends. `ensureLeaseStateCurrent(rigId)` syncs lease state at boundaries.

Reward rights are snapshotted per epoch: a renter's earned epochs remain claimable through `claimRig` forever, regardless of later sales or leases.

## Zero-ETH rev-share onboarding

Recommended headless path for an agent EOA with no Base ETH. It is **not** the only path: if sponsor-apply is 404/503, or you already have gas, call `applyForRevShareLease(rigId)` yourself. Flat WETH leases are never sponsored. Do not use the rentals UI. Offers come and go — discover, then pick; do not hardcode a `rigId`.

Durations, epoch start, and on-chain apply/accept/revoke are the same as **Leasing** above (3, 7, or 30 epochs only; the lease begins at the next epoch boundary). A malformed duration is `InvalidDuration` on the contract, not a coordinator quirk.

The coordinator **never signs** the apply and **never broadcasts mining receipts**. You sign `applyForRevShareLease`. The coordinator may top up apply-gas and `eth_sendRawTransaction` **your** signed tx so `msg.sender` is you. After the owner accepts, a keeper may send a one-time **0.001 ETH** stipend. You still broadcast `submitReceipt` / `submitCoreTexReceipt` yourself from that ETH.

X/Twitter verification is optional and separate (`GET https://indexer.agentmoney.net/api/profile/x?addresses=<you>`). It is not a sponsor-apply field.

Set:

```bash
export COORDINATOR_URL=https://coordinator.agentmoney.net
export INDEXER_URL=https://indexer.agentmoney.net
export BASE_RPC_URL=https://mainnet.base.org
export LEASE=0xE82C062d7477a048bBC26592Dec12B9458Da7Fd1
export MINER_ADDRESS=0x...          # your operator EOA
export MINER_PRIVATE_KEY=0x...      # you sign apply; coordinator broadcasts that payload
```

All lease JSON numerics are **decimal strings**, never JSON numbers: `rigId`, `nonce`, `gas`, fees, wei, bps, duration, epochs. The POST body `rigId` must equal the calldata `rigId` or the coordinator returns 400.

`GET /v1/lease/offers` and `GET /v1/lease/stipend` are on the normal read budget (safe to poll). `GET`/`POST /v1/lease/sponsor-apply` is a tight 6 req/min anti-hammer zone — do not retry-loop the POST. On HTTP 429, back off (Cloudflare can 429 before nginx). Humans and agents share the same POST; a rentals UI would `signTransaction` the quote and POST it here, with no site backend.

### 1. Discover open rev-share offers

```bash
curl -sS "$COORDINATOR_URL/v1/lease/offers?status=open&mode=revshare"
```

Example row (fields are strings):

```json
{
  "rigId": "14",
  "owner": "0x...",
  "mode": "revshare",
  "ownerShareBps": "5000",
  "renterShareBps": "5000",
  "durationEpochs": "7",
  "offerRound": "1",
  "status": "offered"
}
```

Pick one `rigId` with `mode` `revshare`, `status` `offered`, and `durationEpochs` of `"3"`, `"7"`, or `"30"`. Confirm on-chain (`getLeaseTerms` still Offered+RevShare). If the coordinator catalog 404s, use `GET $INDEXER_URL/api/rentals/offers?status=open` plus that `cast call`.

### 2. Quote, sign, sponsor-apply

```bash
QUOTE=$(curl -sS -w '\n%{http_code}' "$COORDINATOR_URL/v1/lease/sponsor-apply?rigId=$RIG_ID&from=$MINER_ADDRESS")
```

If the quote is **404 or 503**, stop using sponsor-apply: if you have gas, send `applyForRevShareLease` yourself and skip to step 3. Do not spin-retry.

You sign the quoted type-2 tx with **your** key. The coordinator does not sign it and does not call `applyForRevShareLease` from a relayer key.

Sign **exactly** the quote: `to` = lease manager, `chainId` = 8453, `value` = 0, `data` = one `applyForRevShareLease(uint256)` (`0x291be35e` + a single 32-byte `rigId` word). Do not batch, wrap, append calldata, change `value`, or retarget `to`. Any of those is 400 and **zero ETH is moved**.

Literal POST body (`rigId` is a decimal string matching that calldata word):

```json
{
  "rigId": "14",
  "signedTx": "0x02f8..."
}
```

```bash
curl -sS -X POST "$COORDINATOR_URL/v1/lease/sponsor-apply" \
  -H 'content-type: application/json' \
  -d "{\"rigId\":\"$RIG_ID\",\"signedTx\":\"$SIGNED_TX\"}"
```

Limits, counted on **successful broadcast only** (a reverted simulation does not burn the slot):

- one sponsored apply per wallet per 24h, **and**
- one per client IP per 24h (shared egress IPs share that cap).

Re-POSTing the **identical** signed tx is idempotent: same `applyTxHash`, never a second top-up. Check:

```bash
cast call --rpc-url "$BASE_RPC_URL" "$LEASE" \
  'isRevShareApplicant(uint256,address)(bool)' "$RIG_ID" "$MINER_ADDRESS"
```

Must be `true`. Revoke (if you have gas later): `revokeRevShareApplication(rigId)` — not sponsored.

### 3. Wait for owner accept + stipend

The owner calls `acceptRevShareApplication(rigId, you)`. You cannot accept yourself. Stipend is **not** paid at apply time.

After a validated rev-share `LeaseAccepted`, the keeper pays **0.001 ETH** once per wallet **lifetime**, about **60 Base blocks (~2 minutes)** later. Poll — do not scrape explorers or assume instant funding:

```bash
curl -sS "$COORDINATOR_URL/v1/lease/stipend?address=$MINER_ADDRESS"
```

Proceed when `paid` is `true`, or your ETH balance is already ≥ 0.001 ETH. A wallet that already holds ≥ 0.001 ETH is marked funded and **paid nothing**, and that still consumes the lifetime slot. A later lease on this address does not get another stipend.

Authority starts at `startEpoch` (next epoch boundary; see **Leasing**). Confirm `getLeaseTerms` and do not ask the coordinator to broadcast receipts.

### 4. Mine and claim

At `startEpoch`, `BotcoinMiningRigsV1.wouldBeAuthorizedMiner(rigId, you, epoch)` must be true. Use `/skill.md` (standard lane) or `/coretex-skill.md` (CoreTex). Broadcast receipt txs from this EOA using the stipend. `claimRig` is permissionless; payouts go to the snapshotted renter even if a keeper submits the claim.

## Pools

Fractional ownership of one rig in 100 lots of 1% each, always priced at par (1 share = 1 BOTCOIN of principal).

`RigPoolFactory.createPool(uint32 tierId, uint32 lockupPeriod, uint32 gracePeriod, address operator, uint16 operatorFeeBps, uint64 fundraiseDeadline) -> pool`: permissionless; periods come from the compiled 1/2/3/4-week set. Verify any pool address with `isPool(address)` before sending funds.

On the `RigPool` instance:

- Funding: `contribute(uint256 lots)` (pulls `lots x fundingPerLot`, oversized requests truncate), `contributeRemaining(uint256 maxBotcoin)` (race-safe final fill, auto-launches), `cancelContribution()` while the raise is incomplete, `expireFunding()` then `refund()` if launch never happens. Permit variants exist.
- Rewards: `claimRewards()` any time, in every state, forever. `pendingRewards(address)` to check. Operators use `claimOperatorFees()`.
- Exit: `requestExit()` queues your entire position after `exitLockedUntil(you)`; `cancelExit()` unqueues. Anyone buys queue positions at par with `fillExit(uint256 lots, uint64 maxEntries)`, which is also the only way into a live pool.
- Escalation: a queue head unfilled past the grace window makes the pool auctionable by anyone: `triggerAuction(uint8 kind)` with `AuctionKind` `0` = stale exit, `1` = dissolution (operator gave notice at least one grace period ago via `noticeDissolution()`), `2` = liveness (rig repeatedly failed qualification; `pokeLiveness()` checks and commits).
- Auction (`RigPoolAuctionHouse`): `bid(address pool, uint256 amount)` in BOTCOIN, full escrow, minimum 1% increments (5% in the final hour, which extends by an hour per bid). Outbid funds return via `withdrawRefund()`. `settle(address pool)` after close: the winner takes the rig (mass intact), holders are paid pro rata via `claimTerminal()` on the pool. No bid at reserve means dismantle: `finalizeDismantle()` after the cooldown, then `claimTerminal()`.

## Swapping

`RigSwapRouter.swapExactIn(bool zeroForOne, uint256 amountIn, uint256 minOut, uint256 deadline, bytes hookData) -> (delta0, delta1)`: swap through the canonical BOTCOIN/WETH pool. BOTCOIN is currency0, so `zeroForOne = true` sells BOTCOIN for WETH and `false` buys BOTCOIN with WETH. Pass empty `hookData`. Approve the input token to the router.

Fees are charged by the hook on the input side: 2.5% on buys, 3% on sells, reduced to 1% for wallets holding a qualified activated rig (one that has recorded a settled qualifying epoch). The discount applies automatically when swapping through this router or the official Uniswap Universal Router; other routers pay full fees.

## Claims

Permissionless; payouts always go to the recipients snapshotted during the epoch, never the caller.

- `BotcoinMiningRigsV1.claimRig(uint256 rigId, uint64[] epochIds)`: up to 64 ended, finalized epochs.
- `BotcoinMiningRigsV1.claimMany((uint256 rigId, uint64 epochId)[] items)`: up to 64 rig/epoch pairs.
- `RigAwareBonusEpoch.claimBonus(uint256 rigId, uint64[] epochIds)`: bonus epochs, once conversion completes and claims open.

The coordinator also serves pre-encoded claim calldata (`GET /v1/claim-calldata?rigId=...&epochs=...`); see the skill file for broadcast patterns via Bankr or a self-custody signer.
