TANITARDevelopers
Guides

Web3 verification

Check what code is deployed on each testnet your tenant uses, re-verify it on demand, and prove the chain works with a live self-test transaction.

When your tenant uses on-chain features (minting awards, crypto exchanges), you can check for yourself what Tanitar is running on each chain: which contracts or programs, whether their code matches what is expected, and whether the chain accepts a real transaction. Everything here runs on testnets (Ethereum Sepolia, Hedera testnet, Solana). Mainnet networks are refused everywhere, including in your own configuration.

All routes are in the rewards service (REWARD_API_URL) and accept an API key.

RouteScopeIdempotency-Key
GET /v1/web3/capabilitiesrewards:readNo
GET /v1/web3/deploymentsrewards:readNo
POST /v1/web3/deployments/verificationsrewards:mutateNo
POST /v1/web3/self-testsrewards:mutateRequired
GET /v1/web3/self-testsrewards:readNo
GET /v1/web3/self-tests/:selfTestIdrewards:readNo

Whose infrastructure

By default your tenant uses Tanitar's own testnet deployment. You may instead register your own testnet infrastructure for a chain (network, expected network identity, deployed contracts and a signer) from the dashboard; see Web3 configuration in the dashboard. That configuration is dashboard-only: it is written by an administrator of your organization, the signer key is accepted on write and never returned, and a mainnet network identity is refused before anything is stored.

The verification surface tells you which case applies through trustModel on each chain:

trustModelMeaning
defendr-deployed-artifact-verifiedTanitar's deployment, checked against the artifacts Tanitar built
customer-registered-defendr-conformance-verifiedYour registered deployment, checked against the code hash you registered and Tanitar's conformance probes. It is not an audit of your contracts.

Chain states

Each chain is in exactly one of two states, never in between:

  • verified: every artifact the chain lists was verified. Only a verified chain is advertised and used.
  • disabled: the chain is not configured, or one of its checks failed. reason gives a code when a check failed.

GET /v1/web3/capabilities is the short answer: { "chains": [ … ] } with one entry per usable chain and what it can do (fulfillment, exchange). A chain that is absent is disabled.

Read the deployment evidence

GET /v1/web3/deployments returns the evidence behind what your tenant is currently advertised, from the same checks that decide it:

{
  "source": "published-snapshot",
  "observedAt": "2026-09-30T08:00:12.402Z",
  "chains": [
    {
      "chain": "ethereum",
      "status": "verified",
      "reason": null,
      "networkIdentity": "11155111",
      "signer": "0x7a3B…",
      "capabilities": ["fulfillment", "exchange"],
      "trustModel": "defendr-deployed-artifact-verified",
      "checkedAt": "2026-09-30T08:00:11.980Z",
      "explorer": {
        "name": "Etherscan (Sepolia)",
        "transactionUrlTemplate": "https://sepolia.etherscan.io/tx/{signature}",
        "addressUrlTemplate": "https://sepolia.etherscan.io/address/{address}"
      },
      "artifacts": [
        {
          "name": "DefendrMedals",
          "role": "collection",
          "address": "0x1c9f…",
          "authority": "0x7a3B…",
          "authorityAgrees": true,
          "expectedCodeHash": "0x5e0d…",
          "observedCodeHash": "0x5e0d…",
          "codeHashAgrees": true,
          "executable": null,
          "source": "vendored-artifact-hash",
          "verified": true,
          "explorerUrl": "https://sepolia.etherscan.io/address/0x1c9f…"
        }
      ]
    }
  ]
}

For each artifact you see both hashes side by side: expectedCodeHash (what the code should be) and observedCodeHash (what the chain holds now; on EVM chains, the keccak-256 hash of the deployed bytecode). authority and authorityAgrees show whether the contract is controlled by the expected signer; on Solana, executable shows the program is deployable code. signer is the public address that signs your transactions; no key material or node address ever appears in a response.

Explorer links are built from the network identity the chain actually reported, so a link can never point at a different network than the one checked. No link is ever built for a mainnet.

Re-verify on demand

POST /v1/web3/deployments/verifications runs the same checks again, live, and returns the result in the same shape with "source": "re-verification".

  • It is read-only. It does not change what your tenant is advertised or how awards and exchanges are admitted; that stays on the published checks.
  • It does not take an Idempotency-Key: re-verifying is meant to be repeated.
  • Results are cached for 60 seconds per deployment. Requests inside that window get the cached answer without contacting the chain.

Use it to confirm a fix after you change your own deployment, or to see whether a node outage is still ongoing.

Run a self-test transaction

A self-test proves end to end that the chain accepts a transaction from your signer. It sends one zero-value native transfer from the signer to itself: no contract is called, nothing is minted, and only the network fee is spent.

curl -X POST "$REWARD_API_URL/v1/web3/self-tests" \
  -H "Authorization: Bearer $TANITAR_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"chain":"ethereum"}'

The body names only the chain (ethereum, hedera or solana); you cannot choose an amount or a destination. The self-test record is created before anything is signed, and its identity comes from your Idempotency-Key, so retrying with the same key returns the same record and never sends a second transaction.

Limits

These limits are fixed and cannot be changed by configuration:

LimitValue
Per account and chain1 self-test per 15 minutes
Per account, all chains10 self-tests per 24 hours
Signer balanceMust cover the estimated fee plus a safety reserve (0.001 ETH on Ethereum, 0.001 HBAR equivalent on Hedera, 0.005 SOL on Solana)

A signer that cannot cover the fee and reserve is answered 409 WEB3_SELF_TEST_UNDERFUNDED. No transaction is signed, but a failed record is kept and counts against both windows.

Statuses and refusals

A self-test is claimed, then submitted, then confirmed or failed. While it is claimed or submitted it has no explorerUrl; the link appears once the chain has answered.

StatuserrorCodeCause
400WEB3_SELF_TEST_IDEMPOTENCY_KEY_REQUIREDNo Idempotency-Key header
409WEB3_SELF_TEST_KEY_CONFLICTThe key was already used for a self-test on another chain
409WEB3_SELF_TEST_PENDINGThe transaction for this key is still awaiting confirmation; read it again shortly
409WEB3_SELF_TEST_UNDERFUNDEDThe signer cannot cover the fee and the reserve
422WEB3_SELF_TEST_CHAIN_UNAVAILABLEThe chain is not verified for your tenant
429WEB3_SELF_TEST_RATE_LIMITEDA self-test already ran on this chain in the last 15 minutes
429WEB3_SELF_TEST_QUOTA_EXHAUSTEDThe 10-per-24-hours limit is reached
503WEB3_SELF_TEST_BROADCAST_REFUSEDThe chain did not accept the transaction
503WEB3_SELF_TEST_CHAIN_UNREACHABLEThe chain did not answer; retry

The 429 answers here are the self-test's own limits, separate from your plan's rate limits, and like them they carry no Retry-After header.

Read self-tests

  • GET /v1/web3/self-tests returns your 50 most recent self-tests, newest first. Add chain to narrow to one chain.
  • GET /v1/web3/self-tests/:selfTestId returns one, or 404 WEB3_SELF_TEST_NOT_FOUND.

Each record shows the chain, networkIdentity, status, transaction signature, explorerUrl, estimatedFee, attemptCount, failureCode, confirmedAt and who requested it.

On this page