diff --git a/contracts/marketplace/BatchDagora.sol b/contracts/marketplace/BatchDagora.sol index 3af7ee1..393e70d 100644 --- a/contracts/marketplace/BatchDagora.sol +++ b/contracts/marketplace/BatchDagora.sol @@ -23,6 +23,12 @@ abstract contract BatchDagora is Dagora { } } + function batchCancelTransaction(Order[] memory _orders) public { + for (uint256 i = 0; i < _orders.length; i++) { + cancelTransaction(_orders[i]); + } + } + function batchExecuteTransaction(Order[] memory _orders) public { for (uint256 i = 0; i < _orders.length; i++) { executeTransaction(_orders[i]); diff --git a/contracts/marketplace/Dagora.sol b/contracts/marketplace/Dagora.sol index 2638946..28c74f7 100644 --- a/contracts/marketplace/Dagora.sol +++ b/contracts/marketplace/Dagora.sol @@ -56,7 +56,7 @@ abstract contract Dagora is Ownable { uint256 commission; uint256 protocolFee; uint256 confirmationTimeout; /* In days */ - uint256 timestamp; /* A buyer may want to buy the same product twice */ + uint256 nonce; /* A buyer may want to buy the same product twice */ } struct Transaction { @@ -75,6 +75,8 @@ abstract contract Dagora is Ownable { uint256 available; /* Expiration for non-answered transactions */ uint256 expiration; + /* Expiration for non-answered transactions */ + uint256 orders; } struct Staker { @@ -150,14 +152,13 @@ abstract contract Dagora is Ownable { mapping(bytes32 => bool) public cancelledOrFinalized; /* Listings running in the contract */ - mapping(bytes32 => ListingInfo) public listingProducts; + mapping(bytes32 => ListingInfo) public listingInfos; /* Order approve */ mapping(bytes32 => bool) public orderApprove; /* Transactions running in the contract */ mapping(bytes32 => Transaction) public transactions; // Order Hash to Transaction /* Disputes running in the contract*/ mapping(bytes32 => RunningDispute) public disputes; // Listing/Order Hash to Dispute - mapping(uint256 => bytes32) public disputeIDtoHash; /* Tokens allowed */ mapping(address => bool) public contracts; @@ -177,6 +178,7 @@ abstract contract Dagora is Ownable { uint256 public SELLER_CONFIRMATION_TIMEOUT; uint256 public MINIMUM_STAKED_TOKEN; + uint256 public PERCENTAGE_BURN; uint256 public GRACE_PERIOD; constructor(address _token, address _protocolFeeRecipient) @@ -191,14 +193,14 @@ abstract contract Dagora is Ownable { function updateProtocolFeePercentage(uint256 _percent) public onlyOwner { PROTOCOL_FEE_PERCENTAGE = _percent; - emit UptadeProtocolFeePercentage(BLACKLIST_TIMEOUT); + emit UptadeProtocolFeePercentage(PROTOCOL_FEE_PERCENTAGE); } function updateSellerConfirmationTimeout( uint256 _sellerConfirmationTimeoutDays ) public onlyOwner { - BLACKLIST_TIMEOUT = _sellerConfirmationTimeoutDays * (1 days); - emit UptadeSellerConfirmationTimeout(BLACKLIST_TIMEOUT); + SELLER_CONFIRMATION_TIMEOUT = _sellerConfirmationTimeoutDays * (1 days); + emit UptadeSellerConfirmationTimeout(SELLER_CONFIRMATION_TIMEOUT); } function updateBlacklistTimeout(uint256 _blacklistTimeoutDays) @@ -261,26 +263,33 @@ abstract contract Dagora is Ownable { /* Assert sender is authorized to approve listing. */ require(_msgSender() == _listing.seller, "You must be the seller"); + /* Calculate listing hash. */ + bytes32 hash = _requireValidListing(_listing); + + if ( + listingInfos[hash].expiration < now && listingInfos[hash].orders > 0 + ) { + // BURN TOKENS + _burnStake(_msgSender()); + } require( stakers[_msgSender()].balance >= MINIMUM_STAKED_TOKEN, "You don't have enoght funds" ); - /* Calculate listing hash. */ - bytes32 hash = _requireValidListing(_listing); /* Assert listing has not already been approved. */ /* EFFECTS */ uint256 stakerCount = SafeMath.add( SafeMath.sub( stakers[_msgSender()].productCount, - listingProducts[hash].available + listingInfos[hash].available ), _quantity ); - listingProducts[hash].available = _quantity; - listingProducts[hash].expiration = _listing.expiration; + listingInfos[hash].available = _quantity; + listingInfos[hash].expiration = _listing.expiration; stakers[_msgSender()].productCount = stakerCount; @@ -301,17 +310,23 @@ abstract contract Dagora is Ownable { /* Assert sender is authorized to cancel listing. */ require(_msgSender() == _listing.seller, "You must be the seller"); - /* EFFECTS */ + if ( + listingInfos[hash].expiration < now && listingInfos[hash].orders > 0 + ) { + // BURN TOKENS + _burnStake(_msgSender()); + } + /* Mark listing as cancelled, preventing it from being matched. */ cancelledOrFinalized[hash] = true; stakers[_msgSender()].productCount = SafeMath.sub( stakers[_msgSender()].productCount, - listingProducts[hash].available + listingInfos[hash].available ); - delete listingProducts[hash]; + delete listingInfos[hash]; /* Log cancel event. */ emit ListingCancelled(hash); @@ -329,14 +344,11 @@ abstract contract Dagora is Ownable { ); // transaction.lastStatusUpdate = now; // transaction.status = Status.WaitingSeller; - // bytes32 listingHash = _hashListing(_order.listing); - // listingProducts[listingHash] = SafeMath.sub( - // listingProducts[listingHash], - // _order.quantity - // ); orderApprove[hash] = true; - if (listingProducts[hash].expiration > _order.confirmationTimeout) { - listingProducts[hash].expiration = _order.confirmationTimeout; + bytes32 listingHash = _hashListing(_order.listing); + listingInfos[listingHash].orders++; + if (listingInfos[listingHash].expiration > _order.confirmationTimeout) { + listingInfos[listingHash].expiration = _order.confirmationTimeout; } emit TransactionCreated( hash, @@ -367,11 +379,8 @@ abstract contract Dagora is Ownable { delete transaction.lastStatusUpdate; delete transaction.status; bytes32 listingHash = _hashListing(_order.listing); - listingProducts[listingHash].available = SafeMath.add( - listingProducts[listingHash].available, - _order.quantity - ); - listingProducts[hash].expiration = _order.listing.expiration; + listingInfos[listingHash].orders--; + listingInfos[listingHash].expiration = _order.listing.expiration; emit TransactionCancelled(hash); } @@ -403,17 +412,20 @@ abstract contract Dagora is Ownable { /* After */ bytes32 listingHash = _hashListing(_order.listing); - listingProducts[listingHash].available = SafeMath.sub( - listingProducts[listingHash].available, + listingInfos[listingHash].available = SafeMath.sub( + listingInfos[listingHash].available, _order.quantity ); - listingProducts[hash].expiration = _order.listing.expiration; - + listingInfos[hash].expiration = _order.listing.expiration; + listingInfos[listingHash].orders--; transaction.lastStatusUpdate = now; transaction.status = Status.WaitingConfirmation; emit TransactionAccepted(hash); } + /** + We need to find incentives for a seller to confirmReceipt + */ function confirmReceipt(Order memory _order) public { bytes32 hash = _hashOrder(_order); Transaction storage transaction = transactions[hash]; @@ -428,8 +440,7 @@ abstract contract Dagora is Ownable { transaction.status = Status.Warranty; transaction.lastStatusUpdate = now; } else { - // We are giving a refund, the buyer doesn't need cashback - _finalizeTransaction(_order, transaction.refund != 0); + _finalizeTransaction(_order, false); } } @@ -475,34 +486,46 @@ abstract contract Dagora is Ownable { bytes32 hash = _hashOrder(_order); Transaction storage transaction = transactions[hash]; uint256 refund = transaction.refund; - uint256 price = SafeMath.sub(_order.total, refund); + uint256 price = _order.total; transaction.status = Status.Finalized; delete transaction.lastStatusUpdate; delete transaction.refund; + if (refund == price) { + // Warranty refund, we don't want to pay for any comissions + require(_order.token.transfer(_order.buyer, refund)); + } else { + if (_order.protocolFee > 0) { + price -= _order.protocolFee; + require( + _order.token.transfer( + protocolFeeRecipient, + _order.protocolFee + ) + ); + } - if (_order.protocolFee > 0) { - price -= _order.protocolFee; - require( - _order.token.transfer(protocolFeeRecipient, _order.protocolFee) - ); - } - - if (_order.commission > 0) { - price -= _order.commission; - require( - _order.token.transfer(_order.commissioner, _order.commission) - ); - } + if (_order.commission > 0) { + price -= _order.commission; + require( + _order.token.transfer( + _order.commissioner, + _order.commission + ) + ); + } - if (!_executed && _order.cashback > 0) { - price -= _order.cashback; - require( - _order.token.transfer(_order.buyer, (_order.cashback + refund)) - ); - } + if (!_executed) { + // We are giving a refund, the buyer doesn't need cashback + uint256 totalRefund = refund > 0 ? refund : _order.cashback; + if (totalRefund > 0) { + price -= totalRefund; + require(_order.token.transfer(_order.buyer, totalRefund)); + } + } - if (price > 0) { - require(_order.token.transfer(_order.listing.seller, price)); + if (price > 0) { + require(_order.token.transfer(_order.listing.seller, price)); + } } emit TransactionFinalized(hash); } @@ -542,10 +565,13 @@ abstract contract Dagora is Ownable { (_order.confirmationTimeout * (1 days)), "Confirmation time has timed out." ); + require( - _refund + - (_order.cashback + _order.protocolFee + _order.commission) <= - _order.total, + _refund > _order.cashback, + "Refund must be greater than cashback." + ); + require( + _refund + (_order.protocolFee + _order.commission) <= _order.total, "Refund can't be greater than total allowed." ); transaction.refund = _refund; @@ -645,8 +671,8 @@ abstract contract Dagora is Ownable { _msgSender() == _order.listing.seller, "Only seller can dispute." ); - prosecution = _order.buyer; - defendant = _order.listing.seller; + prosecution = _order.listing.seller; + defendant = _order.buyer; } transaction.status = Status.InDispute; transaction.lastStatusUpdate = now; @@ -677,7 +703,7 @@ abstract contract Dagora is Ownable { dispute.prosecutionFee += _prosecutionFee; dispute.disputeType = _disputeType; /* We know the token is market token, save gas*/ - if (_disputeType == DisputeType.Report) dispute.token = _token; + if (_disputeType != DisputeType.Report) dispute.token = _token; dispute.status = DisputeStatus.WaitingDefendant; dispute.lastInteraction = now; dispute.metaEvidenceId = metaEvidenceCount++; @@ -695,17 +721,17 @@ abstract contract Dagora is Ownable { now - dispute.lastInteraction >= DISPUTE_TIMEOUT, "Timeout time has not passed yet." ); - bool success; - if (dispute.prosecutionFee != 0) { - uint256 prosecutionFee = dispute.prosecutionFee; - dispute.prosecutionFee = 0; - (success, ) = dispute.prosecution.call{ value: prosecutionFee }(""); - } - if (dispute.defendantFee != 0) { - uint256 defendantFee = dispute.defendantFee; - dispute.defendantFee = 0; - (success, ) = dispute.defendant.call{ value: defendantFee }(""); - } + // bool success; + // if (dispute.prosecutionFee != 0) { + // uint256 prosecutionFee = dispute.prosecutionFee; + // dispute.prosecutionFee = 0; + // (success, ) = dispute.prosecution.call{ value: prosecutionFee }(""); + // } + // if (dispute.defendantFee != 0) { + // uint256 defendantFee = dispute.defendantFee; + // dispute.defendantFee = 0; + // (success, ) = dispute.defendant.call{ value: defendantFee }(""); + // } if (dispute.status == DisputeStatus.WaitingDefendant) { _executeRuling(_hash, uint256(RulingOptions.ProsecutionWins)); } else { @@ -862,7 +888,7 @@ abstract contract Dagora is Ownable { bool success = false; if (_ruling == uint256(RulingOptions.ProsecutionWins)) { /* Defendant is always seller */ - stakers[dispute.defendant].productCount -= listingProducts[_hash] + stakers[dispute.defendant].productCount -= listingInfos[_hash] .available; /* Cancelling Listing */ cancelledOrFinalized[_hash] = true; @@ -894,6 +920,13 @@ abstract contract Dagora is Ownable { } } + function _burnStake(address stakeHolder) internal { + uint256 total = stakers[_msgSender()].balance; + uint256 burn = _calculateTotalFromPercentage(total, PERCENTAGE_BURN); + marketToken.burn(burn); + stakers[_msgSender()].balance = total - burn; + } + function _validateListing(bytes32 _hash, Listing memory _listing) internal view @@ -951,7 +984,7 @@ abstract contract Dagora is Ownable { if ( _order.quantity <= 0 || _order.quantity > - listingProducts[_hashListing(_order.listing)].available + listingInfos[_hashListing(_order.listing)].available ) { return false; } @@ -1038,7 +1071,7 @@ abstract contract Dagora is Ownable { _order.commission, _order.protocolFee, _order.confirmationTimeout, - _order.timestamp + _order.nonce ) ); return hash; diff --git a/contracts/marketplace/KlerosDagora.sol b/contracts/marketplace/KlerosDagora.sol index bfe7c93..a6e7869 100644 --- a/contracts/marketplace/KlerosDagora.sol +++ b/contracts/marketplace/KlerosDagora.sol @@ -11,6 +11,7 @@ contract KlerosDagora is BatchDagora, IArbitrable { bytes public reportExtraData; // Extra data to set up the arbitration. bytes public orderExtraData; // Extra data to set up the arbitration. string public ipfsDomain; + mapping(uint256 => bytes32) public disputeIDtoHash; constructor( address _arbitrator, @@ -28,8 +29,8 @@ contract KlerosDagora is BatchDagora, IArbitrable { function report(Listing memory _listing) public - override payable + override returns (bytes32 hash) { hash = Dagora.report(_listing); @@ -41,8 +42,8 @@ contract KlerosDagora is BatchDagora, IArbitrable { function disputeTransaction(Order memory _order) public - override payable + override returns (bytes32 hash) { hash = Dagora.disputeTransaction(_order); @@ -58,12 +59,14 @@ contract KlerosDagora is BatchDagora, IArbitrable { { RunningDispute storage dispute = disputes[_hash]; dispute.status = DisputeStatus.DisputeCreated; - uint256 disputeId = arbitrator.createDispute{ value: _arbitrationCost }( - AMOUNT_OF_CHOICES, - dispute.disputeType == DisputeType.Order - ? orderExtraData - : reportExtraData - ); + uint256 disputeId = + arbitrator.createDispute{ value: _arbitrationCost }( + AMOUNT_OF_CHOICES, + dispute.disputeType == DisputeType.Order + ? orderExtraData + : reportExtraData + ); + dispute.disputeId = disputeId; disputeIDtoHash[disputeId] = _hash; emit Dispute( arbitrator, @@ -74,8 +77,8 @@ contract KlerosDagora is BatchDagora, IArbitrable { // Refund sender if it overpaid. bool success; if (dispute.prosecutionFee > _arbitrationCost) { - uint256 extraFeeProsecution = dispute.prosecutionFee - - _arbitrationCost; + uint256 extraFeeProsecution = + dispute.prosecutionFee - _arbitrationCost; dispute.prosecutionFee = _arbitrationCost; (success, ) = dispute.prosecution.call{ value: extraFeeProsecution @@ -134,7 +137,7 @@ contract KlerosDagora is BatchDagora, IArbitrable { _executeRuling(hash, _ruling); } - function appeal(bytes32 _hash) public override payable { + function appeal(bytes32 _hash) public payable override { RunningDispute storage dispute = disputes[_hash]; require( dispute.disputeType == DisputeType.Order, @@ -149,8 +152,8 @@ contract KlerosDagora is BatchDagora, IArbitrable { function arbitrationCost(DisputeType _type) public - override view + override returns (uint256 fee) { require(_type > DisputeType.None); diff --git a/package.json b/package.json index dbb5f79..af94879 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ }, "dependencies": { "@opengsn/gsn": "^0.10.0", + "csv-writer": "^1.6.0", "openzeppelin-solidity": "^3.1.0", "solc": "^0.6.0" }, diff --git a/test/Dagora.js b/test/Dagora.js index 89ff202..9550310 100644 --- a/test/Dagora.js +++ b/test/Dagora.js @@ -1,4 +1,7 @@ const DagoraMarket = artifacts.require("marketplace/TestDagora.sol"); +const CentralizedArbitrator = artifacts.require( + "arbitrator/CentralizedArbitrator.sol" +); const DagoraPaymaster = artifacts.require("gsn/DagoraPaymaster.sol"); const DagoraToken = artifacts.require("token/DagoraToken.sol"); const signHelper = require("./helpers/signatureHelper"); @@ -18,6 +21,8 @@ const Web3 = require("web3"); const ethers = require("ethers"); const { generateListing, generateOrder } = require("./helpers/populator"); const { waitForTransaction } = require("./helpers/gsnHelper"); +const { createCsvFile } = require("./helpers/csvExporter"); +const { updateGasCost, advanceTime } = require("./helpers/truffleTestHelper"); const { hashListing, hashOrder } = require("./helpers/signatureHelper"); const AcceptEverythingPaymaster = artifacts.require( @@ -115,152 +120,1091 @@ contract("Dagora", async (accounts) => { console.log(`Buyer gas used: ${buyerGasUsed}`); }); - it("should create batched orders", async () => { - let seller = accounts[0]; - let buyer = accounts[1]; + // it("should create batched orders", async () => { + // let seller = accounts[0]; + // let buyer = accounts[1]; - let sellerGasUsed = 0; - let buyerGasUsed = 0; + // let sellerGasUsed = 0; + // let buyerGasUsed = 0; - const approveSeller = await token.approve(dagora.address, -1, { - from: seller, - }); - sellerGasUsed += approveSeller.receipt.gasUsed; + // const approveSeller = await token.approve(dagora.address, -1, { + // from: seller, + // }); + // sellerGasUsed += approveSeller.receipt.gasUsed; - const approveBuyer = await token.approve(dagora.address, -1, { - from: buyer, - }); - buyerGasUsed += approveBuyer.receipt.gasUsed; + // const approveBuyer = await token.approve(dagora.address, -1, { + // from: buyer, + // }); + // buyerGasUsed += approveBuyer.receipt.gasUsed; - const deposit = await dagora.stakeTokens(10, { from: seller }); - sellerGasUsed += deposit.receipt.gasUsed; + // const deposit = await dagora.stakeTokens(10, { from: seller }); + // sellerGasUsed += deposit.receipt.gasUsed; - let orders = []; - let timestamp = 1; - const total = 50; + // let orders = []; + // let timestamp = 1; + // const total = 50; - let listing = generateListing(seller); - const updateListing = await dagora.updateListing(listing, total * 5); - sellerGasUsed += updateListing.receipt.gasUsed; + // let listing = generateListing(seller); + // const updateListing = await dagora.updateListing(listing, total * 5); + // sellerGasUsed += updateListing.receipt.gasUsed; - for (let i = 0; i < total; i++) { - let order = generateOrder( - listing, - buyer, - token.address, - protocol_percentage, - timestamp++ - ); - orders.push(order); - } + // for (let i = 0; i < total; i++) { + // let order = generateOrder( + // listing, + // buyer, + // token.address, + // protocol_percentage, + // timestamp++ + // ); + // orders.push(order); + // } - let orderBatchHash = await dagora.batchCreateTransaction(orders, { - from: buyer, - }); - buyerGasUsed += orderBatchHash.receipt.gasUsed; - console.log( - `batchCreateTransaction() gas used per order: ${ - orderBatchHash.receipt.gasUsed / total - }` - ); + // let orderBatchHash = await dagora.batchCreateTransaction(orders, { + // from: buyer, + // }); + // buyerGasUsed += orderBatchHash.receipt.gasUsed; + // console.log( + // `batchCreateTransaction() gas used per order: ${ + // orderBatchHash.receipt.gasUsed / total + // }` + // ); - const accept = await dagora.batchAcceptTransaction(orders, { - from: seller, - }); - sellerGasUsed += accept.receipt.gasUsed; - console.log( - `batchAcceptTransaction() gas used per order: ${ - accept.receipt.gasUsed / total - }` - ); + // const accept = await dagora.batchAcceptTransaction(orders, { + // from: seller, + // }); + // sellerGasUsed += accept.receipt.gasUsed; + // console.log( + // `batchAcceptTransaction() gas used per order: ${ + // accept.receipt.gasUsed / total + // }` + // ); - const execute = await dagora.batchExecuteTransaction(orders, { - from: seller, - }); - sellerGasUsed += execute.receipt.gasUsed; - console.log( - `batchExecuteTransaction() gas used per order: ${ - execute.receipt.gasUsed / total - }` - ); + // const execute = await dagora.batchExecuteTransaction(orders, { + // from: seller, + // }); + // sellerGasUsed += execute.receipt.gasUsed; + // console.log( + // `batchExecuteTransaction() gas used per order: ${ + // execute.receipt.gasUsed / total + // }` + // ); - console.log(`Seller gas used: ${sellerGasUsed}`); - console.log(`Buyer gas used: ${buyerGasUsed}`); - }); + // console.log(`Seller gas used: ${sellerGasUsed}`); + // console.log(`Buyer gas used: ${buyerGasUsed}`); + // }); }); context("Gas cost evaluation", function () { let token; let dagora; let protocol_percentage; - let REPETITIONS = 10; + let dispute_timeout; + let GAS_EVALUATION = { + stakeTokens: [], + unstakeTokens: [], + updateListing: [], + cancelListing: [], + createTransaction: [], + cancelTransaction: [], + acceptTransaction: [], + executeTransaction: [], + confirmReceipt: [], + claimWarranty: [], + updateRefund: [], + disputeTransaction: [], + report: [], + disputeTimeout: [], + claimWarranty: [], + appeal: [], + batchCreateTransaction: [], + batchAcceptTransaction: [], + batchCancelTransaction: [], + batchExecuteTransaction: [], + }; + let REPETITIONS = 15; before(async () => { token = await DagoraToken.deployed(); dagora = await DagoraMarket.deployed(); + arbitrator = await CentralizedArbitrator.deployed(); await token.mint(accounts[0], 100000, { from: accounts[0] }); await token.mint(accounts[1], 100000, { from: accounts[0] }); if (!dagora.contracts.call(token.address)) await dagora.grantAuthentication(token.address); await dagora.updateMinimumStakeToken(10); - protocol_percentage = 100; + protocol_percentage = 100; // 1% await dagora.updateProtocolFeePercentage(protocol_percentage); + dispute_timeout = 7; // 7 days + await dagora.updateDisputeTimeout(dispute_timeout); }); - it("listing update", async () => { - let gasUsed = 0; - await token.approve(dagora.address, 10, { from: accounts[0] }); - await dagora.stakeTokens(10, { from: accounts[0] }); + it("contract deploy", async () => { + GAS_EVALUATION = { + contract_deploy: [], + }; for (let i = 0; i < REPETITIONS; i++) { - let listing = generateListing(accounts[0]); - let updateListing = await dagora.updateListing(listing, 1); - let valid = await dagora.requireValidListing(listing); - assert.equal(valid.valueOf(), true); - console.log( - `updateListing() gas used: ${updateListing.receipt.gasUsed}` - ); - gasUsed += updateListing.receipt.gasUsed; + let someInstance = await DagoraMarket.new( + arbitrator.address, + arbitrator.address, + token.address, + arbitrator.address, + web3.utils.toHex("a"), + web3.utils.toHex("a"), + "http://ipfs.infura.io/ipfs/" + ); + let receipt = await web3.eth.getTransactionReceipt( + someInstance.transactionHash + ); + GAS_EVALUATION.contract_deploy.push(receipt.gasUsed); } - console.log(`updateListing() AVERAGE GAS USED: ${gasUsed / REPETITIONS}`); + createCsvFile("contract_deploy", GAS_EVALUATION, REPETITIONS); }); - it("create transaction", async () => { + it("successful flow with expiration", async () => { + GAS_EVALUATION = { + stakeTokens: [], + updateListing: [], + createTransaction: [], + acceptTransaction: [], + executeTransaction: [], + }; + let seller = accounts[0]; let buyer = accounts[1]; - let gasUsed = 0; - await token.approve(dagora.address, 10, { from: seller }); - await dagora.stakeTokens(10, { from: seller }); + await token.approve(dagora.address, -1, { from: seller }); + await token.approve(dagora.address, -1, { from: buyer }); + for (let i = 0; i < REPETITIONS; i++) { + let stakeTokens = await dagora.stakeTokens(10, { from: seller }); - await token.approve(dagora.address, -1, { - from: buyer, - }); + updateGasCost(GAS_EVALUATION, "stakeTokens", stakeTokens); + let listing = generateListing(seller, false, false); - let listing = generateListing(seller); - await dagora.updateListing(listing, REPETITIONS * 5); - let valid = await dagora.requireValidListing(listing); - assert.equal(valid.valueOf(), true); + let updateListing = await dagora.updateListing( + listing, + Math.floor(Math.random() * 100) + 5 + ); + updateGasCost(GAS_EVALUATION, "updateListing", updateListing); + + let order = generateOrder( + listing, + buyer, + token.address, + protocol_percentage, + new Date().getTime(), + true + ); + let createTransaction = await dagora.createTransaction(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "createTransaction", createTransaction); + + let acceptTransaction = await dagora.acceptTransaction(order, { + from: seller, + }); + updateGasCost(GAS_EVALUATION, "acceptTransaction", acceptTransaction); + + advanceTime(order.confirmationTimeout * 24 * 60 * 60); // seconds + + let executeTransaction = await dagora.executeTransaction(order, { + from: seller, + }); + updateGasCost(GAS_EVALUATION, "executeTransaction", executeTransaction); + } + createCsvFile("success_expiration", GAS_EVALUATION, REPETITIONS); + }); + + it("successful flow without warranty and buyer confirmation", async () => { + GAS_EVALUATION = { + stakeTokens: [], + updateListing: [], + createTransaction: [], + acceptTransaction: [], + confirmReceipt: [], + }; + + let seller = accounts[0]; + let buyer = accounts[1]; + + await token.approve(dagora.address, -1, { from: seller }); + await token.approve(dagora.address, -1, { from: buyer }); + for (let i = 0; i < REPETITIONS; i++) { + let stakeTokens = await dagora.stakeTokens(10, { from: seller }); + + updateGasCost(GAS_EVALUATION, "stakeTokens", stakeTokens); + let listing = generateListing(seller, false, false); + + let updateListing = await dagora.updateListing( + listing, + Math.floor(Math.random() * 100) + 5 + ); + updateGasCost(GAS_EVALUATION, "updateListing", updateListing); + + let order = generateOrder( + listing, + buyer, + token.address, + protocol_percentage, + new Date().getTime(), + true + ); + let createTransaction = await dagora.createTransaction(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "createTransaction", createTransaction); + + let acceptTransaction = await dagora.acceptTransaction(order, { + from: seller, + }); + updateGasCost(GAS_EVALUATION, "acceptTransaction", acceptTransaction); + + let confirmReceipt = await dagora.confirmReceipt(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "confirmReceipt", confirmReceipt); + } + createCsvFile( + "success_no_warranty_buyer_confirm", + GAS_EVALUATION, + REPETITIONS + ); + }); + + it("successful flow with warranty and buyer confirmation", async () => { + GAS_EVALUATION = { + stakeTokens: [], + updateListing: [], + createTransaction: [], + acceptTransaction: [], + confirmReceipt: [], + executeTransaction: [], + }; + + let seller = accounts[0]; + let buyer = accounts[1]; + + await token.approve(dagora.address, -1, { from: seller }); + await token.approve(dagora.address, -1, { from: buyer }); + for (let i = 0; i < REPETITIONS; i++) { + let stakeTokens = await dagora.stakeTokens(10, { from: seller }); + + updateGasCost(GAS_EVALUATION, "stakeTokens", stakeTokens); + let listing = generateListing(seller, true, false); + + let updateListing = await dagora.updateListing( + listing, + Math.floor(Math.random() * 100) + 5 + ); + updateGasCost(GAS_EVALUATION, "updateListing", updateListing); + + let order = generateOrder( + listing, + buyer, + token.address, + protocol_percentage, + new Date().getTime(), + true + ); + let createTransaction = await dagora.createTransaction(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "createTransaction", createTransaction); + + let acceptTransaction = await dagora.acceptTransaction(order, { + from: seller, + }); + updateGasCost(GAS_EVALUATION, "acceptTransaction", acceptTransaction); + + let confirmReceipt = await dagora.confirmReceipt(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "confirmReceipt", confirmReceipt); + + advanceTime(order.listing.warranty * 24 * 60 * 60); // seconds + + let executeTransaction = await dagora.executeTransaction(order, { + from: seller, + }); + updateGasCost(GAS_EVALUATION, "executeTransaction", executeTransaction); + } + createCsvFile( + "success_warranty_buyer_confirm", + GAS_EVALUATION, + REPETITIONS + ); + }); + + it("successful flow with refund", async () => { + GAS_EVALUATION = { + stakeTokens: [], + updateListing: [], + createTransaction: [], + acceptTransaction: [], + updateRefund: [], + confirmReceipt: [], + }; + + let seller = accounts[0]; + let buyer = accounts[1]; + + await token.approve(dagora.address, -1, { from: seller }); + await token.approve(dagora.address, -1, { from: buyer }); + for (let i = 0; i < REPETITIONS; i++) { + let stakeTokens = await dagora.stakeTokens(10, { from: seller }); + + updateGasCost(GAS_EVALUATION, "stakeTokens", stakeTokens); + let listing = generateListing(seller, true, false); + + let updateListing = await dagora.updateListing( + listing, + Math.floor(Math.random() * 100) + 5 + ); + updateGasCost(GAS_EVALUATION, "updateListing", updateListing); + + let order = generateOrder( + listing, + buyer, + token.address, + protocol_percentage, + new Date().getTime(), + true + ); + let createTransaction = await dagora.createTransaction(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "createTransaction", createTransaction); + + let acceptTransaction = await dagora.acceptTransaction(order, { + from: seller, + }); + updateGasCost(GAS_EVALUATION, "acceptTransaction", acceptTransaction); + + let updateRefund = await dagora.updateRefund( + order, + order.cashback + + 1 + + Math.floor( + Math.random() * + (order.total - + order.protocolFee - + order.commission - + order.cashback - + 1) + ), + { + from: seller, + } + ); + updateGasCost(GAS_EVALUATION, "updateRefund", updateRefund); + + let confirmReceipt = await dagora.confirmReceipt(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "confirmReceipt", confirmReceipt); + } + createCsvFile("success_refund", GAS_EVALUATION, REPETITIONS); + }); + + it("unsuccessful flow with claim warranty and expiration", async () => { + GAS_EVALUATION = { + stakeTokens: [], + updateListing: [], + createTransaction: [], + acceptTransaction: [], + confirmReceipt: [], + claimWarranty: [], + executeTransaction: [], + }; + + let seller = accounts[0]; + let buyer = accounts[1]; + + await token.approve(dagora.address, -1, { from: seller }); + await token.approve(dagora.address, -1, { from: buyer }); + for (let i = 0; i < REPETITIONS; i++) { + let stakeTokens = await dagora.stakeTokens(10, { from: seller }); + + updateGasCost(GAS_EVALUATION, "stakeTokens", stakeTokens); + let listing = generateListing(seller, true, false); + + let updateListing = await dagora.updateListing( + listing, + Math.floor(Math.random() * 100) + 5 + ); + updateGasCost(GAS_EVALUATION, "updateListing", updateListing); + + let order = generateOrder( + listing, + buyer, + token.address, + protocol_percentage, + new Date().getTime(), + true + ); + let createTransaction = await dagora.createTransaction(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "createTransaction", createTransaction); + + let acceptTransaction = await dagora.acceptTransaction(order, { + from: seller, + }); + updateGasCost(GAS_EVALUATION, "acceptTransaction", acceptTransaction); + + let confirmReceipt = await dagora.confirmReceipt(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "confirmReceipt", confirmReceipt); + + let claimWarranty = await dagora.claimWarranty(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "claimWarranty", claimWarranty); + + advanceTime(order.confirmationTimeout * 24 * 60 * 60); // seconds + + let executeTransaction = await dagora.executeTransaction(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "executeTransaction", executeTransaction); + } + createCsvFile( + "unsuccess_claim_warranty_expire", + GAS_EVALUATION, + REPETITIONS + ); + }); + + it("unsuccessful flow with claim warranty and confirmation", async () => { + GAS_EVALUATION = { + stakeTokens: [], + updateListing: [], + createTransaction: [], + acceptTransaction: [], + buyerConfirmReceipt: [], + claimWarranty: [], + sellerConfirmReceipt: [], + }; + + let seller = accounts[0]; + let buyer = accounts[1]; + + await token.approve(dagora.address, -1, { from: seller }); + await token.approve(dagora.address, -1, { from: buyer }); + for (let i = 0; i < REPETITIONS; i++) { + let stakeTokens = await dagora.stakeTokens(10, { from: seller }); + + updateGasCost(GAS_EVALUATION, "stakeTokens", stakeTokens); + let listing = generateListing(seller, true, false); + + let updateListing = await dagora.updateListing( + listing, + Math.floor(Math.random() * 100) + 5 + ); + updateGasCost(GAS_EVALUATION, "updateListing", updateListing); + + let order = generateOrder( + listing, + buyer, + token.address, + protocol_percentage, + new Date().getTime(), + true + ); + let createTransaction = await dagora.createTransaction(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "createTransaction", createTransaction); + + let acceptTransaction = await dagora.acceptTransaction(order, { + from: seller, + }); + updateGasCost(GAS_EVALUATION, "acceptTransaction", acceptTransaction); + + let buyerConfirmReceipt = await dagora.confirmReceipt(order, { + from: buyer, + }); + updateGasCost( + GAS_EVALUATION, + "buyerConfirmReceipt", + buyerConfirmReceipt + ); + + let claimWarranty = await dagora.claimWarranty(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "claimWarranty", claimWarranty); + + advanceTime(order.confirmationTimeout * 24 * 60 * 60); // seconds + + let sellerConfirmReceipt = await dagora.confirmReceipt(order, { + from: seller, + }); + updateGasCost( + GAS_EVALUATION, + "sellerConfirmReceipt", + sellerConfirmReceipt + ); + } + createCsvFile( + "unsuccess_claim_warranty_confirm", + GAS_EVALUATION, + REPETITIONS + ); + }); + + it("unsuccessful flow with buyer dispute and seller timeout", async () => { + GAS_EVALUATION = { + stakeTokens: [], + updateListing: [], + createTransaction: [], + acceptTransaction: [], + disputeTransaction: [], + disputeTimeout: [], + }; + + let seller = accounts[0]; + let buyer = accounts[1]; + + await token.approve(dagora.address, -1, { from: seller }); + await token.approve(dagora.address, -1, { from: buyer }); + for (let i = 0; i < REPETITIONS; i++) { + let stakeTokens = await dagora.stakeTokens(10, { from: seller }); + + updateGasCost(GAS_EVALUATION, "stakeTokens", stakeTokens); + let listing = generateListing(seller, true, false); + + let updateListing = await dagora.updateListing( + listing, + Math.floor(Math.random() * 100) + 5 + ); + updateGasCost(GAS_EVALUATION, "updateListing", updateListing); + + let order = generateOrder( + listing, + buyer, + token.address, + protocol_percentage, + new Date().getTime(), + true + ); + let createTransaction = await dagora.createTransaction(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "createTransaction", createTransaction); + + let acceptTransaction = await dagora.acceptTransaction(order, { + from: seller, + }); + updateGasCost(GAS_EVALUATION, "acceptTransaction", acceptTransaction); + + let cost = (await dagora.arbitrationCost(2)).toNumber(); + let disputeTransaction = await dagora.disputeTransaction(order, { + from: buyer, + value: cost, + }); + + updateGasCost(GAS_EVALUATION, "disputeTransaction", disputeTransaction); + + advanceTime(dispute_timeout * 24 * 60 * 60); // seconds + + let disputeHash = hashOrder(order); + let disputeTimeout = await dagora.disputeTimeout(disputeHash, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "disputeTimeout", disputeTimeout); + } + createCsvFile( + "unsuccess_dispute_seller_timeout", + GAS_EVALUATION, + REPETITIONS + ); + }); + + it("unsuccessful flow with buyer dispute and seller accept", async () => { + GAS_EVALUATION = { + stakeTokens: [], + updateListing: [], + createTransaction: [], + acceptTransaction: [], + disputeTransaction: [], + payArbitrationFee: [], + giveRuling: [], + }; + + let judge = accounts[0]; + let seller = accounts[0]; + let buyer = accounts[1]; + + await token.approve(dagora.address, -1, { from: seller }); + await token.approve(dagora.address, -1, { from: buyer }); + for (let i = 0; i < REPETITIONS; i++) { + let stakeTokens = await dagora.stakeTokens(10, { from: seller }); + + updateGasCost(GAS_EVALUATION, "stakeTokens", stakeTokens); + let listing = generateListing(seller, true, false); + + let updateListing = await dagora.updateListing( + listing, + Math.floor(Math.random() * 100) + 5 + ); + updateGasCost(GAS_EVALUATION, "updateListing", updateListing); + + let order = generateOrder( + listing, + buyer, + token.address, + protocol_percentage, + new Date().getTime(), + true + ); + let createTransaction = await dagora.createTransaction(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "createTransaction", createTransaction); + + let acceptTransaction = await dagora.acceptTransaction(order, { + from: seller, + }); + updateGasCost(GAS_EVALUATION, "acceptTransaction", acceptTransaction); + + let cost = (await dagora.arbitrationCost(2)).toNumber(); + let disputeTransaction = await dagora.disputeTransaction(order, { + from: buyer, + value: cost, + }); + + updateGasCost(GAS_EVALUATION, "disputeTransaction", disputeTransaction); + + let disputeHash = hashOrder(order); + let payArbitrationFee = await dagora.payArbitrationFee(disputeHash, { + from: seller, + value: cost, + }); + updateGasCost(GAS_EVALUATION, "payArbitrationFee", payArbitrationFee); + let dispute = await dagora.disputes.call(disputeHash); + let giveRuling = await arbitrator.giveRuling( + dispute.disputeId, + Math.floor(Math.random() * 3), + { + from: judge, + } + ); + updateGasCost(GAS_EVALUATION, "giveRuling", giveRuling); + } + createCsvFile( + "unsuccess_dispute_seller_accepts", + GAS_EVALUATION, + REPETITIONS + ); + }); + + it("unsuccessful flow with warranty dispute and buyer timeout", async () => { + GAS_EVALUATION = { + stakeTokens: [], + updateListing: [], + createTransaction: [], + acceptTransaction: [], + confirmReceipt: [], + claimWarranty: [], + disputeTransaction: [], + disputeTimeout: [], + }; + + let seller = accounts[0]; + let judge = accounts[0]; + let buyer = accounts[1]; + + await token.approve(dagora.address, -1, { from: seller }); + await token.approve(dagora.address, -1, { from: buyer }); + for (let i = 0; i < REPETITIONS; i++) { + let stakeTokens = await dagora.stakeTokens(10, { from: seller }); + + updateGasCost(GAS_EVALUATION, "stakeTokens", stakeTokens); + let listing = generateListing(seller, true, false); + + let updateListing = await dagora.updateListing( + listing, + Math.floor(Math.random() * 100) + 5 + ); + updateGasCost(GAS_EVALUATION, "updateListing", updateListing); + + let order = generateOrder( + listing, + buyer, + token.address, + protocol_percentage, + new Date().getTime(), + true + ); + let createTransaction = await dagora.createTransaction(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "createTransaction", createTransaction); + + let acceptTransaction = await dagora.acceptTransaction(order, { + from: seller, + }); + updateGasCost(GAS_EVALUATION, "acceptTransaction", acceptTransaction); + + let confirmReceipt = await dagora.confirmReceipt(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "confirmReceipt", confirmReceipt); + + let claimWarranty = await dagora.claimWarranty(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "claimWarranty", claimWarranty); - let timestamp = 1; + let cost = (await dagora.arbitrationCost(2)).toNumber(); + let disputeTransaction = await dagora.disputeTransaction(order, { + from: seller, + value: cost, + }); + + updateGasCost(GAS_EVALUATION, "disputeTransaction", disputeTransaction); + + advanceTime(dispute_timeout * 24 * 60 * 60); // seconds + + let disputeHash = hashOrder(order); + let disputeTimeout = await dagora.disputeTimeout(disputeHash, { + from: judge, + }); + updateGasCost(GAS_EVALUATION, "disputeTimeout", disputeTimeout); + } + createCsvFile( + "unsuccess_warranty_dispute_timeout", + GAS_EVALUATION, + REPETITIONS + ); + }); + + it("unsuccessful flow with warranty dispute and buyer accept", async () => { + GAS_EVALUATION = { + stakeTokens: [], + updateListing: [], + createTransaction: [], + acceptTransaction: [], + confirmReceipt: [], + claimWarranty: [], + disputeTransaction: [], + payArbitrationFee: [], + giveRuling: [], + }; + + let seller = accounts[0]; + let judge = accounts[0]; + let buyer = accounts[1]; + + await token.approve(dagora.address, -1, { from: seller }); + await token.approve(dagora.address, -1, { from: buyer }); for (let i = 0; i < REPETITIONS; i++) { + let stakeTokens = await dagora.stakeTokens(10, { from: seller }); + + updateGasCost(GAS_EVALUATION, "stakeTokens", stakeTokens); + let listing = generateListing(seller, true, false); + + let updateListing = await dagora.updateListing( + listing, + Math.floor(Math.random() * 100) + 5 + ); + updateGasCost(GAS_EVALUATION, "updateListing", updateListing); + let order = generateOrder( listing, buyer, token.address, protocol_percentage, - timestamp++ + new Date().getTime(), + true ); - let orderHash = await dagora.createTransaction(order, { from: buyer }); - console.log( - `createTransaction() gas used: ${orderHash.receipt.gasUsed}` + let createTransaction = await dagora.createTransaction(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "createTransaction", createTransaction); + + let acceptTransaction = await dagora.acceptTransaction(order, { + from: seller, + }); + updateGasCost(GAS_EVALUATION, "acceptTransaction", acceptTransaction); + + let confirmReceipt = await dagora.confirmReceipt(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "confirmReceipt", confirmReceipt); + + let claimWarranty = await dagora.claimWarranty(order, { + from: buyer, + }); + updateGasCost(GAS_EVALUATION, "claimWarranty", claimWarranty); + + let cost = (await dagora.arbitrationCost(2)).toNumber(); + let disputeTransaction = await dagora.disputeTransaction(order, { + from: seller, + value: cost, + }); + + updateGasCost(GAS_EVALUATION, "disputeTransaction", disputeTransaction); + + let disputeHash = hashOrder(order); + let payArbitrationFee = await dagora.payArbitrationFee(disputeHash, { + from: buyer, + value: cost, + }); + updateGasCost(GAS_EVALUATION, "payArbitrationFee", payArbitrationFee); + + let disputeId = (await dagora.disputes.call(disputeHash)).disputeId; + let giveRuling = await arbitrator.giveRuling( + disputeId, + Math.floor(Math.random() * 3), + { + from: judge, + } ); - gasUsed += orderHash.receipt.gasUsed; + updateGasCost(GAS_EVALUATION, "giveRuling", giveRuling); } - console.log( - `createTransaction() AVERAGE GAS USED: ${gasUsed / REPETITIONS}` + createCsvFile( + "unsuccess_warranty_dispute_accept", + GAS_EVALUATION, + REPETITIONS ); }); + + it("report and seller expire", async () => { + GAS_EVALUATION = { + sellerStakeTokens: [], + reporterStakeTokens: [], + updateListing: [], + report: [], + disputeTimeout: [], + }; + + let seller = accounts[0]; + let reporter = accounts[1]; + + await token.approve(dagora.address, -1, { from: seller }); + await token.approve(dagora.address, -1, { from: reporter }); + for (let i = 0; i < REPETITIONS; i++) { + let sellerStakeTokens = await dagora.stakeTokens(10, { from: seller }); + + updateGasCost(GAS_EVALUATION, "sellerStakeTokens", sellerStakeTokens); + + let reporterStakeTokens = await dagora.stakeTokens(10, { + from: reporter, + }); + + updateGasCost( + GAS_EVALUATION, + "reporterStakeTokens", + reporterStakeTokens + ); + + let listing = generateListing(seller, true, false); + + let updateListing = await dagora.updateListing( + listing, + Math.floor(Math.random() * 100) + 5 + ); + updateGasCost(GAS_EVALUATION, "updateListing", updateListing); + + let cost = (await dagora.arbitrationCost(1)).toNumber(); + let report = await dagora.report(listing, { + from: reporter, + value: cost, + }); + updateGasCost(GAS_EVALUATION, "report", report); + + advanceTime(dispute_timeout * 24 * 60 * 60); // seconds + + let disputeHash = hashListing(listing); + let disputeTimeout = await dagora.disputeTimeout(disputeHash, { + from: reporter, + }); + updateGasCost(GAS_EVALUATION, "disputeTimeout", disputeTimeout); + } + createCsvFile("report_seller_expire", GAS_EVALUATION, REPETITIONS); + }); + + it("report and seller accept, reporter wins", async () => { + GAS_EVALUATION = { + sellerStakeTokens: [], + reporterStakeTokens: [], + updateListing: [], + report: [], + payArbitrationFee: [], + giveRuling: [], + }; + + let judge = accounts[0]; + let seller = accounts[0]; + let reporter = accounts[1]; + + await token.approve(dagora.address, -1, { from: seller }); + await token.approve(dagora.address, -1, { from: reporter }); + for (let i = 0; i < REPETITIONS; i++) { + let sellerStakeTokens = await dagora.stakeTokens(10, { from: seller }); + + updateGasCost(GAS_EVALUATION, "sellerStakeTokens", sellerStakeTokens); + + let reporterStakeTokens = await dagora.stakeTokens(10, { + from: reporter, + }); + + updateGasCost( + GAS_EVALUATION, + "reporterStakeTokens", + reporterStakeTokens + ); + + let listing = generateListing(seller, true, false); + + let updateListing = await dagora.updateListing( + listing, + Math.floor(Math.random() * 100) + 5 + ); + updateGasCost(GAS_EVALUATION, "updateListing", updateListing); + + let cost = (await dagora.arbitrationCost(1)).toNumber(); + let report = await dagora.report(listing, { + from: reporter, + value: cost, + }); + updateGasCost(GAS_EVALUATION, "report", report); + + let disputeHash = hashListing(listing); + + let payArbitrationFee = await dagora.payArbitrationFee(disputeHash, { + from: seller, + value: cost, + }); + updateGasCost(GAS_EVALUATION, "payArbitrationFee", payArbitrationFee); + + let dispute = await dagora.disputes.call(disputeHash); + let giveRuling = await arbitrator.giveRuling(dispute.disputeId, 1, { + from: judge, + }); + updateGasCost(GAS_EVALUATION, "giveRuling", giveRuling); + } + createCsvFile("report_reporter_wins", GAS_EVALUATION, REPETITIONS); + }); + + it("report and seller accept, seller wins", async () => { + GAS_EVALUATION = { + sellerStakeTokens: [], + reporterStakeTokens: [], + updateListing: [], + report: [], + payArbitrationFee: [], + giveRuling: [], + }; + + let judge = accounts[0]; + let seller = accounts[0]; + let reporter = accounts[1]; + + await token.approve(dagora.address, -1, { from: seller }); + await token.approve(dagora.address, -1, { from: reporter }); + for (let i = 0; i < REPETITIONS; i++) { + let sellerStakeTokens = await dagora.stakeTokens(10, { from: seller }); + + updateGasCost(GAS_EVALUATION, "sellerStakeTokens", sellerStakeTokens); + + let reporterStakeTokens = await dagora.stakeTokens(10, { + from: reporter, + }); + + updateGasCost( + GAS_EVALUATION, + "reporterStakeTokens", + reporterStakeTokens + ); + + let listing = generateListing(seller, true, false); + + let updateListing = await dagora.updateListing( + listing, + Math.floor(Math.random() * 100) + 5 + ); + updateGasCost(GAS_EVALUATION, "updateListing", updateListing); + + let cost = (await dagora.arbitrationCost(1)).toNumber(); + let report = await dagora.report(listing, { + from: reporter, + value: cost, + }); + updateGasCost(GAS_EVALUATION, "report", report); + + let disputeHash = hashListing(listing); + + let payArbitrationFee = await dagora.payArbitrationFee(disputeHash, { + from: seller, + value: cost, + }); + updateGasCost(GAS_EVALUATION, "payArbitrationFee", payArbitrationFee); + + let dispute = await dagora.disputes.call(disputeHash); + let giveRuling = await arbitrator.giveRuling(dispute.disputeId, 2, { + from: judge, + }); + updateGasCost(GAS_EVALUATION, "giveRuling", giveRuling); + advanceTime(dispute_timeout * 24 * 60 * 60); // seconds + } + createCsvFile("report_seller_wins", GAS_EVALUATION, REPETITIONS); + }); + + // it("listing update", async () => { + // await token.approve(dagora.address, 10, { from: accounts[0] }); + // await dagora.stakeTokens(10, { from: accounts[0] }); + // for (let i = 0; i < REPETITIONS; i++) { + // let listing = generateListing(accounts[0]); + // let updateListing = await dagora.updateListing(listing, 1); + // let valid = await dagora.requireValidListing(listing); + // assert.equal(valid.valueOf(), true); + // console.log( + // `updateListing() gas used: ${updateListing.receipt.gasUsed}` + // ); + // updateGasCost(GAS_EVALUATION, "updateListing", updateListing); + // } + // }); + + // it("create transaction", async () => { + // let seller = accounts[0]; + // let buyer = accounts[1]; + + // let gasUsed = 0; + // await token.approve(dagora.address, 10, { from: seller }); + // await dagora.stakeTokens(10, { from: seller }); + + // await token.approve(dagora.address, -1, { + // from: buyer, + // }); + + // let listing = generateListing(seller); + // await dagora.updateListing(listing, REPETITIONS * 5); + // let valid = await dagora.requireValidListing(listing); + // assert.equal(valid.valueOf(), true); + + // let timestamp = 1; + // for (let i = 0; i < REPETITIONS; i++) { + // let order = generateOrder( + // listing, + // buyer, + // token.address, + // protocol_percentage, + // timestamp++ + // ); + // let orderHash = await dagora.createTransaction(order, { from: buyer }); + // console.log( + // `createTransaction() gas used: ${orderHash.receipt.gasUsed}` + // ); + // gasUsed += orderHash.receipt.gasUsed; + // } + // console.log( + // `createTransaction() AVERAGE GAS USED: ${gasUsed / REPETITIONS}` + // ); + // }); }); // context("GSN", function () { // let token; diff --git a/test/helpers/csvExporter.js b/test/helpers/csvExporter.js new file mode 100644 index 0000000..bffc594 --- /dev/null +++ b/test/helpers/csvExporter.js @@ -0,0 +1,21 @@ +const fs = require("fs"); + +function createCsvFile(fileName, gasCostMap, repetitions) { + let csvContent = Object.keys(gasCostMap).join(",") + "\n"; + let rows = []; + for (let i = 0; i < repetitions; i++) { + rows[i] = []; + for (let key of Object.keys(gasCostMap)) { + rows[i].push(gasCostMap[key][i]); + } + } + csvContent += rows.map((e) => e.join(",")).join("\n"); + if (!fs.existsSync("./results/")) { + fs.mkdirSync("./results/"); + } + fs.writeFileSync(`./results/${fileName}.csv`, csvContent, "utf8"); +} + +module.exports = { + createCsvFile, +}; diff --git a/test/helpers/populator.js b/test/helpers/populator.js index 9fa6891..ec4348f 100644 --- a/test/helpers/populator.js +++ b/test/helpers/populator.js @@ -1,15 +1,16 @@ -function generateListing(sellerAddress) { +function generateListing(sellerAddress, warranty, expiration) { return { ipfsHash: web3.utils.randomHex(32), seller: sellerAddress, commissionPercentage: Math.floor(Math.random() * 10) * 10, - warranty: 0, //Math.floor(Math.random() * 7), + warranty: warranty ? Math.floor(Math.random() * 7) + 1 : 0, cashbackPercentage: Math.floor(Math.random() * 10) * 10, - expiration: - Math.random() < 0.5 + expiration: expiration + ? Math.random() < 0.5 ? -1 : Math.floor(new Date().getTime() / 1000) + - (Math.floor(Math.random() * 29) + 1) * 86400, // Days + (Math.floor(Math.random() * 29) + 1) * 86400 + : -1, // Days }; } @@ -18,7 +19,8 @@ function generateOrder( buyer, tokenAddress, protocol_percentage, - timestamp = 0 + nonce = 0, + timeout = false ) { price = Math.floor(Math.random() * 1000); return { @@ -31,8 +33,8 @@ function generateOrder( cashback: Math.floor((price * listing.cashbackPercentage) / 10000), commission: Math.floor((price * listing.commissionPercentage) / 10000), protocolFee: Math.floor((price * protocol_percentage) / 10000), - confirmationTimeout: 0, //Math.floor(Math.random() * 30), - timestamp: timestamp, + confirmationTimeout: timeout ? Math.floor(Math.random() * 30) + 1 : 0, + nonce: nonce, }; } diff --git a/test/helpers/signatureHelper.js b/test/helpers/signatureHelper.js index 452d662..1a6afaa 100644 --- a/test/helpers/signatureHelper.js +++ b/test/helpers/signatureHelper.js @@ -22,9 +22,9 @@ function hashListing(_listing) { ); } -function hashOrder(listingHash, _order) { +function hashOrder(_order) { return web3.utils.soliditySha3( - listingHash, + hashListing(_order.listing), _order.buyer, _order.commissioner, _order.token, @@ -34,7 +34,7 @@ function hashOrder(listingHash, _order) { _order.commission, _order.protocolFee, _order.confirmationTimeout, - _order.timestamp + _order.nonce ); } diff --git a/test/helpers/truffleTestHelper.js b/test/helpers/truffleTestHelper.js index f4af4bc..bdd7e71 100644 --- a/test/helpers/truffleTestHelper.js +++ b/test/helpers/truffleTestHelper.js @@ -1,41 +1,61 @@ advanceTimeAndBlock = async (time) => { - await advanceTime(time); - await advanceBlock(); + await advanceTime(time); + await advanceBlock(); - return Promise.resolve(web3.eth.getBlock('latest')); -} + return Promise.resolve(web3.eth.getBlock("latest")); +}; advanceTime = (time) => { - return new Promise((resolve, reject) => { - web3.currentProvider.send({ - jsonrpc: "2.0", - method: "evm_increaseTime", - params: [time], - id: new Date().getTime() - }, (err, result) => { - if (err) { return reject(err); } - return resolve(result); - }); - }); -} + return new Promise((resolve, reject) => { + web3.currentProvider.send( + { + jsonrpc: "2.0", + method: "evm_increaseTime", + params: [time], + id: new Date().getTime(), + }, + (err, result) => { + if (err) { + return reject(err); + } + return resolve(result); + } + ); + }); +}; advanceBlock = () => { - return new Promise((resolve, reject) => { - web3.currentProvider.send({ - jsonrpc: "2.0", - method: "evm_mine", - id: new Date().getTime() - }, (err, result) => { - if (err) { return reject(err); } - const newBlockHash = web3.eth.getBlock('latest').hash; + return new Promise((resolve, reject) => { + web3.currentProvider.send( + { + jsonrpc: "2.0", + method: "evm_mine", + id: new Date().getTime(), + }, + (err, result) => { + if (err) { + return reject(err); + } + const newBlockHash = web3.eth.getBlock("latest").hash; - return resolve(newBlockHash) - }); - }); -} + return resolve(newBlockHash); + } + ); + }); +}; + +getGasCost = (transaction) => { + return transaction.receipt.gasUsed; +}; + +updateGasCost = (gas_eval, name, transaction) => { + gas_eval[name].push(transaction.receipt.gasUsed); +}; module.exports = { - advanceTime, - advanceBlock, - advanceTimeAndBlock -} \ No newline at end of file + advanceTime, + advanceBlock, + advanceTimeAndBlock, + getGasCost, + updateGasCost, +}; diff --git a/yarn.lock b/yarn.lock index 275eddc..4106689 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2642,6 +2642,11 @@ css-what@2.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== +csv-writer@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/csv-writer/-/csv-writer-1.6.0.tgz#d0cea44b6b4d7d3baa2ecc6f3f7209233514bcf9" + integrity sha512-NOx7YDFWEsM/fTRAJjRpPp8t+MKRVvniAg9wQlUKx20MFrPs73WLJhFf5iteqrxNYnsy924K3Iroh3yNHeYd2g== + d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a"