eip-1592.md
1 --- 2 eip: 1592 3 title: Address and ERC20-compliant transfer rules 4 author: Cyril Lapinte <cyril.lapinte@mtpelerin.com>, Laurent Aapro <laurent.aapro@mtpelerin.com> 5 discussions-to: https://github.com/ethereum/EIPs/issues/1597 6 type: Standards Track 7 category: ERC 8 status: Draft 9 created: 2018-11-09 10 --- 11 12 ## Simple Summary 13 14 We propose a standard and an interface to define transfer rules, in the context of ERC20 tokens and possibly beyond. 15 16 17 A rule can act based on sender, destination and amount, and is triggered (and rejects the transfer) according to any required business logic. 18 19 20 To ease rule reusability and composition, we also propose an interface and base implementation for a rule engine. 21 22 ## Abstract 23 24 This standard proposal should answer the following challenges: 25 - Enable integration of rules with interacting platforms such as exchanges, decentralized wallets and DApps. 26 - Externale code and storage, improve altogether reusability, gas costs and contracts' memory footprint. 27 - Highlight contract behavior and its evolution, in order to ease user interaction with such contract. 28 29 30 If these challenges are answered, this proposal will provide a unified basis for transfer rules and hopefully address the transfer restriction needs of other EIPs as well, e.g. 31 [EIP-902](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-902.md), 32 [EIP-1066](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1066.md) 33 and [EIP-1175](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1175.md). 34 35 This document proposes specifications for a standard of **transfer rules** and interfaces to both the rules and the rule engine, which was made to be inherited by a token, but may have a much broader scope in the authors' opinion. 36 37 The last section of this document illustrates the proposal with a rule template and links to rule implementations. 38 39 ## Motivation 40 41 ERC20 was designed as a standard interface allowing any token on Ethereum to be handled by other applications: from wallets to decentralized exchanges. This has been extremely powerful, but future developments in the industry of tokenization are bringing new challenges. For example it is already hard to know exactly why an ERC20 transfer failed, and it will become even harder when many tokens add their own transfer rules to the mix; we propose that it should be trivial to determine before a tx is sent, whether the transfer should turn out valid or invalid, and why (unless conditions change in the meantime obviously). On the other hand, if the rules were changed, it should also be easily detected, so that the interacting party knows it must adjust its expectations or model. 42 43 ## Specification 44 45 We define below an interface for a rule. Rules are meant to be as simple as possible, to limit gas expenditure, since that logic will be executed on every transfer. Another reason for keeping rules simple and short, and strive for atomicity, is to facilitate both composition and interpretation of rejected transfers. By knowing which rule was triggered, we obtain a clear picture of the reason for rejection. 46 47 The engine we propose executes all the rules defined by its owner, on every transfer and it is easy to add and remove rules individually, although we have chosen to use quite a raw rule update method, to save on deployment costs, which are often tight when it comes to token smart contracts. 48 49 Rules are deployed on the blockchain as individual smart contracts, and called upon by the rule engine they were attached to. But any third party, for example an exchange preparing a cashout for a customer, can very cheaply query the rule engine of the token, or a single rule directly, to verify the validity of a transfer before execution, so as to never get a rejected transaction. 50 51 ## Rule interface 52 53 `IRule` interface should provide a way to validate if an address or a transfer is valid. 54 55 If one of these two methods is not applicable, it can simply be made to return true systematically. 56 If any parameter of `isTransferValid` is not needed, its name should be commented out with `/* */`. 57 58 ```js 59 pragma solidity ^0.4.25; 60 61 interface IRule { 62 function isAddressValid(address _address) external view returns (bool); 63 function isTransferValid(address _from, address _to, uint256 _amount) 64 external view returns (bool); 65 } 66 ``` 67 68 ## WithRules interface 69 70 `WithRules` interface describes the integration of rules to a rule engine. 71 Developers may choose to not implement this interface if their code will only deal with one rule, or if it is not desirable to update the rules. 72 73 The rules ordering must be thought through carefully. 74 Rules which are cheaper to validate or have a higher chance to break should be put first to reduce global gas expenditure, then business logic should guide the ordering of rules. That is why rules for a given context should be defined as a whole and not individually. 75 76 ```js 77 pragma solidity ^0.4.25; 78 79 import "./IRule.sol"; 80 81 interface IWithRules { 82 function ruleLength() public view returns (uint256); 83 function rule(uint256 _ruleId) public view returns (IRule); 84 function validateAddress(address _address) public view returns (bool); 85 function validateTransfer(address _from, address _to, uint256 _amount) 86 public view returns (bool); 87 88 function defineRules(IRule[] _rules) public; 89 90 event RulesDefined(uint256 count); 91 } 92 ``` 93 94 ## WithRules implementation 95 96 We also propose a simple implementation of the rule engine, available [here](https://github.com/MtPelerin/MtPelerin-protocol/blob/master/contracts/rule/WithRules.sol). It has been kept minimal both to save on gas costs on each transfer, and to reduce the deployment cost overhead for the derived smart contract. 97 98 99 On top of implementing the interface above, this engine also defines two modifiers (`whenAddressRulesAreValid`and `whenTransferRulesAreValid`), which can be used throughout the token contract to restrict `transfer()`, `transferFrom` and any other function that needs to respect either a simple whitelist or complex transfer rules. 100 101 102 ## Integration 103 104 To use rules within a token is as easy as having the token inherit from WithRules, then writing rules according to the IRule interface and deploying each rule individually. The token owner can then use `defineRules()` to attach all rules in the chosen order, within a single transaction. 105 106 Below is a template for a rule. 107 108 ```solidity 109 import "../interface/IRule.sol"; 110 111 contract TemplateRule is IRule { 112 113 // state vars for business logic 114 115 constructor(/* arguments for init */) public { 116 117 // initializations 118 119 } 120 121 function isAddressValid(address _from) public view returns (bool) { 122 boolean isValid; 123 124 // business logic 125 126 return isValid; 127 } 128 129 function isTransferValid( 130 address _from, 131 address _to, 132 uint256 _amount) 133 public view returns (bool) 134 { 135 boolean isValid; 136 137 // business logic 138 139 return isValid; 140 } 141 } 142 ``` 143 144 *** Notes *** 145 The MPS (Mt Pelerin's Share) token is the current live implementation of this standard. 146 Other implementations may be written with different trade-offs: from gas savings to improved security. 147 148 #### Example of rules implementations 149 150 - [YesNo rule](https://github.com/MtPelerin/MtPelerin-protocol/tree/master/contracts/rule/YesNoRule.sol): Trivial rule used to demonstrate both a rule and the rule engine. 151 152 - [Freeze rule](https://github.com/MtPelerin/MtPelerin-protocol/tree/master/contracts/rule/FreezeRule.sol): This rule allows to prevent any transfer of tokens to or from chosen addresses. A smart blacklist. 153 154 - [Lock rule](https://github.com/MtPelerin/MtPelerin-protocol/tree/master/contracts/rule/LockRule.sol): Define a global transfer policy preventing either sending or receiving tokens within a period of time. Exceptions may be granted to some addresses by the token admin. A smart whitelist. 155 156 - [User Kyc Rule](https://github.com/MtPelerin/MtPelerin-protocol/tree/master/contracts/rule/UserKycRule.sol): Rule example relying on an existing whitelist to assert transfer and addresses validity. It is a good example of a rule that completely externalizes it's tasks. 157 158 #### Example implementations are available at 159 - [Mt Pelerin Bridge protocol rules implementation](https://github.com/MtPelerin/MtPelerin-protocol/tree/master/contracts/rule) 160 - [Mt Pelerin Token with rules](https://github.com/MtPelerin/MtPelerin-protocol/blob/master/contracts/token/component/TokenWithRules.sol) 161 162 ## History 163 164 Historical links related to this standard: 165 166 - The first regulated tokenized share issued by Mt Pelerin (MPS token) is using an early version of this proposal: https://www.mtpelerin.com/blog/world-first-tokenized-shares 167 The rule engine was updated several times, after the token issuance and during the tokensale, to match changing business and legal requirements, showcasing the solidity and flexibility of the rule engine. 168 169 ## Copyright 170 Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). 171 External references outside this repository will have their own specific copyrights.