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

ERC-1888: Transferable Certificate (Claim) #1888

Closed
greg-flexidao opened this issue Mar 31, 2019 · 17 comments
Closed

ERC-1888: Transferable Certificate (Claim) #1888

greg-flexidao opened this issue Mar 31, 2019 · 17 comments

Comments

@greg-flexidao
Copy link

greg-flexidao commented Mar 31, 2019

---
eip: 1888
title: ERC-1888 Transferable Certificate
author: Grzegorz Bytniewski <g.bytniewski@flexidao.com>, Francesc Ortiz <f.ortiz@flexidao.com>, Joseph Bagaric <bagaricjos@gmail.com>
type: Standards Track
category: ERC
status: Draft
created: 2019-03-31
discussions-to: https://github.com/ethereum/EIPs/issues/1888
requires: 1155
---

Simple Summary

A standard interface for a transferable certificate (claim). That extends highly flexible ERC-1155 Multi-token standard.

Abstract

This standard extends the ERC-1155 Multi Token Standard, with transferable certificate. The standard describes functions for issuing and claiming of transferable certificates.
These certificates are attested by third parties (issuers).
Primarily thought to accommodate the real-life needs of a utility sector (electricity, gas, water, heat).

Motivation

Tokens once issued are immutable by design. We cannot simply burn (invalidate) someone else's tokens, without their explicit permission. This design principle is not suitable for use cases where the data source, due to its nature cannot be 100% trusted, yet someone has to take that responsibility for the system to operate (i.e. utility sector).

In some real-life cases, there will be always certifying authorities (issuers) responsible for providing such data to the blockchain, without being in ultimate control of all the system end-points (i.e. utility metering devices at end-user premises).

Inspired by the concept of ERC-735 claims, the certificate itself is a light transferable claim which validity can be revoked by the issuer. Trust is here transferred to the issuers of the certificates.

ERC-1888 is not specific to the utility sector, and many other applications can potentially benefit from its additional features extending ERC-1155.

Definitions

issuer: is another smart contract, which issues a transferable certificate.

certificate: A certificate is an information an issuer has about the certificate (on top of the token balance). This contains the following:

  • topic: uint256 number which represents the topic of the certificate. (e.g. 1: origin-of-electricity, 2: electricity-with-origin) EXACT NUMBERING SCHEME TBD
  • issuer: The issuer is a contract address itself, at which the claim can be verified using the call validityData.
  • validityData: validity call data to issuer. Contains the signature of the function to verify the certificate as well as encoded parameters of the function to ultimately check the validity of the certificate.
  • issuenceData: The hash of the certificate data, sitting in another location, a bit-mask, or actual data based on the certificate topic.

Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

Smart contracts implementing the ERC-1888 standard MUST implement the ERC1888, ERC1155 and ERC165interfaces.

Certificate Structure

The certificates issued is bounded to the tokenId. Returns the certificate's properties.

struct Certificate {
    uint256 topic;
    address issuer; // msg.sender
    bytes validityData;
    bytes issuenceData;
}

Methods

issue

Requests the ADDITION or the CHANGE of a certificate from an issuer.

MUST pass the _to and _value to ERC1155.safeTransferFrom function.

MUST pass 0x0 as _from to ERC1155.safeTransferFrom function.

MUST follow the ERC-1155 standard regarding minting new tokens.

issuer of the certificate SCHOULD be msg.sender
Caller CAN be approved to issue new certificates, on top of ERC1155 token minting permission

MUST check validity of the certificate before continuing with the token minting
(i.e. issuer.staticcall(validityData) should return (true, ))

Possible certificate topics:

  • 01: Utilities
    • 01: Electricity
      • 01: Origin ("Green certificates")
      • 02: Origin + Electricity
      • 03: Flexibility
      • 04: Flexibility + Electricity
      • 05: Efficiency ("White certificates")
    • 02: Water
    • 03: Gas
    • 04: Heat

(TODO: standardize and add more topics)

