Skip to content
Actuator
← Back to Guides

Checking a “No Admin Keys” Claim

intermediate~9 min read

Every chain read below was made on HEX day 2506 at block 27,782,703 (2026-10-13 00:24 UTC, the evening of October 12 in New York) through the public RPC at rpc.pulsechain.com, and every source line was checked the same day against the verified source PulseChain’s explorer publishes for that address (for HTT-7000, the source of HTT-3000, a contract with the same bytecode). Contract state can change; the reads are how to check it again.

What the Claim Says

“No admin keys” says that once a protocol is deployed, no address holds a special power over its contracts: nobody can change a setting, swap a component, pause, upgrade or drain them. That is a claim about code, and code on a public chain can be read. Every check on this page is a free read, on the explorer or through a public RPC, with no wallet connected.

Four reads cover most of it.

Read What it answers An admin-free answer
1. owner() Is there an owner address, and whose is it? The call fails (no such function) or returns 0x0000…0000 (renounced)
2. The ABI and verified source Which functions change state, and who may call each one? Each is open to any user, or gated to another contract of the same system
3. Who the timelock really is When a function is gated to an address named timelock or admin, what sits at that address? No such gate, or a real delay contract
4. Is the oracle verified? If prices come from an oracle contract, can anyone read its code? No oracle, or one with verified source and no owner

The Four Reads

1. owner(). Many contracts use an “Ownable” pattern in which owner() names one address with special powers. On the explorer, an address’s Contract tab (behind ··· on a narrow window) has a Read/Write contract tab; its Read filter lists every free read. The Write entries send transactions from a connected wallet; the reads need none. A zero address means the owner was renounced; no owner entry at all means the contract has no such function. Either fits “no admin keys”, and neither proves it: a contract can gate a power some other way, which is what read 2 is for.

2. The ABI and the verified source. The ABI is the list of a contract’s functions. Those marked view or pure only read; every other one changes state. In the verified source, the first lines of each state-changing function say who may call it, as a require(msg.sender == …) or a modifier such as onlyOwner. When the explorer has no verified source for an address, and no verified contract with the same bytecode, it shows bytecode only, and nobody outside the team can read what the code does.

3. Who the timelock really is. A gate named timelock suggests a delay: a contract that announces each change and makes it wait before it runs. The name proves nothing. Paste the address the gate returns into the explorer. An address with no code is a wallet, one key. A Safe is a multisig: its getThreshold() and getOwners() say how many signatures move it, and its modules and guard say whether anything delays it.

4. Is the oracle verified? A lending system values its collateral with a price oracle, so every collateral ratio, and every liquidation, runs on the oracle’s number. The oracle’s address is usually one read away, often a function named priceFeed(). Its Contract tab shows whether its source is verified, and its own owner() shows who controls it.

Worked Example: INC Printer, Where the Claim Does Not Hold

INC Printer is a Liquity fork on PulseChain. It lends INCD, an overcollateralized stablecoin it names “INC Dollar”, against INC locked in troves, and keeps Liquity’s two lines: on its TroveManager, MCR() returns 1.1 × 10¹⁸, a minimum collateral ratio of 110%, and CCR(), the Recovery Mode line, returns 1.5 × 10¹⁸, or 150%. Its app at incprinter.com describes the protocol as “Immutable. Governance-Free. No Admin Keys.”

The original Liquity, on Ethereum, is built that way. In its published source (github.com/liquity/dev), the TroveManager, BorrowerOperations and StabilityPool contracts renounce ownership once they are wired together, and none of the three contains a timelock or a function that replaces the price feed afterwards. Here is what the four reads return for the fork.

Read 1: owner() is zero on all three. TroveManager (0x248262ea52198643DD1512Ce7a2c93B32a03E45F), BorrowerOperations (0x41dCD47f482629E7F6dB339B523b590Fd8ED9Ed9) and StabilityPool (0xfd2689b36E34453CB844637a7c8f1406A56eDE92) each return 0x0000…0000. On this read alone, the fork looks like Liquity. The PRINT token has no owner function at all.

Read 2: one added function. The verified source of all three contracts carries a function that Liquity’s does not:

function updatePriceFeed(address _priceFeedAddress) onlyTimelock external override {
    checkContract(_priceFeedAddress);
    priceFeed = IPriceFeed(_priceFeedAddress);
    emit PriceFeedAddressChanged(_priceFeedAddress);
}

It sits at line 313 of TroveManager, line 173 of BorrowerOperations and line 339 of StabilityPool, in the source the explorer publishes for each. It replaces the contract that prices INC. The new price feed is the one the system reads when troves are opened, adjusted, closed and liquidated. This is the power that owner() does not show.

Read 3: the “timelock” is a 3-of-5 Safe. timelock() returns the same address on all three contracts, and the gate itself checks only that the caller is that address. The address is a Safe (the explorer has no verified source for the address itself, but matches its bytecode to a verified GnosisSafeProxy; VERSION() returns 1.3.0). getThreshold() returns 3, and getOwners() lists five addresses, none of which has code: five keys, any three of which can sign. The Safe has no modules enabled and no guard set, so nothing between the three signatures and updatePriceFeed adds a delay. It is a multisig; it is not a timelock.

