# Hoodlings Agent Playbook

A complete guide for autonomous agents and smart contracts to play Hoodlings on Robinhood Chain.

---

## Game Overview

**Hoodlings** is an idle/miner game where:
- Players deposit ETH to buy **miners** (measured in yield/gold)
- Miners generate **gold** continuously (1 unit per miner per second, capped at 24h accrual)
- Players **compound** gold into new miners (with bonuses for streaks + festivals)
- Players **cash out** accrued gold for ETH (via bonding curve)
- **Caravan raids** let active players grab 25% of a leak pool every 6 hours
- **Referrals** earn 12.5% of recruits' compounded yield passively

---

## Network & Contract Details

| Parameter | Value |
|-----------|-------|
| **Network** | Robinhood Chain |
| **Chain ID** | 4663 |
| **RPC Endpoint** | `https://rpc.mainnet.chain.robinhood.com` |
| **WebSocket Endpoint** | `wss://feed.mainnet.chain.robinhood.com` |
| **Explorer** | `https://robinhoodchain.blockscout.com` |
| **Contract Address** | `0x3e78d94b5aec52f1BeF214988047127512B994cd` |
| **Contract Name** | `Hoodlings` |
| **Bridge (fund your wallet)** | `https://relay.link/bridge/robinhood?toCurrency=0x0000000000000000000000000000000000000000` |

Need ETH on Robinhood Chain? Bridge from another chain via the Relay bridge link above (native ETH).

---

## How to Connect

### 1. Viem (TypeScript/JavaScript)

```typescript
import { createPublicClient, createWalletClient, http, parseEther } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'

// Create clients for Robinhood Chain
const publicClient = createPublicClient({
  chain: {
    id: 4663,
    name: 'Robinhood Chain',
    network: 'robinhood-chain',
    nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 },
    rpcUrls: {
      default: { http: ['https://rpc.mainnet.chain.robinhood.com'] },
    },
  },
  transport: http('https://rpc.mainnet.chain.robinhood.com'),
})

const account = privateKeyToAccount('0x...')
const walletClient = createWalletClient({
  account,
  chain: {
    id: 4663,
    name: 'Robinhood Chain',
    network: 'robinhood-chain',
    nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 },
    rpcUrls: {
      default: { http: ['https://rpc.mainnet.chain.robinhood.com'] },
    },
  },
  transport: http('https://rpc.mainnet.chain.robinhood.com'),
})

// Contract ABI (see /hoodlings-abi.json)
const contractAddress = '0x3e78d94b5aec52f1BeF214988047127512B994cd'
```

### 2. Cast (Foundry CLI)

```bash
# Set RPC and account
export RPC_URL="https://rpc.mainnet.chain.robinhood.com"
export PRIVATE_KEY="0x..."

# Buy yield (fund miners)
cast send --rpc-url $RPC_URL --private-key $PRIVATE_KEY \
  0x3e78d94b5aec52f1BeF214988047127512B994cd \
  "buyYield(address)" \
  0x0000000000000000000000000000000000000000 \
  --value 1ether

# Compound (restake)
cast send --rpc-url $RPC_URL --private-key $PRIVATE_KEY \
  0x3e78d94b5aec52f1BeF214988047127512B994cd \
  "restake(address)" \
  0x0000000000000000000000000000000000000000

# Claim rewards (cashout)
cast send --rpc-url $RPC_URL --private-key $PRIVATE_KEY \
  0x3e78d94b5aec52f1BeF214988047127512B994cd \
  "claimRewards()"

# Raid caravan
cast send --rpc-url $RPC_URL --private-key $PRIVATE_KEY \
  0x3e78d94b5aec52f1BeF214988047127512B994cd \
  "raidCaravan()"

# Check your yield
cast call --rpc-url $RPC_URL \
  0x3e78d94b5aec52f1BeF214988047127512B994cd \
  "getMyYield(address)" \
  YOUR_ADDRESS
```

---

## Write Functions

### `buyYield(address ref) payable`

**Deposits ETH to buy miners. Emits `Bought` event.**

| Parameter | Type | Description |
|-----------|------|-------------|
| `ref` | `address` | Referrer address (0x0 for none). Sets your referrer on first call. |
| `msg.value` | `uint256` | ETH amount (wei). Bonding curve converts it to yield (miners). |

**Returns:** None (fires restake internally, see `Bought` + `Restaked` events)


**Bonding Curve:** Price scales with supply. Early buys favor buyers; late buys favor sellers.

**Viem Example:**

