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.
| Route | Scope | Idempotency-Key |
|---|---|---|
GET /v1/web3/capabilities | rewards:read | No |
GET /v1/web3/deployments | rewards:read | No |
POST /v1/web3/deployments/verifications | rewards:mutate | No |
POST /v1/web3/self-tests | rewards:mutate | Required |
GET /v1/web3/self-tests | rewards:read | No |
GET /v1/web3/self-tests/:selfTestId | rewards:read | No |
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:
trustModel | Meaning |
|---|---|
defendr-deployed-artifact-verified | Tanitar's deployment, checked against the artifacts Tanitar built |
customer-registered-defendr-conformance-verified | Your 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.reasongives 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:
| Limit | Value |
|---|---|
| Per account and chain | 1 self-test per 15 minutes |
| Per account, all chains | 10 self-tests per 24 hours |
| Signer balance | Must 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.
| Status | errorCode | Cause |
|---|---|---|
400 | WEB3_SELF_TEST_IDEMPOTENCY_KEY_REQUIRED | No Idempotency-Key header |
409 | WEB3_SELF_TEST_KEY_CONFLICT | The key was already used for a self-test on another chain |
409 | WEB3_SELF_TEST_PENDING | The transaction for this key is still awaiting confirmation; read it again shortly |
409 | WEB3_SELF_TEST_UNDERFUNDED | The signer cannot cover the fee and the reserve |
422 | WEB3_SELF_TEST_CHAIN_UNAVAILABLE | The chain is not verified for your tenant |
429 | WEB3_SELF_TEST_RATE_LIMITED | A self-test already ran on this chain in the last 15 minutes |
429 | WEB3_SELF_TEST_QUOTA_EXHAUSTED | The 10-per-24-hours limit is reached |
503 | WEB3_SELF_TEST_BROADCAST_REFUSED | The chain did not accept the transaction |
503 | WEB3_SELF_TEST_CHAIN_UNREACHABLE | The 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-testsreturns your 50 most recent self-tests, newest first. Addchainto narrow to one chain.GET /v1/web3/self-tests/:selfTestIdreturns one, or404 WEB3_SELF_TEST_NOT_FOUND.
Each record shows the chain, networkIdentity, status, transaction signature, explorerUrl,
estimatedFee, attemptCount, failureCode, confirmedAt and who requested it.
Membership tiers and rename pricing
Define your own membership tiers, grant and revoke them per player, and set the price a player pays to change their nickname.
Dead-letter recovery
Inspect the messages that exhausted their retries on your tenant, replay or discard them, revive stuck outbox records and schedules, and redispatch archived events.