Skip to content

Commit

Permalink
External token locking for reputation (#495)
Browse files Browse the repository at this point in the history
* Add external locking 4 reputation

* Add more tests
  • Loading branch information
orenyodfat authored Jul 12, 2018
1 parent ca914ce commit 9b8be62
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 0 deletions.
69 changes: 69 additions & 0 deletions contracts/schemes/ExternalLocking4Reputation.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
pragma solidity ^0.4.24;

import "./Locking4Reputation.sol";


/**
* @title A scheme for external locking Tokens for reputation
*/

contract ExternalLocking4Reputation is Locking4Reputation {

address public externalLockingContract;
string public getBalanceFuncSignature;

// locker->bool
mapping(address=>bool) public externalLockers;

/**
* @dev constructor
* @param _avatar the avatar to mint reputation from
* @param _reputationReward the total reputation this contract will reward
* for the token locking
* @param _lockingStartTime locking starting period time.
* @param _lockingEndTime the locking end time.
* redeem reputation can be done after this period.
* locking is disable after this time.
* @param _externalLockingContract the contract which lock the token.
* @param _getBalanceFuncSignature get balance function signature
* e.g "lockedTokenBalances(address)"
*/
constructor(Avatar _avatar,
uint _reputationReward,
uint _lockingStartTime,
uint _lockingEndTime,
address _externalLockingContract,
string _getBalanceFuncSignature)
Locking4Reputation(_avatar,
_reputationReward,
_lockingStartTime,
_lockingEndTime,
1)
public
{
require(_lockingEndTime > _lockingStartTime,"_lockingEndTime should be greater than _lockingStartTime");
externalLockingContract = _externalLockingContract;
getBalanceFuncSignature = _getBalanceFuncSignature;
}

/**
* @dev lock function
* @return lockingId
*/
function lock() public returns(bytes32) {
require(externalLockers[msg.sender] == false,"locking twice is not allowed");
externalLockers[msg.sender] = true;
// solium-disable-next-line security/no-low-level-calls
bool result = externalLockingContract.call(abi.encodeWithSignature(getBalanceFuncSignature,msg.sender));
uint lockedAmount;
// solium-disable-next-line security/no-inline-assembly
assembly {
returndatacopy(0, 0, returndatasize)
switch result
// call returns 0 on error.
case 0 { revert(0, returndatasize) }
default { lockedAmount := mload(0) }
}
return super._lock(lockedAmount,1,msg.sender);
}
}
12 changes: 12 additions & 0 deletions contracts/test/ExternalTokenLockerMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pragma solidity ^0.4.24;


contract ExternalTokenLockerMock {

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

function lock(uint _amount) public {
lockedTokenBalances[msg.sender] = _amount;
}
}
149 changes: 149 additions & 0 deletions test/externallocking4reputation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
const helpers = require('./helpers');
const DaoCreator = artifacts.require("./DaoCreator.sol");
const ControllerCreator = artifacts.require("./ControllerCreator.sol");
const constants = require('./constants');
var ExternalLocking4Reputation = artifacts.require("./ExternalLocking4Reputation.sol");
var ExternalTokenLockerMock = artifacts.require("./ExternalTokenLockerMock.sol");

const setup = async function (accounts,_repAllocation = 100,_lockingStartTime = 0,_lockingEndTime = 3000) {
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.lockingEndTime = await web3.eth.getBlock("latest").timestamp + _lockingEndTime;
testSetup.lockingStartTime = await web3.eth.getBlock("latest").timestamp + _lockingStartTime;
testSetup.extetnalTokenLockerMock = await ExternalTokenLockerMock.new();
await testSetup.extetnalTokenLockerMock.lock(100,{from:accounts[0]});
await testSetup.extetnalTokenLockerMock.lock(200,{from:accounts[1]});
await testSetup.extetnalTokenLockerMock.lock(300,{from:accounts[2]});

testSetup.externalLocking4Reputation = await ExternalLocking4Reputation.new(testSetup.org.avatar.address,
_repAllocation,
testSetup.lockingStartTime,
testSetup.lockingEndTime,
testSetup.extetnalTokenLockerMock.address,
"lockedTokenBalances(address)");

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

contract('ExternalLocking4Reputation', accounts => {
it("constructor", async () => {
let testSetup = await setup(accounts);
assert.equal(await testSetup.externalLocking4Reputation.reputationReward(),100);
assert.equal(await testSetup.externalLocking4Reputation.lockingEndTime(),testSetup.lockingEndTime);
assert.equal(await testSetup.externalLocking4Reputation.lockingStartTime(),testSetup.lockingStartTime);
assert.equal(await testSetup.externalLocking4Reputation.externalLockingContract(),testSetup.extetnalTokenLockerMock.address);
assert.equal(await testSetup.externalLocking4Reputation.getBalanceFuncSignature(),"lockedTokenBalances(address)");
});

it("lock", async () => {
let testSetup = await setup(accounts);
var tx = await testSetup.externalLocking4Reputation.lock();
var lockingId = await helpers.getValueFromLogs(tx, '_lockingId',1);
assert.equal(tx.logs.length,1);
assert.equal(tx.logs[0].event,"Lock");
assert.equal(tx.logs[0].args._lockingId,lockingId);
assert.equal(tx.logs[0].args._amount,100);
assert.equal(tx.logs[0].args._period,1);
assert.equal(tx.logs[0].args._locker,accounts[0]);
});

it("lock with value == 0 should revert", async () => {
let testSetup = await setup(accounts);
try {
await testSetup.externalLocking4Reputation.lock({from:accounts[4]});
assert(false, "lock with value == 0 should revert");
} catch(error) {
helpers.assertVMException(error);
}
});

it("lock after _lockingEndTime should revert", async () => {
let testSetup = await setup(accounts);
await helpers.increaseTime(3001);
try {
await testSetup.externalLocking4Reputation.lock();
assert(false, "lock after _lockingEndTime should revert");
} catch(error) {
helpers.assertVMException(error);
}
});

it("lock before start should revert", async () => {
let testSetup = await setup(accounts,100,100);
try {
await testSetup.externalLocking4Reputation.lock();
assert(false, "lock before start should revert");
} catch(error) {
helpers.assertVMException(error);
}
});

it("cannot lock twice for the same user", async () => {
let testSetup = await setup(accounts);
await testSetup.externalLocking4Reputation.lock();
try {
await testSetup.externalLocking4Reputation.lock();
assert(false, "cannot lock twice for the same user");
} catch(error) {
helpers.assertVMException(error);
}
});

it("redeem", async () => {
let testSetup = await setup(accounts);
var tx = await testSetup.externalLocking4Reputation.lock();
var lockingId = await helpers.getValueFromLogs(tx, '_lockingId',1);
await helpers.increaseTime(3001);
tx = await testSetup.externalLocking4Reputation.redeem(accounts[0],lockingId);
assert.equal(tx.logs.length,1);
assert.equal(tx.logs[0].event,"Redeem");
assert.equal(tx.logs[0].args._lockingId,lockingId);
assert.equal(tx.logs[0].args._amount,100);
assert.equal(tx.logs[0].args._beneficiary,accounts[0]);
assert.equal(await testSetup.org.reputation.reputationOf(accounts[0]),1000+100);
});

it("redeem score ", async () => {
let testSetup = await setup(accounts);
var tx = await testSetup.externalLocking4Reputation.lock({from:accounts[0]});
var lockingId1 = await helpers.getValueFromLogs(tx, '_lockingId',1);
tx = await testSetup.externalLocking4Reputation.lock({from:accounts[2]});
var lockingId2 = await helpers.getValueFromLogs(tx, '_lockingId',1);
await helpers.increaseTime(3001);
await testSetup.externalLocking4Reputation.redeem(accounts[0],lockingId1);
await testSetup.externalLocking4Reputation.redeem(accounts[2],lockingId2);
assert.equal(await testSetup.org.reputation.reputationOf(accounts[0]),1000+25);
assert.equal(await testSetup.org.reputation.reputationOf(accounts[2]),75);
});

it("redeem cannot redeem twice", async () => {
let testSetup = await setup(accounts);
var tx = await testSetup.externalLocking4Reputation.lock();
var lockingId = await helpers.getValueFromLogs(tx, '_lockingId',1);
await helpers.increaseTime(3001);
await testSetup.externalLocking4Reputation.redeem(accounts[0],lockingId);
try {
await testSetup.externalLocking4Reputation.redeem(accounts[0],lockingId);
assert(false, "cannot redeem twice");
} catch(error) {
helpers.assertVMException(error);
}
});

it("redeem before lockingEndTime should revert", async () => {
let testSetup = await setup(accounts);
var tx = await testSetup.externalLocking4Reputation.lock();
var lockingId = await helpers.getValueFromLogs(tx, '_lockingId',1);
await helpers.increaseTime(50);
try {
await testSetup.externalLocking4Reputation.redeem(accounts[0],lockingId);
assert(false, "redeem before lockingEndTime should revert");
} catch(error) {
helpers.assertVMException(error);
}
});
});

0 comments on commit 9b8be62

Please sign in to comment.