Skip to content
Integration guide Mining5 source only Source e0617449

Read contractsLink to this section

TL;DRLink to this section

  • Call the Core and Mining proxies for application state, not their implementation addresses.
  • Verify the chain, addresses and deployed code before reading positions or balances.
  • Mining5 views differ from Mining4; dev's API and generated bindings are not Mining5 integration evidence.

Before you startLink to this section

Obtain an independently approved deployment record: chain ID, proxy addresses, Token, NFT, implementations, runtime hashes, storage-layout digests and selected Ethereum block. A version number alone does not verify the code.

Use an RPC for that chain and the ABI from the pinned source/build. Mining5 changes include finalizedThroughRound, lastActivationRound, weightActivationCursor and pendingWeight; do not decode it with Mining4's tuples or event ABI.

StepsLink to this section

  1. Require the expected chain ID and choose one Ethereum block for every read. If the RPC lacks historical state at that block, stop rather than mixing latest and historical values.
  2. Check that each supplied address contains code matching the approved deployment record. Account for the admin address embedded in proxy code and the immutable values embedded in Token and NFT code.
  3. Read Core's implementation metadata and Mining's deploymentIdentity(). Check the actual implementation and the admin that handles proxy upgrades, then compare both implementations' code version and storage-layout digest.
  4. Check that Mining's configuration() names the supplied Core, Token and collection. Token's mining() and the collection's mining() must both name the supplied Mining proxy.
  5. Read Core state, Mining state, position and ownership at the same block. Read claimable only with strictly ascending live IDs, at most 32.

Read-only exampleLink to this section

This read-only example requires an expected chain ID and caller-supplied addresses. Run the code and layout checks above first: the example checks versions and contract connections, not security.

import { createPublicClient, getAddress, http, parseAbi } from "viem";

const required = (name: string) => {
  const value = process.env[name];
  if (!value) throw new Error(`Supply ${name}`);
  return value;
};
const core = getAddress(required("CORE_ADDRESS"));
const mining = getAddress(required("MINING_ADDRESS"));
const token = getAddress(required("TOKEN_ADDRESS"));
const collection = getAddress(required("COLLECTION_ADDRESS"));
const expectedChainId = BigInt(required("EXPECTED_CHAIN_ID"));
const tokenId = BigInt(required("TOKEN_ID"));
const client = createPublicClient({ transport: http(required("RPC_URL")) });
if (BigInt(await client.getChainId()) !== expectedChainId) {
  throw new Error("Chain mismatch");
}
const blockNumber = await client.getBlockNumber();
const abi = parseAbi([
  "function implementationCodeVersion() view returns (uint64)",
  "function core() view returns (address)",
  "function mining() view returns (address)",
  "function sqkCore() view returns (address)",
  "function ownerOf(uint256 tokenId) view returns (address)",
  "function claimable(uint256[] tokenIds) view returns (uint256)",
  "function position(uint256 tokenId) view returns ((uint64 activeWeight, uint64 pendingWeight, uint40 pendingRound, uint48 nextMergeBlock, uint8 level, uint256 lastIndex, uint256 accruedScaled))",
]);
const coreVersion = await client.readContract({
  address: core, abi, functionName: "implementationCodeVersion", blockNumber,
});
const miningVersion = await client.readContract({
  address: mining, abi, functionName: "implementationCodeVersion", blockNumber,
});
if (coreVersion !== 2n || miningVersion !== 5n) throw new Error("Version mismatch");
const same = (left: string, right: string) => left.toLowerCase() === right.toLowerCase();
for (const address of [token, collection]) {
  const bound = await client.readContract({
    address, abi, functionName: "mining", blockNumber,
  });
  if (!same(bound, mining)) throw new Error("Mining binding mismatch");
}
const boundCore = await client.readContract({
  address: mining, abi, functionName: "core", blockNumber,
});
const collectionCore = await client.readContract({
  address: collection, abi, functionName: "sqkCore", blockNumber,
});
if (!same(boundCore, core) || !same(collectionCore, core)) throw new Error("Core mismatch");
const owner = await client.readContract({
  address: collection, abi, functionName: "ownerOf", args: [tokenId], blockNumber,
});
const position = await client.readContract({
  address: mining, abi, functionName: "position", args: [tokenId], blockNumber,
});
const claimableBaseUnits = await client.readContract({
  address: mining, abi, functionName: "claimable", args: [[tokenId]], blockNumber,
});
console.log({ blockNumber, owner, position, claimableBaseUnits });

Expected resultLink to this section

Read Meaning
position.activeWeight Mining power currently used to share funded rewards
pendingWeight, pendingRound Replacement mining power and its activation round; zero round means no pending change
nextMergeBlock Earliest completed Core block height for another merge, not an Ethereum block
accruedScaled Unclaimed reward credit scaled by 2^128, not whole SQK
claimable(ids) Payout in SQK base units across the selected live IDs, without checking ownership
accounting() Actual Token balance, accounted-for reward balance and reserved rounding remainders

position() includes weight changes already applied by Mining and calculates reward credit without calling Core or writing state. Reading it does not process new Core rounds or fund new rewards.

Common failuresLink to this section

  • Absent or burned positions return Level 0 from position; claimable rejects them and ownerOf reverts.
  • Mixed, duplicate or descending IDs make claimable fail.
  • Core-dependent reads fail if the fixed Core is unavailable or returns malformed data.
  • A safe API snapshot and a latest RPC read describe different Ethereum blocks.
  • If implementation metadata disagrees with the approved code, stop; do not try a different ABI.

Verify the resultLink to this section

Record the chain ID, Ethereum block number/hash, contract addresses and ABI revision with the output. Check Token.totalSupply() == Mining.state().minted and trackedCustody == minted - paid; donations increase the actual balance without funding claims.

For an owner action, repeat current ownership and simulation immediately before signing. A read-only claim estimate does not establish that the caller owns every selected ID.

SourceLink to this section

Mining5 source specification e0617449. First-party source is private; see source and release scope.