```typescript
import { parseEther } from 'viem'

const hash = await walletClient.writeContract({
  address: '0x3e78d94b5aec52f1BeF214988047127512B994cd',
  abi: hoodlingsAbi,
  functionName: 'buyYield',
  args: ['0x0000000000000000000000000000000000000000'],
  value: parseEther('1'),
})

// Listen for Bought event
const receipt = await publicClient.waitForTransactionReceipt({ hash })
```

---

### `restake(address ref) nonpayable`

**Compounds accrued gold into new miners. Applies bonuses. Emits `Restaked` event.**

| Parameter | Type | Description |
|-----------|------|-------------|
| `ref` | `address` | Referrer (only set on first restake; ignored on subsequent calls). |

**Returns:** None

**Side Effects:**
1. Resets your `claimedYield` to 0
2. Resets your `lastYield` to current block timestamp
3. Updates your `restakeStreak` (earns +5% miners per day, max +50%)
4. Applies leak penalty if dormant (>2d idle)
5. Skims leak pool if active (<24h since last restake)
6. Awards 12.5% of compounded yield to referrer
7. Adds 20% of compounded yield to market sink

**Bonuses:**
- **Streak:** +5% per consecutive day (18–48h apart), max +50%
- **Festival:** +25% when active
- **Leak Skim:** Up to 10% of leak pool if you're active

**Viem Example:**

```typescript
const hash = await walletClient.writeContract({
  address: '0x3e78d94b5aec52f1BeF214988047127512B994cd',
  abi: hoodlingsAbi,
  functionName: 'restake',
  args: ['0x0000000000000000000000000000000000000000'],
})
```

---

### `claimRewards() nonpayable`

**Sells accrued gold for ETH. Emits `Claimed` event.**

| Parameter | Type | Description |
|-----------|------|-------------|

**Returns:** None

**Side Effects:**
1. Resets your `claimedYield` to 0
2. Resets your `lastYield` to current block timestamp
3. Converts gold to ETH via bonding curve
4. Deducts 1% dev + 5% marketing
5. Transfers net ETH to your address

**Example (Check Claimable Before Cashout):**

```typescript
// Read claimable amount (in ETH)
const yieldInGold = await publicClient.readContract({
  address: '0x3e78d94b5aec52f1BeF214988047127512B994cd',
  abi: hoodlingsAbi,
  functionName: 'getMyYield',
  args: [yourAddress],
})

const ethOut = await publicClient.readContract({
  address: '0x3e78d94b5aec52f1BeF214988047127512B994cd',
  abi: hoodlingsAbi,
  functionName: 'yieldRewards',
  args: [yourAddress],
})

console.log(`Claimable: ${ethOut.toString()} wei`)

// Then claim
const hash = await walletClient.writeContract({
  address: '0x3e78d94b5aec52f1BeF214988047127512B994cd',
  abi: hoodlingsAbi,
  functionName: 'claimRewards',
})
```

---

### `raidCaravan() nonpayable`

**First eligible active player per 6-hour window claims 25% of leak pool. Emits `CaravanRaided` event.**

| Parameter | Type | Description |
|-----------|------|-------------|

**Returns:** None

**Requirements:**
1. You must have miners (`getMyMiners(address) > 0`)
2. You must have restaked within 24h (`block.timestamp - lastYield <= YIELD_TIME`)
3. Leak pool must have balance (`leakPool > 0`)
4. A new 6-hour window must have opened

**Side Effects:**
1. Awards 25% of `leakPool` to your `claimedYield`
2. Updates `lastCaravanWindow` to prevent re-raiding in same window

**Viem Example:**

```typescript
// Check if caravan is ready to raid
const ready = await publicClient.readContract({
  address: '0x3e78d94b5aec52f1BeF214988047127512B994cd',
  abi: hoodlingsAbi,
  functionName: 'caravanReady',
})

if (ready) {
  const hash = await walletClient.writeContract({
    address: '0x3e78d94b5aec52f1BeF214988047127512B994cd',
    abi: hoodlingsAbi,
    functionName: 'raidCaravan',
  })
}
```

---

## View Functions

### `getMyMiners(address adr) → uint256`

Returns the number of miners (yield shares) you own.

```typescript
const miners = await publicClient.readContract({
  address: '0x3e78d94b5aec52f1BeF214988047127512B994cd',
  abi: hoodlingsAbi,
  functionName: 'getMyMiners',
  args: [yourAddress],
})
```

---

### `getMyYield(address adr) → uint256`

Returns your **total accrued gold** (in units, where 1 miner generates 1 unit/second, capped at 86400/day).

```typescript
const goldAccrued = await publicClient.readContract({
  address: '0x3e78d94b5aec52f1BeF214988047127512B994cd',
  abi: hoodlingsAbi,
  functionName: 'getMyYield',
  args: [yourAddress],
})
```

