Skip to content

Commit

Permalink
fixup! Add function challengeFraudulentDealByTrade (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
jijordre committed Apr 30, 2018
1 parent d6aaf97 commit ac5bd0c
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 36 deletions.
52 changes: 22 additions & 30 deletions contracts/Exchange.sol
Expand Up @@ -8,7 +8,7 @@
pragma solidity ^0.4.21;
pragma experimental ABIEncoderV2 ;

import ". / SafeMath . sol ";
import "./SafeMath.sol";

import "./Configuration.sol";

Expand Down Expand Up @@ -148,52 +148,49 @@ contract Exchange {
}
}

// TODO Remove
function getUint256Values(Trade trade) public view returns (uint256, uint256) {
uint256 feePartsPer = configuration.partsPer();
return (trade.singleFees.intended, trade.amount.mul(configuration.getTradeMakerFee(0, 0)).div(feePartsPer));
}

/// @notice Submit a trade candidate in continuous Fraudulent Deal Challenge (FDC)
/// @dev The seizure of client funds remains to be enabled once implemented in ClientFund contract
/// @param trade Fraudulent trade candidate
function challengeFraudulentDealByTrade(Trade trade) public {
uint256 feePartsPer = configuration.partsPer();
uint256 amountConjugate = trade.amount.div(trade.rate);

Trader memory maker;
Trader memory taker;
if (LiquidityRole.Maker == trade.buyer.liquidityRole) {
maker = trade.buyer;
taker = trade.seller;
} else {
maker = trade.seller;
taker = trade.buyer;
}

bool genuine = (getHashOfTrade(trade) == trade.seal.hash)
&& (trade.singleFees.intended <= trade.amount.mul(configuration.getTradeMakerFee(0, 0)).div(feePartsPer))
&& (trade.singleFees.intended == trade.amount.mul(configuration.getTradeMakerFee(0, maker.rollingVolume)).div(feePartsPer))
&& (trade.singleFees.intended >= trade.amount.mul(configuration.getTradeMakerMinimumFee(0)).div(feePartsPer))
&& (trade.singleFees.conjugate <= amountConjugate.mul(configuration.getTradeTakerFee(0, 0)).div(feePartsPer))
&& (trade.singleFees.conjugate == amountConjugate.mul(configuration.getTradeTakerFee(0, taker.rollingVolume)).div(feePartsPer))
// Gauge the genuineness of maker fee. Depending on whether maker is buyer or seller
// this result is baked into genuineness by buyer and seller below.
uint256 makerRollingVolume = (LiquidityRole.Maker == trade.buyer.liquidityRole ? trade.buyer.rollingVolume : trade.seller.rollingVolume);
bool genuineMakerFee = (trade.singleFees.intended <= trade.amount.mul(configuration.getTradeMakerFee(0, 0)).div(feePartsPer))
&& (trade.singleFees.intended == trade.amount.mul(configuration.getTradeMakerFee(0, makerRollingVolume)).div(feePartsPer))
&& (trade.singleFees.intended >= trade.amount.mul(configuration.getTradeMakerMinimumFee(0)).div(feePartsPer));

// Gauge the genuineness of taker fee. Depending on whether maker is buyer or seller
// this result is baked into genuineness by buyer and seller below.
uint256 takerRollingVolume = (LiquidityRole.Taker == trade.buyer.liquidityRole ? trade.buyer.rollingVolume : trade.seller.rollingVolume);
bool genuineTakerFee = (trade.singleFees.conjugate <= amountConjugate.mul(configuration.getTradeTakerFee(0, 0)).div(feePartsPer))
&& (trade.singleFees.conjugate == amountConjugate.mul(configuration.getTradeTakerFee(0, takerRollingVolume)).div(feePartsPer))
&& (trade.singleFees.conjugate >= amountConjugate.mul(configuration.getTradeTakerMinimumFee(0)).div(feePartsPer));

// Genuineness that does not related to buyer or seller
bool genuine = (getHashOfTrade(trade) == trade.seal.hash);

// Genuineness affected by buyer
bool genuineByBuyer = (trade.buyer._address != trade.seller._address)
&& (trade.buyer._address != owner)
&& (trade.buyer.balances.intended.current == trade.buyer.balances.intended.previous.add(trade.transfers.intended.single).sub(trade.singleFees.intended))
&& (trade.buyer.balances.conjugate.current == trade.buyer.balances.conjugate.previous.sub(trade.transfers.conjugate.single))
&& (trade.buyer.order.amount >= trade.buyer.order.residuals.current)
&& (trade.buyer.order.amount >= trade.buyer.order.residuals.previous)
&& (trade.buyer.order.residuals.previous >= trade.buyer.order.residuals.current);
&& (trade.buyer.order.residuals.previous >= trade.buyer.order.residuals.current)
&& (LiquidityRole.Maker == trade.buyer.liquidityRole ? genuineMakerFee : genuineTakerFee);

// Genuineness affected by seller
bool genuineBySeller = (trade.buyer._address != trade.seller._address)
&& (trade.seller._address != owner)
&& (trade.seller.balances.intended.current == trade.seller.balances.intended.previous.sub(trade.transfers.intended.single))
&& (trade.seller.balances.conjugate.current == trade.seller.balances.conjugate.previous.add(trade.transfers.conjugate.single).sub(trade.singleFees.conjugate))
&& (trade.seller.order.amount >= trade.seller.order.residuals.current)
&& (trade.seller.order.amount >= trade.seller.order.residuals.previous)
&& (trade.seller.order.residuals.previous >= trade.seller.order.residuals.current);
&& (trade.seller.order.residuals.previous >= trade.seller.order.residuals.current)
&& (LiquidityRole.Maker == trade.seller.liquidityRole ? genuineMakerFee : genuineTakerFee);

require(!genuine || !genuineByBuyer || !genuineBySeller);

Expand All @@ -220,11 +217,6 @@ contract Exchange {
// TODO Implement fully
function getHashOfTrade(Trade trade) private pure returns (bytes32) {
return keccak256(bytes32(trade.nonce));

// bytes memory prefix = "\x19Ethereum Signed Message:\n32";
// bytes32 hash = keccak256(trade.nonce/*trade.buyOrderHash, trade.sellOrderHash, trade.buyerOrderNonce, trade.sellerOrderNonce, trade.buyer, trade.seller, trade.tokenAmount, trade.etherAmount, trade.token, trade.immediateSettlement*/);
// hash = keccak256(prefix, hash);
// return hash;
}

function changeConfiguration(Configuration _configuration) public onlyOwner {
Expand Down
127 changes: 121 additions & 6 deletions test/scenarios/Exchange.js
Expand Up @@ -122,11 +122,6 @@ module.exports = (glob) => {
overrideOptions = {gasLimit: 1e6};

await ethersConfiguration.setTradeMakerFee(utils.bigNumberify(0), utils.parseUnits('0.001', 18), [], [], overrideOptions);
// await ethersConfiguration.setTradeMakerFee(utils.bigNumberify(0), utils.parseUnits('0.001', 18), [1], [utils.parseUnits('0.1', 18)], overrideOptions);
// const nominal = await ethersConfiguration.getTradeMakerFee(0, 0);
// console.log(`nominal: ${nominal.toNumber()}`);
// const discounted = await ethersConfiguration.getTradeMakerFee(0, 1);
// console.log(`discounted: ${discounted.toNumber()}`);
await ethersConfiguration.setTradeMakerMinimumFee(utils.bigNumberify(0), utils.parseUnits('0.0001', 18), overrideOptions);
await ethersConfiguration.setTradeTakerFee(utils.bigNumberify(0), utils.parseUnits('0.002', 18), [1], [utils.parseUnits('0.1', 18)], overrideOptions);
await ethersConfiguration.setTradeTakerMinimumFee(utils.bigNumberify(0), utils.parseUnits('0.0002', 18), overrideOptions);
Expand Down Expand Up @@ -223,7 +218,7 @@ module.exports = (glob) => {
};
});

it.only('should revert if trade is genuine', async () => {
it('should revert if trade is genuine', async () => {
return ethersExchange.challengeFraudulentDealByTrade(trade, overrideOptions).should.be.rejected;
});

Expand Down Expand Up @@ -381,6 +376,66 @@ module.exports = (glob) => {
});
});

describe('if (buyer\'s) maker fee is greater than the nominal maker fee', () => {
let seizedWalletCount;

beforeEach(async () => {
trade.singleFees.intended = trade.singleFees.intended.mul(utils.bigNumberify(10));
const count = await ethersExchange.seizedWalletsCount();
seizedWalletCount = count.toNumber() + 1;
});

it('should record fraudulent trade, toggle operational mode and emit event', async () => {
ethersExchange.onchallengefraudulentdealbytradeevent = async () => {
(await ethersExchange.fraudulentTrade())[0].toNumber().should.equal(trade.nonce.toNumber());
(await ethersExchange.operationalMode()).should.equal(1);
};
await ethersExchange.challengeFraudulentDealByTrade(trade, overrideOptions);
const count = await ethersExchange.seizedWalletsCount();
count.toNumber().should.equal(seizedWalletCount);
});
});

describe('if (buyer\'s) maker fee is different than provided by Configuration contract', () => {
let seizedWalletCount;

beforeEach(async () => {
trade.singleFees.intended = trade.singleFees.intended.mul(utils.bigNumberify(90)).div(utils.bigNumberify(100));
const count = await ethersExchange.seizedWalletsCount();
seizedWalletCount = count.toNumber() + 1;
});

it('should record fraudulent trade, toggle operational mode and emit event', async () => {
ethersExchange.onchallengefraudulentdealbytradeevent = async () => {
(await ethersExchange.fraudulentTrade())[0].toNumber().should.equal(trade.nonce.toNumber());
(await ethersExchange.operationalMode()).should.equal(1);
};
await ethersExchange.challengeFraudulentDealByTrade(trade, overrideOptions);
const count = await ethersExchange.seizedWalletsCount();
count.toNumber().should.equal(seizedWalletCount);
});
});

describe('if (buyer\'s) maker fee is smaller than the minimum maker fee', () => {
let seizedWalletCount;

beforeEach(async () => {
trade.singleFees.intended = trade.singleFees.intended.div(utils.bigNumberify(100));
const count = await ethersExchange.seizedWalletsCount();
seizedWalletCount = count.toNumber() + 1;
});

it('should record fraudulent trade, toggle operational mode and emit event', async () => {
ethersExchange.onchallengefraudulentdealbytradeevent = async () => {
(await ethersExchange.fraudulentTrade())[0].toNumber().should.equal(trade.nonce.toNumber());
(await ethersExchange.operationalMode()).should.equal(1);
};
await ethersExchange.challengeFraudulentDealByTrade(trade, overrideOptions);
const count = await ethersExchange.seizedWalletsCount();
count.toNumber().should.equal(seizedWalletCount);
});
});

describe('if seller address equals owner address', () => {
let seizedWalletCount;

Expand Down Expand Up @@ -500,6 +555,66 @@ module.exports = (glob) => {
count.toNumber().should.equal(seizedWalletCount);
});
});

