refactor: decouple arbitration licenses from disputes

This commit is contained in:
Richard Ramos 2019-06-13 18:26:13 -04:00
parent 551c0bc67c
commit dd2acd5540
13 changed files with 227 additions and 249 deletions

View File

@ -1,6 +1,109 @@
/* solium-disable security/no-block-members */
pragma solidity ^0.5.8;
interface Arbitrable {
function setArbitrationResult(uint _escrowId, bool _releaseFunds, address _arbitrator) external;
function getArbitrator(uint _escrowId) external view returns(address);
}
import "./License.sol";
contract Arbitrable {
mapping(uint => ArbitrationCase) public arbitrationCases;
struct ArbitrationCase {
bool open;
address openBy;
address arbitrator;
ArbitrationResult result;
string motive;
}
event ArbitratorChanged(address arbitrator);
event ArbitrationCanceled(uint escrowId, uint date);
event ArbitrationRequired(uint escrowId, uint date);
event ArbitrationResolved(uint escrowId, ArbitrationResult result, address arbitrator, uint date);
enum ArbitrationResult {UNSOLVED, BUYER, SELLER}
License public arbitratorLicenses;
constructor(address _arbitratorLicenses) public {
arbitratorLicenses = License(_arbitratorLicenses);
}
// Abstract functions
function solveDispute(uint _escrowId, bool _releaseFunds, address _arbitrator) internal;
function getArbitrator(uint _escrowId) public view returns(address);
/**
* @notice arbitration exists
* @param _escrowId Escrow to verify
* @return bool result
*/
function isDisputed(uint _escrowId) public view returns (bool) {
return arbitrationCases[_escrowId].open || arbitrationCases[_escrowId].result != ArbitrationResult.UNSOLVED;
}
/**
* @notice cancel arbitration
* @param _escrowId Escrow to cancel
*/
function cancelArbitration(uint _escrowId) public {
require(arbitrationCases[_escrowId].openBy == msg.sender, "Arbitration can only be canceled by the opener");
require(arbitrationCases[_escrowId].result == ArbitrationResult.UNSOLVED && arbitrationCases[_escrowId].open,
"Arbitration already solved or not open");
delete arbitrationCases[_escrowId];
emit ArbitrationCanceled(_escrowId, block.timestamp);
}
function openDispute(uint _escrowId, address _openBy, string memory motive) internal {
require(!arbitrationCases[_escrowId].open, "Arbitration already open");
require(arbitrationCases[_escrowId].result == ArbitrationResult.UNSOLVED, "Arbitration already solved");
arbitrationCases[_escrowId] = ArbitrationCase({
open: true,
openBy: _openBy,
arbitrator: address(0),
result: ArbitrationResult.UNSOLVED,
motive: motive
});
emit ArbitrationRequired(_escrowId, block.timestamp);
}
/**
* @notice Set arbitration result in favour of the buyer or seller and transfer funds accordingly
* @param _escrowId Id of the escrow
* @param _result Result of the arbitration
*/
function setArbitrationResult(uint _escrowId, ArbitrationResult _result) public {
require(arbitrationCases[_escrowId].open && arbitrationCases[_escrowId].result == ArbitrationResult.UNSOLVED,
"Case must be open and unsolved");
require(_result != ArbitrationResult.UNSOLVED, "Arbitration does not have result");
require(arbitratorLicenses.isLicenseOwner(msg.sender), "Only arbitrators can invoke this function");
require(getArbitrator(_escrowId) == msg.sender, "Invalid escrow arbitrator");
arbitrationCases[_escrowId].open = false;
arbitrationCases[_escrowId].result = _result;
arbitrationCases[_escrowId].arbitrator = msg.sender;
// TODO: incentive mechanism for opening arbitration process
// if(arbitrationCases[_escrowId].openBy != trx.seller || arbitrationCases[_escrowId].openBy != trx.buyer){
// Consider deducting a fee as reward for whoever opened the arbitration process.
// }
emit ArbitrationResolved(_escrowId, _result, msg.sender, block.timestamp);
if(_result == ArbitrationResult.BUYER){
solveDispute(_escrowId, true, msg.sender);
} else {
solveDispute(_escrowId, false, msg.sender);
}
}
}

View File

