Compare commits

..

1 Commits

Author SHA1 Message Date
Râu Cao
cf58b9214d CI
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2022-08-23 10:51:18 +01:00
32 changed files with 157 additions and 596 deletions

View File

@@ -24,7 +24,7 @@ steps:
- name: build contracts
image: gitea.kosmos.org/kredits/docker-ci:latest
commands:
- su drone -c 'npm run devchain -- --silent' &
- su drone -c 'npm run devchain' &
- sleep 5
- su drone -c 'npm run build'
depends_on:
@@ -32,8 +32,8 @@ steps:
- name: test
image: gitea.kosmos.org/kredits/docker-ci:latest
commands:
- su drone -c 'npm run devchain -- --silent' &
- sleep 5
# - su drone -c 'npm run devchain' &
# - sleep 5
- su drone -c 'npm test'
depends_on:
- setup

View File

@@ -1,14 +0,0 @@
name-template: 'v$RESOLVED_VERSION'
tag-template: 'v$RESOLVED_VERSION'
version-resolver:
major:
labels:
- release/major
minor:
labels:
- release/minor
- feature
patch:
labels:
- release/patch
default: patch

View File

@@ -1,11 +0,0 @@
name: Release Drafter
on:
pull_request:
types: [closed]
jobs:
release_drafter_job:
name: Update release notes draft
runs-on: ubuntu-latest
steps:
- name: Release Drafter
uses: https://github.com/raucao/gitea-release-drafter@dev

View File

