-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b9ac751
commit 308eb7f
Showing
16 changed files
with
1,657 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
name: Bun Test | ||
|
||
on: push | ||
|
||
jobs: | ||
build-and-test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- uses: oven-sh/setup-bun@v1 | ||
with: | ||
bun-version: latest | ||
- name: Install CDT | ||
run: | | ||
curl https://gateway.pinata.cloud/ipfs/QmfFhXmbaZMgwVs51MHcUemG1A2XCNSTByRcAEG7S6iXaD -o cdt_4.0.1-1_amd64.deb | ||
sudo apt install ./cdt_4.0.1-1_amd64.deb | ||
- run: bun install | ||
- run: bun run build | ||
- run: bun run test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,9 @@ | ||
# Prerequisites | ||
*.d | ||
node_modules | ||
.DS_Store | ||
*.wasm | ||
*.abi | ||
|
||
# Compiled Object files | ||
*.slo | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,26 @@ | ||
# eosio.bpay | ||
eosio.bpay | ||
# EOS Block Pay (`eosio.bpay`) [![Bun Test](https://github.com/eosnetworkfoundation/eosio.bpay/actions/workflows/test.yml/badge.svg)](https://github.com/eosnetworkfoundation/eosio.bpay/actions/workflows/test.yml) | ||
|
||
## Overview | ||
|
||
The `eosio.bpay` contract handles system block pay distribution. | ||
|
||
## Development and Testing | ||
|
||
### Build Instructions | ||
|
||
To compile the contract, developers can use the following command: | ||
|
||
```sh | ||
$ cdt-cpp eosio.bpay.cpp -I ./include | ||
``` | ||
|
||
### Testing Framework | ||
|
||
The contract includes a comprehensive testing suite designed to validate its functionality. The tests are executed using the following commands: | ||
|
||
```sh | ||
$ npm test | ||
|
||
> test | ||
> bun test | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
<h1 class="clause">UserAgreement</h1> | ||
|
||
The `eosio.bpay` contract handles block pay distribution. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<h1 class="contract">claimrewards</h1> | ||
|
||
--- | ||
spec_version: "0.2.0" | ||
title: claimrewards | ||
summary: 'Claim Rewards' | ||
icon: https://gateway.pinata.cloud/ipfs/QmZ4HSZDuSrZ4BHawtZRhVfwyYJ4DepNJqVDzxY59KveiM#3830f1ce8cb07f7757dbcf383b1ec1b11914ac34a1f9d8b065f07600fa9dac19 | ||
--- | ||
|
||
Claim rewards {{ owner }} has earned as a block producer. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
#include "eosio.bpay.hpp" | ||
|
||
namespace eosio { | ||
|
||
[[eosio::action]] | ||
void bpay::claimrewards( const name owner ) { | ||
require_auth( owner ); | ||
|
||
rewards_table _rewards( get_self(), get_self().value ); | ||
|
||
const auto& row = _rewards.get( owner.value, "not eligible to claim producer block pay" ); | ||
check( row.quantity.amount > 0, "no rewards to claim"); | ||
|
||
// transfer rewards to owner | ||
eosio::token::transfer_action transfer( "eosio.token"_n, { get_self(), "active"_n }); | ||
transfer.send( get_self(), owner, row.quantity, "producer block pay" ); | ||
|
||
_rewards.modify(row, get_self(), [&](auto& row) { | ||
row.quantity.amount = 0; | ||
}); | ||
} | ||
|
||
[[eosio::on_notify("*::transfer")]] | ||
void bpay::on_transfer( const name from, const name to, const asset quantity, const string memo ) | ||
{ | ||
if (from == get_self() || to != get_self()) { | ||
return; | ||
} | ||
|
||
check( get_first_receiver() == "eosio.token"_n, "only eosio.token allowed") ; | ||
check( quantity.symbol == eosio::symbol("EOS", 4), "only EOS token allowed" ); | ||
|
||
rewards_table _rewards( get_self(), get_self().value ); | ||
eosiosystem::system_contract::producers_table _producers( "eosio"_n, "eosio"_n.value ); | ||
|
||
// get voter secondary index | ||
auto idx = _producers.get_index<"prototalvote"_n>(); | ||
|
||
// get producer with the most votes | ||
// reverse iterator to get the last element | ||
auto prod = idx.rbegin(); | ||
|
||
// get top 21 producers by vote | ||
std::vector<name> top_producers; | ||
for (int i = 0; i < 21; i++) { | ||
if ( prod->is_active == false ) { | ||
prod++; | ||
i--; | ||
continue; | ||
} | ||
top_producers.push_back(prod->owner); | ||
print(i, " ", prod->owner, " ", prod->total_votes, "\n"); | ||
prod++; | ||
} | ||
|
||
// calculate rewards equal share for top 21 producers | ||
asset reward = quantity / 21; | ||
|
||
// update rewards table | ||
for (auto producer : top_producers) { | ||
auto row = _rewards.find( producer.value ); | ||
if (row == _rewards.end()) { | ||
_rewards.emplace( get_self(), [&](auto& row) { | ||
row.owner = producer; | ||
row.quantity = reward; | ||
}); | ||
} else { | ||
_rewards.modify(row, get_self(), [&](auto& row) { | ||
row.quantity += reward; | ||
}); | ||
} | ||
} | ||
} | ||
|
||
} /// namespace eosio |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#pragma once | ||
|
||
#include <eosio/eosio.hpp> | ||
#include <eosio.system/eosio.system.hpp> | ||
#include <eosio.token/eosio.token.hpp> | ||
|
||
using namespace std; | ||
|
||
namespace eosio { | ||
/** | ||
* The `eosio.bpay` contract handles system bpay distribution. | ||
*/ | ||
class [[eosio::contract("eosio.bpay")]] bpay : public contract { | ||
public: | ||
using contract::contract; | ||
|
||
/** | ||
* ## TABLE `rewards` | ||
* | ||
* @param owner - block producer owner account | ||
* @param quantity - reward quantity in EOS | ||
* | ||
* ### example | ||
* | ||
* ```json | ||
* [ | ||
* { | ||
* "owner": "alice", | ||
* "quantity": "8.800 EOS" | ||
* } | ||
* ] | ||
* ``` | ||
*/ | ||
struct [[eosio::table("rewards")]] rewards_row { | ||
name owner; | ||
asset quantity; | ||
|
||
uint64_t primary_key() const { return owner.value; } | ||
}; | ||
typedef eosio::multi_index< "rewards"_n, rewards_row > rewards_table; | ||
|
||
/** | ||
* Claim rewards for a block producer. | ||
* | ||
* @param owner - block producer owner account | ||
*/ | ||
[[eosio::action]] | ||
void claimrewards( const name owner); | ||
|
||
[[eosio::on_notify("*::transfer")]] | ||
void on_transfer( const name from, const name to, const asset quantity, const string memo ); | ||
|
||
private: | ||
}; | ||
} /// namespace eosio |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
owner: | ||
threshold: 1 | ||
accounts: | ||
- permission: | ||
actor: eosio | ||
permission: active | ||
weight: 1 | ||
|
||
active: | ||
threshold: 2 | ||
accounts: | ||
- permission: | ||
actor: enf | ||
permission: active | ||
weight: 1 | ||
- permission: | ||
actor: eosio.prods | ||
permission: prod.minor | ||
weight: 1 | ||
- permission: | ||
actor: eosio.bpay | ||
permission: eosio.code | ||
weight: 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import { Asset, Name } from '@wharfkit/antelope' | ||
import { describe, expect, test } from 'bun:test' | ||
import { Blockchain, expectToThrow } from '@eosnetwork/vert' | ||
|
||
// Vert EOS VM | ||
const blockchain = new Blockchain() | ||
|
||
// 21 producers | ||
const alice = "bp.alice" | ||
const bob = "bp.bob" | ||
const charly = "bp.charly" | ||
const producers = [ | ||
alice, | ||
bob, | ||
charly, | ||
"bp.d", | ||
"bp.e", | ||
"bp.f", | ||
"bp.g", | ||
"bp.h", | ||
"bp.i", | ||
"bp.j", | ||
"bp.k", | ||
"bp.l", | ||
"bp.m", | ||
"bp.n", | ||
"bp.o", | ||
"bp.p", | ||
"bp.q", | ||
"bp.r", | ||
"bp.s", | ||
"bp.t", | ||
"bp.u", | ||
] | ||
const standby = "bp.standby" | ||
const inactive = "bp.inactive" | ||
blockchain.createAccounts(...producers, standby, inactive) | ||
|
||
const bpay_contract = 'eosio.bpay' | ||
const contracts = { | ||
bpay: blockchain.createContract(bpay_contract, bpay_contract, true), | ||
token: blockchain.createContract('eosio.token', 'external/eosio.token/eosio.token', true), | ||
system: blockchain.createContract('eosio', 'external/eosio.system/eosio', true), | ||
fake: { | ||
token: blockchain.createContract('fake.token', 'external/eosio.token/eosio.token', true), | ||
}, | ||
} | ||
|
||
function getProducers(owner: string) { | ||
const scope = Name.from("eosio").value.value | ||
const primary_key = Name.from(owner).value.value | ||
return contracts.system.tables | ||
.producers(scope) | ||
.getTableRow(primary_key) | ||
} | ||
|
||
function getRewards(owner: string) { | ||
const scope = Name.from(bpay_contract).value.value | ||
const primary_key = Name.from(owner).value.value | ||
const row = contracts.bpay.tables | ||
.rewards(scope) | ||
.getTableRow(primary_key) | ||
if (!row) return 0; | ||
return Asset.from(row.quantity).units.toNumber() | ||
} | ||
|
||
function getTokenBalance(account: string, symcode: string) { | ||
const scope = Name.from(account).value.value | ||
const primary_key = Asset.SymbolCode.from(symcode).value.value | ||
const row = contracts.token.tables | ||
.accounts(scope) | ||
.getTableRow(primary_key) | ||
if (!row) return 0; | ||
return Asset.from(row.balance).units.toNumber() | ||
} | ||
|
||
describe(bpay_contract, () => { | ||
test('eosio::setproducers', async () => { | ||
let votes = 100.0; | ||
for ( const producer of producers ) { | ||
await contracts.system.actions.setproducer([producer, votes, true]).send() | ||
Check failure on line 81 in eosio.bpay.spec.ts GitHub Actions / build-and-testTypeError: contracts.system.actions.setproducer is not a function. (In 'contracts.system.actions.setproducer([producer
|
||
votes -= 1.0; | ||
} | ||
await contracts.system.actions.setproducer([standby, 1, true]).send() // lowest vote | ||
await contracts.system.actions.setproducer([inactive, 200.0, false]).send() // highest vote, but inactive | ||
}) | ||
|
||
test('eosio.token::issue::EOS', async () => { | ||
const supply = `1000000000.0000 EOS` | ||
await contracts.token.actions.create(['eosio.token', supply]).send() | ||
Check failure on line 90 in eosio.bpay.spec.ts GitHub Actions / build-and-testTypeError: contracts.token.actions.create is not a function. (In 'contracts.token.actions.create(["eosio.token"
|
||
await contracts.token.actions.issue(['eosio.token', supply, '']).send() | ||
}) | ||
|
||
test("eosio.bpay::transfer", async () => { | ||
await contracts.token.actions.transfer(['eosio.token', bpay_contract, '2100.0000 EOS', '']).send(); | ||
Check failure on line 95 in eosio.bpay.spec.ts GitHub Actions / build-and-testTypeError: contracts.token.actions.transfer is not a function. (In 'contracts.token.actions.transfer(["eosio.token"
|
||
const before = { | ||
bpay: { | ||
balance: getTokenBalance(bpay_contract, 'EOS'), | ||
}, | ||
alice: { | ||
balance: getTokenBalance(alice, 'EOS'), | ||
}, | ||
bob: { | ||
balance: getTokenBalance(bob, 'EOS'), | ||
}, | ||
|
||
} | ||
await contracts.bpay.actions.claimrewards([alice]).send(alice); | ||
|
||
const after = { | ||
bpay: { | ||
balance: getTokenBalance(bpay_contract, 'EOS'), | ||
}, | ||
alice: { | ||
balance: getTokenBalance(alice, 'EOS'), | ||
}, | ||
bob: { | ||
balance: getTokenBalance(bob, 'EOS'), | ||
}, | ||
} | ||
|
||
// EOS | ||
expect(after.bpay.balance - before.bpay.balance).toBe(-1000000) | ||
expect(after.alice.balance - before.alice.balance).toBe(1000000) | ||
expect(after.bob.balance - before.bob.balance).toBe(0) | ||
}); | ||
|
||
test('eosio.bpay::claimrewards::error - no rewards to claim', async () => { | ||
const action = contracts.bpay.actions.claimrewards([alice]).send(alice); | ||
await expectToThrow(action, 'eosio_assert: no rewards to claim'); | ||
}) | ||
|
||
test.skip('eosio.bpay::claimrewards::error - not eligible to claim block rewards', async () => { | ||
for ( const owner of [inactive, standby] ) { | ||
const action = contracts.bpay.actions.claimrewards([owner]).send(owner); | ||
await expectToThrow(action, 'eosio_assert: not eligible to claim producer block pay'); | ||
} | ||
}) | ||
|
||
}) |
Oops, something went wrong.