@ -1,124 +0,0 @@
/* solium-disable security/no-block-members */
pragma solidity ^0.5.8;
import "../common/Ownable.sol";
import "./Arbitrable.sol";
import "./License.sol";
contract Arbitration is Ownable, License {
mapping(uint => ArbitrationCase) public arbitrationCases;
struct ArbitrationCase {
bool open;
address openBy;
address arbitrator;
ArbitrationResult result;
string motive;
}
event ArbitratorChanged(address arbitrator);
event ArbitrationCanceled(uint escrowId, uint date);
event ArbitrationRequired(uint escrowId, uint date);
event ArbitrationResolved(uint escrowId, ArbitrationResult result, address arbitrator, uint date);
enum ArbitrationResult {UNSOLVED, BUYER, SELLER}
Arbitrable public escrow;
constructor(address payable _tokenAddress, uint256 _price)
License(_tokenAddress, _price)
public {
// Do nothing
}
function setEscrowAddress(address _escrow) public onlyOwner {
escrow = Arbitrable(_escrow);
}
/**
* @notice Determine if address is arbitrator
* @param _addr Address to be verified
* @return result
*/
function isArbitrator(address _addr) public view returns(bool){
return isLicenseOwner(_addr);
}
/**
* @notice arbitration exists
* @param _escrowId Escrow to verify
* @return bool result
*/
function exists(uint _escrowId) public view returns (bool) {
return arbitrationCases[_escrowId].open || arbitrationCases[_escrowId].result != ArbitrationResult.UNSOLVED;
}
/**
* @notice cancel arbitration
* @param _escrowId Escrow to cancel
*/
function cancelArbitration(uint _escrowId) public {
require(arbitrationCases[_escrowId].openBy == msg.sender, "Arbitration can only be canceled by the opener");
require(arbitrationCases[_escrowId].result == ArbitrationResult.UNSOLVED && arbitrationCases[_escrowId].open,
"Arbitration already solved or not open");
delete arbitrationCases[_escrowId];
emit ArbitrationCanceled(_escrowId, block.timestamp);
}
function openCase(uint _escrowId, address _openBy, string memory motive) public {
assert(msg.sender == address(escrow)); // Only the escrow address can open cases
require(!arbitrationCases[_escrowId].open, "Arbitration already open");
require(arbitrationCases[_escrowId].result == ArbitrationResult.UNSOLVED, "Arbitration already solved");
arbitrationCases[_escrowId] = ArbitrationCase({
open: true,
openBy: _openBy,
arbitrator: address(0),
result: ArbitrationResult.UNSOLVED,
motive: motive
});
emit ArbitrationRequired(_escrowId, block.timestamp);
}
/**
* @notice Set arbitration result in favour of the buyer or seller and transfer funds accordingly
* @param _escrowId Id of the escrow
* @param _result Result of the arbitration
*/
function setArbitrationResult(uint _escrowId, ArbitrationResult _result) public {
require(arbitrationCases[_escrowId].open && arbitrationCases[_escrowId].result == ArbitrationResult.UNSOLVED,
"Case must be open and unsolved");
require(_result != ArbitrationResult.UNSOLVED, "Arbitration does not have result");
require(isArbitrator(msg.sender), "Only arbitrators can invoke this function");
require(escrow.getArbitrator(_escrowId) == msg.sender, "Invalid escrow arbitrator");
arbitrationCases[_escrowId].open = false;
arbitrationCases[_escrowId].result = _result;
arbitrationCases[_escrowId].arbitrator = msg.sender;
// TODO: incentive mechanism for opening arbitration process
// if(arbitrationCases[_escrowId].openBy != trx.seller || arbitrationCases[_escrowId].openBy != trx.buyer){
// Consider deducting a fee as reward for whoever opened the arbitration process.
// }
emit ArbitrationResolved(_escrowId, _result, msg.sender, block.timestamp);
if(_result == ArbitrationResult.BUYER){
escrow.setArbitrationResult(_escrowId, true, msg.sender);
} else {
escrow.setArbitrationResult(_escrowId, false, msg.sender);
}
}
}

View File

