-
Notifications
You must be signed in to change notification settings - Fork 5.4k
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: Offchain checks for ERC-20 distribution #7099
Closed
Closed
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
4161ad0
Add eip-draft_offchain_checks.md
civia-code 40cd099
fixed some Lint errors
mtsquant 78b501d
rename file to EIPS/eip-7099.md
mtsquant cd20395
fixed header format
mtsquant 9828fab
Fix formatting issues
mtsquant e74cdd3
Fix formatting issues
mtsquant 93d3739
Merge branch 'ethereum:master' into master
civia-code b5f6426
Merge branch 'ethereum:master' into master
civia-code d5d3f5c
Merge branch 'ethereum:master' into master
civia-code b2a6dc6
Removed blank sections from the document.
mtsquant 1fd77b4
added missing section ## Rationale
mtsquant 31edea3
Merge branch 'ethereum:master' into master
civia-code 5a4bdf5
Merge branch 'master' of https://github.com/civia-code/EIPs
mtsquant 0a1b7e8
Merge branch 'ethereum:master' into master
civia-code 2dcbc65
Merge branch 'ethereum:master' into master
civia-code e2b2cf8
Merge branch 'ethereum:master' into master
civia-code bc9a5d3
title: Offchain checks for ERC-20 distribution
mtsquant 63b61c8
Merge branch 'master' of https://github.com/civia-code/EIPs
mtsquant File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,230 @@ | ||||
--- | ||||
eip: 7099 | ||||
title: Offchain checks for ERC-20 distribution | ||||
description: Low cost distribution of ERC-20 tokens with off-chain check writing and batched or bundled mint | ||||
author: CiviaTeam (@civia-code), Peng Cheng <[email protected]>, Roc Xu (@goldroc-Hsu), SHAOFEI GAO (@sfgao), Camille Li (@camille1022), Joye Wang (@0xjoye) | ||||
discussions-to: https://ethereum-magicians.org/t/eip-7099-off-chain-checks/14526 | ||||
status: Draft | ||||
type: Standards Track | ||||
category: ERC | ||||
created: 2023-05-31 | ||||
requires: 20, 712 | ||||
--- | ||||
|
||||
## Abstract | ||||
|
||||
We propose a low cost approach to distribute [ERC-20](./eip-20.md) tokens for high-frequency token issuance use cases such as game rewards, implemented with a one-time deployed Check contract, the off-chain issuing and signing of Checks by the issuers, and batching or bundling of the Checks to mint tokens on-chain by the receivers. | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sentence is a bit of a run on. |
||||
|
||||
## Motivation | ||||
|
||||
Mass token issuance is one of the most common blockchain activities, traditionally done by direct transfers of tokens to receiver addresses, or programming the issuance logic into smart contracts for receivers to mind their tokens on-chain. Both approaches involve high gas cost especially in large receiver base or high frequency issuance cases. | ||||
|
||||
This approach minimize on-chain operations while maintaining security with the following steps: | ||||
|
||||
1. Issuer register the token once with the Check contract which would mint tokens for the receivers | ||||
1. Issuer writes as many off-chain Checks as needed to receivers with no gas, Checks signed by the issuer are non-reputable | ||||
1. Receiver collects any number of Checks off-chain with no gas cost | ||||
1. Receiver decides when to batch or bundle multiple Checks which can be minted on-chain, with substantial savings in gas cost | ||||
|
||||
## Specification | ||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. | ||||
|
||||
The Check contract MAY be deployed by any party prior to Check issuance, and MUST implement the following interfaces: | ||||
|
||||
```javascript | ||||
interface IERC20Check { | ||||
/** | ||||
* The Check data structure | ||||
* @param tokenAddr token contract address | ||||
* @param issuerAddr issuer's Check signing address | ||||
* @param receiverAddr receiver address | ||||
* @param beginId begin id of the issued Check | ||||
* @param endId end id of the issued Check | ||||
* @param amt token amount in the Check | ||||
* For each Check issued individually, beginId == endId | ||||
* For bundled Check, beginId < endId, representing a Check sequence | ||||
*/ | ||||
struct Check { | ||||
address tokenAddr; | ||||
address issuerAddr; | ||||
address receiverAddr; | ||||
uint256 beginId; | ||||
uint256 endId; | ||||
uint256 amt; | ||||
} | ||||
|
||||
/** | ||||
* Issuer register a token with the Check contract | ||||
* @dev MUST revert if the msg.sender has no minter role | ||||
* @param tokenAddr token contract address | ||||
* @param issuerAddr issuer's Check signing address | ||||
* @param maxAmt max number of tokens allowed to issue with Check | ||||
* un_register() is not supported because Checks issued are not revokable | ||||
*/ | ||||
function register(address tokenAddr, address issuerAddr, uint256 maxAmt); | ||||
|
||||
/** | ||||
* Get the last minted Check id for a (tokenAddr, receiverAddr) tuple | ||||
* @dev MUST revert if tokenAddr is not registered | ||||
* @param tokenAddr token contract address | ||||
* @param receiverAddr receiver address | ||||
* @return The last minted Check id for a (tokenAddr, receiverAddr) tuple | ||||
*/ | ||||
function getLastCheckId(address tokenAddr, address receiverAddr) view returns (uint256); | ||||
|
||||
/** | ||||
* Batch mint. Checks in the batch MAY have different tokenAddr, receiverAddr | ||||
* Check numbers for any (tokenAddr, receiverAddr) tuple MUST be continuous, incremental | ||||
* @dev MUST revert if any signature in the batch is invalid | ||||
* @dev MUST revert if the beginId for a (tokenAddr, receiverAddr) != last minted Check id + 1 | ||||
* @dev the Check contract MUST keep a record of the last minted Check id for all (tokenAddr, receiverAddr) | ||||
* @param checks list of Check data | ||||
* @param [v|r|s]_[issuer|receiver] list of EIP-712 signatures corresponding to each Check | ||||
*/ | ||||
function mint( | ||||
Check [] memory checks, | ||||
uint8[] memory v_issuer, | ||||
bytes32[] memory r_issuer, | ||||
bytes32[] memory s_issuer, | ||||
uint8[] memory v_receiver, | ||||
bytes32[] memory r_receiver, | ||||
bytes32[] memory s_receiver | ||||
); | ||||
} | ||||
``` | ||||
|
||||
### Off-chain Check data | ||||
|
||||
* The off-chain Check data MUST comply with the Check data structure defined in the `IERC20Check` interface | ||||
* beginId for each (tokenAddr, receiverAddr) MUST starts with 1 | ||||
* Checks issued individually from the issuer MUST have beginId == endId | ||||
* In the bundle request sent from the receiver to the issuer, Check numbers for a (tokenAddr, receiverAddr) tuple MUST be continuous and incremental for the Check sequence | ||||
* The issuer's response to the bundle request includes the bundled Check data, in which beginId < endId, representing a Check sequence bundled in a single Check | ||||
* Issuer MUST sign each Check before sending to receiver | ||||
* Receiver MUST sign each received Check (individual or bundled) before submit to the Check contract to mint | ||||
|
||||
* Example of a single signed Check from issuer | ||||
|
||||
```json | ||||
{ | ||||
"tokenAddr":"0x80307b478b1e4cc06c5ED1a4cedD0d6Bf312dd4E", | ||||
"issuerAddr":"0x39e60EA6d6417ab2b4a44f714b7503748Ce658eD", | ||||
"receiverAddr":"0x39e60ea6d6417ab2b4a44f714b7503748ce658ed", | ||||
"beginId":10, | ||||
"endId":10, | ||||
"amt":"32000000000000000000", | ||||
"sig": { | ||||
"r":"0x1ed1f53536f6568c9208e63886e367be4cc1ad55fca38299b65bc1c216f1aecc", | ||||
"s":"0x1a682018ceb4a7123d86e1ae6216e243f806997480cbe05aa7660bdbe912ad81", | ||||
"v":27 | ||||
} | ||||
} | ||||
``` | ||||
|
||||
* Example of a bundle Check request sent by the receiver to the issuer: "please bundle Check 10-11 into a single Check" | ||||
|
||||
```json | ||||
[ | ||||
{ | ||||
"tokenAddr":"0x80307b478b1e4cc06c5ED1a4cedD0d6Bf312dd4E", | ||||
"issuerAddr":"0x39e60EA6d6417ab2b4a44f714b7503748Ce658eD", | ||||
"receiverAddr":"0x39e60ea6d6417ab2b4a44f714b7503748ce658ed", | ||||
"beginId":10, | ||||
"endId":10, | ||||
"amt":"32000000000000000000", | ||||
"sig": { | ||||
"r":"0x1ed1f53536f6568c9208e63886e367be4cc1ad55fca38299b65bc1c216f1aecc", | ||||
"s":"0x1a682018ceb4a7123d86e1ae6216e243f806997480cbe05aa7660bdbe912ad81", | ||||
"v":27 | ||||
} | ||||
}, | ||||
{ | ||||
"tokenAddr":"0x80307b478b1e4cc06c5ED1a4cedD0d6Bf312dd4E", | ||||
"issuerAddr":"0x39e60EA6d6417ab2b4a44f714b7503748Ce658eD", | ||||
"receiverAddr":"0x39e60ea6d6417ab2b4a44f714b7503748ce658ed", | ||||
"beginId":11, | ||||
"endId":11, | ||||
"amt":"55000000000000000000", | ||||
"sig": { | ||||
"r":"0x0a45617ad36134d2e79a7396ccf08edd6084aa30dcdcf3e06ace4a31c8b2fda0", | ||||
"s":"0x4f61246852246a16319cf62cf4d7b8aca76d3447c6ffb4682287317c3b4c3726", | ||||
"v":28 | ||||
} | ||||
} | ||||
] | ||||
``` | ||||
|
||||
* Example of a bundled Check returned by the issuer as response to bundle request | ||||
|
||||
```json | ||||
{ | ||||
"tokenAddr":"0x80307b478b1e4cc06c5ED1a4cedD0d6Bf312dd4E", | ||||
"issuerAddr":"0x39e60EA6d6417ab2b4a44f714b7503748Ce658eD", | ||||
"receiverAddr":"0x39e60ea6d6417ab2b4a44f714b7503748ce658ed", | ||||
"beginId":10, | ||||
"endId":11, | ||||
"amt":"87000000000000000000", | ||||
"sig": { | ||||
"r":"0x51e35d25c40407fc4540f2cd06d17dfe7fb8deeab403595e2b800db5cede3507", | ||||
"s":"0x136c23c91dfc511d6e7365f011a3739e7f23d0f888210269fa7ab5f1d8f6923c", | ||||
"v":27 | ||||
} | ||||
} | ||||
``` | ||||
|
||||
### Check workflow | ||||
|
||||
```mermaid | ||||
sequenceDiagram | ||||
autonumber | ||||
participant Erc-20 Token | ||||
actor Issuer | ||||
participant CheckContract | ||||
actor Receiver | ||||
|
||||
Issuer->>+CheckContract: registers token addr and the Check signing addr with the Check contract | ||||
CheckContract-->>-Issuer: success on chain | ||||
Issuer->>+Erc-20 Token: authorizes the Check contract to mint tokens with the ERC-20 contract | ||||
Erc-20 Token-->>-Issuer: success on chain | ||||
loop every check | ||||
Issuer->>Issuer: writes and signs Checks | ||||
Issuer->>Receiver: sends Check to receiver | ||||
end | ||||
alt batch mint | ||||
Receiver->>Receiver: collects and signs each Check | ||||
Receiver->>+CheckContract: creates Check batch and sends to the Check contract to mint tokens | ||||
CheckContract->>Erc-20 Token: mint | ||||
CheckContract-->>-Receiver: success on chain | ||||
else bundle mint | ||||
Receiver->>Issuer: sends request to Issuer to bundle multiple checks | ||||
Issuer->>Issuer: merges the receiver's Checks | ||||
Issuer->>Receiver: issues a bundled Check with the sum of token values | ||||
Receiver->>Receiver: signs the bundled Check | ||||
Receiver->>+CheckContract: sends to the Check contract to mint tokens | ||||
CheckContract->>Erc-20 Token: mint | ||||
CheckContract-->>-Receiver: success on chain | ||||
end | ||||
``` | ||||
|
||||
## Rationale | ||||
|
||||
The off-chain check workflow, while shifting the main cost of token distribution from the issuers to the receivers, adds flexibilities to token issuance. The issuers don't have to decide beforehand when and how many tokens must be minted to what group of receivers. After they register the tokens, the issuers can write Checks as they go. | ||||
|
||||
The receivers on the other hand, before minting the tokens on-chain, are assured of the promised tokens with issuers' signatures on the Checks. The receivers accumulate Checks to mint in batch or bundle to save gas, and have the option to mint at a low gas time period. | ||||
|
||||
## Backwards Compatibility | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Section is optional, no need to include if nothing relevant is in the section. |
||||
|
||||
No backward compatibility issues found | ||||
|
||||
## Security Considerations | ||||
|
||||
To maintain the integrity of the Check sequence issued for a (tokenAddr, receiverAddr), Check ids must follow the numbering rules specified. The Check contract keeps the last minted Check id for each (tokenAddr, receiverAddr) and refuse to mint if the coming beginId in the mint request is out of order. Enforcing the Check sequencing numbers also defies same Check double issue by the issuer or double mint by the receiver. | ||||
|
||||
Off-chain checks are signed by the issuer and are not reputable once issued. After receiving multiple Checks from the issuer, if the issuer refuses to bundle, the receiver can still submit Checks to batch mint, albeit with reduced gas saving effect. | ||||
|
||||
The Check contract requires receiver signature on each Check in the mint request, so that the issuer, after sending a Check with high value, cannot issue a low value Check with the same Check id and mint for the receiver, i.e. replacing a high value Check with a low value Check in the Check sequence on-chain for the receiver before the receive having a chance to mint the high value Check. | ||||
|
||||
## Copyright | ||||
|
||||
Copyright and related rights waived via [CC0](../LICENSE.md). |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.