embark/dapps/tests/teller-contracts/test/funding_escrows_spec.js

283 lines
12 KiB
JavaScript

/*global contract, config, it, web3, before, describe, beforeEach, artifacts*/
/* eslint require-atomic-updates:0, no-await-in-loop:0*/
const TestUtils = require("../utils/testUtils");
const UserStore = artifacts.require('UserStore');
const OfferStore = artifacts.require('OfferStore');
const ArbitrationLicense = artifacts.require('ArbitrationLicense');
const Escrow = artifacts.require('Escrow');
const StandardToken = artifacts.require('StandardToken');
const SNT = artifacts.require('SNT');
const BURN_ADDRESS = "0x0000000000000000000000000000000000000002";
const CONTACT_DATA = "Status:0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
let accounts;
const fundAmount = 100;
const feePercent = 1;
const feeAmount = Math.round(fundAmount * (feePercent / 100));
config({
contracts: {
deploy: {
"MiniMeToken": {"deploy": false},
"MiniMeTokenFactory": {},
"SNT": {
"instanceOf": "MiniMeToken",
"args": [
"$MiniMeTokenFactory",
"0x0000000000000000000000000000000000000000",
0,
"TestMiniMeToken",
18,
"STT",
true
]
},
License: {
deploy: false
},
SellerLicense: {
instanceOf: "License",
args: ["$SNT", 10, BURN_ADDRESS]
},
UserStore: {
args: ["$SellerLicense", "$ArbitrationLicense"]
},
Medianizer: {
},
OfferStore: {
args: ["$UserStore", "$SellerLicense", "$ArbitrationLicense", BURN_ADDRESS, "$Medianizer"],
onDeploy: ["UserStore.methods.setAllowedContract('$OfferStore', true).send()"]
},
ArbitrationLicense: {
args: ["$SNT", 10, BURN_ADDRESS]
},
/*
StakingPool: {
file: 'staking-pool/contracts/StakingPool.sol',
args: ["$SNT"]
},
*/
Escrow: {
args: ["$accounts[0]", "0x0000000000000000000000000000000000000002", "$ArbitrationLicense", "$OfferStore", "$UserStore", BURN_ADDRESS, feePercent * 1000]
},
StandardToken: {}
}
}
}, (_err, web3_accounts) => {
accounts = web3_accounts;
});
function sequentialPromiseExec(tasks) {
return tasks.reduce((p, task) => p.then(task), Promise.resolve());
}
contract("Escrow Funding", function() {
const {toBN} = web3.utils;
const value = fundAmount + feeAmount;
let receipt, escrowId, ethOfferId, tokenOfferId, SNTOfferId, arbitrator;
this.timeout(0);
before(async () => {
await StandardToken.methods.mint(accounts[0], 100000000).send();
await SNT.methods.generateTokens(accounts[0], 100000000).send();
// Register arbitrators
arbitrator = accounts[9];
await SNT.methods.generateTokens(arbitrator, 1000).send();
const encodedCall2 = ArbitrationLicense.methods.buy().encodeABI();
await SNT.methods.approveAndCall(ArbitrationLicense.options.address, 10, encodedCall2).send({from: arbitrator});
await ArbitrationLicense.methods.changeAcceptAny(true).send({from: arbitrator});
let 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;
amountToStake = await OfferStore.methods.getAmountToStake(accounts[0]).call();
receipt = await OfferStore.methods.addOffer(StandardToken.options.address, CONTACT_DATA, "London", "USD", "Iuri", [0], 0, 0, 1, arbitrator).send({from: accounts[0], value: amountToStake});
tokenOfferId = receipt.events.OfferAdded.returnValues.offerId;
amountToStake = await OfferStore.methods.getAmountToStake(accounts[0]).call();
receipt = await OfferStore.methods.addOffer(SNT.options.address, CONTACT_DATA, "London", "USD", "Iuri", [0], 0, 0, 1, arbitrator).send({from: accounts[0], value: amountToStake});
SNTOfferId = receipt.events.OfferAdded.returnValues.offerId;
});
describe("ETH as asset", async () => {
beforeEach(async () => {
const hash = await UserStore.methods.getDataHash("Iuri", CONTACT_DATA).call({from: accounts[1]});
const nonce = await UserStore.methods.user_nonce(accounts[1]).call();
const signature = await web3.eth.sign(hash, accounts[1]);
receipt = await Escrow.methods.createEscrow(ethOfferId, fundAmount, 140, accounts[1], CONTACT_DATA, "U", "Iuri", nonce, signature)
.send({from: accounts[0]});
escrowId = receipt.events.Created.returnValues.escrowId;
});
it("Should fund escrow and deduct an SNT fee", async () => {
// Still requires 2 transactions, because approveAndCall cannot send ETH
// TODO: test if inside the contract we can encode the call, and call approveAndCall
receipt = await Escrow.methods.fund(escrowId).send({from: accounts[0], value});
});
});
describe("Tokens as Asset", async () => {
let escrowIdSNT, escrowIdToken;
beforeEach(async () => {
// Reset allowance
await SNT.methods.approve(Escrow.options.address, "0").send({from: accounts[0]});
await StandardToken.methods.approve(Escrow.options.address, "0").send({from: accounts[0]});
let hash = await UserStore.methods.getDataHash("Iuri", CONTACT_DATA).call({from: accounts[1]});
let signature = await web3.eth.sign(hash, accounts[1]);
let nonce = await UserStore.methods.user_nonce(accounts[1]).call();
receipt = await Escrow.methods.createEscrow(SNTOfferId, fundAmount, 140, accounts[1], CONTACT_DATA, "U", "Iuri", nonce, signature)
.send({from: accounts[0]});
escrowIdSNT = receipt.events.Created.returnValues.escrowId;
hash = await UserStore.methods.getDataHash("Iuri", CONTACT_DATA).call({from: accounts[1]});
signature = await web3.eth.sign(hash, accounts[1]);
nonce = await UserStore.methods.user_nonce(accounts[1]).call();
receipt = await Escrow.methods.createEscrow(tokenOfferId, fundAmount, 140, accounts[1], CONTACT_DATA, "U", "Iuri", nonce, signature)
.send({from: accounts[0]});
escrowIdToken = receipt.events.Created.returnValues.escrowId;
});
const execute = async (token, escrowId) => {
const {approvalPromises, trxToSend} = await tokenApprovalAndBuildTrx(token, escrowId);
await sequentialPromiseExec(approvalPromises);
await trxToSend.send({from: accounts[0]});
};
it("Allowance == to funds and fee. Token is SNT", async () => {
const amount = toBN(feeAmount).add(toBN(fundAmount)).toString(10);
await SNT.methods.approve(Escrow.options.address, amount).send({from: accounts[0]});
await execute(SNT, escrowIdSNT);
});
it("Allowance > to funds and fee. Token is SNT", async () => {
const amount = toBN(feeAmount).add(toBN(fundAmount)).add(toBN(100)).toString(10);
await SNT.methods.approve(Escrow.options.address, amount).send({from: accounts[0]});
await execute(SNT, escrowIdSNT);
});
it("Allowance < than funds and fee. Token is SNT", async () => {
const amount = toBN(feeAmount).add(toBN(fundAmount)).sub(toBN(10)).toString(10);
await SNT.methods.approve(Escrow.options.address, amount).send({from: accounts[0]});
await execute(SNT, escrowIdSNT);
});
it("Allowance == to required funds. Token is not SNT. SNT Allowance == required Fees", async () => {
await StandardToken.methods.approve(Escrow.options.address, fundAmount + feeAmount).send({from: accounts[0]});
await execute(StandardToken, escrowIdToken);
});
it("Allowance > to required funds. Token is not SNT. SNT Allowance == required Fees", async () => {
const amount = toBN(feeAmount).add(toBN(fundAmount)).add(toBN(100)).toString(10);
await StandardToken.methods.approve(Escrow.options.address, amount).send({from: accounts[0]});
await execute(StandardToken, escrowIdToken);
});
it("Allowance < to required funds. Token is not SNT. SNT Allowance == required Fees", async () => {
const amount = toBN(fundAmount).sub(toBN(10)).toString(10);
await StandardToken.methods.approve(Escrow.options.address, amount).send({from: accounts[0]});
await execute(StandardToken, escrowIdToken);
});
it("Allowance == to required funds. Token is not SNT. SNT Allowance > required Fees", async () => {
await StandardToken.methods.approve(Escrow.options.address, fundAmount + feeAmount).send({from: accounts[0]});
await SNT.methods.approve(Escrow.options.address, 1000).send({from: accounts[0]});
await execute(StandardToken, escrowIdToken);
});
it("Allowance > to required funds. Token is not SNT. SNT Allowance > required Fees", async () => {
const amount = toBN(feeAmount).add(toBN(fundAmount)).add(toBN(100)).toString(10);
await StandardToken.methods.approve(Escrow.options.address, amount).send({from: accounts[0]});
await SNT.methods.approve(Escrow.options.address, 1000).send({from: accounts[0]});
await execute(StandardToken, escrowIdToken);
});
it("Allowance < to required funds. Token is not SNT. SNT Allowance > required Fees", async () => {
const amount = toBN(fundAmount).sub(toBN(10)).toString(10);
await StandardToken.methods.approve(Escrow.options.address, amount).send({from: accounts[0]});
await SNT.methods.approve(Escrow.options.address, 1000).send({from: accounts[0]});
await execute(StandardToken, escrowIdToken);
});
it("Allowance == to required funds. Token is not SNT. SNT Allowance < required Fees", async () => {
await StandardToken.methods.approve(Escrow.options.address, fundAmount + feeAmount).send({from: accounts[0]});
await SNT.methods.approve(Escrow.options.address, 1).send({from: accounts[0]});
await execute(StandardToken, escrowIdToken);
});
it("Allowance > to required funds. Token is not SNT. SNT Allowance < required Fees", async () => {
const amount = toBN(feeAmount).add(toBN(fundAmount)).add(toBN(100)).toString(10);
await StandardToken.methods.approve(Escrow.options.address, amount).send({from: accounts[0]});
await SNT.methods.approve(Escrow.options.address, 1).send({from: accounts[0]});
await execute(StandardToken, escrowIdToken);
});
it("Allowance < to required funds. Token is not SNT. SNT Allowance < required Fees", async () => {
const amount = toBN(fundAmount).sub(toBN(10)).toString(10);
await StandardToken.methods.approve(Escrow.options.address, amount).send({from: accounts[0]});
await SNT.methods.approve(Escrow.options.address, 1).send({from: accounts[0]});
await execute(StandardToken, escrowIdToken);
});
const tokenApprovalAndBuildTrx = async (token, escrowId) => {
const tokenAllowance = await token.methods.allowance(accounts[0], Escrow.options.address).call();
const toSend = Escrow.methods.fund(escrowId);
// const encodedCall = toSend.encodeABI();
let approvalPromises = [];
let trxToSend;
const resetApproval = (token, tokenAllowance) => {
// Reset approval
// due to: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
if(toBN(tokenAllowance).gt(toBN(0))){
approvalPromises.push(token.methods.approve(Escrow.options.address, "0").send({from: accounts[0]}));
}
};
// Verifying token allowance for funding
if (toBN(tokenAllowance).lt(toBN(fundAmount))) {
resetApproval(token, tokenAllowance);
approvalPromises.push(token.methods.approve(Escrow.options.address, fundAmount + feeAmount).send({from: accounts[0]}));
}
trxToSend = toSend; // Enough funds. Execute directly.
return {approvalPromises, trxToSend};
};
});
});