Read this first. Every limitation below is real, none is hidden, and each one is stated before anybody has to find it.
The registry catches a second pledge only if the first one was registered. Attestcoin proves that a transaction happened. It cannot prove that one did not, so proof of absence is out of reach by construction.
This is exactly how UCC-9 works, and UCC-9 governs a trillion dollar lien market. Prevention comes from priority plus the habit of checking before lending, not from omniscience.
And the answer is about a key, not about an asset. Found by using the
register rather than by reading it: type an address that is not a contract and a
token id that was never minted, and it answers free to lend against, because
nothing is on file against that key and nothing is what there is to report. The
register cannot tell an unencumbered asset from one that does not exist.
That is a limit rather than a bug, and closing it would cost the property the
page is built on. Whether a token exists is one call to its own contract on its
own chain, and every number on /register is an eth_call against a single
Creditcoin node, which is what makes it repeatable by anybody with no
infrastructure. A lender integrating this already holds the collateral contract
in hand and can ask it directly. The page now says so where it answers.
MonetaGo already runs a secure financing registry that solves this problem. It is permissioned, bank-facing, and routed through Swift. That validates the pain rather than diminishing it.
The wedge is that the same job is done here with no trusted operator: the witness is a precompile proof, not a company. We do not claim to beat MonetaGo on network effect or on legal standing, because we would lose both arguments.
A registry is worth exactly as much as its coverage. Nobody is compelled to consult it before lending.
Unilateral witnessing softens this, because a pledge can be recorded without the pledging protocol cooperating or even knowing. It does not remove it.
The source chain must be EVM and attested by Creditcoin, which today means Ethereum and Sepolia. The fact must live in a log. Anything off-chain, or on a non-attested chain, is outside the system.
assetKey deliberately excludes the emitter, so that the same asset pledged in
two protocols produces one key and collides. That is the product. The cost is
that state[assetKey] is global across every allowlisted emitter.
A compromised or malicious allowlisted protocol can therefore freeze an asset it does not hold, by registering a pledge against it.
For custodial pledges this closes: require a Transfer of the token to the
emitter in the same receipt, and a griefer cannot forge it, because a real
transfer needs the owner's key.
For non-custodial liens there is no transfer to require, so this stays a genuine limit. What softens it:
- the allowlist is kept minimal, append-mostly, and visible on-chain, so the worst case is a detectable and reversible freeze by a named party rather than a silent fabrication by an indexer nobody audits
- roadmap: an owner-signed consent, EIP-712, carried inside the pledge event. That converts the mitigation into cryptographic prevention, at the cost of requiring the protocol to opt in
Worth stating plainly, because it shapes what the product is for.
If a protocol takes custody of the token, the borrower cannot then pledge it elsewhere. There is nothing to collide. Double pledging is only possible when at least the second leg is non-custodial, meaning the borrower keeps the asset and the protocol records a lien against it.
So the registry's market is non-custodial liens and mixed cases. That is not a narrowing, it is a location: custody prevents the fraud mechanically, and everywhere custody is absent the fraud is live and unaddressed.
The uncomfortable consequence, checked on chain rather than assumed. Both real
protocols this submission reads take custody. A live Blend borrow transfers
the token from the borrower to Blend, and NFTfi's own LoanTerms carries an
escrow field naming the contract that holds it. So the six mainnet proofs
demonstrate that the reader works against protocols that never heard of us, and
they do not demonstrate that the fraud is possible in those protocols, because
it is not. The collision is shown on Sepolia, between two non-custodial lenders
written for this submission.
That gap is the honest state of the beachhead, and the reason for it is more interesting than the gap.
On chain, possession costs one transfer, so escrow is the default. We went looking for a live non-custodial lender to read, and what came back is worth writing down.
For fungible collateral the shape exists and is busy, though not in the shape this file claimed until 2026-08-21.
It used to say that Aave v3 and Euler v2 leave the collateral in the borrower's
own wallet. That is false, and the transaction cited as proof of it could not be
opened: eight hex characters is not a transaction anybody can look up. Both
faults were found by script/audit-claims.mjs and the sentence was checked
properly rather than repaired.
What the chain says. In Aave v3 on Sepolia, supplying moves the underlying
out. In 0x7cd6a3537c4d302bb3013ef631f9068dfb600058e1ad7890aaedad583e7950cf,
block 11,534,505, WETH leaves the supplier for the aToken contract
0x5b071b590a59395fe4025a0ccc1fcc931aac1830 and aEthWETH is minted back. The
borrower ends up holding a receipt, not the asset. Euler v2 was not checked and
no claim is made about it here.
What is true is narrower and still the point. The borrow moves nothing: in
0x1dcf21883efc829c745f29d6081b189c448737feaef086dfbb4f09917f53a68b, block
11,534,036, both transfers are inbound to the borrower, a variable debt token
minted from the zero address and the borrowed USDC paid out. The lien is a
registry entry rather than a per asset escrow, and the borrower's claim stays
fungible and composable. That is the property worth having, and it is not the
same as the collateral staying put.
Either way the key does not fit. Their lien is keyed by (reserve, account),
because a share of a pool has no token id, and this registry is built on
(chainKey, token, tokenId) for a single unique asset. Reading Aave here would
mean either a different registry or a dishonest key, so it is named rather than
bolted on.
For unique collateral, the market is not thin, it is empty, and it could not have been otherwise. Every mechanism for encumbering a token in place needs the token contract to have opted in: a lockable ERC has to be implemented by the collection itself. So none of them can ever apply to collateral that is already deployed, which is all the collateral that exists. That is why every attempt at non-custodial NFT lending has reached for a proxy wallet instead, and a proxy wallet is possession wearing a different hat. A scan of roughly a month of Ethereum blocks for lockable-token lien events returned nothing from any lender.
Two independent searches were run and agreed, which is worth saying because the numbers are small enough to look like a search that gave up. The one protocol that ever shipped in-wallet encumbrance for NFTs, PWN's Asset Transfer Rights, minted twelve tokens in its life and financed one loan; its registry holds zero today. Of the three lockable token standards, one has a live implementation whose lock is dormant, one has a live implementation with no lender, and one has no implementation found at all. Every product that markets itself as non-custodial NFT lending moves the asset into a smart wallet it co-signs, which is possession with a longer name.
So the honest sentence is not "we serve this market". It is closer to the opposite, and it is the strongest thing in this file:
Non-custodial lending does not exist because there is no priority register. Without one, a lender cannot see whether somebody already lent against the collateral, so the only safe move is to take it. Possession is not a preference here, it is the fallback that a missing register forces. That makes the empty market a consequence rather than an objection, and it makes this the piece that has to exist first rather than the piece that arrives after the demand.
That is an argument, not a proof, and it is placed in the caveats rather than on the front page for exactly that reason. What is proven here is the primitive.
It also means the dual-log transfer binding in caveat 5 and the collision demo cannot both apply to the same pledge. The binding is an optional stronger mode for custodial emitters; the demonstration is non-custodial.
A proof carries the emitter and the log index it is about. The registry does not search the receipt for a matching log, and does not count how many there are.
This is the correction to two findings that were the same mistake twice. The party who sends a transaction on a source chain is usually the borrower, so anything the registry infers by scanning that receipt is chosen by the borrower. An external review on 2026-08-19 found that a decoy log with a borrowed topic zero made a genuine pledge unregisterable. A second review the same day found that the fix was not enough: a real log from any other allowlisted protocol, ordered first, decided who the emitter was, and the genuine pledge became unregisterable again at the cost of one throwaway lien. An unregisterable pledge is first to file inverted, because the borrower then chooses which lender gets priority.
Naming the log removes the inference and the whole class with it. A receipt full
of decoys changes nothing about what a relayer can file, and a batch pledge of
several assets in one transaction is now supported rather than refused, because
each log is filed on its own and the replay nullifier is keyed by log as well as
by transaction. test_aDecoyWithTheSameSignatureChangesNothing and
test_anUnrelatedAllowlistedLogDoesNotSuppressAPledge carry both attacks as regression
tests.
For on-chain RWA the key is free: keccak256(chainKey, tokenAddress, tokenId)
names an asset with zero ambiguity and no agreement between protocols.
For off-chain invoices, the trillion dollar case, it is not free. Two protocols will hash the same invoice differently, and reconciling that is make or break. We prove the primitive where the key costs nothing and name the canonical standard as the bridge to the larger market. That is a roadmap, not a claim.
The allowlist governs which logs are read. The BlockProver governs whether they are true. Those are different powers, and the boundary between them is the one real limit on an administrator: no admin action makes the precompile accept a transaction that was never mined.
Everything on this side of that line, an administrator can do. This used to read
"an administrator can exclude, but cannot fabricate", and that sentence was
false. The ABI adapter maps a protocol's native event onto
(token, tokenId, instanceId), so an administrator who installs an adapter
decides what a real log means, and an adapter that ignores its argument can file
a lien against an asset whose owner was never involved. Removing the adapter
afterwards does not undo what it wrote. The test that used to back the old claim
only proved that de-allowlisting a lender blocks that lender's pledge; it never
attempted fabrication. test_theAdminCanFabricateThroughAnAdapter attempts it, and it succeeds.
So the honest statement is narrower. The adapter is trusted for interpretation of something already proven to exist, which is auditable in the fifty to a hundred and thirty lines of pure code. An indexer would be trusted for existence itself, which is not bounded by anything. That is the difference worth defending, and it is smaller than the sentence it replaces.
Exclusion is bounded in the other direction: the allowlist gates entry, not exit. A lender that has been excluded can still release what it already holds, so excluding a protocol cannot strand the assets of borrowers who were not party to that decision.
Two limits are worth naming with it.
An adapter can only carry what the protocol actually emits, and protocols emit less than one would like. Blur's Blend publishes the token id when a loan is taken and omits it when the loan is repaid; NFTfi has no settlement step at all.
Three answers, depending on what is missing.
Where a transition does not exist at all, the adapter declares it unsupported
and the registry refuses it with TransitionUnsupported rather than mapping
some other event onto it.
Where the transition exists but does not name the collateral, the adapter returns a zero token and the registry resolves the lien through the instance id it recorded when the loan was opened, keyed by that emitter and no other. An opening pledge never gets that fallback: a lien has to name what it claims.
Where a protocol ends a lien in more than one way, the adapter names every event
that ends it. Blend closes a lien with Repay when the borrower pays and with
Seize when an auction fails, and both are proven. Missing one of them would
leave liens on file that the source chain has already closed, which is the
failure mode a registry has to care about most: a stale claim looks exactly like
a live one.
An adapter is also a per protocol integration written by us, not by the protocol. It is the one place where being wrong looks like being right, which is why each one stays short enough to read in a sitting and is tested against real logs captured from the chain it claims to read.
The registry reads how many attestors are bonded for a source chain and refuses to create records once that set falls below a stated floor. This is a real guard, and it has a real cost: a chain whose attestor set thins stops being readable, so a lender that would have filed first loses the race for reasons that have nothing to do with the borrower or the asset.
This is not hypothetical in either direction, which was established by reading the precompile at every block the public node still keeps. Sepolia went 0 to 1 to 6 to 7 between 2026-05-01 and 2026-07-09; Ethereum went 0 to 1 to 3 to 4 between 2026-05-01 and 2026-07-16, and spent part of that window on the supported chain list with no attestors behind it at all. The floor would have refused Ethereum proofs for most of May. It also sits one attestor below the current Ethereum set, so a single deregistration would halt the mainnet half of this project's own demo. The full table is in VERIFICATION.md.
That is the intended trade. A first to file register whose records are only as good as the quorum behind them should stop writing rather than write records nobody can weigh. But it is a trade, not a free improvement, and it means the floor is a number an administrator sets and can move. Setting it high is a way to halt a chain, which belongs on the same page as everything else in caveat 9.
Two things bound it.
The floor gates entry and never exit, tested in
test_aThinnedAttestorSetDoesNotTrapAnAssetAlreadyOnFile. A settlement or a
release goes through whatever the attestor set is doing, so no administrator and
no attestor rotation can strand an asset already on file.
Zero is refused. setMinAttestors(chainKey, 0) reverts QuorumNotSet, and a
chain with no floor stated records nothing at all rather than recording
everything. The registry has already had one review find a guard whose default
disabled it, and this one was written after that lesson rather than before.
What the floor cannot do is make a thin set safe. If Creditcoin's attestors for a chain are compromised rather than merely few, the count reads high and the proofs are worthless, and no number this contract can read would say so. The floor is a check on how much security is standing, not on whether it is honest.