fix: reduce escrow creation gas cost by 50K (#390)

* fix: reduce createTransaction gas cost
* fix: reducing arbitrable gas cost
* fix: reducing gas cost of calling MetadataStore from escrow
This commit is contained in:
Richard Ramos 2019-07-24 13:03:18 -04:00 committed by GitHub
parent e81e0fa662
commit bbaa9b613a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 61 additions and 100 deletions

View File

@ -47,7 +47,7 @@ contract Arbitrable {
* @notice Get arbitrator of an escrow
* @return address Arbitrator address
*/
function getArbitrator(uint _escrowId) public view returns(address);
function _getArbitrator(uint _escrowId) internal view returns(address);
/**
* @notice Determine if a dispute exists/existed for an escrow
@ -55,6 +55,10 @@ contract Arbitrable {
* @return bool result
*/
function isDisputed(uint _escrowId) public view returns (bool) {
return _isDisputed(_escrowId);
}
function _isDisputed(uint _escrowId) internal view returns (bool) {
return arbitrationCases[_escrowId].open || arbitrationCases[_escrowId].result != ArbitrationResult.UNSOLVED;
}
@ -91,7 +95,7 @@ contract Arbitrable {
require(arbitrationCases[_escrowId].result == ArbitrationResult.UNSOLVED && !arbitrationCases[_escrowId].open,
"Arbitration already solved or has been opened before");
address arbitratorAddress = getArbitrator(_escrowId);
address arbitratorAddress = _getArbitrator(_escrowId);
require(arbitratorAddress != address(0), "Arbitrator is required");

View File

@ -140,19 +140,20 @@ contract Escrow is IEscrow, Pausable, MessageSigned, Fees, Arbitrable {
require(!deleted, "Offer is not valid");
require(sellerLicenses.isLicenseOwner(seller), "Must be a valid seller to create escrow transactions");
require(seller != _buyer, "Seller and Buyer must be different");
require(arbitrator != _buyer, "Cannot buy offers where buyer is arbitrator");
require(arbitrator != _buyer && arbitrator != address(0), "Cannot buy offers where buyer is arbitrator");
require(_tokenAmount != 0 && _assetPrice != 0, "Trade amounts cannot be 0");
require(arbitrator != address(0), "Arbitrator is required");
escrowId = transactions.length++;
transactions[escrowId].offerId = _offerId;
transactions[escrowId].token = token;
transactions[escrowId].buyer = _buyer;
transactions[escrowId].seller = seller;
transactions[escrowId].arbitrator = arbitrator;
transactions[escrowId].tokenAmount = _tokenAmount;
transactions[escrowId].assetPrice = _assetPrice;
EscrowTransaction storage trx = transactions[escrowId];
trx.offerId = _offerId;
trx.token = token;
trx.buyer = _buyer;
trx.seller = seller;
trx.arbitrator = arbitrator;
trx.tokenAmount = _tokenAmount;
trx.assetPrice = _assetPrice;
emit Created(_offerId, seller, _buyer, escrowId);
}
@ -349,7 +350,7 @@ contract Escrow is IEscrow, Pausable, MessageSigned, Fees, Arbitrable {
EscrowStatus mStatus = transactions[_escrowId].status;
require(transactions[_escrowId].seller == msg.sender, "Only the seller can invoke this function");
require(mStatus == EscrowStatus.PAID || mStatus == EscrowStatus.FUNDED, "Invalid transaction status");
require(!isDisputed(_escrowId), "Can't release a transaction that has an arbitration process");
require(!_isDisputed(_escrowId), "Can't release a transaction that has an arbitration process");
_release(_escrowId, transactions[_escrowId], false);
}
@ -531,7 +532,7 @@ contract Escrow is IEscrow, Pausable, MessageSigned, Fees, Arbitrable {
* @param _escrowId Id of the escrow
* @return Arbitrator address
*/
function getArbitrator(uint _escrowId) public view returns(address) {
function _getArbitrator(uint _escrowId) internal view returns(address) {
return transactions[_escrowId].arbitrator;
}

View File

@ -11,10 +11,10 @@ contract IEscrow {
uint256 expirationTime;
uint256 rating;
uint256 assetPrice;
EscrowStatus status;
address payable buyer;
address payable seller;
address payable arbitrator;
EscrowStatus status;
}
function createEscrow(

View File

@ -32,9 +32,7 @@ contract MetadataStore is MessageSigned {
License public sellingLicenses;
ArbitrationLicense public arbitrationLicenses;
User[] public users;
mapping(address => bool) public userWhitelist;
mapping(address => uint256) public addressToUser;
mapping(address => User) public users;
mapping(address => uint) public user_nonce;
Offer[] public offers;
@ -57,8 +55,6 @@ contract MetadataStore is MessageSigned {
event OfferRemoved(address owner, uint256 offerId);
event UserUpdated(address owner, bytes statusContactCode, string location, string username);
/**
* @param _sellingLicenses Sellers licenses contract address
* @param _arbitrationLicenses Arbitrators licenses contract address
@ -82,8 +78,6 @@ contract MetadataStore is MessageSigned {
sellingLicenses = License(_sellingLicenses);
arbitrationLicenses = ArbitrationLicense(_arbitrationLicenses);
users.length++;
}
/**
@ -155,17 +149,10 @@ contract MetadataStore is MessageSigned {
string memory _location,
string memory _username
) internal {
if (!userWhitelist[_user]) {
User memory user = User(_statusContactCode, _location, _username);
uint256 userId = users.push(user) - 1;
addressToUser[_user] = userId;
userWhitelist[_user] = true;
} else {
User storage tmpUser = users[addressToUser[_user]];
tmpUser.statusContactCode = _statusContactCode;
tmpUser.location = _location;
tmpUser.username = _username;
}
User storage u = users[_user];
u.statusContactCode = _statusContactCode;
u.location = _location;
u.username = _username;
}
/**
@ -247,30 +234,12 @@ contract MetadataStore is MessageSigned {
emit OfferAdded(msg.sender, offerId, _asset, _statusContactCode, _location, _currency, _username, _paymentMethods, _margin);
}
/**
* @notice Update the user information
* @param _statusContactCode Status contact code
* @param _location Location on earth
* @param _username Username of the user
*/
function updateUser(bytes calldata _statusContactCode, string calldata _location, string calldata _username) external {
require(userWhitelist[msg.sender], "User does not exist");
User storage tmpUser = users[addressToUser[msg.sender]];
tmpUser.statusContactCode = _statusContactCode;
tmpUser.location = _location;
tmpUser.username = _username;
emit UserUpdated(msg.sender, _statusContactCode, _location, _username);
}
/**
* @notice Remove user offer
* @dev Removed offers are marked as deleted instead of being deleted
* @param _offerId Id of the offer to remove
*/
function removeOffer(uint256 _offerId) external {
require(userWhitelist[msg.sender], "User does not exist");
require(offerWhitelist[msg.sender][_offerId], "Offer does not exist");
offers[_offerId].deleted = true;
@ -293,20 +262,22 @@ contract MetadataStore is MessageSigned {
address payable arbitrator,
bool deleted
) {
Offer memory theOffer = offers[_id];
// In case arbitrator rejects the seller
address payable offerArbitrator = offers[_id].arbitrator;
if(!arbitrationLicenses.isAllowed(offers[_id].owner, offerArbitrator)){
address payable offerArbitrator = theOffer.arbitrator;
if(!arbitrationLicenses.isAllowed(theOffer.owner, offerArbitrator)){
offerArbitrator = address(0);
}
return (
offers[_id].asset,
offers[_id].currency,
offers[_id].margin,
offers[_id].paymentMethods,
offers[_id].owner,
theOffer.asset,
theOffer.currency,
theOffer.margin,
theOffer.paymentMethods,
theOffer.owner,
offerArbitrator,
offers[_id].deleted
theOffer.deleted
);
}
@ -340,14 +311,6 @@ contract MetadataStore is MessageSigned {
return (offers[_id].arbitrator);
}
/**
* @notice Get the size of the users
* @return Number of users stored in the contract
*/
function usersSize() external view returns (uint256) {
return users.length;
}
/**
* @notice Get the size of the offers
* @return Number of offers stored in the contract

View File

@ -172,10 +172,8 @@ export function *doLoadEscrows({address}) {
const escrow = yield Escrow.methods.transactions(ev.returnValues.escrowId).call();
escrow.escrowId = ev.returnValues.escrowId;
escrow.offer = yield MetadataStore.methods.offer(escrow.offerId).call();
const sellerId = yield MetadataStore.methods.addressToUser(escrow.offer.owner).call();
escrow.seller = yield MetadataStore.methods.users(sellerId).call();
const buyerId = yield MetadataStore.methods.addressToUser(escrow.buyer).call();
escrow.buyerInfo = yield MetadataStore.methods.users(buyerId).call();
escrow.seller = yield MetadataStore.methods.users(escrow.offer.owner).call();
escrow.buyerInfo = yield MetadataStore.methods.users(escrow.buyer).call();
return escrow;
}));
@ -195,12 +193,9 @@ export function *doGetEscrow({escrowId}) {
const escrow = yield Escrow.methods.transactions(escrowId).call();
escrow.escrowId = escrowId;
escrow.offer = yield MetadataStore.methods.offer(escrow.offerId).call();
const sellerId = yield MetadataStore.methods.addressToUser(escrow.offer.owner).call();
escrow.seller = yield MetadataStore.methods.users(sellerId).call();
const buyerId = yield MetadataStore.methods.addressToUser(escrow.buyer).call();
escrow.buyerInfo = yield MetadataStore.methods.users(buyerId).call();
const arbitratorId = yield MetadataStore.methods.addressToUser(escrow.arbitrator).call();
escrow.arbitratorInfo = yield MetadataStore.methods.users(arbitratorId).call();
escrow.seller = yield MetadataStore.methods.users(escrow.offer.owner).call();
escrow.buyerInfo = yield MetadataStore.methods.users(escrow.buyer).call();
escrow.arbitratorInfo = yield MetadataStore.methods.users(escrow.arbitrator).call();
yield put({type: GET_ESCROW_SUCCEEDED, escrow, escrowId});
} catch (error) {
console.error(error);

View File

@ -30,22 +30,20 @@ export function *loadUser({address}) {
try {
const isArbitrator = yield ArbitrationLicense.methods.isLicenseOwner(address).call();
const isSeller = yield SellerLicense.methods.isLicenseOwner(address).call();
const isUser = yield MetadataStore.methods.userWhitelist(address).call();
let user = {
let userLicenses = {
isArbitrator,
isSeller
};
if (!isUser){
const user = Object.assign(userLicenses, yield MetadataStore.methods.users(address).call());
if(!user.statusContactCode){
if(isArbitrator || isSeller) {
yield put({type: LOAD_USER_SUCCEEDED, user, address});
yield put({type: LOAD_USER_SUCCEEDED, user: userLicenses, address});
}
return;
return;
}
const id = yield MetadataStore.methods.addressToUser(address).call();
user = Object.assign(user, yield MetadataStore.methods.users(id).call());
if (user.location) {
yield put({type: LOAD_USER_LOCATION, user, address});
@ -100,8 +98,7 @@ export function *loadOffers({address}) {
const offer = yield MetadataStore.methods.offer(id).call();
if(!addressCompare(offer.arbitrator, zeroAddress)){
const id = yield MetadataStore.methods.addressToUser(offer.arbitrator).call();
offer.arbitratorData = yield MetadataStore.methods.users(id).call();
offer.arbitratorData = yield MetadataStore.methods.users(offer.arbitrator).call();
}
if (!loadedUsers.includes(offer.owner)) {

View File

@ -73,10 +73,10 @@ contract("MetadataStore", function () {
it("should not allow to add new user when not license owner", async function () {
try {
await MetadataStore.methods.addOffer(SNT.address, signature, SellerLicense.address, "London", "USD", "Iuri", [0], 1, accounts[9]).send();
} catch(error) {
const usersSize = await MetadataStore.methods.usersSize().call();
assert.strictEqual(usersSize, '1');
await MetadataStore.methods.addOffer(SNT.address, SellerLicense.address, "London", "USD", "Iuri", [0], 1, accounts[9]).send();
assert.fail('should have reverted before');
} catch (error) {
assert.strictEqual(error.message, "VM Exception while processing transaction: revert Not a license owner");
}
});
@ -84,34 +84,35 @@ contract("MetadataStore", function () {
const encodedCall = SellerLicense.methods.buy().encodeABI();
await SNT.methods.approveAndCall(SellerLicense.options.address, 10, encodedCall).send();
const receipt = await MetadataStore.methods.addOffer(SNT.address, "0x00", "London", "USD", "Iuri", [0], 1, accounts[9]).send();
const usersSize = await MetadataStore.methods.usersSize().call();
assert.strictEqual(usersSize, '2');
const offersSize = await MetadataStore.methods.offersSize().call();
assert.strictEqual(offersSize, '1');
const userInfo = await MetadataStore.methods.users(accounts[0]).call();
assert.strictEqual(userInfo.username, "Iuri");
});
it("should allow to add new offer only when already a user", async function () {
await MetadataStore.methods.addOffer(SNT.address, "0x00", "London", "EUR", "Iuri", [0], 1, accounts[9]).send();
const usersSize = await MetadataStore.methods.usersSize().call();
assert.strictEqual(usersSize, '2');
const offersSize = await MetadataStore.methods.offersSize().call();
assert.strictEqual(offersSize, '2');
const offerIds = await MetadataStore.methods.getOfferIds(accounts[0]).call();
assert.strictEqual(offerIds.length, 2);
});
it("should not allow to add new offer when margin is more than 100", async function () {
try {
await MetadataStore.methods.addOffer(SNT.address, "0x00", "London", "USD", "Iuri", [0], 101, accounts[9]).send();
} catch(error) {
const usersSize = await MetadataStore.methods.usersSize().call();
assert.strictEqual(usersSize, '2');
assert.fail('should have reverted before');
} catch (error) {
assert.strictEqual(error.message, "VM Exception while processing transaction: revert Margin too high");
}
});
it("should allow to update a user", async function () {
await MetadataStore.methods.updateUser(SNT.address, "Montreal", "Anthony").send();
const usersSize = await MetadataStore.methods.usersSize().call();
assert.strictEqual(usersSize, '2');
const user = await MetadataStore.methods.users(1).call();
await MetadataStore.methods.addOrUpdateUser(SNT.address, "Montreal", "Anthony").send();
const user = await MetadataStore.methods.users(accounts[0]).call();
assert.strictEqual(user.location, 'Montreal');
assert.strictEqual(user.username, 'Anthony');
});