-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
External token locking for reputation (#495)
* Add external locking 4 reputation * Add more tests
- Loading branch information
1 parent
ca914ce
commit 9b8be62
Showing
3 changed files
with
230 additions
and
0 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,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); | ||
} | ||
} |
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,12 @@ | ||
pragma solidity ^0.4.24; | ||
|
||
|
||
contract ExternalTokenLockerMock { | ||
|
||
// user => amount | ||
mapping (address => uint) public lockedTokenBalances; | ||
|
||
function lock(uint _amount) public { | ||
lockedTokenBalances[msg.sender] = _amount; | ||
} | ||
} |
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,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); | ||
} | ||
}); | ||
}); |