convert liquidPledging to accept a different token per pledge

This commit is contained in:
perissology 2018-02-16 13:44:44 -08:00
parent 46e87cffa1
commit 3ae23500c6
15 changed files with 320 additions and 102 deletions

View File

@ -49,6 +49,7 @@ contract ILiquidPledgingPlugin {
uint64 pledgeFrom,
uint64 pledgeTo,
uint64 context,
address token,
uint amount ) public returns (uint maxAllowed);
/// @notice Plugins are used (much like web hooks) to initiate an action
@ -76,6 +77,7 @@ contract ILiquidPledgingPlugin {
uint64 pledgeFrom,
uint64 pledgeTo,
uint64 context,
address token,
uint amount
) public;
}

View File

@ -18,9 +18,6 @@ pragma solidity ^0.4.18;
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/// @title LPVault
/// @author Jordi Baylina
/// @dev This contract holds ether securely for liquid pledging systems; for
/// this iteration the funds will come often be escaped to the Giveth Multisig
/// (safety precaution), but once fully tested and optimized this contract will
@ -28,6 +25,7 @@ pragma solidity ^0.4.18;
/// to allow for an optional escapeHatch to be implemented in case of issues;
/// future versions of this contract will be enabled for tokens
import "./EscapableApp.sol";
import "giveth-common-contracts/contracts/ERC20.sol";
/// @dev `LiquidPledging` is a basic interface to allow the `LPVault` contract
/// to confirm and cancel payments in the `LiquidPledging` contract.
@ -53,6 +51,7 @@ contract LPVault is EscapableApp {
uint indexed idPayment,
bytes32 indexed ref,
address indexed dest,
address token,
uint amount
);
@ -66,9 +65,10 @@ contract LPVault is EscapableApp {
/// each payment the `ref` param makes it easy to track the movements of
/// funds transparently by its connection to other `Payment` structs
struct Payment {
PaymentStatus state; // Pending, Paid or Canceled
bytes32 ref; // an input that references details from other contracts
address dest; // recipient of the ETH
PaymentStatus state; // Pending, Paid or Canceled
address token;
uint amount; // amount of ETH (in wei) to be sent
}
@ -102,10 +102,6 @@ contract LPVault is EscapableApp {
liquidPledging = ILiquidPledging(_liquidPledging);
}
/// @dev The fall back function allows ETH to be deposited into the LPVault
/// through a simple send
function () public payable {}
/// @notice Used to decentralize, toggles whether the LPVault will
/// automatically confirm a payment after the payment has been authorized
/// @param _automatic If true, payments will confirm instantly, if false
@ -126,6 +122,7 @@ contract LPVault is EscapableApp {
function authorizePayment(
bytes32 _ref,
address _dest,
address _token,
uint _amount
) external authP(AUTHORIZE_PAYMENT_ROLE, arr(_dest, _amount)) returns (uint)
{
@ -134,9 +131,10 @@ contract LPVault is EscapableApp {
payments[idPayment].state = PaymentStatus.Pending;
payments[idPayment].ref = _ref;
payments[idPayment].dest = _dest;
payments[idPayment].token = _token;
payments[idPayment].amount = _amount;
AuthorizePayment(idPayment, _ref, _dest, _amount);
AuthorizePayment(idPayment, _ref, _dest, _token, _amount);
if (autoPay) {
_doConfirmPayment(idPayment);
@ -177,20 +175,13 @@ contract LPVault is EscapableApp {
}
}
/// Transfer eth or tokens to the escapeHatchDestination.
/// Transfer tokens to the escapeHatchDestination.
/// Used as a safety mechanism to prevent the vault from holding too much value
/// before being thoroughly battle-tested.
/// @param _token to transfer, use 0x0 for ether
/// @param _token to transfer
/// @param _amount to transfer
function escapeFunds(address _token, uint _amount) public authP(ESCAPE_HATCH_CALLER_ROLE, arr(_token)) {
/// @dev Logic for ether
if (_token == 0x0) {
require(this.balance >= _amount);
escapeHatchDestination.transfer(_amount);
EscapeHatchCalled(_token, _amount);
return;
}
/// @dev Logic for tokens
require(_token != 0x0);
ERC20 token = ERC20(_token);
uint balance = token.balanceOf(this);
require(balance >= _amount);
@ -215,7 +206,8 @@ contract LPVault is EscapableApp {
p.state = PaymentStatus.Paid;
liquidPledging.confirmPayment(uint64(p.ref), p.amount);
p.dest.transfer(p.amount); // Transfers ETH denominated in wei
ERC20 token = ERC20(p.token);
require(token.transfer(p.dest, p.amount)); // Transfers token to dest
ConfirmPayment(_idPayment, p.ref);
}

View File

@ -27,19 +27,19 @@ import "./LiquidPledgingBase.sol";
/// to allow for expanded functionality.
contract LiquidPledging is LiquidPledgingBase {
function addGiverAndDonate(uint64 idReceiver)
public payable
function addGiverAndDonate(uint64 idReceiver, address token, uint amount)
public
{
addGiverAndDonate(idReceiver, msg.sender);
addGiverAndDonate(idReceiver, msg.sender, token, amount);
}
function addGiverAndDonate(uint64 idReceiver, address donorAddress)
public payable
function addGiverAndDonate(uint64 idReceiver, address donorAddress, address token, uint amount)
public
{
require(donorAddress != 0);
// default to a 3 day (259200 seconds) commitTime
uint64 idGiver = _addGiver(donorAddress, "", "", 259200, ILiquidPledgingPlugin(0));
donate(idGiver, idReceiver);
donate(idGiver, idReceiver, token, amount);
}
/// @notice This is how value enters the system and how pledges are created;
@ -50,19 +50,17 @@ contract LiquidPledging is LiquidPledgingBase {
/// @param idGiver The id of the Giver donating; if 0, a new id is created
/// @param idReceiver The Admin receiving the donation; can be any Admin:
/// the Giver themselves, another Giver, a Delegate or a Project
function donate(uint64 idGiver, uint64 idReceiver)
public payable
function donate(uint64 idGiver, uint64 idReceiver, address token, uint amount)
public
{
require(idGiver > 0); // prevent burning donations. idReceiver is checked in _transfer
require(amount > 0);
require(token != 0x0);
PledgeAdmin storage sender = _findAdmin(idGiver);
require(sender.adminType == PledgeAdminType.Giver);
uint amount = msg.value;
require(amount > 0);
// Sends the `msg.value` (in wei) to the `vault`
// b/c the vault is a proxy, send & transfer will fail since they only provide 2300
// gas, and the delegateProxy will sub(gas, 10000) before even making the call
require(vault.call.value(amount).gas(16000)());
require(ERC20(token).transferFrom(msg.sender, address(vault), amount)); // transfer the token to the `vault`
uint64 idPledge = _findOrCreatePledge(
idGiver,
@ -70,6 +68,7 @@ contract LiquidPledging is LiquidPledgingBase {
0,
0,
0,
token,
PledgeState.Pledged
);
@ -120,12 +119,13 @@ contract LiquidPledging is LiquidPledgingBase {
0,
0,
p.oldPledge,
p.token,
PledgeState.Paying
);
_doTransfer(idPledge, idNewPledge, amount);
vault.authorizePayment(bytes32(idNewPledge), owner.addr, amount);
vault.authorizePayment(bytes32(idNewPledge), owner.addr, p.token, amount);
}
/// @notice `onlyVault` Confirms a withdraw request changing the PledgeState
@ -143,6 +143,7 @@ contract LiquidPledging is LiquidPledgingBase {
0,
0,
p.oldPledge,
p.token,
PledgeState.Paid
);
@ -165,6 +166,7 @@ contract LiquidPledging is LiquidPledgingBase {
0,
0,
p.oldPledge,
p.token,
PledgeState.Pledged
);
@ -177,7 +179,6 @@ contract LiquidPledging is LiquidPledgingBase {
/// @param idProject Id of the project that is to be canceled
function cancelProject(uint64 idProject) authP(PLEDGE_ADMIN_ROLE, arr(uint(idProject))) public {
PledgeAdmin storage project = _findAdmin(idProject);
// _checkAdminOwner(project);
project.canceled = true;
CancelProject(idProject);

View File

@ -122,6 +122,7 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
0,
0,
p.oldPledge,
p.token,
PledgeState.Pledged
);
uint64 toPledge = _findOrCreatePledge(
@ -130,6 +131,7 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
0,
0,
oldPledge,
p.token,
PledgeState.Pledged
);
_doTransfer(idPledge, toPledge, p.amount);
@ -186,6 +188,7 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
0,
0,
p.oldPledge,
p.token,
PledgeState.Pledged);
_doTransfer(idPledge, toPledge, amount);
} else {
@ -203,7 +206,7 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
}
} else {
// This should never be reached as the reciever.adminType
// This should never be reached as the receiver.adminType
// should always be either a Giver, Project, or Delegate
assert(false);
}
@ -299,6 +302,7 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
0,
0,
p.oldPledge,
p.token,
PledgeState.Pledged
);
uint64 toPledge = _findOrCreatePledge(
@ -306,7 +310,8 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
new uint64[](0), // clear the delegation chain
0,
0,
uint64(oldPledge),
oldPledge,
p.token,
PledgeState.Pledged
);
_doTransfer(idPledge, toPledge, amount);
@ -316,8 +321,8 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
/// @notice `transferOwnershipToGiver` allows for the transfer of
/// value back to the Giver, value is placed in a pledged state
/// without being attached to a project, delegation chain, or time line.
/// @param idPledge the id of the pledge to be transfered.
/// @param amount Quantity of value that's being transfered
/// @param idPledge the id of the pledge to be transferred.
/// @param amount Quantity of value that's being transferred
/// @param idReceiver The new owner of the pledge
function _transferOwnershipToGiver(
uint64 idPledge,
@ -325,12 +330,15 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
uint64 idReceiver
) internal
{
Pledge storage p = _findPledge(idPledge);
uint64 toPledge = _findOrCreatePledge(
idReceiver,
new uint64[](0),
0,
0,
0,
p.token,
PledgeState.Pledged
);
_doTransfer(idPledge, toPledge, amount);
@ -366,6 +374,7 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
0,
0,
p.oldPledge,
p.token,
PledgeState.Pledged
);
_doTransfer(idPledge, toPledge, amount);
@ -397,6 +406,7 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
0,
0,
p.oldPledge,
p.token,
PledgeState.Pledged
);
_doTransfer(idPledge, toPledge, amount);
@ -426,6 +436,7 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
idReceiver,
uint64(_getTime() + _maxCommitTime(p)),
p.oldPledge,
p.token,
PledgeState.Pledged
);
_doTransfer(idPledge, toPledge, amount);
@ -520,6 +531,7 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
uint64 fromPledge,
uint64 toPledge,
uint64 context,
address token,
uint amount
) internal returns (uint allowedAmount)
{
@ -537,6 +549,7 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
fromPledge,
toPledge,
context,
token,
amount
);
require(newAmount <= allowedAmount);
@ -547,6 +560,7 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
fromPledge,
toPledge,
context,
token,
amount
);
}
@ -585,6 +599,7 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
fromPledge,
toPledge,
offset,
p.token,
allowedAmount
);
@ -596,6 +611,7 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
fromPledge,
toPledge,
offset + i + 1,
p.token,
allowedAmount
);
}
@ -610,6 +626,7 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
fromPledge,
toPledge,
offset + 255,
p.token,
allowedAmount
);
}

