EIPs/EIPS/eip-1175.md

534 lines
18 KiB
Markdown
Raw Normal View History

---
eip: 1175
title: Wallet & shop standard for all tokens (erc20)
author: Jet Lim (@Nitro888)
discussions-to: https://github.com/ethereum/EIPs/issues/1182
status: Draft
type: Standards Track
category: ERC
created: 2018-06-21
requires: 20
---
# All tokens go to heaven
## Simple Summary
Make wallets and shops created from certified contracts make erc20 tokens easy to use for commerce.
![wallet](https://user-images.githubusercontent.com/11692220/41762799-ee17c480-7636-11e8-9930-681be2c59b56.png)
## Abstract
The mutual trust between the wallet and the shop created by the authenticated contract allows you to pay for and purchase items at a simple process.
## Motivation
New standards with improvements have been released, but the majority of tokens currently being developed are erc20 tokens. So I felt I needed a proposal to use old tokens in commerce.
To use various erc20 tokens for trading, you need a custom contract. However, a single wallet with a variety of tokens, and a mutually trusted store, can make transactions that are simple and efficient. The erc20 token is traded through two calls, `approve (address _spender, uint256 _value)` and `transferFrom (address _from, address _to, uint256 _value)`, but when using the wallet contract, `paySafe (address _shop, uint256 _item)`will be traded only in one call.
And if you only reuse the store interface, you can also trade using `payUnsafe (address _shop, uint256 _item)`.
## Specification
![workflow](https://user-images.githubusercontent.com/11692220/41841025-2ed6e024-78a2-11e8-9faf-2b43aeaa2303.png)
## WalletCenter
### Methods
#### createWallet
Create wallet contract and add to list. Returns the address of new wallet.
``` js
function createWallet() public returns (address _wallet)
```
#### isWallet
Returns true or false value for test this address is a created by createWallet.
``` js
function isWallet(address _wallet) public constant returns (bool)
```
#### createShop
Create Shop contract and add to list. Returns the address of new Shop with erc20 token address.
``` js
function createShop(address _erc20) public returns (address _shop)
```
#### isShop
Returns true or false value for test this address is a created by createWallet.
``` js
function isShop(address _shop) public constant returns (bool)
```
### Events
#### Wallet
Search for my wallet.
``` js
event Wallet(address indexed _owner, address indexed _wallet)
```
#### Shop
Search for my shop.
``` js
event Shop(address indexed _owner, address indexed _shop, address indexed _erc20)
```
## Wallet
Wallet must be created by wallet center.
### Methods
#### balanceOf
Returns the account balance of Wallet.
``` js
function balanceOf(address _erc20) public constant returns (uint256 balance)
```
#### withdrawal
withdrawal `_value` amount of `_erc20` token to `_owner`.
``` js
function withdrawal(address _erc20, uint256 _value) onlyOwner public returns (bool success)
```
#### paySafe
Pay for safe shop (created by contract) item with item index `_item`.
``` js
function paySafe(address _shop, uint256 _item) onlyOwner onlyShop(_shop) public payable returns (bool success)
```
#### payUnsafe
Pay for unsafe shop (did not created by contract) item with item index `_item`.
``` js
function payUnsafe(address _shop, uint256 _item) onlyOwner public payable returns (bool success)
```
#### payCancel
Cancel pay and refund. (only weekly model)
``` js
function payCancel(address _shop, uint256 _item) onlyOwner public returns (bool success)
```
#### refund
Refund from shop with item index `_item`.
``` js
function refund(uint256 _item, uint256 _value) public payable returns (bool success)
```
### Events
#### Pay
``` js
event Pay(address indexed _shop, uint256 indexed _item, uint256 indexed _value)
```
#### Refund
``` js
event Refund(address indexed _shop, uint256 indexed _item, uint256 indexed _value)
```
## Shop
Shop is created by wallet center or not. but Shop that created by wallet center is called safe shop.
### Methods
#### balanceOf
Returns the account balance of Shop.
``` js
function balanceOf(address _erc20) public constant returns (uint256 balance)
```
#### withdrawal
withdrawal `_value` amount of `_erc20` token to `_owner`.
``` js
function withdrawal(address _erc20, uint256 _value) onlyOwner public returns (bool success)
```
#### pay
Pay from buyer with item index `_item`.
``` js
function pay(uint256 _item) onlyWallet(msg.sender) public payable returns (bool success)
```
#### refund
refund token to `_to`.
``` js
function refund(address _buyer, uint256 _item, uint256 _value) onlyWallet(_buyer) onlyOwner public payable returns (bool success)
```
#### resister
Listing item for sell.
``` js
function resister(uint8 _category, uint256 _price, uint256 _stock) onlyOwner public returns (uint256 _itemId)
```
#### update
Update item state for sell. (change item `_price` or add item `_stock`)
``` js
function update(uint256 _item, uint256 _price, uint256 _stock) onlyOwner public
```
#### price
Get token address and price from buyer with item index `_item`.
``` js
function price(uint256 _item) public constant returns (address _erc20, uint256 _value)
```
#### canBuy
`_who` can Buy `_item`.
``` js
function canBuy(address _who, uint256 _item) public constant returns (bool _canBuy)
```
#### isBuyer
`_who` is buyer of `_item`.
``` js
function isBuyer(address _who, uint256 _item) public constant returns (bool _buyer)
```
#### info
Set shop information bytes.
``` js
function info(bytes _msgPack)
```
#### upVote
Up vote for this shop.
``` js
function upVote()
```
#### dnVote
Down vote for this shop.
``` js
function dnVote()
```
#### about
Get shop token, up vote and down vote.
``` js
function about() view returns (address _erc20, uint256 _up, uint256 _down)
```
#### infoItem
Set item information bytes.
``` js
function infoItem(uint256 _item, bytes _msgPack)
```
#### upVoteItem
Up vote for this item.
``` js
function upVoteItem(uint256 _item)
```
#### dnVoteItem
Down vote for this item.
``` js
function dnVoteItem(uint256 _item)
```
#### aboutItem
Get Item price, up vote and down vote.
``` js
function aboutItem(uint256 _item) view returns (uint256 _price, uint256 _up, uint256 _down)
```
### Events
#### Pay
``` js
event Pay(address indexed _buyer, uint256 indexed _item, uint256 indexed _value)
```
#### Refund
``` js
event Refund(address indexed _to, uint256 indexed _item, uint256 indexed _value)
```
#### Item
``` js
event Item(uint256 indexed _item, uint256 _price)
```
#### Info
``` js
event Info(bytes _msgPack)
```
#### InfoItem
``` js
event InfoItem(uint256 indexed _item, bytes _msgPack)
```
## Implementation
Sample token contract address is [0x393dd70ce2ae7b30501aec94727968c517f90d52](https://ropsten.etherscan.io/address/0x393dd70ce2ae7b30501aec94727968c517f90d52)
WalletCenter contract address is [0x1fe0862a4a8287d6c23904d61f02507b5044ea31](https://ropsten.etherscan.io/address/0x1fe0862a4a8287d6c23904d61f02507b5044ea31)
WalletCenter create shop contract address is [0x59117730D02Ca3796121b7975796d479A5Fe54B0](https://ropsten.etherscan.io/address/0x59117730D02Ca3796121b7975796d479A5Fe54B0)
WalletCenter create wallet contract address is [0x39da7111844df424e1d0a0226183533dd07bc5c6](https://ropsten.etherscan.io/address/0x39da7111844df424e1d0a0226183533dd07bc5c6)
## Appendix
``` js
pragma solidity ^0.4.24;
contract ERC20Interface {
function totalSupply() public constant returns (uint);
function balanceOf(address tokenOwner) public constant returns (uint balance);
function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
function transfer(address to, uint tokens) public returns (bool success);
function approve(address spender, uint tokens) public returns (bool success);
function transferFrom(address from, address to, uint tokens) public returns (bool success);
event Transfer(address indexed from, address indexed to, uint tokens);
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}
contract SafeMath {
function safeAdd(uint a, uint b) public pure returns (uint c) {
c = a + b;
require(c >= a);
}
function safeSub(uint a, uint b) public pure returns (uint c) {
require(b <= a);
c = a - b;
}
function safeMul(uint a, uint b) public pure returns (uint c) {
c = a * b;
require(a == 0 || c / a == b);
}
function safeDiv(uint a, uint b) public pure returns (uint c) {
require(b > 0);
c = a / b;
}
}
contract _Base {
address internal owner;
address internal walletCenter;
modifier onlyOwner {
require(owner == msg.sender);
_;
}
modifier onlyWallet(address _addr) {
require(WalletCenter(walletCenter).isWallet(_addr));
_;
}
modifier onlyShop(address _addr) {
require(WalletCenter(walletCenter).isShop(_addr));
_;
}
function balanceOf(address _erc20) public constant returns (uint256 balance) {
if(_erc20==address(0))
return address(this).balance;
return ERC20Interface(_erc20).balanceOf(this);
}
function transfer(address _to, address _erc20, uint256 _value) internal returns (bool success) {
require((_erc20==address(0)?address(this).balance:ERC20Interface(_erc20).balanceOf(this))>=_value);
if(_erc20==address(0))
_to.transfer(_value);
else
ERC20Interface(_erc20).approve(_to,_value);
return true;
}
function withdrawal(address _erc20, uint256 _value) public returns (bool success);
event Pay(address indexed _who, uint256 indexed _item, uint256 indexed _value);
event Refund(address indexed _who, uint256 indexed _item, uint256 indexed _value);
event Prize(address indexed _who, uint256 indexed _item, uint256 indexed _value);
}
contract _Wallet is _Base {
constructor(address _who) public {
owner = _who;
walletCenter = msg.sender;
}
function pay(address _shop, uint256 _item) private {
require(_Shop(_shop).canBuy(this,_item));
address _erc20;
uint256 _value;
(_erc20,_value) = _Shop(_shop).price(_item);
transfer(_shop,_erc20,_value);
_Shop(_shop).pay(_item);
emit Pay(_shop,_item,_value);
}
function paySafe(address _shop, uint256 _item) onlyOwner onlyShop(_shop) public payable returns (bool success) {
pay(_shop,_item);
return true;
}
function payUnsafe(address _shop, uint256 _item) onlyOwner public payable returns (bool success) {
pay(_shop,_item);
return true;
}
function payCancel(address _shop, uint256 _item) onlyOwner public returns (bool success) {
_Shop(_shop).payCancel(_item);
return true;
}
function refund(address _erc20, uint256 _item, uint256 _value) public payable returns (bool success) {
require((_erc20==address(0)?msg.value:ERC20Interface(_erc20).allowance(msg.sender,this))==_value);
if(_erc20!=address(0))
ERC20Interface(_erc20).transferFrom(msg.sender,this,_value);
emit Refund(msg.sender,_item,_value);
return true;
}
function prize(address _erc20, uint256 _item, uint256 _value) public payable returns (bool success) {
require((_erc20==address(0)?msg.value:ERC20Interface(_erc20).allowance(msg.sender,this))==_value);
if(_erc20!=address(0))
ERC20Interface(_erc20).transferFrom(msg.sender,this,_value);
emit Prize(msg.sender,_item,_value);
return true;
}
function withdrawal(address _erc20, uint256 _value) onlyOwner public returns (bool success) {
require((_erc20==address(0)?address(this).balance:ERC20Interface(_erc20).balanceOf(this))>=_value);
if(_erc20==address(0))
owner.transfer(_value);
else
ERC20Interface(_erc20).transfer(owner,_value);
return true;
}
}
contract _Shop is _Base, SafeMath{
address erc20;
constructor(address _who, address _erc20) public {
owner = _who;
walletCenter = msg.sender;
erc20 = _erc20;
}
struct item {
uint8 category; // 0 = disable, 1 = non Stock, non Expire, 2 = can Expire (after 1 week), 3 = stackable
uint256 price;
uint256 stockCount;
mapping(address=>uint256) customer;
}
uint index;
mapping(uint256=>item) items;
function pay(uint256 _item) onlyWallet(msg.sender) public payable returns (bool success) {
require(canBuy(msg.sender, _item));
require((erc20==address(0)?msg.value:ERC20Interface(erc20).allowance(msg.sender,this))==items[_item].price);
if(erc20!=address(0))
ERC20Interface(erc20).transferFrom(msg.sender,this,items[_item].price);
if(items[_item].category==1 || items[_item].category==2 && now > safeAdd(items[_item].customer[msg.sender], 1 weeks))
items[_item].customer[msg.sender] = now;
else if(items[_item].category==2 && now < safeAdd(items[_item].customer[msg.sender], 1 weeks) )
items[_item].customer[msg.sender] = safeAdd(items[_item].customer[msg.sender], 1 weeks);
else if(items[_item].category==3) {
items[_item].customer[msg.sender] = safeAdd(items[_item].customer[msg.sender],1);
items[_item].stockCount = safeSub(items[_item].stockCount,1);
}
emit Pay(msg.sender,_item,items[_item].customer[msg.sender]);
return true;
}
function payCancel(uint256 _item) onlyWallet(msg.sender) public returns (bool success) {
require (items[_item].category==2&&safeAdd(items[_item].customer[msg.sender],2 weeks)>now&&balanceOf(erc20)>=items[_item].price);
items[_item].customer[msg.sender] = safeSub(items[_item].customer[msg.sender],1 weeks);
transfer(msg.sender, erc20, items[_item].price);
_Wallet(msg.sender).refund(erc20,_item,items[_item].price);
emit Refund(msg.sender,_item,items[_item].price);
return true;
}
function refund(address _to, uint256 _item) onlyWallet(_to) onlyOwner public payable returns (bool success) {
require(isBuyer(_to,_item)&&items[_item].category>0&&(items[_item].customer[_to]>0||(items[_item].category==2&&safeAdd(items[_item].customer[_to],2 weeks)>now)));
require((erc20==address(0)?address(this).balance:ERC20Interface(erc20).balanceOf(this))>=items[_item].price);
if(items[_item].category==1)
items[_item].customer[_to] = 0;
else if(items[_item].category==2)
items[_item].customer[_to] = safeSub(items[_item].customer[_to],1 weeks);
else
items[_item].customer[_to] = safeSub(items[_item].customer[_to],1);
transfer(_to, erc20, items[_item].price);
_Wallet(_to).refund(erc20,_item,items[_item].price);
emit Refund(_to,_item,items[_item].price);
return true;
}
event Item(uint256 indexed _item, uint256 _price);
function resister(uint8 _category, uint256 _price, uint256 _stock) onlyOwner public returns (uint256 _itemId) {
require(_category>0&&_category<4);
require(_price>0);
items[index] = item(_category,_price,_stock);
index = safeAdd(index,1);
emit Item(index,_price);
return safeSub(index,1);
}
function update(uint256 _item, uint256 _price, uint256 _stock) onlyOwner public {
require(items[_item].category>0);
require(_price>0);
uint256 temp = items[_item].price;
items[_item].price = _price;
items[_item].stockCount = safeAdd(items[_item].stockCount,_stock);
if(temp!=items[_item].price)
emit Item(index,items[_item].price);
}
function price(uint256 _item) public constant returns (address _erc20, uint256 _value) {
return (erc20,items[_item].price);
}
function canBuy(address _who, uint256 _item) public constant returns (bool _canBuy) {
return (items[_item].category>0) &&
!(items[_item].category==1&&items[_item].customer[_who]>0) &&
(items[_item].stockCount>0);
}
function isBuyer(address _who, uint256 _item) public constant returns (bool _buyer) {
return (items[_item].category==1&&items[_item].customer[_who]>0)||(items[_item].category==2&&safeAdd(items[_item].customer[_who],1 weeks)>now)||(items[_item].category==3&&items[_item].customer[_who]>0);
}
uint lastWithdrawal;
function withdrawal(address _erc20, uint256 _value) onlyOwner public returns (bool success) {
require(safeAdd(lastWithdrawal,1 weeks)<=now);
require((_erc20==address(0)?address(this).balance:ERC20Interface(_erc20).balanceOf(this))>=_value);
if(_erc20==address(0))
owner.transfer(_value);
else
ERC20Interface(_erc20).transfer(owner,_value);
lastWithdrawal = now;
return true;
}
}
contract WalletCenter {
mapping(address=>bool) public wallet;
event Wallet(address indexed _owner, address indexed _wallet);
function createWallet() public returns (address _wallet) {
_wallet = new _Wallet(msg.sender);
wallet[_wallet] = true;
emit Wallet(msg.sender,_wallet);
return _wallet;
}
function isWallet(address _wallet) public constant returns (bool) {
return wallet[_wallet];
}
mapping(address=>bool) public shop;
event Shop(address indexed _owner, address indexed _shop, address indexed _erc20);
function createShop(address _erc20) public returns (address _shop) {
_shop = new _Shop(msg.sender,_erc20);
shop[_shop] = true;
emit Shop(msg.sender,_shop,_erc20);
return _shop;
}
function isShop(address _shop) public constant returns (bool) {
return shop[_shop];
}
}
```
## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).