> For the complete documentation index, see [llms.txt](https://kittypunch.gitbook.io/kittypunch-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kittypunch.gitbook.io/kittypunch-docs/token-economy/kona-pearl/kona-points-pearl-integration-guide.md).

# Kona Points → PEARL: Integration Guide

### Contract Address Registry

#### Core System

| Contract           | Address                                      |
| ------------------ | -------------------------------------------- |
| KonaPointsFarm     | `0x89e9d9b7D87591875359247f7d8eb786e7ea6910` |
| PearlMinter        | `0x92C18C91Ad1356DDEA429Bf5C49949489Cea4608` |
| PointsSpenderProxy | `0x7de407C3a07Da1CC405e024374186f9249d76B00` |

#### Tokens

| Token | Address                                      | Decimals |
| ----- | -------------------------------------------- | -------- |
| KONA  | `0x92aba186c85b5aFEB3a2CEdC8772Ae8638F1B565` | 18       |
| PEARL | `0x792CF0A64a46DbB48CA414DFf20DcD341812579e` | 18       |
| WETH  | `0x3439153EB7AF838Ad19d56E1571FBD09333C2809` | 18       |
| USDC  | `0x84A71ccD554Cc1b02749b35d22F684CC8ec987e1` | 6        |
| USDT  | `0x0709F39376dEEe2A2dfC94A58EdEb2Eb9DF012bD` | 6        |
| PENGU | `0x9eBe3A824Ca958e4b3Da772D2065518F009CBa62` | 18       |

#### Kona Lend (Aave V3)

| Contract                | Address                                      |
| ----------------------- | -------------------------------------------- |
| Pool Proxy              | `0x8f16B5713F412C5dE4951Aaf678Eb8409101f819` |
| Pool Addresses Provider | `0x7326d30aDAec89c992eBfEbA3beDB9044AC2F704` |
| Protocol Data Provider  | `0xDfc422c8793864ecD12Bc59F2024614034BcB078` |
| Aave Oracle             | `0xE4Cd99d4698482ff263C300fbe2fA5fE08af1622` |
| RewardsController       | `0x4237d126713bf1FaBA29Ed8A55A6A0634886e223` |
| Emission Manager        | `0xC8Ffda370edF4a15554A4120c95641Ddd6c084C8` |

#### Kona Lend Incentivized Assets

| Market | kToken (Supply)                              | variableDebt (Borrow)                        |
| ------ | -------------------------------------------- | -------------------------------------------- |
| PENGU  | `0x5f8eee4e8033978e19b45399f044d05928527fbe` | `0xdb7c7029e541028d0dc79da4a6e744fca7d96356` |
| USDC   | `0xdf480d354d2136257d3cb61bf121fde6d4fce646` | `0xa1cd10e2c8923e1174007073f47f8fa200163fc8` |
| USDT   | `0xcb77c57249ef930d014d227a4fb64bcaaea982da` | `0x59cf6d4027761bd560216e08981e7746106e7c4e` |
| WETH   | `0x79e8627dcb7abe2160c830795c07b78b648577b5` | `0xf59fadd8200993025490e746fd1e4bcf6d3dfa75` |

#### Kona Swap

| Contract           | Address                                      |
| ------------------ | -------------------------------------------- |
| Aggregator Router  | `0xf9D3E237427C84741394C7C2E9cd66f94EA3d4D6` |
| Aggregator Factory | `0x29372c22459a4e373851798bFd6808e71EA34A71` |
| V2 Router          | `0x441E0627Db5173Da098De86b734d136b27925250` |
| V2 Factory         | `0x7c2e370CA0fCb60D8202b8C5b01f758bcAD41860` |

***

### ⚠️ KONA Gating: Read This First

**Points alone cannot be converted to PEARL.** Every single PEARL mint permanently burns 1 KONA token. KONA has a hard cap of \~90,000 tokens and that number only goes down. There is no mechanism to create more KONA beyond the existing FROTH → KONA conversion (1,000 FROTH burned = 1 KONA minted), and FROTH itself is a finite, decaying supply.

This means:

* A contract or agent that earns 1,000,000 points but holds zero KONA **cannot mint a single PEARL**.
* Every PEARL mint removes 1 KONA from circulation permanently. The cost to produce PEARL rises over time — like Bitcoin mining difficulty, except it never adjusts downward.
* Early integrators who acquire KONA and convert at today's prices have a structural cost advantage that compounds as supply shrinks.

**If you are building a vault, bot, or agent that farms Kona Points, you need a KONA acquisition and treasury strategy.** Points are the easy part. KONA is the bottleneck.

***

### How the System Works

Two protocols feed a single points ledger:

**Kona Lend** — Points accrue per-second to holders of kTokens (supply) and variableDebtTokens (borrow). No staking required. Claim via RewardsController.

**Kona Swap** — Points accrue per-block to users who stake LP tokens in KonaPointsFarm. MasterChef-style.

Both converge into `KonaPointsFarm.totalPoints[user]`. Those points convert to PEARL via PearlMinter at 100 points = 1 PEARL, burning 1 KONA per transaction.

```
Kona Lend                          Kona Swap
(hold kTokens / debtTokens)        (stake LP tokens)
       │                                  │
       ▼                                  ▼
RewardsController              KonaPointsFarm.stake()
       │                                  │
       ▼                                  │
claimAllRewardsToSelf()                   │
       │                                  │
       └──────────┬───────────────────────┘
                  ▼
        KonaPointsFarm
        totalPoints[user]
                  │
                  ▼
          PointsSpenderProxy
                  │
                  ▼
            PearlMinter
       100 points + 1 KONA (burned)
              = 1 PEARL
```

***

### Part 1: Earning Points via Kona Lend

Depositing into Kona Lend gives you kTokens. Borrowing gives you variableDebtTokens. Both accrue points automatically via the RewardsController. You call `claimAllRewardsToSelf()` to harvest into KonaPointsFarm.

#### Current Emission Rates

| Asset             | Type   | Points/Second |
| ----------------- | ------ | ------------- |
| kPENGU            | Supply | 8             |
| variableDebtPENGU | Borrow | 3             |
| kUSDC             | Supply | 10            |
| variableDebtUSDC  | Borrow | 4             |
| kUSDT             | Supply | 10            |
| variableDebtUSDT  | Borrow | 4             |
| kWETH             | Supply | 8             |
| variableDebtWETH  | Borrow | 3             |

Note: stablecoin markets (USDC, USDT) carry the highest emission rates. Builders optimizing for point velocity should factor this into allocation strategy.

These rates are configurable via the Emission Manager and will eventually be governed by vePEARL gauge voting.

#### Point Accrual Math

```
userPoints/sec = emissionPerSecond × (userBalance / totalSupply)
```

Example: 10,000 kPENGU held out of 100,000 total, emission = 8/sec:

```
8 × (10,000 / 100,000) = 0.8 points/sec = 69,120 points/day
```

#### RewardsController Functions

```solidity
// Read pending (unclaimed) rewards
// Returns 18-decimal scaled values — divide by 1e18 for actual points
function getAllUserRewards(
    address[] calldata assets,   // all 8 kToken + debtToken addresses
    address user
) external view returns (
    address[] memory rewardsList,
    uint256[] memory unclaimedAmounts
);

// Claim all pending rewards to caller's KonaPointsFarm balance
function claimAllRewardsToSelf(
    address[] calldata assets
) external returns (
    address[] memory rewardsList,
    uint256[] memory claimedAmounts
);

// Claim to a different address
function claimAllRewards(
    address[] calldata assets,
    address to
) external returns (
    address[] memory rewardsList,
    uint256[] memory claimedAmounts
);
```

#### Reading Pending Lend Points (JS/viem)

```javascript
const REWARDS_CONTROLLER = '0x4237d126713bf1FaBA29Ed8A55A6A0634886e223';

const assets = [
  '0x5f8eee4e8033978e19b45399f044d05928527fbe', // kPENGU
  '0xdb7c7029e541028d0dc79da4a6e744fca7d96356', // variableDebtPENGU
  '0xdf480d354d2136257d3cb61bf121fde6d4fce646', // kUSDC
  '0xa1cd10e2c8923e1174007073f47f8fa200163fc8', // variableDebtUSDC
  '0xcb77c57249ef930d014d227a4fb64bcaaea982da', // kUSDT
  '0x59cf6d4027761bd560216e08981e7746106e7c4e', // variableDebtUSDT
  '0x79e8627dcb7abe2160c830795c07b78b648577b5', // kWETH
  '0xf59fadd8200993025490e746fd1e4bcf6d3dfa75', // variableDebtWETH
];

const [rewardsList, unclaimedAmounts] = await publicClient.readContract({
  address: REWARDS_CONTROLLER,
  abi: LendingRewardsControllerABI,
  functionName: 'getAllUserRewards',
  args: [assets, userAddress]
});

// Convert from 18-decimal to actual points
const totalAccrued = unclaimedAmounts.reduce((sum, amt) => sum + amt, 0n);
const actualPoints = Number(formatUnits(totalAccrued, 18));
```

#### Claim Flow (Internal)

When `claimAllRewardsToSelf()` is called:

```
RewardsController.claimAllRewardsToSelf(assets)
    │
    ▼
PointsTransferStrategy.performTransfer(to, reward, amount)
    │  // Converts 18-decimal to raw points:
    │  uint256 points = amount / 1e18;
    ▼
KonaPointsFarmAdmin.batchDistributePoints([user], [points])
    │
    ▼
KonaPointsFarm.totalPoints[user] += points
```

Points land in the unified KonaPointsFarm ledger and are immediately available for PEARL minting.

***

### Part 2: Earning Points via Kona Swap LP Farming

Provide liquidity on Kona Swap, receive LP tokens, then stake them in KonaPointsFarm. Points accrue per-block proportional to your staked share.

#### KonaPointsFarm Functions

```solidity
// Stake LP tokens into a farm
function stake(uint256 _farmId, uint256 _amount) external;

// Unstake LP tokens (also claims pending points)
function unstake(uint256 _farmId, uint256 _amount) external;

// Claim pending points without changing position
function claimPoints(uint256 _farmId) external;

// View pending unharvested points for a user in a farm
function pendingPoints(uint256 _farmId, address _user) external view returns (uint256);

// Read farm config
function farms(uint256 _farmId) external view returns (
    address lpToken,
    uint256 pointsPerBlock,    // divide by 100 for actual rate
    uint256 totalStaked,
    uint256 lastRewardBlock,
    uint256 accPointsPerShare,
    bool active,
    uint256 minStakeAmount
);

// Total farms count
function getFarmsLength() external view returns (uint256);

// Total earned points (all sources combined)
function totalPoints(address user) external view returns (uint256);

// Points already spent (PEARL mints, gear, etc.)
function spentPoints(address user) external view returns (uint256);

// Real-time breakdown
function getRealTimePoints(address _user) external view returns (
    uint256 claimedPoints,
    uint256 pendingTotalPoints
);

// User staking info for a farm
function userInfo(address user, uint256 farmId) external view returns (
    uint256 amount,        // staked LP amount
    uint256 pointsDebt     // internal accounting
);
```

#### Points Per Block Scaling

The `pointsPerBlock` value stored on-chain is scaled — **divide by 100** to get actual points per block. Daily output depends on Abstract's variable block time:

```
actualPointsPerBlock = rawPointsPerBlock / 100

Min daily points = actualPointsPerBlock × 86,400    (1 sec/block)
Max daily points = actualPointsPerBlock × 432,000   (200ms/block)
```

#### Available Points Calculation

```
availablePoints = totalPoints[user] - spentPoints[user]
```

This balance includes points from both Kona Lend claims and Kona Swap farming. They're fungible once in the farm contract.

#### Enumerating Farms Programmatically

Builders can discover all active farms on-chain:

```javascript
const POINTS_FARM = '0x89e9d9b7D87591875359247f7d8eb786e7ea6910';

const farmCount = await publicClient.readContract({
  address: POINTS_FARM,
  abi: PointFarmingABI,
  functionName: 'getFarmsLength'
});

for (let i = 0; i < farmCount; i++) {
  const farm = await publicClient.readContract({
    address: POINTS_FARM,
    abi: PointFarmingABI,
    functionName: 'farms',
    args: [i]
  });
  
  const [lpToken, rawPointsPerBlock, totalStaked, , , active, minStake] = farm;
  const actualRate = Number(rawPointsPerBlock) / 100;
  
  console.log(`Farm ${i}: LP=${lpToken}, rate=${actualRate} pts/block, active=${active}`);
}
```

***

### Part 3: Converting Points to PEARL

The PearlMinter is the sole contract that creates PEARL. Every mint requires sufficient points in KonaPointsFarm **and** KONA approved to the minter for burning.

#### PearlMinter Functions

```solidity
// Mint PEARL by spending points and burning KONA
// Requires KONA approval to PearlMinter before calling
function mint(uint256 _pointsAmount) external returns (
    uint256 pearlsMinted,
    uint256 pointsSpent
);

// Mint using all available points
function mintAll() external returns (
    uint256 pearlsMinted,
    uint256 pointsSpent
);

// Preview mint output without executing
function estimateMint() external view returns (
    uint256 pearlOut,
    uint256 pointsToSpend,
    uint256 konaRequired
);

// Read current config (do NOT hardcode — these are governance-configurable)
function pointsPerPearl() external view returns (uint256);  // currently 100
function konaFeeAmount() external view returns (uint256);    // currently 1e18
function paused() external view returns (bool);

// Protocol stats
function totalKonaBurned() external view returns (uint256);
function totalPearlsMinted() external view returns (uint256);
function totalPointsConverted() external view returns (uint256);
```

**Events:**

```solidity
event PearlMinted(
    address indexed user,
    uint256 pearlsAmount,
    uint256 requiredPoint,
    uint256 konaBurned
);
```

#### Mint Execution Flow

```
1. User approves KONA to PearlMinter (konaFeeAmount, currently 1e18)
2. User calls mint(pointsAmount) or mintAll()
3. PearlMinter transfers KONA from user → burn address (0xdead)
4. PearlMinter calls PointsSpenderProxy.spendPoints(user, pointsAmount)
    └─→ KonaPointsFarm.spendPoints(user, pointsAmount)
        └─→ spentPoints[user] += pointsAmount
5. PearlMinter mints floor(pointsAmount / 100) PEARL to user
6. Emits PearlMinted(user, pearlsAmount, requiredPoint, konaBurned)
```

#### PointsSpenderProxy Functions

```solidity
// Get user's total available points (used by PearlMinter internally)
function getTotalPoints(address _user) external view returns (uint256);

// Spend points — only callable by authorized contracts (PearlMinter, etc.)
function spendPoints(address _user, uint256 _amount) external;

// Check if an address is authorized to spend points
function authorizedSpenders(address) external view returns (bool);
```

**Important:** Third-party contracts do not need to be authorized on the PointsSpenderProxy. You call `PearlMinter.mint()` or `PearlMinter.mintAll()` directly — the PearlMinter is already authorized and handles the proxy call internally. Only the protocol team can add new authorized spenders via the proxy owner.

#### Batching Economics

The KONA fee is fixed per transaction regardless of mint size. Larger mints are dramatically more capital efficient:

| Points    | PEARL Out | KONA Cost | Cost/PEARL (at $1 KONA) |
| --------- | --------- | --------- | ----------------------- |
| 100       | 1         | 1 KONA    | $1.0000                 |
| 10,000    | 100       | 1 KONA    | $0.0100                 |
| 100,000   | 1,000     | 1 KONA    | $0.0010                 |
| 1,000,000 | 10,000    | 1 KONA    | $0.0001                 |

**Accumulate points and mint in bulk.** There is no maximum mint size. Use `mintAll()` to convert your entire balance in one transaction.

***

### Part 4: Full Mint Integration (JS/viem)

```javascript
const PEARL_MINTER = '0x92C18C91Ad1356DDEA429Bf5C49949489Cea4608';
const POINTS_SPENDER = '0x7de407C3a07Da1CC405e024374186f9249d76B00';
const POINTS_FARM = '0x89e9d9b7D87591875359247f7d8eb786e7ea6910';
const KONA = '0x92aba186c85b5aFEB3a2CEdC8772Ae8638F1B565';

// 1. Check available points
const totalPoints = await publicClient.readContract({
  address: POINTS_SPENDER,
  abi: PointsSpenderProxyABI,
  functionName: 'getTotalPoints',
  args: [userAddress]
});

const spentPoints = await publicClient.readContract({
  address: POINTS_FARM,
  abi: PointFarmingABI,
  functionName: 'spentPoints',
  args: [userAddress]
});

const available = totalPoints - spentPoints;

// 2. Preview the mint
const [pearlOut, pointsToSpend, konaRequired] = await publicClient.readContract({
  address: PEARL_MINTER,
  abi: PearlMinterABI,
  functionName: 'estimateMint'
});

// 3. Check KONA balance
const konaBalance = await publicClient.readContract({
  address: KONA,
  abi: erc20Abi,
  functionName: 'balanceOf',
  args: [userAddress]
});

if (konaBalance < konaRequired) {
  throw new Error('Insufficient KONA — acquire before minting');
}

// 4. Approve KONA to PearlMinter
await walletClient.writeContract({
  address: KONA,
  abi: erc20Abi,
  functionName: 'approve',
  args: [PEARL_MINTER, konaRequired]
});

// 5. Mint PEARL (use mintAll() for full balance, or mint(amount) for partial)
const hash = await walletClient.writeContract({
  address: PEARL_MINTER,
  abi: PearlMinterABI,
  functionName: 'mintAll'
});
```

***

### Part 5: Solidity Integration Pattern

A complete vault/agent contract covering earn, harvest, and convert:

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

interface IPool {
    function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode) external;
    function withdraw(address asset, uint256 amount, address to) external returns (uint256);
}