@ -10,7 +10,6 @@ import "../token/ERC20Token.sol";
import "./License.sol";
import "./MetadataStore.sol";
import "./Fees.sol";
import "./Arbitration.sol";
import "./Arbitrable.sol";
import "tabookey-gasless/contracts/RelayRecipient.sol";
@ -26,14 +25,15 @@ contract Escrow is Pausable, MessageSigned, Fees, Arbitrable, RelayRecipient {
constructor(
address _license,
address _arbitration,
address _arbitrationLicense,
address _metadataStore,
address _feeToken,
address _feeDestination,
uint _feeAmount)
Fees(_feeToken, _feeDestination, _feeAmount) public {
Fees(_feeToken, _feeDestination, _feeAmount)
Arbitrable(_arbitrationLicense)
public {
license = License(_license);
arbitration = Arbitration(_arbitration);
metadataStore = MetadataStore(_metadataStore);
}
@ -41,8 +41,6 @@ contract Escrow is Pausable, MessageSigned, Fees, Arbitrable, RelayRecipient {
set_relay_hub(RelayHub(_relayHub));
}
Arbitration arbitration;
struct EscrowTransaction {
uint256 offerId;
uint256 tokenAmount;
@ -423,7 +421,7 @@ contract Escrow is Pausable, MessageSigned, Fees, Arbitrable, RelayRecipient {
function rateTransaction(uint _escrowId, uint _rate) external whenNotPaused {
require(_rate >= 1, "Rating needs to be at least 1");
require(_rate <= 5, "Rating needs to be at less than or equal to 5");
require(!arbitration.exists(_escrowId), "Can't rate a transaction that has an arbitration process");
require(!isDisputed(_escrowId), "Can't rate a transaction that has an arbitration process");
EscrowTransaction storage trx = transactions[_escrowId];
@ -445,11 +443,11 @@ contract Escrow is Pausable, MessageSigned, Fees, Arbitrable, RelayRecipient {
function openCase(uint _escrowId, string calldata motive) external {
EscrowTransaction storage trx = transactions[_escrowId];
require(!arbitration.exists(_escrowId), "Case already exist");
require(!isDisputed(_escrowId), "Case already exist");
require(trx.buyer == get_sender() || metadataStore.getOfferOwner(trx.offerId) == get_sender(), "Only a buyer or seller can open a case");
require(trx.status == EscrowStatus.PAID, "Cases can only be open for paid transactions");
arbitration.openCase(_escrowId, get_sender(), motive);
openDispute(_escrowId, get_sender(), motive);
}
/**
@ -458,19 +456,18 @@ contract Escrow is Pausable, MessageSigned, Fees, Arbitrable, RelayRecipient {
* @param _signature Signed message result of openCaseSignHash(uint256)
* @dev Consider opening a dispute in aragon court.
*/
function openCaseWithSignature(uint _escrowId, bytes calldata _signature) external {
function openCaseWithSignature(uint _escrowId, string calldata motive, bytes calldata _signature) external {
EscrowTransaction storage trx = transactions[_escrowId];
require(!arbitration.exists(_escrowId), "Case already exist");
require(!isDisputed(_escrowId), "Case already exist");
require(trx.status == EscrowStatus.PAID, "Cases can only be open for paid transactions");
address senderAddress = recoverAddress(getSignHash(openCaseSignHash(_escrowId)), _signature);
address senderAddress = recoverAddress(getSignHash(openCaseSignHash(_escrowId, motive)), _signature);
require(trx.buyer == senderAddress || metadataStore.getOfferOwner(trx.offerId) == senderAddress,
"Only a buyer or seller can open a case");
// FIXME get actual motive from the signature if possible
arbitration.openCase(_escrowId, get_sender(), '');
openDispute(_escrowId, get_sender(), motive);
}
/**
@ -479,9 +476,7 @@ contract Escrow is Pausable, MessageSigned, Fees, Arbitrable, RelayRecipient {
* @param _releaseFunds Release funds to buyer or cancel escrow
* @param _arbitrator Arbitrator address
*/
function setArbitrationResult(uint _escrowId, bool _releaseFunds, address _arbitrator) external {
assert(get_sender() == address(arbitration)); // Only arbitration contract can invoke this
function solveDispute(uint _escrowId, bool _releaseFunds, address _arbitrator) internal {
EscrowTransaction storage trx = transactions[_escrowId];
address payable seller = metadataStore.getOfferOwner(trx.offerId);
@ -511,12 +506,13 @@ contract Escrow is Pausable, MessageSigned, Fees, Arbitrable, RelayRecipient {
* @return message hash
* @dev Once message is signed, pass it as _signature of openCase(uint256,bytes)
*/
function openCaseSignHash(uint _escrowId) public view returns(bytes32){
function openCaseSignHash(uint _escrowId, string memory motive) public view returns(bytes32){
return keccak256(
abi.encodePacked(
address(this),
"openCase(uint256)",
_escrowId
_escrowId,
motive
)
);
}

View File

@ -69,25 +69,29 @@ module.exports = {
contracts: {
License: {
deploy: false
},
SellerLicense: {
instanceOf: "License",
args: [
"$SNT",
LICENSE_PRICE
]
},
"MetadataStore": {
args: ["$License", "$Arbitration"]
args: ["$SellerLicense", "$ArbitrationLicense"]
},
Arbitration: {
ArbitrationLicense: {
instanceOf: "License",
args: [
"$SNT",
ARB_LICENSE_PRICE
]
},
Escrow: {
args: ["$License", "$Arbitration", "$MetadataStore", "$SNT", BURN_ADDRESS, FEE_AMOUNT],
args: ["$SellerLicense", "$ArbitrationLicense", "$MetadataStore", "$SNT", BURN_ADDRESS, FEE_AMOUNT],
deps: ['RelayHub'],
onDeploy: [
"Arbitration.methods.setEscrowAddress('$Escrow').send()",
"MetadataStore.methods.setEscrowAddress('$Escrow').send()",
"Escrow.methods.setRelayHubAddress('$RelayHub').send()",
"RelayHub.methods.depositFor('$Escrow').send({value: 1000000000000000000})"
@ -264,10 +268,9 @@ module.exports = {
tracking: 'shared.ropsten.chains.json',
contracts: {
Escrow: {
args: ["$License", "$Arbitration", "$MetadataStore", "$SNT", BURN_ADDRESS, FEE_AMOUNT],
args: ["$SellerLicense", "$ArbitrationLicense", "$MetadataStore", "$SNT", BURN_ADDRESS, FEE_AMOUNT],
deps: ['RelayHub'],
onDeploy: [
"Arbitration.methods.setEscrowAddress('$Escrow').send()",
"MetadataStore.methods.setEscrowAddress('$Escrow').send()",
"Escrow.methods.setRelayHubAddress('$RelayHub').send()",
"RelayHub.methods.depositFor('$Escrow').send({value: 300000000000000000})"

View File

@ -39,11 +39,8 @@ module.exports = async (licensePrice, arbitrationLicensePrice, feeAmount, deps)
console.log("Buy arbitration license");
{
console.log(deps.contracts.Arbitration._address);
console.log(arbitrationLicensePrice);
const buyLicense = deps.contracts.Arbitration.methods.buy().encodeABI();
const toSend = deps.contracts.SNT.methods.approveAndCall(deps.contracts.Arbitration._address, arbitrationLicensePrice, buyLicense);
const buyLicense = deps.contracts.ArbitrationLicense.methods.buy().encodeABI();
const toSend = deps.contracts.SNT.methods.approveAndCall(deps.contracts.ArbitrationLicense._address, arbitrationLicensePrice, buyLicense);
const gas = await toSend.estimateGas({from: arbitrator});
await toSend.send({from: arbitrator, gas});
@ -51,8 +48,8 @@ module.exports = async (licensePrice, arbitrationLicensePrice, feeAmount, deps)
console.log('Buy Licenses...');
await Promise.all(addresses.slice(1, 8).map(async (address) => {
const buyLicense = deps.contracts.License.methods.buy().encodeABI();
const toSend = deps.contracts.SNT.methods.approveAndCall(deps.contracts.License._address, licensePrice, buyLicense);
const buyLicense = deps.contracts.SellerLicense.methods.buy().encodeABI();
const toSend = deps.contracts.SNT.methods.approveAndCall(deps.contracts.SellerLicense._address, licensePrice, buyLicense);
const gas = await toSend.estimateGas({from: address});
return toSend.send({from: address, gas});
@ -148,7 +145,7 @@ module.exports = async (licensePrice, arbitrationLicensePrice, feeAmount, deps)
const accounts = await Promise.all(addresses.map(async(address) => {
const ethBalance = await deps.web3.eth.getBalance(address);
const sntBalance = await deps.contracts.SNT.methods.balanceOf(address).call();
const isLicenseOwner = await deps.contracts.License.methods.isLicenseOwner(address).call();
const isLicenseOwner = await deps.contracts.SellerLicense.methods.isLicenseOwner(address).call();
let user = {};
let offers = [];
const isUser = await deps.contracts.MetadataStore.methods.userWhitelist(address).call();

View File

@ -1,6 +1,5 @@
import {ARBITRATION_UNSOLVED, GET_DISPUTED_ESCROWS, RESOLVE_DISPUTE, RESOLVE_DISPUTE_FAILED, BUY_LICENSE, CHECK_LICENSE_OWNER, LOAD_PRICE, LOAD_ARBITRATION, GET_ARBITRATORS, OPEN_DISPUTE, CANCEL_DISPUTE} from './constants';
import Escrow from '../../../embarkArtifacts/contracts/Escrow';
import Arbitration from '../../../embarkArtifacts/contracts/Arbitration';
export const getDisputedEscrows = () => ({type: GET_DISPUTED_ESCROWS});
@ -15,13 +14,13 @@ export const resolveDispute = (escrowId, result) => {
type: RESOLVE_DISPUTE,
escrowId,
result,
toSend: Arbitration.methods.setArbitrationResult(escrowId, result)
toSend: Escrow.methods.setArbitrationResult(escrowId, result)
};
};
export const openDispute = (escrowId, motive) => ({type: OPEN_DISPUTE, escrowId, toSend: Escrow.methods.openCase(escrowId, motive || '')});
export const cancelDispute = (escrowId) => ({type: CANCEL_DISPUTE, escrowId, toSend: Arbitration.methods.cancelArbitration(escrowId)});
export const cancelDispute = (escrowId) => ({type: CANCEL_DISPUTE, escrowId, toSend: Escrow.methods.cancelArbitration(escrowId)});
export const loadArbitration = (escrowId) => {
return {type: LOAD_ARBITRATION, escrowId};

View File

@ -1,6 +1,6 @@
/* global web3 */
import Escrow from '../../../embarkArtifacts/contracts/Escrow';
import Arbitration from '../../../embarkArtifacts/contracts/Arbitration';
import ArbitrationLicense from '../../../embarkArtifacts/contracts/ArbitrationLicense';
import SNT from '../../../embarkArtifacts/contracts/SNT';
import MetadataStore from '../../../embarkArtifacts/contracts/MetadataStore';
import moment from 'moment';
@ -31,10 +31,10 @@ export function *onCancelDispute() {
export function *doGetArbitrators() {
try {
const cnt = yield call(Arbitration.methods.getNumLicenseOwners().call);
const cnt = yield call(ArbitrationLicense.methods.getNumLicenseOwners().call);
const arbitrators = [];
for(let i = 0; i < cnt; i++){
arbitrators.push(yield call(Arbitration.methods.licenseOwners(i).call));
arbitrators.push(yield call(ArbitrationLicense.methods.licenseOwners(i).call));
}
yield put({type: GET_ARBITRATORS_SUCCEEDED, arbitrators});
} catch (error) {
@ -45,7 +45,7 @@ export function *doGetArbitrators() {
export function *doGetEscrows() {
try {
const events = yield Arbitration.getPastEvents('ArbitrationRequired', {fromBlock: 1});
const events = yield Escrow.getPastEvents('ArbitrationRequired', {fromBlock: 1});
const escrows = [];
for (let i = 0; i < events.length; i++) {
@ -56,7 +56,7 @@ export function *doGetEscrows() {
escrow.escrowId = escrowId;
escrow.seller = offer.owner;
escrow.arbitration = yield call(Arbitration.methods.arbitrationCases(escrowId).call);
escrow.arbitration = yield call(Escrow.methods.arbitrationCases(escrowId).call);
escrow.arbitration.createDate = moment(events[i].returnValues.date * 1000).format("DD.MM.YY");
escrows.push(escrow);
@ -88,7 +88,7 @@ export function *doLoadArbitration({escrowId}) {
escrow.escrowId = escrowId;
escrow.seller = offer.owner;
escrow.offer = offer;
escrow.arbitration = yield call(Arbitration.methods.arbitrationCases(escrowId).call);
escrow.arbitration = yield call(Escrow.methods.arbitrationCases(escrowId).call);
yield put({type: LOAD_ARBITRATION_SUCCEEDED, escrow});
} catch (error) {
@ -104,9 +104,9 @@ export function *onLoadArbitration() {
export function *doBuyLicense() {
try {
const price = yield call(Arbitration.methods.price().call);
const encodedCall = Arbitration.methods.buy().encodeABI();
const toSend = SNT.methods.approveAndCall(Arbitration.options.address, price, encodedCall);
const price = yield call(ArbitrationLicense.methods.price().call);
const encodedCall = ArbitrationLicense.methods.buy().encodeABI();
const toSend = SNT.methods.approveAndCall(ArbitrationLicense.options.address, price, encodedCall);
const estimatedGas = yield call(toSend.estimateGas);
const promiseEvent = toSend.send({gasLimit: estimatedGas + 2000});
const channel = eventChannel(promiseEventEmitter.bind(null, promiseEvent));
@ -134,7 +134,7 @@ export function *onBuyLicense() {
export function *loadPrice() {
try {
const price = yield call(Arbitration.methods.price().call);
const price = yield call(ArbitrationLicense.methods.price().call);
yield put({type: LOAD_PRICE_SUCCEEDED, price});
} catch (error) {
console.error(error);
@ -148,7 +148,7 @@ export function *onLoadPrice() {
export function *doCheckLicenseOwner() {
try {
const isLicenseOwner = yield call(Arbitration.methods.isLicenseOwner(web3.eth.defaultAccount).call);
const isLicenseOwner = yield call(ArbitrationLicense.methods.isLicenseOwner(web3.eth.defaultAccount).call);
yield put({type: CHECK_LICENSE_OWNER_SUCCEEDED, isLicenseOwner});
} catch (error) {
console.error(error);

View File

@ -1,5 +1,5 @@
/*global web3*/
import License from '../../../embarkArtifacts/contracts/License';
import SellerLicense from '../../../embarkArtifacts/contracts/SellerLicense';
import SNT from '../../../embarkArtifacts/contracts/SNT';
import {fork, takeEvery, call, put, take} from 'redux-saga/effects';
import {
@ -18,9 +18,9 @@ window.SNT = SNT;
export function *doBuyLicense() {
try {
const price = yield call(License.methods.price().call);
const encodedCall = License.methods.buy().encodeABI();
const toSend = SNT.methods.approveAndCall(License.options.address, price, encodedCall);
const price = yield call(SellerLicense.methods.price().call);
const encodedCall = SellerLicense.methods.buy().encodeABI();
const toSend = SNT.methods.approveAndCall(SellerLicense.options.address, price, encodedCall);
const estimatedGas = yield call(toSend.estimateGas);
const promiseEvent = toSend.send({gasLimit: estimatedGas + 2000});
const channel = eventChannel(promiseEventEmitter.bind(null, promiseEvent));
@ -48,7 +48,7 @@ export function *onBuyLicense() {
export function *loadPrice() {
try {
const price = yield call(License.methods.price().call);
const price = yield call(SellerLicense.methods.price().call);
yield put({type: LOAD_PRICE_SUCCEEDED, price});
} catch (error) {
console.error(error);
@ -62,7 +62,7 @@ export function *onLoadPrice() {
export function *doCheckLicenseOwner() {
try {
const isLicenseOwner = yield call(License.methods.isLicenseOwner(web3.eth.defaultAccount).call);
const isLicenseOwner = yield call(SellerLicense.methods.isLicenseOwner(web3.eth.defaultAccount).call);
yield put({type: CHECK_LICENSE_OWNER_SUCCEEDED, isLicenseOwner});
} catch (error) {
console.error(error);
@ -77,7 +77,7 @@ export function *onCheckLicenseOwner() {
export function *doGetLicenseOwners() {
try {
// TODO get more information like position and rate
const events = yield License.getPastEvents('Bought', {fromBlock: 1});
const events = yield SellerLicense.getPastEvents('Bought', {fromBlock: 1});
const licenseOwners = events.map(event => {
return {address: event.returnValues.buyer};
});

View File

@ -1,5 +1,5 @@
import MetadataStore from '../../../embarkArtifacts/contracts/MetadataStore';
import Arbitration from '../../../embarkArtifacts/contracts/Arbitration';
import ArbitrationLicense from '../../../embarkArtifacts/contracts/ArbitrationLicense';
import Escrow from '../../../embarkArtifacts/contracts/Escrow';
import {fork, takeEvery, put, all} from 'redux-saga/effects';
@ -16,7 +16,7 @@ import {getLocation} from '../../services/googleMap';
export function *loadUser({address}) {
try {
const isArbitrator = yield Arbitration.methods.isArbitrator(address).call();
const isArbitrator = yield ArbitrationLicense.methods.isLicenseOwner(address).call();
const isUser = yield MetadataStore.methods.userWhitelist(address).call();
let user = {

View File

@ -78,9 +78,6 @@ class EditMyContact extends Component {
}
render() {
console.log(this.props);
console.log(this.state);
if(!this.props.profile){
return <Loading />;
}

View File

@ -2,12 +2,12 @@
const EthUtil = require('ethereumjs-util');
const TestUtils = require("../utils/testUtils");
const License = embark.require('Embark/contracts/License');
const SellerLicense = embark.require('Embark/contracts/SellerLicense');
const ArbitrationLicense = embark.require('Embark/contracts/ArbitrationLicense');
const MetadataStore = embark.require('Embark/contracts/MetadataStore');
const Escrow = embark.require('Embark/contracts/Escrow');
const StandardToken = embark.require('Embark/contracts/StandardToken');
const SNT = embark.require('Embark/contracts/SNT');
const Arbitration = embark.require('Embark/contracts/Arbitration');
const ESCROW_CREATED = 0;
@ -54,20 +54,22 @@ config({
]
},
License: {
deploy: false
},
SellerLicense: {
instanceOf: "License",
args: ["$SNT", 10]
},
ArbitrationLicense: {
instanceOf: "License",
args: ["$SNT", 10]
},
MetadataStore: {
args: ["$License", "$Arbitration"]
},
Arbitration: {
args: ["$SNT", 10]
args: ["$SellerLicense", "$ArbitrationLicense"]
},
Escrow: {
args: ["$License", "$Arbitration", "$MetadataStore", "$SNT", "0x0000000000000000000000000000000000000001", feeAmount],
onDeploy: [
"Arbitration.methods.setEscrowAddress('$Escrow').send()",
"MetadataStore.methods.setEscrowAddress('$Escrow').send()"
]
args: ["$SellerLicense", "$ArbitrationLicense", "$MetadataStore", "$SNT", "0x0000000000000000000000000000000000000001", feeAmount],
onDeploy: ["MetadataStore.methods.setEscrowAddress('$Escrow').send()"]
},
StandardToken: {
}
@ -95,27 +97,21 @@ contract("Escrow", function() {
this.timeout(0);
before(async () => {
const escrowEvents = Escrow.options.jsonInterface.filter(x => x.type === 'event');
const arbitrationEvents = Arbitration.options.jsonInterface.filter(x => x.type === 'event');
Escrow.options.jsonInterface = Escrow.options.jsonInterface.concat(arbitrationEvents);
Arbitration.options.jsonInterface = Arbitration.options.jsonInterface.concat(escrowEvents);
await SNT.methods.generateTokens(accounts[0], 1000).send();
const encodedCall = License.methods.buy().encodeABI();
await SNT.methods.approveAndCall(License.options.address, 10, encodedCall).send({from: accounts[0]});
const encodedCall = SellerLicense.methods.buy().encodeABI();
await SNT.methods.approveAndCall(SellerLicense.options.address, 10, encodedCall).send({from: accounts[0]});
// Register arbitrators
await SNT.methods.generateTokens(arbitrator, 1000).send();
await SNT.methods.generateTokens(arbitrator2, 1000).send();
const encodedCall2 = Arbitration.methods.buy().encodeABI();
await SNT.methods.approveAndCall(Arbitration.options.address, 10, encodedCall2).send({from: arbitrator});
await SNT.methods.approveAndCall(Arbitration.options.address, 10, encodedCall2).send({from: arbitrator2});
receipt = await MetadataStore.methods.addOffer(TestUtils.zeroAddress, License.address, "London", "USD", "Iuri", [0], 1, arbitrator).send({from: accounts[0]});
const encodedCall2 = ArbitrationLicense.methods.buy().encodeABI();
await SNT.methods.approveAndCall(ArbitrationLicense.options.address, 10, encodedCall2).send({from: arbitrator});
await SNT.methods.approveAndCall(ArbitrationLicense.options.address, 10, encodedCall2).send({from: arbitrator2});
receipt = await MetadataStore.methods.addOffer(TestUtils.zeroAddress, SellerLicense.address, "London", "USD", "Iuri", [0], 1, arbitrator).send({from: accounts[0]});
ethOfferId = receipt.events.OfferAdded.returnValues.offerId;
receipt = await MetadataStore.methods.addOffer(StandardToken.options.address, License.address, "London", "USD", "Iuri", [0], 1, arbitrator).send({from: accounts[0]});
receipt = await MetadataStore.methods.addOffer(StandardToken.options.address, SellerLicense.address, "London", "USD", "Iuri", [0], 1, arbitrator).send({from: accounts[0]});
tokenOfferId = receipt.events.OfferAdded.returnValues.offerId;
});
@ -676,12 +672,12 @@ contract("Escrow", function() {
receipt = await Escrow.methods['pay(uint256,bytes)'](escrowId, signatureRPC).send({from: accounts[8]});
messageToSign = await Escrow.methods.openCaseSignHash(escrowId).call();
messageToSign = await Escrow.methods.openCaseSignHash(escrowId, "My motive is...").call();
msgHash = EthUtil.hashPersonalMessage(Buffer.from(EthUtil.stripHexPrefix(messageToSign), 'hex'));
signature = EthUtil.ecsign(msgHash, privateKey);
signatureRPC = EthUtil.toRpcSig(signature.v, signature.r, signature.s);
receipt = await Escrow.methods.openCaseWithSignature(escrowId, signatureRPC).send({from: accounts[9]});
receipt = await Escrow.methods.openCaseWithSignature(escrowId, "My motive is...", signatureRPC).send({from: accounts[9]});
const arbitrationRequired = receipt.events.ArbitrationRequired;
assert(!!arbitrationRequired, "ArbitrationRequired() not triggered");
assert.equal(arbitrationRequired.returnValues.escrowId, escrowId, "Invalid escrowId");
@ -695,7 +691,7 @@ contract("Escrow", function() {
await Escrow.methods.openCase(escrowId, 'Motive').send({from: accounts[1]});
try {
receipt = await Arbitration.methods.setArbitrationResult(escrowId, ARBITRATION_SOLVED_BUYER).send({from: accounts[1]});
receipt = await Escrow.methods.setArbitrationResult(escrowId, ARBITRATION_SOLVED_BUYER).send({from: accounts[1]});
assert.fail('should have reverted before');
} catch (error) {
assert.strictEqual(error.message, "VM Exception while processing transaction: revert Only arbitrators can invoke this function");
@ -707,7 +703,7 @@ contract("Escrow", function() {
await Escrow.methods.openCase(escrowId, 'Motive').send({from: accounts[1]});
try {
receipt = await Arbitration.methods.setArbitrationResult(escrowId, ARBITRATION_SOLVED_BUYER).send({from: arbitrator2});
receipt = await Escrow.methods.setArbitrationResult(escrowId, ARBITRATION_SOLVED_BUYER).send({from: arbitrator2});
assert.fail('should have reverted before');
} catch (error) {
TestUtils.assertJump(error);
@ -719,13 +715,13 @@ contract("Escrow", function() {
receipt = await Escrow.methods.openCase(escrowId, 'Motive').send({from: accounts[1]});
try {
receipt = await Arbitration.methods.cancelArbitration(escrowId).send({from: accounts[0]});
receipt = await Escrow.methods.cancelArbitration(escrowId).send({from: accounts[0]});
assert.fail('should have reverted before');
} catch (error) {
assert.strictEqual(error.message, "VM Exception while processing transaction: revert Arbitration can only be canceled by the opener");
}
receipt = await Arbitration.methods.cancelArbitration(escrowId).send({from: accounts[1]});
receipt = await Escrow.methods.cancelArbitration(escrowId).send({from: accounts[1]});
const arbitrationCanceled = receipt.events.ArbitrationCanceled;
assert(!!arbitrationCanceled, "ArbitrationCanceled() not triggered");
assert.equal(arbitrationCanceled.returnValues.escrowId, escrowId, "Invalid escrowId");
@ -735,7 +731,7 @@ contract("Escrow", function() {
await Escrow.methods.pay(escrowId).send({from: accounts[1]});
await Escrow.methods.openCase(escrowId, 'Motive').send({from: accounts[1]});
receipt = await Arbitration.methods.setArbitrationResult(escrowId, ARBITRATION_SOLVED_BUYER).send({from: arbitrator});
receipt = await Escrow.methods.setArbitrationResult(escrowId, ARBITRATION_SOLVED_BUYER).send({from: arbitrator});
const released = receipt.events.Released;
assert(!!released, "Released() not triggered");
});
@ -744,7 +740,7 @@ contract("Escrow", function() {
await Escrow.methods.pay(escrowId).send({from: accounts[1]});
await Escrow.methods.openCase(escrowId, 'Motive').send({from: accounts[1]});
receipt = await Arbitration.methods.setArbitrationResult(escrowId, ARBITRATION_SOLVED_SELLER).send({from: arbitrator});
receipt = await Escrow.methods.setArbitrationResult(escrowId, ARBITRATION_SOLVED_SELLER).send({from: arbitrator});
const released = receipt.events.Canceled;
assert(!!released, "Canceled() not triggered");
@ -753,10 +749,10 @@ contract("Escrow", function() {
it("cannot cancel a solved arbitration", async() => {
await Escrow.methods.pay(escrowId).send({from: accounts[1]});
receipt = await Escrow.methods.openCase(escrowId, 'Motive').send({from: accounts[1]});
receipt = await Arbitration.methods.setArbitrationResult(escrowId, ARBITRATION_SOLVED_SELLER).send({from: arbitrator});
receipt = await Escrow.methods.setArbitrationResult(escrowId, ARBITRATION_SOLVED_SELLER).send({from: arbitrator});
try {
receipt = await Arbitration.methods.cancelArbitration(escrowId).send({from: accounts[1]});
receipt = await Escrow.methods.cancelArbitration(escrowId).send({from: accounts[1]});
assert.fail('should have reverted before');
} catch (error) {
assert.strictEqual(error.message, "VM Exception while processing transaction: revert Arbitration already solved or not open");
@ -868,10 +864,10 @@ contract("Escrow", function() {
});
it("arbitrator should be valid", async () => {
const isArbitrator = await Arbitration.methods.isArbitrator(arbitrator).call();
const isArbitrator = await ArbitrationLicense.methods.isLicenseOwner(arbitrator).call();
assert.equal(isArbitrator, true, "Invalid arbitrator");
const nonArbitrator = await Arbitration.methods.isArbitrator(accounts[5]).call();
const nonArbitrator = await ArbitrationLicense.methods.isLicenseOwner(accounts[5]).call();
assert.equal(nonArbitrator, false, "Account should not be an arbitrator");
});
});

View File

@ -1,9 +1,9 @@
/*global contract, config, it, embark, web3, before, describe, beforeEach*/
const TestUtils = require("../utils/testUtils");
const License = embark.require('Embark/contracts/License');
const SellerLicense = embark.require('Embark/contracts/SellerLicense');
const MetadataStore = embark.require('Embark/contracts/MetadataStore');
const Arbitration = embark.require('Embark/contracts/Arbitration');
const ArbitrationLicense = embark.require('Embark/contracts/ArbitrationLicense');
const Escrow = embark.require('Embark/contracts/Escrow');
const StandardToken = embark.require('Embark/contracts/StandardToken');
const SNT = embark.require('Embark/contracts/SNT');
@ -36,18 +36,22 @@ config({
]
},
License: {
deploy: false
},
SellerLicense: {
instanceOf: "License",
args: ["$SNT", 10]
},
MetadataStore: {
args: ["$License", "$Arbitration"]
args: ["$SellerLicense", "$ArbitrationLicense"]
},
Arbitration: {
ArbitrationLicense: {
instanceOf: "License",
args: ["$SNT", 10]
},
Escrow: {
args: ["$License", "$accounts[5]", "$MetadataStore", "$SNT", "0x0000000000000000000000000000000000000001", feeAmount],
args: ["$SellerLicense", "$ArbitrationLicense", "$MetadataStore", "$SNT", "0x0000000000000000000000000000000000000001", feeAmount],
onDeploy: [
"Arbitration.methods.setEscrowAddress('$Escrow').send()",
"MetadataStore.methods.setEscrowAddress('$Escrow').send()"
]
},
@ -75,22 +79,22 @@ contract("Escrow Funding", function() {
before(async () => {
await StandardToken.methods.mint(accounts[0], 100000000).send();
await SNT.methods.generateTokens(accounts[0], 100000000).send();
const encodedCall = License.methods.buy().encodeABI();
await SNT.methods.approveAndCall(License.options.address, 10, encodedCall).send({from: accounts[0]});
const encodedCall = SellerLicense.methods.buy().encodeABI();
await SNT.methods.approveAndCall(SellerLicense.options.address, 10, encodedCall).send({from: accounts[0]});
// Register arbitrators
arbitrator = accounts[9];
await SNT.methods.generateTokens(arbitrator, 1000).send();
const encodedCall2 = Arbitration.methods.buy().encodeABI();
await SNT.methods.approveAndCall(Arbitration.options.address, 10, encodedCall2).send({from: arbitrator});
const encodedCall2 = ArbitrationLicense.methods.buy().encodeABI();
await SNT.methods.approveAndCall(ArbitrationLicense.options.address, 10, encodedCall2).send({from: arbitrator});
receipt = await MetadataStore.methods.addOffer(TestUtils.zeroAddress, License.address, "London", "USD", "Iuri", [0], 1, arbitrator).send({from: accounts[0]});
receipt = await MetadataStore.methods.addOffer(TestUtils.zeroAddress, SellerLicense.address, "London", "USD", "Iuri", [0], 1, arbitrator).send({from: accounts[0]});
ethOfferId = receipt.events.OfferAdded.returnValues.offerId;
receipt = await MetadataStore.methods.addOffer(StandardToken.options.address, License.address, "London", "USD", "Iuri", [0], 1, arbitrator).send({from: accounts[0]});
receipt = await MetadataStore.methods.addOffer(StandardToken.options.address, SellerLicense.address, "London", "USD", "Iuri", [0], 1, arbitrator).send({from: accounts[0]});
tokenOfferId = receipt.events.OfferAdded.returnValues.offerId;
receipt = await MetadataStore.methods.addOffer(SNT.options.address, License.address, "London", "USD", "Iuri", [0], 1, arbitrator).send({from: accounts[0]});
receipt = await MetadataStore.methods.addOffer(SNT.options.address, SellerLicense.address, "London", "USD", "Iuri", [0], 1, arbitrator).send({from: accounts[0]});
SNTOfferId = receipt.events.OfferAdded.returnValues.offerId;
});

View File

@ -1,6 +1,8 @@
/*global contract, config, it, assert, before*/
const License = require('Embark/contracts/License');
const SellerLicense = require('Embark/contracts/SellerLicense');
const ArbitrationLicense = require('Embark/contracts/ArbitrationLicense');
const SNT = require('Embark/contracts/SNT');
const MetadataStore = require('Embark/contracts/MetadataStore');
@ -24,14 +26,19 @@ config({
true
]
},
Arbitration: {
License: {
deploy: false
},
SellerLicense: {
instanceOf: "License",
args: ["$SNT", 10]
},
License: {
ArbitrationLicense: {
instanceOf: "License",
args: ["$SNT", 10]
},
MetadataStore: {
args: ["$License", "$Arbitration"]
args: ["$SellerLicense", "$ArbitrationLicense"]
}
}
}, (_err, web3_accounts) => {
@ -45,7 +52,7 @@ contract("MetadataStore", function () {
it("should not allow to add new user when not license owner", async function () {
try {
await MetadataStore.methods.addOffer(SNT.address, License.address, "London", "USD", "Iuri", [0], 1, accounts[9]).send();
await MetadataStore.methods.addOffer(SNT.address, SellerLicense.address, "London", "USD", "Iuri", [0], 1, accounts[9]).send();
} catch(error) {
const usersSize = await MetadataStore.methods.usersSize().call();
assert.strictEqual(usersSize, '0');
@ -53,9 +60,9 @@ contract("MetadataStore", function () {
});
it("should allow to add new user and offer when license owner", async function () {
const encodedCall = License.methods.buy().encodeABI();
await SNT.methods.approveAndCall(License.options.address, 10, encodedCall).send();
await MetadataStore.methods.addOffer(SNT.address, License.address, "London", "USD", "Iuri", [0], 1, accounts[9]).send();
const encodedCall = SellerLicense.methods.buy().encodeABI();
await SNT.methods.approveAndCall(SellerLicense.options.address, 10, encodedCall).send();
await MetadataStore.methods.addOffer(SNT.address, SellerLicense.address, "London", "USD", "Iuri", [0], 1, accounts[9]).send();
const usersSize = await MetadataStore.methods.usersSize().call();
assert.strictEqual(usersSize, '1');
const offersSize = await MetadataStore.methods.offersSize().call();
@ -63,7 +70,7 @@ contract("MetadataStore", function () {
});
it("should allow to add new offer only when already a user", async function () {
await MetadataStore.methods.addOffer(SNT.address, License.address, "London", "EUR", "Iuri", [0], 1, accounts[9]).send();
await MetadataStore.methods.addOffer(SNT.address, SellerLicense.address, "London", "EUR", "Iuri", [0], 1, accounts[9]).send();
const usersSize = await MetadataStore.methods.usersSize().call();
assert.strictEqual(usersSize, '1');
const offersSize = await MetadataStore.methods.offersSize().call();
@ -72,7 +79,7 @@ contract("MetadataStore", function () {
it("should not allow to add new offer when margin is more than 100", async function () {
try {
await MetadataStore.methods.addOffer(SNT.address, License.address, "London", "USD", "Iuri", [0], 101, accounts[9]).send();
await MetadataStore.methods.addOffer(SNT.address, SellerLicense.address, "London", "USD", "Iuri", [0], 101, accounts[9]).send();
} catch(error) {
const usersSize = await MetadataStore.methods.usersSize().call();
assert.strictEqual(usersSize, '1');
@ -80,7 +87,7 @@ contract("MetadataStore", function () {
});
it("should allow to update a user and offer", async function () {
await MetadataStore.methods.updateOffer(0, SNT.address, License.address, "Paris", "EUR", "Iuri", [0], 1, accounts[9]).send();
await MetadataStore.methods.updateOffer(0, SNT.address, SellerLicense.address, "Paris", "EUR", "Iuri", [0], 1, accounts[9]).send();
const usersSize = await MetadataStore.methods.usersSize().call();
assert.strictEqual(usersSize, '1');
const user = await MetadataStore.methods.users(0).call();
@ -100,7 +107,7 @@ contract("MetadataStore", function () {
});
it("should allow to delete an offer", async function () {
const receipt = await MetadataStore.methods.addOffer(SNT.address, License.address, "London", "EUR", "Iuri", [0], 1, accounts[9]).send();
const receipt = await MetadataStore.methods.addOffer(SNT.address, SellerLicense.address, "London", "EUR", "Iuri", [0], 1, accounts[9]).send();
const offerAdded = receipt.events.OfferAdded;
const offerId = offerAdded.returnValues.offerId;