Read 4: the oracle has no published source. priceFeed() returns 0xF03e136F1727eA3215e498e783eC2942AbA8213c on all three contracts. That address holds 18,535 bytes of code, and the explorer has no verified source for it. Its owner() returns the wallet that deployed it, an address with no code, and its own timelock() returns another address with no code. Its bytecode answers to function names from Compound’s open-source TWAP oracle, and anchorPeriod() returns 420, which in that design is the averaging window in seconds. Without verified source, nobody outside the team can confirm what the rest of the code does, or what its two keys can change.

What the four reads add up to. The token and the core contracts gave up their owner keys, as Liquity’s do. Two powers remain. Three of five keys can replace the price feed every trove is valued by, and the price feed installed today is code nobody outside its team can read, owned by one wallet. Neither power shows up in owner(). The app’s “No Admin Keys” describes Liquity’s design; the contracts as deployed carry an admin path.

Counter-Example: Actuator, Where the Claim Holds

Actuator’s own docs make the claim about its farms: the farm pools were chosen at launch and cannot be changed after deployment, “in order to eliminate admin keys and minimize the attack surface” (liquidity-mining docs). The same four reads, on the four contracts of its published source (the ACTR token, the HEXTimeTokenManager, the MasterChef farm and the HTTs, read here on HTT-7000), plus the team’s TimeLock:

Contract Address
ACTR 0x85DF7cE20A4CE0cF859804b45cB540FFE42074Da
HEXTimeTokenManager 0x0d5d61FDDf84feFAB26f98164D8009022d740206
MasterChef 0x4469A40D4243aC1c6cF350d99B6d69b49b5005F1
HTT-7000 0x47810bb3ECDc6b080CeB2d39E769F21Ff14AB7E9
TimeLock 0xFDA8246BcDb586c70afB9690cE1A4118F16901a1

Read 1. None of the five has an owner function; the call fails on every one.

Read 2. Four of the five have verified source (Solidity 0.8.24); HTT-7000 has none of its own, and the explorer shows the verified source of HTT-3000, a contract with the same bytecode. None is a proxy: the explorer reports no proxy and the standard upgrade slot reads empty. In the four protocol contracts, every state-changing function is either open to any caller under fixed rules, acting on the caller’s own stakes and tokens (staking, delegating, minting, retiring, redeeming, ending stakes, vault and farm deposits and withdrawals, transfers) or doing public upkeep anyone may run (ending a backing stake once its HTTs are redeemable, creating a new HTT series, updating the daily payout data and the farm pools), or gated to another contract of the same system, fixed when the contract was built, with no function to change it:

  • ACTR’s mint answers only to the MasterChef (masterChef() returns the MasterChef’s address).
  • An HTT’s mint and burn answer only to the HEXTimeTokenManager that created it, and its vault deposit and withdraw only to the ACTR contract.
  • The MasterChef has no outside function that adds a pool or changes a weight. Its pools are added and reweighted by private code, on a schedule fixed in its constructor, as each year of the farm calendar begins (poolLength() returns 6).

Read 3. No Actuator contract has a timelock or admin gate. The contract named TimeLock is something else: it holds the team’s ACTR. Its two state-changing functions, transferUnlockedFunds and transferTeamAddress, can be called only by the team’s address and move only the ACTR it holds. None of the four protocol contracts refers to it.

Read 4. None of the four protocol contracts reads a market price, so there is no oracle to verify. The MasterChef uses the PulseX factory only to look up each farm’s HTT/HEX pair address.

What these reads do not cover. Actuator’s contracts call the HEX and Hedron contracts; the reads above do not test those. HEX’s own source is read in Reading HEX from the Source. And no admin keys means nobody can change the rules; it says nothing about whether the rules themselves are sound, which is what audits test.

Check One Thing Yourself — Right Now

Open the INC Printer TroveManager on PulseChain’s explorer, go to Contract (behind ··· on a narrow window), then Read/Write contract, pick the Read filter, and read timelock. Paste the address it returns into the explorer’s search: the explorer has no verified source for it, and under Code it shows the GnosisSafeProxy source of a contract with the same bytecode. Its Read/Write contract tab lists no reads, because a Safe’s reads sit in the contract its proxy forwards to; the 3 of 5 above comes from calling getThreshold() and getOwners() on the address through the RPC. Then open the Actuator MasterChef the same way and look for an owner read: there is none. No wallet is needed, and the claim has been tested against the chain itself.


By HexBonds — read from PulseChain on HEX day 2506 (2026-10-13 UTC). No affiliation with INC Printer or Actuator; we quote the contracts, nothing more.

Suggest or Correct

Spotted an error?  Have an idea?  Found something missing?
Let us know.  This site is community-built and your input is welcome.

Anything sent here arrives as an email that a person reads.  A Seed Phrase sent here gives its wallet to whoever reads it.