├── .gitignore ├── BankofStaked-logo.png ├── LICENSE ├── Order-Process-of-BankofStaked.svg ├── README-CN.md ├── README-KR.md ├── README.md ├── _config.yml ├── build.sh ├── cpu-emergency ├── Centralized-CPU-Emergency.svg ├── README.md ├── eoslive.png └── meetone.jpeg ├── doc ├── BankofStaked-Creditor-Resource-Guide.md ├── BankofStaked-Creditor-Term-CN.md └── BankofStaked-Creditor-Term-EN.md ├── include └── bankofstaked │ └── bankofstaked.hpp ├── rc └── bankofstaked.ricardian.clauses.md ├── scripts ├── add_creditor.sh ├── bank_perm.sh ├── creditor_perm.sh ├── dev.sh ├── dev │ ├── check.sh │ ├── deploy.sh │ └── spammer.sh ├── empty.sh ├── get_table.sh ├── order_perm.sh └── set_plan.sh ├── src ├── bankofstaked.cpp ├── lock.cpp ├── safedelegatebw.hpp ├── utils.cpp └── validation.cpp ├── stats ├── fetch.py └── requirements.txt ├── tests ├── CMakeLists.txt ├── LICENSE ├── README.md ├── UnitTestsExternalProject.txt ├── build.sh ├── clean-build.sh ├── faketransfer │ ├── build.sh │ └── faketransfer.cpp └── tests │ ├── CMakeLists.txt │ ├── bankofstaked_tester.hpp │ ├── bankofstaked_tests.cpp │ ├── contracts.hpp.in │ ├── eosio.system_tester.hpp │ ├── main.cpp │ ├── test_contracts │ ├── eosio.msig │ │ ├── eosio.msig.abi │ │ └── eosio.msig.wasm │ ├── eosio.system │ │ ├── eosio.system.abi │ │ └── eosio.system.wasm │ └── eosio.token │ │ ├── eosio.token.abi │ │ └── eosio.token.wasm │ └── test_symbol.hpp └── unittest.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.wasm 2 | *.wast 3 | *.abi 4 | build 5 | tests/build 6 | -------------------------------------------------------------------------------- /BankofStaked-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EOSLaoMao/BankofStaked-CE/3b764debe9bad8047c0cf28930b91b7a736a09d6/BankofStaked-logo.png -------------------------------------------------------------------------------- /README-CN.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |
6 |

Bank of Staked

7 |

CPU&NET 自助租赁合约

8 |
9 | 10 | 11 | English README 12 | 13 | 한국의 README 14 | 15 | ### 关于 16 | BankofStaked 智能合约旨在向 EOS 用户和开发者提供便捷的 CPU 和 NET 资源租赁服务。 17 | 18 | 该合约由 `EOSLaoMao` 团队开发。 19 | 20 | ### 设计思路 21 | 22 | 23 | BankofStaked 的设计初衷,是希望提供便捷的 CPU 和 NET 自助租赁体验。用户只需要向 BankofStaked 合约发送一笔符合要求的 EOS 转账,即可完成 CPU 和 NET 资源的租赁,并且,在租赁到期之后,合约也将自动取消 CPU 和 NET 的抵押,实现完全的自动化。你可以把它视为一台 CPU & NET 资源的自动贩卖机。 24 | 25 | BankofStaked 合约的主要业务逻辑由下面 3 张数据表(table)构成: 26 | 27 | #### 1. Plan 表 28 | 29 | Plan 用于存储 BankofStaked 合约目前可用的价格方案,其主要字段如下: 30 | 31 | ``` 32 | price asset; // 价格(单位为 EOS) 33 | cpu asset; // 该价格下提供的 CPU(单位为 EOS) 34 | net asset; // 该价格下提供的 NET(单位为 EOS) 35 | duration unit64; // 服务有效期(单位为分钟) 36 | is_free uint64; // 服务是否免费,如果 is_free 字段为 1, 用户转入 BankofStaked 合约的 EOS 将瞬间自动返还 37 | ... 38 | ``` 39 | 40 | #### 2. Creditor 表 41 | 42 | 43 | 44 | `Creditor` 表存储的是真正给用户提供 CPU 和 NET 抵押的账户。 45 | 46 | 当合约收到符合条件的 EOS 转账的时候,合约将从 `Creditor` 表中查找符合条件的“贷出人”账户,该账户将自动为用户抵押相应的 CPU 和 NET 资源。在服务到期之后,“贷出人”账户也将自动解除这笔抵押。 47 | 48 | ``` 49 | account account_name; 50 | is_active uint64; 51 | ... 52 | ``` 53 | 54 | `is_active` 字段表示该“贷出人”账户是否提供租赁服务。 55 | 56 | 在生产环境中,`Creditor` 表中应该提供多位“贷出人”,并采用合适的轮换策略,以保证非活跃的贷出人账户中解除抵押尚未到账的 EOS 及时到账。 57 | 58 | #### 3. Order 表 59 | 60 | Order 表存储的,是服务还在有效期内的订单。其主要字段如下: 61 | 62 | ``` 63 | buyer account_name; 64 | creditor account_name; 65 | beneficiary account_name; 66 | cpu_staked asset; 67 | net_staked asset; 68 | expire_at uint64; 69 | ... 70 | ``` 71 | 72 | `buyer` 存储的是下单账户,也就是向 BankofStaked 合约转账 EOS 购买租赁服务的账户。(如果有退款发生,退款也将返还给 `buyer` 账户) 73 | 74 | `beneficiary` 存储的是该订单真正的受益账户,`buyer` 可以在转账的 memo 中指定受益账户,未指定的话,`beneficiary` 跟 `buyer` 相同。 75 | 76 | `creditor` 存储的是真正给 `beneficiary` 做抵押的“贷出人”账户。 77 | 78 | `cpu_staked` 和 `net_staked` 记录了抵押的 CPU 和 NET 的多少。 79 | 80 | `expire_at` 表示租赁服务的过期时间,到期后,订单记录将被自动删除。 81 | 82 | 83 | 除了上述的 3 张主要的数据表以外,还有一些辅助的数据表,比如: 84 | 85 | `freelock` 表,用来给免费的 Plan 加锁,防止单一用户滥用。 86 | 87 | `history` 表,用来记录过期之后被删除的历史订单。 88 | 89 | `blacklist` 表,用于管理黑名单,禁止特定用户使用租赁服务。 90 | 91 | ![Process](./Order-Process-of-BankofStaked.svg) 92 | 93 | 94 | # EOS 主网上的 BankofStaked 合约 95 | 96 | ### 如何使用 97 | 98 | 我们已经将 BankofStaked 合约部署在了 EOS 主网,以及 Kylin 和 Jungle 测试网络: 99 | 100 | EOS 主网:https://www.myeoskit.com/#/tx/bankofstaked 101 | 102 | Jungle 测试网:https://jungle.bloks.io/account/bankofstaked 103 | 104 | Kylin 测试网:https://kylin.bloks.io/account/bankofstaked 105 | 106 | 107 | 108 | #### 第一步:查询可用的价格方案 109 | 110 | 查询 `Plan` 表,获取当前可用的价格方案: 111 | 112 | 113 | ``` 114 | cleos -u https://api1.eosasia.one get table bankofstaked bankofstaked plan 115 | 116 | { 117 | "rows": [{ 118 | "id": 0, 119 | "price": "0.1000 EOS", 120 | "is_free": "1", 121 | "cpu": "1.0000 EOS", 122 | "net": "0.0000 EOS", 123 | "duration": 60, 124 | "created_at": 1535965927, 125 | "updated_at": 1535965927 126 | }, 127 | ], 128 | "more": false 129 | } 130 | ``` 131 | 132 | `price` 表示你需要转给 `bankofstaked` 的 EOS,`cpu` and `net` 表示该价格下,你将获得的 CPU 和 NET 资源,`duration` 表示租赁期限,单位为分钟。 133 | 134 | `is_free` 表示该价格计划是否免费(用户成功购买免费的价格计划之后,会立即获得全额的退款) 135 | 136 | 我们将很快提供更多的价格方案。 137 | 138 | #### 第二步:转账购买 CPU&NET 资源 139 | 140 | 141 | 目前 BankofStaked 在 EOS 主网上暂只提供 1 个免费计划。你可以向 `bankofstaked` 账户转账 `0.1 EOS` 立即获得 `1 EOS` 的 CPU 抵押一小时服务。并且 `0.1 EOS` 的转账也将被合约瞬间返还。 142 | 143 | 你可以直接通过 EOS 钱包进行转账,也可以使用命令行的方式: 144 | 145 | ``` 146 | cleos -u https://api1.eosasia.one transfer 你的账户 bankofstaked "0.1 EOS" -p 你的账户@active 147 | ``` 148 | 149 | 150 | 转账成功后,你将立即获得全额的返还,并且立即获得 `1 EOS` CPU 的抵押。一个小时之后,该抵押将自动解除。(注:同一个账户每 24 个小时,只能购买一次免费服务) 151 | 152 | 153 | 154 | --- 155 | 156 | 如果你是 BP 或者 EOS 持有者,想要跟我们一起向用户提供免费的救急抵押服务,或者向开发者提供租赁服务,欢迎联系我们:contact@eoslaomao.com 157 | 158 | 159 | built with love by EOSLaoMao Team. :) 160 | 161 | Icon made by Freepik from www.flaticon.com, special thanks~ 162 | -------------------------------------------------------------------------------- /README-KR.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |
6 |

Bank of Staked

7 |

a self-serve CPU&NET Vending Machine

8 |
9 | 10 | English README 11 | 12 | 中文版 README 13 | 14 | 15 | ### Bank of Staked 에 대해서 16 | 뱅크 오브 스테이크 (Bank of Staked)는 EOS 사용자와 개발자 모두에게 값싼 CPU 및 NET 리스를 제공하기 위해 EOSLaoMao 팀이 작성한 EOS 스마트 컨트랙트입니다. 17 | 18 | 19 | ### Design 20 | 21 | Bank of Staked의 UX는 모든 계정이 간단한 과정을 통해 CPU 와 NET을 자동으로 위임 받을 수 있게 하는 목표를 달성하고자 합니다. 이러한 목표에는 취소 프로세스의 자동실행도 포함됩니다. 22 | 23 | 스테이크 뱅크의 주요 로직은 다음 세 테이블을 통해 실현됩니다: 24 | 25 | #### 1. Plan Table(계획 테이블) 26 | 27 | 계획 테이블은 사용자가 선택할 수 있는 모든 사용가능한 계획을 보유하고 있으며, 주요 필드는 다음과 같습니다: 28 | 29 | ``` 30 | price asset; //plan price 31 | cpu asset; // delegated cpu this plan offers. 32 | net asset; // delegated net this plan offers. 33 | duration unit64; //the period of time service gonna last, in minutes. 34 | is_free uint64; //free plan or not, if is_free is 1, order will be auto refunded. 35 | ... 36 | ``` 37 | 38 | #### 2. Creditor Table (채권자 테이블) 39 | 40 | 채권자는 CPU 와 NET을 위임해주고 위임 받는 실제 주인들입니다. 유효한 양도 거래가 이루어지면 스마트 컨트랙트는 `is _active` 채권자를 찾아 해당 채권자의 계좌를 통해 자동으로 자원을 위임합니다. 41 | 42 | 43 | ``` 44 | account account_name; 45 | is_active uint64; 46 | ... 47 | ``` 48 | 49 | `is_active`는 채권자가 위임을 할 준비가 되어있는지 알려줍니다. 50 | 51 | 프로덕션 환경에서는 채권자가 항상 3일 기준으로 바뀌게 하여 비활성화 상태인 채권자에게도 토큰을 돌려줄 수 있도록 합니다. 52 | 53 | #### 3. Order Table (주문 테이블) 54 | 55 | 주문 테이블은 활성 주문 기록으로 구성되며, 활성이라는 것은 만료되지 않은 주문을 말합니다. 56 | 57 | ``` 58 | buyer account_name; 59 | creditor account_name; 60 | beneficiary account_name; 61 | cpu_staked asset; 62 | net_staked asset; 63 | expire_at uint64; 64 | ... 65 | ``` 66 | 67 | 구매자는 처음에 이체를 실제로 실행한 계정입니다. 환불 거래가 발생하면 토큰도 구매자 계정으로 환급됩니다. 68 | 69 | 수혜자는 CPU 와 NET을 실제로 위임받는 계정입니다. 구매자는 원하는 경우에 이 계좌를 송금 메모에 지정할 수 있습니다. 70 | 71 | 채권자는 토큰을 위임한 계좌입니다. 72 | 73 | `cpu_staked` 와 `net_staked`는 이 순서대로 위임된 CPU 와 NET을 말합니다. 74 | 75 | `expire_at` 은 주문이 만료되는 시기입니다. 주문이 만료되면 주문 기록이 주문 표에서 삭제됩니다. 76 | 77 | 78 | 이 계약을 용이하게 하는 다른 몇 가지 표가 더 있습니다. 예를 들어, 79 | 80 | 24시간 동안 각 계정의 무료 계획을 잠그는 데 사용되는 `freelock` table 이 있고, 81 | 82 | 이미 삭제된 만료된 주문의 메타 데이터를 저장하는 데 사용되는 `history` table, 83 | 84 | Bank of Staked에 특정 계정을 블랙리스트 할 수 있는 `blacklist`가 있습니다. 85 | 86 | 87 | ![Process](./Order-Process-of-BankofStaked.svg) 88 | 89 | 90 | # Bank of Staked on Mainnet 91 | 92 | ### How to use it 93 | 94 | 저희는 Banked of Staked를 메인넷에 올렸고, 아래의 링크에서 확인하실 수 있습니다: 95 | 96 | https://www.myeoskit.com/#/tx/bankofstaked 97 | 98 | 99 | #### 1.Check available price plan 100 | 101 | 계획 테이블 쿼리를 통해 이용 가능한 계획 확인: 102 | 103 | ``` 104 | cleos -u https://api1.eosasia.one get table bankofstaked bankofstaked plan 105 | 106 | { 107 | "rows": [{ 108 | "id": 0, 109 | "price": "0.1000 EOS", 110 | "is_free": "1", 111 | "cpu": "1.0000 EOS", 112 | "net": "0.0000 EOS", 113 | "duration": 60, 114 | "created_at": 1535965927, 115 | "updated_at": 1535965927 116 | }, 117 | ], 118 | "more": false 119 | } 120 | ``` 121 | 122 | 가격은 `duration`(특정 기간/분)동안 특정 `CPU`와 `NET`을 얻기 위해 `bankofstaked`로 전송해야 하는 EOS를 나타냅니다. 123 | 124 | `is_free` 는 환불을 받을 수 있는지 알려줍니다 (환불이 적용되었다면 바로 진행됩니다). 125 | 126 | 현재 우리는 1개의 무료 계획(plan)을 제공하고 있습니다: `0.1 EOS`를 `60 min of 1 EOS CPU`에 스테이킹하면, 전송 후 0.1 EOS를 바로 환불받으실 수 있습니다. 127 | 128 | 다른 계획들이 빠른 시일 내에 추가될 것입니다. 129 | 130 | #### 2.EOS를 전송하고 위임받으세요! 131 | 132 | `bankofstaked`에 `0.1 EOS`를 전송하면, 스테이크된 `1 EOS`의 `cpu`를 `60 minutes`(60 분) 동안 이용하실 수 있습니다. 133 | 134 | 노트: `bankofstaked`는 시간이 되면 자동으로 토큰을 환급합니다. 135 | 136 | 137 | 138 | ``` 139 | cleos -u https://api1.eosasia.one transfer YOUR_ACCOUNT bankofstaked "0.1 EOS" -p YOUR_ACCOUNT@active 140 | ``` 141 | 142 | 0.1EOS를 전송하면, 1 EOS의 CPU를 위임받을 수 있고, 60분 후에 지연 거래를 사용하여 자동으로 환급이 진행됩니다. 143 | 토큰을 전송한 후, `1 EOS of CPU`를 위임받을 수 있고, 60분 후에, 지연 거래(deferred transaction)를 사용하여 자동으로 환급이 진행됩니다. 144 | 145 | --- 146 | 147 | 당신이 EOS 채권자 기금에 기부하고자 하는 경우 (위임, 양도는 필요하지 않음), 또는 BP와 사용자들과 개발자들을 도울 의사가 있다면 contact@eoslaomao.com으로 문의해주세요. 148 | 149 | EOSLaoMao 팀이 사랑으로 개발했습니다. :) 150 | 151 | 아이콘은 www.flaticon.com의 Freepik이 만들었습니다, 감사의 말씀 전합니다~ 152 | 153 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |
6 |

Bank of Staked

7 |

a self-serve CPU&NET Vending Machine

