diff --git a/contracts/teller-network/Arbitrable.sol b/contracts/teller-network/Arbitrable.sol index 48f3cab8..64de5c87 100644 --- a/contracts/teller-network/Arbitrable.sol +++ b/contracts/teller-network/Arbitrable.sol @@ -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"); diff --git a/contracts/teller-network/Escrow.sol b/contracts/teller-network/Escrow.sol index e139f288..d948bfb8 100644 --- a/contracts/teller-network/Escrow.sol +++ b/contracts/teller-network/Escrow.sol @@ -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; } diff --git a/contracts/teller-network/IEscrow.sol b/contracts/teller-network/IEscrow.sol index 48d01c65..a479eaca 100644 --- a/contracts/teller-network/IEscrow.sol +++ b/contracts/teller-network/IEscrow.sol @@ -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( diff --git a/contracts/teller-network/MetadataStore.sol b/contracts/teller-network/MetadataStore.sol index 086f427d..331e3e92 100644 --- a/contracts/teller-network/MetadataStore.sol +++ b/contracts/teller-network/MetadataStore.sol @@ -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 diff --git a/src/js/features/escrow/saga.js b/src/js/features/escrow/saga.js index 9bc87a6d..ff033f01 100644 --- a/src/js/features/escrow/saga.js +++ b/src/js/features/escrow/saga.js @@ -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); diff --git a/src/js/features/metadata/saga.js b/src/js/features/metadata/saga.js index 9c2a979b..c0d44ac0 100644 --- a/src/js/features/metadata/saga.js +++ b/src/js/features/metadata/saga.js @@ -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)) { diff --git a/test/metadata_store_spec.js b/test/metadata_store_spec.js index dc081295..933ed51f 100644 --- a/test/metadata_store_spec.js +++ b/test/metadata_store_spec.js @@ -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'); });