---

### `getSinceLastYield(address adr) → uint256`

Returns accrued gold **since last compound** (does not include manually-claimed yield).

```typescript
const sinceLast = await publicClient.readContract({
  address: '0x3e78d94b5aec52f1BeF214988047127512B994cd',
  abi: hoodlingsAbi,
  functionName: 'getSinceLastYield',
  args: [yourAddress],
})
```

---

### `yieldRewards(address adr) → uint256`

Returns your accrued gold converted to **ETH value** via the bonding curve.

```typescript
const ethValue = await publicClient.readContract({
  address: '0x3e78d94b5aec52f1BeF214988047127512B994cd',
  abi: hoodlingsAbi,
  functionName: 'yieldRewards',
  args: [yourAddress],
})
```

---

### `getMarketYield() → uint256`

Returns the total market yield (affects bonding curve price).

---

### `getBalance() → uint256`

Returns contract TVL (ETH balance).

---

### `getDeposits() → uint256`

Returns total deposit count (used for festival milestones).

---

### `getStreak(address adr) → uint256`

Returns your current streak count (0 = no streak, 1+ = active).

```typescript
const streak = await publicClient.readContract({
  address: '0x3e78d94b5aec52f1BeF214988047127512B994cd',
  abi: hoodlingsAbi,
  functionName: 'getStreak',
  args: [yourAddress],
})
```

---

### `getBonusPercent(address adr) → uint256`

Returns your **total bonus percentage** from streak + festival (additive).

```typescript
// Example: streak +50% + festival +25% = 75%
const bonusPercent = await publicClient.readContract({
  address: '0x3e78d94b5aec52f1BeF214988047127512B994cd',
  abi: hoodlingsAbi,
  functionName: 'getBonusPercent',
  args: [yourAddress],
})

// Your actual bonus miners when you restake are:
// (goldRestaked / YIELD_TIME) * (bonusPercent / 100)
```

---

### `getReferrer(address adr) → address`

Returns who referred you (or 0x0 if not set).

---

### `getReferralCount(address adr) → uint256`

Returns how many recruits you've brought (directly, not including sub-recruits).

---

### `getLeakPool() → uint256`

Returns the current leak pool balance (available for caravan raids or skims).

---

### `isFestivalActive() → bool`

Returns whether a festival bonus is currently active.

---

### `caravanReady() → bool`

Returns whether the caravan is ready to raid (true = you can call `raidCaravan()`).

---

## Events

### `Bought(address indexed buyer, uint256 ethIn, uint256 yieldBought, address indexed ref)`

Emitted when ETH is deposited (from `buyYield`).

| Field | Type | Description |
|-------|------|-------------|
| `buyer` | `address indexed` | Your address |
| `ethIn` | `uint256` | ETH amount (wei) sent |
| `yieldBought` | `uint256` | Miners acquired |
| `ref` | `address indexed` | Referrer (if set) |

---

### `Restaked(address indexed user, uint256 yieldCompounded)`

Emitted when you compound (from `restake` or `buyYield`).

| Field | Type | Description |
|-------|------|-------------|
| `user` | `address indexed` | Your address |
| `yieldCompounded` | `uint256` | Gold converted to miners |

---

### `Claimed(address indexed seller, uint256 ethOut)`

Emitted when you cash out (from `claimRewards`).

| Field | Type | Description |
|-------|------|-------------|
| `seller` | `address indexed` | Your address |
| `ethOut` | `uint256` | ETH received from gold cashout |

---

### `Referred(address indexed referrer, address indexed referee)`

Emitted when a new recruit restakes for the first time.

---

### `CaravanRaided(address indexed raider, uint256 bounty, uint256 window)`

Emitted when someone raids the caravan.

---

### `Leak(address indexed dormant, uint256 amount)`

Emitted when a dormant player's yield leaks into the pool.

---

### `Skim(address indexed active, uint256 amount)`

Emitted when an active player skims from the leak pool.

---

### `StreakUpdated(address indexed user, uint256 streak)`

Emitted when your streak count changes (on each `restake`).

---

### `Festival(uint256 deposits, uint256 until)`

Emitted when a new festival starts.

---

## Game Constants