@@ -1,5 +1,4 @@
[![npm](https://img.shields.io/npm/v/@kredits/contracts.svg)](https://www.npmjs.com/package/@kredits/contracts)
[![Build Status](https://drone.kosmos.org/api/badges/kredits/contracts/status.svg)](https://drone.kosmos.org/kredits/contracts)
[![npm](https://img.shields.io/npm/v/kredits-contracts.svg)](https://www.npmjs.com/package/kredits-contracts)
# Kredits Contracts
@@ -27,27 +26,27 @@ To run a local development chain run:
### Bootstrap
1. Run an EVM node and ipfs
1. Run an Ethereum node and ipfs
$ npm run devchain
$ ipfs daemon
2. Compile contracts and build ABIs
2. Compile contracts and build ABIs
(compiled artifacts will be in `/artifacts`)
$ npm run build
3. Deploy new upgradable contract proxies
3. Deploy new upgradable contract proxies
$ npm run deploy:dao
4. Execute seeds to create demo contributors, contributions, etc. (optional)
4. Execute seeds to create demo contributors, contributions, etc. (optional)
$ npm run seeds
**Step 2-4 is also summarized in `npm run bootstrap`**
5. Show contract addresses
5. Show contract addresses
$ cat lib/addresses.json
@@ -57,16 +56,6 @@ If you need to fund development accounts with devchain coins:
$ npm run fund # or hardhat fund --network localhost
## Specs / Testing
With a local development chain running:
$ hardhat test
If you add or change contract code, please make sure to add and/or adapt tests
accordingly. Don't worry, it's easy! You can use existing tests as a template
for new ones.
## Contract architecture
We use the [OpenZeppelin hardhat
@@ -156,3 +145,12 @@ To run the console on one of the non localhost networks you can also just pass
on the --network argument.
$ hardhat console --network rsk
## Known Issues
When resetting ganache Metamask might have an invalid transaction nonce and
transactions get rejected. Nonces in Ethereum must be incrementing and have no
gap.
To solve this reset the metamask account (Account -> Settings -> Reset Account)

View File

@@ -15,7 +15,7 @@ const contractCalls = [
name: 'raucao',
kind: 'person',
url: '',
github_username: 'raucao',
github_username: 'skddc',
github_uid: 842,
gitea_username: 'raucao',
wiki_username: 'Basti',
@@ -33,7 +33,7 @@ const contractCalls = [
['Contribution', 'add', [{ contributorId: 1, contributorIpfsHash: 'QmWKCYGr2rSf6abUPaTYqf98urvoZxGrb7dbspFZA6oyVF', date: '2019-04-11', amount: 500, kind: 'dev', description: '[67P/kredits-contracts] Test this thing', url: '' }, { gasLimit: 350000 }]],
['Contribution', 'add', [{ contributorId: 2, contributorIpfsHash: 'QmcHzEeAM26HV2zHTf5HnZrCtCtGdEccL5kUtDakAB7ozB', date: '2019-04-11', amount: 1500, kind: 'dev', description: '[67P/kredits-web] Reviewed stuff', url: '' }, { gasLimit: 350000 }]],
['Contribution', 'add', [{ contributorId: 1, contributorIpfsHash: 'QmWKCYGr2rSf6abUPaTYqf98urvoZxGrb7dbspFZA6oyVF', date: '2019-04-11', amount: 5000, kind: 'dev', description: '[67P/kredits-contracts] Add tests', url: '' }, { gasLimit: 350000 }]],
['Contribution', 'add', [{ contributorId: 1, contributorIpfsHash: 'QmWKCYGr2rSf6abUPaTYqf98urvoZxGrb7dbspFZA6oyVF', date: '2019-04-11', amount: 1500, kind: 'dev', description: '[67P/kredits-contracts] Add tests', url: '' }, { gasLimit: 350000 }]],
['Contribution', 'add', [{ contributorId: 1, contributorIpfsHash: 'QmWKCYGr2rSf6abUPaTYqf98urvoZxGrb7dbspFZA6oyVF', date: '2019-04-11', amount: 1500, kind: 'dev', description: '[67P/kredits-contracts] Introduce contribution token', url: '' }, { gasLimit: 350000 }]],
['Contribution', 'add', [{ contributorId: 2, contributorIpfsHash: 'QmcHzEeAM26HV2zHTf5HnZrCtCtGdEccL5kUtDakAB7ozB', date: '2019-04-11', amount: 5000, kind: 'dev', description: '[67P/kredits-web] Expense UI, first draft', url: '' }, { gasLimit: 350000 }]],

View File

@@ -2,6 +2,10 @@ pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
interface IToken {
function mintFor(address contributorAccount, uint256 amount, uint32 contributionId) external;
}
interface ContributorInterface {
function getContributorAddressById(uint32 contributorId) external view returns (address);
function getContributorIdByAddress(address contributorAccount) external view returns (uint32);
@@ -12,10 +16,12 @@ interface ContributorInterface {
contract Contribution is Initializable {
ContributorInterface public contributorContract;
IToken public tokenContract;
struct ContributionData {
uint32 contributorId;
uint32 amount;
bool claimed;
bytes32 hashDigest;
uint8 hashFunction;
uint8 hashSize;
@@ -36,16 +42,10 @@ contract Contribution is Initializable {
mapping(uint32 => ContributionData) public contributions;
uint32 public contributionsCount;
// Confirmation veto period
uint32 public blocksToWait;
// The address that deployed the contract
address public deployer;
// Data migration flag
bool public migrationDone;
event ContributionAdded(uint32 id, uint32 indexed contributorId, uint32 amount);
event ContributionClaimed(uint32 id, uint32 indexed contributorId, uint32 amount);
event ContributionVetoed(uint32 id, address vetoedByAccount);
modifier onlyCore {
@@ -53,19 +53,13 @@ contract Contribution is Initializable {
_;
}
modifier onlyDeployer {
require(msg.sender == deployer, "Deployer only");
_;
}
function initialize(uint32 blocksToWait_) public initializer {
deployer = msg.sender;
migrationDone = false;
blocksToWait = blocksToWait_;
}
function finishMigration() public onlyDeployer {
migrationDone = true;
function setTokenContract(address token) public {
require(address(tokenContract) == address(0) || contributorContract.addressIsCore(msg.sender), "Core only");
tokenContract = IToken(token);
}
function setContributorContract(address contributor) public {
@@ -139,13 +133,14 @@ contract Contribution is Initializable {
}
}
function getContribution(uint32 contributionId) public view returns (uint32 id, uint32 contributorId, uint32 amount, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256 confirmedAtBlock, bool exists, bool vetoed) {
function getContribution(uint32 contributionId) public view returns (uint32 id, uint32 contributorId, uint32 amount, bool claimed, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256 confirmedAtBlock, bool exists, bool vetoed) {
id = contributionId;
ContributionData storage c = contributions[id];
return (
id,
c.contributorId,
c.amount,
c.claimed,
c.hashDigest,
c.hashFunction,
c.hashSize,
@@ -155,29 +150,24 @@ contract Contribution is Initializable {
);
}
function add(uint32 amount, uint32 contributorId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, uint256 confirmedAtBlock, bool vetoed) public {
// require(canPerform(msg.sender, ADD_CONTRIBUTION_ROLE, new uint32[](0)), 'nope');
// TODO hubot neither has kredits nor a core account
require((confirmedAtBlock == 0 && vetoed == false) || migrationDone == false, 'extra arguments during migration only');
require(balanceOf(msg.sender) > 0 || contributorContract.addressIsCore(msg.sender), 'requires kredits or core status');
function add(uint32 amount, uint32 contributorId, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public {
//require(canPerform(msg.sender, ADD_CONTRIBUTION_ROLE, new uint32[](0)), 'nope');
require(balanceOf(msg.sender) > 0 || contributorContract.addressIsCore(msg.sender), 'must have kredits or core');
uint32 contributionId = contributionsCount + 1;
ContributionData storage c = contributions[contributionId];
c.exists = true;
c.amount = amount;
c.claimed = false;
c.contributorId = contributorId;
c.hashDigest = hashDigest;
c.hashFunction = hashFunction;
c.hashSize = hashSize;
if (confirmedAtBlock > 0) {
c.confirmedAtBlock = confirmedAtBlock;
if (contributionId < 10) {
c.confirmedAtBlock = block.number;
} else {
c.confirmedAtBlock = block.number + 1 + blocksToWait;
}
if (vetoed) { c.vetoed = true; }
contributionsCount++;
contributionOwner[contributionId] = contributorId;
@@ -187,15 +177,32 @@ contract Contribution is Initializable {
}
function veto(uint32 contributionId) public onlyCore {
ContributionData storage c = contributions[contributionId];
require(c.exists, 'NOT_FOUND');
require(!c.claimed, 'ALREADY_CLAIMED');
require(block.number < c.confirmedAtBlock, 'VETO_PERIOD_ENDED');
c.vetoed = true;
emit ContributionVetoed(contributionId, msg.sender);
}
function claim(uint32 contributionId) public {
ContributionData storage c = contributions[contributionId];
require(c.exists, 'NOT_FOUND');
require(!c.claimed, 'ALREADY_CLAIMED');
require(!c.vetoed, 'VETOED');
require(block.number >= c.confirmedAtBlock, 'NOT_CLAIMABLE');
c.claimed = true;
address contributorAccount = getContributorAddressById(c.contributorId);
uint256 amount = uint256(c.amount);
tokenContract.mintFor(contributorAccount, amount, contributionId);
emit ContributionClaimed(contributionId, c.contributorId, c.amount);
}
function exists(uint32 contributionId) public view returns (bool) {
return contributions[contributionId].exists;
}
}

View File

@@ -2,8 +2,7 @@ pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
interface IToken {
function mintFor(address contributorAccount, uint256 amount) external;
interface ITokenBalance {
function balanceOf(address contributorAccount) external view returns (uint256);
}
interface IContributionBalance {
@@ -12,9 +11,9 @@ interface IContributionBalance {
}
contract Contributor is Initializable {
address public deployer;
address deployer;
IContributionBalance public contributionContract;
IToken public tokenContract;
ITokenBalance public tokenContract;
struct Contributor {
address account;
@@ -22,15 +21,12 @@ contract Contributor is Initializable {
uint8 hashFunction;
uint8 hashSize;
bool exists;
uint32 kreditsWithdrawn;
}
mapping (address => uint32) public contributorIds;
mapping (uint32 => Contributor) public contributors;
uint32 public contributorsCount;
address public profileManager;
event ContributorProfileUpdated(uint32 id, bytes32 oldHashDigest, bytes32 newHashDigest); // what should be logged
event ContributorAccountUpdated(uint32 id, address oldAccount, address newAccount);
event ContributorAdded(uint32 id, address account);
@@ -40,18 +36,8 @@ contract Contributor is Initializable {
_;
}
modifier onlyContributors {
require(addressExists(msg.sender) && contributionContract.balanceOf(msg.sender) > 0, "Contributors only");
_;
}
function initialize(address profileManagerAddress) public initializer {
function initialize() public initializer {
deployer = msg.sender;
profileManager = profileManagerAddress;
}
function reinitialize(address profileManagerAddress) public reinitializer(2) {
profileManager = profileManagerAddress;
}
function setContributionContract(address contribution) public onlyCore {
@@ -61,7 +47,7 @@ contract Contributor is Initializable {
function setTokenContract(address token) public onlyCore {
require(address(tokenContract) == address(0) || addressIsCore(msg.sender), "Core only");
tokenContract = IToken(token);
tokenContract = ITokenBalance(token);
}
function coreContributorsCount() public view returns (uint32) {
@@ -91,12 +77,11 @@ contract Contributor is Initializable {
c.hashFunction = hashFunction;
c.hashSize = hashSize;
emit ContributorProfileUpdated(id, oldHashDigest, c.hashDigest);
ContributorProfileUpdated(id, oldHashDigest, c.hashDigest);
}
function addContributor(address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public {
require(!addressExists(account), "Address already in use");
require((msg.sender == profileManager) || addressIsCore(msg.sender), "Only core and profile manager");
function addContributor(address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize) public onlyCore {
require(!addressExists(account));
uint32 _id = contributorsCount + 1;
assert(!contributors[_id].exists); // this can not be acually
Contributor storage c = contributors[_id];
@@ -105,7 +90,6 @@ contract Contributor is Initializable {
c.hashFunction = hashFunction;
c.hashSize = hashSize;
c.account = account;
c.kreditsWithdrawn = 0;
contributorIds[account] = _id;
contributorsCount += 1;
@@ -148,7 +132,7 @@ contract Contributor is Initializable {
return contributors[id];
}
function getContributorById(uint32 _id) view public returns (uint32 id, address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, bool isCore, uint256 balance, uint32 totalKreditsEarned, uint256 contributionsCount, bool exists, uint256 kreditsWithdrawn) {
function getContributorById(uint32 _id) public view returns (uint32 id, address account, bytes32 hashDigest, uint8 hashFunction, uint8 hashSize, bool isCore, uint256 balance, uint32 totalKreditsEarned, uint256 contributionsCount, bool exists ) {
id = _id;
Contributor storage c = contributors[_id];
account = c.account;
@@ -160,19 +144,6 @@ contract Contributor is Initializable {
totalKreditsEarned = contributionContract.totalKreditsEarnedByContributor(_id, true);
contributionsCount = contributionContract.balanceOf(c.account);
exists = c.exists;
kreditsWithdrawn = c.kreditsWithdrawn;
}
function withdraw() public onlyContributors {
uint32 id = getContributorIdByAddress(msg.sender);
Contributor storage c = contributors[id];
// TODO check if we need a failsafe for unconfirmed or malicious txs
uint32 confirmedKredits = contributionContract.totalKreditsEarnedByContributor(id, true);
uint32 amountWithdrawable = confirmedKredits - c.kreditsWithdrawn;
require (amountWithdrawable > 0, "No kredits available");
c.kreditsWithdrawn += amountWithdrawable;
tokenContract.mintFor(msg.sender, amountWithdrawable);
}
}

View File

@@ -16,7 +16,6 @@ contract Reimbursement is Initializable {
struct ReimbursementData {
uint32 recipientId;
uint256 amount;
// TODO remove token entirely
address token;
bytes32 hashDigest;
uint8 hashFunction;

View File

@@ -7,35 +7,38 @@ interface ContributorInterface {
function getContributorAddressById(uint32 contributorId) external view returns (address);
function getContributorIdByAddress(address contributorAccount) external view returns (uint32);
function addressIsCore(address sender) external view returns (bool);
// TODO Maybe use for validation
// function exists(uint32 contributorId) public view returns (bool);
}
contract Token is Initializable, ERC20Upgradeable {
ContributorInterface public contributorContract;
using SafeMathUpgradeable for uint256;
address public contributorContractAddress;
address public contributionContract;
event KreditsMinted(address indexed recipient, uint256 amount);
event LogMint(address indexed recipient, uint256 amount, uint32 contributionId);
function initialize() public virtual initializer {
__ERC20_init("Kredits", "KS");
__ERC20_init('Kredits', 'KS');
}
function decimals() public view virtual override returns (uint8) {
return 0;
function setContributionContract(address contribution) public {
require(address(contributionContract) == address(0) || contributorContract.addressIsCore(msg.sender), "Core only");
contributionContract = contribution;
}
function setContributorContract(address contributor) public {
require(address(contributorContract) == address(0) || contributorContract.addressIsCore(msg.sender), "Core only");
contributorContract = ContributorInterface(contributor);
contributorContractAddress = contributor;
}
function mintFor(address contributorAccount, uint256 amount) public {
require(contributorContractAddress == msg.sender, "Only Contributor");
function mintFor(address contributorAccount, uint256 amount, uint32 contributionId) public {
require(contributionContract == msg.sender, "Only Contribution");
require(amount > 0, "INVALID_AMOUNT");
_mint(contributorAccount, amount);
emit KreditsMinted(contributorAccount, amount);
uint256 amountInWei = amount.mul(1 ether);
_mint(contributorAccount, amountInWei);
emit LogMint(contributorAccount, amount, contributionId);
}
}

View File

@@ -1,7 +1,6 @@
require("@nomiclabs/hardhat-waffle");
require("hardhat-deploy");
require("hardhat-deploy-ethers");
require("@nomicfoundation/hardhat-chai-matchers");
require("@openzeppelin/hardhat-upgrades");
const Kredits = require("./lib/kredits");

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"id","type":"uint32"},{"indexed":true,"internalType":"address","name":"addedByAccount","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ReimbursementAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"id","type":"uint32"},{"indexed":false,"internalType":"address","name":"vetoedByAccount","type":"address"}],"name":"ReimbursementVetoed","type":"event"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32","name":"recipientId","type":"uint32"},{"internalType":"bytes32","name":"hashDigest","type":"bytes32"},{"internalType":"uint8","name":"hashFunction","type":"uint8"},{"internalType":"uint8","name":"hashSize","type":"uint8"}],"name":"add","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"blocksToWait","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contributorContract","outputs":[{"internalType":"contract ContributorInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"reimbursementId","type":"uint32"}],"name":"exists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"reimbursementId","type":"uint32"}],"name":"get","outputs":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"uint32","name":"recipientId","type":"uint32"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes32","name":"hashDigest","type":"bytes32"},{"internalType":"uint8","name":"hashFunction","type":"uint8"},{"internalType":"uint8","name":"hashSize","type":"uint8"},{"internalType":"uint256","name":"confirmedAtBlock","type":"uint256"},{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"bool","name":"vetoed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"contributorId","type":"uint32"}],"name":"getContributorAddressById","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contributorAccount","type":"address"}],"name":"getContributorIdByAddress","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"reimbursements","outputs":[{"internalType":"uint32","name":"recipientId","type":"uint32"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes32","name":"hashDigest","type":"bytes32"},{"internalType":"uint8","name":"hashFunction","type":"uint8"},{"internalType":"uint8","name":"hashSize","type":"uint8"},{"internalType":"uint256","name":"confirmedAtBlock","type":"uint256"},{"internalType":"bool","name":"vetoed","type":"bool"},{"internalType":"bool","name":"exists","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reimbursementsCount","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contributor","type":"address"}],"name":"setContributorContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"confirmedOnly","type":"bool"}],"name":"totalAmount","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"reimbursementId","type":"uint32"}],"name":"veto","outputs":[],"stateMutability":"nonpayable","type":"function"}]
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"id","type":"uint32"},{"indexed":true,"internalType":"address","name":"addedByAccount","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ReimbursementAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"id","type":"uint32"},{"indexed":false,"internalType":"address","name":"vetoedByAccount","type":"address"}],"name":"ReimbursementVetoed","type":"event"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint32","name":"recipientId","type":"uint32"},{"internalType":"bytes32","name":"hashDigest","type":"bytes32"},{"internalType":"uint8","name":"hashFunction","type":"uint8"},{"internalType":"uint8","name":"hashSize","type":"uint8"}],"name":"add","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"blocksToWait","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contributorContract","outputs":[{"internalType":"contract ContributorInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"reimbursementId","type":"uint32"}],"name":"exists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"reimbursementId","type":"uint32"}],"name":"get","outputs":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"uint32","name":"recipientId","type":"uint32"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes32","name":"hashDigest","type":"bytes32"},{"internalType":"uint8","name":"hashFunction","type":"uint8"},{"internalType":"uint8","name":"hashSize","type":"uint8"},{"internalType":"uint256","name":"confirmedAtBlock","type":"uint256"},{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"bool","name":"vetoed","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"contributorId","type":"uint32"}],"name":"getContributorAddressById","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contributorAccount","type":"address"}],"name":"getContributorIdByAddress","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"reimbursements","outputs":[{"internalType":"uint32","name":"recipientId","type":"uint32"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes32","name":"hashDigest","type":"bytes32"},{"internalType":"uint8","name":"hashFunction","type":"uint8"},{"internalType":"uint8","name":"hashSize","type":"uint8"},{"internalType":"uint256","name":"confirmedAtBlock","type":"uint256"},{"internalType":"bool","name":"vetoed","type":"bool"},{"internalType":"bool","name":"exists","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"reimbursementsCount","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contributor","type":"address"}],"name":"setContributorContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"confirmedOnly","type":"bool"}],"name":"totalAmount","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"reimbursementId","type":"uint32"}],"name":"veto","outputs":[],"stateMutability":"nonpayable","type":"function"}]

View File

@@ -1 +1 @@
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"KreditsMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contributorContract","outputs":[{"internalType":"contract ContributorInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contributorContractAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"contributorAccount","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mintFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contributor","type":"address"}],"name":"setContributorContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"contributionId","type":"uint32"}],"name":"LogMint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contributionContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contributorContract","outputs":[{"internalType":"contract ContributorInterface","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"contributorAccount","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint32","name":"contributionId","type":"uint32"}],"name":"mintFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"contribution","type":"address"}],"name":"setContributionContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"contributor","type":"address"}],"name":"setContributorContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]

View File

@@ -1,14 +1,8 @@
{
"31": {
"Contributor": "0xf1073Dab6e305583F95e451Cba449bB867a6e3Fd",
"Contribution": "0x1C531F824e339cD37D75B7F391cB8E42e0E0d4bd",
"Token": "0x56F64C3BB45e6a248F4C783f5a1633E53D6A2371",
"Reimbursement": "0x9C5fFBFba2570A9b31D60338453C5480Ce74B342"
},
"1337": {
"Contributor": "0xCc66f9A3cA2670972938FAD91d0865c4a62DFB25",
"Contribution": "0x8999CaBc43E28202c5A2257f2a95A45b1F8A62BD",
"Token": "0xe082678eCF749982e33Ea6839852a8cd989aEDE2",
"Reimbursement": "0x984f797d26d3da2E9b9f8Ae4eeFEACC60fCAA90C"
"Contributor": "0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0",
"Contribution": "0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9",
"Token": "0x0165878A594ca255338adfa4d48449f69242Eb8F",
"Reimbursement": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6"
}
}

View File

@@ -58,7 +58,7 @@ class Contribution extends Record {
ipfsHashAttr.hashSize,
];
return this.contract.add(...contribution, 0, false, callOptions);
return this.contract.add(...contribution, callOptions);
});
}

View File

@@ -16,7 +16,7 @@ class Reimbursement extends Record {
}
getData (id) {
return this.contract.get(id);
return this.contract.getReimbursement(id);
}
async add (attrs, callOptions = {}) {

View File

@@ -69,10 +69,7 @@ class Kredits {
if (wallet) {
signer = wallet.connect(ethProvider);
} else if (ethProvider.getSigner) {
// Only useful for reading data, not writing. The (unused) address is
// necessary because without an address, ethers.js will try to look up
// the provider's account 0, which doesn't work on our public RSK nodes.
signer = ethProvider.getSigner('0xfa77675540E550b911a6AABF3805ac17C6641ec1');
signer = ethProvider.getSigner();
}
return new Kredits(ethProvider, signer, kreditsOptions);
}

40
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@kredits/contracts",
"version": "7.2.0",
"name": "kredits-contracts",
"version": "7.0.0-beta.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@kredits/contracts",
"version": "7.2.0",
"name": "kredits-contracts",
"version": "7.0.0-beta.0",
"license": "MIT",
"dependencies": {
"@kosmos/schemas": "^3.1.0",
@@ -20,7 +20,7 @@
"@nomicfoundation/hardhat-chai-matchers": "^1.0.3",
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@openzeppelin/contracts-upgradeable": "^4.8.3",
"@openzeppelin/contracts-upgradeable": "^4.3.2",
"@openzeppelin/hardhat-upgrades": "^1.10.0",
"async-each-series": "^1.1.0",
"chai": "^4.3.6",
@@ -1216,18 +1216,12 @@
"dependencies": {
"@types/sinon-chai": "^3.2.3",
"@types/web3": "1.0.19"
},
"peerDependencies": {
"@nomiclabs/hardhat-ethers": "^2.0.0",
"ethereum-waffle": "^3.2.0",
"ethers": "^5.0.0",
"hardhat": "^2.0.0"
}
},
"node_modules/@openzeppelin/contracts-upgradeable": {
"version": "4.8.3",
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.8.3.tgz",
"integrity": "sha512-SXDRl7HKpl2WDoJpn7CK/M9U4Z8gNXDHHChAKh0Iz+Wew3wu6CmFYBeie3je8V0GSXZAIYYwUktSrnW/kwVPtg==",
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.5.2.tgz",
"integrity": "sha512-xgWZYaPlrEOQo3cBj97Ufiuv79SPd8Brh4GcFYhPgb6WvAq4ppz8dWKL6h+jLAK01rUqMRp/TS9AdXgAeNvCLA==",
"dev": true
},
"node_modules/@openzeppelin/hardhat-upgrades": {
@@ -1714,9 +1708,9 @@
}
},
"node_modules/@types/sinon": {
"version": "10.0.13",
"resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.13.tgz",
"integrity": "sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==",
"version": "10.0.11",
"resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.11.tgz",
"integrity": "sha512-dmZsHlBsKUtBpHriNjlK0ndlvEh8dcb9uV9Afsbt89QIyydpC7NcR+nWlAhASfy3GHnxTl4FX/aKE7XZUt/B4g==",
"dev": true,
"dependencies": {
"@types/sinonjs__fake-timers": "*"
@@ -20951,9 +20945,9 @@
}
},
"@openzeppelin/contracts-upgradeable": {
"version": "4.8.3",
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.8.3.tgz",
"integrity": "sha512-SXDRl7HKpl2WDoJpn7CK/M9U4Z8gNXDHHChAKh0Iz+Wew3wu6CmFYBeie3je8V0GSXZAIYYwUktSrnW/kwVPtg==",
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/@openzeppelin/contracts-upgradeable/-/contracts-upgradeable-4.5.2.tgz",
"integrity": "sha512-xgWZYaPlrEOQo3cBj97Ufiuv79SPd8Brh4GcFYhPgb6WvAq4ppz8dWKL6h+jLAK01rUqMRp/TS9AdXgAeNvCLA==",
"dev": true
},
"@openzeppelin/hardhat-upgrades": {
@@ -21400,9 +21394,9 @@
}
},
"@types/sinon": {
"version": "10.0.13",
"resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.13.tgz",
"integrity": "sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ==",
"version": "10.0.11",
"resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.11.tgz",
"integrity": "sha512-dmZsHlBsKUtBpHriNjlK0ndlvEh8dcb9uV9Afsbt89QIyydpC7NcR+nWlAhASfy3GHnxTl4FX/aKE7XZUt/B4g==",
"dev": true,
"requires": {
"@types/sinonjs__fake-timers": "*"

View File

@@ -1,7 +1,7 @@
{
"name": "@kredits/contracts",
"version": "7.2.0",
"description": "Smart contracts and JavaScript API for Kredits",
"name": "kredits-contracts",
"version": "7.0.0-beta.0",
"description": "Ethereum contracts and npm wrapper for Kredits",
"main": "./lib/kredits.js",
"directories": {
"test": "test"
@@ -42,7 +42,7 @@
"@nomicfoundation/hardhat-chai-matchers": "^1.0.3",
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@openzeppelin/contracts-upgradeable": "^4.8.3",
"@openzeppelin/contracts-upgradeable": "^4.3.2",
"@openzeppelin/hardhat-upgrades": "^1.10.0",
"async-each-series": "^1.1.0",
"chai": "^4.3.6",

View File

@@ -36,7 +36,7 @@ async function main() {
const blocksVetoPeriod = 40320; // 7 days; 15 seconds block time
await deployContractProxy('Contributor', [ '0x0000000000000000000000000000000000000000' ] );
await deployContractProxy('Contributor');
await deployContractProxy('Contribution', [ blocksVetoPeriod ]);
await deployContractProxy('Token');
await deployContractProxy('Reimbursement');
@@ -57,6 +57,16 @@ async function main() {
return res.wait();
}).catch(handleError);
console.log('Calling Contribution#setTokenContract')
await contracts.Contribution.functions
.setTokenContract(contracts.Token.address)
.then(res => {
console.log(`...transaction published: ${res.hash}`);
return res.wait();
}).catch(handleError);
console.log('Calling Contribution#setContributorContract')
await contracts.Contribution.functions
.setContributorContract(contracts.Contributor.address)
@@ -65,6 +75,14 @@ async function main() {
return res.wait();
}).catch(handleError);
console.log('Calling Token#setContributionContract')
await contracts.Token.functions
.setContributionContract(contracts.Contribution.address)
.then(res => {
console.log(`...transaction published: ${res.hash}`);
return res.wait();
}).catch(handleError);
console.log('Calling Token#setContributorContract')
await contracts.Token.functions
.setContributorContract(contracts.Contributor.address)

View File

@@ -1,4 +1,3 @@
const fs = require('fs');
const Kredits = require('../../lib/kredits');
async function main() {
@@ -8,7 +7,7 @@ async function main() {
console.log(`Using Contribution at: ${kredits.Contribution.contract.address}`);
const count = await kredits.Contribution.count;
const currentBlockHeight = await hre.ethers.provider.getBlockNumber();
const currentBlockHeight = await XMLHttpRequest.ethers.provider.getBlockNumber();
const backup = {};
const promises = [];

View File

@@ -1,44 +0,0 @@
const fs = require('fs');
const Kredits = require('../../lib/kredits');
async function main() {
kredits = new Kredits(hre.ethers.provider, hre.ethers.provider.getSigner())
await kredits.init();
console.log(`Using Reimbursement at: ${kredits.Reimbursement.contract.address}`);
const count = await kredits.Reimbursement.count;
const currentBlockHeight = await hre.ethers.provider.getBlockNumber();
const backup = {};
const promises = [];
for (let i = 1; i <= count; i++) {
promises.push(new Promise((resolve, reject) => {
setTimeout(async () => {
console.log(`Loading reimbursement #${i}`);
await kredits.Reimbursement.contract.get(i).then(contractData => {
backup[i] = {
recipientId: contractData.recipientId,
amount: contractData.amount,
token: contractData.token,
hashDigest: contractData.hashDigest,
hashFunction: contractData.hashFunction,
hashSize: contractData.hashSize,
confirmedAtBlock: contractData.confirmedAtBlock,
confirmed: contractData.confirmedAtBlock <= currentBlockHeight,
vetoed: contractData.vetoed,
id: contractData.id,
}
resolve();
});
}, 100 * i);
}));
}
await Promise.all(promises).then(() => {
fs.writeFileSync("./data/reimbursements.json", JSON.stringify(backup, null, 2));
console.log("Exported");
});
}
main();

View File

@@ -1,41 +0,0 @@
const fs = require('fs');
const Kredits = require('../../lib/kredits');
async function main() {
kredits = new Kredits(hre.ethers.provider, hre.ethers.provider.getSigner())
await kredits.init();
console.log(`Using Contribution at: ${kredits.Contribution.contract.address}`);
const count = await kredits.Contribution.count;
console.log(`Currently ${count} entries`);
try {
const data = fs.readFileSync("./data/contributions.json");
const contributions = JSON.parse(data);
const ids = Object.keys(contributions)
.map(k => parseInt(k))
.sort(function(a, b){return a-b});
const currentBlockHeight = await kredits.provider.getBlockNumber();
const confirmationPeriod = 40320 // blocks
const unconfirmedHeight = currentBlockHeight + confirmationPeriod;
for (const contributionId of ids) {
const c = contributions[contributionId.toString()];
const confirmedAtBlock = c.confirmed ? currentBlockHeight : unconfirmedHeight;
const result = await kredits.Contribution.contract.add(
c.amount, c.contributorId,
c.hashDigest, c.hashFunction, c.hashSize,
confirmedAtBlock, c.vetoed
);
console.log(`Adding contribution #${contributionId}: ${result.hash}`);
await result.wait();
};
} catch(e) {
console.error(e);
}
}
main();

View File

@@ -6,8 +6,7 @@ async function main() {
await kredits.init();
console.log(`Using Contributor at: ${kredits.Contributor.contract.address}`);
const count = await kredits.Contributor.count;
console.log(`Currently ${count} entries`);
try {
const data = fs.readFileSync("./data/contributors.json");
const contributors = JSON.parse(data);
@@ -23,11 +22,11 @@ async function main() {
contributor.hashFunction,
contributor.hashSize,
);
console.log(`Adding contributor #${contributorId}: ${result.hash}`);
await result.wait();
// await result.wait();
console.log(`Added contributor #${contributorId}: ${result.hash}`);
};
} catch(e) {
console.error(e);
console.log(e);
}
}

View File

@@ -1,36 +0,0 @@
const fs = require('fs');
const Kredits = require('../../lib/kredits');
async function main() {
kredits = new Kredits(hre.ethers.provider, hre.ethers.provider.getSigner())
await kredits.init();
console.log(`Using Reimbursement at: ${kredits.Reimbursement.contract.address}`);
const count = await kredits.Reimbursement.count;
console.log(`Currently ${count} entries`);
try {
const data = fs.readFileSync("./data/reimbursements.json");
const reimbursements = JSON.parse(data);
const ids = Object.keys(reimbursements)
.map(k => parseInt(k))
.sort(function(a, b) { return a - b });
for (const reimbursementId of ids) {
const reimbursement = reimbursements[reimbursementId.toString()];
const result = await kredits.Reimbursement.contract.add(
reimbursement.amount,
reimbursement.token,
reimbursement.recipientId,
reimbursement.hashDigest,
reimbursement.hashFunction,
reimbursement.hashSize,
);
console.log(`Adding reimbursement #${reimbursementId}: ${result.hash}`);
await result.wait();
};
} catch (e) {
console.error(e);
}
}
main();

View File

@@ -12,7 +12,7 @@ async function main() {
console.log(`Using Contribution at: ${kredits.Contribution.contract.address}`);
const table = new Table({
head: ['ID', 'Contributor ID', 'Description', 'Amount', 'Confirmed?', 'Vetoed?', 'IPFS']
head: ['ID', 'Contributor ID', 'Description', 'Amount', 'Confirmed?', 'Vetoed?', 'Claimed?', 'IPFS']
})
try {
@@ -31,6 +31,7 @@ async function main() {
c.amount.toString(),
`${confirmed} (${c.confirmedAtBlock})`,
c.vetoed,
c.claimed,
c.ipfsHash
])
});

View File

@@ -6,8 +6,6 @@ async function main() {
await kredits.init();
console.log(`Using Contributor at: ${kredits.Contributor.contract.address}`);
const count = await kredits.Contributors.count;
console.log(`Currently ${count} entries`);
const table = new Table({
head: ['ID', 'Account', 'Name', 'Core?', 'Balance', 'Kredits earned', 'Contributions count', 'IPFS']

View File

@@ -7,17 +7,7 @@ async function main() {
await kredits.init();
const ContributorV2 = await ethers.getContractFactory("Contributor");
const contributor = await upgrades.upgradeProxy(
kredits.Contributor.address,
ContributorV2,
{
call: {
fn: "reinitialize",
args: [
"0xc80d2513277FA04B10403E2D1d7dAa86F931f4d1"
]
}
});
const contributor = await upgrades.upgradeProxy(kredits.Contributor.address, ContributorV2);
console.log("Contributor upgraded");
console.log(`Contributor address: ${contributor.address}`);
@@ -26,4 +16,4 @@ async function main() {
console.log("DONE!");
}
main()
main();

19
test/Contribution.js Normal file
View File

@@ -0,0 +1,19 @@
const { expect } = require("chai");
const { ethers, upgrades } = require("hardhat");
let hardhatContribution;
describe("Contribution contract", function () {
describe("Deployment", function () {
before(async function () {
// const [owner] = await ethers.getSigners();
const Contribution = await ethers.getContractFactory("Contribution");
hardhatContribution = await upgrades.deployProxy(Contribution, [40321]);
});
it("sets the veto confirmation period", async function () {
expect(await hardhatContribution.blocksToWait()).to.equal(40321);
});
});
});

View File

@@ -1,157 +0,0 @@
const { expect } = require("chai");
const { ethers, upgrades } = require("hardhat");
const { anyValue } = require("@nomicfoundation/hardhat-chai-matchers/withArgs");
let owner, addr1, addr2, addr3, addr4, addr5, addr6, addr7;
let Contribution, Contributor;
describe("Contribution contract", async function () {
before(async function () {
[owner, addr1, addr2, addr3, addr4, addr5, addr6, addr7] = await ethers.getSigners();
let accounts = [owner, addr1, addr2, addr3, addr4, addr5, addr6, addr7];
const contributorFactory = await ethers.getContractFactory("Contributor");
Contributor = await upgrades.deployProxy(contributorFactory, ["0x2946fFfd31096435cb0fc927D306E1C006C5D1aF"]);
for (const account of accounts) {
await Contributor.addContributor(account.address, "0x99b8afd7b266e19990924a8be9099e81054b70c36b20937228a77a5cf75723b8", 18, 32);
}
});
describe("initialize()", function () {
before(async function () {
// const [owner] = await ethers.getSigners();
const contributionFactory = await ethers.getContractFactory("Contribution");
Contribution = await upgrades.deployProxy(contributionFactory, [40321]);
});
it("sets the veto confirmation period", async function () {
expect(await Contribution.blocksToWait()).to.equal(40321);
});
it("sets the data migration flag", async function () {
expect(await Contribution.migrationDone()).to.be.false;
});
it("sets the deployer address", async function () {
expect(await Contribution.deployer()).to.equal(owner.address);
expect(await Contribution.deployer()).to.not.equal(addr1.address);
});
});
describe("finishMigration()", function () {
before(async function () {
const contributionFactory = await ethers.getContractFactory("Contribution");
Contribution = await upgrades.deployProxy(contributionFactory, [40321]);
await Contribution.setContributorContract(Contributor.address).then(res => res.wait())
});
it("does not allow random accounts to mark the migration as finished", async function () {
await expect(Contribution.connect(addr1).finishMigration()).to.be.revertedWith("Deployer only");
expect(await Contribution.migrationDone()).to.be.false;
});
it("allows the deployer to mark the migration as finished", async function () {
await Contribution.finishMigration();
expect(await Contribution.migrationDone()).to.equal(true);
});
});
describe("add()", function () {
before(async function () {
const contributionFactory = await ethers.getContractFactory("Contribution");
Contribution = await upgrades.deployProxy(contributionFactory, [40321]);
await Contribution.setContributorContract(Contributor.address).then(res => res.wait())
await Contribution.finishMigration();
});
it("does not allow non-contributors to add a contribution", async function () {
await expect(Contribution.connect(addr7).add(
500, 1,
"0xe794f010e617449719c64076546254129f63a6d16cf200031afa646aeb35777f",
18, 32, 0, false
)).to.be.revertedWith("requires kredits or core status");
expect(await Contribution.contributionsCount()).to.equal(0);
});
it("does not allow special arguments outside of a migration", async function () {
await expect(Contribution.connect(addr7).add(
500, 1,
"0xe794f010e617449719c64076546254129f63a6d16cf200031afa646aeb35777f",
18, 32, 23000, true
)).to.be.revertedWith("extra arguments during migration only");
expect(await Contribution.contributionsCount()).to.equal(0);
});
it("allows core contributors to add a contribution", async function () {
await Contribution.connect(addr2).add(
2001, 1,
"0xe794f010e617449719c64076546254129f63a6d16cf200031afa646aeb35777f",
18, 32, 0, false
);
expect(await Contribution.contributionsCount()).to.equal(1);
});
it("allows contributors to add a contribution", async function () {
// Issue some kredits for new contributor #8 (addr7)
await Contribution.connect(addr1).add(
5000, 8,
"0xe794f010e617449719c64076546254129f63a6d16cf200031afa646aeb35777f",
18, 32, 0, false
);
await Contribution.connect(addr7).add(
1500, 1,
"0xe794f010e617449719c64076546254129f63a6d16cf200031afa646aeb35777f",
18, 32, 0, false
);
expect(await Contribution.contributionsCount()).to.equal(3);
});
it("sets confirmedAtBlock to current block plus blocksToWait", async function () {
await Contribution.connect(addr1).add(
500, 3,
"0xe794f010e617449719c64076546254129f63a6d16cf200031afa646aeb35777f",
18, 32, 0, false
);
expect(await Contribution.contributionsCount()).to.equal(4);
const c = await Contribution.getContribution(4);
const currentBlockNumber = await kredits.provider.getBlockNumber();
expect(c['confirmedAtBlock']).to.equal(currentBlockNumber + 1 + 40321);
});
it("emits a ContributionAdded event", async function () {
await expect(Contribution.connect(addr1).add(
2001, 1,
"0xe794f010e617449719c64076546254129f63a6d16cf200031afa646aeb35777f",
18, 32, 0, false
)).to.emit(Contribution, "ContributionAdded").withArgs(anyValue, 1, 2001);
});
describe("with extra arguments during migration", async function () {
before(async function () {
const contributionFactory = await ethers.getContractFactory("Contribution");
Contribution = await upgrades.deployProxy(contributionFactory, [40321]);
await Contribution.setContributorContract(Contributor.address);
});
it("allows to add a contribution with custom confirmedAtBlock", async function () {
await Contribution.connect(addr2).add(
2001, 1,
"0xe794f010e617449719c64076546254129f63a6d16cf200031afa646aeb35777f",
18, 32, 23000, false
);
expect(await Contribution.contributionsCount()).to.equal(1);
const c = await Contribution.getContribution(1);
expect(c['confirmedAtBlock'].toNumber()).to.equal(23000);
});
it("allows to add a vetoed contribution", async function () {
await Contribution.connect(addr2).add(
2001, 1,
"0xe794f010e617449719c64076546254129f63a6d16cf200031afa646aeb35777f",
18, 32, 23000, true
);
expect(await Contribution.contributionsCount()).to.equal(2);
const c = await Contribution.getContribution(2);
expect(c['vetoed']).to.be.true;
});
});
});
});

View File

@@ -1,122 +0,0 @@
const { expect } = require("chai");
const { ethers, upgrades } = require("hardhat");
let owner, addr1, addr2, addr3, addr4, addr5, addr6, addr7, addr8;
let Contribution, Contributor, Token;
describe("Contributor contract", async function () {
before(async function () {
[owner, addr1, addr2, addr3, addr4, addr5, addr6, addr7, addr8] = await ethers.getSigners();
const contributorFactory = await ethers.getContractFactory("Contributor");
Contributor = await upgrades.deployProxy(contributorFactory, [addr8.address]);
const contributionFactory = await ethers.getContractFactory("Contribution");
Contribution = await upgrades.deployProxy(contributionFactory, [40321]);
const tokenFactory = await ethers.getContractFactory("Token");
Token = await upgrades.deployProxy(tokenFactory);
await Contributor.setTokenContract(Token.address);
await Contributor.setContributionContract(Contribution.address);
await Contribution.setContributorContract(Contributor.address);
await Token.setContributorContract(Contributor.address);
let accounts = [owner, addr1, addr2, addr3, addr4, addr5, addr6, addr7];
for (const account of accounts) {
await Contributor.addContributor(account.address, "0x99b8afd7b266e19990924a8be9099e81054b70c36b20937228a77a5cf75723b8", 18, 32);
}
});
describe("initialize()", function () {
it("sets the deployer address", async function () {
expect(await Contributor.deployer()).to.equal(owner.address);
expect(await Contributor.deployer()).to.not.equal(addr1.address);
});
it("sets a profile manager address", async function () {
expect(await Contributor.profileManager()).to.equal(addr8.address);
});
});
describe("add()", function () {
it("does not allow random accounts to create a contributor profile", async function () {
await expect(Contributor.connect(addr7).addContributor(
"0x608FD4b95116Ea616990Aaeb1d4f1ce07612f261",
"0x1d9de6de5c72eedca6d7a5e8a9159e2f5fe676506aece3000acefcc821723429",
18, 32
)).to.be.revertedWith("Only core and profile manager");
expect(await Contributor.contributorsCount()).to.equal(8);
});
it("allows core contributors to create a contributor profile", async function () {
await Contributor.connect(addr1).addContributor(
"0x608FD4b95116Ea616990Aaeb1d4f1ce07612f261",
"0x1d9de6de5c72eedca6d7a5e8a9159e2f5fe676506aece3000acefcc821723429",
18, 32
);
expect(await Contributor.contributorsCount()).to.equal(9);
const c = await Contributor.getContributorById(9);
expect(c['account']).to.equal("0x608FD4b95116Ea616990Aaeb1d4f1ce07612f261");
expect(c['kreditsWithdrawn']).to.equal(0);
});
it("does not allow to create accounts with an existing address", async function () {
await expect(Contributor.connect(addr1).addContributor(
"0x608FD4b95116Ea616990Aaeb1d4f1ce07612f261",
"0x1d9de6de5c72eedca6d7a5e8a9159e2f5fe676506aece3000acefcc821723429",
18, 32
)).to.be.revertedWith("Address already in use");
expect(await Contributor.contributorsCount()).to.equal(9);
});
it("emits a ContributorAdded event", async function () {
await expect(Contributor.connect(addr1).addContributor(
"0x765E88b4F9a59C3a3b300C6eFF9E6E9fDDf9FbD9",
"0xcfbeeadc244dfdc55bbad50d431871439df067970db84c73023956c96a6f5df2",
18, 32
)).to.emit(Contributor, "ContributorAdded").withArgs(10, "0x765E88b4F9a59C3a3b300C6eFF9E6E9fDDf9FbD9");
});
it("allows the profile manager account to create a contributor profile", async function () {
await Contributor.connect(addr8).addContributor(
"0x954712B8703Df5255A219B139ba7BFC256E72a15",
"0x1d9de6de5c72eedca6d7a5e8a9159e2f5fe676506aece3000acefcc821723429",
18, 32
);
expect(await Contributor.contributorsCount()).to.equal(11);
const c = await Contributor.getContributorById(11);
expect(c['account']).to.equal("0x954712B8703Df5255A219B139ba7BFC256E72a15");
});
});
describe("withdraw()", function () {
before(async function () {
// Add some pre-confirmed contributions (confirmedAtBlock 1)
await Contribution.add(
1500, 2, "0xe794f010e617449719c64076546254129f63a6d16cf200031afa646aeb35777f",
18, 32, 1, false
);
await Contribution.add(
5000, 2, "0xe794f010e617449719c64076546254129f63a6d16cf200031afa646aeb35777f",
18, 32, 1, false
);
await Contribution.finishMigration();
});
it("requires the transaction sender to be a contributor", async function () {
await expect(Contributor.withdraw()).to.be.revertedWith("Contributors only");
});
it("executes a withdrawal of all available ERC20 kredits", async function () {
let c = await Contributor.getContributorById(2);
expect(c['balance']).to.equal(0);
await Contributor.connect(addr1).withdraw();
c = await Contributor.getContributorById(2);
expect(c['balance']).to.equal(6500);
expect(c['kreditsWithdrawn']).to.equal(6500);
});
it("requires the withdrawable amount to be larger than 0", async function () {
await expect(Contributor.connect(addr1).withdraw()).to.be.revertedWith("No kredits available");
});
});
});