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 EIP-5289: Ethereum Notary Interface #5289

Merged
merged 45 commits into from
Aug 9, 2022
Merged
Changes from 24 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
f39aa65
Add eip-legal-smart-contracts
Pandapip1 Jul 18, 2022
43ca35a
Self-assign EIP number 5289
Pandapip1 Jul 18, 2022
5e85c82
Add FEM link
Pandapip1 Jul 18, 2022
667751e
Empty commit
Pandapip1 Jul 18, 2022
23a899a
Apply suggestions from code review
Pandapip1 Jul 19, 2022
b07d106
Commit changes so far
Pandapip1 Jul 19, 2022
33a29c6
Add custom revert reason
Pandapip1 Jul 19, 2022
1faa90f
Make a few more changes
Pandapip1 Jul 19, 2022
44e80f2
Not always ECDSA
Pandapip1 Jul 19, 2022
e09e5d4
Missed a #
Pandapip1 Jul 19, 2022
d228fa2
Add language
Pandapip1 Jul 19, 2022
bd6c88f
Change name based on @xinvenlv's suggestion
Pandapip1 Jul 20, 2022
30d4a91
Change to multi-document libraries
Pandapip1 Jul 20, 2022
a602ece
Fix typo
Pandapip1 Jul 20, 2022
2e68eae
Fix small typo
Pandapip1 Jul 20, 2022
6e23081
Remove a hypen
Pandapip1 Jul 20, 2022
ca7e0e2
Move back to erc
Pandapip1 Jul 20, 2022
2fef7d2
Update EIPS/eip-5289.md
Pandapip1 Jul 20, 2022
f1e3657
Make documentSigned return all relevant info
Pandapip1 Jul 20, 2022
c790c99
Update ethereum magicians link
Pandapip1 Jul 21, 2022
ee2f1c7
Add 'Smart' to title
Pandapip1 Jul 22, 2022
03fa0a0
Ethereum Notary Interface
Pandapip1 Jul 22, 2022
b6ffdaa
Merge branch 'ethereum:master' into eip-legal-smart-contracts
Pandapip1 Jul 26, 2022
bc86a0c
Remove PDF requirement
Pandapip1 Jul 27, 2022
83d186a
Remove reference to OZ
Pandapip1 Jul 29, 2022
0f4f727
Create SignatureChecker.sol
Pandapip1 Jul 29, 2022
b80928b
Create ECDSA.sol
Pandapip1 Jul 29, 2022
3b5b7e4
Create Strings.sol
Pandapip1 Jul 29, 2022
bebdcf2
Create Address.sol
Pandapip1 Jul 29, 2022
64ed337
Create IERC1271.sol
Pandapip1 Jul 29, 2022
5234c6f
Typo
Pandapip1 Jul 29, 2022
46b796c
Merge branch 'ethereum:master' into eip-legal-smart-contracts
Pandapip1 Jul 29, 2022
ea0c2ae
Move some files to the assets folder
Pandapip1 Jul 29, 2022
0491790
Create ERC165Storage.sol
Pandapip1 Jul 29, 2022
30d084c
Create ERC165.sol
Pandapip1 Jul 29, 2022
9d20d44
Create IERC165.sol
Pandapip1 Jul 29, 2022
75b5142
Create IERC5289Library.sol
Pandapip1 Jul 29, 2022
f9f23e2
Use internal links
Pandapip1 Jul 29, 2022
0eed597
Not sure how I managed to mess that up
Pandapip1 Jul 29, 2022
62aaa34
eipw errors
Pandapip1 Aug 4, 2022
58bf6e5
Significantly reduce the length of the messages
Pandapip1 Aug 5, 2022
bbbffc9
Merge branch 'ethereum:master' into eip-legal-smart-contracts
Pandapip1 Aug 5, 2022
c4a0bb6
When you remember account abstraction is going to be a thing
Pandapip1 Aug 5, 2022
84549b4
Some more optimizations
Pandapip1 Aug 5, 2022
554da56
Update eip-5289.md
Pandapip1 Aug 9, 2022
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
139 changes: 139 additions & 0 deletions EIPS/eip-5289.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
---
eip: 5289
title: Ethereum Notary Interface
description: Allows Smart Contracts to be Legally Binding Off-Chain
author: Pandapip1 (@Pandapip1)
discussions-to: https://ethereum-magicians.org/t/pr-5289-discussion-notary-interface/9980
status: Draft
type: Standards Track
category: ERC
created: 2022-07-16
requires: 165, 191, 1271
---