interface IRewardsController {
    function getAllUserRewards(address[] calldata assets, address user) 
        external view returns (address[] memory, uint256[] memory);
    function claimAllRewardsToSelf(address[] calldata assets) 
        external returns (address[] memory, uint256[] memory);
}

interface IKonaPointsFarm {
    function stake(uint256 _farmId, uint256 _amount) external;
    function unstake(uint256 _farmId, uint256 _amount) external;
    function claimPoints(uint256 _farmId) external;
    function pendingPoints(uint256 _farmId, address _user) external view returns (uint256);
    function totalPoints(address user) external view returns (uint256);
    function spentPoints(address user) external view returns (uint256);
}

interface IPointsSpenderProxy {
    function getTotalPoints(address _user) external view returns (uint256);
}

interface IPearlMinter {
    function mint(uint256 _pointsAmount) external returns (uint256, uint256);
    function mintAll() external returns (uint256, uint256);
    function estimateMint() external view returns (uint256, uint256, uint256);
    function konaFeeAmount() external view returns (uint256);
    function paused() external view returns (bool);
}

contract KonaYieldAgent is Ownable {
    IPool public pool;                          // 0x8f16B5713F412C5dE4951Aaf678Eb8409101f819
    IRewardsController public rewardsCtrl;      // 0x4237d126713bf1FaBA29Ed8A55A6A0634886e223
    IKonaPointsFarm public pointsFarm;          // 0x89e9d9b7D87591875359247f7d8eb786e7ea6910
    IPointsSpenderProxy public spenderProxy;    // 0x7de407C3a07Da1CC405e024374186f9249d76B00
    IPearlMinter public pearlMinter;            // 0x92C18C91Ad1356DDEA429Bf5C49949489Cea4608
    IERC20 public kona;                         // 0x92aba186c85b5aFEB3a2CEdC8772Ae8638F1B565

    address[] public lendAssets;   // all 8 kToken + debtToken addresses
    uint256[] public activeFarms;  // farm IDs this agent stakes in

    constructor(
        address _pool,
        address _rewardsCtrl,
        address _pointsFarm,
        address _spenderProxy,
        address _pearlMinter,
        address _kona
    ) Ownable(msg.sender) {
        pool = IPool(_pool);
        rewardsCtrl = IRewardsController(_rewardsCtrl);
        pointsFarm = IKonaPointsFarm(_pointsFarm);
        spenderProxy = IPointsSpenderProxy(_spenderProxy);
        pearlMinter = IPearlMinter(_pearlMinter);
        kona = IERC20(_kona);
    }

    // ─── EARN (Lend) ───

    function lendDeposit(address asset, uint256 amount) external onlyOwner {
        IERC20(asset).approve(address(pool), amount);
        pool.supply(asset, amount, address(this), 0);
        // kTokens now held — points accrue automatically per second
    }

    function lendWithdraw(address asset, uint256 amount, address to) external onlyOwner {
        pool.withdraw(asset, amount, to);
    }

    // ─── EARN (Swap) ───

    function farmStake(uint256 farmId, address lpToken, uint256 amount) external onlyOwner {
        IERC20(lpToken).approve(address(pointsFarm), amount);
        pointsFarm.stake(farmId, amount);
    }

    function farmUnstake(uint256 farmId, uint256 amount) external onlyOwner {
        pointsFarm.unstake(farmId, amount);
    }

    // ─── HARVEST ───

    function harvestAll() external {
        // Harvest Kona Lend points
        if (lendAssets.length > 0) {
            rewardsCtrl.claimAllRewardsToSelf(lendAssets);
        }
        // Harvest Kona Swap farm points
        for (uint256 i = 0; i < activeFarms.length; i++) {
            pointsFarm.claimPoints(activeFarms[i]);
        }
    }

    // ─── CONVERT ───

    function mintPearl(uint256 pointsAmount) external onlyOwner {
        uint256 fee = pearlMinter.konaFeeAmount();
        require(kona.balanceOf(address(this)) >= fee, "Need KONA");
        require(!pearlMinter.paused(), "Minter paused");

        kona.approve(address(pearlMinter), fee);
        pearlMinter.mint(pointsAmount);
    }

    function mintAllPearl() external onlyOwner {
        uint256 fee = pearlMinter.konaFeeAmount();
        require(kona.balanceOf(address(this)) >= fee, "Need KONA");
        require(!pearlMinter.paused(), "Minter paused");

        kona.approve(address(pearlMinter), fee);
        pearlMinter.mintAll();
    }

    /// @notice Harvest everything then mint in one call
    function harvestAndMint() external onlyOwner {
        this.harvestAll();

        uint256 available = spenderProxy.getTotalPoints(address(this))
                          - pointsFarm.spentPoints(address(this));
        uint256 fee = pearlMinter.konaFeeAmount();

        if (available >= 100 && kona.balanceOf(address(this)) >= fee && !pearlMinter.paused()) {
            kona.approve(address(pearlMinter), fee);
            pearlMinter.mintAll();
        }
    }

    // ─── VIEW ───

    function availablePoints() external view returns (uint256) {
        return spenderProxy.getTotalPoints(address(this))
             - pointsFarm.spentPoints(address(this));
    }

    function konaReserves() external view returns (uint256) {
        return kona.balanceOf(address(this));
    }

    function previewMint() external view returns (
        uint256 pearlOut,
        uint256 pointsToSpend,
        uint256 konaRequired
    ) {
        return pearlMinter.estimateMint();
    }

    // ─── ADMIN ───

    function setLendAssets(address[] calldata _assets) external onlyOwner {
        lendAssets = _assets;
    }

    function setActiveFarms(uint256[] calldata _farmIds) external onlyOwner {
        activeFarms = _farmIds;
    }

    function rescue(address token, uint256 amount, address to) external onlyOwner {
        IERC20(token).transfer(to, amount);
    }
}
```

***

### Important Notes for Integrators

**Points are non-transferable.** They accrue to the address that holds kTokens or staked LP. If your vault contract holds the positions, the vault earns the points. Design your architecture accordingly.

**Claim Lend points explicitly.** Unlike Swap farms where points auto-update on stake/unstake, Lend points require an explicit `claimAllRewardsToSelf()` call. Lend rewards are returned as 18-decimal scaled values — divide by `1e18` for actual points.

**Batch your mints.** The fixed KONA fee per transaction means small mints are extremely capital-inefficient. Accumulate 10,000+ points before converting. Use `mintAll()` for maximum efficiency.

**`pointsPerPearl` and `konaFeeAmount` are configurable.** Read them from PearlMinter rather than hardcoding. They may change via governance.

**`pointsPerBlock` is scaled.** The raw value returned by `farms()` must be divided by 100 to get actual points per block.

**Third-party contracts don't need proxy authorization.** Call `PearlMinter.mint()` or `mintAll()` directly. The PearlMinter is already an authorized spender on the PointsSpenderProxy. You do not need to interact with the proxy yourself.

**KONA is the scarce resource.** Any contract can earn unlimited points. Converting them requires KONA, which is permanently deflationary. Every PEARL mint burns 1 KONA from a hard-capped supply that will never increase. Your KONA acquisition timing is the real alpha.
