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

ERC 1257: Proof of Payment by smart contracts #1257

Open
yaronvel opened this Issue Jul 25, 2018 · 19 comments

Comments

Projects
None yet
6 participants
@yaronvel
Copy link

yaronvel commented Jul 25, 2018


eip: ERC 1257
title: Proof of Payment (POP) Standard
author: Victor Tran, and Yaron Velner (and future others)
status: Draft
type: ERC
category ERC
created: 2018-07-25

Proof of Payment

Simple Summary

Proof of Payment (POP) is an event (evm log) that provide a record of successful payment. Either via Ether or ERC20 token.

Abstract

Currently merchants and vendors in online stores are using transaction hash (tx hash) as a method to track user payment and provide a form of payment receipt.
This approach stemmed from bitcoin payment methods, where the main form of payment is done by a human agent with a dedicated user interface.
However, this approach cannot scale to payments that are done directly by smart contracts. E.g., multisig wallets, decentralized autonomous organizations (DAO), and exchange services.
The root source is the difficulty to trace and parse payments done by so called "internal transactions".
In this ERC, we propose a standard and generic way to provide a proof of payment with an evm log.

Motivation

Online merchants and vendors who provide physical goods in return to crypto payments have to be able to monitor payments done by their users, and users should also be able to provide a proof to the merchants about their payments (in the case of a dispute).
Classical approaches are using the tx hash as a witness to a performed payment. In this approach the merchant's website generates a payment tx, and record the connection between the payment and the good to be delivered in an internal database.

The current approach, while being simple and elegant, will fail to scale to the case where the payment is done by a smart contract.
A payment that is done by a smart contract is hard to detect and parse. A notable example is the coinbase deposit bug which allowed users to give themselves unlimited ether deposit balance.
On the other hand, logging the payment as solidity event is easy to monitor with standard web3 tools.

The motivation to support payment by smart contracts is two-fold, namely, payment by online conversion services, and payment by autonomous organizations.

Online conversion services

Decentralized applications such as Kyber Network, Bancor, AirSwap, 0x and Etherdelta, make it possible for a merchant to accept payment in any ERC20 token and convert it to a currency of his choice (e.g, Ether).
An important feature of such conversion service is that the conversion is done in the same transaction of the payment. Hence, the final payment should eventually be performed by a smart contract that does the conversion and send the output to the merchant's contract.

Decentralized autonomous organizations

The rise of DAO applications, such as DAOStack, Aragon, Gnosis, Harbour and Colony, makes it imperative to allow non-human agent to pay on behalf of the organization.
In the most simple setting, payment by a multisig wallet is already hard to capture under the classical tx hash monitoring scheme.
In the more advance setting, an autonomous organization might want to buy physical goods in an online stores for his members, but only provided a formal decision of the organization.

Specification

We introduce a standard to log payments that are done by smart contracts:

event ProofOfPayment(address indexed _payer, address indexed _payee, address _token, uint _amount, bytes _data);

where:

  1. _payer denotes the agent who is paying for the product. We note that it need not be msg.sender.
  2. _payee denotes the agent who is receiving the payment.
  3. _token denotes the address of the token (e.g., ERC20, but could also be of other types) that is used for the payment. As a convention, we propose to denote Ether with 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.
  4. _amount denotes the amount paid, in basic token units (e.g., wei).
  5. _data denotes application specific auxiliary data. Usually in the form of the hash of multiple application specific parameters.

Rationale

Making a community standard will help providing standard software libraries to allow crypto payments for merchants.

In this document, we assume the agent is paying to a contract it is familiar with,
and typical token payment will be with the approve and transferFrom scheme.
Under this assumption the proof of payment is guaranteed to be logged on the blockchain.
We note that such proof does not guarantee the delivery of the product in any way.
However, in the case of a dispute it could be used by both parties to make their claims, either in a court of law, or in a call with customer support center.

We decided to explicitly log the beneficiary address in order to allow conversion services to pay on behalf of the original tx sender.
The _data field is intended to encode a compact 32 bytes proof for application specific parameters that are relevant in the payment process.

Implementation

As a tribute to the famous pizza purchase, we provide a sample implementation of a pizza store contract.
The contract implement a proof of payment for payment either in Ether or Pizza token.
In both cases the _data field is a witness to the number of ordered pizza slices and the beneficiary home address.

UPDATE: a first mainnet implementation can be found here

Copyright

Copyright and related rights waived via CC0.

@yaronvel yaronvel changed the title ERC: Proof of Payment by smart contracts ERC 1257: Proof of Payment by smart contracts Jul 25, 2018

@mickys

This comment has been minimized.

Copy link

mickys commented Jul 25, 2018

Your assumption about DAOs is a bit problematic, because they are not actually able to read events emitted in contracts. Only external listeners do that. ( Please correct me if this has been changed recently ).

While DAO's should already have an internal state that is updated upon successful payment execution.

I do see value in standardising the payment interface and being able to roll out a generic event listener.

I would love to see some use cases that you're thinking of.

@yaronvel

This comment has been minimized.

Copy link

yaronvel commented Jul 25, 2018

@mickys they idea is that external listener will do it in behalf of the DAO.
Note that this event does not guarantee the delivery of the product. On gives a proof for the payment.
So in case of a dispute, an human agent will have to act on behalf of the DAO.

Adding @leviadam from dao stack to the discussion to give more insights on DAO needs.

@yaronvel

This comment has been minimized.

Copy link

yaronvel commented Jul 25, 2018

@mickys regarding internal DAO state. It is a proof for the dao members at best. One cannot expect merchant customer support to read the DAO code and understand the proof.

@leviadam

This comment has been minimized.

Copy link

leviadam commented Jul 25, 2018

I think it is a good idea that transfers will have an identifier.
It is important for example for UIs of DAOs referencing transfers.

I might advise to also standard the way data is constructed. Like a hash of a counter+contract address. This cannot be enforced, but can still be useful.

@xinbenlv

This comment has been minimized.

Copy link
Contributor

xinbenlv commented Jul 26, 2018

Interesting idea. One brainstorm input: how will a return / cancel of payment look like in this ERC?

@yaronvel

This comment has been minimized.

Copy link

yaronvel commented Jul 26, 2018

@leviadam contract address is already embedded in the event topic.
As for standardizing the rest, would be happy to hear suggestions. If we had ideas on what should be there we could add specific fields.

@yaronvel

This comment has been minimized.

Copy link

yaronvel commented Jul 26, 2018

@xinbenlv interesting question. It give rise to a lot of questions like whether returning the payment in delay should be allowed and what is the actual value of delayed payment.

On a practical level though, at least on first stage, the needed software stack is on the merchant side. We cannot expect the consumer to run its on client for that.

@yudilevi

This comment has been minimized.

Copy link

yudilevi commented Nov 19, 2018

Interesting idea.

I agree with @xinbenlv - I think that in order to have a complete solution that allows 3rd parties to track payments, the solution should also include cancellations/refunds etc., especially if 3rd parties are creating their own flows based on these proofs.

@Alexintosh

This comment has been minimized.

Copy link

Alexintosh commented Nov 28, 2018

Great proposal @yaronvel !

@mickys has a good point since the external listener introduces a point of centralization and increases friction. I assume to consume that information some kind of relay should be involved to update the DAO of the payment execution.

In the attempt to standardize the payment interface it could be useful to include a callback function besides the emission of an event.

Something like:

function paymentCallback(address indexed _payer, address _token, uint _amount, bytes _data)

In the context of the PizzaStore, it could look like this.

    function buyPizzaWithEther(uint _slices, address _beneficiary, string _homeAddress) public payable {
        require(msg.value == _slices * SLICE_PRICE_IN_ETH);

        bytes32 data = keccak256(abi.encodePacked(_slices,_homeAddress));
        
        emit ProofOfPayment(_beneficiary,0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE,msg.value,data);
        if (isContract(_beneficiary)) {
            ContractReceiver rx = ContractReceiver( _beneficiary );
            require(
                address(rx).call.value(0)(
                    bytes4(keccak256("paymentCallback")),
                   0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE,
                   msg.value,
                   data
                )
            );
        }
    }
@yaronvel

This comment has been minimized.

Copy link

yaronvel commented Nov 28, 2018

@yudilevi maybe this can be solved in application level.
There is no really payment cancellation, but rather transferring back ETH (or token).
The vendor can use same event to record the payment refund to the user and the _data will contain relevant tag that denotes the cancellation.

I guess we can add as a convention that last byte in the data denotes whether it is payment or refund.
But it can also be reasoned by the _payer and _payee addresses.

@yaronvel

This comment has been minimized.

Copy link

yaronvel commented Nov 28, 2018

@Alexintosh what you suggest will not be compatible with most existing deployed contracts.
And the payment will just revert.
For example if the payment is done from a gnosis multisig.

Your idea does make sense, but I am not sure why standards for all DAOs is needed here rather than dao-specific implementation.

Note that this standard is proposed only for the purpose of making it easier to build standard off-chain tools to track payments.
For on-chain tracking, I am not sure standardization is needed.
Every dapp can implement the recording logic along with the execution logic (if needed).

@yudilevi

This comment has been minimized.

Copy link

yudilevi commented Nov 29, 2018

@yaronvel I prefer an explicit argument over trying to figure it out by the _payer/_payee arguments. I think it will also keep it more generic.
For example, if 2 different entities have payments for each other, we would like to know whether a payment is a refund or not etc.
There might also be other different types of transfers that we might want to support later on.

@yaronvel

This comment has been minimized.

Copy link

yaronvel commented Nov 29, 2018

@yudilevi can you propose argument type? should it be boolean? uint?

@yudilevi

This comment has been minimized.

Copy link

yudilevi commented Nov 29, 2018

@yaronvel uint8 _type? uint8 should suffice I think.

@yaronvel

This comment has been minimized.

Copy link

yaronvel commented Nov 29, 2018

@yudilevi ok, I see. Sure, we are fine with adding it, but can you please offer a spec for the different values?
0 is payment. 1 is refund? other values are also defined?
Please advise.

@yudilevi

This comment has been minimized.

Copy link

yudilevi commented Nov 29, 2018

@yaronvel what do you think about defining it as bytes32, declaring some pre-defined values like "payment" and "refund" and allowing custom values? I can see values like "fee" or "subscription" in the future but I wouldn't want to limit apps to these as they might be a bit too narrow for some.

The handler can decide ignore the values and payment providers can declare application specific values? Do you think that it gives too much flexibility (and possibly lead to non standard implementations)?

@yaronvel

This comment has been minimized.

Copy link

yaronvel commented Nov 29, 2018

@yudilevi can live with it, though maybe uint256 is better, because comparison is easier. unless you aim to hold sha3 of a string?
I am fine with both. Just does not like bytes32 much because how solidity treats it.
I mean it will force you to do:
bytes32 constant REFUND = bytes32(0).
Also at the backend level it give rise to confusing endianess. Meaning the order of bytes.

@yudilevi

This comment has been minimized.

Copy link

yudilevi commented Nov 29, 2018

@yaronvel I agree, it might be too much of a hassle.
So uint256 with some predefined values, reserved range and application specific custom values? :)

@yaronvel

This comment has been minimized.

Copy link

yaronvel commented Nov 29, 2018

@yudilevi sure, want to take the lead on the predefined value and ranges spec? can add you to the EIP. PR is welcomed (though not sure if possible), or just write something here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment