Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ERC: Tap to Pay #686

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 200 additions & 0 deletions ERCS/erc-7798.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
---
eip: 7798
title: Tap to Pay
description: Initializing contactless payment transactions from EVM wallets
author: Amhed Herrera (@amhed), Justin Lee (@JustinDLee), Arjun Dureja (@arjun-dureja)
discussions-to: https://ethereum-magicians.org/t/erc-7798-contactless-payment/21501
status: Draft
type: Standards Track
category: ERC
created: 2024-10-25
requires: 20, 681, 712
---

## Abstract
This ERC defines a standard for contactless payment transactions that can allow for interoperable customer to merchant onchain payments, regardless of the wallets the customer and merchant are using.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The abstract should contain enough detail to give the reader a high-level (but still technical) overview of how your proposal accomplishes its goals.


## Motivation
Currently there is no standard mechanism in crypto to do contactless payment transactions via NFC. This ERC defines a standardized way of exchanging payment information between merchants and customers, or peer-to-peer individuals, so that efficient checkout can occur.

## Specification

Comprised of three parts:
- A new Ethereum Provider JavaScript API method called `requestContactlessPayment`
- An agreement on the payload for data exchange between the parties
- An optional mechanism for relaying large JSON payloads using a backend relayer


```mermaid
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our renderer doesn't support mermaid. Please replace it with an image in your assets directory (preferably an SVG.)

sequenceDiagram
Sender->>PoS: Generate checkout intent
PoS->>NFC Relayer: Upload checkout intent JSON
PoS->>JSON RPC: requestContactlessPayment(uuid)
JSON RPC->>Wallet: Trigger NFC HCE
Recipient->>Wallet: Scan NFC Tag
Wallet->>Recipient: Checkout Intent URI
Recipient->>NFC Relayer: requestPaymentParams
NFC Relayer->>Recipient: Transaction data
Recipient->>Recipient: signTransaction
Recipient->>RPC Proxy: submitTransaction
```


### Use Cases

#### Use Case 1: Customer purchases from a merchant
1. A customer approaches a merchant and orders an item at their cash register (e.g. a cup of coffee).
1. The merchant is running an app on their point-of-sale (PoS) system that allows initiating a checkout flow.
1. When the customer is done ordering, the merchant goes through the checkout flow up and triggers a payment request.
1. The merchant app calls the `requestContactlessPayment` RPC method with the expected payload.
1. The merchant's device emits an NFC signal via host card emulation (HCE) for the customer’s device to read.
1. The customer, running their wallet software of choice, reads the NFC signal. The customer’s wallet constructs the necessary transaction(s) to execute the onchain checkout.
1. The wallet then signs and submits all transactions.
1. Upon successful onchain execution of these transactions, the checkout is complete.

#### Use Case 2: Peer-to-peer Sends
1. Alice requests a payment on her wallet app by emitting an NFC signal. The contents of the signal is an [ERC-681](erc-681.md) URI
> []will this only work with 681? 681 has limitations, like receiver intent which would be great to solve too.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
> []will this only work with 681? 681 has limitations, like receiver intent which would be great to solve too.
<!-- will this only work with 681? 681 has limitations, like receiver intent which would be great to solve too. -->

If you use HTML-style comments, the linter will make sure you replace them before going into Review.


1. [ERC-681](erc-681.md) URI Bob reads the NFC signal from Alice on his wallet app and constructs the transaction
2. Bob holds his phone close to Alice's which triggers the NFC flow.
1. Bob then signs and submits the transaction onchain.
1. Send flow is complete.

### requestContactlessPayment
The requestContactlessPayment method will be added as a standard method to the [EIP-1193 Ethereum Provider JavaScript API](eip-1193).

This method takes in two parameters: `type` and `uri`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I usually recommend including a JSON Schema for any new API endpoints.


- `type` is an enum that represents the payload type:
- 1 means the URI is an ERC-681-compliant payload.
- 2 means the URI points to an HTTP relayer, where the response of the relayer is either a JSON object with the transaction data already encoded as a 0x string, or a message for the customer to sign. The customer wallet is responsible for constructing, signing and submitting the transaction/message.

- `uri` represents either the ERC-681-compliant payload or the relayer endpoint to query for the JSON payload.

Example calls:

```js
window.ethereum.request({
method: 'requestContactlessPayment',
params: {
type: 1,
uri: "ethereum:0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913@8453/transfer?address=0xf7d07Ee99095FDC09001710Ec232162a788BB989&uint256=1e5",
});

window.ethereum.request({
method: 'requestContactlessPayment',
params: {
type: 2,
uri: "https://nfcrelay.xyz/paymentTxParams?uuid=1234-abcd-56678",
verificationCode: "1234567890"
}
});
```

### Type 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are offchain signatures supported for type 1? i dont think it's covered by 681?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, we covered them with the relayed URI. You can basically pass down any long-form payload / set of batched transactions for EIP-3009 or permit2


The transaction is a regular crypto send. The receiving party transmits the [ERC-681](erc-681.md) URI to the sender’s wallet.


### Type 2

