ERC-777 A New Advanced Token Standard (#907)
* ERC777 A New Advanced Token Standard * ERC777: Add ERC777 Logo * ERC777: Normalize EIP/ERC names * ERC777: Spec for tokensToSend when burning tokens * ERC777: Update official repository * ERC777: Clarification and corrections - Clarify unclear sections of the spec - Fix typos and grammar mistakes - Improve aesthetics of the text * ERC777: Change terminology of the repo, small fix - Don't refer to the repo as the official repo but the repo of the reference implementation - Fix a small typo in the AuthorizedOperator event * ERC777: logo & release to public domain (CC0) * ERC777: Use markdown not html & relative links * ERC777: Adapt to new EIP template * ERC777: Use solidity syntax and fix relative links * ERC777: Add discussions-to link * ERC777: Fix link in discussion-to * ERC777: Fix image links * ERC777: Fix eip type * ERC777: Update header
544
EIPS/eip-777.md
Normal file
@ -0,0 +1,544 @@
|
||||
---
|
||||
eip: 777
|
||||
title: A New Advanced Token Standard
|
||||
author: Jordi Baylina <jordi@baylina.cat>, Jacques Dafflon <jacques.dafflon@gmail.com>, Thomas Shababi <tom@truelevel.io>
|
||||
discussions-to: https://github.com/ethereum/EIPs/issues/777
|
||||
status: Draft
|
||||
type: Standards Track
|
||||
category: ERC
|
||||
created: 2017-11-20
|
||||
requires: 820
|
||||
---
|
||||
|
||||
## Simple Summary
|
||||
|
||||
This EIP defines a standard interface for token contracts.
|
||||
|
||||
*The repository for this standard containing the reference implementation can be found at [jacquesd/ERC777](https://github.com/jacquesd/ERC777) and installed via npm with: `npm install erc777`.*
|
||||
|
||||
## Abstract
|
||||
|
||||
This standard defines a new way to interact with a Token Contract.
|
||||
|
||||
It defines operators to send tokens on behalf of another address – contract or regular account. It takes advantage of [ERC820](https://github.com/ethereum/EIPs/issues/820) to find out whether and where to notify contracts and regular addresses when they receive tokens as well as to allow compatibility with old contracts.
|
||||
|
||||
## Motivation
|
||||
|
||||
This standard tries to improve the widely used [ERC20](https://github.com/ethereum/EIPs/issues/20) token standard. The main advantages of this standard are:
|
||||
|
||||
1. Uses the same philosophy as Ether in that tokens are sent with `send(dest, value, data)`.
|
||||
2. Both contracts and regular address can control and reject which token they send by registering a `tokensToSend` function – rejection is done by throwing in the function.
|
||||
3. Both contracts and regular addresses can control and reject which token they receive by registering a `tokensReceived` function – rejection is done by throwing in the function.
|
||||
4. The `tokensReceived` function also avoids the double call needed in the ERC20 standard (`approve` / `transferFrom`).
|
||||
5. The token holder can "authorize" and "revoke" operators who can send tokens on their behalf. These operators are intended to be verified contracts such as an exchange, a cheque processor or an automatic charging system.
|
||||
6. Every token transaction contains a `userData` bytes field and a similar `operatorData` – in case of an operator transaction – to be used freely by the user (token holder) and the operator respectively to pass data to the recipient.
|
||||
7. It is backwards compatible way with wallets that do not contain the `tokensReceived` function by deploying a proxy contract for the wallet.
|
||||
|
||||
## Specification
|
||||
|
||||
### ERC777Token (Token Contract)
|
||||
|
||||
``` solidity
|
||||
interface ERC777Token {
|
||||
function name() public constant returns (string);
|
||||
function symbol() public constant returns (string);
|
||||
function totalSupply() public constant returns (uint256);
|
||||
function granularity() public constant returns (uint256);
|
||||
function balanceOf(address owner) public constant returns (uint256);
|
||||
|
||||
function send(address to, uint256 amount) public;
|
||||
function send(address to, uint256 amount, bytes userData) public;
|
||||
|
||||
function authorizeOperator(address operator) public;
|
||||
function revokeOperator(address operator) public;
|
||||
function isOperatorFor(address operator, address tokenHolder) public constant returns (bool);
|
||||
function operatorSend(address from, address to, uint256 amount, bytes userData, bytes operatorData) public;
|
||||
|
||||
event Sent(
|
||||
address indexed operator,
|
||||
address indexed from,
|
||||
address indexed to,
|
||||
uint256 amount,
|
||||
bytes userData,
|
||||
bytes operatorData
|
||||
);
|
||||
event Minted(address indexed operator, address indexed to, uint256 amount, bytes operatorData);
|
||||
event Burned(address indexed operator, address indexed from, uint256 amount, bytes userData, bytes operatorData);
|
||||
event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
|
||||
event RevokedOperator(address indexed operator, address indexed tokenHolder);
|
||||
}
|
||||
```
|
||||
|
||||
The token-contract MUST register the `ERC777Token` interface via ERC820.
|
||||
|
||||
The basic unit token MUST be 10<sup>18</sup>.
|
||||
|
||||
#### Methods
|
||||
##### name
|
||||
|
||||
``` solidity
|
||||
function name() public constant returns (string)
|
||||
```
|
||||
|
||||
Returns the name of the token – e.g. `"MyToken"`.
|
||||
|
||||
> **returns:** Name of the token
|
||||
|
||||
<br/>
|
||||
|
||||
##### symbol
|
||||
|
||||
``` solidity
|
||||
function symbol() public constant returns (string)
|
||||
```
|
||||
|
||||
Returns the symbol of the token – e.g. `"MYT"`.
|
||||
|
||||
> **returns:** Symbol of the token
|
||||
|
||||
<br/>
|
||||
|
||||
##### granularity
|
||||
|
||||
``` solidity
|
||||
function granularity() public constant returns (uint256)
|
||||
```
|
||||
|
||||
Returns the smallest part of the token that's not divisible.
|
||||
|
||||
Any minting, transfer or burning of tokens MUST be a multiple of this value. Any operation that would result in a balance that's not a multiple of this value, MUST be considered invalid and the transaction MUST throw.
|
||||
|
||||
Most of the tokens SHOULD be fully partitionable, i.e. this function SHOULD return `1` unless there is a good reason for not allowing any partition of the token.
|
||||
|
||||
*NOTE*: `granularity` MUST be greater or equal to `1`.
|
||||
|
||||
> **returns:** The smallest non divisible part of the token.
|
||||
|
||||
<br/>
|
||||
|
||||
##### totalSupply
|
||||
|
||||
``` solidity
|
||||
function totalSupply() public constant returns (uint256)
|
||||
```
|
||||
|
||||
Get the total number of minted tokens.
|
||||
|
||||
> **returns:** Total supply of tokens currently in circulation.
|
||||
|
||||
<br/>
|
||||
|
||||
##### balanceOf
|
||||
|
||||
``` solidity
|
||||
function balanceOf(address tokenHolder) public constant returns (uint256)
|
||||
```
|
||||
|
||||
Get the balance of the account with address `tokenHolder`.
|
||||
> **parameters**
|
||||
> - `tokenHolder`: Address for which the balance is returned
|
||||
>
|
||||
> **returns:** Amount of token held by `tokenHolder` in the token contract.
|
||||
|
||||
<br/>
|
||||
|
||||
##### send
|
||||
|
||||
``` solidity
|
||||
function send(address to, uint256 amount) public
|
||||
function send(address to, uint256 amount, bytes userData) public
|
||||
```
|
||||
|
||||
Send `amount` of tokens to address `to`.
|
||||
|
||||
This call MUST:
|
||||
- call the `tokensToSend` method on the contract implementing `ERC777TokensSender` as returned by a ERC820 lookup on the `from` address – regardless if `from` is a regular address or a contract.
|
||||
- call the `tokensReceived` method on the address implementing `ERC777TokensRecipient` as returned by a ERC820 lookup on the `to` address – regardless if `to` is a regular address or a contract.
|
||||
- fire the `Sent` event.
|
||||
|
||||
If `to` is a contract which is not prepared to receive tokens. Specifically, it is a contract which does not register an address (its own or another) via ERC820 implementing the `ERC777TokensRecipient` interface. Then `send` MUST throw.
|
||||
|
||||
The function MUST `throw` if:
|
||||
- `msg.sender` account balance does not have enough tokens to spend
|
||||
- `to` is a contract which is not prepared to receive tokens.
|
||||
|
||||
*NOTE*: Sending an amount of `0` is valid and MUST be treated as a regular send.
|
||||
> **parameters**
|
||||
> - `to`: tokens recipient
|
||||
> - `amount`: number of tokens transferred
|
||||
> - `userData`: information attached to the transaction by the user (sender)
|
||||
|
||||
<br/>
|
||||
|
||||
##### authorizeOperator
|
||||
|
||||
``` solidity
|
||||
function authorizeOperator(address operator) public
|
||||
```
|
||||
|
||||
Authorize a third party `operator` to send tokens on behalf of `msg.sender`.
|
||||
|
||||
A `AuthorizedOperator` event MUST be fired on a successful call to this function.
|
||||
|
||||
*NOTE*: The token holder (`msg.sender`) is always an operator for himself. That is, he can call `operatorSend` on himself. This right cannot be revoked. Therefore if this function is called to set the token holder (`msg.sender`) as operator, then the function MUST throw.
|
||||
|
||||
*NOTE*: A token holder CAN authorize multiple operators at the same time.
|
||||
|
||||
> **parameters**
|
||||
> - `operator`: Address which will be granted rights to manage the tokens.
|
||||
|
||||
<br/>
|
||||
|
||||
##### revokeOperator
|
||||
|
||||
``` solidity
|
||||
function revokeOperator(address operator) public
|
||||
```
|
||||
|
||||
Revoke a third party `operator`'s rights to send tokens on behalf of `msg.sender`.
|
||||
|
||||
A `RevokedOperator` event MUST be fired on a successful call to this function.
|
||||
|
||||
*NOTE*: The token holder (`msg.sender`) is always an operator for himself. That is, he can call `operatorSend` on himself. This right cannot be revoked. Therefore if this function is called to set the token holder (`msg.sender`) as operator, then the function MUST throw.
|
||||
|
||||
> **parameters**
|
||||
> - `operator`: Address which whose rights to manage the tokens will be revoked.
|
||||
|
||||
<br/>
|
||||
|
||||
##### isOperatorFor
|
||||
|
||||
``` solidity
|
||||
function isOperatorFor(address operator, address tokenHolder) public constant returns (bool)
|
||||
```
|
||||
|
||||
Check whether the `operator` address is allowed to send the tokens held by the `tokenHolder` address.
|
||||
|
||||
> **parameters**
|
||||
> - `operator`: address to check if it has the right to manage the tokens.
|
||||
> - `tokenHolder`: address which holds the tokens to be managed.
|
||||
|
||||
<br/>
|
||||
|
||||
##### operatorSend
|
||||
|
||||
``` solidity
|
||||
function operatorSend(address from, address to, uint256 amount, bytes userData, bytes operatorData) public
|
||||
```
|
||||
|
||||
Send `amount` of tokens on behalf of the address `from` to the address `to`.
|
||||
|
||||
`msg.sender` MUST be an authorized operator or the owner for the `from` address.
|
||||
|
||||
This call MUST:
|
||||
- call the `tokensToSend` method on the contract implementing `ERC777TokensSender` as returned by a ERC820 lookup on the `from` address
|
||||
- call the `tokensReceived` method on the contract implementing `ERC777TokensRecipient` as returned by a ERC820 lookup on the `to` address
|
||||
- fire the `Sent` event
|
||||
|
||||
If `to` is a contract which is not prepared to receive tokens. Specifically, it is a contract which does not register an address (its own or another) via ERC820 implementing the `ERC777TokensRecipient` interface. Then `operatorSend` MUST throw.
|
||||
|
||||
The method MUST throw if:
|
||||
- `from` account balance does not have enough tokens to spend.
|
||||
- `to` is a contract which does not register an address (its own or another) via ERC820 which implement the `ERC777TokensRecipient` interface.
|
||||
- `to` is a contract which is not prepared to receive tokens.
|
||||
- `msg.sender` is not an authorized operator for `from`.
|
||||
|
||||
> **parameters**
|
||||
> - `from`: token holder (sender)
|
||||
> - `to`: tokens recipient
|
||||
> - `amount`: number of tokens transferred
|
||||
> - `userData`: information attached to the transaction, previously provided by the sender (`from` address).
|
||||
> - `operatorData`: information attached to the transaction by the operator
|
||||
|
||||
*NOTE*: The operator is free to pass any data via the `operatorData` parameter but the `userData` parameter is reserved for data provided by the user (token holder). The token holder should provide this data to the operator beforehand.
|
||||
|
||||
<br/>
|
||||
|
||||
#### Events
|
||||
|
||||
##### Sent
|
||||
|
||||
``` solidity
|
||||
event Sent(address indexed operator, address indexed from, address indexed to, uint256 amount, bytes userData, bytes operatorData)
|
||||
```
|
||||
|
||||
Indicate a transaction of `amount` of tokens from the `from` address to the `to` address.
|
||||
|
||||
This event MUST be fired on a successful call to `send` and `operatorSend`.
|
||||
|
||||
> **parameters**
|
||||
> - `operator`: address which triggered the transfer, either the token holder for a direct send or an authorized operator for `operatorSend`
|
||||
> - `from`: token holder
|
||||
> - `to`: tokens recipient
|
||||
> - `amount`: number of tokens sent
|
||||
> - `userData`: information attached to the transaction by the token holder
|
||||
> - `operatorData`: information attached to the transaction by the operator
|
||||
|
||||
<br/>
|
||||
|
||||
##### Minted
|
||||
|
||||
``` solidity
|
||||
event Minted(address indexed operator, address indexed to, uint256 amount, bytes operatorData)
|
||||
```
|
||||
|
||||
Indicate the minting of `amount` of tokens to the `to` address.
|
||||
|
||||
This standard does not enforce a specific way to mint tokens as this can be done in various ways depending on the use case of the tokens.
|
||||
|
||||
However, this event MUST be fired every time tokens are minted and credited to a `to` recipient address. A `Sent` event (even with the `0x0` as `from` address) MUST NOT be fired to indicate minting.
|
||||
|
||||
> **parameters**
|
||||
> - `operator`: address which triggered the minting
|
||||
> - `to`: tokens recipient
|
||||
> - `amount`: number of tokens minted
|
||||
> - `operatorData`: information attached to the minting by the operator
|
||||
|
||||
###### `tokensReceived` for minting
|
||||
|
||||
Every mint MUST call `tokensReceived` on the address implementing `ERC777TokensRecipient` for the `to` address as returned by a ERC820 lookup.
|
||||
|
||||
Minting MUST follow the same rules as `send`/`operatorSend` with the exception that `tokensToSend` MUST NOT be called in any case on any address. In addition, if `to` is a contract which is not prepared to receive tokens. Specifically, it is a contract which does not register an address (its own or another) via ERC820 implementing the `ERC777TokensRecipient` interface. Then the minting MUST throw.
|
||||
|
||||
The `from` parameter of `tokensReceived` MUST be `0x0`. The operator MUST be `msg.sender`. The `userData` MUST be empty. `operatorData` MAY contain data related to the minting.
|
||||
|
||||
<br/>
|
||||
|
||||
##### Burned
|
||||
|
||||
``` solidity
|
||||
event Burned(address indexed operator, address indexed from, uint256 amount, bytes userData, bytes operatorData)
|
||||
```
|
||||
|
||||
Indicate the burning of `amount` of tokens from the `from` address.
|
||||
|
||||
This standard does not enforce a specific way to burn tokens as this can be done in various ways depending on the use case of the tokens.
|
||||
|
||||
However, this event MUST be fired every time tokens are burned and taken from a `from` recipient address. A `Sent` event (even with the `0x0` as `to` address) MUST NOT be fired.
|
||||
|
||||
> **parameters**
|
||||
> - `operator`: address which triggered the minting
|
||||
> - `from`: token holder
|
||||
> - `amount`: number of tokens burned
|
||||
> - `userData`: information attached to the burn by the token holder
|
||||
> - `operatorData`: information attached to the burn by the operator
|
||||
|
||||
###### `tokensToSend` for burning
|
||||
|
||||
Every burn MUST call `tokensToSend` on the address implementing `ERC777TokensSender` for the `from` address as returned by a ERC820 lookup.
|
||||
|
||||
Burning MUST follow the same rules as `send`/`operatorSend` with the exception that `tokensReceived` MUST NOT be called in any case on any address. But if the `from` address register an address via ERC820 implementing the `ERC777TokensSender` interface, `tokensToSend` MUST be called.
|
||||
|
||||
The `to` parameter of `tokensToSend` MUST be `0x0`. The operator MUST be `msg.sender`. The `userData` MUST be empty. `operatorData` MAY contain data related to the minting.
|
||||
|
||||
<br/>
|
||||
|
||||
##### AuthorizedOperator
|
||||
|
||||
``` solidity
|
||||
event AuthorizedOperator(address indexed operator, address indexed tokenHolder)
|
||||
```
|
||||
|
||||
Indicate that the `operator` address is allowed to send the token of (i.e. is an operator for) the address `tokenHolder`.
|
||||
|
||||
This event MUST be fired on a successful call to `authorizeOperator`.
|
||||
|
||||
> **parameters**
|
||||
> - `operator`: Address which is granted rights to manage the tokens.
|
||||
> - `tokenHolder`: address which holds the tokens to be managed.
|
||||
|
||||
<br/>
|
||||
|
||||
##### RevokedOperator
|
||||
|
||||
``` solidity
|
||||
event RevokedOperator(address indexed operator, address indexed tokenHolder)
|
||||
```
|
||||
|
||||
Indicate that the `operator` address is denied the rights to send the token of (i.e. is not operator for) the address `tokenHolder`.
|
||||
|
||||
This event MUST be fired on a successful call to `revokeOperator`.
|
||||
|
||||
> **parameters**
|
||||
> - `operator`: Address which is denied the rights to manage the tokens.
|
||||
> - `tokenHolder`: address which holds the tokens to be managed.
|
||||
|
||||
<br/>
|
||||
|
||||
### ERC777TokensSender
|
||||
|
||||
Any address (contract or regular account) CAN register a contract (itself or an other) implementing the `ERC777TokensSender` interface via the ERC820 registry.
|
||||
|
||||
``` solidity
|
||||
interface ERC777TokensSender {
|
||||
function tokensToSend(
|
||||
address operator,
|
||||
address from,
|
||||
address to,
|
||||
uint value,
|
||||
bytes userData,
|
||||
bytes operatorData
|
||||
) public;
|
||||
}
|
||||
```
|
||||
|
||||
#### Methods
|
||||
|
||||
##### tokensToSend
|
||||
|
||||
``` solidity
|
||||
function tokensToSend(address operator, address from, address to, uint value, bytes userData, bytes operatorData) public
|
||||
```
|
||||
|
||||
Notify the transmission of `amount` of tokens from the `from` address.
|
||||
|
||||
> **parameters**
|
||||
> - `operator`: address which triggered the transfer, either sender for a direct send or an authorized operator for `operatorSend`
|
||||
> - `from`: token holder (sender)
|
||||
> - `to`: tokens recipient (or `0x` for burning)
|
||||
> - `amount`: number of tokens sent or burned
|
||||
> - `userData`: information attached to the transaction by the sender
|
||||
> - `operatorData`: information attached to the transaction by the operator
|
||||
|
||||
###### Burning Versus Sending
|
||||
|
||||
When tokens are sent as a result of burning:
|
||||
- `to` MUST be `0x0`
|
||||
- `userData` MUST be empty
|
||||
- `operator` MUST be the address which initiated the burning
|
||||
- `operatorData` MAY contain data
|
||||
|
||||
When tokens are sent as a result of sending (`send` or `operatorSend`):
|
||||
- `to` MUST be the address to which the tokens will be sent
|
||||
- `to` MUST NOT be `0x0`
|
||||
|
||||
If it is a direct `send` (i.e. not an `operatorSend`) the `operator` MUST be the address from which the tokens originate. That is the `from` and `operator` addresses MUST be equals.
|
||||
|
||||
|
||||
### ERC777TokensRecipient
|
||||
|
||||
Any address (contract or regular account) CAN register a contract (itself or an other) implementing the `ERC777TokensRecipient` interface via the ERC820 registry.
|
||||
|
||||
``` solidity
|
||||
interface ERC777TokensRecipient {
|
||||
function tokensReceived(address operator, address from, address to, uint amount, bytes userData, bytes operatorData) public;
|
||||
}
|
||||
```
|
||||
|
||||
#### Methods
|
||||
|
||||
##### tokensReceived
|
||||
|
||||
``` solidity
|
||||
function tokensReceived(address operator, address from, address to, uint amount, bytes userData, bytes operatorData) public
|
||||
```
|
||||
|
||||
Notify the transmission of `amount` of tokens to the `to` address.
|
||||
|
||||
> **parameters**
|
||||
> - `operator`: address which triggered the transfer, either sender for a direct send or an authorized operator for `operatorSend`
|
||||
> - `from`: token holder (sender or `0x` for minting)
|
||||
> - `to`: tokens recipient (or `0x` for burning)
|
||||
> - `amount`: number of tokens sent, minted or burned
|
||||
> - `userData`: information attached to the transaction by the sender
|
||||
> - `operatorData`: information attached to the transaction by the operator
|
||||
|
||||
###### Minting Versus Sending
|
||||
|
||||
When tokens are received as a result of minting:
|
||||
- `from` MUST be `0x0`
|
||||
- `userData` MUST be empty
|
||||
- `operator` MUST be the address which initiated the minting
|
||||
- `operatorData` MAY contain data.
|
||||
|
||||
When tokens are received as a result of sending (`send` or `operatorSend`):
|
||||
- `from` MUST be the one from which the tokens originate and
|
||||
- `from` MUST NOT be `0x0`.
|
||||
|
||||
If it is a direct `send` (i.e. not an `operatorSend`) the `operator` MUST be the address from which the tokens originate. That is the `from` and `operator` addresses MUST be equals.
|
||||
|
||||
### Logo
|
||||
|
||||
| **Image** | ![beige logo](../assets/eip-777/logo/png/ERC-777-logo-beige-48px.png) | ![white logo](../assets/eip-777/logo/png/ERC-777-logo-white-48px.png) | ![light grey logo](../assets/eip-777/logo/png/ERC-777-logo-light_grey-48px.png) | ![dark grey logo](../assets/eip-777/logo/png/ERC-777-logo-dark_grey-48px.png) | ![black logo](../assets/eip-777/logo/png/ERC-777-logo-black-48px.png?raw=true)
|
||||
|----------:|:---------:|:---------:|:------------:|:-----------:|:---------:|
|
||||
| **Color** | beige | white | light grey | dark grey | black |
|
||||
| **Hex** | `#C99D66` | `#FFFFFF` | `#EBEFF0` | `#3C3C3D` | `#000000` |
|
||||
|
||||
The logo MUST NOT be used to advertise, promote or associate in any way technology – such as tokens – which is not ERC777 compliant.
|
||||
|
||||
The logo for the standard can be found in the `/assets/eip-777/logo` folder in `svg` and `png` formats. The `png` version of the logo offers a few sizes in pixel. If needed, other sizes CAN be created by converting from `svg` into `png`.
|
||||
|
||||
ERC777 token contract authors CAN create a specific logo for their token based on this logo.
|
||||
|
||||
## Rationale
|
||||
|
||||
This standard solves some of the problems of the [EIP223](https://github.com/ethereum/EIPs/issues/223) and goes an step further by allowing operators (generally contracts) that can manage the tokens in the same way that the ERC20 with infinite `approve` was allowed.
|
||||
|
||||
Also the usage of ERC820 allows backwards compatibility with wallets and proxy contracts without having to be redeployed.
|
||||
|
||||
## Backwards Compatibility (ERC20Token)
|
||||
|
||||
This EIP does not introduce backward incompatibilities and is compatible with the older ERC20 token standard.
|
||||
|
||||
This EIP does not uses `transfer` and `transferFrom` and uses `send` and `operatorSend` to avoid mistakes in knowing which interface you are using.
|
||||
|
||||
This standard allows the implementation of ERC20 functions `transfer`, `transferFrom`, `approve` and `allowance` alongside to make a token compatible with ERC20.
|
||||
|
||||
The token can implement `decimals()` for backwards compatibility. If implemented, it MUST always return `18`.
|
||||
|
||||
Therefore a token contract can implement both ERC20 and ERC777 in parallel. Read-only functions (such as `name`, `symbol`, `balanceOf`, `totalSupply`) and internal data (such as the mapping of balances) overlap without problem. Note however that the following functions are mandatory in ERC777 and MUST be implemented: `name`, `symbol` `balanceOf` and `totalSupply` (`decimal` is not part of the ERC777 standard).
|
||||
|
||||
The write methods from both standards are decoupled and can operate independently from each other. Note that ERC20 functions SHOULD be limited to only being called from old contracts.
|
||||
|
||||
If the token implements ERC20, it MUST be register the `ERC20Token` interface via ERC820. If the contract has a switch to enable or disable ERC20 methods, every time the switch is triggered, the token MUST register or unregister accordingly the `ERC20Token` interface via ERC820.
|
||||
|
||||
The only difference for new contracts implementing ERC20 is that registration of `ERC777TokensSender` and `ERC777TokensRecipient` via ERC820 takes precedence over ERC20. This means that even with a ERC20 `transfer` call, the token contract MUST check via ERC820 if the `from` / `to` address implements `tokensToSend` / `tokensReceived` and call it if available. Note that when calling ERC20 `transfer` on a contract, if the contract does not implement `tokensReceived`, the `transfer` call SHOULD still be accepted even if this means the tokens will probably be locked.
|
||||
|
||||
The table below summarize the different actions the token contract must take when sending, minting and transferring token via ERC777 and ERC20:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>ERC820</th>
|
||||
<th><code>to</code> address</th>
|
||||
<th>ERC777 <code>send</code>/<code>operatorSend</code> and Minting</th>
|
||||
<th>ERC20 <code>transfer</code></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2" align="right">
|
||||
<code>ERC777TokensRecipient</code><br/>registered
|
||||
</td>
|
||||
<td>regular address</td>
|
||||
<td colspan="2" rowspan="2" align="center">
|
||||
MUST call <code>tokensReceived</code>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>contract</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="2" align="right">
|
||||
<code>ERC777TokensRecipient</code><br/>not registered
|
||||
</td>
|
||||
<td>regular address</td>
|
||||
<td align="center">SHOULD accept</td>
|
||||
<td rowspan="2" align="center">SHOULD accept</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>contract</td>
|
||||
<td align="center">MUST throw</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
There is no particular action to take if `tokensToSend` is not implemented. The transfer MUST proceed and be canceled only if another condition is not respected such as lack of funds, a throw in `tokensReceived` (if present) or in some specific cases if `tokensReceived` is not implemented.
|
||||
|
||||
## Test Cases
|
||||
|
||||
The [repository with the reference implementation](https://github.com/jacquesd/ERC777) contains all the [tests](https://github.com/jacquesd/ERC777/blob/master/test/ReferenceToken-test.js).
|
||||
|
||||
## Implementation
|
||||
|
||||
The repository at [/jacquesd/ERC777](https://github.com/jacquesd/ERC777) contains the [reference implementation](https://github.com/jacquesd/ERC777/blob/master/contracts/ReferenceToken.sol).
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
|
BIN
assets/eip-777/logo/png/ERC-777-logo-beige-1024px.png
Normal file
After Width: | Height: | Size: 4.0 MiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-beige-192px.png
Normal file
After Width: | Height: | Size: 144 KiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-beige-2048px.png
Normal file
After Width: | Height: | Size: 16 MiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-beige-48px.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-beige-600px.png
Normal file
After Width: | Height: | Size: 1.4 MiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-black-1024px.png
Normal file
After Width: | Height: | Size: 4.0 MiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-black-192px.png
Normal file
After Width: | Height: | Size: 144 KiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-black-2048px.png
Normal file
After Width: | Height: | Size: 16 MiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-black-48px.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-black-600px.png
Normal file
After Width: | Height: | Size: 1.4 MiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-dark_grey-1024px.png
Normal file
After Width: | Height: | Size: 4.0 MiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-dark_grey-192px.png
Normal file
After Width: | Height: | Size: 144 KiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-dark_grey-2048px.png
Normal file
After Width: | Height: | Size: 16 MiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-dark_grey-48px.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-dark_grey-600px.png
Normal file
After Width: | Height: | Size: 1.4 MiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-light_grey-1024px.png
Normal file
After Width: | Height: | Size: 4.0 MiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-light_grey-192px.png
Normal file
After Width: | Height: | Size: 144 KiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-light_grey-2048px.png
Normal file
After Width: | Height: | Size: 16 MiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-light_grey-48px.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-light_grey-600px.png
Normal file
After Width: | Height: | Size: 1.4 MiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-white-1024px.png
Normal file
After Width: | Height: | Size: 4.0 MiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-white-192px.png
Normal file
After Width: | Height: | Size: 144 KiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-white-2048px.png
Normal file
After Width: | Height: | Size: 16 MiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-white-48px.png
Normal file
After Width: | Height: | Size: 9.1 KiB |
BIN
assets/eip-777/logo/png/ERC-777-logo-white-600px.png
Normal file
After Width: | Height: | Size: 1.4 MiB |
21
assets/eip-777/logo/svg/ERC-777-logo-beige.svg
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 595.3 841.9" style="enable-background:new 0 0 595.3 841.9;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#C99D66;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M410.1,329.9c20,0,39.1,0.1,58.3-0.1c4.2,0,5.6,0.4,4.8,5.3c-2.5,14.1-4.4,28.3-6.3,42.5
|
||||
c-0.5,3.6-2,4.5-5.5,4.5c-28.3-0.1-56.7,0-85-0.2c-4,0-6.2,1.3-8.3,4.6c-9.6,14.8-18.6,30-26.7,45.6c-0.5,1-0.9,2.1-1.6,3.7
|
||||
c2.4,0,4.1,0,5.9,0c35.5,0,71,0.1,106.5-0.1c4.5,0,6.3,0.2,5.3,5.8c-2.7,14.2-4.6,28.6-6.4,43c-0.6,4.4-2.1,5.6-6.6,5.6
|
||||
c-41.2-0.2-82.3-0.1-123.5-0.2c-3.6,0-5.1,1.1-6.2,4.5c-13.7,38.9-22.8,78.9-28.7,119.6c-2.8,19.1-5.9,38.2-8.6,57.3
|
||||
c-0.5,3.4-1.8,3.8-4.7,3.8c-31.8-0.1-63.7,0-95.5-0.1c-1.5,0-4.1,1.4-3.5-2.6c7.5-48.5,13-97.3,27.4-144.5
|
||||
c3.8-12.5,8-24.9,12.8-37.9c-7.6,0-14.7,0-21.8,0c-14.7,0-29.3-0.2-44,0.1c-4.1,0.1-4.6-1.3-4.1-4.9c2.5-15.4,4.9-30.9,7-46.4
|
||||
c0.5-3.4,2.3-3,4.5-3c12.2,0,24.3,0,36.5,0c14.2,0,28.3,0,42.5,0c2.2,0,4.1,0,5.4-2.5c9.2-17.5,20-34.1,31.5-51.4c-2.3,0-4,0-5.8,0
|
||||
c-33.7,0-67.3-0.1-101,0.1c-4.4,0-5.3-1.2-4.6-5.2c2.3-14,4.6-27.9,6.3-42c0.6-4.6,2.5-5,6.4-5c44.2,0.1,88.3,0,132.5,0.2
|
||||
c3.7,0,6-1.1,8.3-4c23.2-28.6,48.2-55.6,74.4-81.6c1.2-1.2,2.9-2,4.4-3c-0.3-0.5-0.7-1-1-1.5c-1.8,0-3.5,0-5.3,0
|
||||
c-83.5,0-167,0-250.5,0.1c-3.9,0-5.7,0.1-4.8-5.2c3.8-23.5,7.1-47.1,10.3-70.6c0.5-4.1,2.5-4.1,5.7-4.1c82.3,0.1,164.7,0,247,0
|
||||
c35.2,0,70.3,0.1,105.5-0.1c3.8,0,5,0.5,4.3,4.8c-3.7,23.7-7.1,47.4-10.4,71.1c-0.4,3-1.8,5.2-3.9,7.2
|
||||
c-27.1,26.8-53,54.8-77.3,84.3C411.5,328,411.1,328.5,410.1,329.9z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
21
assets/eip-777/logo/svg/ERC-777-logo-black.svg
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 595.3 841.9" style="enable-background:new 0 0 595.3 841.9;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#000000;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M410.1,329.9c20,0,39.1,0.1,58.3-0.1c4.2,0,5.6,0.4,4.8,5.3c-2.5,14.1-4.4,28.3-6.3,42.5
|
||||
c-0.5,3.6-2,4.5-5.5,4.5c-28.3-0.1-56.7,0-85-0.2c-4,0-6.2,1.3-8.3,4.6c-9.6,14.8-18.6,30-26.7,45.6c-0.5,1-0.9,2.1-1.6,3.7
|
||||
c2.4,0,4.1,0,5.9,0c35.5,0,71,0.1,106.5-0.1c4.5,0,6.3,0.2,5.3,5.8c-2.7,14.2-4.6,28.6-6.4,43c-0.6,4.4-2.1,5.6-6.6,5.6
|
||||
c-41.2-0.2-82.3-0.1-123.5-0.2c-3.6,0-5.1,1.1-6.2,4.5c-13.7,38.9-22.8,78.9-28.7,119.6c-2.8,19.1-5.9,38.2-8.6,57.3
|
||||
c-0.5,3.4-1.8,3.8-4.7,3.8c-31.8-0.1-63.7,0-95.5-0.1c-1.5,0-4.1,1.4-3.5-2.6c7.5-48.5,13-97.3,27.4-144.5
|
||||
c3.8-12.5,8-24.9,12.8-37.9c-7.6,0-14.7,0-21.8,0c-14.7,0-29.3-0.2-44,0.1c-4.1,0.1-4.6-1.3-4.1-4.9c2.5-15.4,4.9-30.9,7-46.4
|
||||
c0.5-3.4,2.3-3,4.5-3c12.2,0,24.3,0,36.5,0c14.2,0,28.3,0,42.5,0c2.2,0,4.1,0,5.4-2.5c9.2-17.5,20-34.1,31.5-51.4c-2.3,0-4,0-5.8,0
|
||||
c-33.7,0-67.3-0.1-101,0.1c-4.4,0-5.3-1.2-4.6-5.2c2.3-14,4.6-27.9,6.3-42c0.6-4.6,2.5-5,6.4-5c44.2,0.1,88.3,0,132.5,0.2
|
||||
c3.7,0,6-1.1,8.3-4c23.2-28.6,48.2-55.6,74.4-81.6c1.2-1.2,2.9-2,4.4-3c-0.3-0.5-0.7-1-1-1.5c-1.8,0-3.5,0-5.3,0
|
||||
c-83.5,0-167,0-250.5,0.1c-3.9,0-5.7,0.1-4.8-5.2c3.8-23.5,7.1-47.1,10.3-70.6c0.5-4.1,2.5-4.1,5.7-4.1c82.3,0.1,164.7,0,247,0
|
||||
c35.2,0,70.3,0.1,105.5-0.1c3.8,0,5,0.5,4.3,4.8c-3.7,23.7-7.1,47.4-10.4,71.1c-0.4,3-1.8,5.2-3.9,7.2
|
||||
c-27.1,26.8-53,54.8-77.3,84.3C411.5,328,411.1,328.5,410.1,329.9z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
21
assets/eip-777/logo/svg/ERC-777-logo-dark_grey.svg
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 595.3 841.9" style="enable-background:new 0 0 595.3 841.9;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#3C3C3D;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M410.1,329.9c20,0,39.1,0.1,58.3-0.1c4.2,0,5.6,0.4,4.8,5.3c-2.5,14.1-4.4,28.3-6.3,42.5
|
||||
c-0.5,3.6-2,4.5-5.5,4.5c-28.3-0.1-56.7,0-85-0.2c-4,0-6.2,1.3-8.3,4.6c-9.6,14.8-18.6,30-26.7,45.6c-0.5,1-0.9,2.1-1.6,3.7
|
||||
c2.4,0,4.1,0,5.9,0c35.5,0,71,0.1,106.5-0.1c4.5,0,6.3,0.2,5.3,5.8c-2.7,14.2-4.6,28.6-6.4,43c-0.6,4.4-2.1,5.6-6.6,5.6
|
||||
c-41.2-0.2-82.3-0.1-123.5-0.2c-3.6,0-5.1,1.1-6.2,4.5c-13.7,38.9-22.8,78.9-28.7,119.6c-2.8,19.1-5.9,38.2-8.6,57.3
|
||||
c-0.5,3.4-1.8,3.8-4.7,3.8c-31.8-0.1-63.7,0-95.5-0.1c-1.5,0-4.1,1.4-3.5-2.6c7.5-48.5,13-97.3,27.4-144.5
|
||||
c3.8-12.5,8-24.9,12.8-37.9c-7.6,0-14.7,0-21.8,0c-14.7,0-29.3-0.2-44,0.1c-4.1,0.1-4.6-1.3-4.1-4.9c2.5-15.4,4.9-30.9,7-46.4
|
||||
c0.5-3.4,2.3-3,4.5-3c12.2,0,24.3,0,36.5,0c14.2,0,28.3,0,42.5,0c2.2,0,4.1,0,5.4-2.5c9.2-17.5,20-34.1,31.5-51.4c-2.3,0-4,0-5.8,0
|
||||
c-33.7,0-67.3-0.1-101,0.1c-4.4,0-5.3-1.2-4.6-5.2c2.3-14,4.6-27.9,6.3-42c0.6-4.6,2.5-5,6.4-5c44.2,0.1,88.3,0,132.5,0.2
|
||||
c3.7,0,6-1.1,8.3-4c23.2-28.6,48.2-55.6,74.4-81.6c1.2-1.2,2.9-2,4.4-3c-0.3-0.5-0.7-1-1-1.5c-1.8,0-3.5,0-5.3,0
|
||||
c-83.5,0-167,0-250.5,0.1c-3.9,0-5.7,0.1-4.8-5.2c3.8-23.5,7.1-47.1,10.3-70.6c0.5-4.1,2.5-4.1,5.7-4.1c82.3,0.1,164.7,0,247,0
|
||||
c35.2,0,70.3,0.1,105.5-0.1c3.8,0,5,0.5,4.3,4.8c-3.7,23.7-7.1,47.4-10.4,71.1c-0.4,3-1.8,5.2-3.9,7.2
|
||||
c-27.1,26.8-53,54.8-77.3,84.3C411.5,328,411.1,328.5,410.1,329.9z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
21
assets/eip-777/logo/svg/ERC-777-logo-light_grey.svg
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 595.3 841.9" style="enable-background:new 0 0 595.3 841.9;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#EBEFF0;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M410.1,329.9c20,0,39.1,0.1,58.3-0.1c4.2,0,5.6,0.4,4.8,5.3c-2.5,14.1-4.4,28.3-6.3,42.5
|
||||
c-0.5,3.6-2,4.5-5.5,4.5c-28.3-0.1-56.7,0-85-0.2c-4,0-6.2,1.3-8.3,4.6c-9.6,14.8-18.6,30-26.7,45.6c-0.5,1-0.9,2.1-1.6,3.7
|
||||
c2.4,0,4.1,0,5.9,0c35.5,0,71,0.1,106.5-0.1c4.5,0,6.3,0.2,5.3,5.8c-2.7,14.2-4.6,28.6-6.4,43c-0.6,4.4-2.1,5.6-6.6,5.6
|
||||
c-41.2-0.2-82.3-0.1-123.5-0.2c-3.6,0-5.1,1.1-6.2,4.5c-13.7,38.9-22.8,78.9-28.7,119.6c-2.8,19.1-5.9,38.2-8.6,57.3
|
||||
c-0.5,3.4-1.8,3.8-4.7,3.8c-31.8-0.1-63.7,0-95.5-0.1c-1.5,0-4.1,1.4-3.5-2.6c7.5-48.5,13-97.3,27.4-144.5
|
||||
c3.8-12.5,8-24.9,12.8-37.9c-7.6,0-14.7,0-21.8,0c-14.7,0-29.3-0.2-44,0.1c-4.1,0.1-4.6-1.3-4.1-4.9c2.5-15.4,4.9-30.9,7-46.4
|
||||
c0.5-3.4,2.3-3,4.5-3c12.2,0,24.3,0,36.5,0c14.2,0,28.3,0,42.5,0c2.2,0,4.1,0,5.4-2.5c9.2-17.5,20-34.1,31.5-51.4c-2.3,0-4,0-5.8,0
|
||||
c-33.7,0-67.3-0.1-101,0.1c-4.4,0-5.3-1.2-4.6-5.2c2.3-14,4.6-27.9,6.3-42c0.6-4.6,2.5-5,6.4-5c44.2,0.1,88.3,0,132.5,0.2
|
||||
c3.7,0,6-1.1,8.3-4c23.2-28.6,48.2-55.6,74.4-81.6c1.2-1.2,2.9-2,4.4-3c-0.3-0.5-0.7-1-1-1.5c-1.8,0-3.5,0-5.3,0
|
||||
c-83.5,0-167,0-250.5,0.1c-3.9,0-5.7,0.1-4.8-5.2c3.8-23.5,7.1-47.1,10.3-70.6c0.5-4.1,2.5-4.1,5.7-4.1c82.3,0.1,164.7,0,247,0
|
||||
c35.2,0,70.3,0.1,105.5-0.1c3.8,0,5,0.5,4.3,4.8c-3.7,23.7-7.1,47.4-10.4,71.1c-0.4,3-1.8,5.2-3.9,7.2
|
||||
c-27.1,26.8-53,54.8-77.3,84.3C411.5,328,411.1,328.5,410.1,329.9z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
21
assets/eip-777/logo/svg/ERC-777-logo-white.svg
Normal file
@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 595.3 841.9" style="enable-background:new 0 0 595.3 841.9;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M410.1,329.9c20,0,39.1,0.1,58.3-0.1c4.2,0,5.6,0.4,4.8,5.3c-2.5,14.1-4.4,28.3-6.3,42.5
|
||||
c-0.5,3.6-2,4.5-5.5,4.5c-28.3-0.1-56.7,0-85-0.2c-4,0-6.2,1.3-8.3,4.6c-9.6,14.8-18.6,30-26.7,45.6c-0.5,1-0.9,2.1-1.6,3.7
|
||||
c2.4,0,4.1,0,5.9,0c35.5,0,71,0.1,106.5-0.1c4.5,0,6.3,0.2,5.3,5.8c-2.7,14.2-4.6,28.6-6.4,43c-0.6,4.4-2.1,5.6-6.6,5.6
|
||||
c-41.2-0.2-82.3-0.1-123.5-0.2c-3.6,0-5.1,1.1-6.2,4.5c-13.7,38.9-22.8,78.9-28.7,119.6c-2.8,19.1-5.9,38.2-8.6,57.3
|
||||
c-0.5,3.4-1.8,3.8-4.7,3.8c-31.8-0.1-63.7,0-95.5-0.1c-1.5,0-4.1,1.4-3.5-2.6c7.5-48.5,13-97.3,27.4-144.5
|
||||
c3.8-12.5,8-24.9,12.8-37.9c-7.6,0-14.7,0-21.8,0c-14.7,0-29.3-0.2-44,0.1c-4.1,0.1-4.6-1.3-4.1-4.9c2.5-15.4,4.9-30.9,7-46.4
|
||||
c0.5-3.4,2.3-3,4.5-3c12.2,0,24.3,0,36.5,0c14.2,0,28.3,0,42.5,0c2.2,0,4.1,0,5.4-2.5c9.2-17.5,20-34.1,31.5-51.4c-2.3,0-4,0-5.8,0
|
||||
c-33.7,0-67.3-0.1-101,0.1c-4.4,0-5.3-1.2-4.6-5.2c2.3-14,4.6-27.9,6.3-42c0.6-4.6,2.5-5,6.4-5c44.2,0.1,88.3,0,132.5,0.2
|
||||
c3.7,0,6-1.1,8.3-4c23.2-28.6,48.2-55.6,74.4-81.6c1.2-1.2,2.9-2,4.4-3c-0.3-0.5-0.7-1-1-1.5c-1.8,0-3.5,0-5.3,0
|
||||
c-83.5,0-167,0-250.5,0.1c-3.9,0-5.7,0.1-4.8-5.2c3.8-23.5,7.1-47.1,10.3-70.6c0.5-4.1,2.5-4.1,5.7-4.1c82.3,0.1,164.7,0,247,0
|
||||
c35.2,0,70.3,0.1,105.5-0.1c3.8,0,5,0.5,4.3,4.8c-3.7,23.7-7.1,47.4-10.4,71.1c-0.4,3-1.8,5.2-3.9,7.2
|
||||
c-27.1,26.8-53,54.8-77.3,84.3C411.5,328,411.1,328.5,410.1,329.9z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |