diff --git a/package.json b/package.json index 6cbb24b9..6f284753 100755 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "html-webpack-plugin": "3.2.0", "husky": "2.1.0", "jdenticon": "2.1.0", + "js-lnurl": "^0.2.3", "less": "^3.7.1", "less-loader": "^4.1.0", "mini-css-extract-plugin": "^0.4.2", diff --git a/src/app/PromptRoutes.tsx b/src/app/PromptRoutes.tsx index 67ed3423..16ecde87 100644 --- a/src/app/PromptRoutes.tsx +++ b/src/app/PromptRoutes.tsx @@ -6,6 +6,7 @@ import Loader from 'components/Loader'; import OnboardingPrompt from 'prompts/onboarding'; import AuthorizePrompt from 'prompts/authorize'; import PaymentPrompt from 'prompts/payment'; +import LnurlPayPrompt from 'prompts/lnurl'; import InvoicePrompt from 'prompts/invoice'; import SignPrompt from 'prompts/sign'; import VerifyPrompt from 'prompts/verify'; @@ -39,6 +40,7 @@ class Routes extends React.Component { + diff --git a/src/app/components/AmountField/index.tsx b/src/app/components/AmountField/index.tsx index 373aabef..af90eae9 100644 --- a/src/app/components/AmountField/index.tsx +++ b/src/app/components/AmountField/index.tsx @@ -186,7 +186,7 @@ class AmountField extends React.Component { } if (minimumSats) { const min = new BN(minimumSats); - if (min.gte(valueBN)) { + if (min.gt(valueBN)) { const minAmount = `${fromBaseToUnit(min.toString(), denomination)} ${ denominationSymbols[chain][denomination] }`; diff --git a/src/app/components/PaymentRequest/index.tsx b/src/app/components/PaymentRequest/index.tsx new file mode 100644 index 00000000..9428918b --- /dev/null +++ b/src/app/components/PaymentRequest/index.tsx @@ -0,0 +1,140 @@ +import React from 'react'; +import { Alert } from 'antd'; +import './style.less'; + +import Identicon from 'components/Identicon'; +import Unit from 'components/Unit'; +import { unixMoment, SHORT_FORMAT } from 'utils/time'; +import moment from 'moment'; +import Loader from 'components/Loader'; +import { PaymentRequestData } from 'modules/payment/types'; + +import { PaymentRequestState } from 'modules/payment/types'; + +interface Props { + routedRequest: PaymentRequestData | null; + requestData: PaymentRequestState; +} + +interface State { + showMoreInfo: boolean; +} + +const INITIAL_STATE = { + showMoreInfo: false, +}; + +export default class PaymentRequest extends React.Component { + state: State = INITIAL_STATE; + + render() { + const { showMoreInfo } = this.state; + const { requestData, routedRequest } = this.props; + + const expiry = + requestData.data && + unixMoment(requestData.data.request.timestamp).add( + requestData.data.request.expiry, + 'seconds', + ); + + return ( +
+ {routedRequest ? ( +
+
+ +
+
+ {routedRequest.node.alias} +
+ + {routedRequest.node.pub_key} + +
+
+ {routedRequest.route ? ( +
+ + + {routedRequest.request.num_satoshis && ( + + + + + )} + + + + + + + + + {showMoreInfo && ( + <> + + + + + + + + + + + + + + )} + +
Amount + +
Fee + {requestData.isLoading ? ( + + ) : ( + + )} +
Total + {requestData.isLoading ? ( + + ) : ( + + )} +
Hops{routedRequest.route.hops.length} node(s)
Time lock + {moment() + .add(routedRequest.route.total_time_lock, 'seconds') + .fromNow(true)} +
Expires{expiry && expiry.format(SHORT_FORMAT)}
+ {!showMoreInfo && ( + this.setState({ showMoreInfo: true })} + > + More info + + )} +
+ ) : ( + + )} +
+ ) : ( + '' + )} +
+ ); + } +} diff --git a/src/app/components/PaymentRequest/style.less b/src/app/components/PaymentRequest/style.less new file mode 100644 index 00000000..399d03c0 --- /dev/null +++ b/src/app/components/PaymentRequest/style.less @@ -0,0 +1,133 @@ +@import '~style/variables.less'; + +.PaymentRequest { + &-pr { + margin-bottom: 1rem; + + &-input { + font-family: @code-family; + font-size: 0.65rem; + resize: none; + } + + &-qr { + position: absolute; + bottom: 0; + right: 10px; + } + } + + &-payment { + margin-bottom: 0.5rem; + + &-node { + display: flex; + padding: 0.6rem; + margin-bottom: 0.5rem; + + &-avatar { + flex-shrink: 0; + width: 2.8rem; + height: 2.8rem; + margin-right: 0.75rem; + } + + &-info { + flex: 1; + min-width: 0; + + &-alias { + font-size: 1.1rem; + font-weight: 500; + margin-bottom: 0.1rem; + } + + &-pubkey { + display: block; + font-size: 0.8rem; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } + } + } + + &-value { + padding: 0 1rem; + margin-bottom: 0.5rem; + + // Ant overrides + .ant-input-group.ant-input-group-compact { + display: flex; + } + + .ant-select-selection-selected-value { + font-size: 0.85rem; + } + } + + &-details { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + table { + max-width: 280px; + width: 100%; + margin-bottom: 0.5rem; + + tr { + border-bottom: 1px solid rgba(#000, 0.05); + + &:last-child { + border: none; + } + + td { + padding: 0.2rem; + + &:first-child { + text-align: left; + min-width: 80px; + padding-right: 0.5rem; + } + + &:last-child { + text-align: right; + font-weight: 500; + } + } + } + } + + &-more { + padding: 0.5rem 0; + } + } + } + + &-placeholder { + display: flex; + justify-content: center; + align-items: center; + height: 8rem; + margin-bottom: 1rem; + color: rgba(#000, 0.2); + border: 2px dashed rgba(#000, 0.08); + border-radius: 4px; + } + + &-buttons { + display: flex; + + .ant-btn { + &:first-child { + margin-right: 0.5rem; + } + &:last-child { + flex: 1; + } + } + } +} diff --git a/src/app/prompts/lnurl.less b/src/app/prompts/lnurl.less new file mode 100644 index 00000000..74dd3c1c --- /dev/null +++ b/src/app/prompts/lnurl.less @@ -0,0 +1,87 @@ +@import '~style/variables.less'; + +.LnurlPayPrompt { + // Default ant overrides, overridden below + .ant-form-item-label label { + font-size: 0.85rem; + text-transform: uppercase; + font-weight: bold; + letter-spacing: 0.1rem; + color: @text-color-secondary; + } + + &-metadata { + margin: 0; + padding: 0; + + &-item { + margin: 0; + padding: 0; + } + } + + &-buttons { + display: flex; + + .ant-btn { + &:first-child { + margin-right: 0.5rem; + } + &:last-child { + flex: 1; + } + } + } + + &-header { + display: flex; + align-items: center; + padding: 1rem; + margin-bottom: 1rem; + background: #fff; + border-bottom: 1px solid #eee; + + &-icon { + img { + width: 40px; + height: 40px; + } + } + + &-title { + font-size: 1.15rem; + margin: 0 0 0 1rem; + } + } + + &-form { + padding: 1rem; + + &-value { + // Ant overrides + .ant-input-group.ant-input-group-compact { + display: flex; + } + + .ant-select-selection-selected-value { + font-size: 0.85rem; + } + + .ant-form-explain { + display: flex; + font-size: 0.75rem; + margin: 0.2rem 0 1rem; + + > span { + margin-right: 0.5rem; + } + + > .is-fiat { + flex: 1; + text-align: right; + font-size: 0.85rem; + } + } + } + } +} diff --git a/src/app/prompts/lnurl.tsx b/src/app/prompts/lnurl.tsx new file mode 100644 index 00000000..e2fb61cd --- /dev/null +++ b/src/app/prompts/lnurl.tsx @@ -0,0 +1,327 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PromptTemplate from 'components/PromptTemplate'; +import { + getPromptArgs, + getPromptOrigin, + watchUntilPropChange, + OriginData, +} from 'utils/prompt'; +import { removeDomainPrefix } from 'utils/formatters'; +import { AppState } from 'store/reducers'; +import { getNodeChain } from 'modules/node/selectors'; +import { getChainRates } from 'modules/rates/selectors'; +import { PaymentRequestData } from 'modules/payment/types'; +import { + sendPayment, + resetSendPayment, + checkPaymentRequest, +} from 'modules/payment/actions'; +import AmountField from 'components/AmountField'; +import PaymentRequest from 'components/PaymentRequest'; +import { Denomination } from 'utils/constants'; +import { fromBaseToUnit, fromUnitToBase } from 'utils/units'; +import { Form, Button, Result } from 'antd'; +import Loader from 'components/Loader'; +import './lnurl.less'; +import { getParams as getlnurlParams, LNURLPayParams } from 'js-lnurl'; +import { rejectPrompt, confirmPrompt } from 'utils/prompt'; + +interface StateProps { + paymentRequests: AppState['payment']['paymentRequests']; + sendLightningReceipt: AppState['payment']['sendLightningReceipt']; + isSending: AppState['payment']['isSending']; + sendError: AppState['payment']['sendError']; + denomination: AppState['settings']['denomination']; + fiat: AppState['settings']['fiat']; + isNoFiat: AppState['settings']['isNoFiat']; + rates: ReturnType; + chain: ReturnType; +} + +interface DispatchProps { + sendPayment: typeof sendPayment; + resetSendPayment: typeof resetSendPayment; + checkPaymentRequest: typeof checkPaymentRequest; +} + +type Props = StateProps & DispatchProps; + +interface State { + lnurlParams: LNURLPayParams; + isLoading: boolean; + value: string; + denomination: Denomination; + routedRequest: PaymentRequestData | null; + paymentRequestValue: string; + showMoreInfo: boolean; +} + +const INITIAL_STATE = { + lnurlParams: {} as LNURLPayParams, + isLoading: true, + value: '', + denomination: Denomination.SATOSHIS, + paymentRequestValue: '', + routedRequest: null, + showMoreInfo: false, +}; + +interface LnurlArgs { + lnurl: string; +} + +const notNilNum = (v: string | number | null | undefined): v is string | number => + !!v || v === 0; + +class LnurlPayPrompt extends React.Component { + private args: LnurlArgs; + private origin: OriginData; + + constructor(props: Props) { + super(props); + const args = getPromptArgs(); + this.args = args; + this.origin = getPromptOrigin(); + this.state = INITIAL_STATE; + } + + componentDidMount() { + getlnurlParams(this.args.lnurl).then((params: any) => { + const valueSats = + params.minSendable === params.maxSendable ? params.maxSendable : null; + const denomination = Denomination.SATOSHIS; + const value = notNilNum(valueSats) + ? fromBaseToUnit(valueSats.toString(), denomination).toString() + : ''; + this.setState({ + lnurlParams: params, + isLoading: false, + value, + denomination, + }); + }); + } + + componentWillUnmount() { + this.props.resetSendPayment(); + } + + componentWillUpdate(nextProps: Props) { + const { paymentRequestValue } = this.state; + const oldPr = this.props.paymentRequests[paymentRequestValue]; + const newPr = nextProps.paymentRequests[paymentRequestValue]; + + if (newPr && newPr.data && newPr !== oldPr) { + this.setState({ routedRequest: newPr.data }); + } + } + + render() { + // Early exit for send state + const { sendLightningReceipt, isSending, sendError } = this.props; + if (isSending) { + return ; + } else if (sendLightningReceipt || sendError) { + const type = sendError ? 'error' : 'success'; + const closeButton = ( + + ); + return ( +
+ + {sendError || + (sendLightningReceipt && ( + <> +

Pre-image

+ {sendLightningReceipt.payment_preimage} + + ))} +
+
+ ); + } + + // not in sending state + + const { value, routedRequest, lnurlParams, paymentRequestValue } = this.state; + const requestData = this.props.paymentRequests[paymentRequestValue] || {}; + const isSubmitDisabled = + this.state.isLoading || + !notNilNum(this.state.value) || + (routedRequest !== null && routedRequest.route === null); + const isValueDisabled = + lnurlParams && lnurlParams.maxSendable === lnurlParams.minSendable; + + return ( + +
+
+
+ +
+

+ Pay request from {removeDomainPrefix(this.origin.domain)} +

+
+
+ {this.state.isLoading ? ( + + ) : ( +
+ + {this.renderMetadata()} + + {routedRequest ? ( + + ) : ( + + )} + +
+ + +
+
+ )} + +
+
+ ); + } + + private handleClose = () => { + return confirmPrompt(); + }; + + private handleReject = () => { + return rejectPrompt(); + }; + + private renderMetadata = () => { + const text: string = this.state.lnurlParams.decodedMetadata + .filter(([typ, _]: any) => typ === 'text/plain') + .map(([_, content]: any) => content)[0]; + + const image: string = this.state.lnurlParams.decodedMetadata + .filter(([typ, _]: any) => typ.slice(0, 6) === 'image/') + .map(([typ, content]: any) => `data:${typ},${content}`)[0]; + + return ( +
+ {image ? : null} +

{text}

+
+ ); + }; + + private handleChangeValue = (value: string) => { + this.setState({ value }); + }; + + private handleSubmit = async () => { + if (this.state.paymentRequestValue !== '') { + return this.handleSend(); + } else { + return this.getPaymentRequest(); + } + }; + + private handleSend = async () => { + const value = fromUnitToBase(this.state.value, this.state.denomination); + this.props.sendPayment({ + payment_request: this.state.paymentRequestValue, + amt: value, + }); + + const receipt = await watchUntilPropChange( + () => this.props.sendLightningReceipt, + () => this.props.sendError, + ); + + if (!receipt) { + throw new Error('Payment failed to send'); + } + return { preimage: receipt.payment_preimage }; + }; + + private getPaymentRequest = async () => { + const { lnurlParams } = this.state; + const value = fromUnitToBase(this.state.value, this.state.denomination); + + const callbackUrl = new URL(lnurlParams.callback); + const queryParams = new URLSearchParams(callbackUrl.search.slice(1)); + queryParams.append('amount', value); + callbackUrl.search = queryParams.toString(); + + const r = await fetch(callbackUrl.toString()); + if (r.status >= 300) { + throw new Error(await r.text()); + } + const res = await r.json(); + + if (res.status === 'ERROR') { + throw new Error(res.reason); + } + const paymentRequestValue = res.pr; + this.setState( + { + paymentRequestValue, + showMoreInfo: false, + }, + () => { + if (paymentRequestValue) { + this.props.checkPaymentRequest(paymentRequestValue); + } + }, + ); + }; +} + +export default connect( + state => ({ + paymentRequests: state.payment.paymentRequests, + sendLightningReceipt: state.payment.sendLightningReceipt, + isSending: state.payment.isSending, + sendError: state.payment.sendError, + denomination: state.settings.denomination, + fiat: state.settings.fiat, + isNoFiat: state.settings.isNoFiat, + rates: getChainRates(state), + chain: getNodeChain(state), + }), + { sendPayment, resetSendPayment, checkPaymentRequest }, +)(LnurlPayPrompt); diff --git a/src/content_script/index.ts b/src/content_script/index.ts index 4c67c98b..3164a041 100755 --- a/src/content_script/index.ts +++ b/src/content_script/index.ts @@ -54,13 +54,22 @@ if (document) { const lightningLink = target.closest('[href^="lightning:"]'); if (lightningLink) { const href = lightningLink.getAttribute('href') as string; - const paymentRequest = href.replace('lightning:', ''); + const request = href.replace('lightning:', ''); + let promptType = null; + let args = null; + if (request.toLowerCase().startsWith('lnurl')) { + promptType = PROMPT_TYPE.LNURL; + args = { lnurl: request }; + } else { + promptType = PROMPT_TYPE.PAYMENT; + args = { paymentRequest: request }; + } browser.runtime.sendMessage({ application: 'Joule', prompt: true, - type: PROMPT_TYPE.PAYMENT, + type: promptType, origin: getOriginData(), - args: { paymentRequest }, + args, }); ev.preventDefault(); } diff --git a/src/webln/types.ts b/src/webln/types.ts index c0f0b1ad..57aa814c 100644 --- a/src/webln/types.ts +++ b/src/webln/types.ts @@ -2,6 +2,7 @@ export enum PROMPT_TYPE { AUTHORIZE = 'AUTHORIZE', INFO = 'INFO', PAYMENT = 'PAYMENT', + LNURL = 'LNURL', INVOICE = 'INVOICE', SIGN = 'SIGN', VERIFY = 'VERIFY', diff --git a/yarn.lock b/yarn.lock index ab48ee24..7d2583b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -808,11 +808,21 @@ "@svgr/core" "^3.1.0" loader-utils "^1.1.0" +"@types/aes-js@^3.1.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@types/aes-js/-/aes-js-3.1.1.tgz#34b3978122310c135de4b377270d1d65676fae28" + integrity sha512-SDSGgXT3LRCH6qMWk8OHT1vLSVNuHNvCpKCx2/TYtQMbMGGgxJC9fspwSkQjqzRagrWnCrxuLL3jMNXLXHHvSw== + "@types/anymatch@*": version "1.3.1" resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== +"@types/base64-js@^1.2.5": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@types/base64-js/-/base64-js-1.3.0.tgz#c939fdba49846861caf5a246b165dbf5698a317c" + integrity sha512-ZmI0sZGAUNXUfMWboWwi4LcfpoVUYldyN6Oe0oJ5cCsHDU/LlRq8nQKPXhYLOx36QYSW9bNIb1vvRrD6K7Llgw== + "@types/bn.js@4.11.2": version "4.11.2" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.2.tgz#3179b977b9d18aa81f256cae7556282ee99fd1cc" @@ -1226,6 +1236,11 @@ add-dom-event-listener@^1.1.0: dependencies: object-assign "4.x" +aes-js@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" + integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ== + ajv-errors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" @@ -1595,7 +1610,7 @@ base-64@0.1.0: resolved "https://registry.yarnpkg.com/base-64/-/base-64-0.1.0.tgz#780a99c84e7d600260361511c4877613bf24f6bb" integrity sha1-eAqZyE59YAJgNhURxId2E78k9rs= -base64-js@^1.0.2: +base64-js@^1.0.2, base64-js@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== @@ -1625,6 +1640,11 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bech32@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" + integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== + big.js@^3.1.3: version "3.2.0" resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" @@ -1828,6 +1848,14 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786" + integrity sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -2034,9 +2062,9 @@ classnames@2.x, classnames@^2.2.0, classnames@^2.2.1, classnames@^2.2.3, classna integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== clean-css@4.2.x: - version "4.2.1" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17" - integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g== + version "4.2.3" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78" + integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA== dependencies: source-map "~0.6.0" @@ -2090,7 +2118,7 @@ clone@2.1.1: resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb" integrity sha1-0hfR6WERjjrJpLi7oyhVU79kfNs= -clone@^2.1.1, clone@^2.1.2: +clone@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= @@ -2399,6 +2427,13 @@ cross-env@5.2.0: cross-spawn "^6.0.5" is-windows "^1.0.0" +cross-fetch@^3.0.4: + version "3.0.5" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.5.tgz#2739d2981892e7ab488a7ad03b92df2816e03f4c" + integrity sha512-FFLcLtraisj5eteosnX1gf01qYDCOc4fDy0+euOt8Kn9YBY2NtXL/pCoYPavw24NIQkQqm5ZOLsGD5Zzj0gyew== + dependencies: + node-fetch "2.6.0" + cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -3852,16 +3887,16 @@ he@1.2.x: integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== history@^4.7.2: - version "4.9.0" - resolved "https://registry.yarnpkg.com/history/-/history-4.9.0.tgz#84587c2068039ead8af769e9d6a6860a14fa1bca" - integrity sha512-H2DkjCjXf0Op9OAr6nJ56fcRkTSNrUiv41vNJ6IswJjif6wlpZK0BTfFbi7qK9dXLSYZxkq5lBsj3vUjlYBYZA== + version "4.10.1" + resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" + integrity sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew== dependencies: "@babel/runtime" "^7.1.2" loose-envify "^1.2.0" - resolve-pathname "^2.2.0" + resolve-pathname "^3.0.0" tiny-invariant "^1.0.2" tiny-warning "^1.0.0" - value-equal "^0.4.0" + value-equal "^1.0.1" hmac-drbg@^1.0.0: version "1.0.1" @@ -3877,7 +3912,7 @@ hoist-non-react-statics@^2.3.1, hoist-non-react-statics@^2.5.0: resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw== -hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0: +hoist-non-react-statics@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b" integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA== @@ -4503,6 +4538,21 @@ js-levenshtein@^1.1.3: resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== +js-lnurl@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/js-lnurl/-/js-lnurl-0.2.3.tgz#1832598596f339e504283f69f48fa3eebbd0810a" + integrity sha512-FUQG23EnyoPbVn6/+PZ5ZcezBIaa7I5++8UR5eb4fM8tlgcq4N/56FxPK0zO+qiIYNWrbdn2mwVBKUaTlEQtZw== + dependencies: + "@types/aes-js" "^3.1.1" + "@types/base64-js" "^1.2.5" + aes-js "^3.1.2" + base64-js "^1.3.1" + bech32 "^1.1.4" + buffer "^5.6.0" + cross-fetch "^3.0.4" + query-string "^6.12.1" + safe-buffer "^5.2.1" + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -4704,19 +4754,18 @@ less-loader@^4.1.0: pify "^3.0.0" less@^3.7.1: - version "3.10.3" - resolved "https://registry.yarnpkg.com/less/-/less-3.10.3.tgz#417a0975d5eeecc52cff4bcfa3c09d35781e6792" - integrity sha512-vz32vqfgmoxF1h3K4J+yKCtajH0PWmjkIFgbs5d78E/c/e+UQTnI+lWK+1eQRE95PXM2mC3rJlLSSP9VQHnaow== + version "3.12.2" + resolved "https://registry.yarnpkg.com/less/-/less-3.12.2.tgz#157e6dd32a68869df8859314ad38e70211af3ab4" + integrity sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q== dependencies: - clone "^2.1.2" + tslib "^1.10.0" optionalDependencies: errno "^0.1.1" graceful-fs "^4.1.2" image-size "~0.5.0" + make-dir "^2.1.0" mime "^1.4.1" - mkdirp "^0.5.0" - promise "^7.1.1" - request "^2.83.0" + native-request "^1.0.5" source-map "~0.6.0" levn@~0.3.0: @@ -4839,11 +4888,16 @@ lodash.throttle@^4.0.0: resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4" integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ= -lodash@^4.0.0, lodash@^4.16.5, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@~4.17.10: +lodash@^4.0.0, lodash@^4.16.5, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.2.1, lodash@~4.17.10: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +lodash@^4.17.3: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + loglevel@^1.4.1: version "1.6.4" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.4.tgz#f408f4f006db8354d0577dcf6d33485b3cb90d56" @@ -5176,11 +5230,16 @@ mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: dependencies: minimist "0.0.8" -moment@2.x, moment@^2.22.1, moment@^2.22.2, moment@^2.24.0: +moment@2.x, moment@^2.22.1, moment@^2.24.0: version "2.24.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== +moment@^2.22.2: + version "2.27.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d" + integrity sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ== + move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -5268,6 +5327,11 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" +native-request@^1.0.5: + version "1.0.7" + resolved "https://registry.yarnpkg.com/native-request/-/native-request-1.0.7.tgz#ff742dc555b4c8f2f1c14b548639ba174e573856" + integrity sha512-9nRjinI9bmz+S7dgNtf4A70+/vPhnd+2krGpy4SUlADuOuSa24IDkNaZ+R/QT1wQ6S8jBdi6wE7fLekFZNfUpQ== + needle@^2.2.1: version "2.4.0" resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" @@ -5299,6 +5363,11 @@ no-case@^2.2.0: dependencies: lower-case "^1.1.1" +node-fetch@2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" + integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + node-fetch@^1.0.1: version "1.7.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" @@ -5383,9 +5452,9 @@ node-releases@^1.1.29: semver "^5.3.0" node-sass@^4.9.2: - version "4.12.0" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.12.0.tgz#0914f531932380114a30cc5fa4fa63233a25f017" - integrity sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ== + version "4.14.1" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.14.1.tgz#99c87ec2efb7047ed638fb4c9db7f3a42e2217b5" + integrity sha512-sjCuOlvGyCJS40R8BscF5vhVlQjNN069NtQ1gSxyK1u9iqvn6tf7O1R4GNowVZfiZUCRt5MmMs1xd+4V/7Yr0g== dependencies: async-foreach "^0.1.3" chalk "^1.1.1" @@ -5394,14 +5463,14 @@ node-sass@^4.9.2: get-stdin "^4.0.1" glob "^7.0.3" in-publish "^2.0.0" - lodash "^4.17.11" + lodash "^4.17.15" meow "^3.7.0" mkdirp "^0.5.1" nan "^2.13.2" node-gyp "^3.8.0" npmlog "^4.0.0" request "^2.88.0" - sass-graph "^2.2.4" + sass-graph "2.2.5" stdout-stream "^1.4.0" "true-case-path" "^1.0.2" @@ -5843,7 +5912,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-is-inside@^1.0.1: +path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= @@ -6208,6 +6277,15 @@ query-string@6.2.0: decode-uri-component "^0.2.0" strict-uri-encode "^2.0.0" +query-string@^6.12.1: + version "6.13.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.13.1.tgz#d913ccfce3b4b3a713989fe6d39466d92e71ccad" + integrity sha512-RfoButmcK+yCta1+FuU8REvisx1oEzhMKwhLUNcepQTPGcNMp1sIqjnfCtfnvGSQZQEhaBHvccujtWoUV3TTbA== + dependencies: + decode-uri-component "^0.2.0" + split-on-first "^1.0.0" + strict-uri-encode "^2.0.0" + querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -6752,14 +6830,14 @@ react-copy-to-clipboard@5.0.1: prop-types "^15.5.8" react-dom@^16.4.0: - version "16.9.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.9.0.tgz#5e65527a5e26f22ae3701131bcccaee9fb0d3962" - integrity sha512-YFT2rxO9hM70ewk9jq0y6sQk8cL02xm4+IzYBz75CQGlClQQ1Bxq0nhHF6OtSbit+AIahujJgb/CPRibFkMNJQ== + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f" + integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.15.0" + scheduler "^0.19.1" react-hot-loader@^4.6.3: version "4.12.12" @@ -6780,7 +6858,12 @@ react-is@^16.12.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q== -react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: +react-is@^16.6.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-is@^16.7.0, react-is@^16.8.1: version "16.9.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb" integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw== @@ -6801,12 +6884,12 @@ react-lifecycles-compat@^3.0.0, react-lifecycles-compat@^3.0.2, react-lifecycles integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== react-redux@^5.0.7: - version "5.1.1" - resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.1.tgz#88e368682c7fa80e34e055cd7ac56f5936b0f52f" - integrity sha512-LE7Ned+cv5qe7tMV5BPYkGQ5Lpg8gzgItK07c67yHvJ8t0iaD9kPFPAli/mYkiyJYrs2pJgExR2ZgsGqlrOApg== + version "5.1.2" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-5.1.2.tgz#b19cf9e21d694422727bf798e934a916c4080f57" + integrity sha512-Ns1G0XXc8hDyH/OcBHOxNgQx9ayH3SPxBnFCOidGKSle8pKihysQw2rG/PmciUQRoclhVBO8HMhiRmGXnDja9Q== dependencies: "@babel/runtime" "^7.1.2" - hoist-non-react-statics "^3.1.0" + hoist-non-react-statics "^3.3.0" invariant "^2.2.4" loose-envify "^1.1.0" prop-types "^15.6.1" @@ -6850,9 +6933,9 @@ react-slick@~0.25.2: resize-observer-polyfill "^1.5.0" react@^16.4.0: - version "16.9.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.9.0.tgz#40ba2f9af13bc1a38d75dbf2f4359a5185c4f7aa" - integrity sha512-+7LQnFBwkiw+BobzOF6N//BdoNw0ouwmSJTEm9cglOOmsg/TMiFHZLe2sEoN5M7LgJTj9oHH0gxklfnQe66S1w== + version "16.13.1" + resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" + integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -7147,7 +7230,7 @@ request-promise-native@^1.0.7: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.83.0, request@^2.87.0, request@^2.88.0: +request@^2.87.0, request@^2.88.0: version "2.88.0" resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== @@ -7302,6 +7385,11 @@ safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== +safe-buffer@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + safe-regex@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" @@ -7720,6 +7808,11 @@ spdy@^4.0.0: select-hose "^2.0.0" spdy-transport "^3.0.0" +split-on-first@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f" + integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw== + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -8210,6 +8303,11 @@ tslib@1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.0.tgz#e37a86fda8cbbaf23a057f473c9f4dc64e5fc2e8" integrity sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ== +tslib@^1.10.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== + tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" @@ -8507,11 +8605,16 @@ uuid@3.2.1: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" integrity sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA== -uuid@^3.0.1, uuid@^3.3.2: +uuid@^3.3.2: version "3.3.3" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== +uuid@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + v8-compile-cache@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe" @@ -8721,6 +8824,13 @@ webpack@4.39.3: watchpack "^1.6.0" webpack-sources "^1.4.1" +websocket-driver@0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" + integrity sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY= + dependencies: + websocket-extensions ">=0.1.1" + websocket-driver@>=0.5.1: version "0.7.3" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.3.tgz#a2d4e0d4f4f116f1e6297eba58b05d430100e9f9"