Personal
nostr-linkr
A trustless bridge between a Nostr identity and an Ethereum address, with full BIP-340 Schnorr verification performed on-chain: no DNS, no platform post, no third party to believe.
nostr-linkr lets someone prove that a Nostr public key and an Ethereum address belong to the same person, and lets anyone else check that proof without asking a server. It is a smart contract, a TypeScript SDK, and a specification proposed to the Nostr protocol.
- Parts
- Contract · SDK · example client · NIP proposal
- Proposal
- NIP-XX, On-Chain EVM Identity Linking (open)
- Role
- Author
- Repository
- VincenzoImp/nostr-linkr
The problem
Nostr already has two ways to say “this account is also me somewhere else”, and the specification proposal states the objection to both. NIP-05 verification depends on a DNS-controlled server: whoever runs the domain can change or withdraw the claim. NIP-39 depends on a post on GitHub or Twitter, which the platform can delete and the platform’s owner can decide about.
Both work. Both are mutable, censorable, and require trusting a service that is not a party to the identity being claimed.
What I built
The link is written to a smart contract, and it requires both private keys, which is what makes it a claim neither side can fake.
The Nostr key signs an event containing the Ethereum address. The Ethereum key sends the
transaction that submits it. The contract then does the part that would ordinarily be taken on
trust: it recomputes the NIP-01 event id from the event’s fields, checks it matches the id
presented, and verifies the Schnorr signature against the Nostr public key. Only then is the link
recorded, and it can be looked up in either direction. A pullLinkr call removes it, so the link
is revocable by the address that made it.
Around the contract sit a TypeScript SDK, an example client, and a proposal written up as a NIP and submitted to the specification repository, where it is open.
Hard parts
The EVM cannot verify a Nostr signature. Ethereum’s precompiled ecrecover handles ECDSA;
Nostr signs with BIP-340 Schnorr. Same curve, different scheme, and no precompile for it, so the
verification is implemented in Solidity: lifting the x-only public key back to a curve point,
which works because secp256k1’s field prime is congruent to 3 mod 4 and the square root is
therefore a single modular exponentiation, then checking that s·G == R + e·P. It runs entirely
on-chain, which is the whole point; verification that happens off-chain is a claim about a claim.
The hand-written verifier has not received a security audit, and the deployed contract is limited
to Base Sepolia. It demonstrates the proof path rather than establishing production security.
Recomputing the event id rather than accepting it. A signature is over the event id, so a contract that trusts a submitted id verifies nothing about the event’s contents. The contract serialises the fields per NIP-01 and derives the id itself, and only compares afterwards.