Attestation
The program cannot read Collector Crypt's off-chain records, so an attestor key signs a statement about each card, and deposit accepts the card only with that signature. The statement binds the deployment, the card, its class, its record, its cert and an expiry.
On this page
The signed message
The message is 160 bytes.
| Offset | Size | Field |
|---|---|---|
| 0 | 8 | Domain CARDPAD1 |
| 8 | 32 | Vault config address, so a statement cannot be replayed against another deployment |
| 40 | 32 | Core asset address |
| 72 | 32 | class_id |
| 104 | 32 | json_hash |
| 136 | 16 | Grader cert number, ASCII, zero padded (truncated to 16 bytes if longer) |
| 152 | 8 | expires_at, unix seconds, little endian |
json_hash is the sha256 of the canonical string company|grade|cert|universal|itemName|metadataUri|insuredValue, built from Collector Crypt's record of the card. The program stores it with the copy. A later check re-reads the record and reports whether it changed since the deposit.
How the program checks it
The instruction before
The instruction immediately before
depositmust be an Ed25519 precompile instruction.One signature, nothing outside
It must carry exactly one signature, and its signature, public key and message offsets must all point into its own data.
The right key
The public key must be the vault config's attestor. Any other key fails with
WrongAttestor.The same deposit
The signed message must equal the message rebuilt from the deposit's own accounts and arguments, or it fails with
AttestationMismatch.Not expired
The deposit must land no later than
expires_at, or it fails withAttestationExpired.
What the attestor checks before it signs
The attestor service answers POST /attest with the asset and the depositor. It refuses, in this order:
| Refusal | Why |
|---|---|
| 400 | The body is not JSON, or the asset or depositor is not a public key |
| 404 | The asset is not found |
| 422 | Not a Metaplex Core asset in a collection |
| 422 | The asset is owned by someone other than the depositor |
| 422 | The vault is not initialized, or the collection is not accepted by the vault |
| 422 | The service's key is not the vault's attestor |
| 422 | Collector Crypt's record could not be read, or lacks the grader, grade, gemrate id or cert |
| 404 | No class exists yet for this card's identity |
Collector Crypt's record must also name the same asset, and its metadata URI must equal the asset's on-chain URI. A | in any identity field is refused.
On success it returns the identity, class, mint, json_hash, cert, expiry, the 160-byte message and the signature. The statement expires 120 seconds after it is signed, as the service runs.
The rest of this path
Written from the protocol, security and card documents of the pokedex repository and from its code (card-vault, card-launch, cp-ix). Every figure on these pages comes from there.