About Resolution Proofs
General
What are resolution proofs
Resolution proofs (also called "resolved proofs") are proofs created by the ledger during intent processing that serve as routing instructions for the two-phase commit (2PC) protocol.
They are added to an intent after ledger adds pending proof, and act as the foundation for all subsequent 2PC coordination and intent processing.
They are derived from claims present in the intent, and express how those claims are going to be executed and with which consequences, namely:
- They determine which external systems (bridges), if any, will participate in executing each part of an intent and establish the ledger's commitment to coordinate the distributed transaction.
- They determine which
wallet(s)will receive and lose balance, of whichsymboland of whichamount
Claim types destroy and issue currently do not contact bridges. Resolved proofs will still contain references to bridge(s) assigned to the wallet from the claim(s).
Unlike participant proofs (prepared, committed, failed, aborted) which are created by bridges responding to ledger requests, resolution proofs are always created by the ledger itself using its own signing key.
Related documentation
- About Intents - Overall intent processing lifecycle
- About Balance Reservations - How balances are reserved based on info from debit resolution proofs
- About Bridges - How bridges communicate in 2PC
- About Wallets - Wallet routing and bridge assignment
How to find them
Resolution proofs are proofs in the intent, identifiable by having the value of custom.status as resolved, and being signed by ledger's public key.
Why they matter
- Immutable source of truth: They create an auditable record of the ledger's routing decisions for the intent. These are the source of truth for:
- how much balance(s) should be moved from which wallet to which wallet's exact handle (no need to guess what the wallet address resolves into)
- which bridges the ledger will communicate with as participants
- Entry tracking: Each resolution proof contains a unique entry handle (starting with
deb_orcre_) that is used throughout the 2PC lifecycle. - Balance reservation: Resolution proofs with schema
debitcontain the information (wallet, amount, symbol) that the ledger uses to create balance reservations when it prepares internally (as a 2PC participant).
When they are created
Resolution proofs are created during the pending phase of intent processing, specifically:
- Intent is created and validated (aspects execute)
- Intent enters pending status
- Ledger analyzes claims and determines routing
- Resolution proofs are created for each debit/credit operation
- DTC initiates communication with participant bridges, and processes intent
Resolution proofs are created before any bridge is contacted. They represent the ledger's plan for executing the intent, not the result of bridge responses.
Resolution proof structure
Resolution proofs follow the standard ledger proof structure with specific custom fields:
Example resolution proof:
{
"method": "ed25519-v2",
"digest": "...",
"result": "...",
"public": "<ledger-public-key>",
"custom": {
"status": "resolved",
"handle": "deb_xyz123", // Unique entry identifier
"schema": "debit", // Operation type: "debit" or "credit"
"wallet": "wallet_alice", // Target wallet for the operation
"bridge": "bridge_bank_a", // Bridge that must handle this operation
"symbol": "usd", // Symbol being transferred
"amount": 1000, // Amount for this operation
"inputs": [0], // Claim indices that contribute to this entry
"moment": "2025-01-15T10:30:00.000Z"
}
}Key fields
| Field | Type | Description |
|---|---|---|
public | string | Always the ledger's public key (identifies this as a ledger-created proof) |
custom.status | string | Always "resolved" for resolution proofs |
custom.handle | string | Unique resolution proof identifier, prefixed with deb_ (debit) or cre_ (credit) |
custom.schema | string | Operation type: "debit" or "credit" |
custom.wallet | string | Handle of the wallet to be debited or credited (exact wallet, no more resolution involved) |
custom.bridge | string | Handle of the bridge responsible for this operation (optional) |
custom.symbol | string | Symbol (currency/token) being transferred |
custom.amount | number | Amount for this specific resolution proof |
custom.inputs | number[] | Array of claim indices that contribute to this resolution proof |
Examples
Example 1: Simple transfer
Intent: Transfer 100 USD from Alice to Bob
Claims:
[
{
"action": "transfer",
"source": { "handle": "wallet_alice" },
"target": { "handle": "wallet_bob" },
"symbol": { "handle": "usd" },
"amount": 10000 // extra 00 because of `symbol` factor
}
]Resolution proofs created:
[
// Debit resolution proof for Alice
{
"method": "ed25519-v2",
"digest": "...",
"result": "...",
"public": "<ledger-public-key>",
"custom": {
"status": "resolved",
"handle": "deb_abc123",
"schema": "debit",
"wallet": "wallet_alice",
"bridge": "bridge_bank_a",
"symbol": "usd",
"amount": 10000,
"inputs": [0]
}
},
// Credit resolution proof for Bob
{
"method": "ed25519-v2",
"digest": "...",
"result": "...",
"public": "<ledger-public-key>",
"custom": {
"status": "resolved",
"handle": "cre_xyz789",
"schema": "credit",
"wallet": "wallet_bob",
"bridge": "bridge_bank_b",
"symbol": "usd",
"amount": 10000,
"inputs": [0]
}
}
]Example 2: Multi-claim intent
Intent: Exchange 100 USD for 85 EUR with 2 USD fee
Claims:
[
{
"action": "transfer",
"source": { "handle": "wallet_alice" },
"target": { "handle": "wallet_exchange" },
"symbol": { "handle": "usd" },
"amount": 10000
},
{
"action": "transfer",
"source": { "handle": "wallet_exchange" },
"target": { "handle": "wallet_alice" },
"symbol": { "handle": "eur" },
"amount": 8500
},
{
"action": "transfer",
"source": { "handle": "wallet_exchange" },
"target": { "handle": "wallet_fee_collector" },
"symbol": { "handle": "usd" },
"amount": 200
}
]Resolution proofs created: 6 total (one debit + one credit per claim)
[
// Claim 0: Debit Alice 100 USD
{ "custom": { "handle": "deb_001", "wallet": "wallet_alice", "amount": 10000, "symbol": "usd", "inputs": [0] } },
// Claim 0: Credit Exchange 100 USD
{ "custom": { "handle": "cre_001", "wallet": "wallet_exchange", "amount": 10000, "symbol": "usd", "inputs": [0] } },
// Claim 1: Debit Exchange 85 EUR
{ "custom": { "handle": "deb_002", "wallet": "wallet_exchange", "amount": 8500, "symbol": "eur", "inputs": [1] } },
// Claim 1: Credit Alice 85 EUR
{ "custom": { "handle": "cre_002", "wallet": "wallet_alice", "amount": 8500, "symbol": "eur", "inputs": [1] } },
// Claim 2: Debit Exchange 2 USD fee
{ "custom": { "handle": "deb_003", "wallet": "wallet_exchange", "amount": 200, "symbol": "usd", "inputs": [2] } },
// Claim 2: Credit Fee Collector 2 USD
{ "custom": { "handle": "cre_003", "wallet": "wallet_fee_collector", "amount": 200, "symbol": "usd", "inputs": [2] } }
]Example 3: Issue claim
Intent: Issue 1000 USD to merchant wallet
Claims:
[
{
"action": "issue",
"target": { "handle": "wallet_merchant" },
"symbol": { "handle": "usd" },
"amount": 100000
}
]Resolution proofs created: 1 (credit only, no debit for issue actions)
[
{
"method": "ed25519-v2",
"digest": "...",
"result": "...",
"public": "<ledger-public-key>",
"custom": {
"status": "resolved",
"handle": "cre_abc123",
"schema": "credit",
"wallet": "wallet_merchant",
"bridge": "bridge_merchant_system",
"symbol": "usd",
"amount": 100000,
"inputs": [0]
}
}
]How resolution proofs work
The process of creating resolution proofs involves several steps:
1. Claim analysis
The ledger analyzes the intent's claims to determine which operations need resolution:
- Transfer claims → Create both debit and credit entries (2 resolution proofs)
- Issue claims → Create credit resolution proof only (1 resolution proof)
- Destroy claims → Create debit resolution proof only (1 resolution proof)
2. Wallet routing
The ledger follows wallet routing rules (if configured) to determine the final wallets that will be debited/credited and which bridges are assigned to those wallets. See About Wallets for details on routing mechanics.
Wallet routing can create chains where the actual debited/credited wallet differs from the wallet specified in the claim. Resolution proofs always reference the final routed wallet, not the original claim wallet.
3. Bridge entry creation
For each routed wallet operation, the ledger creates a bridge entry with:
- A unique handle (e.g.,
deb_abc123orcre_xyz789), of same value as corresponding resolution proof's handle - The operation schema (debit or credit)
- The final routed wallet
- The bridge assigned to that wallet (if any)
- The symbol and amount
- The claim indices that contribute to this bridge entry
4. Proof signing and attachment
Each bridge entry is used to generate and sign a proof, which is then appended to the intent's meta.proofs array.
Relationship to 2PC requests
Resolution proofs directly determine which prepare/commit/abort requests are sent to which bridge(s):
- Debit resolution proofs → Determine which bridge receives debit prepare/commit/abort requests
- Credit resolution proofs → Determine which bridge receives credit prepare/commit/abort requests
The bridge entry handle in prepare requests (e.g., deb_abc123) comes directly from the corresponding resolution proof's handle. Bridges must respond with participant proofs using this same handle to link their response to the correct resolution proof.
For complete details on 2PC request/response structures, see About Bridges and About Intents.
Resolution proof lifecycle
1. Intent created
↓
2. Resolution proofs created by ledger
- Signed with ledger's key, their `custom.status` is `resolved`
- Added to intent.meta.proofs
↓
3. Processing starts, using resolution proofs as source of truthResolution proofs remain in intent.meta.proofs throughout the entire intent lifecycle. They are never removed or modified, providing a permanent record of the original routing decisions.
Interaction with other concepts in system
Resolution proofs and balance reservations
Resolution proofs with schema: "debit" contain the wallet, amount, and symbol information that the ledger uses to create balance reservations when it prepares internally as a 2PC participant.
See About Balance Reservations for complete details on how reservations work throughout the 2PC lifecycle.
Wallet routing and resolution
Resolution proofs reflect the final routed wallets, not necessarily the wallets specified in claims. This is important to understand if you've configured wallet routing rules.
Scenario: Alice's wallet has an outgoing route to Bob's wallet
Claim:
{
"action": "transfer",
"source": { "handle": "wallet_alice" },
"target": { "handle": "wallet_charlie" },
"symbol": { "handle": "usd" },
"amount": 10000
}If wallet_alice has route: { "action": "debit", "target": "wallet_bob" }
Resolution proof reflects the routed wallet:
{
"custom": {
"status": "resolved",
"handle": "deb_xyz",
"schema": "debit",
"wallet": "wallet_bob", // Routed wallet, not wallet_alice
"bridge": "bridge_bank_b", // Bridge assigned to wallet_bob
"amount": 10000,
"inputs": [0]
}
}See About Wallets for complete details on routing mechanics, depth limits, and cycle detection.
Troubleshooting
Debugging with resolution proofs
Resolution proofs are valuable for debugging intent processing issues:
Check if resolution occurred
Using the API:
GET /v2/intents/:handle
# Check for proofs with custom.status === "resolved"Using the CLI:
minka intent show <handle> -v
# Look for proofs with status "resolved" signed by ledgerIdentify which bridges are involved
From the intent response, filter resolution proofs and extract the custom.bridge field:
// Example: Extract bridges from resolution proofs
{
"meta": {
"proofs": [
{ "custom": { "status": "resolved", "bridge": "bridge_bank_a" } },
{ "custom": { "status": "resolved", "bridge": "bridge_bank_b" } },
{ "custom": { "status": "resolved", "bridge": "bridge_bank_a" } }
]
}
}
// Unique bridges involved: bridge_bank_a, bridge_bank_bMatch resolution proofs with participant responses
For each resolution proof, check if a participant proof with the same custom.handle exists:
// Resolution proof
{ "custom": { "status": "resolved", "handle": "deb_abc123", "bridge": "bridge_x" } }
// Expected participant prepared proof (same handle)
{ "custom": { "status": "prepared", "handle": "deb_abc123" } }
// If missing → bridge_x has not responded to prepare requestDetect missing responses
Compare resolution proof handles with participant proof handles to find missing responses:
Resolution proofs: [deb_001, cre_001, deb_002]
Participant proofs: [deb_001, cre_001]
Missing: deb_002 → Check which bridge was assigned deb_002Common issues
Issue: Intent stuck in pending, no resolution proofs
Symptom: Intent remains in pending status, no proofs with status: "resolved" exist
Possible causes:
- Intent has no resolvable claims (only limit claims or unsupported actions)
- Routing failed (routed wallet doesn't exist or routing cycle detected)
- Ledger signer not configured (contact support to report a bug)
What to check:
- Verify all wallet handles in claims exist
- Check if wallets have routing rules that might cause issues
- Ensure claims use supported actions:
transfer,issue,destroy
Issue: Wrong bridge assigned in resolution proof
Symptom: Resolution proof shows unexpected bridge
Possible causes:
- Wallet routing redirected to a different wallet with different bridge
- Wallet's
bridgefield was updated after you created the intent
What to check:
# Check wallet's current bridge assignment
GET /v2/wallets/:handle
# Response shows bridge field
{
"data": {
"handle": "wallet_alice",
"bridge": "bridge_bank_a" // ← Current bridge assignment
}
}
# Check wallet routes
{
"data": {
"routes": [
{ "action": "debit", "target": "wallet_bob" } // ← May redirect to different bridge
]
}
}Issue: Bridge not receiving prepare requests
Symptom: Resolution proof exists with your bridge handle, but you're not receiving prepare requests
Possible causes:
- Bridge endpoint (
config.server) misconfigured in bridge record - Network connectivity issues between ledger and your Bridge
- Bridge not assigned to resolved wallet
What to check:
# Verify bridge configuration
GET /v2/bridges/:bridgeHandle (or `minka bridge show :bridgeHandle`)
# Check config.server URL is correct
{
"data": {
"handle": "your_bridge",
"schema": "rest",
"config": {
"server": "https://your-bridge.example.com/v2" // ← Verify this URL
}
}
}# Verify bridge assigned to wallet from resolution proof
GET /v2/wallets/:walletHandle (or `minka wallet show :walletHandle`)
# Check bridge handle is correct
{
"data": {
"handle": "your_wallet",
"bridge": "your_bridge",
}
}- Test your bridge
/statusendpoint is accessible - Check your bridge logs for incoming requests
- Verify firewall rules allow ledger to reach your bridge
Bridge implementers: The prepare request entry includes the full intent with resolution proofs in intent.meta.proofs. When responding with participant proofs, you must use the same handle from the entry. For complete bridge implementation details, see About Bridges and About Intents.