8 |
9 | 10 | 中文版 README 11 | 12 | 한국의 README 13 | 14 | ### About 15 | Bank of Staked is an EOS smart contract aiming to provide cheap CPU&NET lease to both EOS users and developers. This contract is build by `EOSLaoMao Team`. 16 | 17 | Bank of Staked is now live on EOS Mainnet providing 1 free emergency plan and 3 paid plans. With 20+ block producers providing free creditors, free emergency plan is now able to servce 20K EOS accounts simultaneously. 18 | 19 | Check it out: https://eoslaomao.com/bankofstaked 20 | 21 | For more details, check announcement here: https://steemit.com/eos/@eoslaomao/announcing-bankofstaked-a-self-serve-cpu-and-net-resource-vending-machine-supported-by-block-producers 22 | 23 | ### Design 24 | 25 | The user experience Bank of Staked wants to achieve is that any account could get CPU&NET delegated automatically through a simple transfer, no more action needed. And the undelegate process will also be triggered automatically. 26 | 27 | The main logic of Bank of Staked are realized through the following three tables: 28 | 29 | #### 1. Plan Table 30 | 31 | Plan table holds all available plans user can choose, main fields are: 32 | 33 | ``` 34 | price asset; //plan price 35 | cpu asset; // delegated cpu this plan offers. 36 | net asset; // delegated net this plan offers. 37 | duration unit64; //the period of time service gonna last, in minutes. 38 | is_free uint64; //free plan or not, if is_free is 1, order will be auto refunded. 39 | ... 40 | ``` 41 | 42 | #### 2. Creditor Table 43 | 44 | `Creditors` are the actual accounts who delegate and undelegate. When a valid transfer happens, the contract will try to find `active` creditor to do an auto delegation using that creditor account. 45 | 46 | ``` 47 | account account_name; 48 | is_active uint64; 49 | ... 50 | ``` 51 | 52 | `is_active` indicates if this creditor is ready to serve new orders. 53 | 54 | in production, you should always have creditors shifting like X days in a roll(X depends on plans it provide), so that non-active creditors have enough time to get their undelegated token back. 55 | 56 | #### 3. Order Table 57 | 58 | Order table consists of active order records, by active, it means these orders are not expired. 59 | 60 | ``` 61 | buyer account_name; 62 | creditor account_name; 63 | beneficiary account_name; 64 | cpu_staked asset; 65 | net_staked asset; 66 | expire_at uint64; 67 | ... 68 | ``` 69 | 70 | `buyer` is the account who did the actual transfer in the first place, if any refund happens, token will be refunded to `buyer` account also. 71 | 72 | `beneficiary` is the actual account who get cpu and net delegated. `buyer` can specify this account in transfer memo if he/she like. 73 | 74 | `creditor` is the account who did delegation. 75 | 76 | `cpu_staked` and `net_staked` is CPU&NET delegated in this order. 77 | 78 | `expire_at` is when this order will expire. After order expired, order record will be deleted from Order Table. 79 | 80 | 81 | There are also several other tables facilitating this contract. such as, 82 | 83 | `freelock` table, used to lock free plan for each account for 24 hours. 84 | 85 | `history` table, used to store meta data of deleted expired order. 86 | 87 | `blacklist`, used to blacklist certain account from using `bankofstaked` contract. 88 | 89 | 90 | ![Process](./Order-Process-of-BankofStaked.svg) 91 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | IMAGE=eoslaomao/eos-dev:1.4.0 3 | NAME=bankofstaked 4 | FOLDER=bankofstaked 5 | 6 | docker ps | grep $NAME-eos-dev 7 | if [ $? -ne 0 ]; then 8 | echo "Run eos dev env " 9 | docker run --name $NAME-eos-dev -dit --rm -v `(pwd)`:/$NAME $IMAGE 10 | fi 11 | 12 | docker exec $NAME-eos-dev eosio-cpp --contract bankofstaked \ 13 | -abigen /$NAME/src/$NAME.cpp -o $NAME.wasm \ 14 | -I /contracts/eosio.token/include \ 15 | -I /contracts/eosio.system/include \ 16 | -R /$NAME/rc/bankofstaked.ricardian.clauses.md 17 | docker exec $NAME-eos-dev cp /$NAME.abi /$NAME.wasm /$NAME/ 18 | 19 | if [ -d build ]; then 20 | rm -rf build 21 | fi 22 | 23 | mkdir build 24 | echo "Create build dir!!" 25 | 26 | mv $NAME.abi ./build 27 | mv $NAME.wasm ./build 28 | echo "Build SUCCESS!!!" 29 | 30 | # For test and debug 31 | docker exec nodeosd mkdir /$NAME 32 | docker cp ./build/$NAME.abi nodeosd:/$NAME/ 33 | docker cp ./build/$NAME.wasm nodeosd:/$NAME/ 34 | docker cp scripts nodeosd:/ 35 | 36 | #run unit test 37 | docker exec $NAME-eos-dev /bankofstaked/unittest.sh 38 | -------------------------------------------------------------------------------- /cpu-emergency/Centralized-CPU-Emergency.svg: -------------------------------------------------------------------------------- 1 | CentralizedServerUsers run out of CPUCPU run out?ENDCall BankofStakedUser gotfree CPUENDAVOID SPAMMINGDetermine CPU of submitted account ran out or notYESuse account in WHITELIST to claim free plan using BankofStaked for qualified account.0.8 EOS staked for CPU0.2 EOS staked for NETfor 8 hoursCentralized CPU emergency using Bank of Staked NO -------------------------------------------------------------------------------- /cpu-emergency/README.md: -------------------------------------------------------------------------------- 1 | # CPU Emergency Document 2 | 3 | BankofStaked provides a mechanism called `whitelist` to enable centralized CPU emergency service. MEET.ONE and EOS.LIVE wallet have already developed CPU emergency services using this feature. 4 | 5 | This document is a guide to develop a centralized CPU emergency service for wallets/tools. 6 | 7 | # How it works 8 | 9 | Here is a diagram showing how it works: 10 | 11 | ![Process](./Centralized-CPU-Emergency.svg) 12 | 13 | The grey part of the diagram should be provided by wallets/tools who wants to provide centralized CPU emergency service. 14 | 15 | details of the requirements are: 16 | 17 | 18 | 1. a frontend for users in your wallet/tool for users to submit accounts. 19 | 2. an API to submit account with proper anti-spamming setup. 20 | 3. an EOS account provided to `bankofstaked` which will be added to `whitelist` with a proper `capacity`. you can reach out to contact@eoslaomao.com for details. 21 | 22 | for part one, here are frontend examples provided by MEET.ONE and EOS.LIVE: 23 | 24 | ![MEET.ONE](./meetone.jpeg) 25 | 26 | ![EOS.LIVE](./eoslive.png) 27 | -------------------------------------------------------------------------------- /cpu-emergency/eoslive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EOSLaoMao/BankofStaked-CE/3b764debe9bad8047c0cf28930b91b7a736a09d6/cpu-emergency/eoslive.png -------------------------------------------------------------------------------- /cpu-emergency/meetone.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EOSLaoMao/BankofStaked-CE/3b764debe9bad8047c0cf28930b91b7a736a09d6/cpu-emergency/meetone.jpeg -------------------------------------------------------------------------------- /doc/BankofStaked-Creditor-Resource-Guide.md: -------------------------------------------------------------------------------- 1 | # BankofStaked Creditor Resource Guide 2 | 3 | 4 | 5 | ## RAM 6 | 7 | ### free creditor 8 | 9 | ` 10 | RAM = (liquid_balance) * 0.16 (kb) 11 | ` 12 | 13 | eg. creditor account with 1000 liquid EOS should also have 160kb unused RAM in this account initially. 14 | 15 | 16 | ### paid creditor 17 | 18 | ` 19 | RAM = (liquid_balance) * 0.12 / 30 (kb) 20 | ` 21 | 22 | *30 is the minimum EOS quanity for each paid order. 23 | 24 | eg. creditor account with 10000 liquid EOS should also have 40kb unused RAM in this account initially. 25 | 26 | 27 | # CPU 28 | 29 | This is a tricky one. You have to make sure your creditor account has enough CPU for auto-undelegation, otherwise auto-undelegation will not be executed. Good news is BankofStaked has a built-in passive check to make sure to trigger undelegation again and again. 30 | 31 | 32 | 33 | # Current creditor resource status 34 | 35 | Here is the creditor RAM resource summary, please purchase enough ram for your account according to this chart if your account has no enough RAM. 36 | 37 | 38 | ## FREE CREDITORS 39 | 40 | | Account | Balance | RAM Owned(kb) | RAM Requird(kb) | Enough RAM? | 41 | | ------- | ------- | --------- | ----------- | ----------- | 42 | | acroeosrndev | 500.0023 EOS | 42.54 | 80.00 | ❌ | 43 | | bankofeosys2 | 300.1019 EOS | 4.83 | 48.02 | ❌ | 44 | | bankofstkarg | 500.0024 EOS | 86.75 | 80.00 | ✅ | 45 | | bosauthority | 89.0118 EOS | 11.03 | 14.24 | ❌ | 46 | | cannonstaked | 200.0020 EOS | 32.30 | 32.00 | ✅ | 47 | | charity.bank | 10100.0026 EOS | 200.82 | 1616.00 | ❌ | 48 | | dublinstaked | 506.0017 EOS | 79.02 | 80.96 | ❌ | 49 | | eos42reserve | 8520.0052 EOS | 7.96 | 1363.20 | ❌ | 50 | | eosasia.bp | 498.0027 EOS | 88.40 | 79.68 | ✅ | 51 | | eosbeijinghp | 100.0028 EOS | 58.34 | 16.00 | ✅ | 52 | | eosbixinbank | 500.0025 EOS | 89.23 | 80.00 | ✅ | 53 | | eoscafestake | 994.8943 EOS | 168.52 | 159.18 | ✅ | 54 | | eoseco.bp | 951.0023 EOS | 635.15 | 152.16 | ✅ | 55 | | eosgravitygo | 100.0024 EOS | 9.32 | 16.00 | ❌ | 56 | | eospacestake | 100.2025 EOS | 16.90 | 16.03 | ✅ | 57 | | eosriobrfree | 405.0016 EOS | 7.51 | 64.80 | ❌ | 58 | | eostribefree | 102.0017 EOS | 11.32 | 16.32 | ❌ | 59 | | freestakeswe | 1004.0016 EOS | 319.76 | 160.64 | ✅ | 60 | | fundstostake | 12.9024 EOS | 71.78 | 2.06 | ✅ | 61 | | jedaaastaked | 367.3101 EOS | 84.05 | 58.77 | ✅ | 62 | | meetone1free | 500.0010 EOS | 103.89 | 80.00 | ✅ | 63 | 64 | 65 | ## PAID CREDITORS 66 | | Account | Balance | RAM Owned(kb) | RAM Requird(kb) | Enough RAM? | 67 | | ------- | ------- | --------- | ----------- | ----------- | 68 | | bankofeosys1 | 1026.2817 EOS | 10.53 | 4.11 | ✅ | 69 | | cyberneticsx | 4470.5650 EOS | 24.30 | 17.88 | ✅ | 70 | | dublinsaving | 20000.9284 EOS | 4865.03 | 80.00 | ✅ | 71 | | eosbeijingbk | 1062.1522 EOS | 13.17 | 4.25 | ✅ | 72 | | eosriostaked | 14159.9581 EOS | 10.20 | 56.64 | ❌ | 73 | | eostribecred | 1014.1014 EOS | 11.32 | 4.06 | ✅ | 74 | | jedacreditor | 40021.5302 EOS | 113.82 | 160.09 | ❌ | 75 | | paidstakeswe | 13161.7804 EOS | 319.76 | 52.65 | ✅ | 76 | | serenityhome | 13247.9882 EOS | 108.11 | 52.99 | ✅ | 77 | | staking.bank | 34551.5327 EOS | 173.50 | 138.21 | ✅ | 78 | | stakingfunda | 19195.5208 EOS | 129.28 | 76.78 | ✅ | 79 | | stakingfundb | 10195.8706 EOS | 119.57 | 40.78 | ✅ | 80 | | stakingfundc | 10167.2706 EOS | 119.57 | 40.67 | ✅ | 81 | | stakingfundd | 30172.1106 EOS | 119.57 | 120.69 | ❌ | 82 | | stakingfunde | 10189.1007 EOS | 119.57 | 40.76 | ✅ | 83 | | stakingfundf | 20432.8848 EOS | 119.57 | 81.73 | ✅ | 84 | | stakingfundg | 10248.2183 EOS | 119.57 | 40.99 | ✅ | 85 | | stakingfundh | 10174.4407 EOS | 119.57 | 40.70 | ✅ | 86 | | stakingfundi | 17153.0407 EOS | 119.57 | 68.61 | ✅ | 87 | | sxzzsxzzsxz1 | 151002.7887 EOS | 116.50 | 604.01 | ❌ | 88 | | sxzzsxzzsxzz | 101917.5803 EOS | 110.92 | 407.67 | ❌ | 89 | -------------------------------------------------------------------------------- /doc/BankofStaked-Creditor-Term-CN.md: -------------------------------------------------------------------------------- 1 | # BankofStaked Creditor 公开计划 2 | 3 | ## 关于 BankofStaked 4 | 5 | BankofStaked 是由 EOSLaoMao 团队开发并维护的一款 EOS 资源自助租赁智能合约([https://eoslaomao.com/bankofstaked](https://eoslaomao.com/bankofstaked))。 6 | 该合约巧妙地利用 EOS 权限系统,实现了无需转币,零资金风险,且不影响投票权的自动租赁功能。 7 | 8 | BankofStaked 上线两个月以来(2018 年 10 月正式上线),已经累计自动完成了 40,000 多笔订单,累计自动贷出 3,000,000 EOS。同时,BankofStaked 还联合了 EOS 社区的 27 个 BP 向用户提供免费的 CPU 救急计划。截至目前,BankofStaked 已经给超过 30,000 个 EOS 账户提供了免费救急服务。MEET.ONE, imToken, EOS.LIVE 等钱包工具也已经接入该功能。 9 | 10 | ### Creditor 开放计划 11 | 12 | 随着 EOS 生态的发展,BankofStaked 的业务量迅速增长,BankofStaked 目前的存量 EOS 已经无法满足日益增长的业务需求。为此我们现将 BankofStaked 的 Creditor 功能向 EOS 社区限量开放。 13 | 14 | 第一期开放额度为 100 万 EOS,满额即止。由于 Creditor 账户的设置需要一定的 cleos 命令行工具基础,请务必评估风险后,自行决定是否参与。由于自己操作或设置不当导致的问题,BankofStaked 概不负责。 15 | 16 | Creditor 定义: 经 BankofStaked 团队审核通过,加入到 BankofStaked 出租池,通过 BankofStaked 智能合约自动出租 EOS 获取收益的 EOS 账户。 17 | 18 | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) 友情提醒:BankofStaked 不会以任何名义让 Creditor 向我方进行转账,请知悉。 19 | 20 | ### Creditor 要求 21 | 22 | 1. 有一个独立的 EOS 账户用做 Creditor,且账户余额大于 20,000 EOS。 23 | 2. 该 Creditor 账户需部署 BankofStaked 提供的安全抵押合约(详情参见:[SafeDelegatebw](https://github.com/EOSLaoMao/SafeDelegatebw))。 24 | 3. 该 Creditor 账户需授权安全抵押合约的抵押和解抵押权限给 BankofStaked(详情参见:[SafeDelegatebw](https://github.com/EOSLaoMao/SafeDelegatebw))。 25 | 4. 该 Creditor 账户需确保账户自身具有足够的 RAM/CPU/NET 资源(详情参见:[Creditor 资源指引](./BankofStaked-Creditor-Resource-Guide.md))。 26 | 27 | ### Creditor 义务 28 | 29 | 1. 加入时,需满足上述 Creditor 要求,且需要通过 BankofStaked 团队的审核。 30 | 2. 在提供服务期间,Creditor 不可以私自将正在出租的 EOS 提前解除抵押。 31 | 3. 在提供服务期间,Creditor 不可以私自将抵押和解抵押权限从 BankofStaked 智能合约撤除。 32 | 4. 退出 Creditor 需提前申请,并需等待正在出租的 EOS 到期自动解除抵押之后,方可退出。 33 | 34 | ### Creditor 权利 35 | 36 | 1. 在遵守上述 Creditor 义务的前提下,享有 EOS 租金收益的 90%。租金收益会在每笔订单到期之后自动分配至 Cerditor 账户。 37 | 2. 监督 BankofStaked 团队的开发进度。 38 | 39 | ### BankofStaked 权利 40 | 41 | 1. 享有出租方案的定价权。 42 | 2. 享有将违反规则的 Creditor 踢除的权利。 43 | 44 | ### BankofStaked 义务 45 | 46 | 1. 维护 BankofStaked 项目的发展。 47 | 2. 开发 BankofStaked 新功能,修复可能的问题。 48 | 49 | 其他未尽事宜,以 [BankofStaked Ricardian Contract](../rc/bankofstaked-ricardian-clauses.md) 为准。 50 | 51 | 有意参与者,请加入 Telegram 群聊:[https://t.me/BOSCreditor](https://t.me/BOSCreditor) 52 | 53 | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) 友情提醒:BankofStaked 不会以任何名义让 Creditor 向我方进行转账,请知悉。 54 | -------------------------------------------------------------------------------- /doc/BankofStaked-Creditor-Term-EN.md: -------------------------------------------------------------------------------- 1 | # BankofStaked Open Creditor Program 2 | 3 | ## About BankofStaked 4 | 5 | BankofStaked is an EOS resource automatic leasing DAPP developed by EOSLaoMao team ([https://eoslaomao.com/bankofstaked](https://eoslaomao.com/bankofstaked)). 6 | 7 | BankofStaked has been online for more than two months since it was launched in October 2018, and has leased out more than 3,000,000 EOS automatically. At the same time, BankofStaked has been providing free emergency plan to the EOS community from day one with supports from 27 Block Producers. To date, BankofStaked has provided free emergency services to more than 30,000 EOS accounts. Wallets such as MEET.ONE, imToken, EOS.LIVE have also integrited free emergency plan into their wallets. 8 | 9 | BankofStaked has acheived a ZERO-fund-loss-risk while leasing creditors' EOS out by using EOS permission system cleverly. 10 | 11 | Comparing to other platforms, the benefits of being creditor of BankofStaked are as follows: 12 | 13 | 1. NO EOS transfer needed. Your EOS sits in your account, under your control all the time, which means there's ZERO risk of fund loss. 14 | 2. ALL airdrops will be recieved automatically, again, because your EOS sits in your account. 15 | 3. Voting right is under your control, too! As a DPOS blockchain, the importance of voting right is self-evident in EOS. You should always control your voting right fully. 16 | 17 | 18 | ## Open Creditor Program 19 | With the development of the EOS ecosystem, BankofStaked's business volume has grown rapidly, and BankofStaked's current creditors have been unable to meet the growing business needs. To this end, we now open a limited amount of BankofStaked's Creditor to qualified EOS token holders. 20 | 21 | Since setting up the Creditor account requires a certain knowledge of cleos command line tool, it is important for token holders to decide whether or not to participate after evaluating the risk. BankofStaked is not responsible for problems caused by improper operation or improper settings operated by token holders themselves. 22 | 23 | ### Creditor Requirements 24 | 1. An independent EOS account for creditor use and the account balance should be greater than 20,000 EOS. 25 | 2. The creditor account is required to deploy SafeDelegatebw smart contract provided by BankofStaked ([SafeDelegatebw](https://github.com/EOSLaoMao/SafeDelegatebw)). 26 | 3. The creditor account is required to authorize the delegate/undelagete permission to BankofStaked ([SafeDelegatebw](https://github.com/EOSLaoMao/SafeDelegatebw)). 27 | 4. The creditor account needs to ensure that the account itself has sufficient RAM/CPU/NET resources ([Creditor Resource Guide](./BankofStaked-Creditor-Resource-Guide.md)). 28 | 29 | ### Creditor Obligation 30 | 1. When you join, you need to meet the above creditor requirements and need to be reviewed and approved by the BankofStaked team. 31 | 2. During the service period, creditor may not arbitrarily revoke the EOS being leased to others in advance. 32 | 3. During the service period, creditor may not revoke the delegate/undelegate permission from the BankofStaked Smart Contract. 33 | 4. To quit being a creditor, you must apply in advance and wait for the EOS that is being leased to expire automatically before you can get a fully quit. 34 | 35 | ### Creditor Rights 36 | 1. Subject to the above Creditor obligations, creditor will get 90% of EOS lease income that happened in their account. Lease income is automatically allocated to the Cerditor account after each order expires. 37 | 2. Oversee the development of the BankofStaked team. 38 | 39 | ### BankofStaked Rights 40 | 1. Full control of the leasing price. 41 | 2. Have the right to kick off Creditor who violates the rules. 42 | 43 | ### BankofStaked Obligation 44 | 1. Maintainance and future development of the BankofStaked project. 45 | 2. Develop new BankofStaked features. 46 | 3. Fix possible bug and vulnarabilities. 47 | 4. Any other matters are subject to the [BankofStaked Ricardian Contract](../rc/bankofstaked-ricardian-clauses.md). 48 | 49 | Interested? Feel free to join the Telegram group chat: [https://t.me/BOSCreditor](https://t.me/BOSCreditor) 50 | 51 | ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) Friendly reminder: BankofStaked will not ask any creditor to transfer any token to us, please be aware. 52 | -------------------------------------------------------------------------------- /include/bankofstaked/bankofstaked.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file bankofstaked.hpp 3 | */ 4 | #include 5 | #include 6 | #include 7 | 8 | #define EOS_SYMBOL symbol("EOS", 4) 9 | 10 | using namespace eosio; 11 | 12 | namespace bank 13 | { 14 | static const name CODE_ACCOUNT = "bankofstaked"_n; 15 | static const name RAM_PAYER = "bankofstaked"_n; 16 | static const name MASK_TRANSFER = "masktransfer"_n; 17 | static const name STAKED_INCOME = "stakedincome"_n; 18 | static const name EOSIO = "eosio"_n; 19 | static const uint64_t FREE_PLAN_AMOUNT = 1000; 20 | static const uint64_t SECONDS_PER_MIN = 60; 21 | static const uint64_t SECONDS_PER_DAY = 24 * 3600; 22 | static const uint64_t MAX_FREE_ORDERS = 5; 23 | static const uint64_t MAX_PAID_ORDERS = 20; 24 | static const uint64_t TRUE = 1; 25 | static const uint64_t FALSE = 0; 26 | static const uint64_t CHECK_MAX_DEPTH = 3; 27 | static const uint64_t MAX_EOS_BALANCE = 500 * 10000; // 500 EOS at most 28 | static const uint64_t MIN_FREE_CREDITOR_BALANCE = 10 * 10000; // 10 EOS at least 29 | static const uint64_t DEFAULT_DIVIDEND_PERCENTAGE = 90; // 90% income will be allocated to creditor 30 | 31 | // To protect your table, you can specify different scope as random numbers 32 | static const uint64_t SCOPE = 921459758687; 33 | 34 | struct [[eosio::table, eosio::contract("bankofstaked")]] freelock 35 | { 36 | name beneficiary; // account who received CPU&NET 37 | uint64_t created_at; // unix time, in seconds 38 | uint64_t expire_at; // unix time, in seconds 39 | 40 | uint64_t primary_key() const { return beneficiary.value; } 41 | uint64_t get_expire_at() const { return expire_at; } 42 | 43 | EOSLIB_SERIALIZE(freelock, (beneficiary)(created_at)(expire_at)); 44 | }; 45 | 46 | typedef multi_index<"freelock"_n, freelock, 47 | indexed_by<"expire.at"_n, const_mem_fun>> 48 | freelock_table; 49 | 50 | struct [[eosio::table, eosio::contract("bankofstaked")]] order 51 | { 52 | uint64_t id; 53 | name buyer; 54 | asset price{0, EOS_SYMBOL}; // amount of EOS paied 55 | uint64_t is_free; // default is FALSE, for free plan, when service expired, it will do a auto refund 56 | name creditor; // account who delegated CPU&NET 57 | name beneficiary; // account who received CPU&NET 58 | uint64_t plan_id; // foreignkey of table plan 59 | asset cpu_staked{0, EOS_SYMBOL}; // amount of EOS staked for cpu 60 | asset net_staked{0, EOS_SYMBOL}; // amount of EOS staked for net 61 | uint64_t created_at; // unix time, in seconds 62 | uint64_t expire_at; // unix time, in seconds 63 | 64 | auto primary_key() const { return id; } 65 | uint64_t get_buyer() const { return buyer.value; } 66 | uint64_t get_beneficiary() const { return beneficiary.value; } 67 | uint64_t get_expire_at() const { return expire_at; } 68 | 69 | EOSLIB_SERIALIZE(order, (id)(buyer)(price)(is_free)(creditor)(beneficiary)(plan_id)(cpu_staked)(net_staked)(created_at)(expire_at)); 70 | }; 71 | 72 | typedef multi_index<"order"_n, order, 73 | indexed_by<"buyer"_n, const_mem_fun>, 74 | indexed_by<"expire.at"_n, const_mem_fun>, 75 | indexed_by<"beneficiary"_n, const_mem_fun>> 76 | order_table; 77 | 78 | struct [[eosio::table, eosio::contract("bankofstaked")]] history 79 | { 80 | uint64_t id; 81 | string content; // content 82 | uint64_t created_at; // unix time, in seconds 83 | 84 | auto primary_key() const { return id; } 85 | EOSLIB_SERIALIZE(history, (id)(content)(created_at)); 86 | }; 87 | typedef multi_index<"history"_n, history> history_table; 88 | 89 | struct [[eosio::table, eosio::contract("bankofstaked")]] plan 90 | { 91 | uint64_t id; 92 | asset price{0, EOS_SYMBOL}; // amount of EOS paied 93 | asset cpu{0, EOS_SYMBOL}; // amount of EOS staked for cpu 94 | asset net{0, EOS_SYMBOL}; // amount of EOS staked for net 95 | uint64_t duration; // affective time, in minutes 96 | uint64_t is_free; // default is FALSE, for free plan, when service expired, it will do a auto refund 97 | uint64_t is_active; // on active plan could be choosen 98 | uint64_t created_at; // unix time, in seconds 99 | uint64_t updated_at; // unix time, in seconds 100 | 101 | auto primary_key() const { return id; } 102 | uint64_t get_price() const { return (uint64_t)price.amount; } 103 | EOSLIB_SERIALIZE(plan, (id)(price)(cpu)(net)(duration)(is_free)(is_active)(created_at)(updated_at)); 104 | }; 105 | typedef multi_index<"plan"_n, plan, 106 | indexed_by<"price"_n, const_mem_fun>> 107 | plan_table; 108 | 109 | struct [[eosio::table, eosio::contract("bankofstaked")]] safecreditor 110 | { 111 | name account; 112 | uint64_t created_at; // unix time, in seconds 113 | uint64_t updated_at; // unix time, in seconds 114 | 115 | uint64_t primary_key() const { return account.value; } 116 | 117 | EOSLIB_SERIALIZE(safecreditor, (account)(created_at)(updated_at)); 118 | }; 119 | typedef multi_index<"safecreditor"_n, safecreditor> safecreditor_table; 120 | 121 | struct [[eosio::table, eosio::contract("bankofstaked")]] dividend 122 | { 123 | name account; 124 | uint64_t percentage; // percentage of income allocating to creditor 125 | 126 | uint64_t primary_key() const { return account.value; } 127 | 128 | EOSLIB_SERIALIZE(dividend, (account)(percentage)); 129 | }; 130 | typedef multi_index<"dividend"_n, dividend> dividend_table; 131 | 132 | struct [[eosio::table, eosio::contract("bankofstaked")]] creditor 133 | { 134 | name account; 135 | uint64_t is_active; 136 | uint64_t for_free; // default is FALSE, for_free means if this creditor provide free staking or not 137 | string free_memo; // memo for refund transaction 138 | asset balance{0, EOS_SYMBOL}; // amount of EOS paied 139 | asset cpu_staked{0, EOS_SYMBOL}; // amount of EOS paied 140 | asset net_staked{0, EOS_SYMBOL}; // amount of EOS paied 141 | asset cpu_unstaked{0, EOS_SYMBOL}; // amount of EOS paied 142 | asset net_unstaked{0, EOS_SYMBOL}; // amount of EOS paied 143 | uint64_t created_at; // unix time, in seconds 144 | uint64_t updated_at; // unix time, in seconds 145 | 146 | uint64_t primary_key() const { return account.value; } 147 | uint64_t get_is_active() const { return is_active; } 148 | uint64_t get_updated_at() const { return updated_at; } 149 | 150 | EOSLIB_SERIALIZE(creditor, (account)(is_active)(for_free)(free_memo)(balance)(cpu_staked)(net_staked)(cpu_unstaked)(net_unstaked)(created_at)(updated_at)); 151 | }; 152 | 153 | typedef multi_index<"creditor"_n, creditor, 154 | indexed_by<"is.active"_n, const_mem_fun>, 155 | indexed_by<"updated.at"_n, const_mem_fun>> 156 | creditor_table; 157 | 158 | struct [[eosio::table, eosio::contract("bankofstaked")]] blacklist 159 | { 160 | name account; 161 | uint64_t created_at; // unix time, in seconds 162 | 163 | uint64_t primary_key() const { return account.value; } 164 | EOSLIB_SERIALIZE(blacklist, (account)(created_at)); 165 | }; 166 | typedef multi_index<"blacklist"_n, blacklist> blacklist_table; 167 | 168 | struct [[eosio::table, eosio::contract("bankofstaked")]] whitelist 169 | { 170 | name account; 171 | uint64_t capacity; // max in-use free orders 172 | uint64_t updated_at; // unix time, in seconds 173 | uint64_t created_at; // unix time, in seconds 174 | 175 | uint64_t primary_key() const { return account.value; } 176 | EOSLIB_SERIALIZE(whitelist, (account)(capacity)(updated_at)(created_at)); 177 | }; 178 | typedef multi_index<"whitelist"_n, whitelist> whitelist_table; 179 | 180 | }// namespace bank 181 | 182 | struct [[eosio::table, eosio::contract("bankofstaked")]] recipient 183 | { 184 | name creditor; 185 | name recipient_account; 186 | uint64_t created_at; // unix time, in seconds 187 | uint64_t updated_at; // unix time, in seconds 188 | 189 | uint64_t primary_key() const { return creditor.value; } 190 | 191 | EOSLIB_SERIALIZE(recipient, (creditor)(recipient_account)(created_at)(updated_at)); 192 | }; 193 | typedef multi_index<"recipient"_n, recipient> recipient_table; 194 | -------------------------------------------------------------------------------- /rc/bankofstaked.ricardian.clauses.md: -------------------------------------------------------------------------------- 1 | # Ricardian Clauses for **BankofStaked** 2 | 3 | ## Preamble 4 | 5 | EOS is a wonderful tool for the world to use in commercial applications; there are however some complications that may arise from its resource allocation and planning. 6 | 7 | BankofStaked aims to help by providing an automated service for resource allocation within EOS in exchange for fixed costs over a predefined duration. 8 | 9 | ## Executive Summary 10 | 11 | BankofStaked provides the workflow to arrange Creditor to temporarily delegate resources to Beneficiary in exchange for a Fee. 12 | 13 | ## Language and Interpretation 14 | 15 | The operative language is English. 16 | 17 | ## Definitions 18 | 19 | * Beneficiary: the person who is recipient of delegation. 20 | * Chain: the chain with the ID `aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906`. 21 | * Creditor: the person who makes resources available for delegation. 22 | * Delegation: the assignment of resources from Creditor to Beneficiary. 23 | * Fee: tokens transfered into BankofStaked that exactly correspond to a fee schedule amount. 24 | * Fee Schedule: the list of services provided with their corresponding amount, which can be fetched from pricing table of BankofStaked smart contract. 25 | 26 | ## Roles 27 | 28 | This smart contract defines the following roles: 29 | 1. Code: the account that manages this contract 30 | 1. Creditor: any account that grants Code delegate/undelegate permission on some of its token 31 | 1. Beneficiary: any account that pays fee in order to receive delegation 32 | 33 | ## Scope 34 | 35 | This contract applies to Chain, with the ID as defined above. Each chain may have its own operative version of this contract. Each chain's contract is severable and independent. 36 | 37 | ## Acceptance 38 | 39 | Any party making use or taking benefit from this contract is considered to agree to the terms set forth herein, as per their role as defined in Roles section. 40 | 41 | ## Role: Code 42 | 43 | ### Obligations 44 | 45 | On a best effort basis, Code aims to: 46 | 1. maintain this contract's operational status. 47 | 1. grant delegation to beneficiary resources as per Fee Schedule. 48 | 1. return funds transfered to this contract which does not match Fee Schedule. 49 | 1. release delegation upon expiry. 50 | 1. to distribute 90% of the fee to Creditor whose token was invovled in a delegation. 51 | 52 | ### Rights 53 | 54 | Code reserves the right to: 55 | 1. update the terms of Fee Schedule without notice nor justification. 56 | 1. revoke delegation to any beneficiary for violation of terms set forth herein. 57 | 1. decline service to any beneficiary without notice nor justification. 58 | 59 | ## Role: Creditor 60 | 61 | ### Obligations 62 | 63 | 1. Creditor agrees to refrain from revoking delegate/undelegate permission without submitting Code 3 days' prior notice, as per Communications Protocols defined herein. 64 | 1. Failure to provide proper notice before revoking delegate/undelegate permission, resulting in interruption of service for Beneficiary, Creditor forfeits his right to proceeds from the fees. 65 | 1. All communications intended for Code are to be sent as per Communications Protocol prescribed below. 66 | 67 | ### Rights 68 | 69 | 1. Creditor has a right to 90% of proceeds from the fees accrued by a delegation at the time of release. 70 | 71 | ## Role: Beneficiary 72 | 73 | ### Obligations 74 | 75 | 1. To refrain from submitting an unreasonable amount of requests. 76 | 1. All communications intended for Code are to be sent as per Communications Protocol prescribed below. 77 | 78 | ### Rights 79 | 80 | 1. For duly submitted fees resulting in a delegation order, Beneficiary may have an expectation of maintenance of delegation. 81 | 1. Beneficiaries who suffer an interruption of delegation may claim either: (i) a refund, or (ii) credit toward a delegation of equal value. 82 | 83 | ## Communications Protocol 84 | 85 | All notices under this contract intended for Code, must be sent by email to the following address: `bos@eoslaomao.com` 86 | 87 | ## Force Majeure 88 | 89 | In any event outside of BankofStaked's ability to control or influence, BankofStaked is not liable for any failure to perform as a result of said event. 90 | 91 | This may include--without limitation: 92 | * an interruption of service of the EOS Chain; 93 | * hacking/compromise of BankofStaked or Code account; 94 | * natural disater or act of God. 95 | -------------------------------------------------------------------------------- /scripts/add_creditor.sh: -------------------------------------------------------------------------------- 1 | API=${1:-http://localhost:8888} 2 | cleos -u $API push action bankofstaked addcreditor '{"account": "charity.bank", "for_free": 1, "free_memo": "A gift from EOSLaoMao team"}' -p bankofstaked 3 | cleos -u $API push action bankofstaked addcreditor '{"account": "staking.bank", "for_free": 0, "free_memo": ""}' -p bankofstaked 4 | cleos -u $API push action bankofstaked addcreditor '{"account": "eosbeijinghp", "for_free": 1, "free_memo": "gift from EOS Beijing, EOS Navigation: https://www.shensi.com"}' -p bankofstaked 5 | cleos -u $API push action bankofstaked addcreditor '{"account": "eosbeijingbk", "for_free": 0, "free_memo": ""}' -p bankofstaked 6 | cleos -u $API push action bankofstaked addcreditor '{"account": "meetone1free", "for_free": 1, "free_memo": "A gift from MEET.ONE team"}' -p bankofstaked 7 | cleos -u $API push action bankofstaked addcreditor '{"account": "eosbixinbank", "for_free": 1, "free_memo": "A gift from EOSBIXIN team"}' -p bankofstaked 8 | cleos -u $API push action bankofstaked addcreditor '{"account": "jedaaastaked", "for_free": 1, "free_memo": "A gift from JEDA team with love"}' -p bankofstaked 9 | cleos -u $API push action bankofstaked addcreditor '{"account": "cannonstaked", "for_free": 1, "free_memo": "A gift from EOSCannon team"}' -p bankofstaked 10 | cleos -u $API push action bankofstaked addcreditor '{"account": "acroeosrndev", "for_free": 1, "free_memo": "A gift from AcroEOS"}' -p bankofstaked 11 | cleos -u $API push action bankofstaked addcreditor '{"account": "bankofeosys2", "for_free": 1, "free_memo": "A gift from EOSYS"}' -p bankofstaked 12 | cleos -u $API push action bankofstaked addcreditor '{"account": "bankofeosys1", "for_free": 0, "free_memo": ""}' -p bankofstaked 13 | cleos -u $API push action bankofstaked addcreditor '{"account": "eosasia.bp", "for_free": 1, "free_memo": "A gift from block producer eosasia11111"}' -p bankofstaked 14 | cleos -u $API push action bankofstaked addcreditor '{"account": "eoseco.bp", "for_free": 1, "free_memo": "A gift from EOSeco"}' -p bankofstaked 15 | cleos -u $API push action bankofstaked addcreditor '{"account": "eospacestake", "for_free": 1, "free_memo": "A gift from EOSpace"}' -p bankofstaked 16 | cleos -u $API push action bankofstaked addcreditor '{"account": "bankofstkarg", "for_free": 1, "free_memo": "A gift from EOS Argentina"}' -p bankofstaked 17 | cleos -u $API push action bankofstaked addcreditor '{"account": "eoscafestake", "for_free": 1, "free_memo": "A gift from EOS Cafe Block"}' -p bankofstaked 18 | cleos -u $API push action bankofstaked addcreditor '{"account": "eosgravitygo", "for_free": 1, "free_memo": "A gift from EOS Gravity"}' -p bankofstaked 19 | cleos -u $API push action bankofstaked addcreditor '{"account": "eosriobrfree", "for_free": 1, "free_memo": "A gift from EOS Rio"}' -p bankofstaked 20 | cleos -u $API push action bankofstaked addcreditor '{"account": "eosriostaked", "for_free": 0, "free_memo": ""}' -p bankofstaked 21 | cleos -u $API push action bankofstaked addcreditor '{"account": "bosauthority", "for_free": 1, "free_memo": "A gift from EOS Authority"}' -p bankofstaked 22 | cleos -u $API push action bankofstaked addcreditor '{"account": "eostribefree", "for_free": 1, "free_memo": "A gift from EOS Tribe"}' -p bankofstaked 23 | cleos -u $API push action bankofstaked addcreditor '{"account": "eostribecred", "for_free": 0, "free_memo": ""}' -p bankofstaked 24 | cleos -u $API push action bankofstaked addcreditor '{"account": "eospacificbs", "for_free": 1, "free_memo": "A gift from EOS Pacific"}' -p bankofstaked 25 | cleos -u $API push action bankofstaked addcreditor '{"account": "eos42reserve", "for_free": 1, "free_memo": "A gift from EOS42"}' -p bankofstaked 26 | #cleos -u $API push action bankofstaked activate '{"account": "charity.bank"}' -p bankofstaked 27 | #cleos -u $API push action bankofstaked activate '{"account": "staking.bank"}' -p bankofstaked 28 | #cleos -u $API push action bankofstaked activate '{"account": "eosbeijinghp"}' -p bankofstaked 29 | #cleos -u $API push action bankofstaked activate '{"account": "eosriostaked"}' -p bankofstaked 30 | -------------------------------------------------------------------------------- /scripts/bank_perm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ACCOUNT=$1 4 | PKEY=${2:-EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV} 5 | PERM_ACCOUNT=${3:-bankofstaked} 6 | PERMISSION=${4:-eosio.code} 7 | API=${5:-http://localhost:8888} 8 | 9 | cleos set account permission $ACCOUNT bankperm '{"threshold": 1,"keys": [{"key": "'$PKEY'","weight": 1}],"accounts": [{"permission":{"actor":"'$PERM_ACCOUNT'","permission":"'$PERMISSION'"},"weight":1}]}' "active" -p $ACCOUNT@active 10 | cleos set action permission $ACCOUNT eosio.token transfer bankperm -p $ACCOUNT@active 11 | cleos set action permission $ACCOUNT eosio delegatebw bankperm -p $ACCOUNT@active 12 | cleos set action permission $ACCOUNT eosio undelegatebw bankperm -p $ACCOUNT@active 13 | cleos set action permission $ACCOUNT bankofstaked expireorder bankperm -p $ACCOUNT@active 14 | cleos set action permission $ACCOUNT bankofstaked check bankperm -p $ACCOUNT@active 15 | cleos set action permission $ACCOUNT bankofstaked rotate bankperm -p $ACCOUNT@active 16 | -------------------------------------------------------------------------------- /scripts/creditor_perm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ACCOUNT=$1 4 | PKEY=${2:-EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV} 5 | SAFE=${3:-false} 6 | API=${4:-http://localhost:8888} 7 | PERM_ACCOUNT=${5:-bankofstaked} 8 | PERMISSION=${6:-eosio.code} 9 | 10 | cleos set account permission $ACCOUNT creditorperm '{"threshold": 1,"keys": [{"key": "'$PKEY'","weight": 1}],"accounts": [{"permission":{"actor":"'$PERM_ACCOUNT'","permission":"'$PERMISSION'"},"weight":1}]}' "active" -p $ACCOUNT@active 11 | if [ "$SAFE" = true ] ; then 12 | cleos set action permission $ACCOUNT $ACCOUNT delegatebw creditorperm -p $ACCOUNT@active 13 | else 14 | cleos set action permission $ACCOUNT eosio delegatebw creditorperm -p $ACCOUNT@active 15 | fi 16 | cleos set action permission $ACCOUNT eosio undelegatebw creditorperm -p $ACCOUNT@active 17 | -------------------------------------------------------------------------------- /scripts/dev.sh: -------------------------------------------------------------------------------- 1 | ./scripts/deploy.sh 2 | ./scripts/set_plan.sh 3 | API=${1:-http://localhost:8888} 4 | cleos -u $API push action bankofstaked addcreditor '{"account": "voter1", "for_free": 1, "free_memo": "A gift from EOSLaoMao team"}' -p bankofstaked 5 | cleos -u $API push action bankofstaked addcreditor '{"account": "voter2", "for_free": 0, "free_memo": ""}' -p bankofstaked 6 | cleos -u $API push action bankofstaked addcreditor '{"account": "freestaking1", "for_free": 1, "free_memo": "A gift from EOSLaoMao team"}' -p bankofstaked 7 | cleos -u $API push action bankofstaked addcreditor '{"account": "fundstaking1", "for_free": 0, "free_memo": ""}' -p bankofstaked 8 | cleos -u $API push action bankofstaked addcreditor '{"account": "freestaking2", "for_free": 1, "free_memo": "A gift from EOSLaoMao team"}' -p bankofstaked 9 | cleos -u $API push action bankofstaked addcreditor '{"account": "fundstaking2", "for_free": 0, "free_memo": ""}' -p bankofstaked 10 | cleos -u $API push action bankofstaked activate '{"account": "voter1"}' -p bankofstaked 11 | cleos -u $API push action bankofstaked activate '{"account": "voter2"}' -p bankofstaked 12 | ./scripts/set_perm.sh bankofstaked 13 | ./scripts/add_perm.sh voter1 14 | ./scripts/add_perm.sh voter2 15 | ./scripts/add_perm.sh freestaking1 16 | ./scripts/add_perm.sh freestaking2 17 | ./scripts/add_perm.sh fundstaking1 18 | ./scripts/add_perm.sh fundstaking2 19 | -------------------------------------------------------------------------------- /scripts/dev/check.sh: -------------------------------------------------------------------------------- 1 | cleos get account voter1 2 | cleos get account voter2 3 | cleos get account bankofstaked 4 | -------------------------------------------------------------------------------- /scripts/dev/deploy.sh: -------------------------------------------------------------------------------- 1 | API=${1:-http://localhost:8888} 2 | sleep 1 3 | cleos -u $API system newaccount voter3 bankofstaked EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV --stake-cpu "100 EOS" --stake-net "100 EOS" --buy-ram "100 EOS" 4 | sleep 1 5 | cleos -u $API set contract bankofstaked bankofstaked 6 | sleep 1 7 | cleos -u $API system newaccount voter3 stakedincome EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV --buy-ram "100 EOS" --stake-cpu "100 EOS" --stake-net "100 EOS" 8 | sleep 1 9 | cleos -u $API system newaccount voter3 masktransfer EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV --buy-ram "100 EOS" --stake-cpu "100 EOS" --stake-net "100 EOS" 10 | sleep 1 11 | cleos -u $API system newaccount voter3 fundstostake EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV --buy-ram "100 EOS" --stake-cpu "100 EOS" --stake-net "100 EOS" 12 | sleep 1 13 | 14 | cleos -u $API set contract masktransfer proxytoken 15 | sleep 1 16 | ./proxytoken/perms.sh masktransfer EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV 17 | sleep 1 18 | 19 | cleos -u $API set contract voter1 safedelegatebw 20 | sleep 1 21 | ./safedelegatebw/delegate_perm.sh voter1 22 | sleep 1 23 | ./safedelegatebw/creditor_perm.sh voter1 24 | sleep 1 25 | ./scripts/creditor_perm.sh voter2 EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV 26 | sleep 1 27 | ./scripts/bank_perm.sh bankofstaked EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV 28 | sleep 1 29 | ./scripts/order_perm.sh bankofstaked EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV 30 | sleep 1 31 | cleos -u $API push action bankofstaked addcreditor '{"account": "voter1", "for_free": 1, "free_memo": "A gift from EOSLaoMao team"}' -p bankofstaked 32 | sleep 1 33 | cleos -u $API push action bankofstaked addcreditor '{"account": "voter2", "for_free": 0, "free_memo": ""}' -p bankofstaked 34 | sleep 1 35 | cleos -u $API push action bankofstaked activate '{"account": "voter1"}' -p bankofstaked 36 | sleep 1 37 | cleos -u $API push action bankofstaked activate '{"account": "voter2"}' -p bankofstaked 38 | sleep 1 39 | cleos -u $API push action bankofstaked addsafeacnt '{"account": "voter1"}' -p bankofstaked 40 | sleep 1 41 | 42 | cleos -u $API push action bankofstaked setplan '{"price": "0.1000 EOS", "cpu": "0.5000 EOS", "net": "0.5000 EOS", "duration": 1, "is_free": true}' -p bankofstaked 43 | sleep 1 44 | cleos -u $API push action bankofstaked setplan '{"price": "0.2000 EOS", "cpu": "36.0000 EOS", "net": "4.0000 EOS", "duration": 1, "is_free": false}' -p bankofstaked 45 | sleep 1 46 | cleos -u $API push action bankofstaked activateplan '{"price": "0.1000 EOS", "is_active": true}' -p bankofstaked 47 | sleep 1 48 | cleos -u $API push action bankofstaked activateplan '{"price": "0.2000 EOS", "is_active": true}' -p bankofstaked 49 | sleep 1 50 | 51 | 52 | # add voter3 to whitelist table 53 | cleos -u $API push action bankofstaked addwhitelist '{"account": "voter3", "capacity": 1000}' -p bankofstaked 54 | sleep 1 55 | -------------------------------------------------------------------------------- /scripts/dev/spammer.sh: -------------------------------------------------------------------------------- 1 | rm multi_* 2 | ./scripts/check.sh > multi_before 3 | for i in a b c d e f g h i j k l m n o p q r s t u v w x y z 4 | do 5 | for j in a b c d e f g h i j k l m n o p q r s t u v w x y z 6 | do 7 | name=testaccoua$i$j 8 | cleos system newaccount voter3 $name EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV --stake-net "10 EOS" --stake-cpu "10 EOS" --buy-ram "10 EOS" 9 | cleos transfer voter3 bankofstaked '0.1 EOS' "$name" 10 | done 11 | done 12 | ./scripts/check.sh > multi_after 13 | sleep 70 14 | ./scripts/check.sh > multi_undelegate 15 | sleep 70 16 | ./scripts/check.sh > multi_refund 17 | -------------------------------------------------------------------------------- /scripts/empty.sh: -------------------------------------------------------------------------------- 1 | API=${1:-http://localhost:8888} 2 | cleos -u $API push action bankofstaked empty "" -p bankofstaked 3 | -------------------------------------------------------------------------------- /scripts/get_table.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | API=${1:-http://localhost:8888} 4 | limit=${2:-100} 5 | v=921459758687; k=creditor; declare "table_$k=$v"; 6 | v=921459758687; k=safecreditor; declare "table_$k=$v"; 7 | v=921459758687; k=history; declare "table_$k=$v"; 8 | v=921459758687; k=order; declare "table_$k=$v"; 9 | v=921459758687; k=freelock; declare "table_$k=$v"; 10 | v=921459758687; k=blacklist; declare "table_$k=$v"; 11 | v=921459758687; k=whitelist; declare "table_$k=$v"; 12 | v=bankofstaked; k=plan; declare "table_$k=$v"; 13 | 14 | 15 | for name in creditor safecreditor plan order history freelock blacklist whitelist 16 | do 17 | echo "==============TABLE "$name"========" 18 | scope="table_$name" 19 | cleos -u $API get table bankofstaked ${!scope} $name -l " $limit" 20 | echo "------------------------------------" 21 | echo 22 | done 23 | -------------------------------------------------------------------------------- /scripts/order_perm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ACCOUNT=$1 4 | PKEY=${2:-EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV} 5 | PERM_ACCOUNT=${3:-bankofstaked} 6 | PERMISSION=${4:-eosio.code} 7 | 8 | cleos set account permission $ACCOUNT orderperm '{"threshold": 1,"keys": [{"key": "'$PKEY'","weight": 1}],"accounts": [{"permission":{"actor":"'$PERM_ACCOUNT'","permission":"'$PERMISSION'"},"weight":1}]}' "active" -p $ACCOUNT@active 9 | cleos set action permission $ACCOUNT bankofstaked customorder orderperm -p $ACCOUNT@active 10 | -------------------------------------------------------------------------------- /scripts/set_plan.sh: -------------------------------------------------------------------------------- 1 | API=${1:-http://localhost:8888} 2 | cleos -u $API push action -s -j -d bankofstaked setplan '{"price": "0.2000 EOS", "cpu": "22.0000 EOS", "net": "2.0000 EOS", "duration": 10080, "is_free": false}' -p bankofstaked >> plan.json 3 | cleos -u $API push action -s -j -d bankofstaked setplan '{"price": "0.5000 EOS", "cpu": "58.0000 EOS", "net": "2.0000 EOS", "duration": 10080, "is_free": false}' -p bankofstaked >> plan.json 4 | cleos -u $API push action -s -j -d bankofstaked setplan '{"price": "1.0000 EOS", "cpu": "118.0000 EOS", "net": "2.0000 EOS", "duration": 10080, "is_free": false}' -p bankofstaked >> plan.json 5 | cleos -u $API push action -s -j -d bankofstaked setplan '{"price": "2.0000 EOS", "cpu": "238.0000 EOS", "net": "2.0000 EOS", "duration": 10080, "is_free": false}' -p bankofstaked >> plan.json 6 | cleos -u $API push action -s -j -d bankofstaked setplan '{"price": "180.0000 EOS", "cpu": "9900.0000 EOS", "net": "100.0000 EOS", "duration": 40320, "is_free": false}' -p bankofstaked >> plan.json 7 | cleos -u $API push action -s -j -d bankofstaked setplan '{"price": "800.0000 EOS", "cpu": "49500.0000 EOS", "net": "500.0000 EOS", "duration": 40320, "is_free": false}' -p bankofstaked >> plan.json 8 | #cleos -u $API push action -s -j -d bankofstaked activateplan '{"price": "130.0000 EOS", "is_active": false}' -p bankofstaked >> plan.json 9 | #cleos -u $API push action -s -j -d bankofstaked activateplan '{"price": "580.0000 EOS", "is_active": false}' -p bankofstaked >> plan.json 10 | #cleos -u $API push action -s -j -d bankofstaked activateplan '{"price": "180.0000 EOS", "is_active": true}' -p bankofstaked >> plan.json 11 | #cleos -u $API push action -s -j -d bankofstaked activateplan '{"price": "780.0000 EOS", "is_active": true}' -p bankofstaked >> plan.json 12 | -------------------------------------------------------------------------------- /src/bankofstaked.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "../include/bankofstaked/bankofstaked.hpp" 5 | #include "lock.cpp" 6 | #include "utils.cpp" 7 | #include "validation.cpp" 8 | #include "safedelegatebw.hpp" 9 | 10 | using namespace eosio; 11 | using namespace eosiosystem; 12 | using namespace bank; 13 | using namespace lock; 14 | using namespace utils; 15 | using namespace validation; 16 | 17 | class [[eosio::contract]] bankofstaked : contract 18 | { 19 | 20 | public: 21 | using contract::contract; 22 | 23 | [[eosio::action]] 24 | void clearhistory(uint64_t max_depth) 25 | { 26 | require_auth(CODE_ACCOUNT); 27 | uint64_t depth = 0; 28 | history_table o(CODE_ACCOUNT, SCOPE); 29 | while (o.begin() != o.end()) 30 | { 31 | depth += 1; 32 | if(depth > max_depth) { 33 | break; 34 | } 35 | auto itr = o.end(); 36 | itr--; 37 | o.erase(itr); 38 | history_table o(CODE_ACCOUNT, SCOPE); 39 | } 40 | } 41 | 42 | // DEBUG only, action to empty entires in both tables 43 | [[eosio::action]] 44 | void empty() 45 | { 46 | require_auth(CODE_ACCOUNT); 47 | /* 48 | plan_table p(CODE_ACCOUNT, CODE_ACCOUNT); 49 | while (p.begin() != p.end()) 50 | { 51 | auto itr = p.end(); 52 | itr--; 53 | p.erase(itr); 54 | plan_table p(CODE_ACCOUNT, CODE_ACCOUNT); 55 | } 56 | order_table o(CODE_ACCOUNT, SCOPE); 57 | while (o.begin() != o.end()) 58 | { 59 | auto itr = o.end(); 60 | itr--; 61 | o.erase(itr); 62 | order_table o(CODE_ACCOUNT, SCOPE); 63 | } 64 | 65 | plan_table p(CODE_ACCOUNT, CODE_ACCOUNT); 66 | while (p.begin() != p.end()) 67 | { 68 | auto itr = p.end(); 69 | itr--; 70 | p.erase(itr); 71 | plan_table p(CODE_ACCOUNT, CODE_ACCOUNT); 72 | } 73 | 74 | creditor_table c(CODE_ACCOUNT, SCOPE); 75 | while (c.begin() != c.end()) 76 | { 77 | auto itr = c.end(); 78 | itr--; 79 | c.erase(itr); 80 | creditor_table c(CODE_ACCOUNT, SCOPE); 81 | } 82 | freelock_table c(CODE_ACCOUNT, SCOPE); 83 | while (c.begin() != c.end()) 84 | { 85 | auto itr = c.end(); 86 | itr--; 87 | c.erase(itr); 88 | freelock_table c(CODE_ACCOUNT, SCOPE); 89 | } 90 | */ 91 | } 92 | 93 | [[eosio::action]] 94 | void test(name creditor) 95 | { 96 | require_auth(CODE_ACCOUNT); 97 | 98 | validate_creditor(creditor); 99 | 100 | plan_table p(CODE_ACCOUNT, CODE_ACCOUNT.value); 101 | auto idx = p.get_index<"price"_n>(); 102 | auto plan = idx.find(FREE_PLAN_AMOUNT); 103 | 104 | //INLINE ACTION to test delegate CPU&NET for creditor itself 105 | if (is_safe_creditor(creditor)) { 106 | INLINE_ACTION_SENDER(safedelegatebw, delegatebw) 107 | (creditor, {{creditor, "creditorperm"_n}}, {creditor, plan->net, plan->cpu}); 108 | } else { 109 | INLINE_ACTION_SENDER(eosiosystem::system_contract, delegatebw) 110 | (EOSIO, {{creditor, "creditorperm"_n}}, {creditor, creditor, plan->net, plan->cpu, false}); 111 | } 112 | 113 | INLINE_ACTION_SENDER(eosiosystem::system_contract, undelegatebw) 114 | (EOSIO, {{creditor, "creditorperm"_n}}, {creditor, creditor, plan->net, plan->cpu}); 115 | 116 | } 117 | 118 | [[eosio::action]] 119 | void rotate(name creditor, uint64_t for_free) 120 | { 121 | require_auth(CODE_ACCOUNT); 122 | 123 | validate_creditor(creditor); 124 | } 125 | 126 | [[eosio::action]] 127 | void check(name creditor) 128 | { 129 | require_auth(CODE_ACCOUNT); 130 | 131 | validate_creditor(creditor); 132 | 133 | order_table o(CODE_ACCOUNT, SCOPE); 134 | uint64_t depth = 0; 135 | std::vector order_ids; 136 | 137 | // order ordered by expire_at 138 | auto idx = o.get_index<"expire.at"_n>(); 139 | auto itr = idx.begin(); 140 | //force expire at most CHECK_MAX_DEPTH orders 141 | while (itr != idx.end() && depth < CHECK_MAX_DEPTH) 142 | { 143 | if(now() >= itr->expire_at) { 144 | order_ids.emplace_back(itr->id); 145 | } 146 | depth++; 147 | itr++; 148 | } 149 | undelegate(order_ids, 0); 150 | expire_freelock(); 151 | rotate_creditor(); 152 | get_balance(creditor); 153 | } 154 | 155 | [[eosio::action]] 156 | void forcexpire(const std::vector& order_ids=std::vector()) 157 | { 158 | require_auth(CODE_ACCOUNT); 159 | 160 | //force expire provided orders 161 | undelegate(order_ids, 0); 162 | expire_freelock(); 163 | rotate_creditor(); 164 | } 165 | 166 | [[eosio::action]] 167 | void expireorder(uint64_t id) 168 | { 169 | require_auth(CODE_ACCOUNT); 170 | 171 | order_table o(CODE_ACCOUNT, SCOPE); 172 | auto order = o.find(id); 173 | eosio_assert(order != o.end(), "order entry not found!!!"); 174 | 175 | 176 | // updated cpu_staked/net_staked/cpu_unstaked/net_unstaked of creditor entry 177 | creditor_table c(CODE_ACCOUNT, SCOPE); 178 | auto creditor_itr = c.find(order->creditor.value); 179 | c.modify(creditor_itr, RAM_PAYER, [&](auto &i) { 180 | i.cpu_staked -= order->cpu_staked; 181 | i.net_staked -= order->net_staked; 182 | i.cpu_unstaked += order->cpu_staked; 183 | i.net_unstaked += order->net_staked; 184 | i.balance = get_balance(order->creditor); 185 | i.updated_at = now(); 186 | }); 187 | 188 | save_order_history_table(&(*order)); 189 | 190 | //delete order entry 191 | o.erase(order); 192 | } 193 | 194 | [[eosio::action]] 195 | void addwhitelist(name account, uint64_t capacity) 196 | { 197 | require_auth(CODE_ACCOUNT); 198 | whitelist_table w(CODE_ACCOUNT, SCOPE); 199 | auto itr = w.find(account.value); 200 | if(itr == w.end()) { 201 | w.emplace(RAM_PAYER, [&](auto &i) { 202 | i.account = account; 203 | i.capacity = capacity; 204 | i.created_at = now(); 205 | i.updated_at = now(); 206 | }); 207 | } else { 208 | w.modify(itr, RAM_PAYER, [&](auto &i) { 209 | i.capacity = capacity; 210 | i.updated_at = now(); 211 | }); 212 | } 213 | } 214 | 215 | [[eosio::action]] 216 | void delwhitelist(name account, uint64_t capacity) 217 | { 218 | require_auth(CODE_ACCOUNT); 219 | whitelist_table w(CODE_ACCOUNT, SCOPE); 220 | auto itr = w.find(account.value); 221 | eosio_assert(itr != w.end(), "account not found in whitelist table"); 222 | //delelete whitelist entry 223 | w.erase(itr); 224 | } 225 | 226 | [[eosio::action]] 227 | void addcreditor(name account, uint64_t for_free, std::string free_memo) 228 | { 229 | require_auth(CODE_ACCOUNT); 230 | creditor_table c(CODE_ACCOUNT, SCOPE); 231 | auto itr = c.find(account.value); 232 | eosio_assert(itr == c.end(), "account already exist in creditor table"); 233 | 234 | c.emplace(RAM_PAYER, [&](auto &i) { 235 | i.is_active = FALSE; 236 | i.for_free = for_free?TRUE:FALSE; 237 | i.free_memo = for_free?free_memo:""; 238 | i.account = account; 239 | i.balance = get_balance(account); 240 | i.created_at = now(); 241 | i.updated_at = 0; // set to 0 for creditor auto rotation 242 | }); 243 | } 244 | 245 | [[eosio::action]] 246 | void addsafeacnt(name account) 247 | { 248 | require_auth(CODE_ACCOUNT); 249 | 250 | validate_creditor(account); 251 | 252 | safecreditor_table s(CODE_ACCOUNT, SCOPE); 253 | s.emplace(RAM_PAYER, [&](auto &i) { 254 | i.account = account; 255 | i.created_at = now(); 256 | i.updated_at = now(); 257 | }); 258 | } 259 | 260 | [[eosio::action]] 261 | void delsafeacnt(name account) 262 | { 263 | require_auth(CODE_ACCOUNT); 264 | safecreditor_table s(CODE_ACCOUNT, SCOPE); 265 | auto itr = s.find(account.value); 266 | eosio_assert(itr != s.end(), "account does not exist in safecreditor table"); 267 | s.erase(itr); 268 | } 269 | 270 | [[eosio::action]] 271 | void setrecipient(name creditor, name recipient) 272 | { 273 | require_auth(CODE_ACCOUNT); 274 | 275 | validate_creditor(creditor); 276 | 277 | recipient_table r(CODE_ACCOUNT, CODE_ACCOUNT.value); 278 | auto itr = r.find(creditor.value); 279 | if(itr == r.end()) { 280 | r.emplace(RAM_PAYER, [&](auto &i) { 281 | i.creditor = creditor; 282 | i.recipient_account = recipient; 283 | i.created_at = now(); 284 | i.updated_at = now(); 285 | }); 286 | } else { 287 | r.modify(itr, RAM_PAYER, [&](auto &i) { 288 | i.recipient_account = recipient; 289 | i.updated_at = now(); 290 | }); 291 | } 292 | } 293 | 294 | 295 | [[eosio::action]] 296 | void delorders(const std::vector &order_ids = std::vector()) 297 | { 298 | require_auth(CODE_ACCOUNT); 299 | if (order_ids.size() == 0) 300 | { 301 | return; 302 | } 303 | 304 | order_table o(CODE_ACCOUNT, SCOPE); 305 | 306 | for (int i = 0; i < order_ids.size(); i++) 307 | { 308 | uint64_t order_id = order_ids[i]; 309 | auto order = o.find(order_id); 310 | eosio_assert(order != o.end(), "order entry not found!!!"); 311 | save_order_history_table(&(*order)); 312 | o.erase(order); 313 | } 314 | } 315 | 316 | 317 | [[eosio::action]] 318 | void delrecipient(name creditor) 319 | { 320 | require_auth(CODE_ACCOUNT); 321 | recipient_table r(CODE_ACCOUNT, CODE_ACCOUNT.value); 322 | auto itr = r.find(creditor.value); 323 | eosio_assert(itr != r.end(), "recipient entry not found!!!"); 324 | r.erase(itr); 325 | } 326 | 327 | [[eosio::action]] 328 | void delcreditor(name account) 329 | { 330 | require_auth(CODE_ACCOUNT); 331 | creditor_table c(CODE_ACCOUNT, SCOPE); 332 | auto itr = c.find(account.value); 333 | eosio_assert(itr!= c.end(), "account not found in creditor table"); 334 | eosio_assert(itr->is_active == FALSE, "cannot delete active creditor"); 335 | //delelete creditor entry 336 | c.erase(itr); 337 | } 338 | 339 | 340 | [[eosio::action]] 341 | void addblacklist(name account) 342 | { 343 | require_auth(CODE_ACCOUNT); 344 | blacklist_table b(CODE_ACCOUNT, SCOPE); 345 | auto itr = b.find(account.value); 346 | eosio_assert(itr == b.end(), "account already exist in blacklist table"); 347 | 348 | // add entry 349 | b.emplace(RAM_PAYER, [&](auto &i) { 350 | i.account = account; 351 | i.created_at = now(); 352 | }); 353 | } 354 | 355 | 356 | [[eosio::action]] 357 | void delblacklist(name account) 358 | { 359 | require_auth(CODE_ACCOUNT); 360 | blacklist_table b(CODE_ACCOUNT, SCOPE); 361 | 362 | //make sure specified blacklist account exists 363 | auto itr = b.find(account.value); 364 | eosio_assert(itr!= b.end(), "account not found in blacklist table"); 365 | //delelete entry 366 | b.erase(itr); 367 | } 368 | 369 | 370 | [[eosio::action]] 371 | void activate(name account) 372 | { 373 | require_auth(CODE_ACCOUNT); 374 | activate_creditor(account); 375 | } 376 | 377 | 378 | [[eosio::action]] 379 | void setplan(asset price, 380 | asset cpu, 381 | asset net, 382 | uint64_t duration, 383 | bool is_free) 384 | { 385 | require_auth(CODE_ACCOUNT); 386 | validate_asset(price, cpu, net); 387 | plan_table p(CODE_ACCOUNT, CODE_ACCOUNT.value); 388 | auto idx = p.get_index<"price"_n>(); 389 | auto itr = idx.find(price.amount); 390 | if (itr == idx.end()) 391 | { 392 | p.emplace(RAM_PAYER, [&](auto &i) { 393 | i.id = p.available_primary_key(); 394 | i.price = price; 395 | i.cpu = cpu; 396 | i.net = net; 397 | i.duration = duration; 398 | i.is_active = FALSE; 399 | i.is_free = is_free?TRUE:FALSE; 400 | i.created_at = now(); 401 | i.updated_at = now(); 402 | }); 403 | } 404 | else 405 | { 406 | idx.modify(itr, RAM_PAYER, [&](auto &i) { 407 | i.cpu = cpu; 408 | i.net = net; 409 | i.duration = duration; 410 | i.is_free = is_free?TRUE:FALSE; 411 | i.updated_at = now(); 412 | }); 413 | } 414 | } 415 | 416 | [[eosio::action]] 417 | void activateplan(asset price, bool is_active) 418 | { 419 | require_auth(CODE_ACCOUNT); 420 | eosio_assert(price.is_valid(), "invalid price"); 421 | plan_table p(CODE_ACCOUNT, CODE_ACCOUNT.value); 422 | auto idx = p.get_index<"price"_n>(); 423 | auto itr = idx.find(price.amount); 424 | eosio_assert(itr != idx.end(), "price not found"); 425 | 426 | idx.modify(itr, RAM_PAYER, [&](auto &i) { 427 | i.is_active = is_active?TRUE:FALSE; 428 | i.updated_at = now(); 429 | }); 430 | } 431 | 432 | [[eosio::action]] 433 | void customorder(name beneficiary, asset quantity, asset cpu, asset net, int64_t duration) 434 | { 435 | require_auth(CODE_ACCOUNT); 436 | bool is_free = false; 437 | 438 | // limit max quantity and resources 439 | eosio_assert(quantity <= asset{4000000, EOS_SYMBOL}, "quantity exceeds limit"); 440 | eosio_assert(cpu <= asset{1980000000, EOS_SYMBOL}, "cpu exceeds limit"); 441 | eosio_assert(net <= asset{20000000, EOS_SYMBOL}, "net exceeds limit"); 442 | eosio_assert(duration >= 10080, "duration less than limit"); 443 | 444 | // make sure beneficiary is a valid account 445 | eosio_assert( is_account( beneficiary ), "to account does not exist"); 446 | 447 | //get active creditor 448 | name creditor = get_active_creditor(is_free); 449 | 450 | //plan is not free, make sure creditor has enough balance to delegate 451 | asset to_delegate = cpu + net; 452 | if(get_balance(creditor) < to_delegate) { 453 | creditor = get_qualified_paid_creditor(to_delegate); 454 | } 455 | 456 | //make sure creditor is a valid account 457 | eosio_assert( is_account( creditor ), "creditor account does not exist"); 458 | 459 | //validate beneficiary 460 | //1. beneficiary shouldnt be CODE_ACCOUNT 461 | //2. beneficiary shouldnt be in blacklist 462 | //3. each beneficiary could only have 5 affective orders at most 463 | validate_beneficiary(beneficiary, creditor, is_free); 464 | 465 | //INLINE ACTION to delegate CPU&NET for beneficiary account 466 | if (is_safe_creditor(creditor)) { 467 | INLINE_ACTION_SENDER(safedelegatebw, delegatebw) 468 | (creditor, {{creditor, "creditorperm"_n}}, {beneficiary, net, cpu}); 469 | } else { 470 | INLINE_ACTION_SENDER(eosiosystem::system_contract, delegatebw) 471 | (EOSIO, {{creditor, "creditorperm"_n}}, {creditor, beneficiary, net, cpu, false}); 472 | } 473 | 474 | //INLINE ACTION to call check action of `bankofstaked` 475 | INLINE_ACTION_SENDER(bankofstaked, check) 476 | (CODE_ACCOUNT, {{CODE_ACCOUNT, "bankperm"_n}}, {creditor}); 477 | 478 | // add cpu_staked&net_staked to creditor entry 479 | creditor_table c(CODE_ACCOUNT, SCOPE); 480 | auto creditor_itr = c.find(creditor.value); 481 | c.modify(creditor_itr, RAM_PAYER, [&](auto &i) { 482 | i.cpu_staked += cpu; 483 | i.net_staked += net; 484 | i.balance = get_balance(creditor); 485 | i.updated_at = now(); 486 | }); 487 | 488 | //create Order entry 489 | uint64_t order_id; 490 | order_table o(CODE_ACCOUNT, SCOPE); 491 | o.emplace(RAM_PAYER, [&](auto &i) { 492 | i.id = o.available_primary_key(); 493 | i.buyer = CODE_ACCOUNT; 494 | i.price = quantity; 495 | i.creditor = creditor; 496 | i.beneficiary = beneficiary; 497 | i.plan_id = std::numeric_limits::max(); 498 | i.cpu_staked = cpu; 499 | i.net_staked = net; 500 | i.is_free = is_free; 501 | i.created_at = now(); 502 | i.expire_at = now() + duration * SECONDS_PER_MIN; 503 | 504 | order_id = i.id; 505 | }); 506 | 507 | //deferred transaction to auto undelegate after expired 508 | std::vector order_ids; 509 | order_ids.emplace_back(order_id); 510 | undelegate(order_ids, duration); 511 | } 512 | 513 | //token received 514 | void received_token(name from, name to, asset quantity, string memo) 515 | { 516 | //validation token transfer, only accept EOS transfer 517 | eosio_assert(quantity.symbol == EOS_SYMBOL, "only accept EOS transfer"); 518 | 519 | if (to == _self) 520 | { 521 | name buyer = from; 522 | //if token comes from fundstostake, do nothing, just take it :) 523 | if (from == "fundstostake"_n) 524 | { 525 | return; 526 | } 527 | //validate plan, is_active should be TRUE 528 | plan_table p(CODE_ACCOUNT, CODE_ACCOUNT.value); 529 | auto idx = p.get_index<"price"_n>(); 530 | auto plan = idx.find(quantity.amount); 531 | eosio_assert(plan->is_active == TRUE, "plan is in-active"); 532 | eosio_assert(plan != idx.end(), "invalid price"); 533 | 534 | name beneficiary = get_beneficiary(memo, buyer); 535 | 536 | // if plan is free, validate there is no Freelock for this beneficiary 537 | if(plan->is_free == TRUE) 538 | { 539 | validate_freelock(beneficiary); 540 | } 541 | 542 | //get active creditor 543 | name creditor = get_active_creditor(plan->is_free); 544 | 545 | //if plan is not free, make sure creditor has enough balance to delegate 546 | if(plan->is_free == FALSE) 547 | { 548 | asset to_delegate = plan->cpu + plan->net; 549 | if(get_balance(creditor) < to_delegate) { 550 | creditor = get_qualified_paid_creditor(to_delegate); 551 | } 552 | } 553 | 554 | //make sure creditor is a valid account 555 | eosio_assert( is_account( creditor ), "creditor account does not exist"); 556 | 557 | //validate buyer 558 | //1. buyer shouldnt be CODE_ACCOUNT 559 | //2. buyer shouldnt be in blacklist 560 | //3. each buyer could only have 5 affective orders at most 561 | validate_buyer(buyer, plan->is_free); 562 | 563 | //validate beneficiary 564 | //1. beneficiary shouldnt be CODE_ACCOUNT 565 | //2. beneficiary shouldnt be in blacklist 566 | //3. each beneficiary could only have 5 affective orders at most 567 | validate_beneficiary(beneficiary, creditor, plan->is_free); 568 | 569 | //INLINE ACTION to delegate CPU&NET for beneficiary account 570 | if (is_safe_creditor(creditor)) { 571 | INLINE_ACTION_SENDER(safedelegatebw, delegatebw) 572 | (creditor, {{creditor, "creditorperm"_n}}, {beneficiary, plan->net, plan->cpu}); 573 | } else { 574 | INLINE_ACTION_SENDER(eosiosystem::system_contract, delegatebw) 575 | (EOSIO, {{creditor, "creditorperm"_n}}, {creditor, beneficiary, plan->net, plan->cpu, false}); 576 | } 577 | 578 | //INLINE ACTION to call check action of `bankofstaked` 579 | INLINE_ACTION_SENDER(bankofstaked, check) 580 | (CODE_ACCOUNT, {{CODE_ACCOUNT, "bankperm"_n}}, {creditor}); 581 | 582 | // add cpu_staked&net_staked to creditor entry 583 | creditor_table c(CODE_ACCOUNT, SCOPE); 584 | auto creditor_itr = c.find(creditor.value); 585 | c.modify(creditor_itr, RAM_PAYER, [&](auto &i) { 586 | i.cpu_staked += plan->cpu; 587 | i.net_staked += plan->net; 588 | i.balance = get_balance(creditor); 589 | i.updated_at = now(); 590 | }); 591 | 592 | //create Order entry 593 | uint64_t order_id; 594 | order_table o(CODE_ACCOUNT, SCOPE); 595 | o.emplace(RAM_PAYER, [&](auto &i) { 596 | i.id = o.available_primary_key(); 597 | i.buyer = buyer; 598 | i.price = plan->price; 599 | i.creditor = creditor; 600 | i.beneficiary = beneficiary; 601 | i.plan_id = plan->id; 602 | i.cpu_staked = plan->cpu; 603 | i.net_staked = plan->net; 604 | i.is_free = plan->is_free; 605 | i.created_at = now(); 606 | i.expire_at = now() + plan->duration * SECONDS_PER_MIN; 607 | 608 | order_id = i.id; 609 | }); 610 | 611 | if(plan->is_free == TRUE) 612 | { 613 | // if plan is free, add a Freelock entry 614 | add_freelock(beneficiary); 615 | // auto refund immediately 616 | //INLINE ACTION to auto refund 617 | creditor_table c(CODE_ACCOUNT, SCOPE); 618 | std::string free_memo = c.get(creditor.value).free_memo; 619 | std::string buyer_name = buyer.to_string(); 620 | std::string memo = buyer_name + " " + free_memo; 621 | INLINE_ACTION_SENDER(eosio::token, transfer) 622 | ("eosio.token"_n, {{CODE_ACCOUNT, "bankperm"_n}}, {CODE_ACCOUNT, MASK_TRANSFER, plan->price, memo}); 623 | } 624 | 625 | //deferred transaction to auto undelegate after expired 626 | std::vector order_ids; 627 | order_ids.emplace_back(order_id); 628 | undelegate(order_ids, plan->duration); 629 | } 630 | } 631 | 632 | private: 633 | 634 | void save_order_history_table(const order *order){ 635 | 636 | 637 | std::string content = ""; 638 | //save order meta to history 639 | //buyer|creditor|beneficiary|plan_id|price|cpu|net|created_at|expire_at 640 | content += order->buyer.to_string(); 641 | content += "|" + order->creditor.to_string(); 642 | content += "|" + order->beneficiary.to_string(); 643 | content += "|" + std::to_string(order->plan_id); 644 | content += "|" + std::to_string(order->price.amount); 645 | content += order->is_free==TRUE?"|free":"|paid"; 646 | content += "|" + std::to_string(order->cpu_staked.amount); 647 | content += "|" + std::to_string(order->net_staked.amount); 648 | content += "|" + std::to_string(order->created_at); 649 | content += "|" + std::to_string(order->expire_at); 650 | 651 | // save order mete data to history table 652 | history_table h(CODE_ACCOUNT, SCOPE); 653 | h.emplace(RAM_PAYER, [&](auto &i) { 654 | i.id = h.available_primary_key(); 655 | i.content = content; 656 | i.created_at = now(); 657 | }); 658 | } 659 | 660 | //undelegate Orders specified by order_ids 661 | //deferred(if duration > 0) transaction to auto undelegate after expired 662 | void undelegate(const std::vector& order_ids=std::vector(), uint64_t duration=0) 663 | { 664 | if(order_ids.size() == 0) 665 | { 666 | return; 667 | } 668 | eosio::transaction out; 669 | 670 | order_table o(CODE_ACCOUNT, SCOPE); 671 | plan_table p(CODE_ACCOUNT, CODE_ACCOUNT.value); 672 | 673 | uint64_t nonce = 0; 674 | 675 | for(int i=0; i 0) { 730 | out.delay_sec = duration * SECONDS_PER_MIN; 731 | } 732 | out.send((uint128_t(CODE_ACCOUNT.value) << 64) | current_time() | nonce, CODE_ACCOUNT, true); 733 | } 734 | 735 | }; 736 | 737 | extern "C" { 738 | void apply(uint64_t receiver, uint64_t code, uint64_t action) { 739 | if (code == "eosio.token"_n.value && action == "transfer"_n.value) { 740 | eosio::execute_action( 741 | name(receiver), name(code), &bankofstaked::received_token 742 | ); 743 | } 744 | 745 | if (code == receiver) { 746 | switch (action) { 747 | EOSIO_DISPATCH_HELPER(bankofstaked, 748 | (clearhistory) 749 | (empty) 750 | (test) 751 | (rotate) 752 | (check) 753 | (forcexpire) 754 | (expireorder) 755 | (addwhitelist) 756 | (delwhitelist) 757 | (addcreditor) 758 | (addsafeacnt) 759 | (delsafeacnt) 760 | (delcreditor) 761 | (delorders) 762 | (addblacklist) 763 | (delblacklist) 764 | (setplan) 765 | (activate) 766 | (activateplan) 767 | (setrecipient) 768 | (delrecipient) 769 | (customorder) 770 | ) 771 | } 772 | } 773 | } 774 | } 775 | -------------------------------------------------------------------------------- /src/lock.cpp: -------------------------------------------------------------------------------- 1 | using namespace eosio; 2 | using namespace eosiosystem; 3 | using namespace bank; 4 | 5 | namespace lock 6 | { 7 | //add freelock entry 8 | void add_freelock(name beneficiary) 9 | { 10 | freelock_table f(CODE_ACCOUNT, SCOPE); 11 | f.emplace(RAM_PAYER, [&](auto &i) { 12 | i.beneficiary = beneficiary; 13 | i.created_at = now(); 14 | i.expire_at = i.created_at + SECONDS_PER_DAY; 15 | }); 16 | } 17 | 18 | //delete expired freelock entries 19 | void expire_freelock() 20 | { 21 | uint64_t depth = 0; 22 | uint64_t n = now(); 23 | freelock_table f(CODE_ACCOUNT, SCOPE); 24 | auto idx = f.get_index<"expire.at"_n>(); 25 | auto last = idx.upper_bound(n); 26 | auto itr = idx.lower_bound(0); 27 | while(itr!=last && depth < CHECK_MAX_DEPTH) 28 | { 29 | idx.erase(itr); 30 | itr = idx.lower_bound(0); 31 | depth += 1; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/safedelegatebw.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace eosio; 6 | using namespace eosiosystem; 7 | using std::string; 8 | 9 | class [[eosio::contract]] safedelegatebw : contract { 10 | public: 11 | using contract::contract; 12 | 13 | [[eosio::action]] 14 | void delegatebw(name to, 15 | asset net_weight, 16 | asset cpu_weight){ 17 | 18 | require_auth(_self); 19 | 20 | INLINE_ACTION_SENDER(eosiosystem::system_contract, delegatebw) 21 | ("eosio"_n, {{_self, "delegateperm"_n}}, {_self, to, net_weight, cpu_weight, false}); 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | using namespace eosio; 2 | using namespace eosiosystem; 3 | using namespace bank; 4 | 5 | namespace utils 6 | { 7 | //try to get beneficiary from memo, otherwise, use sender. 8 | name get_beneficiary(const std::string &memo, name sender) 9 | { 10 | name to = sender; 11 | if (memo.length() > 0) 12 | { 13 | to = name(memo.c_str()); 14 | eosio_assert( is_account( to ), "to account does not exist"); 15 | } 16 | return to; 17 | } 18 | 19 | //get income recipient for creditor 20 | name get_recipient(name creditor) 21 | { 22 | recipient_table i(CODE_ACCOUNT, CODE_ACCOUNT.value); 23 | name recipient = creditor; 24 | auto itr = i.find(creditor.value); 25 | if(itr != i.end()) { 26 | recipient = itr->recipient_account; 27 | } 28 | eosio_assert( is_account( recipient ), "recipient account does not exist"); 29 | return recipient; 30 | } 31 | 32 | //get active creditor from creditor table 33 | name get_active_creditor(uint64_t for_free) 34 | { 35 | uint64_t active = TRUE; 36 | creditor_table c(CODE_ACCOUNT, SCOPE); 37 | auto idx = c.get_index<"is.active"_n>(); 38 | auto itr = idx.begin(); 39 | name creditor; 40 | while (itr != idx.end()) 41 | { 42 | if(itr->is_active != TRUE) 43 | { 44 | itr++; 45 | continue; 46 | } 47 | 48 | if(itr->for_free == for_free) { 49 | creditor = itr->account; 50 | break; 51 | } 52 | itr++; 53 | } 54 | return creditor; 55 | } 56 | 57 | //get account EOS balance 58 | asset get_balance(name owner) 59 | { 60 | auto balance = eosio::token::get_balance("eosio.token"_n, owner, EOS_SYMBOL.code()); 61 | return balance; 62 | } 63 | 64 | //get account EOS balance 65 | asset update_balance(name owner) 66 | { 67 | auto balance = get_balance(owner); 68 | // update creditor if update is true 69 | creditor_table c(CODE_ACCOUNT, SCOPE); 70 | auto creditor_itr = c.find(owner.value); 71 | if(creditor_itr != c.end() && creditor_itr->balance != balance) { 72 | c.modify(creditor_itr, RAM_PAYER, [&](auto &i) { 73 | i.balance = balance; 74 | i.updated_at = now(); 75 | }); 76 | } 77 | return balance; 78 | } 79 | 80 | //get creditor with balance >= to_delegate 81 | name get_qualified_paid_creditor(asset to_delegate) 82 | { 83 | uint64_t active = TRUE; 84 | creditor_table c(CODE_ACCOUNT, SCOPE); 85 | auto idx = c.get_index<"is.active"_n>(); 86 | auto itr = idx.begin(); 87 | name creditor; 88 | while (itr != idx.end()) 89 | { 90 | asset balance = get_balance(itr->account); 91 | if(itr->for_free == FALSE && balance >= to_delegate) { 92 | creditor = itr->account; 93 | break; 94 | } 95 | itr++; 96 | } 97 | return creditor; 98 | } 99 | 100 | //get creditor income 101 | asset get_income(name creditor, asset price) 102 | { 103 | dividend_table c(CODE_ACCOUNT, CODE_ACCOUNT.value); 104 | uint64_t amount = price.amount; 105 | auto itr = c.find(creditor.value); 106 | if(itr != c.end()) { 107 | price.amount = amount * itr->percentage / 100; 108 | } else { 109 | price.amount = amount * DEFAULT_DIVIDEND_PERCENTAGE / 100; 110 | } 111 | return price; 112 | } 113 | 114 | void activate_creditor(name account) 115 | { 116 | creditor_table c(CODE_ACCOUNT, SCOPE); 117 | 118 | auto creditor = c.find(account.value); 119 | //make sure specified creditor exists 120 | eosio_assert(creditor != c.end(), "account not found in creditor table"); 121 | 122 | eosio::transaction out; 123 | //activate creditor, deactivate others 124 | auto itr = c.end(); 125 | while (itr != c.begin()) 126 | { 127 | itr--; 128 | if (itr->for_free != creditor->for_free) { 129 | continue; 130 | } 131 | 132 | if(itr->account==creditor->account) { 133 | c.modify(itr, RAM_PAYER, [&](auto &i) { 134 | i.is_active = TRUE; 135 | i.balance = get_balance(itr->account); 136 | i.updated_at = now(); 137 | }); 138 | action act1 = action( 139 | permission_level{ CODE_ACCOUNT, "bankperm"_n }, 140 | CODE_ACCOUNT, 141 | "rotate"_n, 142 | std::make_tuple(itr->account, itr->for_free) 143 | ); 144 | out.actions.emplace_back(act1); 145 | } else { 146 | if(itr->is_active == FALSE) { 147 | continue; 148 | } 149 | c.modify(itr, RAM_PAYER, [&](auto &i) { 150 | i.is_active = FALSE; 151 | i.balance = get_balance(itr->account); 152 | i.updated_at = now(); 153 | }); 154 | } 155 | } 156 | out.send((uint128_t(CODE_ACCOUNT.value) << 64) | current_time(), CODE_ACCOUNT, true); 157 | } 158 | 159 | //get min paid creditor balance 160 | uint64_t get_min_paid_creditor_balance() 161 | { 162 | 163 | uint64_t balance = 10000 * 10000; // 10000 EOS 164 | plan_table p(CODE_ACCOUNT, CODE_ACCOUNT.value); 165 | eosio_assert(p.begin() != p.end(), "plan table is empty!"); 166 | auto itr = p.begin(); 167 | while (itr != p.end()) 168 | { 169 | auto required = itr->cpu.amount + itr->net.amount; 170 | if (itr->is_free == false && itr->is_active && required < balance) { 171 | balance = required; 172 | } 173 | itr++; 174 | } 175 | return balance; 176 | } 177 | 178 | //check creditor enabled safedelegate or not 179 | bool is_safe_creditor(name creditor) 180 | { 181 | safecreditor_table s(CODE_ACCOUNT, SCOPE); 182 | auto itr = s.find(creditor.value); 183 | if(itr == s.end()){ 184 | return false; 185 | } else { 186 | return true; 187 | } 188 | } 189 | 190 | //rotate active creditor 191 | void rotate_creditor() 192 | { 193 | creditor_table c(CODE_ACCOUNT, SCOPE); 194 | auto free_creditor = get_active_creditor(TRUE); 195 | auto paid_creditor = get_active_creditor(FALSE); 196 | 197 | asset free_balance = get_balance(free_creditor); 198 | asset paid_balance = get_balance(paid_creditor); 199 | uint64_t min_paid_creditor_balance = get_min_paid_creditor_balance(); 200 | auto free_rotated = free_balance.amount > MIN_FREE_CREDITOR_BALANCE ?TRUE:FALSE; 201 | auto paid_rotated = paid_balance.amount > min_paid_creditor_balance ?TRUE:FALSE; 202 | auto idx = c.get_index<"updated.at"_n>(); 203 | auto itr = idx.begin(); 204 | while (itr != idx.end()) 205 | { 206 | if(itr->for_free == TRUE) 207 | { 208 | if(free_rotated == TRUE){itr++;continue;} 209 | auto balance = get_balance(itr->account); 210 | if (itr->account != free_creditor && balance.amount > MIN_FREE_CREDITOR_BALANCE) 211 | { 212 | activate_creditor(itr->account); 213 | free_rotated = TRUE; 214 | } 215 | itr++; 216 | } 217 | else 218 | { 219 | if(paid_rotated == TRUE){itr++;continue;} 220 | auto balance = get_balance(itr->account); 221 | if (itr->account != paid_creditor && balance.amount > min_paid_creditor_balance) 222 | { 223 | activate_creditor(itr->account); 224 | paid_rotated = TRUE; 225 | } 226 | itr++; 227 | } 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/validation.cpp: -------------------------------------------------------------------------------- 1 | using namespace eosio; 2 | using namespace eosiosystem; 3 | using namespace bank; 4 | using namespace utils; 5 | 6 | namespace validation 7 | { 8 | // check freelock 9 | void validate_freelock(name beneficiary) 10 | { 11 | freelock_table f(CODE_ACCOUNT, SCOPE); 12 | auto itr = f.find(beneficiary.value); 13 | eosio_assert(itr == f.end(), "free plan is avaliable every 24 hours for each beneficiary"); 14 | } 15 | 16 | // check blacklist 17 | void validate_blacklist(name account) 18 | { 19 | blacklist_table b(CODE_ACCOUNT, SCOPE); 20 | auto itr = b.find(account.value); 21 | eosio_assert(itr == b.end(), "something wrong with your account"); 22 | } 23 | 24 | //get BUYER's free order amount limit 25 | uint64_t get_free_order_cap(name buyer) 26 | { 27 | uint64_t max_orders = MAX_FREE_ORDERS; 28 | whitelist_table w(CODE_ACCOUNT, SCOPE); 29 | auto itr = w.find(buyer.value); 30 | if(itr != w.end()) 31 | { 32 | max_orders = itr->capacity; 33 | } 34 | return max_orders; 35 | } 36 | 37 | //make sure BUYER's affective records is no more than get_free_order_cap(BUYER) 38 | void validate_buyer(name buyer, uint64_t is_free) 39 | { 40 | eosio_assert(buyer != CODE_ACCOUNT, "buyer cannot be bankofstaked"); 41 | 42 | //validate blacklist 43 | validate_blacklist(buyer); 44 | 45 | // for paid orders, check MAX_PAID_ORDERS 46 | // for free orders, check get_free_order_cap() 47 | uint64_t max_orders = MAX_PAID_ORDERS; 48 | if(is_free == TRUE) { 49 | max_orders = get_free_order_cap(buyer); 50 | } 51 | std::string suffix = " affective orders at most for each buyer"; 52 | std::string error_msg = std::to_string(max_orders) + suffix; 53 | 54 | order_table o(CODE_ACCOUNT, SCOPE); 55 | auto idx = o.get_index<"buyer"_n>(); 56 | auto first = idx.lower_bound(buyer.value); 57 | auto last = idx.upper_bound(buyer.value); 58 | uint64_t count = 0; 59 | while(first != last && first != idx.end()) 60 | { 61 | if(first->is_free == is_free) 62 | { 63 | count += 1; 64 | } 65 | first++; 66 | } 67 | eosio_assert(count < max_orders, error_msg.c_str()); 68 | } 69 | 70 | //make sure BENEFICIARY's affective free orders is no more than MAX_FREE_ORDERS 71 | void validate_beneficiary(name beneficiary, name creditor, uint64_t is_free) 72 | { 73 | eosio_assert(beneficiary != CODE_ACCOUNT, "cannot delegate to bankofstaked"); 74 | eosio_assert(beneficiary != creditor, "cannot delegate to creditor"); 75 | 76 | //validate blacklist 77 | validate_blacklist(beneficiary); 78 | 79 | // for paid orders, check MAX_PAID_ORDERS 80 | // for free orders, check MAX_FREE_ORDERS 81 | uint64_t max_orders = MAX_PAID_ORDERS; 82 | if(is_free == TRUE) { 83 | max_orders = MAX_FREE_ORDERS; 84 | } 85 | 86 | //make sure the account has less than MAX_BALANCE EOS in balance 87 | //disabled temperarily because of account not found issue 88 | /* 89 | auto balance = get_balance(beneficiary); 90 | print("balance:", balance.amount); 91 | eosio_assert(balance.amount(); 96 | auto first = idx.lower_bound(beneficiary.value); 97 | auto last = idx.upper_bound(beneficiary.value); 98 | uint64_t count = 0; 99 | while(first != last && first != idx.end()) 100 | { 101 | if(first->is_free == is_free) 102 | { 103 | count += 1; 104 | } 105 | first++; 106 | } 107 | std::string suffix = " affective orders at most for each beneficiary"; 108 | std::string error_msg = std::to_string(max_orders) + suffix; 109 | eosio_assert(count < max_orders, error_msg.c_str()); 110 | } 111 | 112 | 113 | //validate Plan asset fields 114 | void validate_asset(asset price, 115 | asset cpu, 116 | asset net) 117 | { 118 | eosio_assert(price.is_valid(), "invalid price"); 119 | eosio_assert(cpu.is_valid(), "invalid cpu"); 120 | eosio_assert(net.is_valid(), "invalid net"); 121 | eosio_assert(price.amount >= 100 && price.amount <= 10000000, "price should between 0.01 EOS and 1000 EOS"); 122 | } 123 | 124 | //validate account exist in creditor table 125 | void validate_creditor(name creditor) 126 | { 127 | creditor_table c(CODE_ACCOUNT, SCOPE); 128 | auto itr = c.find(creditor.value); 129 | eosio_assert(itr != c.end(), "account does not exist in creditor table"); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /stats/fetch.py: -------------------------------------------------------------------------------- 1 | import pprint 2 | import time 3 | from eosapi import Client 4 | 5 | c = Client(nodes=['https://geo.eosasia.one']) 6 | 7 | def check_order(lower_bound=1): 8 | expired_orders = [] 9 | now = time.time() 10 | new_lower_bound = lower_bound 11 | r = c.get_table_rows(**{"code": "bankofstaked", "scope": "921459758687", "table": "order", "json": True, "limit": 100, "upper_bound": None, "lower_bound": lower_bound, "table_key": "id"}) 12 | more = r["more"] 13 | total_count = 0 14 | count = 0 15 | free_count = 0 16 | paid = [] 17 | for line in r["rows"]: 18 | total_count += 1 19 | if line["expire_at"] < now: 20 | expired_orders.append(line) 21 | count+=1 22 | #print(line) 23 | if line["is_free"]: 24 | #print("free id:", line["id"]) 25 | free_count += 1 26 | else: 27 | paid.append(line["id"]) 28 | if line["id"] > lower_bound: 29 | new_lower_bound = line["id"] 30 | print("total orders: %d" % total_count) 31 | print("expired orders: %d" % count) 32 | print("expired free orders: %d" % free_count) 33 | return more, new_lower_bound, expired_orders 34 | 35 | 36 | 37 | def fetch_creditors(): 38 | paid_accounts = [] 39 | free_accounts = [] 40 | r = c.get_table_rows(**{"code": "bankofstaked", "scope": "921459758687", "table": "creditor", "json": True, "limit": 1000, "upper_bound": None, "lower_bound": None, "table_key": "account_name"}) 41 | 42 | for a in r["rows"]: 43 | #print(a) 44 | if a["for_free"] == 1: 45 | free_accounts.append(a) 46 | else: 47 | paid_accounts.append(a) 48 | return free_accounts, paid_accounts 49 | 50 | 51 | def get_amount(asset): 52 | amount = float(asset.split(" ")[0]) 53 | return amount 54 | 55 | def get_account(a, free=True): 56 | r = c.get_account(a["account"]) 57 | ram_quota = r["ram_quota"] 58 | liquid_balance = get_amount(r["core_liquid_balance"]) 59 | balance = liquid_balance + get_amount(a["cpu_staked"]) + get_amount(a["net_staked"]) 60 | if r["self_delegated_bandwidth"]: 61 | self_delband = r["self_delegated_bandwidth"] 62 | balance += get_amount(self_delband["cpu_weight"]); 63 | balance += get_amount(self_delband["net_weight"]); 64 | if r["refund_request"]: 65 | refundings.append(r["refund_request"]) 66 | balance += get_amount(r["refund_request"]["cpu_amount"]); 67 | balance += get_amount(r["refund_request"]["net_amount"]); 68 | if free: 69 | ram_required = balance * 0.16 70 | else: 71 | ram_required = balance * 0.12 / 30. 72 | #print(r["account_name"], liquid_balance) 73 | row = " | ".join([r["account_name"], str("%.4f EOS" % liquid_balance), str("%.4f EOS" % balance), str("%.2f" % (ram_quota/1024.)), str("%.2f" % ram_required), "✅" if (ram_quota/1024. > ram_required) else "❌"]) 74 | row = "| %s |" % row 75 | print(row) 76 | return balance, liquid_balance 77 | 78 | 79 | if __name__ == "__main__": 80 | bps = set() 81 | def get_name(d): 82 | """ Return the value of a key in a dictionary. """ 83 | return d["expire_at"] 84 | 85 | expired = [] 86 | more, lower_bound, expired_orders = check_order() 87 | expired.extend(expired_orders) 88 | while more: 89 | more, lower_bound, expired_orders = check_order(lower_bound=lower_bound) 90 | expired.extend(expired_orders) 91 | 92 | expired.sort(key=get_name) 93 | paid_ids = set() 94 | ids = set() 95 | for e in expired: 96 | bps.add(e["creditor"]) 97 | 98 | if e["is_free"]: 99 | ids.add(" ".join([e["creditor"], str(e["id"])])) 100 | else: 101 | paid_ids.add(" ".join([e["creditor"], str(e["id"])])) 102 | ids = list(ids) 103 | paid_ids = list(paid_ids) 104 | ids.sort() 105 | paid_ids.sort() 106 | print("total expired ids:", len(ids), ids) 107 | print("paid expired ids:", len(paid_ids), paid_ids) 108 | f1 = open("expired_order_ids.txt", "w") 109 | f1.write("\n".join([str(i) for i in ids])) 110 | f2 = open("expired_paid_order_ids.txt", "w") 111 | f2.write("\n".join([str(i) for i in paid_ids])) 112 | refundings = [] 113 | free_accounts, paid_accounts = fetch_creditors() 114 | print("=================FREE ACCOUNTS==================") 115 | print("| Account | Liquid Balance | Total Balance | RAM Owned(kb) | RAM Requird(kb) | Enough RAM? |") 116 | print("| ------- | ------- | --------- | --------- | ----------- | ----------- |") 117 | for account in free_accounts: 118 | get_account(account) 119 | print("================================================\n\n") 120 | 121 | print("=================PAID ACCOUNTS==================") 122 | print("| Account | Liquid Balance | Total Balance | RAM Owned(kb) | RAM Requird(kb) | Enough RAM? |") 123 | print("| ------- | ------- | --------- | --------- | ----------- | ----------- |") 124 | total_balance = 0 125 | total_liquid_balance = 0 126 | for account in paid_accounts: 127 | balance, liquid_balance = get_account(account, False) 128 | total_balance += balance 129 | total_liquid_balance += liquid_balance 130 | print("total balance:", total_balance) 131 | print("total liquid balance:", total_liquid_balance) 132 | print("================================================") 133 | 134 | for r in refundings: 135 | print(r) 136 | -------------------------------------------------------------------------------- /stats/requirements.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4==4.7.1 2 | certifi==2018.10.15 3 | chardet==3.0.4 4 | eosapi==0.4 5 | funcy==1.11 6 | idna==2.7 7 | numpy==1.15.4 8 | pandas==0.23.4 9 | prettytable==0.7.2 10 | pyeos-client==0.1.9 11 | python-dateutil==2.7.5 12 | pytz==2018.7 13 | requests==2.19.1 14 | six==1.11.0 15 | soupsieve==1.9.1 16 | toolz==0.9.0 17 | urllib3==1.23 18 | xlrd==1.1.0 19 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(eosio_contracts VERSION 1.5.1) 3 | 4 | set(EOSIO_CDT_VERSION_MIN "1.4") 5 | set(EOSIO_CDT_VERSION_SOFT_MAX "1.4") 6 | #set(EOSIO_CDT_VERSION_HARD_MAX "") 7 | 8 | find_package(eosio.cdt) 9 | 10 | ### Check the version of eosio.cdt 11 | set(VERSION_MATCH_ERROR_MSG "") 12 | EOSIO_CHECK_VERSION(VERSION_OUTPUT "${EOSIO_CDT_VERSION}" 13 | "${EOSIO_CDT_VERSION_MIN}" 14 | "${EOSIO_CDT_VERSION_SOFT_MAX}" 15 | "${EOSIO_CDT_VERSION_HARD_MAX}" 16 | VERSION_MATCH_ERROR_MSG) 17 | if(VERSION_OUTPUT STREQUAL "MATCH") 18 | message(STATUS "Using eosio.cdt version ${EOSIO_CDT_VERSION}") 19 | elseif(VERSION_OUTPUT STREQUAL "WARN") 20 | message(WARNING "Using eosio.cdt version ${EOSIO_CDT_VERSION} even though it exceeds the maximum supported version of ${EOSIO_CDT_VERSION_SOFT_MAX}; continuing with configuration, however build may fail.\nIt is recommended to use eosio.cdt version ${EOSIO_CDT_VERSION_SOFT_MAX}.x") 21 | else() # INVALID OR MISMATCH 22 | message(FATAL_ERROR "Found eosio.cdt version ${EOSIO_CDT_VERSION} but it does not satisfy version requirements: ${VERSION_MATCH_ERROR_MSG}\nPlease use eosio.cdt version ${EOSIO_CDT_VERSION_SOFT_MAX}.x") 23 | endif(VERSION_OUTPUT STREQUAL "MATCH") 24 | 25 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 26 | set(TEST_BUILD_TYPE "Debug") 27 | set(CMAKE_BUILD_TYPE "Release") 28 | else() 29 | set(TEST_BUILD_TYPE ${CMAKE_BUILD_TYPE}) 30 | endif() 31 | 32 | 33 | if (APPLE) 34 | set(OPENSSL_ROOT "/usr/local/opt/openssl") 35 | elseif (UNIX) 36 | set(OPENSSL_ROOT "/usr/include/openssl") 37 | endif() 38 | set(SECP256K1_ROOT "/usr/local") 39 | 40 | include(UnitTestsExternalProject.txt) 41 | -------------------------------------------------------------------------------- /tests/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, Respective Authors all rights reserved. 2 | 3 | The MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | # BankOfStaked 2 | 3 | How to run unit test. 4 | 5 | * ./build.sh 6 | * ./build/tests/unit_test 7 | -------------------------------------------------------------------------------- /tests/UnitTestsExternalProject.txt: -------------------------------------------------------------------------------- 1 | include(ExternalProject) 2 | find_package(Git REQUIRED) 3 | include(GNUInstallDirs) 4 | 5 | string(REPLACE ";" "|" TEST_FRAMEWORK_PATH "${CMAKE_FRAMEWORK_PATH}") 6 | string(REPLACE ";" "|" TEST_MODULE_PATH "${CMAKE_MODULE_PATH}") 7 | 8 | ExternalProject_Add( 9 | contracts_unit_tests 10 | LIST_SEPARATOR | # Use the alternate list separator 11 | CMAKE_ARGS -DCMAKE_BUILD_TYPE=${TEST_BUILD_TYPE} -DCMAKE_FRAMEWORK_PATH=${TEST_FRAMEWORK_PATH} -DCMAKE_MODULE_PATH=${TEST_MODULE_PATH} -DEOSIO_ROOT=${EOSIO_ROOT} -DLLVM_DIR=${LLVM_DIR} 12 | SOURCE_DIR ${CMAKE_SOURCE_DIR}/tests 13 | BINARY_DIR ${CMAKE_BINARY_DIR}/tests 14 | BUILD_ALWAYS 1 15 | TEST_COMMAND "" 16 | INSTALL_COMMAND "" 17 | ) 18 | -------------------------------------------------------------------------------- /tests/build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | printf "\t=========== Building eosio.contracts ===========\n\n" 4 | 5 | RED='\033[0;31m' 6 | NC='\033[0m' 7 | 8 | CORES=`getconf _NPROCESSORS_ONLN` 9 | mkdir -p build 10 | pushd build &> /dev/null 11 | cmake ../ 12 | make -j${CORES} 13 | popd &> /dev/null 14 | -------------------------------------------------------------------------------- /tests/clean-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -rf ./build 3 | rm -rf ./tests/build -------------------------------------------------------------------------------- /tests/faketransfer/build.sh: -------------------------------------------------------------------------------- 1 | docker cp ../faketransfer nodeosd:/ 2 | -------------------------------------------------------------------------------- /tests/faketransfer/faketransfer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | namespace play 5 | { 6 | 7 | using namespace std; 8 | using namespace eosio; 9 | 10 | class[[eosio::contract("faketransfer")]] faketransfer : public contract 11 | { 12 | public: 13 | using contract::contract; 14 | 15 | [[eosio::action]] void transfer(name from, name to, asset quantity, string memo) { 16 | require_recipient("bankofstaked"_n); 17 | } 18 | 19 | }; 20 | 21 | } // namespace play 22 | 23 | extern "C" { 24 | void apply( uint64_t receiver, uint64_t code, uint64_t action ) { 25 | switch( action ) { 26 | EOSIO_DISPATCH_HELPER( play::faketransfer, (transfer) ) 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required( VERSION 3.5 ) 2 | 3 | set(EOSIO_VERSION_MIN "1.4") 4 | set(EOSIO_VERSION_SOFT_MAX "1.4") 5 | #set(EOSIO_VERSION_HARD_MAX "") 6 | 7 | find_package(eosio) 8 | 9 | ### Check the version of eosio 10 | set(VERSION_MATCH_ERROR_MSG "") 11 | EOSIO_CHECK_VERSION(VERSION_OUTPUT "${EOSIO_VERSION}" 12 | "${EOSIO_VERSION_MIN}" 13 | "${EOSIO_VERSION_SOFT_MAX}" 14 | "${EOSIO_VERSION_HARD_MAX}" 15 | VERSION_MATCH_ERROR_MSG) 16 | if(VERSION_OUTPUT STREQUAL "MATCH") 17 | message(STATUS "Using eosio version ${EOSIO_VERSION}") 18 | elseif(VERSION_OUTPUT STREQUAL "WARN") 19 | message(WARNING "Using eosio version ${EOSIO_VERSION} even though it exceeds the maximum supported version of ${EOSIO_VERSION_SOFT_MAX}; continuing with configuration, however build may fail.\nIt is recommended to use eosio version ${EOSIO_VERSION_SOFT_MAX}.x") 20 | else() # INVALID OR MISMATCH 21 | message(FATAL_ERROR "Found eosio version ${EOSIO_VERSION} but it does not satisfy version requirements: ${VERSION_MATCH_ERROR_MSG}\nPlease use eosio version ${EOSIO_VERSION_SOFT_MAX}.x") 22 | endif(VERSION_OUTPUT STREQUAL "MATCH") 23 | 24 | 25 | enable_testing() 26 | 27 | configure_file(${CMAKE_SOURCE_DIR}/contracts.hpp.in ${CMAKE_BINARY_DIR}/contracts.hpp) 28 | 29 | include_directories(${CMAKE_BINARY_DIR}) 30 | 31 | file(GLOB UNIT_TESTS "*.cpp" "*.hpp") 32 | 33 | add_eosio_test( unit_test ${UNIT_TESTS} ) 34 | -------------------------------------------------------------------------------- /tests/tests/bankofstaked_tester.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "contracts.hpp" 5 | #include "eosio.system_tester.hpp" 6 | 7 | #include "Runtime/Runtime.h" 8 | 9 | #include 10 | 11 | using namespace eosio::testing; 12 | using namespace eosio; 13 | using namespace eosio::chain; 14 | using namespace eosio::testing; 15 | using namespace fc; 16 | using namespace std; 17 | using namespace eosio_system; 18 | 19 | using mvo = fc::mutable_variant_object; 20 | 21 | class bankofstaked_tester : public eosio_system_tester 22 | { 23 | public: 24 | bankofstaked_tester() 25 | { 26 | produce_blocks(); 27 | 28 | eosio_system_tester::create_account_with_resources( N(bankofstaked), config::system_account_name, core_sym::from_string("100.0000"), false ); 29 | eosio_system_tester::create_account_with_resources( N(masktransfer), config::system_account_name, core_sym::from_string("10.0000"), false ); 30 | eosio_system_tester::create_account_with_resources( N(stakedincome), config::system_account_name, core_sym::from_string("10.0000"), false ); 31 | 32 | eosio_system_tester::create_account_with_resources( 33 | N(alice.111111), 34 | config::system_account_name, 35 | core_sym::from_string("10.0000"), 36 | false, 37 | core_sym::from_string("10.0000"), 38 | core_sym::from_string("10.0000") 39 | ); 40 | eosio_system_tester::create_account_with_resources( 41 | N(bob.11111111), 42 | config::system_account_name, 43 | core_sym::from_string("10.0000"), 44 | false, 45 | core_sym::from_string("10.0000"), 46 | core_sym::from_string("10.0000") 47 | ); 48 | eosio_system_tester::create_account_with_resources( 49 | N(carol.111111), 50 | config::system_account_name, 51 | core_sym::from_string("10.0000"), 52 | false, 53 | core_sym::from_string("10.0000"), 54 | core_sym::from_string("10.0000") 55 | ); 56 | eosio_system_tester::create_account_with_resources( 57 | N(ted.11111111), 58 | config::system_account_name, 59 | core_sym::from_string("10.0000"), 60 | false, 61 | core_sym::from_string("10.0000"), 62 | core_sym::from_string("10.0000") 63 | ); 64 | eosio_system_tester::create_account_with_resources( 65 | N(carol.222222), 66 | config::system_account_name, 67 | core_sym::from_string("10.0000"), 68 | false, 69 | core_sym::from_string("10.0000"), 70 | core_sym::from_string("10.0000") 71 | ); 72 | 73 | eosio_system_tester::issue(N(bankofstaked), core_sym::from_string("50.0000")); 74 | eosio_system_tester::issue(N(alice.111111), core_sym::from_string("500.0000")); 75 | eosio_system_tester::issue(N(bob.11111111), core_sym::from_string("5000.0000")); 76 | eosio_system_tester::issue(N(ted.11111111), core_sym::from_string("6000.0000")); 77 | eosio_system_tester::issue(N(carol.111111), core_sym::from_string("50000.0000")); 78 | eosio_system_tester::issue(N(carol.222222), core_sym::from_string("60000.0000")); 79 | 80 | produce_blocks(); 81 | 82 | authority auth; 83 | fc::from_variant( 84 | fc::json::from_string(R"({"threshold": 1,"keys":[],"accounts": [{"permission":{"actor":"bankofstaked","permission":"eosio.code"},"weight":1}], "waits":[]})"), 85 | auth 86 | ); 87 | 88 | set_authority(N(bankofstaked), N(bankperm), auth, config::active_name); 89 | link_authority(N(bankofstaked), N(eosio.token), N(bankperm), N(transfer)); 90 | link_authority(N(bankofstaked), N(eosio), N(bankperm), N(delegatebw)); 91 | link_authority(N(bankofstaked), N(eosio), N(bankperm), N(undelegatebw)); 92 | link_authority(N(bankofstaked), N(bankofstaked), N(bankperm), N(expireorder)); 93 | link_authority(N(bankofstaked), N(bankofstaked), N(bankperm), N(check)); 94 | link_authority(N(bankofstaked), N(bankofstaked), N(bankperm), N(rotate)); 95 | 96 | set_authority(N(bob.11111111), N(creditorperm), auth, config::active_name); 97 | set_authority(N(carol.111111), N(creditorperm), auth, config::active_name); 98 | 99 | link_authority(N(bob.11111111), N(eosio), N(creditorperm), N(delegatebw)); 100 | link_authority(N(carol.111111), N(eosio), N(creditorperm), N(delegatebw)); 101 | link_authority(N(bob.11111111), N(eosio), N(creditorperm), N(undelegatebw)); 102 | link_authority(N(carol.111111), N(eosio), N(creditorperm), N(undelegatebw)); 103 | 104 | produce_blocks(); 105 | 106 | set_code(N(bankofstaked), contracts::bank_wasm()); 107 | set_abi(N(bankofstaked), contracts::bank_abi().data()); 108 | produce_blocks(); 109 | 110 | eosio_system_tester::cross_15_percent_threshold(); 111 | 112 | const auto &accnt = control->db().get(N(bankofstaked)); 113 | abi_def bank_abi; 114 | BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, bank_abi), true); 115 | bank_abi_ser.set_abi(bank_abi, abi_serializer_max_time); 116 | } 117 | 118 | transaction_trace_ptr push_action(const account_name &signer, const action_name &name, const variant_object &data, bool auth = true) 119 | { 120 | vector accounts; 121 | if (auth) 122 | accounts.push_back(signer); 123 | auto trace = base_tester::push_action(N(bankofstaked), name, accounts, data); 124 | produce_block(); 125 | BOOST_REQUIRE_EQUAL(true, chain_has_transaction(trace->id)); 126 | return trace; 127 | } 128 | 129 | fc::variant get_creditor(const account_name &act) 130 | { 131 | vector data = get_row_by_account(N(bankofstaked), 921459758687, N(creditor), act); 132 | return data.empty() ? EMPTY : bank_abi_ser.binary_to_variant("creditor", data, abi_serializer_max_time); 133 | } 134 | 135 | fc::variant get_safecreditor(const account_name &act) 136 | { 137 | vector data = get_row_by_account(N(bankofstaked), 921459758687, N(safecreditor), act); 138 | return data.empty() ? EMPTY : bank_abi_ser.binary_to_variant("safecreditor", data, abi_serializer_max_time); 139 | } 140 | 141 | fc::variant get_blacklist(const account_name &act) 142 | { 143 | vector data = get_row_by_account(N(bankofstaked), 921459758687, N(blacklist), act); 144 | return data.empty() ? EMPTY : bank_abi_ser.binary_to_variant("blacklist", data, abi_serializer_max_time); 145 | } 146 | 147 | fc::variant get_whitelist(const account_name &act) 148 | { 149 | vector data = get_row_by_account(N(bankofstaked), 921459758687, N(whitelist), act); 150 | return data.empty() ? EMPTY : bank_abi_ser.binary_to_variant("whitelist", data, abi_serializer_max_time); 151 | } 152 | 153 | fc::variant get_plan(const asset& price) 154 | { 155 | vector data = get_row_by_secondary_key(N(bankofstaked), N(bankofstaked), N(plan), (uint64_t)price.get_amount()); 156 | return data.empty() ? EMPTY : bank_abi_ser.binary_to_variant("plan", data, abi_serializer_max_time); 157 | } 158 | 159 | fc::variant get_order(account_name buyer) 160 | { 161 | vector data = get_row_by_secondary_key(N(bankofstaked), 921459758687, N(order), buyer.value); 162 | return data.empty() ? EMPTY : bank_abi_ser.binary_to_variant("order", data, abi_serializer_max_time); 163 | } 164 | 165 | vector get_row_by_secondary_key( uint64_t code, uint64_t scope, uint64_t table, uint64_t key ) const { 166 | vector data; 167 | const auto& db = control->db(); 168 | const auto* t_id = db.find( boost::make_tuple( code, scope, table ) ); 169 | if ( !t_id ) { 170 | return data; 171 | } 172 | 173 | const auto& idx = db.get_index().indices().get(); 174 | 175 | auto itr = idx.find( boost::make_tuple( t_id->id, key ) ); 176 | if (itr != idx.end()) { 177 | const auto *obj = db.find(boost::make_tuple(t_id->id, itr->primary_key)); 178 | if (obj) { 179 | data.resize( obj->value.size() ); 180 | memcpy( data.data(), obj->value.data(), data.size() ); 181 | } 182 | } 183 | 184 | return data; 185 | } 186 | 187 | // abi_serializer token_abi_ser; 188 | abi_serializer bank_abi_ser; 189 | fc::variant EMPTY = fc::variant(0).as_string().substr(0,8); 190 | }; 191 | -------------------------------------------------------------------------------- /tests/tests/bankofstaked_tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "contracts.hpp" 5 | #include "bankofstaked_tester.hpp" 6 | 7 | #include "Runtime/Runtime.h" 8 | 9 | #include 10 | 11 | using namespace eosio::testing; 12 | using namespace eosio; 13 | using namespace eosio::chain; 14 | using namespace eosio::testing; 15 | using namespace fc; 16 | using namespace std; 17 | 18 | using mvo = fc::mutable_variant_object; 19 | 20 | BOOST_AUTO_TEST_SUITE(bankofstaked_tests) 21 | 22 | // test action delcreditor 23 | BOOST_FIXTURE_TEST_CASE(rotate_test, bankofstaked_tester) 24 | try 25 | { 26 | // add 2 creditors, alice/bob 27 | push_action(N(bankofstaked), N(addcreditor), mvo()("account", "alice.111111")("for_free", 0)("free_memo", "")); 28 | push_action(N(bankofstaked), N(addcreditor), mvo()("account", "bob.11111111")("for_free", 0)("free_memo", "")); 29 | 30 | push_action(N(bankofstaked), N(rotate), mvo()("creditor", "bob.11111111")("for_free", 0)); 31 | 32 | // can't rotate non exist creditor 33 | BOOST_REQUIRE_EXCEPTION( push_action(N(bankofstaked), N(rotate), mvo()("creditor", "carol.111111")("for_free", 0)), 34 | eosio_assert_message_exception, 35 | eosio_assert_message_is("account does not exist in creditor table") 36 | ); 37 | } 38 | FC_LOG_AND_RETHROW() 39 | 40 | // test action addwhitelist 41 | BOOST_FIXTURE_TEST_CASE(addwhitelist_test, bankofstaked_tester) 42 | try 43 | { 44 | // add 2 whitelist account, alice/bob 45 | push_action(N(bankofstaked), N(addwhitelist), mvo()("account", "alice.111111")("capacity", 100)); 46 | push_action(N(bankofstaked), N(addwhitelist), mvo()("account", "bob.11111111")("capacity", 1000)); 47 | 48 | auto whitelist = get_whitelist("alice.111111"); 49 | REQUIRE_MATCHING_OBJECT(mvo() 50 | ("account", "alice.111111") 51 | ("capacity", 100), 52 | whitelist 53 | ); 54 | 55 | whitelist = get_whitelist("bob.11111111"); 56 | REQUIRE_MATCHING_OBJECT(mvo() 57 | ("account", "bob.11111111") 58 | ("capacity", 1000), 59 | whitelist 60 | ); 61 | 62 | // update whitelist 63 | push_action(N(bankofstaked), N(addwhitelist), mvo()("account", "alice.111111")("capacity", 500)); 64 | whitelist = get_whitelist("alice.111111"); 65 | REQUIRE_MATCHING_OBJECT(mvo() 66 | ("account", "alice.111111") 67 | ("capacity", 500), 68 | whitelist 69 | ); 70 | } 71 | FC_LOG_AND_RETHROW() 72 | 73 | 74 | BOOST_FIXTURE_TEST_CASE(delwhitelist_test, bankofstaked_tester) 75 | try 76 | { 77 | // add 2 whitelist account, alice/bob 78 | push_action(N(bankofstaked), N(addwhitelist), mvo()("account", "alice.111111")("capacity", 100)); 79 | 80 | auto whitelist = get_whitelist("alice.111111"); 81 | REQUIRE_MATCHING_OBJECT(mvo() 82 | ("account", "alice.111111") 83 | ("capacity", 100), 84 | whitelist 85 | ); 86 | 87 | push_action(N(bankofstaked), N(delwhitelist), mvo()("account", "alice.111111")("capacity", 100)); 88 | 89 | whitelist = get_blacklist("alice.111111"); 90 | BOOST_REQUIRE_EQUAL(whitelist, "0"); 91 | 92 | // can't delete non exist whitelist 93 | BOOST_REQUIRE_EXCEPTION( push_action(N(bankofstaked), N(delwhitelist), mvo() 94 | ("account", "bob.11111111") 95 | ("capacity", 100)), 96 | eosio_assert_message_exception, 97 | eosio_assert_message_is("account not found in whitelist table") 98 | ); 99 | 100 | } 101 | FC_LOG_AND_RETHROW() 102 | 103 | 104 | BOOST_FIXTURE_TEST_CASE(addcreditor_test, bankofstaked_tester) 105 | try 106 | { 107 | // add 3 creditors, alice/bob/carol 108 | push_action(N(bankofstaked), N(addcreditor), mvo()("account", "alice.111111")("for_free", 1)("free_memo", "lucky you!")); 109 | push_action(N(bankofstaked), N(addcreditor), mvo()("account", "bob.11111111")("for_free", 0)("free_memo", "hell yeah!")); 110 | push_action(N(bankofstaked), N(addcreditor), mvo()("account", "carol.111111")("for_free", 1)("free_memo", "oh")); 111 | 112 | auto creditor = get_creditor("alice.111111"); 113 | REQUIRE_MATCHING_OBJECT(mvo() 114 | ("is_active", 0) 115 | ("for_free", 1) 116 | ("free_memo", "lucky you!") 117 | ("account", "alice.111111") 118 | ("balance", "500.0000 EOS") 119 | ("cpu_staked", "0.0000 EOS") 120 | ("net_staked", "0.0000 EOS") 121 | ("cpu_unstaked", "0.0000 EOS") 122 | ("net_unstaked", "0.0000 EOS"), 123 | creditor 124 | ); 125 | 126 | creditor = get_creditor("bob.11111111"); 127 | REQUIRE_MATCHING_OBJECT(mvo() 128 | ("is_active", 0) 129 | ("for_free", 0) 130 | ("free_memo", "") 131 | ("account", "bob.11111111") 132 | ("balance", "5000.0000 EOS") 133 | ("cpu_staked", "0.0000 EOS") 134 | ("net_staked", "0.0000 EOS") 135 | ("cpu_unstaked", "0.0000 EOS") 136 | ("net_unstaked", "0.0000 EOS"), 137 | creditor 138 | ); 139 | 140 | creditor = get_creditor("carol.111111"); 141 | REQUIRE_MATCHING_OBJECT(mvo() 142 | ("is_active", 0) 143 | ("for_free", 1) 144 | ("free_memo", "oh") 145 | ("account", "carol.111111") 146 | ("balance", "50000.0000 EOS") 147 | ("cpu_staked", "0.0000 EOS") 148 | ("net_staked", "0.0000 EOS") 149 | ("cpu_unstaked", "0.0000 EOS") 150 | ("net_unstaked", "0.0000 EOS"), 151 | creditor 152 | ); 153 | 154 | BOOST_REQUIRE_EXCEPTION( push_action(N(bankofstaked), N(addcreditor), mvo() 155 | ("account", "carol.111111") 156 | ("for_free", 1) 157 | ("free_memo", "oh")), 158 | eosio_assert_message_exception, 159 | eosio_assert_message_is("account already exist in creditor table") 160 | ); 161 | } 162 | FC_LOG_AND_RETHROW() 163 | 164 | 165 | BOOST_FIXTURE_TEST_CASE(addsafeacnt_test, bankofstaked_tester) 166 | try 167 | { 168 | // add 3 creditors, alice/bob/carol 169 | push_action(N(bankofstaked), N(addcreditor), mvo()("account", "alice.111111")("for_free", 1)("free_memo", "lucky you!")); 170 | push_action(N(bankofstaked), N(addcreditor), mvo()("account", "bob.11111111")("for_free", 0)("free_memo", "hell yeah!")); 171 | 172 | auto creditor = get_creditor("alice.111111"); 173 | REQUIRE_MATCHING_OBJECT(mvo() 174 | ("is_active", 0) 175 | ("for_free", 1) 176 | ("free_memo", "lucky you!") 177 | ("account", "alice.111111") 178 | ("balance", "500.0000 EOS") 179 | ("cpu_staked", "0.0000 EOS") 180 | ("net_staked", "0.0000 EOS") 181 | ("cpu_unstaked", "0.0000 EOS") 182 | ("net_unstaked", "0.0000 EOS"), 183 | creditor 184 | ); 185 | 186 | creditor = get_creditor("bob.11111111"); 187 | REQUIRE_MATCHING_OBJECT(mvo() 188 | ("is_active", 0) 189 | ("for_free", 0) 190 | ("free_memo", "") 191 | ("account", "bob.11111111") 192 | ("balance", "5000.0000 EOS") 193 | ("cpu_staked", "0.0000 EOS") 194 | ("net_staked", "0.0000 EOS") 195 | ("cpu_unstaked", "0.0000 EOS") 196 | ("net_unstaked", "0.0000 EOS"), 197 | creditor 198 | ); 199 | 200 | push_action(N(bankofstaked), N(addsafeacnt), mvo()("account", "bob.11111111")); 201 | 202 | auto safecreditor = get_safecreditor("bob.11111111"); 203 | REQUIRE_MATCHING_OBJECT(mvo() 204 | ("account", "bob.11111111"), 205 | safecreditor 206 | ); 207 | 208 | safecreditor = get_safecreditor("alice.111111"); 209 | BOOST_REQUIRE_EQUAL(safecreditor, "0"); 210 | 211 | 212 | BOOST_REQUIRE_EXCEPTION( push_action(N(bankofstaked), N(addsafeacnt), mvo()("account", "carol.111111")), 213 | eosio_assert_message_exception, 214 | eosio_assert_message_is("account does not exist in creditor table") 215 | ); 216 | } 217 | FC_LOG_AND_RETHROW() 218 | 219 | 220 | BOOST_FIXTURE_TEST_CASE(delsafeacnt_test, bankofstaked_tester) 221 | try 222 | { 223 | push_action(N(bankofstaked), N(addcreditor), mvo()("account", "bob.11111111")("for_free", 0)("free_memo", "hell yeah!")); 224 | 225 | auto creditor = get_creditor("bob.11111111"); 226 | REQUIRE_MATCHING_OBJECT(mvo() 227 | ("is_active", 0) 228 | ("for_free", 0) 229 | ("free_memo", "") 230 | ("account", "bob.11111111") 231 | ("balance", "5000.0000 EOS") 232 | ("cpu_staked", "0.0000 EOS") 233 | ("net_staked", "0.0000 EOS") 234 | ("cpu_unstaked", "0.0000 EOS") 235 | ("net_unstaked", "0.0000 EOS"), 236 | creditor 237 | ); 238 | 239 | push_action(N(bankofstaked), N(addsafeacnt), mvo()("account", "bob.11111111")); 240 | 241 | auto safecreditor = get_safecreditor("bob.11111111"); 242 | REQUIRE_MATCHING_OBJECT(mvo() 243 | ("account", "bob.11111111"), 244 | safecreditor 245 | ); 246 | 247 | push_action(N(bankofstaked), N(delsafeacnt), mvo()("account", "bob.11111111")); 248 | safecreditor = get_safecreditor("bob.11111111"); 249 | BOOST_REQUIRE_EQUAL(safecreditor, "0"); 250 | 251 | 252 | BOOST_REQUIRE_EXCEPTION( push_action(N(bankofstaked), N(delsafeacnt), mvo()("account", "alice.111111")), 253 | eosio_assert_message_exception, 254 | eosio_assert_message_is("account does not exist in safecreditor table") 255 | ); 256 | } 257 | FC_LOG_AND_RETHROW() 258 | 259 | 260 | // test action delcreditor 261 | BOOST_FIXTURE_TEST_CASE(delcreditor_test, bankofstaked_tester) 262 | try 263 | { 264 | // add 2 creditors, alice/bob 265 | push_action(N(bankofstaked), N(addcreditor), mvo()("account", "alice.111111")("for_free", 0)("free_memo", "")); 266 | push_action(N(bankofstaked), N(addcreditor), mvo()("account", "bob.11111111")("for_free", 0)("free_memo", "")); 267 | 268 | auto creditor = get_creditor("bob.11111111"); 269 | REQUIRE_MATCHING_OBJECT(mvo() 270 | ("is_active", 0) 271 | ("account", "bob.11111111") 272 | ("balance", "5000.0000 EOS") 273 | ("cpu_staked", "0.0000 EOS") 274 | ("net_staked", "0.0000 EOS") 275 | ("cpu_unstaked", "0.0000 EOS") 276 | ("net_unstaked", "0.0000 EOS"), 277 | creditor 278 | ); 279 | 280 | // del creditor bob 281 | push_action(N(bankofstaked), N(delcreditor), mvo()("account", "bob.11111111")); 282 | //after deletion, bob should be EMPTY 283 | creditor = get_creditor("bob.11111111"); 284 | BOOST_REQUIRE_EQUAL(creditor, "0"); 285 | 286 | // can't delete non exist creditor 287 | BOOST_REQUIRE_EXCEPTION( push_action(N(bankofstaked), N(delcreditor), mvo()("account", "carol.111111")), 288 | eosio_assert_message_exception, 289 | eosio_assert_message_is("account not found in creditor table") 290 | ); 291 | 292 | // can't delete active creditor 293 | push_action(N(bankofstaked), N(activate), mvo()("account", "alice.111111")); 294 | 295 | creditor = get_creditor("alice.111111"); 296 | REQUIRE_MATCHING_OBJECT(mvo() 297 | ("is_active", 1) 298 | ("account", "alice.111111") 299 | ("balance", "500.0000 EOS") 300 | ("cpu_staked", "0.0000 EOS") 301 | ("net_staked", "0.0000 EOS") 302 | ("cpu_unstaked", "0.0000 EOS") 303 | ("net_unstaked", "0.0000 EOS"), 304 | creditor 305 | ); 306 | 307 | BOOST_REQUIRE_EXCEPTION( push_action(N(bankofstaked), N(delcreditor), mvo()("account", "alice.111111")), 308 | eosio_assert_message_exception, 309 | eosio_assert_message_is("cannot delete active creditor") 310 | ); 311 | } 312 | FC_LOG_AND_RETHROW() 313 | 314 | 315 | // test action addblacklist 316 | BOOST_FIXTURE_TEST_CASE(addblacklist_test, bankofstaked_tester) 317 | try 318 | { 319 | // add 2 accounts to blacklist table, alice/bob 320 | push_action(N(bankofstaked), N(addblacklist), mvo()("account", "alice.111111")); 321 | push_action(N(bankofstaked), N(addblacklist), mvo()("account", "bob.11111111")); 322 | 323 | auto blacklist = get_blacklist("alice.111111"); 324 | BOOST_REQUIRE_EQUAL(blacklist["account"], "alice.111111"); 325 | blacklist = get_blacklist("bob.11111111"); 326 | BOOST_REQUIRE_EQUAL(blacklist["account"], "bob.11111111"); 327 | 328 | 329 | BOOST_REQUIRE_EXCEPTION( push_action(N(bankofstaked), N(addblacklist), mvo()("account", "bob.11111111")), 330 | eosio_assert_message_exception, 331 | eosio_assert_message_is("account already exist in blacklist table") 332 | ); 333 | } 334 | FC_LOG_AND_RETHROW() 335 | 336 | 337 | // test action delblacklist 338 | BOOST_FIXTURE_TEST_CASE(delblacklist_test, bankofstaked_tester) 339 | try 340 | { 341 | // add 2 accounts to blacklist table, alice/bob 342 | push_action(N(bankofstaked), N(addblacklist), mvo()("account", "alice.111111")); 343 | push_action(N(bankofstaked), N(addblacklist), mvo()("account", "bob.11111111")); 344 | 345 | auto blacklist = get_blacklist("alice.111111"); 346 | BOOST_REQUIRE_EQUAL(blacklist["account"], "alice.111111"); 347 | blacklist = get_blacklist("bob.11111111"); 348 | BOOST_REQUIRE_EQUAL(blacklist["account"], "bob.11111111"); 349 | 350 | // del blacklist bob 351 | push_action(N(bankofstaked), N(delblacklist), mvo()("account", "bob.11111111")); 352 | blacklist = get_blacklist("alice.111111"); 353 | BOOST_REQUIRE_EQUAL(blacklist["account"], "alice.111111"); 354 | //after deletion, bob should be EMPTY 355 | blacklist = get_blacklist("bob.11111111"); 356 | BOOST_REQUIRE_EQUAL(blacklist, "0"); 357 | 358 | BOOST_REQUIRE_EXCEPTION( push_action(N(bankofstaked), N(delblacklist), mvo()("account", "carol.111111")), 359 | eosio_assert_message_exception, 360 | eosio_assert_message_is("account not found in blacklist table") 361 | ); 362 | } 363 | FC_LOG_AND_RETHROW() 364 | 365 | 366 | BOOST_FIXTURE_TEST_CASE(activate_test, bankofstaked_tester) 367 | try 368 | { 369 | // add 3 creditors, alice/bob/carol 370 | push_action(N(bankofstaked), N(addcreditor), mvo()("account", "alice.111111")("for_free", 0)("free_memo", "")); 371 | push_action(N(bankofstaked), N(addcreditor), mvo()("account", "bob.11111111")("for_free", 0)("free_memo", "")); 372 | 373 | auto creditor = get_creditor("alice.111111"); 374 | BOOST_REQUIRE_EQUAL(creditor["is_active"], 0); 375 | creditor = get_creditor("bob.11111111"); 376 | BOOST_REQUIRE_EQUAL(creditor["is_active"], 0); 377 | 378 | //set bob as active creditor 379 | push_action(N(bankofstaked), N(activate), mvo()("account", "bob.11111111")); 380 | creditor = get_creditor("alice.111111"); 381 | BOOST_REQUIRE_EQUAL(creditor["is_active"], 0); 382 | creditor = get_creditor("bob.11111111"); 383 | BOOST_REQUIRE_EQUAL(creditor["is_active"], 1); 384 | 385 | 386 | BOOST_REQUIRE_EXCEPTION( push_action(N(bankofstaked), N(activate), mvo()("account", "carol.111111")), 387 | eosio_assert_message_exception, 388 | eosio_assert_message_is("account not found in creditor table") 389 | ); 390 | } 391 | FC_LOG_AND_RETHROW() 392 | 393 | 394 | BOOST_FIXTURE_TEST_CASE(setplan_test, bankofstaked_tester) 395 | try 396 | { 397 | push_action(N(bankofstaked), N(setplan), mvo() 398 | ("price", "0.1000 EOS") 399 | ("cpu", "1.0000 EOS") 400 | ("net", "1.0000 EOS") 401 | ("duration", 300) 402 | ("is_free", 1)); 403 | auto plan = get_plan(asset::from_string("0.1000 EOS")); 404 | REQUIRE_MATCHING_OBJECT(mvo() 405 | ("price", "0.1000 EOS") 406 | ("cpu", "1.0000 EOS") 407 | ("net", "1.0000 EOS") 408 | ("duration", 300) 409 | ("is_free", 1), 410 | plan 411 | ); 412 | 413 | push_action(N(bankofstaked), N(setplan), mvo() 414 | ("price", "0.1000 EOS") 415 | ("cpu", "5.0000 EOS") 416 | ("net", "5.0000 EOS") 417 | ("duration", 400) 418 | ("is_free", 0)); 419 | plan = get_plan(asset::from_string("0.1000 EOS")); 420 | REQUIRE_MATCHING_OBJECT(mvo() 421 | ("price", "0.1000 EOS") 422 | ("cpu", "5.0000 EOS") 423 | ("net", "5.0000 EOS") 424 | ("duration", 400) 425 | ("is_free", 0), 426 | plan 427 | ); 428 | 429 | BOOST_REQUIRE_EXCEPTION( push_action( N(bankofstaked), N(setplan), mvo() 430 | ("price", "0.0099 EOS") 431 | ("cpu", "5.0000 EOS") 432 | ("net", "5.0000 EOS") 433 | ("duration", 400) 434 | ("is_free", 0) 435 | ), 436 | eosio_assert_message_exception, 437 | eosio_assert_message_is("price should between 0.01 EOS and 1000 EOS") 438 | ); 439 | 440 | BOOST_REQUIRE_EXCEPTION( push_action( N(bankofstaked), N(setplan), mvo() 441 | ("price", "1000.0001 EOS") 442 | ("cpu", "5.0000 EOS") 443 | ("net", "5.0000 EOS") 444 | ("duration", 400) 445 | ("is_free", 0) 446 | ), 447 | eosio_assert_message_exception, 448 | eosio_assert_message_is("price should between 0.01 EOS and 1000 EOS") 449 | ); 450 | } 451 | FC_LOG_AND_RETHROW() 452 | 453 | BOOST_FIXTURE_TEST_CASE(activateplan_test, bankofstaked_tester) 454 | try 455 | { 456 | push_action(N(bankofstaked), N(setplan), mvo() 457 | ("price", "0.1000 EOS") 458 | ("cpu", "1.0000 EOS") 459 | ("net", "1.0000 EOS") 460 | ("duration", 300) 461 | ("is_free", 1)); 462 | auto plan = get_plan(asset::from_string("0.1000 EOS")); 463 | REQUIRE_MATCHING_OBJECT(mvo() 464 | ("price", "0.1000 EOS") 465 | ("cpu", "1.0000 EOS") 466 | ("net", "1.0000 EOS") 467 | ("is_active", 0) 468 | ("duration", 300) 469 | ("is_free", 1), 470 | plan 471 | ); 472 | 473 | push_action(N(bankofstaked), N(activateplan), mvo() 474 | ("price", "0.1000 EOS") 475 | ("is_active", 1)); 476 | plan = get_plan(asset::from_string("0.1000 EOS")); 477 | REQUIRE_MATCHING_OBJECT(mvo() 478 | ("price", "0.1000 EOS") 479 | ("cpu", "1.0000 EOS") 480 | ("net", "1.0000 EOS") 481 | ("is_active", 1) 482 | ("duration", 300) 483 | ("is_free", 1), 484 | plan 485 | ); 486 | } 487 | FC_LOG_AND_RETHROW() 488 | 489 | BOOST_FIXTURE_TEST_CASE(buy_non_free_plan_test, bankofstaked_tester) 490 | try 491 | { 492 | push_action(N(bankofstaked), N(setplan), mvo() 493 | ("price", "0.1000 EOS") 494 | ("cpu", "1.0000 EOS") 495 | ("net", "1.0000 EOS") 496 | ("duration", 1) 497 | ("is_free", 1)); 498 | push_action(N(bankofstaked), N(setplan), mvo() 499 | ("price", "1.0000 EOS") 500 | ("cpu", "10.0000 EOS") 501 | ("net", "10.0000 EOS") 502 | ("duration", 2) 503 | ("is_free", 0)); 504 | 505 | push_action(N(bankofstaked), N(addcreditor), mvo()("account", "bob.11111111")("for_free", 1)("free_memo", "hell yeah!")); 506 | push_action(N(bankofstaked), N(addcreditor), mvo()("account", "carol.111111")("for_free", 0)("free_memo", "should be ignored")); 507 | 508 | push_action(N(bankofstaked), N(activateplan), mvo() 509 | ("price", "0.1000 EOS") 510 | ("is_active", 1)); 511 | push_action(N(bankofstaked), N(activateplan), mvo() 512 | ("price", "1.0000 EOS") 513 | ("is_active", 1)); 514 | 515 | push_action(N(bankofstaked), N(activate), mvo()("account", "bob.11111111")); 516 | push_action(N(bankofstaked), N(activate), mvo()("account", "carol.111111")); 517 | produce_blocks(); 518 | 519 | eosio_system_tester::transfer(N(alice.111111), N(bankofstaked), core_sym::from_string("1.0000"), N(alice.111111)); 520 | produce_blocks(); 521 | 522 | auto creditor = get_creditor("carol.111111"); 523 | REQUIRE_MATCHING_OBJECT(mvo() 524 | ("is_active", 1) 525 | ("for_free", 0) 526 | ("free_memo", "") 527 | ("account", "carol.111111") 528 | ("balance", "50000.0000 EOS") 529 | ("cpu_staked", "10.0000 EOS") 530 | ("net_staked", "10.0000 EOS") 531 | ("cpu_unstaked", "0.0000 EOS") 532 | ("net_unstaked", "0.0000 EOS"), 533 | creditor 534 | ); 535 | 536 | auto order = get_order("alice.111111"); 537 | REQUIRE_MATCHING_OBJECT(mvo() 538 | ("buyer", "alice.111111") 539 | ("price", "1.0000 EOS") 540 | ("is_free", 0) 541 | ("creditor", "carol.111111") 542 | ("beneficiary", "alice.111111") 543 | ("cpu_staked", "10.0000 EOS") 544 | ("net_staked", "10.0000 EOS"), 545 | order 546 | ); 547 | 548 | // after 2 minutes 549 | produce_blocks(240); 550 | 551 | creditor = get_creditor("carol.111111"); 552 | REQUIRE_MATCHING_OBJECT(mvo() 553 | ("is_active", 1) 554 | ("for_free", 0) 555 | ("free_memo", "") 556 | ("account", "carol.111111") 557 | ("balance", "49980.0000 EOS") 558 | ("cpu_staked", "0.0000 EOS") 559 | ("net_staked", "0.0000 EOS") 560 | ("cpu_unstaked", "10.0000 EOS") 561 | ("net_unstaked", "10.0000 EOS"), 562 | creditor 563 | ); 564 | 565 | BOOST_REQUIRE_EQUAL(get_balance(N(masktransfer)), asset::from_string("1.0000 EOS")); 566 | } 567 | FC_LOG_AND_RETHROW() 568 | 569 | BOOST_FIXTURE_TEST_CASE(buy_free_plan_test, bankofstaked_tester) 570 | try 571 | { 572 | push_action(N(bankofstaked), N(setplan), mvo() 573 | ("price", "0.1000 EOS") 574 | ("cpu", "1.0000 EOS") 575 | ("net", "1.0000 EOS") 576 | ("duration", 1) 577 | ("is_free", 1)); 578 | push_action(N(bankofstaked), N(setplan), mvo() 579 | ("price", "1.0000 EOS") 580 | ("cpu", "10.0000 EOS") 581 | ("net", "10.0000 EOS") 582 | ("duration", 2) 583 | ("is_free", 0)); 584 | 585 | push_action(N(bankofstaked), N(addcreditor), mvo()("account", "bob.11111111")("for_free", 1)("free_memo", "hell yeah!")); 586 | push_action(N(bankofstaked), N(addcreditor), mvo()("account", "carol.111111")("for_free", 0)("free_memo", "should be ignored")); 587 | 588 | push_action(N(bankofstaked), N(activateplan), mvo() 589 | ("price", "0.1000 EOS") 590 | ("is_active", 1)); 591 | push_action(N(bankofstaked), N(activateplan), mvo() 592 | ("price", "1.0000 EOS") 593 | ("is_active", 1)); 594 | 595 | push_action(N(bankofstaked), N(activate), mvo()("account", "bob.11111111")); 596 | push_action(N(bankofstaked), N(activate), mvo()("account", "carol.111111")); 597 | produce_blocks(); 598 | 599 | eosio_system_tester::transfer(N(alice.111111), N(bankofstaked), core_sym::from_string("0.1000"), N(alice.111111)); 600 | produce_blocks(); 601 | 602 | auto creditor = get_creditor("bob.11111111"); 603 | REQUIRE_MATCHING_OBJECT(mvo() 604 | ("is_active", 1) 605 | ("for_free", 1) 606 | ("free_memo", "hell yeah!") 607 | ("account", "bob.11111111") 608 | ("balance", "5000.0000 EOS") 609 | ("cpu_staked", "1.0000 EOS") 610 | ("net_staked", "1.0000 EOS") 611 | ("cpu_unstaked", "0.0000 EOS") 612 | ("net_unstaked", "0.0000 EOS"), 613 | creditor 614 | ); 615 | 616 | auto order = get_order("alice.111111"); 617 | REQUIRE_MATCHING_OBJECT(mvo() 618 | ("buyer", "alice.111111") 619 | ("price", "0.1000 EOS") 620 | ("is_free", 1) 621 | ("creditor", "bob.11111111") 622 | ("beneficiary", "alice.111111") 623 | ("cpu_staked", "1.0000 EOS") 624 | ("net_staked", "1.0000 EOS"), 625 | order 626 | ); 627 | 628 | // after 1 minutes 629 | produce_blocks(120); 630 | 631 | creditor = get_creditor("bob.11111111"); 632 | REQUIRE_MATCHING_OBJECT(mvo() 633 | ("is_active", 1) 634 | ("for_free", 1) 635 | ("free_memo", "hell yeah!") 636 | ("account", "bob.11111111") 637 | ("balance", "4998.0000 EOS") 638 | ("cpu_staked", "0.0000 EOS") 639 | ("net_staked", "0.0000 EOS") 640 | ("cpu_unstaked", "1.0000 EOS") 641 | ("net_unstaked", "1.0000 EOS"), 642 | creditor 643 | ); 644 | 645 | BOOST_REQUIRE_EQUAL(get_balance(N(masktransfer)), asset::from_string("0.1000 EOS")); 646 | BOOST_REQUIRE_EQUAL(get_balance(N(stakedincome)), asset::from_string("0.0000 EOS")); 647 | 648 | } 649 | FC_LOG_AND_RETHROW() 650 | 651 | 652 | // BOOST_FIXTURE_TEST_CASE(rotate_creditor_test, bankofstaked_tester) 653 | // try 654 | // { 655 | // push_action(N(bankofstaked), N(setplan), mvo() 656 | // ("price", "0.1000 EOS") 657 | // ("cpu", "2495.0000 EOS") 658 | // ("net", "2495.0000 EOS") 659 | // ("duration", 1) 660 | // ("is_free", 1)); 661 | // push_action(N(bankofstaked), N(setplan), mvo() 662 | // ("price", "1.0000 EOS") 663 | // ("cpu", "10.0000 EOS") 664 | // ("net", "10.0000 EOS") 665 | // ("duration", 2) 666 | // ("is_free", 0)); 667 | 668 | // push_action(N(bankofstaked), N(addcreditor), mvo()("account", "bob.11111111")("for_free", 1)("free_memo", "")); 669 | // push_action(N(bankofstaked), N(addcreditor), mvo()("account", "ted.11111111")("for_free", 1)("free_memo", "")); 670 | // push_action(N(bankofstaked), N(addcreditor), mvo()("account", "carol.111111")("for_free", 0)("free_memo", "")); 671 | // push_action(N(bankofstaked), N(addcreditor), mvo()("account", "carol.222222")("for_free", 0)("free_memo", "")); 672 | 673 | // push_action(N(bankofstaked), N(activateplan), mvo() 674 | // ("price", "0.1000 EOS") 675 | // ("is_active", 1)); 676 | // push_action(N(bankofstaked), N(activateplan), mvo() 677 | // ("price", "1.0000 EOS") 678 | // ("is_active", 1)); 679 | 680 | // push_action(N(bankofstaked), N(activate), mvo()("account", "bob.11111111")); 681 | // push_action(N(bankofstaked), N(activate), mvo()("account", "carol.111111")); 682 | // produce_blocks(); 683 | 684 | // eosio_system_tester::transfer(N(alice.111111), N(bankofstaked), core_sym::from_string("0.1000"), N(alice.111111)); 685 | // produce_blocks(10); 686 | // // eosio_system_tester::transfer(N(alice.111111), N(bankofstaked), core_sym::from_string("1.0000"), N(alice.111111)); 687 | // // produce_blocks(); 688 | 689 | // auto creditor = get_creditor("bob.11111111"); 690 | // REQUIRE_MATCHING_OBJECT(mvo() 691 | // ("is_active", 0) 692 | // ("for_free", 1) 693 | // ("free_memo", "") 694 | // ("account", "bob.11111111") 695 | // ("balance", "5000.0000 EOS") 696 | // ("cpu_staked", "2495.0000 EOS") 697 | // ("net_staked", "2495.0000 EOS") 698 | // ("cpu_unstaked", "0.0000 EOS") 699 | // ("net_unstaked", "0.0000 EOS"), 700 | // creditor 701 | // ); 702 | 703 | // creditor = get_creditor("ted.11111111"); 704 | // REQUIRE_MATCHING_OBJECT(mvo() 705 | // ("is_active", 1) 706 | // ("for_free", 1) 707 | // ("free_memo", "") 708 | // ("account", "ted.11111111") 709 | // ("balance", "6000.0000 EOS") 710 | // ("cpu_staked", "0.0000 EOS") 711 | // ("net_staked", "0.0000 EOS") 712 | // ("cpu_unstaked", "0.0000 EOS") 713 | // ("net_unstaked", "0.0000 EOS"), 714 | // creditor 715 | // ); 716 | // } 717 | // FC_LOG_AND_RETHROW() 718 | 719 | BOOST_AUTO_TEST_SUITE_END() 720 | -------------------------------------------------------------------------------- /tests/tests/contracts.hpp.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace eosio { namespace testing { 5 | 6 | struct contracts { 7 | static std::vector bank_wasm() { return read_wasm("${CMAKE_SOURCE_DIR}/../../build/bankofstaked.wasm"); } 8 | static std::vector bank_abi() { return read_abi("${CMAKE_SOURCE_DIR}/../../build/bankofstaked.abi"); } 9 | 10 | static std::vector token_wasm() { return read_wasm("${CMAKE_SOURCE_DIR}/test_contracts/eosio.token/eosio.token.wasm"); } 11 | static std::vector token_abi() { return read_abi("${CMAKE_SOURCE_DIR}/test_contracts/eosio.token/eosio.token.abi"); } 12 | static std::vector system_wasm() { return read_wasm("${CMAKE_SOURCE_DIR}/test_contracts/eosio.system/eosio.system.wasm"); } 13 | static std::vector system_abi() { return read_abi("${CMAKE_SOURCE_DIR}/test_contracts/eosio.system/eosio.system.abi"); } 14 | static std::vector msig_wasm() { return read_wasm("${CMAKE_SOURCE_DIR}/test_contracts/eosio.msig/eosio.msig.wasm"); } 15 | static std::vector msig_abi() { return read_abi("${CMAKE_SOURCE_DIR}/test_contracts/eosio.msig/eosio.msig.abi"); } 16 | }; 17 | }} //ns eosio::testing 18 | -------------------------------------------------------------------------------- /tests/tests/eosio.system_tester.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @copyright defined in eos/LICENSE.txt 4 | */ 5 | #pragma once 6 | 7 | #include 8 | #include 9 | #include "contracts.hpp" 10 | #include "test_symbol.hpp" 11 | 12 | #include 13 | #include 14 | 15 | using namespace eosio::chain; 16 | using namespace eosio::testing; 17 | using namespace fc; 18 | 19 | using mvo = fc::mutable_variant_object; 20 | 21 | #ifndef TESTER 22 | #ifdef NON_VALIDATING_TEST 23 | #define TESTER tester 24 | #else 25 | #define TESTER validating_tester 26 | #endif 27 | #endif 28 | 29 | 30 | namespace eosio_system { 31 | 32 | class eosio_system_tester : public TESTER { 33 | public: 34 | 35 | void basic_setup() { 36 | produce_blocks( 2 ); 37 | 38 | create_accounts({ N(eosio.token), N(eosio.ram), N(eosio.ramfee), N(eosio.stake), 39 | N(eosio.bpay), N(eosio.vpay), N(eosio.saving), N(eosio.names) }); 40 | 41 | 42 | produce_blocks( 100 ); 43 | set_code( N(eosio.token), contracts::token_wasm()); 44 | set_abi( N(eosio.token), contracts::token_abi().data() ); 45 | { 46 | const auto& accnt = control->db().get( N(eosio.token) ); 47 | abi_def abi; 48 | BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); 49 | token_abi_ser.set_abi(abi, abi_serializer_max_time); 50 | } 51 | } 52 | 53 | void create_core_token( symbol core_symbol = symbol{CORE_SYM} ) { 54 | FC_ASSERT( core_symbol.precision() != 4, "create_core_token assumes precision of core token is 4" ); 55 | create_currency( N(eosio.token), config::system_account_name, asset(100000000000000, core_symbol) ); 56 | issue(config::system_account_name, asset(10000000000000, core_symbol) ); 57 | BOOST_REQUIRE_EQUAL( asset(10000000000000, core_symbol), get_balance( "eosio", core_symbol ) ); 58 | } 59 | 60 | void deploy_contract( bool call_init = true ) { 61 | set_code( config::system_account_name, contracts::system_wasm() ); 62 | set_abi( config::system_account_name, contracts::system_abi().data() ); 63 | if( call_init ) { 64 | base_tester::push_action(config::system_account_name, N(init), 65 | config::system_account_name, mutable_variant_object() 66 | ("version", 0) 67 | ("core", CORE_SYM_STR) 68 | ); 69 | } 70 | 71 | { 72 | const auto& accnt = control->db().get( config::system_account_name ); 73 | abi_def abi; 74 | BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); 75 | abi_ser.set_abi(abi, abi_serializer_max_time); 76 | } 77 | } 78 | 79 | void remaining_setup() { 80 | produce_blocks(); 81 | 82 | // Assumes previous setup steps were done with core token symbol set to CORE_SYM 83 | create_account_with_resources( N(alice1111111), config::system_account_name, core_sym::from_string("1.0000"), false ); 84 | create_account_with_resources( N(bob111111111), config::system_account_name, core_sym::from_string("0.4500"), false ); 85 | create_account_with_resources( N(carol1111111), config::system_account_name, core_sym::from_string("1.0000"), false ); 86 | 87 | BOOST_REQUIRE_EQUAL( core_sym::from_string("1000000000.0000"), get_balance("eosio") + get_balance("eosio.ramfee") + get_balance("eosio.stake") + get_balance("eosio.ram") ); 88 | } 89 | 90 | enum class setup_level { 91 | none, 92 | minimal, 93 | core_token, 94 | deploy_contract, 95 | full 96 | }; 97 | 98 | eosio_system_tester( setup_level l = setup_level::full ) { 99 | if( l == setup_level::none ) return; 100 | 101 | basic_setup(); 102 | if( l == setup_level::minimal ) return; 103 | 104 | create_core_token(); 105 | if( l == setup_level::core_token ) return; 106 | 107 | deploy_contract(); 108 | if( l == setup_level::deploy_contract ) return; 109 | 110 | remaining_setup(); 111 | } 112 | 113 | template 114 | eosio_system_tester(Lambda setup) { 115 | setup(*this); 116 | 117 | basic_setup(); 118 | create_core_token(); 119 | deploy_contract(); 120 | remaining_setup(); 121 | } 122 | 123 | 124 | void create_accounts_with_resources( vector accounts, account_name creator = config::system_account_name ) { 125 | for( auto a : accounts ) { 126 | create_account_with_resources( a, creator ); 127 | } 128 | } 129 | 130 | transaction_trace_ptr create_account_with_resources( account_name a, account_name creator, uint32_t ram_bytes = 8000 ) { 131 | signed_transaction trx; 132 | set_transaction_headers(trx); 133 | 134 | authority owner_auth; 135 | owner_auth = authority( get_public_key( a, "owner" ) ); 136 | 137 | trx.actions.emplace_back( vector{{creator,config::active_name}}, 138 | newaccount{ 139 | .creator = creator, 140 | .name = a, 141 | .owner = owner_auth, 142 | .active = authority( get_public_key( a, "active" ) ) 143 | }); 144 | 145 | trx.actions.emplace_back( get_action( config::system_account_name, N(buyrambytes), vector{{creator,config::active_name}}, 146 | mvo() 147 | ("payer", creator) 148 | ("receiver", a) 149 | ("bytes", ram_bytes) ) 150 | ); 151 | trx.actions.emplace_back( get_action( config::system_account_name, N(delegatebw), vector{{creator,config::active_name}}, 152 | mvo() 153 | ("from", creator) 154 | ("receiver", a) 155 | ("stake_net_quantity", core_sym::from_string("10.0000") ) 156 | ("stake_cpu_quantity", core_sym::from_string("10.0000") ) 157 | ("transfer", 0 ) 158 | ) 159 | ); 160 | 161 | set_transaction_headers(trx); 162 | trx.sign( get_private_key( creator, "active" ), control->get_chain_id() ); 163 | return push_transaction( trx ); 164 | } 165 | 166 | transaction_trace_ptr create_account_with_resources( account_name a, account_name creator, asset ramfunds, bool multisig, 167 | asset net = core_sym::from_string("10.0000"), asset cpu = core_sym::from_string("10.0000") ) { 168 | signed_transaction trx; 169 | set_transaction_headers(trx); 170 | 171 | authority owner_auth; 172 | if (multisig) { 173 | // multisig between account's owner key and creators active permission 174 | owner_auth = authority(2, {key_weight{get_public_key( a, "owner" ), 1}}, {permission_level_weight{{creator, config::active_name}, 1}}); 175 | } else { 176 | owner_auth = authority( get_public_key( a, "owner" ) ); 177 | } 178 | 179 | trx.actions.emplace_back( vector{{creator,config::active_name}}, 180 | newaccount{ 181 | .creator = creator, 182 | .name = a, 183 | .owner = owner_auth, 184 | .active = authority( get_public_key( a, "active" ) ) 185 | }); 186 | 187 | trx.actions.emplace_back( get_action( config::system_account_name, N(buyram), vector{{creator,config::active_name}}, 188 | mvo() 189 | ("payer", creator) 190 | ("receiver", a) 191 | ("quant", ramfunds) ) 192 | ); 193 | 194 | trx.actions.emplace_back( get_action( config::system_account_name, N(delegatebw), vector{{creator,config::active_name}}, 195 | mvo() 196 | ("from", creator) 197 | ("receiver", a) 198 | ("stake_net_quantity", net ) 199 | ("stake_cpu_quantity", cpu ) 200 | ("transfer", 0 ) 201 | ) 202 | ); 203 | 204 | set_transaction_headers(trx); 205 | trx.sign( get_private_key( creator, "active" ), control->get_chain_id() ); 206 | return push_transaction( trx ); 207 | } 208 | 209 | transaction_trace_ptr setup_producer_accounts( const std::vector& accounts, 210 | asset ram = core_sym::from_string("1.0000"), 211 | asset cpu = core_sym::from_string("80.0000"), 212 | asset net = core_sym::from_string("80.0000") 213 | ) 214 | { 215 | account_name creator(config::system_account_name); 216 | signed_transaction trx; 217 | set_transaction_headers(trx); 218 | 219 | for (const auto& a: accounts) { 220 | authority owner_auth( get_public_key( a, "owner" ) ); 221 | trx.actions.emplace_back( vector{{creator,config::active_name}}, 222 | newaccount{ 223 | .creator = creator, 224 | .name = a, 225 | .owner = owner_auth, 226 | .active = authority( get_public_key( a, "active" ) ) 227 | }); 228 | 229 | trx.actions.emplace_back( get_action( config::system_account_name, N(buyram), vector{ {creator, config::active_name} }, 230 | mvo() 231 | ("payer", creator) 232 | ("receiver", a) 233 | ("quant", ram) ) 234 | ); 235 | 236 | trx.actions.emplace_back( get_action( config::system_account_name, N(delegatebw), vector{ {creator, config::active_name} }, 237 | mvo() 238 | ("from", creator) 239 | ("receiver", a) 240 | ("stake_net_quantity", net) 241 | ("stake_cpu_quantity", cpu ) 242 | ("transfer", 0 ) 243 | ) 244 | ); 245 | } 246 | 247 | set_transaction_headers(trx); 248 | trx.sign( get_private_key( creator, "active" ), control->get_chain_id() ); 249 | return push_transaction( trx ); 250 | } 251 | 252 | action_result buyram( const account_name& payer, account_name receiver, const asset& eosin ) { 253 | return push_action( payer, N(buyram), mvo()( "payer",payer)("receiver",receiver)("quant",eosin) ); 254 | } 255 | action_result buyrambytes( const account_name& payer, account_name receiver, uint32_t numbytes ) { 256 | return push_action( payer, N(buyrambytes), mvo()( "payer",payer)("receiver",receiver)("bytes",numbytes) ); 257 | } 258 | 259 | action_result sellram( const account_name& account, uint64_t numbytes ) { 260 | return push_action( account, N(sellram), mvo()( "account", account)("bytes",numbytes) ); 261 | } 262 | 263 | action_result push_action( const account_name& signer, const action_name &name, const variant_object &data, bool auth = true ) { 264 | string action_type_name = abi_ser.get_action_type(name); 265 | 266 | action act; 267 | act.account = config::system_account_name; 268 | act.name = name; 269 | act.data = abi_ser.variant_to_binary( action_type_name, data, abi_serializer_max_time ); 270 | 271 | return base_tester::push_action( std::move(act), auth ? uint64_t(signer) : signer == N(bob111111111) ? N(alice1111111) : N(bob111111111) ); 272 | } 273 | 274 | action_result stake( const account_name& from, const account_name& to, const asset& net, const asset& cpu ) { 275 | return push_action( name(from), N(delegatebw), mvo() 276 | ("from", from) 277 | ("receiver", to) 278 | ("stake_net_quantity", net) 279 | ("stake_cpu_quantity", cpu) 280 | ("transfer", 0 ) 281 | ); 282 | } 283 | 284 | action_result stake( const account_name& acnt, const asset& net, const asset& cpu ) { 285 | return stake( acnt, acnt, net, cpu ); 286 | } 287 | 288 | action_result stake_with_transfer( const account_name& from, const account_name& to, const asset& net, const asset& cpu ) { 289 | return push_action( name(from), N(delegatebw), mvo() 290 | ("from", from) 291 | ("receiver", to) 292 | ("stake_net_quantity", net) 293 | ("stake_cpu_quantity", cpu) 294 | ("transfer", true ) 295 | ); 296 | } 297 | 298 | action_result stake_with_transfer( const account_name& acnt, const asset& net, const asset& cpu ) { 299 | return stake_with_transfer( acnt, acnt, net, cpu ); 300 | } 301 | 302 | action_result unstake( const account_name& from, const account_name& to, const asset& net, const asset& cpu ) { 303 | return push_action( name(from), N(undelegatebw), mvo() 304 | ("from", from) 305 | ("receiver", to) 306 | ("unstake_net_quantity", net) 307 | ("unstake_cpu_quantity", cpu) 308 | ); 309 | } 310 | 311 | action_result unstake( const account_name& acnt, const asset& net, const asset& cpu ) { 312 | return unstake( acnt, acnt, net, cpu ); 313 | } 314 | 315 | action_result bidname( const account_name& bidder, const account_name& newname, const asset& bid ) { 316 | return push_action( name(bidder), N(bidname), mvo() 317 | ("bidder", bidder) 318 | ("newname", newname) 319 | ("bid", bid) 320 | ); 321 | } 322 | 323 | static fc::variant_object producer_parameters_example( int n ) { 324 | return mutable_variant_object() 325 | ("max_block_net_usage", 10000000 + n ) 326 | ("target_block_net_usage_pct", 10 + n ) 327 | ("max_transaction_net_usage", 1000000 + n ) 328 | ("base_per_transaction_net_usage", 100 + n) 329 | ("net_usage_leeway", 500 + n ) 330 | ("context_free_discount_net_usage_num", 1 + n ) 331 | ("context_free_discount_net_usage_den", 100 + n ) 332 | ("max_block_cpu_usage", 10000000 + n ) 333 | ("target_block_cpu_usage_pct", 10 + n ) 334 | ("max_transaction_cpu_usage", 1000000 + n ) 335 | ("min_transaction_cpu_usage", 100 + n ) 336 | ("max_transaction_lifetime", 3600 + n) 337 | ("deferred_trx_expiration_window", 600 + n) 338 | ("max_transaction_delay", 10*86400+n) 339 | ("max_inline_action_size", 4096 + n) 340 | ("max_inline_action_depth", 4 + n) 341 | ("max_authority_depth", 6 + n) 342 | ("max_ram_size", (n % 10 + 1) * 1024 * 1024) 343 | ("ram_reserve_ratio", 100 + n); 344 | } 345 | 346 | action_result regproducer( const account_name& acnt, int params_fixture = 1 ) { 347 | action_result r = push_action( acnt, N(regproducer), mvo() 348 | ("producer", acnt ) 349 | ("producer_key", get_public_key( acnt, "active" ) ) 350 | ("url", "" ) 351 | ("location", 0 ) 352 | ); 353 | BOOST_REQUIRE_EQUAL( success(), r); 354 | return r; 355 | } 356 | 357 | action_result vote( const account_name& voter, const std::vector& producers, const account_name& proxy = name(0) ) { 358 | return push_action(voter, N(voteproducer), mvo() 359 | ("voter", voter) 360 | ("proxy", proxy) 361 | ("producers", producers)); 362 | } 363 | 364 | uint32_t last_block_time() const { 365 | return time_point_sec( control->head_block_time() ).sec_since_epoch(); 366 | } 367 | 368 | asset get_balance( const account_name& act, symbol balance_symbol = symbol{CORE_SYM} ) { 369 | vector data = get_row_by_account( N(eosio.token), act, N(accounts), balance_symbol.to_symbol_code().value ); 370 | return data.empty() ? asset(0, balance_symbol) : token_abi_ser.binary_to_variant("account", data, abi_serializer_max_time)["balance"].as(); 371 | } 372 | 373 | fc::variant get_total_stake( const account_name& act ) { 374 | vector data = get_row_by_account( config::system_account_name, act, N(userres), act ); 375 | return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "user_resources", data, abi_serializer_max_time ); 376 | } 377 | 378 | fc::variant get_voter_info( const account_name& act ) { 379 | vector data = get_row_by_account( config::system_account_name, config::system_account_name, N(voters), act ); 380 | return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "voter_info", data, abi_serializer_max_time ); 381 | } 382 | 383 | fc::variant get_producer_info( const account_name& act ) { 384 | vector data = get_row_by_account( config::system_account_name, config::system_account_name, N(producers), act ); 385 | return abi_ser.binary_to_variant( "producer_info", data, abi_serializer_max_time ); 386 | } 387 | 388 | fc::variant get_producer_info2( const account_name& act ) { 389 | vector data = get_row_by_account( config::system_account_name, config::system_account_name, N(producers2), act ); 390 | return abi_ser.binary_to_variant( "producer_info2", data, abi_serializer_max_time ); 391 | } 392 | 393 | void create_currency( name contract, name manager, asset maxsupply ) { 394 | auto act = mutable_variant_object() 395 | ("issuer", manager ) 396 | ("maximum_supply", maxsupply ); 397 | 398 | base_tester::push_action(contract, N(create), contract, act ); 399 | } 400 | 401 | void issue( name to, const asset& amount, name manager = config::system_account_name ) { 402 | base_tester::push_action( N(eosio.token), N(issue), manager, mutable_variant_object() 403 | ("to", to ) 404 | ("quantity", amount ) 405 | ("memo", "") 406 | ); 407 | } 408 | void transfer( name from, name to, const asset& amount, name manager = config::system_account_name ) { 409 | base_tester::push_action( N(eosio.token), N(transfer), manager, mutable_variant_object() 410 | ("from", from) 411 | ("to", to ) 412 | ("quantity", amount) 413 | ("memo", "") 414 | ); 415 | } 416 | 417 | double stake2votes( asset stake ) { 418 | auto now = control->pending_block_time().time_since_epoch().count() / 1000000; 419 | return stake.get_amount() * pow(2, int64_t((now - (config::block_timestamp_epoch / 1000)) / (86400 * 7))/ double(52) ); // 52 week periods (i.e. ~years) 420 | } 421 | 422 | double stake2votes( const string& s ) { 423 | return stake2votes( core_sym::from_string(s) ); 424 | } 425 | 426 | fc::variant get_stats( const string& symbolname ) { 427 | auto symb = eosio::chain::symbol::from_string(symbolname); 428 | auto symbol_code = symb.to_symbol_code().value; 429 | vector data = get_row_by_account( N(eosio.token), symbol_code, N(stat), symbol_code ); 430 | return data.empty() ? fc::variant() : token_abi_ser.binary_to_variant( "currency_stats", data, abi_serializer_max_time ); 431 | } 432 | 433 | asset get_token_supply() { 434 | return get_stats("4," CORE_SYM_NAME)["supply"].as(); 435 | } 436 | 437 | uint64_t microseconds_since_epoch_of_iso_string( const fc::variant& v ) { 438 | return static_cast( time_point::from_iso_string( v.as_string() ).time_since_epoch().count() ); 439 | } 440 | 441 | fc::variant get_global_state() { 442 | vector data = get_row_by_account( config::system_account_name, config::system_account_name, N(global), N(global) ); 443 | if (data.empty()) std::cout << "\nData is empty\n" << std::endl; 444 | return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "eosio_global_state", data, abi_serializer_max_time ); 445 | } 446 | 447 | fc::variant get_global_state2() { 448 | vector data = get_row_by_account( config::system_account_name, config::system_account_name, N(global2), N(global2) ); 449 | return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "eosio_global_state2", data, abi_serializer_max_time ); 450 | } 451 | 452 | fc::variant get_global_state3() { 453 | vector data = get_row_by_account( config::system_account_name, config::system_account_name, N(global3), N(global3) ); 454 | return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "eosio_global_state3", data, abi_serializer_max_time ); 455 | } 456 | 457 | fc::variant get_refund_request( name account ) { 458 | vector data = get_row_by_account( config::system_account_name, account, N(refunds), account ); 459 | return data.empty() ? fc::variant() : abi_ser.binary_to_variant( "refund_request", data, abi_serializer_max_time ); 460 | } 461 | 462 | abi_serializer initialize_multisig() { 463 | abi_serializer msig_abi_ser; 464 | { 465 | create_account_with_resources( N(eosio.msig), config::system_account_name ); 466 | BOOST_REQUIRE_EQUAL( success(), buyram( "eosio", "eosio.msig", core_sym::from_string("5000.0000") ) ); 467 | produce_block(); 468 | 469 | auto trace = base_tester::push_action(config::system_account_name, N(setpriv), 470 | config::system_account_name, mutable_variant_object() 471 | ("account", "eosio.msig") 472 | ("is_priv", 1) 473 | ); 474 | 475 | set_code( N(eosio.msig), contracts::msig_wasm() ); 476 | set_abi( N(eosio.msig), contracts::msig_abi().data() ); 477 | 478 | produce_blocks(); 479 | const auto& accnt = control->db().get( N(eosio.msig) ); 480 | abi_def msig_abi; 481 | BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, msig_abi), true); 482 | msig_abi_ser.set_abi(msig_abi, abi_serializer_max_time); 483 | } 484 | return msig_abi_ser; 485 | } 486 | 487 | vector active_and_vote_producers() { 488 | //stake more than 15% of total EOS supply to activate chain 489 | transfer( "eosio", "alice1111111", core_sym::from_string("650000000.0000"), "eosio" ); 490 | BOOST_REQUIRE_EQUAL( success(), stake( "alice1111111", "alice1111111", core_sym::from_string("300000000.0000"), core_sym::from_string("300000000.0000") ) ); 491 | 492 | // create accounts {defproducera, defproducerb, ..., defproducerz} and register as producers 493 | std::vector producer_names; 494 | { 495 | producer_names.reserve('z' - 'a' + 1); 496 | const std::string root("defproducer"); 497 | for ( char c = 'a'; c < 'a'+21; ++c ) { 498 | producer_names.emplace_back(root + std::string(1, c)); 499 | } 500 | setup_producer_accounts(producer_names); 501 | for (const auto& p: producer_names) { 502 | 503 | BOOST_REQUIRE_EQUAL( success(), regproducer(p) ); 504 | } 505 | } 506 | produce_blocks( 250); 507 | 508 | auto trace_auth = TESTER::push_action(config::system_account_name, updateauth::get_name(), config::system_account_name, mvo() 509 | ("account", name(config::system_account_name).to_string()) 510 | ("permission", name(config::active_name).to_string()) 511 | ("parent", name(config::owner_name).to_string()) 512 | ("auth", authority(1, {key_weight{get_public_key( config::system_account_name, "active" ), 1}}, { 513 | permission_level_weight{{config::system_account_name, config::eosio_code_name}, 1}, 514 | permission_level_weight{{config::producers_account_name, config::active_name}, 1} 515 | } 516 | )) 517 | ); 518 | BOOST_REQUIRE_EQUAL(transaction_receipt::executed, trace_auth->receipt->status); 519 | 520 | //vote for producers 521 | { 522 | transfer( config::system_account_name, "alice1111111", core_sym::from_string("100000000.0000"), config::system_account_name ); 523 | BOOST_REQUIRE_EQUAL(success(), stake( "alice1111111", core_sym::from_string("30000000.0000"), core_sym::from_string("30000000.0000") ) ); 524 | BOOST_REQUIRE_EQUAL(success(), buyram( "alice1111111", "alice1111111", core_sym::from_string("30000000.0000") ) ); 525 | BOOST_REQUIRE_EQUAL(success(), push_action(N(alice1111111), N(voteproducer), mvo() 526 | ("voter", "alice1111111") 527 | ("proxy", name(0).to_string()) 528 | ("producers", vector(producer_names.begin(), producer_names.begin()+21)) 529 | ) 530 | ); 531 | } 532 | produce_blocks( 250 ); 533 | 534 | auto producer_keys = control->head_block_state()->active_schedule.producers; 535 | BOOST_REQUIRE_EQUAL( 21, producer_keys.size() ); 536 | BOOST_REQUIRE_EQUAL( name("defproducera"), producer_keys[0].producer_name ); 537 | 538 | return producer_names; 539 | } 540 | 541 | void cross_15_percent_threshold() { 542 | setup_producer_accounts({N(producer1111)}); 543 | regproducer(N(producer1111)); 544 | { 545 | signed_transaction trx; 546 | set_transaction_headers(trx); 547 | 548 | trx.actions.emplace_back( get_action( config::system_account_name, N(delegatebw), 549 | vector{{config::system_account_name, config::active_name}}, 550 | mvo() 551 | ("from", name{config::system_account_name}) 552 | ("receiver", "producer1111") 553 | ("stake_net_quantity", core_sym::from_string("150000000.0000") ) 554 | ("stake_cpu_quantity", core_sym::from_string("0.0000") ) 555 | ("transfer", 1 ) 556 | ) 557 | ); 558 | trx.actions.emplace_back( get_action( config::system_account_name, N(voteproducer), 559 | vector{{N(producer1111), config::active_name}}, 560 | mvo() 561 | ("voter", "producer1111") 562 | ("proxy", name(0).to_string()) 563 | ("producers", vector(1, N(producer1111))) 564 | ) 565 | ); 566 | trx.actions.emplace_back( get_action( config::system_account_name, N(undelegatebw), 567 | vector{{N(producer1111), config::active_name}}, 568 | mvo() 569 | ("from", "producer1111") 570 | ("receiver", "producer1111") 571 | ("unstake_net_quantity", core_sym::from_string("150000000.0000") ) 572 | ("unstake_cpu_quantity", core_sym::from_string("0.0000") ) 573 | ) 574 | ); 575 | 576 | set_transaction_headers(trx); 577 | trx.sign( get_private_key( config::system_account_name, "active" ), control->get_chain_id() ); 578 | trx.sign( get_private_key( N(producer1111), "active" ), control->get_chain_id() ); 579 | push_transaction( trx ); 580 | produce_block(); 581 | } 582 | } 583 | 584 | abi_serializer abi_ser; 585 | abi_serializer token_abi_ser; 586 | }; 587 | 588 | inline fc::mutable_variant_object voter( account_name acct ) { 589 | return mutable_variant_object() 590 | ("owner", acct) 591 | ("proxy", name(0).to_string()) 592 | ("producers", variants() ) 593 | ("staked", int64_t(0)) 594 | //("last_vote_weight", double(0)) 595 | ("proxied_vote_weight", double(0)) 596 | ("is_proxy", 0) 597 | ; 598 | } 599 | 600 | inline fc::mutable_variant_object voter( account_name acct, const asset& vote_stake ) { 601 | return voter( acct )( "staked", vote_stake.get_amount() ); 602 | } 603 | 604 | inline fc::mutable_variant_object voter( account_name acct, int64_t vote_stake ) { 605 | return voter( acct )( "staked", vote_stake ); 606 | } 607 | 608 | inline fc::mutable_variant_object proxy( account_name acct ) { 609 | return voter( acct )( "is_proxy", 1 ); 610 | } 611 | 612 | inline uint64_t M( const string& eos_str ) { 613 | return core_sym::from_string( eos_str ).get_amount(); 614 | } 615 | 616 | } 617 | -------------------------------------------------------------------------------- /tests/tests/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "eosio.system_tester.hpp" 14 | 15 | using namespace eosio_system; 16 | #define BOOST_TEST_STATIC_LINK 17 | 18 | void translate_fc_exception(const fc::exception &e) { 19 | std::cerr << "\033[33m" << e.to_detail_string() << "\033[0m" << std::endl; 20 | BOOST_TEST_FAIL("Caught Unexpected Exception"); 21 | } 22 | 23 | boost::unit_test::test_suite* init_unit_test_suite(int argc, char* argv[]) { 24 | // Turn off blockchain logging if no --verbose parameter is not added 25 | // To have verbose enabled, call "tests/chain_test -- --verbose" 26 | bool is_verbose = false; 27 | std::string verbose_arg = "--verbose"; 28 | for (int i = 0; i < argc; i++) { 29 | if (verbose_arg == argv[i]) { 30 | is_verbose = true; 31 | break; 32 | } 33 | } 34 | if(!is_verbose) fc::logger::get(DEFAULT_LOGGER).set_log_level(fc::log_level::off); 35 | 36 | // Register fc::exception translator 37 | boost::unit_test::unit_test_monitor.template register_exception_translator(&translate_fc_exception); 38 | 39 | std::srand(time(NULL)); 40 | std::cout << "Random number generator seeded to " << time(NULL) << std::endl; 41 | return nullptr; 42 | } 43 | -------------------------------------------------------------------------------- /tests/tests/test_contracts/eosio.msig/eosio.msig.abi: -------------------------------------------------------------------------------- 1 | { 2 | "____comment": "This file was generated with eosio-abigen. DO NOT EDIT Fri Jan 4 07:43:36 2019", 3 | "version": "eosio::abi/1.1", 4 | "structs": [ 5 | { 6 | "name": "action", 7 | "base": "", 8 | "fields": [ 9 | { 10 | "name": "account", 11 | "type": "name" 12 | }, 13 | { 14 | "name": "name", 15 | "type": "name" 16 | }, 17 | { 18 | "name": "authorization", 19 | "type": "permission_level[]" 20 | }, 21 | { 22 | "name": "data", 23 | "type": "bytes" 24 | } 25 | ] 26 | }, 27 | { 28 | "name": "approval", 29 | "base": "", 30 | "fields": [ 31 | { 32 | "name": "level", 33 | "type": "permission_level" 34 | }, 35 | { 36 | "name": "time", 37 | "type": "time_point" 38 | } 39 | ] 40 | }, 41 | { 42 | "name": "approvals_info", 43 | "base": "", 44 | "fields": [ 45 | { 46 | "name": "version", 47 | "type": "uint8" 48 | }, 49 | { 50 | "name": "proposal_name", 51 | "type": "name" 52 | }, 53 | { 54 | "name": "requested_approvals", 55 | "type": "approval[]" 56 | }, 57 | { 58 | "name": "provided_approvals", 59 | "type": "approval[]" 60 | } 61 | ] 62 | }, 63 | { 64 | "name": "approve", 65 | "base": "", 66 | "fields": [ 67 | { 68 | "name": "proposer", 69 | "type": "name" 70 | }, 71 | { 72 | "name": "proposal_name", 73 | "type": "name" 74 | }, 75 | { 76 | "name": "level", 77 | "type": "permission_level" 78 | }, 79 | { 80 | "name": "proposal_hash", 81 | "type": "checksum256$" 82 | } 83 | ] 84 | }, 85 | { 86 | "name": "cancel", 87 | "base": "", 88 | "fields": [ 89 | { 90 | "name": "proposer", 91 | "type": "name" 92 | }, 93 | { 94 | "name": "proposal_name", 95 | "type": "name" 96 | }, 97 | { 98 | "name": "canceler", 99 | "type": "name" 100 | } 101 | ] 102 | }, 103 | { 104 | "name": "exec", 105 | "base": "", 106 | "fields": [ 107 | { 108 | "name": "proposer", 109 | "type": "name" 110 | }, 111 | { 112 | "name": "proposal_name", 113 | "type": "name" 114 | }, 115 | { 116 | "name": "executer", 117 | "type": "name" 118 | } 119 | ] 120 | }, 121 | { 122 | "name": "extension", 123 | "base": "", 124 | "fields": [ 125 | { 126 | "name": "type", 127 | "type": "uint16" 128 | }, 129 | { 130 | "name": "data", 131 | "type": "bytes" 132 | } 133 | ] 134 | }, 135 | { 136 | "name": "invalidate", 137 | "base": "", 138 | "fields": [ 139 | { 140 | "name": "account", 141 | "type": "name" 142 | } 143 | ] 144 | }, 145 | { 146 | "name": "invalidation", 147 | "base": "", 148 | "fields": [ 149 | { 150 | "name": "account", 151 | "type": "name" 152 | }, 153 | { 154 | "name": "last_invalidation_time", 155 | "type": "time_point" 156 | } 157 | ] 158 | }, 159 | { 160 | "name": "old_approvals_info", 161 | "base": "", 162 | "fields": [ 163 | { 164 | "name": "proposal_name", 165 | "type": "name" 166 | }, 167 | { 168 | "name": "requested_approvals", 169 | "type": "permission_level[]" 170 | }, 171 | { 172 | "name": "provided_approvals", 173 | "type": "permission_level[]" 174 | } 175 | ] 176 | }, 177 | { 178 | "name": "permission_level", 179 | "base": "", 180 | "fields": [ 181 | { 182 | "name": "actor", 183 | "type": "name" 184 | }, 185 | { 186 | "name": "permission", 187 | "type": "name" 188 | } 189 | ] 190 | }, 191 | { 192 | "name": "proposal", 193 | "base": "", 194 | "fields": [ 195 | { 196 | "name": "proposal_name", 197 | "type": "name" 198 | }, 199 | { 200 | "name": "packed_transaction", 201 | "type": "bytes" 202 | } 203 | ] 204 | }, 205 | { 206 | "name": "propose", 207 | "base": "", 208 | "fields": [ 209 | { 210 | "name": "proposer", 211 | "type": "name" 212 | }, 213 | { 214 | "name": "proposal_name", 215 | "type": "name" 216 | }, 217 | { 218 | "name": "requested", 219 | "type": "permission_level[]" 220 | }, 221 | { 222 | "name": "trx", 223 | "type": "transaction" 224 | } 225 | ] 226 | }, 227 | { 228 | "name": "transaction", 229 | "base": "transaction_header", 230 | "fields": [ 231 | { 232 | "name": "context_free_actions", 233 | "type": "action[]" 234 | }, 235 | { 236 | "name": "actions", 237 | "type": "action[]" 238 | }, 239 | { 240 | "name": "transaction_extensions", 241 | "type": "extension[]" 242 | } 243 | ] 244 | }, 245 | { 246 | "name": "transaction_header", 247 | "base": "", 248 | "fields": [ 249 | { 250 | "name": "expiration", 251 | "type": "time_point_sec" 252 | }, 253 | { 254 | "name": "ref_block_num", 255 | "type": "uint16" 256 | }, 257 | { 258 | "name": "ref_block_prefix", 259 | "type": "uint32" 260 | }, 261 | { 262 | "name": "max_net_usage_words", 263 | "type": "varuint32" 264 | }, 265 | { 266 | "name": "max_cpu_usage_ms", 267 | "type": "uint8" 268 | }, 269 | { 270 | "name": "delay_sec", 271 | "type": "varuint32" 272 | } 273 | ] 274 | }, 275 | { 276 | "name": "unapprove", 277 | "base": "", 278 | "fields": [ 279 | { 280 | "name": "proposer", 281 | "type": "name" 282 | }, 283 | { 284 | "name": "proposal_name", 285 | "type": "name" 286 | }, 287 | { 288 | "name": "level", 289 | "type": "permission_level" 290 | } 291 | ] 292 | } 293 | ], 294 | "types": [], 295 | "actions": [ 296 | { 297 | "name": "approve", 298 | "type": "approve", 299 | "ricardian_contract": "" 300 | }, 301 | { 302 | "name": "cancel", 303 | "type": "cancel", 304 | "ricardian_contract": "" 305 | }, 306 | { 307 | "name": "exec", 308 | "type": "exec", 309 | "ricardian_contract": "" 310 | }, 311 | { 312 | "name": "invalidate", 313 | "type": "invalidate", 314 | "ricardian_contract": "" 315 | }, 316 | { 317 | "name": "propose", 318 | "type": "propose", 319 | "ricardian_contract": "" 320 | }, 321 | { 322 | "name": "unapprove", 323 | "type": "unapprove", 324 | "ricardian_contract": "" 325 | } 326 | ], 327 | "tables": [ 328 | { 329 | "name": "approvals", 330 | "type": "old_approvals_info", 331 | "index_type": "i64", 332 | "key_names": [], 333 | "key_types": [] 334 | }, 335 | { 336 | "name": "approvals2", 337 | "type": "approvals_info", 338 | "index_type": "i64", 339 | "key_names": [], 340 | "key_types": [] 341 | }, 342 | { 343 | "name": "invals", 344 | "type": "invalidation", 345 | "index_type": "i64", 346 | "key_names": [], 347 | "key_types": [] 348 | }, 349 | { 350 | "name": "proposal", 351 | "type": "proposal", 352 | "index_type": "i64", 353 | "key_names": [], 354 | "key_types": [] 355 | } 356 | ], 357 | "ricardian_clauses": [], 358 | "variants": [], 359 | "abi_extensions": [] 360 | } -------------------------------------------------------------------------------- /tests/tests/test_contracts/eosio.msig/eosio.msig.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EOSLaoMao/BankofStaked-CE/3b764debe9bad8047c0cf28930b91b7a736a09d6/tests/tests/test_contracts/eosio.msig/eosio.msig.wasm -------------------------------------------------------------------------------- /tests/tests/test_contracts/eosio.system/eosio.system.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EOSLaoMao/BankofStaked-CE/3b764debe9bad8047c0cf28930b91b7a736a09d6/tests/tests/test_contracts/eosio.system/eosio.system.wasm -------------------------------------------------------------------------------- /tests/tests/test_contracts/eosio.token/eosio.token.abi: -------------------------------------------------------------------------------- 1 | { 2 | "____comment": "This file was generated with eosio-abigen. DO NOT EDIT Fri Jan 4 07:43:34 2019", 3 | "version": "eosio::abi/1.1", 4 | "structs": [ 5 | { 6 | "name": "account", 7 | "base": "", 8 | "fields": [ 9 | { 10 | "name": "balance", 11 | "type": "asset" 12 | } 13 | ] 14 | }, 15 | { 16 | "name": "close", 17 | "base": "", 18 | "fields": [ 19 | { 20 | "name": "owner", 21 | "type": "name" 22 | }, 23 | { 24 | "name": "symbol", 25 | "type": "symbol" 26 | } 27 | ] 28 | }, 29 | { 30 | "name": "create", 31 | "base": "", 32 | "fields": [ 33 | { 34 | "name": "issuer", 35 | "type": "name" 36 | }, 37 | { 38 | "name": "maximum_supply", 39 | "type": "asset" 40 | } 41 | ] 42 | }, 43 | { 44 | "name": "currency_stats", 45 | "base": "", 46 | "fields": [ 47 | { 48 | "name": "supply", 49 | "type": "asset" 50 | }, 51 | { 52 | "name": "max_supply", 53 | "type": "asset" 54 | }, 55 | { 56 | "name": "issuer", 57 | "type": "name" 58 | } 59 | ] 60 | }, 61 | { 62 | "name": "issue", 63 | "base": "", 64 | "fields": [ 65 | { 66 | "name": "to", 67 | "type": "name" 68 | }, 69 | { 70 | "name": "quantity", 71 | "type": "asset" 72 | }, 73 | { 74 | "name": "memo", 75 | "type": "string" 76 | } 77 | ] 78 | }, 79 | { 80 | "name": "open", 81 | "base": "", 82 | "fields": [ 83 | { 84 | "name": "owner", 85 | "type": "name" 86 | }, 87 | { 88 | "name": "symbol", 89 | "type": "symbol" 90 | }, 91 | { 92 | "name": "ram_payer", 93 | "type": "name" 94 | } 95 | ] 96 | }, 97 | { 98 | "name": "retire", 99 | "base": "", 100 | "fields": [ 101 | { 102 | "name": "quantity", 103 | "type": "asset" 104 | }, 105 | { 106 | "name": "memo", 107 | "type": "string" 108 | } 109 | ] 110 | }, 111 | { 112 | "name": "transfer", 113 | "base": "", 114 | "fields": [ 115 | { 116 | "name": "from", 117 | "type": "name" 118 | }, 119 | { 120 | "name": "to", 121 | "type": "name" 122 | }, 123 | { 124 | "name": "quantity", 125 | "type": "asset" 126 | }, 127 | { 128 | "name": "memo", 129 | "type": "string" 130 | } 131 | ] 132 | } 133 | ], 134 | "types": [], 135 | "actions": [ 136 | { 137 | "name": "close", 138 | "type": "close", 139 | "ricardian_contract": "" 140 | }, 141 | { 142 | "name": "create", 143 | "type": "create", 144 | "ricardian_contract": "" 145 | }, 146 | { 147 | "name": "issue", 148 | "type": "issue", 149 | "ricardian_contract": "" 150 | }, 151 | { 152 | "name": "open", 153 | "type": "open", 154 | "ricardian_contract": "" 155 | }, 156 | { 157 | "name": "retire", 158 | "type": "retire", 159 | "ricardian_contract": "" 160 | }, 161 | { 162 | "name": "transfer", 163 | "type": "transfer", 164 | "ricardian_contract": "" 165 | } 166 | ], 167 | "tables": [ 168 | { 169 | "name": "accounts", 170 | "type": "account", 171 | "index_type": "i64", 172 | "key_names": [], 173 | "key_types": [] 174 | }, 175 | { 176 | "name": "stat", 177 | "type": "currency_stats", 178 | "index_type": "i64", 179 | "key_names": [], 180 | "key_types": [] 181 | } 182 | ], 183 | "ricardian_clauses": [], 184 | "variants": [], 185 | "abi_extensions": [] 186 | } -------------------------------------------------------------------------------- /tests/tests/test_contracts/eosio.token/eosio.token.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EOSLaoMao/BankofStaked-CE/3b764debe9bad8047c0cf28930b91b7a736a09d6/tests/tests/test_contracts/eosio.token/eosio.token.wasm -------------------------------------------------------------------------------- /tests/tests/test_symbol.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define CORE_SYM_NAME "EOS" 4 | #define CORE_SYM_PRECISION 4 5 | 6 | #define _STRINGIZE1(x) #x 7 | #define _STRINGIZE2(x) _STRINGIZE1(x) 8 | 9 | #define CORE_SYM_STR ( _STRINGIZE2(CORE_SYM_PRECISION) "," CORE_SYM_NAME ) 10 | #define CORE_SYM ( ::eosio::chain::string_to_symbol_c( CORE_SYM_PRECISION, CORE_SYM_NAME ) ) 11 | 12 | struct core_sym { 13 | static inline eosio::chain::asset from_string(const std::string& s) { 14 | return eosio::chain::asset::from_string(s + " " CORE_SYM_NAME); 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /unittest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | RED='\033[01;31m' 3 | 4 | cd /bankofstaked/tests 5 | ./clean-build.sh 6 | 7 | ./build.sh 8 | 9 | ./build/tests/unit_test -- --verbose 10 | 11 | if [ $? -eq 0 ]; then 12 | echo "Run unit test success" 13 | else 14 | echo -e "-----------------------------------" 15 | echo -e "${RED}Please run ./build.sh first." 16 | echo -e "-----------------------------------" 17 | fi 18 | --------------------------------------------------------------------------------