Skip to content
Permalink
Browse files

Save notional as Note entity and view request as NoteAccess in graph

  • Loading branch information...
LeilaWang committed May 16, 2019
1 parent 6b93d2f commit 533e9b0de28fe2a12cba586a9a0b8d0535256225
Showing with 934 additions and 738 deletions.
  1. +4 −0 .env.test
  2. +0 −2 client/src/App.jsx
  3. +4 −4 client/src/components/ApproveViewRequestModal/RequestCheckbox.jsx
  4. +43 −28 client/src/components/ApproveViewRequestModal/index.jsx
  5. +4 −2 client/src/components/CreateLoanForm/NewLoanHandler.jsx
  6. +5 −5 client/src/components/Loan/BorrowerActions.jsx
  7. +6 −6 client/src/components/Loan/BorrowerInfo.jsx
  8. +10 −20 client/src/components/Loan/index.jsx
  9. +10 −4 client/src/components/MarkDefaultModal/index.jsx
  10. +3 −3 client/src/components/PayInterestModal/index.jsx
  11. +13 −5 client/src/components/RepayModal/index.jsx
  12. +3 −3 client/src/components/SettleLoanModal/index.jsx
  13. +1 −5 client/src/components/VisibleLoans/index.jsx
  14. +3 −3 client/src/components/WithdrawBalanceModal/index.jsx
  15. +8 −5 client/src/components/WithdrawInterestModal/index.jsx
  16. +1 −1 client/src/pages/LenderLoans.js
  17. +5 −8 client/src/queries/QueryLoans.jsx
  18. +39 −9 client/src/queries/QueryUserLoans.jsx
  19. +14 −5 client/src/queries/config/loanFields.js
  20. +2 −0 client/src/utils/loan/approveViewRequest.js
  21. +1 −1 client/src/utils/note/approveNoteAccess.js
  22. +11 −15 client/src/utils/transformLoanFromGraph.js
  23. +7 −8 client/src/utils/transformLoanStatusFromGraph.js
  24. +0 −15 client/src/utils/transformViewRequestFromGraph.js
  25. +1 −4 contracts/Loan.sol
  26. +106 −69 contracts/LoanDapp.sol
  27. +0 −51 contracts/LoanPayment.sol
  28. +61 −40 graph/mappings/LoanDapp.ts
  29. +8 −11 graph/schema.graphql
  30. +89 −56 graph/types/LoanDapp/LoanDapp.ts
  31. +4 −8 graph/types/LoanDapp/templates/Loan/Loan.ts
  32. +0 −91 graph/types/LoanPayment/LoanPayment.ts
  33. +60 −87 graph/types/schema.ts
  34. 0 migrations/{5_loan_dapp.js → 3_loan_dapp.js}
  35. +0 −10 migrations/6_mock_functions.js
  36. +1 −1 scripts/utils/log.js
  37. +7 −26 subgraph.yaml
  38. +217 −0 test/helpers/LoanService.js
  39. +9 −3 test/helpers/web3Events.js
  40. +65 −124 test/loanFactory.js
  41. +104 −0 test/loanViewRequests.js
  42. +5 −0 test/utils/asyncForEach.js
@@ -9,3 +9,7 @@ GANACHE_TESTING_ACCOUNT_1_BALANCE=1000
GANACHE_TESTING_ACCOUNT_2=0x853582df686e66a683e9dc36fe39bf89ebfbbd73ff58daf587557d323217cca9
GANACHE_TESTING_ACCOUNT_2_MNEMONIC=friend cannon timber process debris phone spoil target huge love hurry fix
GANACHE_TESTING_ACCOUNT_2_BALANCE=1000

