Read contractsLink to this section
TL;DRLink to this section
- Call the
CoreandMiningproxies 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
- 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.
- 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
TokenandNFTcode. - Read
Core's implementation metadata andMining'sdeploymentIdentity(). Check the actual implementation and the admin that handles proxy upgrades, then compare both implementations' code version and storage-layout digest. - Check that
Mining'sconfiguration()names the suppliedCore,Tokenand collection.Token'smining()and the collection'smining()must both name the suppliedMiningproxy. - Read
Corestate,Miningstate, position and ownership at the same block. Readclaimableonly 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;claimablerejects them andownerOfreverts. - Mixed, duplicate or descending IDs make
claimablefail. Core-dependent reads fail if the fixedCoreis 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.