Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions contracts/schemes/ReputationFromToken.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
pragma solidity ^0.5.4;

import "../controller/ControllerInterface.sol";
import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";

/**
* @title A scheme for reputation allocation according to token balances
*/

contract ReputationFromToken {

IERC20 public tokenContract;
// beneficiary -> bool
mapping(address => bool) public redeems;
Avatar public avatar;

event Redeem(address indexed _beneficiary, address indexed _sender, uint256 _amount);

/**
* @dev initialize
* @param _avatar the avatar to mint reputation from
* @param _tokenContract the token contract
*/
function initialize(Avatar _avatar, IERC20 _tokenContract) external
{
require(avatar == Avatar(0), "can be called only one time");
require(_avatar != Avatar(0), "avatar cannot be zero");
tokenContract = _tokenContract;
avatar = _avatar;
}

/**
* @dev redeem function
* @param _beneficiary the beneficiary address to redeem for
*/
function redeem(address _beneficiary) public {
require(avatar != Avatar(0), "should initialize first");
require(redeems[msg.sender] == false, "redeeming twice from the same account is not allowed");
redeems[msg.sender] = true;
uint256 tokenAmount = tokenContract.balanceOf(msg.sender);
require(
ControllerInterface(
avatar.owner())
.mintReputation(tokenAmount, _beneficiary, address(avatar)), "mint reputation should succeed");
emit Redeem(_beneficiary, msg.sender, tokenAmount);
}
}
6 changes: 5 additions & 1 deletion contracts/test/ExternalTokenLockerMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
contract ExternalTokenLockerMock is Ownable {

// user => amount
mapping (address => uint) public lockedTokenBalances;
mapping (address => uint256) public lockedTokenBalances;

function lock(uint256 _amount, address _beneficiary) public onlyOwner {
lockedTokenBalances[_beneficiary] = _amount;
}

function balanceOf(address _beneficiary) public view returns(uint256) {
return lockedTokenBalances[_beneficiary];
}
}
70 changes: 70 additions & 0 deletions test/reputationfromtoken.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
const helpers = require('./helpers');
const DaoCreator = artifacts.require("./DaoCreator.sol");
const ControllerCreator = artifacts.require("./ControllerCreator.sol");
const constants = require('./constants');
var ReputationFromToken = artifacts.require("./ReputationFromToken.sol");
var ExternalTokenLockerMock = artifacts.require("./ExternalTokenLockerMock.sol");

const setup = async function (accounts, _initialize = true) {
var testSetup = new helpers.TestSetup();
var controllerCreator = await ControllerCreator.new({gas: constants.ARC_GAS_LIMIT});
testSetup.daoCreator = await DaoCreator.new(controllerCreator.address,{gas:constants.ARC_GAS_LIMIT});
testSetup.org = await helpers.setupOrganization(testSetup.daoCreator,accounts[0],1000,1000);
testSetup.extetnalTokenLockerMock = await ExternalTokenLockerMock.new();
await testSetup.extetnalTokenLockerMock.lock(100,accounts[0]);
await testSetup.extetnalTokenLockerMock.lock(200,accounts[1]);
await testSetup.extetnalTokenLockerMock.lock(300,accounts[2]);

testSetup.reputationFromToken = await ReputationFromToken.new();
if (_initialize === true) {
await testSetup.reputationFromToken.initialize(testSetup.org.avatar.address,
testSetup.extetnalTokenLockerMock.address);
}

var permissions = "0x00000000";
await testSetup.daoCreator.setSchemes(testSetup.org.avatar.address,[testSetup.reputationFromToken.address],[helpers.NULL_HASH],[permissions],"metaData");
return testSetup;
};

contract('ReputationFromToken', accounts => {
it("initialize", async () => {
let testSetup = await setup(accounts);
assert.equal(await testSetup.reputationFromToken.tokenContract(),testSetup.extetnalTokenLockerMock.address);
assert.equal(await testSetup.reputationFromToken.avatar(),testSetup.org.avatar.address);
});

it("externalLockingMock is onlyOwner", async () => {
let testSetup = await setup(accounts);
try {
await testSetup.extetnalTokenLockerMock.lock(1030,accounts[3],{from:accounts[1]});
assert(false, "externalLockingMock is onlyOwner");
} catch(error) {
helpers.assertVMException(error);
}

});

it("redeem", async () => {
let testSetup = await setup(accounts);
var tx = await testSetup.reputationFromToken.redeem(accounts[1]);
assert.equal(tx.logs.length,1);
assert.equal(tx.logs[0].event,"Redeem");
assert.equal(tx.logs[0].args._beneficiary,accounts[1]);
assert.equal(tx.logs[0].args._amount,100);
assert.equal(tx.logs[0].args._sender,accounts[0]);
assert.equal(await testSetup.org.reputation.balanceOf(accounts[0]),1000);
assert.equal(await testSetup.org.reputation.balanceOf(accounts[1]),100);
});

it("cannot initialize twice", async () => {
let testSetup = await setup(accounts);
try {
await testSetup.reputationFromToken.initialize(testSetup.org.avatar.address,
testSetup.extetnalTokenLockerMock.address,
);
assert(false, "cannot initialize twice");
} catch(error) {
helpers.assertVMException(error);
}
});
});