GANACHE_TESTING_ACCOUNT_3=0x60cd6638b6578d0bced19e5d8673d15a8d3a148136e914ea442b1cc9fd0970a2
GANACHE_TESTING_ACCOUNT_3_MNEMONIC=hill property sniff forest limit clip cool forward slice vault heart wait
GANACHE_TESTING_ACCOUNT_3_BALANCE=1000
@@ -18,7 +18,6 @@ import SettlementToken from './contracts/SettlementToken';
import ACE from './contracts/ACE';
import JoinSplit from './contracts/JoinSplit';
import ZKERC20 from './contracts/ZKERC20';
import LoanPayment from './contracts/LoanPayment';
import Web3Service from './helpers/Web3Service';
import AuthService from './helpers/AuthService';
import CurrencyService from './helpers/CurrencyService';
@@ -85,7 +84,6 @@ class App extends PureComponent {
await Web3Service.registerContract(LoanDapp);
await Web3Service.registerContract(ACE);
await Web3Service.registerContract(JoinSplit);
await Web3Service.registerContract(LoanPayment);

await Web3Service.registerInterface(ZKERC20);
await Web3Service.registerInterface(SettlementToken);
@@ -10,7 +10,7 @@ import {

const RequestCheckbox = ({
viewRequest: {
lenderAddress,
address,
},
selected,
onSelect,
@@ -21,7 +21,7 @@ const RequestCheckbox = ({
padding="s 0"
background={selected ? 'grey-lightest' : ''}
borderRadius="default"
onClick={!approved ? () => onSelect(lenderAddress) : undefined}
onClick={!approved ? () => onSelect(address) : undefined}
>
<FlexBox
valign="center"
@@ -49,7 +49,7 @@ const RequestCheckbox = ({
padding="0 s"
>
<Text
text={lenderAddress || ''}
text={address || ''}
size="xxs"
/>
</Block>
@@ -59,7 +59,7 @@ const RequestCheckbox = ({

RequestCheckbox.propTypes = {
viewRequest: PropTypes.shape({
lenderAddress: PropTypes.string.isRequired,
address: PropTypes.string.isRequired,
}).isRequired,
selected: PropTypes.bool,
approved: PropTypes.bool,
@@ -40,12 +40,18 @@ class ApproveViewRequestModal extends PureComponent {
} = this.props;
const {
viewRequests,
lenderAccess,
} = loan;
const approvedRequests = lenderAccess.map(({ user }) => ({
address: user.address,
}));
const unapprovedRequests = viewRequests.filter(({ address }) =>
!approvedRequests.some(approved => approved.address === address));

this.setState({
showModal: true,
approvedRequests: viewRequests.filter(({ sharedSecret }) => sharedSecret),
unapprovedRequests: viewRequests.filter(({ sharedSecret }) => !sharedSecret),
approvedRequests,
unapprovedRequests,
selectedLenders: [],
});
};
@@ -86,41 +92,38 @@ class ApproveViewRequestModal extends PureComponent {
try {
const {
selectedLenders,
unapprovedRequests: prevUnapprovedRequests,
approvedRequests,
unapprovedRequests,
} = this.state;
const {
loan,
} = this.props;
const {
viewingKey: encryptedViewingKey,
viewRequests,
notionalNote: {
sharedSecret,
},
} = loan;
const unapprovedLenders = new Set(selectedLenders);
const approvedLenders = new Set();
const privateKey = await AuthService.getPrivateKey();
const viewingKey = await decryptMessage(privateKey, encryptedViewingKey);
const viewingKey = await decryptMessage(privateKey, sharedSecret);

await asyncForEach(selectedLenders, async (selectedLender) => {
const viewRequest = prevUnapprovedRequests.find(({
lenderAddress,
}) => lenderAddress === selectedLender);
await asyncForEach(selectedLenders, async (address) => {
const viewRequest = unapprovedRequests.find(request => request.address === address);
const approvedError = await this.approveViewRequest(viewRequest, viewingKey);
if (!approvedError) {
unapprovedLenders.delete(selectedLender);
approvedLenders.add(selectedLender);
unapprovedLenders.delete(address);
approvedLenders.add(address);
}
});

this.setState({
selectedLenders: [...unapprovedLenders],
approvedRequests: viewRequests.filter(({
lenderAddress,
sharedSecret,
}) => sharedSecret || approvedLenders.has(lenderAddress)),
unapprovedRequests: viewRequests.filter(({
lenderAddress,
sharedSecret,
}) => !sharedSecret && !approvedLenders.has(lenderAddress)),
approvedRequests: [
...approvedRequests,
...[...approvedLenders].map(address => ({ address })),
],
unapprovedRequests: unapprovedRequests.filter(({ address }) => !approvedLenders.has(address)),
isSubmitting: false,
error: '',
});
@@ -139,15 +142,20 @@ class ApproveViewRequestModal extends PureComponent {
} = this.props;
const {
id: loanId,
notionalNote,
} = loan;
const {
lenderAddress,
lenderPublicKey,
id: notionalNoteHash,
} = notionalNote;
const {
address: lenderAddress,
publicKey: lenderPublicKey,
} = viewRequest;

try {
await approveViewRequest({
loanId,
notionalNoteHash,
lenderAddress,
lenderPublicKey,
viewingKey,
@@ -206,9 +214,9 @@ class ApproveViewRequestModal extends PureComponent {
</Block>
{unapprovedRequests.map(viewRequest => (
<RequestCheckbox
key={viewRequest.lenderAddress}
key={viewRequest.address}
viewRequest={viewRequest}
selected={selectedLenders.some(lenderAddress => lenderAddress === viewRequest.lenderAddress)}
selected={selectedLenders.some(address => address === viewRequest.address)}
onSelect={this.handleSelectViewRequest}
/>
))}
@@ -230,7 +238,7 @@ class ApproveViewRequestModal extends PureComponent {
</Block>
{approvedRequests.map(viewRequest => (
<RequestCheckbox
key={viewRequest.lenderAddress}
key={viewRequest.address}
viewRequest={viewRequest}
approved
/>
@@ -262,10 +270,17 @@ ApproveViewRequestModal.propTypes = {
interestPeriod: PropTypes.number.isRequired,
loanDuration: PropTypes.number.isRequired,
settlementCurrencyId: PropTypes.string.isRequired,
viewingKey: PropTypes.string.isRequired,
notionalNote: PropTypes.shape({
sharedSecret: PropTypes.string.isRequired,
}).isRequired,
viewRequests: PropTypes.arrayOf(PropTypes.shape({
lenderAddress: PropTypes.string.isRequired,
lenderPublicKey: PropTypes.string.isRequired,
address: PropTypes.string.isRequired,
publicKey: PropTypes.string.isRequired,
})).isRequired,
lenderAccess: PropTypes.arrayOf(PropTypes.shape({
user: PropTypes.shape({
address: PropTypes.string.isRequired,
}).isRequired,
})).isRequired,
}).isRequired,
notionalValue: PropTypes.number,
@@ -26,7 +26,7 @@ class NewLoanHandler extends PureComponent {
id,
notional,
} = newLoan;
if (notional === expectedNotional) {
if (notional.id === expectedNotional) {
onReceiveNewLoan(id);
}
}
@@ -41,7 +41,9 @@ NewLoanHandler.propTypes = {
expectedNotional: PropTypes.string,
newLoan: PropTypes.shape({
id: PropTypes.string.isRequired,
notional: PropTypes.string.isRequired,
notional: PropTypes.shape({
id: PropTypes.string.isRequired,
}).isRequired,
}),
onReceiveNewLoan: PropTypes.func,
};
@@ -26,8 +26,9 @@ const BorrowerActions = ({
case loanStatus('awaiting_settlement'): {
const {
viewRequests,
lenderAccess,
} = loan;
if (!viewRequests || !viewRequests.length) {
if (!viewRequests.length) {
return (
<Block
padding="xs 0"
@@ -41,9 +42,7 @@ const BorrowerActions = ({
);
}

const hasUnapprovedRequests = viewRequests.some(({
sharedSecret,
}) => !sharedSecret);
const hasUnapprovedRequests = viewRequests.length !== lenderAccess.length;
return (
<ApproveViewRequestModal
loan={loan}
@@ -161,7 +160,8 @@ const BorrowerActions = ({
BorrowerActions.propTypes = {
loan: PropTypes.shape({
id: PropTypes.string.isRequired,
viewRequests: PropTypes.arrayOf(PropTypes.object),
viewRequests: PropTypes.array.isRequired,
lenderAccess: PropTypes.array.isRequired,
}).isRequired,
notionalValue: PropTypes.number,
balanceValue: PropTypes.number,
@@ -40,12 +40,11 @@ const BorrowerInfo = ({
case loanStatus('awaiting_settlement'): {
const {
viewRequests,
lenderAccess,
} = loan;
const totalRequests = (viewRequests && viewRequests.length) || 0;
const unapprovedRequests = viewRequests
&& viewRequests.filter(({
sharedSecret,
}) => !sharedSecret).length;
const totalRequests = viewRequests.length;
const approvedRequests = lenderAccess.length;
const unapprovedRequests = totalRequests - approvedRequests;
infoCols.push(
<LoanTermCol
key="received-requests"
@@ -129,7 +128,8 @@ BorrowerInfo.propTypes = {
lender: PropTypes.shape({
address: PropTypes.string.isRequired,
}),
viewRequests: PropTypes.array,
viewRequests: PropTypes.array.isRequired,
lenderAccess: PropTypes.array.isRequired,
}).isRequired,
notionalValue: PropTypes.number,
balanceValue: PropTypes.number,
@@ -18,7 +18,6 @@ import {
displayedDateTimeFormat,
} from '../../config/datetime';
import loanStatus from '../../utils/loanStatus';
import isSameAddress from '../../utils/isSameAddress';
import replaceValue from '../../utils/replaceValue';
import {
decryptNoteValue,
@@ -55,20 +54,12 @@ class Loan extends PureComponent {
balance,
} = prevState;
const {
viewRequest,
borrower,
balance: balanceNoteData,
role,
notionalNote,
balanceNote,
} = loan;

const role = !currentAddress
? ''
: (isSameAddress(currentAddress, borrower.address)
? 'borrower'
: 'lender');

const notionalViewingKey = role === 'borrower'
? loan.viewingKey
: (viewRequest && viewRequest.sharedSecret) || '';
const notionalViewingKey = notionalNote.sharedSecret;
notional = replaceValue(
notional,
'encryptedViewingKey',
@@ -87,7 +78,7 @@ class Loan extends PureComponent {
} = balance;
const {
sharedSecret: balanceSharedSecret,
} = balanceNoteData || {};
} = balanceNote || {};
if (balanceSharedSecret !== prevBalanceViewingKey) {
balance = replaceValue(
balance,
@@ -405,8 +396,10 @@ Loan.propTypes = {
lender: PropTypes.shape({
address: PropTypes.string.isRequired,
}),
viewingKey: PropTypes.string,
notional: PropTypes.string.isRequired,
notionalNote: PropTypes.shape({
id: PropTypes.string.isRequired,
sharedSecret: PropTypes.string,
}).isRequired,
interestRate: PropTypes.number.isRequired,
interestPeriod: PropTypes.number.isRequired,
loanDuration: PropTypes.number.isRequired,
@@ -415,10 +408,7 @@ Loan.propTypes = {
status: PropTypes.string.isRequired,
isLoading: PropTypes.bool,
repaidAt: PropTypes.number,
viewRequest: PropTypes.shape({
sharedSecret: PropTypes.string,
}),
balance: PropTypes.shape({
balanceNote: PropTypes.shape({
id: PropTypes.string.isRequired,
sharedSecret: PropTypes.string,
}),
@@ -69,8 +69,8 @@ class MarkDefaultModal extends PureComponent {
const {
interestRate,
interestPeriod,
viewRequest,
balance,
notionalNote,
balanceNote,
borrower,
settledAt,
maturity,
@@ -86,8 +86,8 @@ class MarkDefaultModal extends PureComponent {
borrower,
settledAt,
maturity,
notionalSharedSecret: viewRequest.sharedSecret,
balanceSharedSecret: balance.sharedSecret,
notionalSharedSecret: notionalNote.sharedSecret,
balanceSharedSecret: balanceNote.sharedSecret,
});

this.setState({
@@ -202,6 +202,12 @@ MarkDefaultModal.propTypes = {
loanDuration: PropTypes.number.isRequired,
maturity: PropTypes.number,
settlementCurrencyId: PropTypes.string.isRequired,
notionalNote: PropTypes.shape({
sharedSecret: PropTypes.string.isRequired,
}).isRequired,
balanceNote: PropTypes.shape({
sharedSecret: PropTypes.string,
}),
}).isRequired,
notionalValue: PropTypes.number,
payableInterest: PropTypes.number,

0 comments on commit 533e9b0

Please sign in to comment.
You can’t perform that action at this time.