mirror of https://github.com/embarklabs/embark.git
295 lines
13 KiB
JavaScript
295 lines
13 KiB
JavaScript
|
/*global contract, config, it, assert, web3, before, describe, beforeEach, artifacts*/
|
||
|
/* eslint require-atomic-updates:0, no-await-in-loop:0*/
|
||
|
const TestUtils = require("../utils/testUtils");
|
||
|
|
||
|
const Escrow = artifacts.require('Escrow');
|
||
|
const ArbitrationLicense = artifacts.require('ArbitrationLicense');
|
||
|
const OfferStore = artifacts.require('OfferStore');
|
||
|
const SNT = artifacts.require('SNT');
|
||
|
|
||
|
let accounts;
|
||
|
let arbitrator, arbitrator2, blacklistedAccount;
|
||
|
|
||
|
const feePercent = 1;
|
||
|
const BURN_ADDRESS = "0x0000000000000000000000000000000000000002";
|
||
|
|
||
|
const CONTACT_DATA = "Status:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
|
||
|
|
||
|
config({
|
||
|
blockchain: {
|
||
|
// The order here corresponds to the order of `web3.eth.getAccounts`, so the first one is the `defaultAccount`
|
||
|
accounts: [
|
||
|
{
|
||
|
mnemonic: "foster gesture flock merge beach plate dish view friend leave drink valley shield list enemy",
|
||
|
balance: "5 ether",
|
||
|
numAddresses: "10"
|
||
|
}
|
||
|
]
|
||
|
},
|
||
|
contracts: {
|
||
|
deploy: {
|
||
|
"MiniMeToken": {"deploy": false},
|
||
|
"MiniMeTokenFactory": {},
|
||
|
"SNT": {
|
||
|
"instanceOf": "MiniMeToken",
|
||
|
"args": [
|
||
|
"$MiniMeTokenFactory",
|
||
|
"0x0000000000000000000000000000000000000000",
|
||
|
0,
|
||
|
"TestMiniMeToken",
|
||
|
18,
|
||
|
"STT",
|
||
|
true
|
||
|
]
|
||
|
},
|
||
|
License: {
|
||
|
deploy: false
|
||
|
},
|
||
|
ArbitrationLicense: {
|
||
|
args: ["$SNT", 10, BURN_ADDRESS]
|
||
|
},
|
||
|
SellerLicense: {
|
||
|
instanceOf: "License",
|
||
|
args: ["$SNT", 10, BURN_ADDRESS]
|
||
|
},
|
||
|
|
||
|
/*
|
||
|
StakingPool: {
|
||
|
file: 'staking-pool/contracts/StakingPool.sol',
|
||
|
args: ["$SNT"]
|
||
|
},
|
||
|
*/
|
||
|
UserStore: {
|
||
|
args: ["$SellerLicense", "$ArbitrationLicense"]
|
||
|
},
|
||
|
Medianizer: {
|
||
|
|
||
|
},
|
||
|
OfferStore: {
|
||
|
args: ["$UserStore", "$SellerLicense", "$ArbitrationLicense", BURN_ADDRESS, "$Medianizer"],
|
||
|
onDeploy: ["UserStore.methods.setAllowedContract('$OfferStore', true).send()"]
|
||
|
},
|
||
|
Escrow: {
|
||
|
args: ["$accounts[0]", "0x0000000000000000000000000000000000000000", "$ArbitrationLicense", "$OfferStore", "$UserStore", BURN_ADDRESS, feePercent * 1000],
|
||
|
onDeploy: [
|
||
|
"OfferStore.methods.setAllowedContract('$Escrow', true).send()",
|
||
|
"UserStore.methods.setAllowedContract('$Escrow', true).send()"
|
||
|
]
|
||
|
},
|
||
|
StandardToken: {}
|
||
|
}
|
||
|
}
|
||
|
}, (_err, web3_accounts) => {
|
||
|
accounts = web3_accounts;
|
||
|
arbitrator = accounts[8];
|
||
|
arbitrator2 = accounts[9];
|
||
|
blacklistedAccount = accounts[5];
|
||
|
});
|
||
|
|
||
|
contract("Escrow", function() {
|
||
|
|
||
|
const tradeAmount = 100;
|
||
|
const feeAmount = Math.round(tradeAmount * (feePercent / 100));
|
||
|
|
||
|
let receipt, escrowId, ethOfferId;
|
||
|
let created;
|
||
|
|
||
|
this.timeout(0);
|
||
|
|
||
|
before(async () => {
|
||
|
await SNT.methods.generateTokens(accounts[0], 1000).send();
|
||
|
await SNT.methods.generateTokens(blacklistedAccount, 1000).send();
|
||
|
|
||
|
// Register arbitrators
|
||
|
await SNT.methods.generateTokens(arbitrator, 1000).send();
|
||
|
await SNT.methods.generateTokens(arbitrator2, 1000).send();
|
||
|
|
||
|
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});
|
||
|
await ArbitrationLicense.methods.changeAcceptAny(true).send({from: arbitrator});
|
||
|
await ArbitrationLicense.methods.changeAcceptAny(true).send({from: arbitrator2});
|
||
|
await ArbitrationLicense.methods.blacklistSeller(blacklistedAccount).send({from: arbitrator});
|
||
|
|
||
|
const amountToStake = await OfferStore.methods.getAmountToStake(accounts[0]).call();
|
||
|
|
||
|
receipt = await OfferStore.methods.addOffer(TestUtils.zeroAddress, CONTACT_DATA, "London", "USD", "Iuri", [0], 0, 0, 1, arbitrator).send({from: accounts[0], value: amountToStake});
|
||
|
ethOfferId = receipt.events.OfferAdded.returnValues.offerId;
|
||
|
});
|
||
|
|
||
|
describe("Arbitrations", async() => {
|
||
|
beforeEach(async() => {
|
||
|
// Create
|
||
|
receipt = await Escrow.methods.createEscrow(ethOfferId, tradeAmount, 140, accounts[1], CONTACT_DATA, "L", "U").send({from: accounts[1]});
|
||
|
created = receipt.events.Created;
|
||
|
escrowId = created.returnValues.escrowId;
|
||
|
// Fund
|
||
|
receipt = await Escrow.methods.fund(escrowId).send({from: accounts[0], value: tradeAmount + feeAmount});
|
||
|
});
|
||
|
|
||
|
it("should allow a buyer to open a case", async() => {
|
||
|
await Escrow.methods.pay(escrowId).send({from: accounts[1]});
|
||
|
|
||
|
receipt = await Escrow.methods.openCase(escrowId, '1').send({from: accounts[1]});
|
||
|
const arbitrationRequired = receipt.events.ArbitrationRequired;
|
||
|
assert(!!arbitrationRequired, "ArbitrationRequired() not triggered");
|
||
|
assert.equal(arbitrationRequired.returnValues.escrowId, escrowId, "Invalid escrowId");
|
||
|
});
|
||
|
|
||
|
it("random account cannot open a case for an existing escrow", async() => {
|
||
|
await Escrow.methods.pay(escrowId).send({from: accounts[1]});
|
||
|
|
||
|
try {
|
||
|
await Escrow.methods.openCase(escrowId, '1').send({from: accounts[3]});
|
||
|
assert.fail('should have reverted before');
|
||
|
} catch (error) {
|
||
|
assert.strictEqual(error.message, "Returned error: VM Exception while processing transaction: revert Only participants can invoke this function");
|
||
|
}
|
||
|
});
|
||
|
|
||
|
it("should allow anyone to open an arbitration case on behalf of a buyer", async() => {
|
||
|
let messageToSign, signature;
|
||
|
|
||
|
// Create
|
||
|
receipt = await Escrow.methods.createEscrow(ethOfferId, tradeAmount, 140, accounts[1], CONTACT_DATA, "L", "U").send({from: accounts[1]});
|
||
|
created = receipt.events.Created;
|
||
|
escrowId = created.returnValues.escrowId;
|
||
|
// Fund
|
||
|
receipt = await Escrow.methods.fund(escrowId).send({from: accounts[0], value: tradeAmount + feeAmount});
|
||
|
|
||
|
messageToSign = await Escrow.methods.paySignHash(escrowId).call();
|
||
|
signature = await web3.eth.sign(messageToSign, accounts[1]);
|
||
|
|
||
|
receipt = await Escrow.methods['pay(uint256,bytes)'](escrowId, signature).send({from: accounts[8]});
|
||
|
|
||
|
messageToSign = await Escrow.methods.openCaseSignHash(escrowId, "1").call();
|
||
|
signature = await web3.eth.sign(messageToSign, accounts[1]);
|
||
|
|
||
|
receipt = await Escrow.methods['openCase(uint256,uint8,bytes)'](escrowId, "1", signature).send({from: accounts[9]});
|
||
|
const arbitrationRequired = receipt.events.ArbitrationRequired;
|
||
|
assert(!!arbitrationRequired, "ArbitrationRequired() not triggered");
|
||
|
assert.equal(arbitrationRequired.returnValues.escrowId, escrowId, "Invalid escrowId");
|
||
|
});
|
||
|
|
||
|
const ARBITRATION_SOLVED_BUYER = 1;
|
||
|
const ARBITRATION_SOLVED_SELLER = 2;
|
||
|
|
||
|
it("non arbitrators cannot resolve a case", async() => {
|
||
|
await Escrow.methods.pay(escrowId).send({from: accounts[1]});
|
||
|
await Escrow.methods.openCase(escrowId, '1').send({from: accounts[1]});
|
||
|
|
||
|
try {
|
||
|
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, "Returned error: VM Exception while processing transaction: revert Only arbitrators can invoke this function");
|
||
|
}
|
||
|
});
|
||
|
|
||
|
it("non selected arbitrator cannot resolve a case", async() => {
|
||
|
await Escrow.methods.pay(escrowId).send({from: accounts[1]});
|
||
|
await Escrow.methods.openCase(escrowId, '1').send({from: accounts[1]});
|
||
|
|
||
|
try {
|
||
|
receipt = await Escrow.methods.setArbitrationResult(escrowId, ARBITRATION_SOLVED_BUYER).send({from: arbitrator2});
|
||
|
assert.fail('should have reverted before');
|
||
|
} catch (error) {
|
||
|
TestUtils.assertJump(error);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
it("should allow whoever opened an arbitration to cancel it", async() => {
|
||
|
await Escrow.methods.pay(escrowId).send({from: accounts[1]});
|
||
|
receipt = await Escrow.methods.openCase(escrowId, '1').send({from: accounts[1]});
|
||
|
|
||
|
try {
|
||
|
receipt = await Escrow.methods.cancelArbitration(escrowId).send({from: accounts[0]});
|
||
|
assert.fail('should have reverted before');
|
||
|
} catch (error) {
|
||
|
assert.strictEqual(error.message, "Returned error: VM Exception while processing transaction: revert Arbitration can only be canceled by the opener");
|
||
|
}
|
||
|
|
||
|
receipt = await Escrow.methods.cancelArbitration(escrowId).send({from: accounts[1]});
|
||
|
const arbitrationCanceled = receipt.events.ArbitrationCanceled;
|
||
|
assert(!!arbitrationCanceled, "ArbitrationCanceled() not triggered");
|
||
|
assert.strictEqual(arbitrationCanceled.returnValues.escrowId, escrowId, "Invalid escrowId");
|
||
|
});
|
||
|
|
||
|
it("should transfer to buyer if case is solved in their favor", async() => {
|
||
|
await Escrow.methods.pay(escrowId).send({from: accounts[1]});
|
||
|
await Escrow.methods.openCase(escrowId, '1').send({from: accounts[1]});
|
||
|
|
||
|
receipt = await Escrow.methods.setArbitrationResult(escrowId, ARBITRATION_SOLVED_BUYER).send({from: arbitrator});
|
||
|
const released = receipt.events.Released;
|
||
|
assert(!!released, "Released() not triggered");
|
||
|
});
|
||
|
|
||
|
it("should cancel escrow if case is solved in favor of the seller", async() => {
|
||
|
await Escrow.methods.pay(escrowId).send({from: accounts[1]});
|
||
|
await Escrow.methods.openCase(escrowId, '1').send({from: accounts[1]});
|
||
|
|
||
|
receipt = await Escrow.methods.setArbitrationResult(escrowId, ARBITRATION_SOLVED_SELLER).send({from: arbitrator});
|
||
|
|
||
|
const released = receipt.events.Canceled;
|
||
|
assert(!!released, "Canceled() not triggered");
|
||
|
});
|
||
|
|
||
|
it("cannot cancel a solved arbitration", async() => {
|
||
|
await Escrow.methods.pay(escrowId).send({from: accounts[1]});
|
||
|
receipt = await Escrow.methods.openCase(escrowId, '1').send({from: accounts[1]});
|
||
|
receipt = await Escrow.methods.setArbitrationResult(escrowId, ARBITRATION_SOLVED_SELLER).send({from: arbitrator});
|
||
|
|
||
|
try {
|
||
|
receipt = await Escrow.methods.cancelArbitration(escrowId).send({from: accounts[1]});
|
||
|
assert.fail('should have reverted before');
|
||
|
} catch (error) {
|
||
|
assert.strictEqual(error.message, "Returned error: VM Exception while processing transaction: revert Arbitration already solved or not open");
|
||
|
}
|
||
|
});
|
||
|
|
||
|
it("can open an arbitration on a escrow that had a canceled arbitration before", async() => {
|
||
|
await Escrow.methods.pay(escrowId).send({from: accounts[1]});
|
||
|
receipt = await Escrow.methods.openCase(escrowId, '1').send({from: accounts[1]});
|
||
|
receipt = await Escrow.methods.cancelArbitration(escrowId).send({from: accounts[1]});
|
||
|
receipt = await Escrow.methods.openCase(escrowId, '1').send({from: accounts[1]});
|
||
|
const arbitrationRequired = receipt.events.ArbitrationRequired;
|
||
|
assert(!!arbitrationRequired, "ArbitrationRequired() not triggered");
|
||
|
});
|
||
|
|
||
|
it("arbitrator should be valid", async () => {
|
||
|
const isArbitrator = await ArbitrationLicense.methods.isLicenseOwner(arbitrator).call();
|
||
|
assert.equal(isArbitrator, true, "Invalid arbitrator");
|
||
|
|
||
|
const nonArbitrator = await ArbitrationLicense.methods.isLicenseOwner(accounts[5]).call();
|
||
|
assert.equal(nonArbitrator, false, "Account should not be an arbitrator");
|
||
|
});
|
||
|
|
||
|
it("should not be able to rate an open dispute", async() => {
|
||
|
await Escrow.methods.pay(escrowId).send({from: accounts[1]});
|
||
|
await Escrow.methods.openCase(escrowId, '1').send({from: accounts[1]});
|
||
|
|
||
|
try {
|
||
|
await Escrow.methods.rateTransaction(escrowId, 2).send({from: accounts[1]});
|
||
|
assert.fail('should have reverted before');
|
||
|
} catch (error) {
|
||
|
assert.strictEqual(error.message, "Returned error: VM Exception while processing transaction: revert Transaction not completed yet");
|
||
|
}
|
||
|
|
||
|
receipt = await Escrow.methods.setArbitrationResult(escrowId, ARBITRATION_SOLVED_BUYER).send({from: arbitrator});
|
||
|
|
||
|
await Escrow.methods.rateTransaction(escrowId, 2).send({from: accounts[1]});
|
||
|
});
|
||
|
|
||
|
it('should not allow a blacklisted seller to open an offer', async () => {
|
||
|
try {
|
||
|
const amountToStake = await OfferStore.methods.getAmountToStake(accounts[0]).call();
|
||
|
await OfferStore.methods.addOffer(TestUtils.zeroAddress, CONTACT_DATA, "London", "USD", "Iuri", [0], 0, 0, 1, arbitrator).send({from: blacklistedAccount, value: amountToStake});
|
||
|
assert.fail('should have reverted before');
|
||
|
} catch (error) {
|
||
|
assert.strictEqual(error.message, "Returned error: VM Exception while processing transaction: revert Arbitrator does not allow this transaction");
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
|
||
|
});
|