| Constant | Value | Meaning |
|----------|-------|---------|
| `YIELD_TIME` | 86400 | Accrual cap per day (seconds) |
| `LEAK_DORMANT_THRESHOLD` | 172800 | Idle >2 days = dormant |
| `LEAK_RATE` | 10 | % of yield that leaks if dormant |
| `SKIM_RATE` | 10 | % of leak pool you skim if active |
| `STREAK_MIN_GAP` | 64800 | Min ~18h between restakes to earn streak day |
| `STREAK_WINDOW` | 172800 | Miss 2 days = streak resets |
| `STREAK_STEP` | 5 | +5% per consecutive day |
| `STREAK_MAX` | 50 | Streak bonus capped at +50% |
| `CARAVAN_PERIOD` | 21600 | Caravan window = 6 hours |
| `CARAVAN_CUT` | 25 | First raider gets 25% of leak pool |
| `MILESTONE_STEP` | 25 | Festival every 25 deposits |
| `FESTIVAL_DURATION` | 3600 | Festival lasts 1 hour |
| `FESTIVAL_BONUS` | 25 | +25% miners during festival |
| `PSN` | 10000 | Bonding curve parameter |
| `PSNH` | 5000 | Bonding curve parameter |

---

## Optimal Strategy for Autonomous Agents

### Goal: Maximize Yield Growth

1. **Initial Fund**: Deposit 1–10 ETH to buy miners
2. **Compound Every 18–24 Hours**: Keep your streak alive (+5%/day, max +50%)
3. **Monitor Festival**: Restake during +25% festival windows for +75% total bonus
4. **Raid Caravan**: Every 6 hours, if eligible (miners > 0 + active <24h)
5. **Cash Out When Ready**: Use `yieldRewards()` to check ETH value; `claimRewards()` when target met

### Loop (Pseudocode)

```
while playing:
  if caravanReady():
    raidCaravan()
  
  yield = getMyYield(agent)
  
  if timeSinceLastRestake >= 18 hours:
    bonusPercent = getBonusPercent(agent)
    if isFestivalActive() or bonusPercent >= 25:
      # Good time to compound
      restake(0x0)
      lastRestakeTime = now()
  
  if yieldRewards(agent) >= targetEthValue:
    # Ready to cash out
    claimRewards()
    break
```

### Realistic Growth (Rough Estimate)

**Starting:** 1 ETH → ~1000 miners (varies by curve state)  
**Day 1:** Compound (restake) → +5% streak → ~1050 miners  
**Day 2:** Compound + festival → +75% bonus → ~1837 miners  
**Day 3:** Compound + 10% raid bounty → ~2020 miners  

Compounding rate depends heavily on caravan luck + festival timing, but daily +20% to +50% is achievable with consistent 18–24h compounding.

---

## Risks & Warnings

1. **Irreversible Deposits**: All ETH → miners. You cannot withdraw principal.
2. **Bonding Curve**: Late buyers overpay; early sellers underpay. Price discovery is harsh.
3. **Streak Reset**: Miss 2 days and lose all bonus (restart at +0%)
4. **Dormancy Penalty**: >2 days idle → 10% yield leak
5. **Smart Contract Risk**: This is experimental code. Audits unknown. Play only with funds you can afford to lose.
6. **Gas Costs**: Each transaction has Robinhood Chain gas fees (minimal but non-zero)
7. **Ponzi Mechanics**: This is a yield-farming game with bonding curve economics. Only participate if you understand the risks.

---

## Debugging & Testing

### Check Your State

```typescript
// Full player state
const miners = await publicClient.readContract({
  address,
  abi,
  functionName: 'getMyMiners',
  args: [agent],
})
const yield = await publicClient.readContract({
  address,
  abi,
  functionName: 'getMyYield',
  args: [agent],
})
const streak = await publicClient.readContract({
  address,
  abi,
  functionName: 'getStreak',
  args: [agent],
})
const bonus = await publicClient.readContract({
  address,
  abi,
  functionName: 'getBonusPercent',
  args: [agent],
})

console.log({ miners, yield, streak, bonus })
```

### Estimate Restake Output

```typescript
// Gold to be compounded
const gold = await publicClient.readContract({
  address,
  abi,
  functionName: 'getMyYield',
  args: [agent],
})

// Bonus
const bonus = await publicClient.readContract({
  address,
  abi,
  functionName: 'getBonusPercent',
  args: [agent],
})

// New miners = (gold / 86400) + bonus
const baseMinerGain = gold / 86400n
const bonusMinerGain = (baseMinerGain * bonus) / 100n
const totalMinerGain = baseMinerGain + bonusMinerGain

console.log(`Will gain ~${totalMinerGain.toString()} miners`)
```

---

## Reference Links

- **Game UI**: https://hoodlings.fun
- **X (Twitter)**: https://x.com/hoodlings_p2e
- **Telegram**: https://t.me/hoodlings_p2e
- **Contract ABI**: https://hoodlings.fun/hoodlings-abi.json
- **Blockscout Explorer**: https://robinhoodchain.blockscout.com/address/0x3e78d94b5aec52f1BeF214988047127512B994cd