View File

@ -6,8 +6,7 @@ import "./ILiquidPledgingPlugin.sol";
/// the ETH that backs the Pledges, only after `LiquidPledging` authorizes
/// payments can Pledges be converted for ETH
interface ILPVault {
function authorizePayment(bytes32 _ref, address _dest, uint _amount) public;
function () public payable;
function authorizePayment(bytes32 _ref, address _dest, address _token, uint _amount) public;
}
/// This contract contains all state variables used in LiquidPledging contracts
@ -46,6 +45,7 @@ contract LiquidPledgingStorage {
uint64 intendedProject; // Used when delegates are sending to projects
uint64 commitTime; // When the intendedProject will become the owner
uint64 oldPledge; // Points to the id that this Pledge was derived from
address token;
PledgeState pledgeState; // Pledged, Paying, Paid
}

View File

@ -52,6 +52,7 @@ contract Pledges is AragonApp, LiquidPledgingStorage {
uint64 intendedProject,
uint64 commitTime,
uint64 oldPledge,
address token,
PledgeState pledgeState
) {
Pledge memory p = _findPledge(idPledge);
@ -61,6 +62,7 @@ contract Pledges is AragonApp, LiquidPledgingStorage {
intendedProject = p.intendedProject;
commitTime = p.commitTime;
oldPledge = p.oldPledge;
token = p.token;
pledgeState = p.pledgeState;
}
@ -91,10 +93,11 @@ contract Pledges is AragonApp, LiquidPledgingStorage {
uint64 intendedProject,
uint64 commitTime,
uint64 oldPledge,
address token,
PledgeState state
) internal returns (uint64)
{
bytes32 hPledge = keccak256(delegationChain, owner, intendedProject, commitTime, oldPledge, state);
bytes32 hPledge = keccak256(delegationChain, owner, intendedProject, commitTime, oldPledge, token, state);
uint64 id = hPledge2idx[hPledge];
if (id > 0) {
return id;
@ -110,6 +113,7 @@ contract Pledges is AragonApp, LiquidPledgingStorage {
intendedProject,
commitTime,
oldPledge,
token,
state
)
);

View File

@ -0,0 +1,151 @@
pragma solidity ^0.4.18;
/**
* WARNING: This token is for testing purposes only
* and has been modified from the original version: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/contracts/token/ERC20/StandardToken.sol
*
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* @dev https://github.com/ethereum/EIPs/issues/20
* @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*/
contract StandardToken {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
mapping (address => mapping (address => uint256)) internal allowed;
mapping(address => uint256) balances;
uint256 totalSupply_;
address owner;
function StandardToken() public {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev total number of tokens in existence
*/
function totalSupply() public view returns (uint256) {
return totalSupply_;
}
function mint(address _to, uint _value) public onlyOwner {
totalSupply_ += _value;
balances[_to] += _value;
Transfer(0, _to, _value);
}
/**
* @dev transfer token for a specified address
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
*/
function transfer(address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
require(_value <= balances[msg.sender]);
// SafeMath.sub will throw if there is not enough balance.
balances[msg.sender] = balances[msg.sender] - _value;
balances[_to] = balances[_to] + _value;
Transfer(msg.sender, _to, _value);
return true;
}
/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}
/**
* @dev Transfer tokens from one address to another
* @param _from address The address which you want to send tokens from
* @param _to address The address which you want to transfer to
* @param _value uint256 the amount of tokens to be transferred
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
require(_value <= balances[_from]);
require(_value <= allowed[_from][msg.sender]);
balances[_from] = balances[_from] - _value;
balances[_to] = balances[_to] + _value;
allowed[_from][msg.sender] = allowed[_from][msg.sender] - _value;
Transfer(_from, _to, _value);
return true;
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
*
* Beware that changing an allowance with this method brings the risk that someone may use both the old
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint256 _value) public returns (bool) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
/**
* @dev Function to check the amount of tokens that an owner allowed to a spender.
* @param _owner address The address which owns the funds.
* @param _spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/
function allowance(address _owner, address _spender) public view returns (uint256) {
return allowed[_owner][_spender];
}
/**
* @dev Increase the amount of tokens that an owner allowed to a spender.
*
* approve should be called when allowed[_spender] == 0. To increment
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param _spender The address which will spend the funds.
* @param _addedValue The amount of tokens to increase the allowance by.
*/
function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
allowed[msg.sender][_spender] = allowed[msg.sender][_spender] + _addedValue;
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
/**
* @dev Decrease the amount of tokens that an owner allowed to a spender.
*
* approve should be called when allowed[_spender] == 0. To decrement
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param _spender The address which will spend the funds.
* @param _subtractedValue The amount of tokens to decrease the allowance by.
*/
function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) {
uint oldValue = allowed[msg.sender][_spender];
if (_subtractedValue > oldValue) {
allowed[msg.sender][_spender] = 0;
} else {
allowed[msg.sender][_spender] = oldValue - _subtractedValue;
}
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
}

View File

@ -12,6 +12,7 @@ class LiquidPledgingState {
.then((res) => {
pledge.amount = res.amount;
pledge.owner = res.owner;
pledge.token = res.token;
if (res.intendedProject) {
pledge.intendedProject = res.intendedProject;
@ -68,12 +69,11 @@ class LiquidPledgingState {
admin.name = res.name;
admin.url = res.url;
admin.commitTime = res.commitTime;
if (admin.adminType === 'Project') {
if (admin.type === 'Project') {
admin.parentProject = res.parentProject;
admin.canceled = res.canceled;
}
admin.plugin = res.plugin;
admin.canceled = res.canceled;
return admin;
});
}

View File

@ -9,7 +9,7 @@
},
"scripts": {
"test": "npm run build; mocha --harmony",
"sol-compile": "solcpiler -i './contracts/+(LPVault|LPFactory|LiquidPledging|LiquidPledgingMock|TestSimpleProjectPlugin|TestSimpleProjectPluginFactory).sol' --solc-version v0.4.18+commit.9cf6e910",
"sol-compile": "solcpiler -i './contracts/{LPVault,LPFactory,LiquidPledging,LiquidPledgingMock,test/{TestSimpleProjectPluginFactory,StandardToken}}.sol' --solc-version v0.4.18+commit.9cf6e910",
"js-compile": "babel -d lib/ js/",
"build": "npm run sol-compile; npm run js-compile",
"prepublish": "npm run build"

View File

@ -29,6 +29,7 @@ describe('LiquidPledging plugins test', function () {
let giver1;
let adminProject1;
let adminDelegate1;
let token;
before(async () => {
testrpc = TestRPC.server({
@ -64,6 +65,10 @@ describe('LiquidPledging plugins test', function () {
liquidPledging = new contracts.LiquidPledgingMock(web3, lpAddress);
liquidPledgingState = new LiquidPledgingState(liquidPledging);
token = await contracts.StandardToken.new(web3);
await token.mint(giver1, web3.utils.toWei('1000'));
await token.approve(liquidPledging.$address, "0xFFFFFFFFFFFFFFFF", { from: giver1 });
});
it('Should create create giver with no plugin', async function () {

View File

@ -26,6 +26,7 @@ describe('LiquidPledging cancelPledge normal scenario', function () {
let giver1;
let adminProject1;
let adminProject2;
let token;
before(async () => {
testrpc = TestRPC.server({
@ -61,11 +62,15 @@ describe('LiquidPledging cancelPledge normal scenario', function () {
liquidPledging = new contracts.LiquidPledgingMock(web3, lpAddress);
liquidPledgingState = new LiquidPledgingState(liquidPledging);
token = await contracts.StandardToken.new(web3);
await token.mint(giver1, web3.utils.toWei('1000'));
await token.approve(liquidPledging.$address, "0xFFFFFFFFFFFFFFFF", { from: giver1 });
});
it('Should add project and donate ', async () => {
await liquidPledging.addProject('Project1', 'URLProject1', adminProject1, 0, 0, '0x0', { from: adminProject1 });
await liquidPledging.addGiverAndDonate(1, { from: giver1, value: '1000' });
await liquidPledging.addGiverAndDonate(1, token.$address, 1000, { from: giver1 });
const nAdmins = await liquidPledging.numberOfPledgeAdmins();
assert.equal(nAdmins, 2);

View File

@ -29,6 +29,7 @@ describe('DelegationChain test', function () {
let delegate2;
let delegate3;
let adminProject1;
let token;
const gasUsage = {};
before(async () => {
@ -68,6 +69,10 @@ describe('DelegationChain test', function () {
liquidPledging = new contracts.LiquidPledgingMock(web3, lpAddress);
liquidPledgingState = new LiquidPledgingState(liquidPledging);
token = await contracts.StandardToken.new(web3);
await token.mint(giver1, web3.utils.toWei('1000'));
await token.approve(liquidPledging.$address, "0xFFFFFFFFFFFFFFFF", { from: giver1 });
});
it('Should add pledgeAdmins', async () => {
@ -83,7 +88,7 @@ describe('DelegationChain test', function () {
});
it('Should allow previous delegate to transfer pledge', async () => {
await liquidPledging.donate(1, 2, { from: giver1, value: 1000 });
await liquidPledging.donate(1, 2, token.$address, 1000, { from: giver1 });
// add delegate2 to chain
await liquidPledging.transfer(2, 2, 1000, 3, { from: delegate1 });
// delegate 1 transfer pledge back to self, thus undelegating delegate2

View File

@ -36,6 +36,8 @@ describe('LiquidPledging test', function () {
let escapeHatchDestination;
let escapeHatchCaller;
let acl;
let giver1Token;
let giver2Token;
before(async () => {
testrpc = TestRPC.server({
@ -86,6 +88,15 @@ describe('LiquidPledging test', function () {
await acl.createPermission(accounts[0], vault.$address, await vault.CONFIRM_PAYMENT_ROLE(), accounts[0], {$extraGas: 200000});
await acl.grantPermission(escapeHatchCaller, vault.$address, await vault.ESCAPE_HATCH_CALLER_ROLE(), {$extraGas: 200000});
await acl.revokePermission(accounts[0], vault.$address, await vault.ESCAPE_HATCH_CALLER_ROLE(), {$extraGas: 200000});
giver1Token = await contracts.StandardToken.new(web3);
giver2Token = await contracts.StandardToken.new(web3);
await giver1Token.mint(giver1, web3.utils.toWei('1000'));
await giver2Token.mint(giver2, web3.utils.toWei('1000'));
await giver1Token.approve(liquidPledging.$address, "0xFFFFFFFFFFFFFFFF", {from: giver1});
await giver2Token.approve(liquidPledging.$address, "0xFFFFFFFFFFFFFFFF", {from: giver2});
});
it('Should create a giver', async () => {
await liquidPledging.addGiver('Giver1', 'URLGiver1', 86400, 0, { from: giver1, gas: 1000000 });
@ -99,12 +110,16 @@ describe('LiquidPledging test', function () {
assert.equal(res[4], 86400);
});
it('Should make a donation', async () => {
const r = await liquidPledging.donate(1, 1, { from: giver1, value: utils.toWei('1'), gas: 1000000 });
const r = await liquidPledging.donate(1, 1, giver1Token.$address, utils.toWei('1'), { from: giver1, $extraGas: 100000 });
const nPledges = await liquidPledging.numberOfPledges();
assert.equal(nPledges, 1);
const p = await liquidPledging.getPledge(1);
assert.equal(p.amount, utils.toWei('1'));
assert.equal(p.owner, 1);
const vaultBal = await giver1Token.balanceOf(vault.$address);
const giver1Bal = await giver1Token.balanceOf(giver1);
assert.equal(vaultBal, web3.utils.toWei('1'))
assert.equal(giver1Bal, web3.utils.toWei('999'))
});
it('Should create a delegate', async () => {
await liquidPledging.addDelegate('Delegate1', 'URLDelegate1', 0, 0, { from: delegate1 });
@ -165,27 +180,29 @@ describe('LiquidPledging test', function () {
const nPledges = await liquidPledging.numberOfPledges();
assert.equal(nPledges, 3);
const res3 = await liquidPledging.getPledge(3);
assert.equal(res3[0], utils.toWei('0.2'));
assert.equal(res3[1], 1); // Owner
assert.equal(res3[2], 1); // Delegates
assert.equal(res3[3], 3); // Proposed Project
assert.isAbove(utils.toDecimal(res3[4]), n + 86000);
assert.equal(res3[5], 0); // Old Node
assert.equal(res3[6], 0); // Not Paid
assert.equal(res3.amount, utils.toWei('0.2'));
assert.equal(res3.owner, 1);
assert.equal(res3.nDelegates, 1);
assert.equal(res3.intendedProject, 3);
assert.isAbove(utils.toDecimal(res3.commitTime), n + 86000);
assert.equal(res3.oldPledge, 0);
assert.equal(res3.token, giver1Token.$address);
assert.equal(res3.pledgeState, 0); // Not Paid
});
it('Giver should change his mind and assign half of it to project2', async () => {
await liquidPledging.transfer(1, 3, utils.toWei('0.1'), 4, { from: giver1 });
const nPledges = await liquidPledging.numberOfPledges();
assert.equal(nPledges, 4);
const res3 = await liquidPledging.getPledge(3);
assert.equal(res3[0], utils.toWei('0.1'));
assert.equal(res3.amount, utils.toWei('0.1'));
const res4 = await liquidPledging.getPledge(4);
assert.equal(res4[1], 4); // Owner
assert.equal(res4[2], 0); // Delegates
assert.equal(res4[3], 0); // Proposed Project
assert.equal(res4[4], 0);
assert.equal(res4[5], 2); // Old Node
assert.equal(res4[6], 0); // Not Paid
assert.equal(res4.owner, 4);
assert.equal(res4.nDelegates, 0);
assert.equal(res4.intendedProject, 0);
assert.equal(res4.commitTime, 0);
assert.equal(res4.oldPledge, 2);
assert.equal(res4.token, giver1Token.$address);
assert.equal(res4.pledgeState, 0); // Not Paid
});
it('After the time, the project1 should be able to spend part of it', async () => {
const n = Math.floor(new Date().getTime() / 1000);
@ -194,27 +211,29 @@ describe('LiquidPledging test', function () {
const nPledges = await liquidPledging.numberOfPledges();
assert.equal(nPledges, 6);
const res5 = await liquidPledging.getPledge(5);
assert.equal(res5[0], utils.toWei('0.05'));
assert.equal(res5[1], 3); // Owner
assert.equal(res5[2], 0); // Delegates
assert.equal(res5[3], 0); // Proposed Project
assert.equal(res5[4], 0); // commit time
assert.equal(res5[5], 2); // Old Node
assert.equal(res5[6], 0); // Not Paid
assert.equal(res5.amount, utils.toWei('0.05'));
assert.equal(res5.owner, 3);
assert.equal(res5.nDelegates, 0);
assert.equal(res5.intendedProject, 0);
assert.equal(res5.commitTime, 0);
assert.equal(res5.oldPledge, 2);
assert.equal(res5.token, giver1Token.$address);
assert.equal(res5.pledgeState, 0); // Not Paid
const res6 = await liquidPledging.getPledge(6);
assert.equal(res6[0], utils.toWei('0.05'));
assert.equal(res6[1], 3); // Owner
assert.equal(res6[2], 0); // Delegates
assert.equal(res6[3], 0); // Proposed Project
assert.equal(res6[4], 0); // commit time
assert.equal(res6[5], 2); // Old Node
assert.equal(res6[6], 1); // Peinding paid Paid
assert.equal(res6.amount, utils.toWei('0.05'));
assert.equal(res6.owner, 3);
assert.equal(res6.nDelegates, 0);
assert.equal(res6.intendedProject, 0);
assert.equal(res6.commitTime, 0);
assert.equal(res6.oldPledge, 2);
assert.equal(res6.token, giver1Token.$address);
assert.equal(res6.pledgeState, 1); // Pending
});
it('Should collect the Ether', async () => {
const initialBalance = await web3.eth.getBalance(adminProject1);
it('Should collect the token', async () => {
const initialBalance = await giver1Token.balanceOf(adminProject1);
await vault.confirmPayment(0, {$extraGas: 200000});
const finalBalance = await web3.eth.getBalance(adminProject1);
const finalBalance = await giver1Token.balanceOf(adminProject1);
const collected = utils.fromWei(utils.toBN(finalBalance).sub(utils.toBN(initialBalance)));
@ -223,13 +242,14 @@ describe('LiquidPledging test', function () {
const nPledges = await liquidPledging.numberOfPledges();
assert.equal(nPledges, 7);
const res7 = await liquidPledging.getPledge(7);
assert.equal(res7[0], utils.toWei('0.05'));
assert.equal(res7[1], 3); // Owner
assert.equal(res7[2], 0); // Delegates
assert.equal(res7[3], 0); // Proposed Project
assert.equal(res7[4], 0); // commit time
assert.equal(res7[5], 2); // Old pledge
assert.equal(res7[6], 2); // Peinding paid Paid
assert.equal(res7.amount, utils.toWei('0.05'));
assert.equal(res7.owner, 3);
assert.equal(res7.nDelegates, 0);
assert.equal(res7.intendedProject, 0);
assert.equal(res7.commitTime, 0);
assert.equal(res7.oldPledge, 2);
assert.equal(res7.token, giver1Token.$address);
assert.equal(res7.pledgeState, 2); // Pending
});
it('Admin of the project1 should be able to cancel project1', async () => {
await liquidPledging.cancelProject(3, { from: adminProject1, $extraGas: 100000 });
@ -243,7 +263,7 @@ describe('LiquidPledging test', function () {
await assertFail(
liquidPledging.withdraw(5, utils.toWei('0.01'), { from: adminProject1, gas: 4000000 })
);
});
});
it('Delegate should send part of this ETH to project2', async () => {
await liquidPledging.transfer(2, 5, utils.toWei('0.03'), 4, {from: delegate1, $extraGas: 100000});
const st = await liquidPledgingState.getState(liquidPledging);
@ -324,10 +344,10 @@ describe('LiquidPledging test', function () {
await liquidPledging.mWithdraw(encodedPledges, { from: giver1, $extraGas: 200000 });
const initialBalance = await web3.eth.getBalance(giver1);
const initialBalance = await giver1Token.balanceOf(giver1);
await vault.multiConfirm([2, 3, 4, 5, 6], {$extraGas: 200000});
const finalBalance = await web3.eth.getBalance(giver1);
const finalBalance = await giver1Token.balanceOf(giver1);
const collected = utils.fromWei(utils.toBN(finalBalance).sub(utils.toBN(initialBalance)));
assert.equal(collected, 0.95);
@ -335,9 +355,9 @@ describe('LiquidPledging test', function () {
it('Should make a donation and create giver', async () => {
const oldNPledges = await liquidPledging.numberOfPledges();
const oldNAdmins = await liquidPledging.numberOfPledgeAdmins();
await liquidPledging.addGiverAndDonate(1, { from: giver2, value: utils.toWei('1'), $extraGas: 200000 });
await liquidPledging.addGiverAndDonate(1, giver2Token.$address, utils.toWei('1'), { from: giver2, $extraGas: 200000 });
const nPledges = await liquidPledging.numberOfPledges();
assert.equal(utils.toDecimal(nPledges), utils.toDecimal(oldNPledges) + 1);
assert.equal(utils.toDecimal(nPledges), utils.toDecimal(oldNPledges) + 2);
const nAdmins = await liquidPledging.numberOfPledgeAdmins();
assert.equal(utils.toDecimal(nAdmins), utils.toDecimal(oldNAdmins) + 1);
const res = await liquidPledging.getPledgeAdmin(nAdmins);
@ -346,6 +366,8 @@ describe('LiquidPledging test', function () {
assert.equal(res[2], '');
assert.equal(res[3], '');
assert.equal(res[4], 259200); // default to 3 day commitTime
const giver2Bal = await giver2Token.balanceOf(giver2);
assert.equal(giver2Bal, utils.toWei('999'));
});
it('Should allow childProject with different parentProject owner', async () => {
const nAdminsBefore = await liquidPledging.numberOfPledgeAdmins();

View File

@ -28,6 +28,7 @@ describe('NormalizePledge test', function () {
let delegate2;
let adminProject1;
let adminProject2;
let token;
before(async () => {
testrpc = TestRPC.server({
@ -66,6 +67,12 @@ describe('NormalizePledge test', function () {
liquidPledging = new contracts.LiquidPledgingMock(web3, lpAddress);
liquidPledgingState = new LiquidPledgingState(liquidPledging);
token = await contracts.StandardToken.new(web3);
await token.mint(giver1, web3.utils.toWei('1000'));
await token.mint(giver2, web3.utils.toWei('1000'));
await token.approve(liquidPledging.$address, "0xFFFFFFFFFFFFFFFF", { from: giver1 });
await token.approve(liquidPledging.$address, "0xFFFFFFFFFFFFFFFF", { from: giver2 });
});
it('Should add pledgeAdmins', async () => {
@ -82,11 +89,11 @@ describe('NormalizePledge test', function () {
it('Should commit pledges if commitTime has passed', async () => {
// commitTime 259200
await liquidPledging.donate(1, 2, { from: giver1, value: 1000 });
await liquidPledging.donate(1, 2, token.$address, 1000, { from: giver1 });
// commitTime 86400
await liquidPledging.donate(1, 3, { from: giver1, value: 1000 });
await liquidPledging.donate(1, 3, token.$address, 1000, { from: giver1 });
// commitTime 0
await liquidPledging.donate(6, 3, { from: giver2, value: 1000 });
await liquidPledging.donate(6, 3, token.$address, 1000, { from: giver2 });
// set the time
const now = Math.floor(new Date().getTime() / 1000);

View File

@ -24,6 +24,7 @@ describe('Vault test', function () {
let giver1;
let adminProject1;
let restrictedPaymentsConfirmer;
let token;
before(async () => {
testrpc = TestRPC.server({
@ -76,12 +77,16 @@ describe('Vault test', function () {
const nAdmins = await liquidPledging.numberOfPledgeAdmins();
assert.equal(nAdmins, 2);
token = await contracts.StandardToken.new(web3);
await token.mint(giver1, web3.utils.toWei('1000'));
await token.approve(liquidPledging.$address, "0xFFFFFFFFFFFFFFFF", {from: giver1});
});
it('Should hold funds from liquidPledging', async function () {
await liquidPledging.addGiverAndDonate(2, { from: giver1, value: 10000, $extraGas: 100000 });
await liquidPledging.addGiverAndDonate(2, token.$address, 10000, { from: giver1, $extraGas: 100000 });
const balance = await web3.eth.getBalance(vault.$address);
const balance = await token.balanceOf(vault.$address);
assert.equal(10000, balance);
});
@ -94,19 +99,21 @@ describe('Vault test', function () {
});
it('escapeFunds should send funds to escapeHatchDestination', async function () {
const preBalance = await web3.eth.getBalance(escapeHatchDestination);
const preBalance = await token.balanceOf(escapeHatchDestination);
await vault.escapeFunds(0x0, 1000, { from: escapeHatchCaller, $extraGas: 200000 });
assertFail(vault.escapeFunds(0x0, 1000, { from: escapeHatchCaller, gas: 1000000}));
const vaultBalance = await web3.eth.getBalance(vault.$address);
await vault.escapeFunds(token.$address, 1000, { from: escapeHatchCaller, $extraGas: 200000 });
const vaultBalance = await token.balanceOf(vault.$address);
assert.equal(9000, vaultBalance);
const expected = web3.utils.toBN(preBalance).add(web3.utils.toBN('1000')).toString();
const postBalance = await web3.eth.getBalance(escapeHatchDestination);
const postBalance = await token.balanceOf(escapeHatchDestination);
assert.equal(expected, postBalance);
await web3.eth.sendTransaction({from: escapeHatchCaller, to: vault.$address, value: '1000', gas: 21000});
await token.transfer(vault.$address, 1000, {from: escapeHatchDestination, $extraGas: 200000});
});
it('should restrict confirm payment to payments under specified amount', async function () {