## Abstract

Currently, the real-world applications of smart contracts are limited by the fact that they aren't legally binding. This EIP proposes a standard that allows smart contracts to be legally binding by providing IPFS links to legal documents and ensuring that the users of the smart contract have privity with the relevant legal documents.

## Motivation

NFTs have oftentimes been branded as a way to hold and prove copyright of a specific work. However, this, in practice, has almost never been the case. Most of the time, NFTs have no legally-binding meaning, and in the rare cases that do, the NFT simply provides a limited license for the initial holder to use the work (but cannot provide any license for any future holders).

## 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.

### Signing a Document

Calculate the signature of the following. The [EIP-191](./eip-191) version byte is `0x53`:

```solidity
keccak256(bytes("\x19Sign Legal Document:") + multihash)
```

This MUST be compatible with [EIP-1271](./eip-1271.md).

Wallets MUST display a rendering of the linked file to the user.

### Legal Contract Library Interface

```solidity
/// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

interface IContractLibrary is IERC165 {
/// @notice Emitted when signDocument is called
event DocumentSigned(address indexed signer, uint48 indexed documentId);

/// @notice The IPFS multihash of the legal document. This MUST use a common file format, such as PDF, HTML, TeX, or Markdown.
function legalDocument(uint48 documentId) public pure returns (bytes memory);

/// @notice Returns whether or not the given user signed the document, and if so, when they did.
/// @dev If the user has not signed the document, the timestamp may be anything.
function documentSigned(address user, uint48 documentId) public view returns (boolean signed, uint64 timestamp);

/// @notice Provide a signature
/// @dev This MUST be validated by the smart contract. This MUST emit DocumentSigned or throw.
function signDocument(address signer, uint48 documentId, bytes memory signature) public;
}
```

### Requesting a Signature

To request that certain documents be signed, revert with the following reason:

```solidity
string.concat("Must Sign Documents:", libraryAddress1, "-", documentId1, ",", libraryAddress2, "-", documentId2, ...)
```

Example:

```solidity
"Must Sign Documents:0x0000000000000000000000000000000000000000-1,0x000000000000000000000000000000000000000-2,0x00000000000000000000000000000000000dead-743"
```

## Rationale

- `uint64` was chosen for the timestamp return type as 64-bit time registers are standard.
- `uint48` was chosen for the document ID as it uses 26 less bytes than `uint256`, and it is unlikely that more than 4 billion documents will ever need be signed in the same library. If so, oh well, just deploy another library.
- `signDocument` allows for the signing user to be different than the transaction sender so that it is possible to subsidize this type of transaction.
- IPFS is mandatory because the authenticity of the signed document can be proven.

## Backwards Compatibility

No backwards compatibility issues found.

## Reference Implementation

### Legal Contract Library

```solidity
/// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/introspection/ERC165Storage.sol";
import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
Pandapip1 marked this conversation as resolved.
Show resolved Hide resolved

contract ContractLibrary is IContractLibrary, ERC165Storage {
uint48 private counter = 0;
mapping(uint48 => bytes memory) private multihashes;
mapping(uint48 => bytes32) private signatureHashes;
mapping(uint48 => mapping(address => bool)) signed;
mapping(uint48 => mapping(address => uint64)) signedAt;

constructor() {
_registerInterface(type(ILegalDocument).interfaceId);
}

function registerDocument(bytes memory multihash) public {
uint48 documentId = counter++;
multihashes[documentId] = multihash;
signatureHashes[documentId] = keccak256(abi.encodePacked(bytes("\x19Sign Legal Document:"), multihash));
}

function legalDocument(uint48 documentId) public view returns (bytes memory) {
return multihash[documentId];
}

function documentSigned(address user, uint48 documentId) public view returns (boolean signed, uint64 timestamp) {
return signed[documentId][user], signedAt[documentId][user];
}

function signDocument(address signer, bytes memory signature) public {
require(SignatureChecker.isValidSignatureNow(signer, signatureHash[documentId], signature), "Invalid signature");
signed[documentId][signer] = true;
signedAt[documentId][signer] = uint64(block.timestamp);
emit DocumentSigned(signer, documentId);
}
}
```

## Security Considerations

No security considerations.
Copy link
Contributor

Choose a reason for hiding this comment

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

Not required for a draft, but a note about the definition of privity and legal jurisdictions might be nice before final


## Copyright

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