Handles cases where the payload necessary to construct, sign, and submit the transaction needed for payment is too large to emit over NFC. The intended flow is as follows:

1. The merchant dapp makes a POST request to an NFC relayer endpoint with the information needed for completing a sale.
1. The merchant’s device then transmits the NFC relayer endpoint URI and verification code to the customer via any means
1. The customer’s device then makes a GET request to the endpoint URI

One of three payloads can be returned from the NFC relayer:

**Regular Send**
```ts
{
payloadType: 'eip681';
chainId: string;
contractAddress: string;
toAddress: string;
value: string;
dappUrl?: string;
dappName?: string;
rpcProxySubmissionParams?: {
submissionUrl?: string;
}
}
```

**Contract Call**

```ts
{
payloadType: 'contractCall';
chainId: string;
approveTxs?: {
data: string; // this represents the approval call data
toAddress: string; // the address to submit the approve transaction to
}[],
paymentTx: {
data: string; // call data of the transaction, only needed for
toAddress: string; // the address to submit the payment transaction to
value: string; // the value of the blockchain's native token to transfer over
},
rpcProxySubmissionParams?: {
submissionUrl?: string; // optional endpoint to submit the tx hash to
},
dappUrl?: string;
dappName?: string;
}
```

**[EIP-712](https://eips.ethereum.org/EIPS/eip-712) Message (offline signature)**

Check failure on line 149 in ERCS/erc-7798.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

non-relative link or image

error[markdown-rel-links]: non-relative link or image --> ERCS/erc-7798.md | 149 | **[EIP-712](https://eips.ethereum.org/EIPS/eip-712) Message (offline signature)** | = help: use `./eip-712.md` instead = help: see https://ethereum.github.io/eipw/markdown-rel-links/

Check failure on line 149 in ERCS/erc-7798.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

non-relative link or image

error[markdown-rel-links]: non-relative link or image --> ERCS/erc-7798.md | 149 | **[EIP-712](https://eips.ethereum.org/EIPS/eip-712) Message (offline signature)** | = help: use `./eip-712.md` instead = help: see https://ethereum.github.io/eipw/markdown-rel-links/

Check failure on line 149 in ERCS/erc-7798.md

View workflow job for this annotation

GitHub Actions / EIP Walidator

non-relative link or image

error[markdown-rel-links]: non-relative link or image --> ERCS/erc-7798.md | 149 | **[EIP-712](https://eips.ethereum.org/EIPS/eip-712) Message (offline signature)** | = help: use `./eip-712.md` instead = help: see https://ethereum.github.io/eipw/markdown-rel-links/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm i guess sendCalls wouldnt work for getting offchain signatures


```ts
{
payloadType: 'eip712';
chainId: string,
rpcProxySubmissionParams: {
submissionUrl: string; // endpoint to submit tx message + signature to
typedData: {
types: {
// any types can go here in accordance with EIP-712 and eth_signtypeddata_v4, it's recommended that every type needed for
// generating the right transactions on the RPC proxy is here
},
primaryType?: string;
domain: {
// any domain fields go here, usually need name, version, chainId and verifyingContract
},
message: {
// any fields can go here, it's recommended that every field needed for
// generating the right transactions on the RPC proxy is here
}
},
},
dappUrl?: string;
dappName?: string;
additionalPayload?: {
// ... any fields can go here, this is additional payload the RPC proxy may need but does not have to bee signed in the message
}
}
```
Upon submitting the transaction for the [ERC-681](erc-681.md) and contract call cases, if `rpcProxySubmissionParams` is present, then the transaction hash can be optionally submitted via a POST request to the submission URL. The body of the POST would have the following structure:
```ts
{
txHash: string;
}
```

## Rationale

The reason for having a relayer URI to pass data is due to limitations in the size of the data that can be transmitted wirelessly between devices directly. For example, QR codes have a maximum character size of 7,089 characters currently, but a user can attempt to buy an indefinite number of items in a single checkout transaction. By having a relayer URI, we can theoretically pass as much calldata as we desire to the customer’s wallet, as long as they fetch from the URI.

[ERC-681](erc-681.md) URIs were also permitted to enable possible direct peer to peer payments. It’s more likely that they’ll be occurring between two end user wallets rather than between a customer and a merchant, but it would be good to include in the event the dapp only requires a simple send transaction to execute the checkout.

## Security Considerations
In the case of an [ERC-681](erc-681.md) URI, the same security considerations apply here. The wallet should display the amount and asset being transferred as well as the recipient very clearly to the customer, and users should only execute transactions using [ERC-681](erc-681.md) URIs from trusted dapps.

With regards to the relayer URI, since it is a publicly facing URI, it’s possible that anyone can make a fetch to it, grab the data, and submit transactions of their own to execute a checkout on someone else’s behalf. To mitigate this, we made a verification code as an optional part of the standard. The verification code is only passed through via the RPC call and is expected to be passed onto the customer’s wallet via some sort of contactless communication mechanism. This way, the data is not accessible to a random person on the internet and can only be accessed from the intended customer’s wallet.


## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).
Loading