Skip to content

Commit

Permalink
Add EIP-5252: Account-bound Finance (#5252)
Browse files Browse the repository at this point in the history
* add eip-5190

* Update eip-5190.md

* Update eip-5190.md

* Update eip-5190.md

* Update eip-5190.md

* update eip-5190.md

* update eip-5190.md

* Update EIPS/eip-5190.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5190.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5190.md

Co-authored-by: Micah Zoltu <[email protected]>

* Update EIPS/eip-5190.md

Co-authored-by: Micah Zoltu <[email protected]>

* Update EIPS/eip-5190.md

Co-authored-by: Micah Zoltu <[email protected]>

* Update EIPS/eip-5190.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5190.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5190.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5190.md

Co-authored-by: Pandapip1 <[email protected]>

* Update and rename eip-5190.md to eip-5252.md

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* update eip-5252.md

* update eip-5252.md

* update eip-5252.md

* update eip-5252.md

* update eip-5252.md

* update eip-5252.md

* update eip-5252.md

* update eip-5252.md

* update eip-5252.md

* add details on abt governance

* update eip-5252

* update eip-5252 contracts

* update eip-5252

* add rationale and security consideration section

* eip-5252 rationale

* update eip-5252

* add .gitignore

* update eip-5252

* update eip5252

* update eip5252

* update eip5252

* Update assets/eip-5252/contracts/ABT.sol

Co-authored-by: Pandapip1 <[email protected]>

* Update assets/eip-5252/contracts/ERC721A.sol

Co-authored-by: Pandapip1 <[email protected]>

* Update assets/eip-5252/contracts/Finance.sol

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update assets/eip-5252/contracts/ERC721A.sol

Co-authored-by: Pandapip1 <[email protected]>

* Update assets/eip-5252/contracts/Factory.sol

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update README.md

* update eip-5252.md

* Update EIPS/eip-5252.md

Co-authored-by: xinbenlv <[email protected]>

* add requirements for interaction

* add properties for global functions

* add requirement keywords to the specification

* add SHALL

* add requirements

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update assets/eip-5252/.gitignore

Co-authored-by: Pandapip1 <[email protected]>

* update contracts

* update gitignore

* update influencer

* add citation

* remove citation

* Update assets/eip-5252/contracts/Manager.sol

Co-authored-by: Pandapip1 <[email protected]>

* Update assets/eip-5252/contracts/interfaces/IFactory.sol

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update assets/eip-5252/contracts/interfaces/IABT.sol

Co-authored-by: Pandapip1 <[email protected]>

* Update assets/eip-5252/contracts/interfaces/IManager.sol

Co-authored-by: Pandapip1 <[email protected]>

* Update eip-5252.md

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Update assets/eip-5252/contracts/interfaces/IFactory.sol

Co-authored-by: Pandapip1 <[email protected]>

* Update assets/eip-5252/contracts/interfaces/IABT.sol

Co-authored-by: Pandapip1 <[email protected]>

* Update assets/eip-5252/contracts/interfaces/IFinance.sol

Co-authored-by: Pandapip1 <[email protected]>

* Update assets/eip-5252/contracts/interfaces/IFinance.sol

Co-authored-by: Pandapip1 <[email protected]>

* Update assets/eip-5252/contracts/governance/Governor.sol

Co-authored-by: Pandapip1 <[email protected]>

* Update EIPS/eip-5252.md

Co-authored-by: Pandapip1 <[email protected]>

* Delete empty file

* Delete second empty file

* Apply suggestions from code review

* add clarification in motivation section

* Fix a few issues

* remove .DS_Store

Co-authored-by: Hyungsuk Kang <[email protected]>
Co-authored-by: Pandapip1 <[email protected]>
Co-authored-by: Micah Zoltu <[email protected]>
Co-authored-by: xinbenlv <[email protected]>
  • Loading branch information
5 people authored Sep 5, 2022
1 parent 2c12b12 commit 60fa745
Show file tree
Hide file tree
Showing 29 changed files with 1,821 additions and 0 deletions.
231 changes: 231 additions & 0 deletions EIPS/eip-5252.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
---
eip: 5252
title: Account-bound Finance
description: An EIP-5114 extension that aids in preventing arbitrary loss of funds
author: Hyungsuk Kang (@hskang9), Viktor Pernjek (@smuxx)
discussions-to: https://ethereum-magicians.org/t/pr-5252-discussion-account-bound-finance/10027
status: Draft
type: Standards Track
category: ERC
created: 2022-06-29
requires: 20, 721, 1155, 5114
---

## Abstract
This EIP proposes a form of smart contract design pattern and a new type of account abstraction on how one's finance should be managed, ensuring transparency of managing investments and protection with self-sovereignty even from its financial operators. This EIP enables greater self-sovereignty of one's assets using a personal finance contract for each individual. The seperation between an investor's funds and the operation fee is clearly specified in the personal smart contract, so investors can ensure safety from arbitrary loss of funds by the operating team's control.

This EIP extends [EIP-5114](./eip-5114.md) to further enable transferring fund to other accounts for mobility between managing multiple wallets.

## Motivation

Decentralized finance (DeFi) faces a trust issue. Smart contracts are often proxies, with the actual logic of the contract hidden away in a separate logic contract. Many projects include a multi-signature "wallet" with unnecessarily-powerful permissions. And it is not possible to independently verify that stablecoins have enough real-world assets to continue maintaining their peg, creating a large loss of funds (such as happened in the official bankruptcy announcement of Celsius and UST de-pegging and anchor protocol failure). One should not trust exchanges or other third parties with one's own investments with the operators' clout in Web3.0.

Smart contracts are best implemented as a promise between two parties written in code, but current DeFi contracts are often formed using less than 7 smart contracts to manage their whole investors' funds, and often have a trusted key that has full control. This is evidently an issue, as investors have to trust contract operators with their funds, meaning that users do not actually own their funds.

The pattern with personal finance contract also offers more transparency than storing mixed fund financial data in the operating team's contract. With a personal finance contract, an account's activity is easier to track than one global smart contract's activity. The pattern introduces a Non-Fungiible Account-Bound Token (ABT) to store credentials from the personal finance contract.

#### Offchain-identity vs Soul-bound token on credentials

This EIP provides a better alternative to off-chain identity solutions which take over the whole system because their backends eventually rely on the trust of the operator, not cryptographic proof (e.g. Proof-of-work, Proof-of-stake, etc). Off-chain identity as credentials are in direct opposition to the whole premise of crypto. Soulbound tokens are a better, verifiable credential, and data stored off-chain is only to store token metadata.

## Specification
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

The specification consists of two patterns for **Interaction** and **Governance**.

### Interaction

#### Interfaces

The interaction pattern consists of 4 components for interaction; manager, factory, finance, account-bound token, and extension.

Interaction contract pattern is defined with these contracts:
- A soul-bound or account bound token contract to give access to interact with a financial contract with credentials
- A manager contract that interacts first contact with an investor
- A factory contract that creates a financial contract for each user
- A finance contract that can interact with the investor

#### Requirements

A soul-bound or account bound token contract is defined with these properties:
1. It SHALL be non-fungible and MUST satisfy [EIP-721](./eip-721.md).
2. Credentials SHOULD be represented with its metadata with `tokenURI()` function.
3. It MUST only reference factory to verify its minting.
4. If it is transferrable, it is account-bound. If not, it is soul-bound.

A manager contract is defined with these properties:
1. It MUST be the only kind of contract which calls factory to create.
2. It SHOULD store all related configurations for financial parameters.

A factory contract is defined with these properties:
1. It SHALL clone the finance contract with uniform implementation.
2. It MUST be the only contract that can mint account-bound token.
3. It MUST keep an recent id of account bound token.

A finance contract is defined with these properties:
1. A finance contract MUST only be initialized once from factory contract in constructor.
2. Funds in the contract SHALL NOT be transferred to other contracts nor accounts unless sender who owns soul-bound or account bound token signs to do so.
3. Every state-changing function of the smart contract MUST only accept sender who owns soul-bound or account bound-token except global function(e.g. liquidation).
4. Global function SHOULD be commented as `/* global */` to clarify the function is can be accessed with anyone.
4. Each finance contract SHOULD be able to represent transaction that has happened only with those who had account-bound token.
5. If soul-bound token is used for access, the finance contract MUST be able to represent transaction that has happened only between whom had the private key and the finance contract.

#### Contracts

<figure>
<img src="../assets/eip-5252/media/media.svg" alt="Diagram" style="width:100%">
<figcaption align = "center"><b>Fig 1 - Contract Diagram of EIP-5252</b></figcaption>
</figure>


**`Manager`**: **`Manager`** contract acts as an entry point to interact with the investor. The contract also stores parameters for **`Finance`** contract.

**`Factory`**: **`Factory`** contract manages contract bytecode to create for managing investor's fund and clones **`Finance`** contract on **`Manager`** contract's approval. It also mints account-bound tokens to interact with the `Finance` contract.

**`Finance`**: **`Finance`** contract specifies all rules on managing an investor's fund. The contract is only accessible with an account that has an Account-bound token. When an investor deposits a fund to **`Manager`** contract, the contract sends the fund to **`Finance`** contract account after separating fees for operation.

**`Account-bound token`**: **`Account-bound token`** contract in this EIP can bring the **`Finance`** contract's data and add metadata. For example, if there is a money market lending
**`Finance`** contract, its **`Account-bound token`** can show how much balance is in agreement using SVG.

**`Extension`**: **`Extension`** contract is another contract that can utilize locked funds in **`Finance`** contract. The contract can access with **`Finance`** contract on operator's approval managed in **`Manager`** contract. Example use case of `Extension` can be a membership.

**`Metadata`**: **`Metadata`** contract is the contract where it stores metadata related to account credentials. Credential related data are stored with specific key. Images are usually displayed as SVG, but offchain image is possible.

---

### Governance

The governance pattern consists of 2 components; influencer and governor.

#### Interfaces

#### Requirements

An influencer contract is defined with these properties:
1. The contract SHALL manage multiplier for votes.
2. The contract SHALL set a decimal to calculated normalized scores.
3. The contract SHALL set a function where governance can decide factor parameters.

A governor contract is defined with these properties:
1. The contract MUST satisfy Governor contract from OpenZeppelin.
2. The contract SHALL refer influencer contract for multiplier
3. The contract MUST limit transfer of account bound token once claimed for double vote prevention.

#### From Token Governance To Contribution Based Governance

| | Token Governance | Credential-based Governance |
|---------------|----------------------------------|--------------------------------------------------|
| Enforcement | More tokens, more power | More contribution, More power |
| Incentives | More tokens, more incentives | More contribution, more incentives |
| Penalty | No penalty | Loss of power |
| Assignment | One who holds the token | One who has the most influence |

<figcaption align = "center"><b>Token Governance vs Credential Based Governance</b></figcaption>

Token governance is not sustainable in that it gives **more** power to "those who most want to rule". Any individual who gets more than 51% of the token supply can forcefully take control.


New governance that considers contributions to the protocol is needed because:

- **Rulers can be penalized on breaking the protocol**
- **Rulers can be more effectively incentivized on maintaining the protocol**

The power should be given to "those who are most responsible". Instead of locked or owned tokens, voting power is determined with contributions marked in Account Bound Tokens (ABT). This EIP defines this form of voting power as **`Influence`**.

#### Calculating Influence

**`Influence`** is a multiplier on staked tokens that brings more voting power of a DAO to its contributors. To get **`Influence`**, a score is calculated on weighted contribution matrix. Then, the score is normalized to give a member's position in whole distribution. Finally, the multiplier is determined on the position in every community members.

#### Calculating score

The weights represent relative importance on each factor. The total importance is the total sum of the factors. More factors that can be normalized at the time of submitting proposal can be added by community.

| | Description |
|----|------------------------------------------------|
| α | Contribution value per each **`Finance`** contract from current proposal|
| β | Time they maintained **`Finance`** per each contract from current timestamp of a proposal|

```math
(score per each ABT) = α * (contribution value) + β * (time that abt was maintained from now)
```

#### Normalization

Normalization is applied for data integrity on user's contribution in a DAO.
Normalized score can be calculated from the state of submitting a proposal

```math
(Normalized score per each ABT) = α * (contribution value)/(total contribution value at submitting tx) + β * (time that abt was maintained)/(time passed from genesis to proposal creation)
```

and have a value between 0 and 1 (since α + β = 1).

#### Multiplier

The multiplier is determined linearly from base factor (b) and multiplier(m).

The equation for influence is :

```math
(influence) = m * (sum(normalized_score))
```

#### Example

For example, if a user has 3 **`Account-bound tokens`** with normalized score of each 1.0, 0.5, 0.3 and the locked token is 100, and multiplier is 0.5 and base factor is 1.5. Then the total influence is

```math
0.5 * {(1.0 + 0.5 + 0.3) / 3} + 1.5 = 1.8
The total voting power would be
```math
(voting power) = 1.8 * sqrt(100) = 18
```

#### Stakers vs Enforcers

| | Stakers | Enforcers |
|--------------|-----------------------|-----------------------------------------------------------------------------------------|
| Role | stake governance token for voting | Contributed on the system, can make proposal to change rule, more voting power like 1.5 |
| Populations | many | small |
| Contribution | Less effect | More effect |
| Influence | sqrt(locked token) | Influence * sqrt(locked token) |

<figcaption align = "center"><b>Fig 1 - Stakers vs Enforcers</b></figcaption>

**Stakers**: Stakers are people who vote to enforcers' proposals and get dividend for staked tokens

**Enforcers**: Enforcers are people who takes risk on managing protocol and contributes to the protocol by making a proposal and change to it.

#### Contracts

**`Influencer`**: An **`Influencer`** contract stores influence configurations and measures the contribution of a user from his activities done in a registered Account Bound Token contract. The contract puts a lock on that Account Bound Token until the proposal is finalized.

**`Governor`**: **`Governor`** contract is compatible with the current governor contract in OpenZeppelin. For its special use case, it configures factors where the influencer manages and has access to changing parameters of **`Manager`** configs. Only the `Enforcer` can propose new parameters.

## Rationale

#### Gas saving for end user
The gas cost of using multiple contracts (as opposed to a single one) actually saves gas long-run if the clone factory pattern is applied. One contract storing users' states globally means each user is actually paying for the storage cost of other users after interacting with the contract. This, for example, means that MakerDAO's contract operating cost is sometimes over 0.1 ETH, limitimg users' minimum deposit for CDP in order to save gas costs. To solve inefficient n-times charging gas cost interaction for future users, one contract per user is used.

#### Separation between investor's and operation fund
The separation between an investor's funds and operation fee is clearly specified in the smart contract, so investors can ensure safety from arbitrary loss of funds by the operating team's control.

## Backwards Compatibility
This EIP has no known backward compatibility issues.

## Reference Implementation

[Reference implementation](../assets/eip-5252/README.md) is a simple deposit account contract as `Finance` contract and its contribution value α is measured with deposit amount with ETH.

## Security Considerations

- **`Factory`** contracts must ensure that each **`Finance`** contract is registered in the factory and check that **`Finance`** contracts are sending transactions related to their bounded owner.

- Reentrancy attack guard should be applied or change state before delegatecall in each user function in **`Manager`** contract or **`Finance`** contract. Otherwise, **`Finance`** can be generated as double and ruin whole indices.

- Once a user locks influence on a proposal's vote, an **`Account Bound Token`** cannot be transferred to another wallet. Otherwise, double influence can happen.

## Copyright
Copyright and related rights waived via [CC0](../LICENSE.md).
14 changes: 14 additions & 0 deletions assets/eip-5252/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
node_modules
.env
coverage
coverage.json
typechain
typechain-types
yarn.lock
package-lock.json
yarn-error.log
.DS_Store

#Hardhat files
cache
artifacts
13 changes: 13 additions & 0 deletions assets/eip-5252/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# EIP 5252 implementation

This project is a reference implementation of EIP-5252.

Try running some of the following tasks:

```shell
npx hardhat help
npx hardhat test
GAS_REPORT=true npx hardhat test
npx hardhat node
npx hardhat run scripts/deploy.ts
```
69 changes: 69 additions & 0 deletions assets/eip-5252/contracts/ABT.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: CC0-1.0

pragma solidity ^0.8.0;

import "./ERC721A.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "./interfaces/IFactory.sol";
import "./interfaces/IFinance.sol";
import "./interfaces/IDescriptor.sol";

contract ABT is ERC721A, AccessControl {
// Create a new role identifier for the minter role
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");
// factory address
address public factory;
// SVG for ABT
address public descriptor;

function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721A, AccessControl) returns (bool) {
return super.supportsInterface(interfaceId);
}

function setDescriptor(address descriptor_) public {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "ABT: Caller is not a default admin");
descriptor = descriptor_;
}

function tokenURI(uint256 tokenId) public view virtual override returns (string memory tokenURI) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
tokenURI = IDescriptor(descriptor).tokenURI(tokenId);
}

constructor(address factory_)
ERC721A("Account-Bound Token", "ABT") {
_setupRole(DEFAULT_ADMIN_ROLE, _msgSender());

_setupRole(MINTER_ROLE, _msgSender());
_setupRole(BURNER_ROLE, _msgSender());
factory = factory_;
}

function setFactory(address factory_) public {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "ABT: Caller is not a default admin");
factory = factory_;
}

function mint(address to) external {
// Check that the calling account has the minter role
require(_msgSender() == factory, "ABT: Caller is not factory");
_safeMint(to, 1);
}

function burn(uint256 tokenId_) external {
require(hasRole(BURNER_ROLE, _msgSender()), "ABT: must have burner role to burn");
_burn(tokenId_);
}

function exists(uint256 tokenId_) external view returns (bool) {
return _exists(tokenId_);
}

function transfer(
address to,
uint256 tokenId
) public virtual {
transferFrom(msg.sender, to, tokenId);
}
}
Loading

0 comments on commit 60fa745

Please sign in to comment.