more progress

This commit is contained in:
perissology 2018-06-15 17:27:47 -07:00
parent 54a1faf570
commit e3c7c3a367
17 changed files with 1267 additions and 2306 deletions

View File

@ -1,98 +0,0 @@
pragma solidity ^0.4.18;
/*
Copyright 2016, Jordi Baylina
Contributor: Adrià Massanet <adria@codecontext.io>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// import "./Owned.sol";
import "giveth-common-contracts/contracts/ERC20.sol";
import "@aragon/os/contracts/apps/AragonApp.sol";
/// @dev `EscapableApp` is a base level contract; it creates an escape hatch
/// function that can be called in an
/// emergency that will allow designated addresses to send any ether or tokens
/// held in the contract to an `escapeHatchDestination` as long as they were
/// not blacklisted
contract EscapableApp is AragonApp {
// warning whoever has this role can move all funds to the `escapeHatchDestination`
bytes32 constant public ESCAPE_HATCH_CALLER_ROLE = keccak256("ESCAPE_HATCH_CALLER_ROLE");
event EscapeHatchBlackistedToken(address token);
event EscapeHatchCalled(address token, uint amount);
address public escapeHatchDestination;
mapping (address=>bool) private escapeBlacklist; // Token contract addresses
uint[20] private storageOffset; // reserve 20 slots for future upgrades
function EscapableApp(address _escapeHatchDestination) public {
_init(_escapeHatchDestination);
}
/// @param _escapeHatchDestination The address of a safe location (usu a
/// Multisig) to send the ether held in this contract; if a neutral address
/// is required, the WHG Multisig is an option:
/// 0x8Ff920020c8AD673661c8117f2855C384758C572
function initialize(address _escapeHatchDestination) onlyInit public {
_init(_escapeHatchDestination);
}
/// @notice The `escapeHatch()` should only be called as a last resort if a
/// security issue is uncovered or something unexpected happened
/// @param _token to transfer, use 0x0 for ether
function escapeHatch(address _token) external authP(ESCAPE_HATCH_CALLER_ROLE, arr(_token)) {
require(escapeBlacklist[_token]==false);
uint256 balance;
/// @dev Logic for ether
if (_token == 0x0) {
balance = this.balance;
escapeHatchDestination.transfer(balance);
EscapeHatchCalled(_token, balance);
return;
}
/// @dev Logic for tokens
ERC20 token = ERC20(_token);
balance = token.balanceOf(this);
require(token.transfer(escapeHatchDestination, balance));
EscapeHatchCalled(_token, balance);
}
/// @notice Checks to see if `_token` is in the blacklist of tokens
/// @param _token the token address being queried
/// @return False if `_token` is in the blacklist and can't be taken out of
/// the contract via the `escapeHatch()`
function isTokenEscapable(address _token) view external returns (bool) {
return !escapeBlacklist[_token];
}
function _init(address _escapeHatchDestination) internal {
initialized();
require(_escapeHatchDestination != 0x0);
escapeHatchDestination = _escapeHatchDestination;
}
/// @notice Creates the blacklist of tokens that are not able to be taken
/// out of the contract; can only be done at the deployment, and the logic
/// to add to the blacklist will be in the constructor of a child contract
/// @param _token the token contract address that is to be blacklisted
function _blacklistEscapeToken(address _token) internal {
escapeBlacklist[_token] = true;
EscapeHatchBlackistedToken(_token);
}
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.11;
pragma solidity ^0.4.0;
/*
Copyright 2017, Jordi Baylina

View File

@ -12,7 +12,7 @@ contract LPFactory is LPConstants, DAOFactory {
event DeployVault(address vault);
event DeployLiquidPledging(address liquidPledging);
function LPFactory(address _vaultBase, address _lpBase) public DAOFactory(0) {
function LPFactory(address _vaultBase, address _lpBase) public DAOFactory(new Kernel(), new ACL(), 0) {
require(_vaultBase != 0);
require(_lpBase != 0);
vaultBase = _vaultBase;
@ -28,12 +28,14 @@ contract LPFactory is LPConstants, DAOFactory {
acl.createPermission(this, address(kernel), appManagerRole, this);
LPVault v = LPVault(kernel.newAppInstance(VAULT_APP_ID, vaultBase));
LiquidPledging lp = LiquidPledging(kernel.newAppInstance(LP_APP_ID, lpBase));
v.initialize(address(lp), _escapeHatchDestination);
lp.initialize(address(v), _escapeHatchDestination);
// deploy & register the lp instance w/ the kernel
LiquidPledging lp = LiquidPledging(kernel.newAppInstance(LP_APP_ID, lpBase, true));
v.initialize(address(lp));
lp.initialize(address(v));
// register the lp instance w/ the kernel
kernel.setApp(kernel.APP_ADDR_NAMESPACE(), LP_APP_ID, address(lp));
// set the recoveryVault to the escapeHatchDestination
kernel.setRecoveryVaultId(keccak256(APP_ADDR_NAMESPACE, keccak256("recoveryVault")));
kernel.setApp(APP_ADDR_NAMESPACE, keccak256("recoveryVault"), _escapeHatchDestination);
_setPermissions(_root, acl, kernel, v, lp);
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.24;
pragma solidity ^0.4.18;
/*
Copyright 2017, Jordi Baylina
@ -85,11 +85,17 @@ contract LPVault is AragonApp, LiquidPledgingACLHelpers {
_;
}
// TODO if we remove EscapableApp, we won't be able to escape the base contracts???
function LPVault() {
initialized();
};
/// @param _liquidPledging Address of the liquidPledging instance associated
/// with this LPVault
function initialize(address _liquidPledging) onlyInit external {
require(_liquidPledging != 0x0);
liquidPledging = ILiquidPledging(_liquidPledging);
initialized();
}
/// @notice Used to decentralize, toggles whether the LPVault will
@ -99,7 +105,7 @@ contract LPVault is AragonApp, LiquidPledgingACLHelpers {
/// every payment
function setAutopay(bool _automatic) external authP(SET_AUTOPAY_ROLE, arr(_automatic)) {
autoPay = _automatic;
emit AutoPaySet(autoPay);
AutoPaySet(autoPay);
}
/// @notice If `autoPay == true` the transfer happens automatically `else` the `owner`
@ -124,7 +130,7 @@ contract LPVault is AragonApp, LiquidPledgingACLHelpers {
payments[idPayment].token = _token;
payments[idPayment].amount = _amount;
emit AuthorizePayment(idPayment, _ref, _dest, _token, _amount);
AuthorizePayment(idPayment, _ref, _dest, _token, _amount);
if (autoPay) {
_doConfirmPayment(idPayment);
@ -198,7 +204,7 @@ contract LPVault is AragonApp, LiquidPledgingACLHelpers {
ERC20 token = ERC20(p.token);
require(token.transfer(p.dest, p.amount)); // Transfers token to dest
emit ConfirmPayment(_idPayment, p.ref);
ConfirmPayment(_idPayment, p.ref);
}
/// @notice Cancels a pending payment (internal function)
@ -212,6 +218,6 @@ contract LPVault is AragonApp, LiquidPledgingACLHelpers {
liquidPledging.cancelPayment(uint64(p.ref), p.amount);
emit CancelPayment(_idPayment, p.ref);
CancelPayment(_idPayment, p.ref);
}
}

View File

@ -27,9 +27,6 @@ import "./LiquidPledgingBase.sol";
/// to allow for expanded functionality.
contract LiquidPledging is LiquidPledgingBase {
function LiquidPledging(address _escapeHatchDestination) EscapableApp(_escapeHatchDestination) public {
}
function addGiverAndDonate(uint64 idReceiver, address token, uint amount)
public
{
@ -63,9 +60,6 @@ contract LiquidPledging is LiquidPledgingBase {
PledgeAdmin storage sender = _findAdmin(idGiver);
require(sender.adminType == PledgeAdminType.Giver);
// TODO should this be done at the end of this function?
// what re-entrancy issues are there if this is done here?
// if done at the end of the function, will that affect plugins?
require(ERC20(token).transferFrom(msg.sender, address(vault), amount)); // transfer the token to the `vault`
uint64 idPledge = _findOrCreatePledge(
@ -258,32 +252,6 @@ contract LiquidPledging is LiquidPledgingBase {
}
}
/// @notice `mConfirmPayment` allows for multiple pledges to be confirmed
/// efficiently
/// @param pledgesAmounts An array of pledge amounts and IDs which are extrapolated
/// using the D64 bitmask
function mConfirmPayment(uint[] pledgesAmounts) public {
for (uint i = 0; i < pledgesAmounts.length; i++ ) {
uint64 idPledge = uint64(pledgesAmounts[i] & (D64-1));
uint amount = pledgesAmounts[i] / D64;
confirmPayment(idPledge, amount);
}
}
/// @notice `mCancelPayment` allows for multiple pledges to be canceled
/// efficiently
/// @param pledgesAmounts An array of pledge amounts and IDs which are extrapolated
/// using the D64 bitmask
function mCancelPayment(uint[] pledgesAmounts) public {
for (uint i = 0; i < pledgesAmounts.length; i++ ) {
uint64 idPledge = uint64(pledgesAmounts[i] & (D64-1));
uint amount = pledgesAmounts[i] / D64;
cancelPayment(idPledge, amount);
}
}
/// @notice `mNormalizePledge` allows for multiple pledges to be
/// normalized efficiently
/// @param pledges An array of pledge IDs

View File

@ -22,12 +22,12 @@ pragma solidity ^0.4.18;
import "./LiquidPledgingStorage.sol";
import "./PledgeAdmins.sol";
import "./Pledges.sol";
import "./EscapableApp.sol";
import "@aragon/os/contracts/apps/AragonApp.sol";
/// @dev `LiquidPledgingBase` is the base level contract used to carry out
/// liquidPledging's most basic functions, mostly handling and searching the
/// data structures
contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins, Pledges {
contract LiquidPledgingBase is AragonApp, LiquidPledgingStorage, PledgeAdmins, Pledges {
event Transfer(uint indexed from, uint indexed to, uint amount);
event CancelProject(uint indexed idProject);
@ -47,18 +47,8 @@ contract LiquidPledgingBase is EscapableApp, LiquidPledgingStorage, PledgeAdmins
// Constructor
///////////////
function initialize(address _escapeHatchDestination) onlyInit public {
require(false); // overload the EscapableApp
_escapeHatchDestination;
}
/// @param _vault The vault where the ETH backing the pledges is stored
/// @param _escapeHatchDestination The address of a safe location (usu a
/// Multisig) to send the ether held in this contract; if a neutral address
/// is required, the WHG Multisig is an option:
/// 0x8Ff920020c8AD673661c8117f2855C384758C572
function initialize(address _vault, address _escapeHatchDestination) onlyInit public {
super.initialize(_escapeHatchDestination);
function initialize(address _vault) onlyInit public {
require(_vault != 0x0);
vault = ILPVault(_vault);

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.11;
pragma solidity ^0.4.18;
/*
Copyright 2017, Jordi Baylina
Contributor: Adrià Massanet <adria@codecontext.io>
@ -26,13 +26,10 @@ contract LiquidPledgingMock is LiquidPledging {
uint public mock_time;
function LiquidPledgingMock(address _escapeHatchDestination) LiquidPledging(_escapeHatchDestination) public {
}
/// @dev `LiquidPledgingMock` creates a standard `LiquidPledging`
/// instance and sets the mocked time to the current blocktime.
function initialize(address _vault, address _escapeHatchDestination) onlyInit public {
super.initialize(_vault, _escapeHatchDestination);
function initialize(address _vault) onlyInit public {
super.initialize(_vault);
mock_time = now;
}

View File

@ -27,10 +27,18 @@ contract LiquidPledgingPlugins is AragonApp, LiquidPledgingStorage, LiquidPledgi
bytes32 constant public PLUGIN_MANAGER_ROLE = keccak256("PLUGIN_MANAGER_ROLE");
/**
* @dev adds an instance of a plugin to the whitelist
*/
function addValidPluginInstance(address addr) auth(PLUGIN_MANAGER_ROLE) external {
pluginInstanceWhitelist[addr] = true;
}
/**
* @dev add a contract to the plugin whitelist.
* @notice Proxy contracts should never be added using this method. Each individual
* proxy instance should be added by calling `addValidPluginInstance`
*/
function addValidPluginContract(bytes32 contractHash) auth(PLUGIN_MANAGER_ROLE) public {
pluginContractWhitelist[contractHash] = true;
}
@ -41,18 +49,31 @@ contract LiquidPledgingPlugins is AragonApp, LiquidPledgingStorage, LiquidPledgi
}
}
/**
* @dev removes a contract from the plugin whitelist
*/
function removeValidPluginContract(bytes32 contractHash) external authP(PLUGIN_MANAGER_ROLE, arr(contractHash)) {
pluginContractWhitelist[contractHash] = false;
}
/**
* @dev removes an instance of a plugin to the whitelist
*/
function removeValidPluginInstance(address addr) external authP(PLUGIN_MANAGER_ROLE, arr(addr)) {
pluginInstanceWhitelist[addr] = false;
}
/**
* @dev enable/disable the plugin whitelist.
* @notice you better know what you're doing if you are going to disable it
*/
function useWhitelist(bool useWhitelist) external auth(PLUGIN_MANAGER_ROLE) {
whitelistDisabled = !useWhitelist;
}
/**
* check if the contract at the provided address is in the plugin whitelist
*/
function isValidPlugin(address addr) public view returns(bool) {
if (whitelistDisabled || addr == 0x0) {
return true;
@ -69,6 +90,9 @@ contract LiquidPledgingPlugins is AragonApp, LiquidPledgingStorage, LiquidPledgi
return pluginContractWhitelist[contractHash];
}
/**
* @return the hash of the code for the given address
*/
function getCodeHash(address addr) public view returns(bytes32) {
bytes memory o_code;
assembly {

View File

@ -58,8 +58,6 @@ contract LiquidPledgingStorage {
ILPVault public vault;
// reserve 50 slots for future upgrades. I'm not sure if this is necessary
// but b/c of multiple inheritance used in lp, better safe then sorry.
// especially since it is free
// reserve 50 slots for future upgrades.
uint[50] private storageOffset;
}

View File

@ -63,7 +63,6 @@ contract PledgeAdmins is AragonApp, LiquidPledgingPlugins {
);
}
// TODO: is there an issue w/ allowing anyone to create a giver on behalf of another addy?
function addGiver(
address addr,
string name,

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.11;
pragma solidity ^0.4.18;
import "../LiquidPledging.sol";
@ -36,6 +36,7 @@ contract TestSimpleDelegatePlugin {
uint amount
) external returns (uint maxAllowed) {
require(!initPending);
maxAllowed;
BeforeTransfer(pledgeAdmin, pledgeFrom, pledgeTo, context, amount);
}

View File

@ -1,4 +1,4 @@
pragma solidity ^0.4.11;
pragma solidity ^0.4.18;
import "../LiquidPledging.sol";
@ -11,7 +11,7 @@ contract TestSimpleProjectPlugin {
event BeforeTransfer(uint64 pledgeAdmin, uint64 pledgeFrom, uint64 pledgeTo, uint64 context, uint amount);
event AfterTransfer(uint64 pledgeAdmin, uint64 pledgeFrom, uint64 pledgeTo, uint64 context, uint amount);
function TestSimpleProjectPlugin() {
function TestSimpleProjectPlugin() public {
require(msg.sender != tx.origin); // Avoids being created directly by mistake.
initPending = true;
}
@ -21,7 +21,7 @@ contract TestSimpleProjectPlugin {
string name,
string url,
uint64 parentProject
) {
) public {
require(initPending);
idProject = liquidPledging.addProject(name, url, address(this), parentProject, 0, ILiquidPledgingPlugin(this));
initPending = false;
@ -35,6 +35,7 @@ contract TestSimpleProjectPlugin {
uint amount
) external returns (uint maxAllowed) {
require(!initPending);
maxAllowed;
BeforeTransfer(pledgeAdmin, pledgeFrom, pledgeTo, context, amount);
}

View File

@ -11,7 +11,7 @@ contract TestSimpleProjectPluginFactory {
string name,
string url,
uint64 parentProject
) {
) public {
TestSimpleProjectPlugin p = new TestSimpleProjectPlugin();
p.init(liquidPledging, name, url, parentProject);
}

View File

@ -1,30 +1,43 @@
const contractInfo = require('./build/LPFactory.sol');
const LiquidPledgingMockInfo = require('./build/LiquidPledgingMock.sol');
const LPVaultInfo = require('./build/LPVault.sol');
const StandardTokenInfo = require('./build/StandardToken.sol');
const KernelInfo = require('./build/Kernel.sol');
const ACLInfo = require('./build/ACL.sol');
const LPFactoryArtifact = require('./build/LPFactory.json');
const LiquidPledgingArtifact = require('./build/LiquidPledging.json');
const LPVaultArtifact = require('./build/LPVault.json');
const KernelArtifact = require('./build/Kernel.json');
const ACLArtifact = require('./build/ACL.json');
const StandardTokenTestAtifact = require('./build/StandardToken.json');
const LiquidPledgingMockArtifact = require('./build/LiquidPledgingMock.json');
const generateClass = require('eth-contract-class').default;
module.exports = {
LiquidPledging: generateClass(
contractInfo.LiquidPledgingAbi,
contractInfo.LiquidPledgingByteCode,
LiquidPledgingArtifact.compilerOutput.abi,
`0x${LiquidPledgingArtifact.compilerOutput.evm.bytecode.object}`,
),
LPFactory: generateClass(
LPFactoryArtifact.compilerOutput.abi,
`0x${LPFactoryArtifact.compilerOutput.evm.bytecode.object}`,
),
LPFactory: generateClass(contractInfo.LPFactoryAbi, contractInfo.LPFactoryByteCode),
LiquidPledgingState: require('./lib/liquidPledgingState.js'),
LPVault: generateClass(contractInfo.LPVaultAbi, contractInfo.LPVaultByteCode),
Kernel: generateClass(KernelInfo.KernelAbi, KernelInfo.KernelByteCode),
ACL: generateClass(ACLInfo.ACLAbi, ACLInfo.ACLByteCode),
LPVault: generateClass(
LPVaultArtifact.compilerOutput.abi,
`0x${LPVaultArtifact.compilerOutput.evm.bytecode.object}`,
),
Kernel: generateClass(
KernelArtifact.compilerOutput.abi,
`0x${KernelArtifact.compilerOutput.evm.bytecode.object}`,
),
ACL: generateClass(
ACLArtifact.compilerOutput.abi,
`0x${ACLArtifact.compilerOutput.evm.bytecode.object}`,
),
test: {
StandardTokenTest: generateClass(
StandardTokenInfo.StandardTokenAbi,
StandardTokenInfo.StandardTokenByteCode,
StandardTokenTestAtifact.compilerOutput.abi,
`0x${StandardTokenTestAtifact.compilerOutput.evm.bytecode.object}`,
),
assertFail: require('./test/helpers/assertFail'),
LiquidPledgingMock: generateClass(
LiquidPledgingMockInfo.LiquidPledgingMockAbi,
LiquidPledgingMockInfo.LiquidPledgingMockByteCode,
LiquidPledgingMockArtifact.compilerOutput.abi,
`0x${LiquidPledgingMockArtifact.compilerOutput.evm.bytecode.object}`,
),
},
};

3266
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "giveth-liquidpledging",
"version": "0.1.1",
"version": "1.0.0-beta.0",
"description": "Liquid Pledging Smart Contract",
"main": "index.js",
"directories": {
@ -8,10 +8,11 @@
"test": "test"
},
"scripts": {
"test": "npm run build; mocha --harmony",
"sol-compile": "solcpiler -i './contracts/**/*.sol' ./node_modules/@aragon/os/contracts/{kernel/Kernel.sol,acl/ACL.sol}",
"js-compile": "babel -d lib/ js/",
"build": "npm run sol-compile; npm run js-compile",
"test": "npm run build && mocha --harmony",
"compile:sol": "solcpiler -i './contracts/**/*.sol' ./node_modules/@aragon/os/contracts/{kernel/Kernel.sol,acl/ACL.sol} --output-sol-dir build/contracts",
"compile:js": "babel -d lib/ js/",
"compile": "npm run compile:sol && npm run compile:js",
"build": "npm run compile",
"prepublish": "npm run build"
},
"repository": {
@ -40,11 +41,11 @@
"eslint-plugin-import": "^2.6.0",
"eslint-plugin-jsx-a11y": "^6.0.2",
"eslint-plugin-react": "^7.1.0",
"ganache-cli": "^7.0.0-beta.0",
"ganache-cli": "^6.1.3",
"lerna": "^2.2.0",
"mocha": "^3.5.0",
"random-bytes": "^1.0.0",
"solcpiler": "1.0.0-beta.2",
"solcpiler": "1.0.0-beta.3",
"web3": "1.0.0-beta.34"
},
"homepage": "https://github.com/Giveth/liquidpledging#readme",
@ -52,7 +53,6 @@
"@aragon/os": "3.1.9",
"async": "^2.4.0",
"chai": "^4.1.0",
"eth-contract-class": "^0.0.9",
"giveth-common-contracts": "0.6.0"
"eth-contract-class": "^0.0.9"
}
}

View File

@ -1,6 +1,6 @@
/* eslint-env mocha */
/* eslint-disable no-await-in-loop */
const TestRPC = require('ganache-cli');
const Ganache = require('ganache-cli');
const Web3 = require('web3');
const { assert } = require('chai');
const { LPVault, LPFactory, LiquidPledgingState, Kernel, ACL, test } = require('../index');
@ -15,7 +15,7 @@ const printState = async liquidPledgingState => {
describe('LiquidPledging test', function() {
this.timeout(0);
let testrpc;
let ganache;
let web3;
let accounts;
let liquidPledging;
@ -36,12 +36,12 @@ describe('LiquidPledging test', function() {
let giver2Token;
before(async () => {
testrpc = TestRPC.server({
ganache = Ganache.server({
gasLimit: 6700000,
total_accounts: 11,
});
testrpc.listen(8545, '127.0.0.1');
ganache.listen(8545, '127.0.0.1');
web3 = new Web3('http://localhost:8545');
accounts = await web3.eth.getAccounts();
@ -58,16 +58,16 @@ describe('LiquidPledging test', function() {
});
after(done => {
testrpc.close();
ganache.close();
done();
});
it('Should deploy LiquidPledging contract', async () => {
const baseVault = await LPVault.new(web3, escapeHatchDestination);
const baseLP = await LiquidPledgingMock.new(web3, escapeHatchDestination, {
const baseVault = await LPVault.new(web3);
const baseLP = await LiquidPledgingMock.new(web3, {
gas: 6700000,
});
lpFactory = await LPFactory.new(web3, baseVault.$address, baseLP.$address);
lpFactory = await LPFactory.new(web3, baseVault.$address, baseLP.$address, { gas: 6700000 });
assert.isAbove(Number(await baseVault.getInitializationBlock()), 0);
assert.isAbove(Number(await baseLP.getInitializationBlock()), 0);