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

EIP-5528 update MD contents in review state #5832

Merged
merged 1 commit into from
Oct 25, 2022
Merged
Changes from all commits
Commits
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
133 changes: 118 additions & 15 deletions EIPS/eip-5528.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,13 @@ In traditional finance, trusted escrow services solve this problem. In the world

There are two types of contract for the escrow process:

- *Payable Contract*: The sellers and buyers use this token to fund the *Escrow Contract*.
- *Escrow Contract*: Defines the escrow policies and holds *Payable Contract*'s token for a certain period.

This standard proposes interfaces on top of the [EIP-20](./eip-20.md) standard.
- *Payable Contract*: The sellers and buyers use this token to fund the *Escrow Contract*. This contract MUST override [EIP-20](./eip-20.md) interfaces.
- *Escrow Contract*: Defines the escrow policies and holds *Payable Contract*'s token for a certain period. This contract does not requires override [EIP-20](./eip-20.md) interfaces.

### Methods

#### constructor
#### `constructor`
The *Escrow Contract* demonstrates details of escrow policies as none-mutable matter in constructor implementation.

The *Escrow Contract* MUST define the following policies:

Expand Down Expand Up @@ -67,8 +66,7 @@ In the case of *Escrow Contract*:
In the case of *Payable Contract*:

- The address `_to` MUST be the *Escrow Contract* address.
- MUST call EIP-20's `transfer` function.
- Before calling `transfer` function, MUST call the same function of the *Escrow Contract* interface. The parameter `_to` MUST be `msg.sender` to recognize the user address in the *Escrow Contract*.
- MUST call the same function of the *Escrow Contract* interface. The parameter `_to` MUST be `msg.sender` to recognize the user address in the *Escrow Contract*.

```solidity
function escrowFund(address _to, uint256 _value) public returns (bool)
Expand All @@ -87,8 +85,7 @@ In the case of *Escrow Contract*:
In the case of *Payable Contract*:

- The address `_from` MUST be the *Escrow Contract* address.
- MUST call EIP-20's `_transfer` likely function.
- Before calling `_transfer` function, MUST call the same function of the *Escrow Contract* interface. The parameter `_from` MUST be `msg.sender` to recognize the user address in the *Escrow Contract*.
- MUST call the same function of the *Escrow Contract* interface. The parameter `_from` MUST be `msg.sender` to recognize the user address in the *Escrow Contract*.

```solidity
function escrowRefund(address _from, uint256 _value) public returns (bool)
Expand All @@ -110,36 +107,142 @@ function escrowWithdraw() public returns (bool)

### Example of interface

This example demonstrates simple exchange of one seller and one buyer in one-to-one exchange rates.
```solidity
pragma solidity ^0.4.20;

interface IERC5528 is ERC20 {
interface IERC5528 {

function escrowFund(address _to, uint256 _value) public returns (bool);

function escrowRefund(address to, uint256 amount) public returns (bool);
function escrowRefund(address _from, uint256 _value) public returns (bool);

function escrowWithdraw() public returns (bool);

}

contract PayableContract is IERC5528, IERC20 {
/*
General ERC20 implementations
*/

function _transfer(address from, address to, uint256 amount) internal {
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
_balances[from] = fromBalance - amount;
_balances[to] += amount;
}

function transfer(address to, uint256 amount) public returns (bool) {
address owner = msg.sender;
_transfer(owner, to, amount);
return true;
}

function escrowFund(address _to, uint256 _value) public returns (bool){
bool res = IERC5528(to).escrowFund(msg.sender, amount);
require(res, "Fund Failed");
_transfer(msg.sender, to, amount);
return true;
}

function escrowRefund(address _from, uint256 _value) public returns (bool){
bool res = IERC5528(_from).escrowRefund(msg.sender, _value);
require(res, "Refund Failed");
_transfer(_from, msg.sender, _value);
return true;
}
}

contract EscrowContract is IERC5528 {

enum State { Inited, Running, Success, Closed }
struct BalanceData {
address addr;
uint256 amount;
}

address _addrSeller;
address _addrBuyer;
BalanceData _fundSeller;
BalanceData _fundBuyer;
EscrowStatus _status;

constructor(address sellerContract, address buyerContract){
_addrSeller = sellerContract;
_addrBuyer = buyerContract;
_status = State.Inited;
}

function escrowFund(address _to, uint256 _value) public returns (bool){
if(msg.sender == _addrSeller){
require(_status.state == State.Running, "must be running state");
_fundSeller.addr = _to;
_fundSeller.amount = _value;
_status = State.Success;
}else if(msg.sender == _addrBuyer){
require(_status.state == State.Inited, "must be init state");
_fundBuyer.addr = _to;
_fundBuyer.amount = _value;
_status = State.Running;
}else{
require(false, "Invalid to address");
}
return true;
}

function escrowRefund(address _from, uint256 amount) public returns (bool){
require(_status.state == State.Running, "refund is only available on running state");
require(msg.sender == _addrBuyer, "invalid caller for refund");
require(_fundBuyer.addr == _from, "only buyer can refund");
require(_fundBuyer.amount >= amount, "buyer fund is not enough to refund");
_fundBuyer.amount = _fundBuyer.amount - amount
return true;
}

function escrowWithdraw() public returns (bool){
require(_status.state == State.Success, "withdraw is only available on success state");
uint256 common = MIN(_fundBuyer.amount, _fundSeller.amount);

if(common > 0){
_fundBuyer.amount = _fundBuyer.amount - common;
_fundSeller.amount = _fundSeller.amount - common;

// Exchange
IERC5528(_addrSeller).transfer(_fundBuyer.addr, common);
IERC5528(_addrBuyer).transfer(_fundSeller.addr, common);

// send back the remaining balances
if(_fundBuyer.amount > 0){
IERC5528(_addrBuyer).transfer(_fundBuyer.addr, _fundBuyer.amount);
}
if(_fundSeller.amount > 0){
IERC5528(_addrSeller).transfer(_fundSeller.addr, _fundSeller.amount);
}
}

_status = State.Closed;
}

}

```

## Rationale

The interfaces described in this EIP have been chosen to cover the refundable issue in the escrow operation.
The interfaces cover the escrow operation's refundable issue.

The suggested 3 functions (`escrowFund`, `escrowRefund` and `escrowWithdraw`) are based on `transfer` function in EIP-20.

`escrowFund` send tokens to the *Escrow Contract*. The *Escrow Contract* can hold the contract in the escrow process or reject tokens if the policy does not meet.

`escrowRefund` can be invoked in the middle of the escrow process or when the escrow process is failed.
`escrowRefund` can be invoked in the middle of the escrow process or when the escrow process fails.

`escrowWithdraw` allows users (sellers and buyers) to transfer tokens from the escrow account. When the escrow process is completed, the seller can get the buyer's token, and the buyers can get the seller's token.
`escrowWithdraw` allows users (sellers and buyers) to transfer tokens from the escrow account. When the escrow process completes, the seller can get the buyer's token, and the buyers can get the seller's token.

## Backwards Compatibility

This EIP is fully backward compatible with the [EIP-20](./eip-20.md) specification.
The *Payable Contract* which implements this EIP is fully backward compatible with the [EIP-20](./eip-20.md) specification.

## Test Cases

Expand Down