Voidly Pay · verify it yourself
Do not take our word for any of this.
On 26 August 2026 Voidly settled a payment in USDC on Base mainnet over its own session rail, and the provider delivered the work that was paid for. Every claim in that sentence is checkable without asking us anything. Below are five read-only commands. They need curl, jq and node; they change nothing, they cost nothing, and for the parts that matter they do not touch a Voidly endpoint at all.
The only thing you have to trust is the chain.
Read this before citing anything below
Both sides of this transaction are ours.
The hirer is the founder's wallet. The provider is Voidly's own session daemon. This is a proving payment and it is dogfooding — the record calls it arranged-first-party in its own schema. It is not a customer, it is not a sale, and it is not evidence that anyone wants this. Read as demand, it is worthless.
What it does prove is narrower and still worth stating: the rail moved real money on a real chain, verified the payment without ever holding or submitting it, and delivered the paid-for work. That is the claim on this page. n = 1 — one payment, 0.05 USDC. There is no rate, no total and no trend here, because a single arranged transaction cannot support one.
What the receipt says
The canonical record is /pay-first-settlement.json, served as a static file. Nothing in it is hand-written; it is the run's own output. The five checks exist to confirm it against sources that are not ours.
- transaction
- 0xb1ac733095c19e2e4829a3d448a02b8297d08e55f98678adfcba2e3e92747a3a
- chain
- eip155:8453 — Base mainnet
- block
- 50498854
- block time
- 2026-08-26T23:30:55Z
- asset
- USDC · 0x833589fc…bda02913
- amount
- 50000 atomic = 0.05 USDC
- payer
- 0x5cad296e…d5965af0 — the founder’s wallet
- payee
- 0xb0b3fca9…4ebd4912 — Voidly’s own session daemon
- service
- voidly.observatory.query/v1
- agreement class
- arranged-first-party
- binding ref
- 02467d7f0144886c4d5d66c0395a43158b073a380cd49b727566eafc5c7f8e4d
- delivered
- true
Why the addresses are cut short
No page on this site prints a full wallet address, so that nothing here can be mistaken for a payment destination. You are not being asked to take the elided halves on faith: the commands below pull the addresses out of the chain on your machine and print them in full if you ask. What you should check is the last eight hex characters — the payee ends 4ebd4912 — and, more to the point, that the manifest and the chain agree, which check 5 does for you.
Or just open it in a block explorer: basescan.org. The explorer is a third party we do not control, which is the point of naming it — but it is also a website, and the commands below are the version of this that does not require you to trust a website either.
The five checks
Checks 1–3 establish what the provider published and signed. Checks 4–5 establish what the chain recorded. The whole argument is that the second matches the first: the payee address and the amount were signed and public before the money moved, so we could not have chosen them afterwards to fit the transaction.
Fetch the signed provider manifest
This is the document the provider publishes about itself: who it is, which key speaks for it, where to pay, and how little it will accept. It is served from the provider host directly — not through an API of ours that could shape the answer per caller.
curl -s https://intelligence.voidly.ai:8443/.well-known/voidly-session-provider.json \
| jq '{provider_did, signing_public_key_base64,
payee_chain: (.services[0].price.payee_account | split(":")[0:2] | join(":")),
payee_ends: (.services[0].price.payee_account | .[-8:]),
min_amount: .services[0].price.min_amount,
signed: (.signature_base64 | length > 0)}'{
"provider_did": "did:voidly:6rGTFa5apSnKNF14bGXZfu",
"signing_public_key_base64": "L16pOb+7U0Qjgs43s61D8KiLi6KRAJ1CpqszP6FzCyE=",
"payee_chain": "eip155:8453",
"payee_ends": "4ebd4912",
"min_amount": "50000",
"signed": true
}payee_ends and min_amount are the two values checks 4 and 5 come back for. Print .services[0].price.payee_account instead of the slice if you want the whole address in your own terminal.
Verify the Ed25519 signature over the document
A manifest served over HTTPS proves only that the host serving it said so. The signature is over the JCS-canonical JSON of every other field, under the key the document itself advertises. If it verifies, the payee address and the price band you just read are the ones the key-holder committed to.
cat > verify-manifest.js <<'EOF'
const crypto = require('crypto')
const URL = 'https://intelligence.voidly.ai:8443/.well-known/voidly-session-provider.json'
// RFC 8785 JCS: keys sorted by code unit, no whitespace, and nothing else.
const jcs = (v) =>
Array.isArray(v) ? '[' + v.map(jcs).join(',') + ']'
: v && typeof v === 'object'
? '{' + Object.keys(v).sort().map((k) => JSON.stringify(k) + ':' + jcs(v[k])).join(',') + '}'
: JSON.stringify(v)
;(async () => {
const doc = await (await fetch(URL)).json()
const { signature_base64, ...signed } = doc
// Raw 32-byte Ed25519 key -> SPKI, so node's verifier will accept it.
const key = crypto.createPublicKey({
key: Buffer.concat([
Buffer.from('302a300506032b6570032100', 'hex'),
Buffer.from(doc.signing_public_key_base64, 'base64'),
]),
format: 'der',
type: 'spki',
})
const ok = crypto.verify(null, Buffer.from(jcs(signed), 'utf8'), key,
Buffer.from(signature_base64, 'base64'))
console.log('signature valid:', ok)
process.exit(ok ? 0 : 1)
})()
EOF
node verify-manifest.jssignature valid: trueA check that always returns true is not a check. Bend one field before verifying and it must fail — including changing the last hex digit of the payee address, which is the smallest edit a thief would need:
node -e '
const c = require("crypto"), U = "https://intelligence.voidly.ai:8443/.well-known/voidly-session-provider.json";
const jcs = v => Array.isArray(v) ? "[" + v.map(jcs).join(",") + "]"
: v && typeof v === "object"
? "{" + Object.keys(v).sort().map(k => JSON.stringify(k) + ":" + jcs(v[k])).join(",") + "}"
: JSON.stringify(v);
(async () => {
const doc = await (await fetch(U)).json();
const key = c.createPublicKey({ key: Buffer.concat([Buffer.from("302a300506032b6570032100","hex"),
Buffer.from(doc.signing_public_key_base64,"base64")]), format: "der", type: "spki" });
const ck = d => { const { signature_base64, ...s } = d;
return c.verify(null, Buffer.from(jcs(s),"utf8"), key, Buffer.from(signature_base64,"base64")); };
const bend = f => { const d = structuredClone(doc); f(d); return ck(d); };
console.log("untouched :", ck(structuredClone(doc)));
console.log("min_amount 50000+1 :", bend(d => d.services[0].price.min_amount = "50001"));
console.log("payee last byte :", bend(d => d.services[0].price.payee_account =
d.services[0].price.payee_account.slice(0, -1) + "3"));
console.log("provider_did :", bend(d => d.provider_did = "did:voidly:AAAAAAAAAAAAAAAAAAAAAA"));
})()'untouched : true
min_amount 50000+1 : false
payee last byte : false
provider_did : falseRe-derive the provider DID from that same key
The identifier is not an assigned name that some registry of ours has to vouch for — it is base58 of the first 16 bytes of the signing key, so it can be recomputed offline. If the derived value matches the one in the document, the name and the key cannot be separated by anyone who does not hold the key.
cat > derive-did.js <<'EOF'
const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
const b58 = (buf) => {
let n = BigInt('0x' + buf.toString('hex')), s = ''
while (n > 0n) { s = ALPHABET[Number(n % 58n)] + s; n /= 58n }
for (const b of buf) { if (b) break; s = '1' + s }
return s
}
;(async () => {
const url = 'https://intelligence.voidly.ai:8443/.well-known/voidly-session-provider.json'
const doc = await (await fetch(url)).json()
const key = Buffer.from(doc.signing_public_key_base64, 'base64')
const derived = 'did:voidly:' + b58(key.subarray(0, 16))
console.log('derived from key:', derived)
console.log('claimed in doc: ', doc.provider_did)
console.log('match: ', derived === doc.provider_did)
process.exit(derived === doc.provider_did ? 0 : 1)
})()
EOF
node derive-did.jsderived from key: did:voidly:6rGTFa5apSnKNF14bGXZfu
claimed in doc: did:voidly:6rGTFa5apSnKNF14bGXZfu
match: trueTruncating to 16 bytes is a real trade and worth naming: this is a 128-bit identifier, not a 256-bit one. It is short enough to read aloud, and the full key sits in the same document for anyone who wants all 32 bytes.
Pull the transaction receipt from a Base RPC
That the transaction exists, succeeded, and is in a block. mainnet.base.org is the public endpoint and is not ours; substitute any Base RPC you prefer and the answer is identical, which is the property that makes this worth doing at all.
curl -s https://mainnet.base.org \
-H 'content-type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"eth_getTransactionReceipt",
"params":["0xb1ac733095c19e2e4829a3d448a02b8297d08e55f98678adfcba2e3e92747a3a"]}' \
| jq 'def hex: ltrimstr("0x") | explode
| reduce .[] as $c (0; . * 16 + (if $c < 58 then $c - 48 else $c - 87 end));
def short: .[0:10] + "…" + .[-8:];
.result
| {status, block: (.blockNumber | hex), from: (.from | short), to: (.to | short)}'{
"status": "0x1",
"block": 50498854,
"from": "0x5cad296e…d5965af0",
"to": "0x833589fc…bda02913"
}status: "0x1" is success. Drop the | short filter to see from and to in full. to is the USDC contract rather than the payee because the transfer was authorised off-chain and submitted against the token itself — which is what check 5 unpacks.
Cross-check the chain against the signed document
This is the check that carries the page. It fetches three things that do not trust each other — the provider's signed manifest, our published record, and a Base RPC — and asserts they agree. Nothing is compared against a value printed on this page.
cat > cross-check.js <<'EOF'
// Fetches three things that do not trust each other: the provider's signed
// manifest, Voidly's published settlement record, and a Base RPC. Then it checks
// they agree. Addresses are printed with the middle elided; drop the elide()
// call to see them whole.
const MANIFEST = 'https://intelligence.voidly.ai:8443/.well-known/voidly-session-provider.json'
const RECORD = 'https://voidly.ai/pay-first-settlement.json'
const RPC = 'https://mainnet.base.org'
const TRANSFER = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
const AUTH_USED = '0x98de503528ee59b575ef0c0a2576a82497bfc029a5685b209e9ec333479b10a5'
const elide = (s) => s.slice(0, 10) + '…' + s.slice(-8)
const topicAddr = (t) => '0x' + t.slice(26)
const row = (label, ok) => console.log(label.padEnd(46), ok ? 'MATCH' : 'MISMATCH')
;(async () => {
const manifest = await (await fetch(MANIFEST)).json()
const record = await (await fetch(RECORD)).json()
const receipt = (await (await fetch(RPC, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0', id: 1, method: 'eth_getTransactionReceipt',
params: [record.result.txHash],
}),
})).json()).result
const price = manifest.services[0].price
// "eip155:8453:0x…" -> "0x…"
const signedPayee = price.payee_account.split(':').pop().toLowerCase()
const transfer = receipt.logs.find((l) => l.topics[0] === TRANSFER)
const auth = receipt.logs.find((l) => l.topics[0] === AUTH_USED)
const chainPayee = topicAddr(transfer.topics[2])
const chainAmount = BigInt(transfer.data).toString()
const chainNonce = auth.topics[2].replace(/^0x/, '')
console.log('tx ', elide(record.result.txHash))
console.log('block ', Number(receipt.blockNumber))
console.log('receipt status ', receipt.status, receipt.status === '0x1' ? '(success)' : '(FAILED)')
console.log('token contract ', elide(transfer.address))
console.log('paid to ', elide(chainPayee), '<- read off the chain, not off this page')
console.log('amount (atomic) ', chainAmount, '=', Number(chainAmount) / 1e6, 'USDC')
console.log('')
row('signed payee_account == chain recipient', signedPayee === chainPayee)
row('signed min_amount == chain amount', price.min_amount === chainAmount)
row('record bindingRef == chain auth nonce', record.result.bindingRef === chainNonce)
row('record txHash == receipt txHash', record.result.txHash === receipt.transactionHash)
const all = signedPayee === chainPayee && price.min_amount === chainAmount
&& record.result.bindingRef === chainNonce
&& record.result.txHash === receipt.transactionHash
process.exit(all && receipt.status === '0x1' ? 0 : 1)
})()
EOF
node cross-check.jstx 0xb1ac7330…92747a3a
block 50498854
receipt status 0x1 (success)
token contract 0x833589fc…bda02913
paid to 0xb0b3fca9…4ebd4912 <- read off the chain, not off this page
amount (atomic) 50000 = 0.05 USDC
signed payee_account == chain recipient MATCH
signed min_amount == chain amount MATCH
record bindingRef == chain auth nonce MATCH
record txHash == receipt txHash MATCHThe receipt carries two USDC events, and both point at things that existed before the block did. The Transfer log's recipient is the address inside the manifest's signed payee_account, and its amount is exactly the signed min_amount of 50000 atomic. The AuthorizationUsed log's nonce is the settlement record's bindingRef — the record stores it without the 0x prefix and is otherwise byte-identical. That nonce is what makes the authorisation single-use: USDC will not accept it a second time.
So the destination and the price were published and signed first, and the chain independently records money arriving at that destination, in that amount, under a nonce the session had already bound. A forged receipt would have to have been signed before the block it claims to explain.
What none of this proves
A verify page that lists only what its checks establish is doing half the job. Here is the other half.
- A valid signature means “I reached who I named”, never “who I named is honest.” Checks 2 and 3 rule out a host in the middle swapping the payee address. They say nothing at all about whether the key-holder will do the work, do it well, or tell you the truth about it. Those are different problems, and this page does not solve them.
- There is no refund, dispute or reversal on this path. The manifest states its own terms in the field
payment_buys: “an attempt, not an outcome”. Once redemption succeeds the grant is spent. A failed attempt comes back as a signed, sealed failure result — auditable, and still not your money back. - It did not settle over a public route. The four public session doors are closed. A
GETon/v1/pay/session/redeem,/deliver,/recoverand/reattesteach answerHTTP 410, measured the same day as the settlement. This payment went straight to the provider daemon. You cannot reproduce it today by pointing a client at a published endpoint, and any sentence implying you could would be false. - Two DIDs have ever touched this rail — the hirer and the provider — and both were hand-registered by us. Any larger identity or registry count you find elsewhere on this site counts something else entirely, and is not a population of users of this.
- The chain is permanently public. What these checks confirm, they confirm for everyone, forever: payer, payee, amount and time are published by construction. The brief and the result are not on the chain, but the fact that two addresses transacted is, and no later decision can withdraw it.
The part a tidier receipt would have deleted
It did not work the first time.
The first redeem attempt was refused settlement_indeterminate, roughly 26 seconds after broadcast, at about 13 confirmations against a 12-confirmation floor. The daemon did not retry on its own. A second settlement hint sent with a current clock — once all three configured RPC operators independently reported the receipt final at 67 confirmations — carried it to delivered.
This is recorded in the _stall field of the settlement record, and it is on this page for the same reason: a receipt that hides its own retry is a worse receipt. The refusal was also the system behaving correctly — it declined to call a payment final while it was unsure — but the missing automatic retry is a real gap, and stating it here costs less than having someone find it.
Something did not match?
Then one of two things is true: this page is wrong, or the manifest has been rotated since it was written. Both are worth knowing about and the first is worth more. Every output above was produced by running the command immediately before publishing it — so if yours differs, the difference is the interesting artifact, not the noise.