describe('if (seller\'s) taker fee is greater than the nominal taker fee', () => {
let seizedWalletCount;

beforeEach(async () => {
trade.singleFees.conjugate = trade.singleFees.conjugate.mul(utils.bigNumberify(10));
const count = await ethersExchange.seizedWalletsCount();
seizedWalletCount = count.toNumber() + 1;
});

it('should record fraudulent trade, toggle operational mode and emit event', async () => {
ethersExchange.onchallengefraudulentdealbytradeevent = async () => {
(await ethersExchange.fraudulentTrade())[0].toNumber().should.equal(trade.nonce.toNumber());
(await ethersExchange.operationalMode()).should.equal(1);
};
await ethersExchange.challengeFraudulentDealByTrade(trade, overrideOptions);
const count = await ethersExchange.seizedWalletsCount();
count.toNumber().should.equal(seizedWalletCount);
});
});

describe('if (seller\'s) taker fee is different than provided by Configuration contract', () => {
let seizedWalletCount;

beforeEach(async () => {
trade.singleFees.conjugate = trade.singleFees.conjugate.mul(utils.bigNumberify(90)).div(utils.bigNumberify(100));
const count = await ethersExchange.seizedWalletsCount();
seizedWalletCount = count.toNumber() + 1;
});

it('should record fraudulent trade, toggle operational mode and emit event', async () => {
ethersExchange.onchallengefraudulentdealbytradeevent = async () => {
(await ethersExchange.fraudulentTrade())[0].toNumber().should.equal(trade.nonce.toNumber());
(await ethersExchange.operationalMode()).should.equal(1);
};
await ethersExchange.challengeFraudulentDealByTrade(trade, overrideOptions);
const count = await ethersExchange.seizedWalletsCount();
count.toNumber().should.equal(seizedWalletCount);
});
});

describe('if (seller\'s) taker fee is smaller than the minimum taker fee', () => {
let seizedWalletCount;

beforeEach(async () => {
trade.singleFees.conjugate = trade.singleFees.conjugate.div(utils.bigNumberify(100));
const count = await ethersExchange.seizedWalletsCount();
seizedWalletCount = count.toNumber() + 1;
});

it('should record fraudulent trade, toggle operational mode and emit event', async () => {
ethersExchange.onchallengefraudulentdealbytradeevent = async () => {
(await ethersExchange.fraudulentTrade())[0].toNumber().should.equal(trade.nonce.toNumber());
(await ethersExchange.operationalMode()).should.equal(1);
};
await ethersExchange.challengeFraudulentDealByTrade(trade, overrideOptions);
const count = await ethersExchange.seizedWalletsCount();
count.toNumber().should.equal(seizedWalletCount);
});
});
});
});
};
Expand Down

0 comments on commit ac5bd0c

Please sign in to comment.