topic SCHOULD define the data format
(i.e topic = 010101, could standardize format of the data to (uint48 startTime, uint48 endTime, address origin))

Returns id: minted ERC-1155 tokenId according to ERC-1155 implementation

MUST emit IssuanceSingle upon successful token minting

function issue(
       address _to,
       bytes calldata _validityData,
       uint256 _topic,
       uint256 _value,
       bytes calldata _issuanceData
   ) external returns (uint256 id);

batchIssue

Requests the ADDITION or the CHANGE of a certificate from an issuer.

MUST pass the _to and _value to ERC1155.safeBatchTransferFrom function.

MUST pass 0x0 as _from to ERC1155.safeBatchTransferFrom function.

MUST follow the ERC-1155 standard regarding minting new tokens.

issuer of the certificate SCHOULD be msg.sender.

Caller CAN be approved to issue new certificates, on top of ERC1155 token minting permission

Each certificate in the batch MUST be validated against the same requrements as in the issue method.

Returns array of id: minted ERC-1155 tokenIds according to ERC-1155 implementation

MUST emit IssuanceBatch upon successful token minting

function batchIssue(
       address _to,
       bytes[] calldata _validityData,
       uint256[] calldata _topics,
       uint256[] calldata _values,
       bytes[] calldata _issuanceData
   ) external returns (uint256[] memory ids);

safeTransferAndClaimFrom

Claims (burns) certificate for the _to while transferring.

_from and _to addresses CAN be equal, which would result in self claim

SCHOULD pass the _from, _to, _id, _value, _data to ERC1155.safeTransferFrom function

MUST decrease the balance of the _to address by _value

MUST increase the claimedBalance of the _to address by _value

_claimData additional data with format specified by the topic to be logged in ClaimBatch event

SCHOULD throw if the token _id lost validity.

MUST emit TransferSingle upon successful token burning

MUST emit ClaimSingle upon successful token burning

   function safeTransferAndClaimFrom(
       address _from,
       address _to,
       uint256 _id,
       uint256 _value,
       bytes calldata _data
       bytes calldata _claimData
   ) external;

safeBatchTransferAndClaimFrom

Claims (burns) multiple certificates for the _to while transferring.

_from and _to addresses CAN be equal, which would result in self claim

SCHOULD pass the _from, _to, _ids, _values, _data to ERC1155.safeBatchTransferFrom function

MUST decrease the corresponding _ids[i] balance of the _to address by _values[i]

MUST increase the corresponding _ids[i] claimedBalance of the _to address by _values[i]

MUST throw is the lengths of the _ids, _values, _cliamData are different

_claimData additional data with format specified by the certificate topic to be logged in ClaimBatch event

SCHOULD throw if any of the token _ids lost validity

MUST emit TransferBatch upon successful token burning

MUST emit ClaimBatch upon successful token burning

function safeBatchTransferAndClaimFrom(
    address _from,
    address _to,
    uint256[] calldata _ids,
    uint256[] calldata _values,
    bytes calldata _data,
    bytes[] calldata _claimData
) external;

getCertifiate

Returns a certificate by _id

function getCertificate(uint256 _id) public view
    returns (
        address issuer,
        uint256 topic,
        bytes memory validityCall,
        bytes memory data
    ):

claimedBalanceOf

Returns an _owner's claimed balance by _id.

function claimedBalanceOf(
    address _owner, 
    uint256 _id
) external  view returns (uint256);

claimedBalanceOfBatch

Returns an array of _owners[i]'s claimed balance by _ids[i].

function claimedBalanceOfBatch(
    address[] calldata _owners,
    uint256[] calldata _ids
) external view returns (uint256[] memory);

Events

IssuanceSingle

MUST be triggered when certificate was successfully created.

_issuer MUST be msg.sender

event IssuanceSingle(
    address indexed _issuer, // msg.sender
    uint256 indexed _topic,
    uint256 _id,
    uint256 _value
);

IssuanceBatch

MUST be triggered when certificate batch was successfully created.

