Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Migrate to the reset ABI document

Pina 0.20 replaced the ABI document’s two integer counters with one abiVersion pinned to the pina_abi release line, and removed every derived field from the document. The old documents are not readable by the new release — not rejected as unsupported, but unparseable — so a program with checked-in migration documents must convert them once.

Your on-chain bytes are untouched. Discriminators, version envelopes, PinaPod payloads, and generated clients are identical before and after. This migration rewrites JSON files on disk; no account data moves and no instruction changes. If your program never enabled migrations, there is nothing to do.

The design is recorded in ADR 0009, and the versioning rules it introduced are in ABI document versioning.

The current release writes abiVersion 0.21. The conversion below still produces 0.20 documents. Step 3 rewrites the ledger at the current version, and every reader converts the manifest in memory until the next pina migrations create writes it at the current version. 0.21 also changed how instructions and events are recorded, so read Upgrading from ABI 0.20 once this guide is done.

The symptom

After upgrading, the first Pina command that reads a migration document fails:

error: Could not decode migration file migrations/manifest.json: migration manifest is
missing a string `abiVersion` field

That is the old document. The new reader fails closed on it because every removed field is rejected by deny_unknown_fields, and the version key itself was renamed. There is no converter from the integer formats: they described a document shape nothing external ever consumed, so the reset retires them instead of migrating them.

Choose a path

SituationPath
migrations never enablednothing to do
Enabled, program never deployed, draft history is disposableStart fresh
Enabled and deployed, or history worth keepingKeep your history

A program that was never deployed can always start fresh: version numbers have no on-chain meaning until an account is written. A deployed program must keep its history — accounts on chain carry the version numbers your manifest recorded, and resetting that history makes every existing account read as a future version the program refuses to load.

Either way, first delete version_type (or version-type) and auto from the [migrations] table of pina.toml, keeping any [migrations.answers]. Current releases record the version width and the auto policy only in the manifest and refuse both keys, naming the pina migrations create flag and value that record the same setting.

Start fresh

From the program directory:

rm -rf migrations
pina migrations create --no-interactive --auto true --version-type u8
pina migrations check

Pass the --auto and --version-type values your pina.toml used to set, or omit either flag for no auto policy and u8 versions. create rebuilds the manifest from source at the current abiVersion, regenerates tests/abi_layout.rs, and recreates the publication ledger on your next deploy. Draft version history collapses to version zero — which is exactly why this path is for programs nothing has been deployed to yet.

Keep your history

Two edits and one repair command, then the normal verify loop. Work from the program directory.

1. Convert the manifest

Save this as convert-manifest.jq:

del(.formatVersion)
| .abiVersion = "0.20"
| .contracts |= with_entries(
	.value.versions |= map(
		del(.version, .schemaSha256, .processSha256)
		| .schema |= del(.physical)
		| .transition |= del(
			.from,
			.to,
			.sourceSchemaSha256,
			.destinationSchemaSha256,
			.sourceProcessSha256,
			.destinationProcessSha256,
			.process
		)
		| if .process then .process.accounts |= map(del(.constraints)) else . end
	)
)

Every deleted key is a field the new model derives on load: the version number is the entry’s position, the hashes are computed from the decoded content, the physical descriptor comes from the field grammar, and a transition’s adjacency is implied by the version it sits on. Apply it:

jq -f convert-manifest.jq migrations/manifest.json > manifest.next &&
	mv manifest.next migrations/manifest.json

2. Convert the publication ledger

Save this as convert-publications.jq:

del(.formatVersion)
| .abiVersion = "0.20"
| .receipts |= map(
	del(.cluster)
	| .versions |= with_entries(.value |= {version: .version, history: []})
)
| if .pending then
	.pending.versions |= with_entries(.value |= {version: .version, history: []})
else
	.
end
jq -f convert-publications.jq migrations/publications.json > publications.next &&
	mv publications.next migrations/publications.json

Two things happen here, and both are deliberate. The cluster label is gone from receipts — the credential-free rpcUrl is the record now, and the pending record keeps its label because pina deploy matches on it. And each receipt’s history is emptied: the old pins hashed a document shape that no longer exists, so keeping them would fail every future check, while emptying them keeps what matters — the recorded version, which the next step pins again.

3. Pin the receipts

A current ledger must pin every version each receipt made live, so every command refuses the emptied histories until they are pinned again, naming the repair:

publication ledger 0.20 cannot be upgraded: receipt 0 names `account:1:01` without pinning its
published schemas. Confirm with version control that migrations/manifest.json still records exactly
what was deployed, then run `pina migrations reconcile --pin-legacy` to pin it

Once version control confirms that the manifest you converted in step 1 still describes exactly what those receipts made live, run:

pina migrations reconcile --pin-legacy

For each emptied entry it pins versions 0 through the recorded version from the manifest: the schema hash of each and, when it has one, the hash of the transition that enters it. That is trust on first use, which is why the confirmation comes first. The command writes the ledger in the current shape, so sequence, programId, manifestSha256, the previousReceiptSha256 chain link, and each contract’s version are gone and each contract becomes its list of pins. Nothing checks the old chain, so the edit in step 2 needs no chain repair.

4. Regenerate and verify

pina migrations create --no-interactive
pina migrations check
pina migrations status

create rewrites the manifest canonically and regenerates tests/abi_layout.rs. check must pass before anything builds. status is your proof the conversion preserved state: every contract shows the same version number it showed before the upgrade, and deployed contracts still read published. Confirm one live account still loads with pina migrations inspect <ADDRESS>, then commit the rewritten documents.

What pina_abi library consumers must change

If you import pina_abi directly — a custom indexer, a verification tool — the API moved from stored fields to derived values. The mapping:

RemovedReplacement
MANIFEST_FORMAT_VERSION, PUBLICATION_FORMAT_VERSIONABI_VERSION, ABI_VERSION_KEY, ABI_OLDEST_SUPPORTED
manifest.format_version: u32manifest.abi_version: String
SchemaVersion::versionthe entry’s index; ContractHistory::current_version(), .version(n)
SchemaVersion::schema_sha256method schema_sha256()
SchemaVersion::process_sha256method process_sha256()
DataSchema::physicalmethod physical() -> Result<PhysicalLayout, String>
Transition::from / ::toimplied: the transition on index i converts i - 1 into i
Transition neighbour hashes and process proofre-derive: ContractHistory::process_transition_into(n)
ProcessAccount::constraintsremoved — validation rules live in the IDL
PublicationReceipt::clusterremoved — receipts keep rpc_url; the pending record keeps cluster
encode_manifest_for_format, convert_manifest_format, convert_publication_ledger_formatdeleted — use encode_manifest, encode_publication_ledger

Added: walk_document and AbiStep for converter tables, parse_document_version and current_abi_version for comparisons, and document_schema / render_document_schema / schema_url behind the new pina abi schema command.

0.21 changed the API again. DataCodec and DataSchema::codec are removed: the wire codec is SCHEMA_CODEC, implied by abiVersion. ContractHistory::identity is no longer serialized and is filled from the contract key when a manifest is read (ContractIdentity::from_key parses one). ContractHistory gains envelope and is_migrated(). AbiStep carries one converter per document (manifest and publications), and walk_document takes an AbiDocument instead of a label.

The 0.21 ledger keeps only what nothing else records. PublicationReceipt and PendingPublication lose sequence, program_id, manifest_sha256, and previous_receipt_sha256, and PublicationReceipt::sha256 is removed. PublishedContract serializes as its list of pins: its version field and PublishedContract::legacy are gone, version() returns the position of the last pin, and pins(n) reports whether version n is pinned. pin_legacy_publications pins a 0.20 ledger’s unpinned entries from a manifest. wire_type and DataSchema::same_wire compare types by what they store, and MigrationAuto and MigrationVersionType implement FromStr for the --auto and --version-type spellings.

Troubleshooting

ErrorCause and remedy
missing a string \abiVersion` field`a pre-0.20 document — this guide
records ABI version 9.9, but this Pina build supports 0.21; upgrade Pinathe document is newer than your build — upgrade Pina; the check is a capability marker, like Cargo.lock
predates the oldest supported version 0.20; regenerate it with \pina migrations create``a document older than the reset — Start fresh
names ... without pinning its published schemasa converted ledger whose histories were emptied — Pin the receipts
`[migrations].auto` no longer belongs in pina.tomla retired pina.toml key — delete it; the manifest records the setting
pins ... for ... , which the manifest does not record or pinned schema ... but the manifest now recordsa receipt history that was not emptied — Keep your history

After the conversion, abiVersion is maintained for you: pina migrations create writes the current version, and the value advances only when a future pina_abi release changes the document contract — never for a CLI fix, never for a program schema change.