The forms
SEP-0023 also reserves
W for muxed contracts. Decoders must reject a strkey that doesn’t re-encode to the same string: some base32 libraries accept non-canonical input.
Mistake 1: one column for every key
A database column sized for a 32-byte account (or a 33-byte version-byte-plus-key) can’t hold anM (40 bytes) or a P (up to 101 bytes). wallet-backend#729 is what happens next: the first P… signer on testnet failed to encode, the persistence transaction rolled back with the cursor, and ingestion retried the same ledger forever. The kit reproduces it on the commit before the fix (sightings).
Decide per column what it holds. An account column takes G only, so reduce M to its G account first. A signer column takes every signer type, at its full length.
Mistake 2: sorting by the text
Base32 puts2–7 after A–Z in value, but ASCII puts them before. Two issuers can sort one way as strkeys and the other way as bytes. The network orders assets by the issuer’s key bytes. Before 17.2.1, js-stellar-sdk ordered them by text (#1766): TransactionBuilder.fromXDR threw on a valid pool-share trustline whose issuers fell the wrong way, and the pool ID couldn’t be computed.
cap38-pool-issuer-order-local-0001 is a real pool-share trustline whose two issuers were chosen to sort in opposite orders. Sort keys as bytes, or compare with your SDK’s asset comparison (17.2.1 and later in JavaScript).
Mistake 3: equal accounts that compare unequal
GA… and MA… for the same account are different strings. Participant sets, deduplication and primary keys built from strings treat them as two accounts, until a database reduces both to the same key and refuses the second. That’s wallet-backend#716: a single cheap ExtendFootprintTtl from a muxed source halted ingestion. IX003 flags the M form used as a participant in Go code.