_issuer MUST be msg.sender

event IssuanceBatch(
    address indexed _issuer, // msg.sender
    uint256[] indexed _topics,
    uint256[] _ids,
    uint256[] _values
);

ClaimSingle

Either ClaimSingle or ClaimBatch MUST be emitted when certificates are claimed.

_claimIssuer MUST be initial owner of the certificate (_from address in safeTransferAndClaimFrom)

_claimData CAN contain additional data with format specified by certificate topic

   event ClaimSingle(
       address indexed _claimIssuer,
       address indexed _claimSubject,
       uint256 indexed _topic,
       uint256 _id,
       uint256 _value,
       bytes _claimData
   );

ClaimBatch

Either ClaimSingle or ClaimBatch MUST be emitted when certificates are claimed.

_claimIssuer MUST be initial owner of the certificate (_from address in safeBatchTransferAndClaimFrom)

_claimData CAN contain additional data with format specified by certificate topics[i]

   event ClaimBatch(
       address indexed _claimIssuer,
       address indexed _claimSubject,
       uint256[] indexed _topics,
       uint256[] _ids,
       uint256[] _values,
       bytes[] _claimData
   );

Rationale

uri is already part of ERC-1155 therefore it has been omitted from the certificate specification

Solidity Interface

// SPDX-License-Identifier: MIT
pragma solidity 0.8.4;

interface ERC1888 /* is ERC1155 */{
   event IssuanceSingle(address indexed _issuer, uint256 indexed _topic, uint256 _id, uint256 _value);
   event IssuanceBatch(address indexed _issuer, uint256[] indexed _topics, uint256[] _ids, uint256[] _values);
   
   event ClaimSingle(address indexed _claimIssuer, address indexed _claimSubject, uint256 indexed _topic, uint256 _id, uint256 _value, bytes _claimData);
   event ClaimBatch(address indexed _claimIssuer, address indexed _claimSubject, uint256[] indexed _topics, uint256[] _ids, uint256[] _values, bytes[] _claimData);
   
   function issue(address _to, bytes calldata _validityData, uint256 _topic, uint256 _value, bytes calldata _data) external returns (uint256 id);
   function batchIssue(address _to, bytes[] calldata _validityData, uint256[] calldata _topics, uint256[] calldata _values, bytes[] calldata _data) external returns (uint256[] memory ids);
   
   function safeTransferAndClaimFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data, bytes calldata _claimData) external;
   function safeBatchTransferAndClaimFrom(address _from, address _to, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data, bytes[] calldata _claimData) external;

   function getCertificate(uint256 _id) public view returns (address issuer, uint256 topic, bytes memory validityCall, bytes memory data);
   function claimedBalanceOf(address _owner, uint256 _id) external view returns (uint256);
   function claimedBalanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) external view returns (uint256[] memory);

}

References

Articles & Discussions

Standards

Implementations

Copyright

Copyright and related rights waived via CC0.

@greg-flexidao greg-flexidao changed the title ERC: Transferable Utility Claim (Certificate) ERC: Transferable Certificate (Claim) Apr 20, 2019
@XanderXioMvB
Copy link

XanderXioMvB commented May 11, 2019

So I could potentially lend one of my tokens to somebody, and then call it back to my wallet, for example renting out a sword for a set amount of time for an X amount of funds, the person renting the item knows they will have the item until the contract completes and I know I will get it back exactly when I should guaranteed or am I not understanding this correctly?

@greg-flexidao
Copy link
Author

@XanderXioMvB : it more allows for revoking the validity of the token issued (a sword), which can be done on a pre-defined condition (lend time) in a transparent smart contract. You would not be in control of the token once you transfer it out, yet the game could potentially implement a simple mechanism to issue a new token (a sword) with pre-defined validity to be able to lend it to someone, keeping your sward as a deposit. After the lending time has passed the lend token would lose its validity and you could get your sword back from the deposit.

