ERC-681: Representing various transactions as URLs

This is a very useful proposal by @nagydani that should be getting more attention now that UX (and what I would call “developer UX”) is such a high priority in the community.

Simple Summary
A standard way of representing various transactions, especially payment requests in Ethers and ERC #20 tokens as URLs.

Abstract
URLs embedded in QR-codes, hyperlinks in web-pages, emails or chat messages provide for robust cross-application signaling between very loosely coupled applications. A standardized URL format for payment requests allows for instant invocation of the user’s preferred wallet application (even if it is a webapp or a swarm đapp), with the correct parameterization of the payment transaction only to be confirmed by the (authenticated) user.

1 Like

Totally agree, we are working on implementing this as part of the WalletConnect standard. However I think this is a great start for doing transaction requests but I find that there is still more information needed to clearly provide human-readable machine-veriable transaction requests similar to Aragon’s Radspec as suggested by @avsa on the thread for EIP-1138

1 Like

Referencing these here:

Agree that this is an important step in improving UX. I do have one (faint) concern:

chain_id is optional and contains the decimal chain ID, such that transactions on various test- and private networks can be requested. If no chain_id is present, the client’s current network setting remains effective.

Would it not be better to mandate that the default chain be the mainnet rather than something dependent on the client’s current settings. By relying on the client settings you’re introducing a vector for introducing debugging hassles:

A Play In One Act
<Enter A Bug>
Dev: What chain_id did you use?
User: ?
Dev: Did the URL contain a chain_id?
User <who doesn't really know what that would look like, but doesn't want to appear stupid>: I don't think there was one...

<By pure chance they're right this time>

Dev: OK, which blockchain was your client using at the time?
User: I've no idea. I think it may have been some test network.

<ad lib mayhem, heads exploding, fx fades>

…you get the idea, I hope!

Whereas, if the default is mandated to be the mainnet, there’s just one less (fertile) source for confusion when it comes to debugging.

Hope this makes some technical sense, but I confess I’m not well qualified to judge that – I’m just coming at it from a perspective of decades of debugging… :wink:

There is not a single chain that can be considered canonical. When a contentious fork occurs, there will be two “main” chains and it is up to users, exchanges, wallets, etc. to decide which one is “main”. People have argued in the past (and I have supported) that the chain ID should change with every fork, thus defaulting is also not really reasonable. Your client follows some chain, someone else’s client follows another chain, both clients call the chain they follow “main”, neither chain uses the same chain ID from before the fork.

1 Like

It has been debated a bit, and to be honest I do not remember why it was decided this way, but I guess that it had something to do with being able to test URL’s without a network id on testnets as well as being able to transition to a different chain ID after some contentious hardfork.

I’ll stand by my conviction that the existence of a hidden variable is a dangerous source of confusion that will result in misery for many people in time to come, and I strongly urge a reconsideration while the EIP is still in draft and amenable to change/improvement.

Perhaps a better solution than I first suggested is simply to make the chain element of the URL mandatory rather than “optional with a hidden default”.

1 Like

AFAIR the chain_id was made optional to provide some backward compatibility to ERC-67 - when making it mandatory would always break compatibility.
Also I do not really see the reason to make it mandatory - it is easy - if the parameter is not there -> then the value is 1. I dont’t see users knowing/remembering about chain_id’s in URLs as a valid case - they just will not know or remember - no matter if there is always a number or not. But just my 2cents.

1 Like

Why is there no explanation whatsoever in the specification about the pay- prefix??

The formal grammar specification says the url starts with ethereum: followed by an optional pay-, that is, either ethereum:0x..... or ethereum:pay-0x....., but says nothing about when the pay- prefix is allowed or required. Does it make no difference whatsoever? If so it should be specified, because it’s quite counterintuitive that there would be a prefix that you can arbitrarily include or leave out and it makes no difference at all.

Intuition suggests that maybe the ppay- prefix should be present when the transaction has a non-zero value, but (a) that’s entirely me speculating, it’s not stated anywhere, and (b) there are examples with non-zero value and with no pay- prefix.

:credit_card: Making ethereum: URIs Actually Work: Proposal for ABI Discovery via ERC-5169 / 7738


We’re working on crypto-native NFC payment flows—think “Apple Pay for crypto.”
The ideal UX looks like this:

  1. Merchant enters amount
  2. User taps their phone on the NFC device
  3. Phone opens wallet with a pre-filled transaction
  4. User taps “Confirm” → done

To achieve this, we want to encode ethereum: payment URIs (EIP-681) into NFC tags.

:fire: The Problem

While native token transfers work fine using ?value=..., smart contract calls break across nearly all major wallets (MetaMask, Rainbow, Trust, Coinbase Wallet, etc).

Example URI:

ethereum:0xABC123/transfer?address=0xDEF456&uint256=1.23

This silently fails or gets ignored. Why?


:x: Root Cause

  1. No ABI context — Wallets don’t know how to encode calldata without knowing parameter types.
  2. No standard ABI discovery — There’s no canonical way for a wallet to fetch a contract’s ABI.
  3. Wallet safety concerns — Guessing types is dangerous, so most wallets opt to ignore.

:white_check_mark: Proposed Solution: ABI Discovery via scriptURI

We propose using ERC-5169 or ERC-7738 to let contracts expose ABI metadata to wallets.

Example:

function scriptURI(bytes4 selector) external view returns (string memory);

Wallets could:

  • Query scriptURI() on the contract
  • Fetch the ABI from the returned URI
  • Parse the URI params using that ABI
  • Construct a valid data field and submit the transaction

This restores the original vision of EIP-681—linking wallets, NFC devices, QR codes, and POS terminals in a seamless, standardized way.


:repeat: Sample Flow

  1. NFC tag encodes:

    ethereum:0xABC123/transfer?address=0xDEF456&uint256=1.23
    
  2. Wallet calls:

    scriptURI("0xa9059cbb")
    
  3. URI returns JSON:

    {
      "name": "transfer",
      "inputs": [
        { "name": "to", "type": "address" },
        { "name": "value", "type": "uint256" }
      ]
    }
    
  4. Wallet builds calldata, user confirms, done.


:bulb: Why this matters

If we want crypto to reach real-world adoption (cafés, transit, vending machines), we must:

  • Make URI-triggered smart contract interactions reliable
  • Avoid requiring dApps or custom flows
  • Empower POS hardware with standards-compliant, low-cost NFC/QR workflows

:mega: Call to Action

We’re calling on:

  • Wallet developers
  • ERC-681 / URI standards contributors
  • Smart token and TokenScript builders
  • Mobile UX & NFC ecosystem folks

Let’s collaborate on a lightweight, secure, standardized ABI-discovery flow so that ethereum: URIs can actually fulfill their promise.

Thoughts, feedback, or interest in prototyping with us? We’d love to hear from you.