@axic axic changed the title ERC: Transferable Certificate (Claim) ERC-1888: Transferable Certificate (Claim) May 23, 2019
@kosecki123
Copy link

@bytniak what is the purpose of _claimSubject in ClaimSingle and ClaimBatch events?

@greg-flexidao
Copy link
Author

Thanks for noticing @kosecki123 ! I updated the description. In general _claimSubject == _to, so for whom/what you are claiming this certificate. _claimSubject and _claimIssuer are named on purpose differently (they equal to_to,_from respectively) to differentiate the transfer arguments and the claim arguments as they are logically different operations.

@josipbagaric
Copy link

josipbagaric commented May 11, 2020

Hi @bytniak,

Is there a reason _claimData is of type bytes32 while certificate data is of type bytes?

I'd like to store more than 32 bytes as _claimData, so I'm curious if this is an arbitrary restriction that we could lift.

@greg-flexidao
Copy link
Author

Hi @JosephBagaric ,
as from ABIEncoderV2 bytes[] are supported for the batch calls, I don't think there is reason to keep _claimData as bytes32 instead of bytes.
We can therefore also "unlock" the batchIssue function!

@kosecki123
Copy link

@bytniak we run into this problem with claimData https://github.com/energywebfoundation/origin/pull/1006/files#r423763681

I think the easiest way to overcome that would be to make id field in the ClaimSingle event as indexed.

@KaddieIII
Copy link

Is there any kind of paper that shows the different topics and data formats? Could someone define his own topic and data format?

@greg-flexidao
Copy link
Author

greg-flexidao commented Jun 24, 2020

@kosecki123 @JosephBagaric the id filed cannot be changed only in ClaimSingle, we would have to change it also in ClaimBatch and this in turn imposes another issue... If you make array of ids indexed then the indexed value is a hash of the array and not the value of each id in the array. I think this is the same reason why in underlying erc1155 the ids are not indexed too.

@KaddieIII there is no reference to topics and data formats at the moment anyone can define his own topics and data formats. This could be subject to standardization at the later stage.

@josipbagaric
Copy link

@bytniak Has there been an update of the interface to the latest solidity ^0.8.0?
I've been looking at implementing the batchIssue functionality, and I have some questions about the interface:

function batchIssue(bytes[] _data, uint256[] _topics, uint256[] _value, bytes32[] _signatures) external returns(uint256[]);
  1. What would we store as the _signatures?
  2. The interface doesn't define a _to address(es) for issuance.

@greg-flexidao
Copy link
Author

The spec has been updated based on your comments. @JosephBagaric

@josipbagaric
Copy link

Thanks @bytniak. I've updated our contracts to the latest spec interface and solc complained about the following:

  1. In batchIssue() the return array is missing the storage type. I suggest to change it to:
function batchIssue(...) external returns (uint256[] memory ids);
  1. In getCertificate() the return types are calldata but solc only accepts calldata when used in function params and not return types. I suggest we change it from calldata to memory:
function getCertificate(uint256 _id) external view returns (address issuer, uint256 topic, bytes memory validityCall, bytes memory data);
  1. Also, I've noticed a typo in batchIssue. Return variable name is _ids in the interface but ids in the spec. I suggest we just use ids in both.

@greg-flexidao
Copy link
Author

Thanks @JosephBagaric! All the points has been fixed.

@josipbagaric
Copy link

Thanks @bytniak, this looks great and works great in our codebase. :)

@Julian-dev28
Copy link

Hello Is there a repo with an ERC-1888.sol example?

Thank you

@josipbagaric
Copy link

Hello Is there a repo with an ERC-1888.sol example?

Thank you

Sure. We have an audited ERC-1888 implementation at Energy Web Foundation:

https://github.com/energywebfoundation/origin/blob/master/packages/traceability/issuer/contracts/Registry.sol

@MicahZoltu
Copy link
Contributor

Closing this issue for housekeeping purposes. People are welcome to continue discussing in this thread, but for additional visibility an EIP should be created or the conversation should be migrated to https://ethereum-magicians.org/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants