Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix walletconnect double qr code updated #444

Merged
merged 6 commits into from
May 4, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@
"@web3-react/types": "8.0.7-beta.0",
"@web3-react/url": "8.0.9-beta.0",
"@web3-react/walletconnect": "8.0.15-beta.0",
"@web3-react/walletconnect-connector": "6.2.4",
"@web3-react/walletconnect-connector": "^7.0.2-alpha.0",
"@web3-react/walletlink-connector": "^6.2.5",
"ajv": "^6.12.3",
"bnc-sdk": "^3.5.0",
Expand Down
13 changes: 12 additions & 1 deletion src/custom/components/WalletModal/WalletModalMod.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import PendingView from 'components/WalletModal/PendingView'
// MOD imports
import ModalMod from '@src/components/Modal'

import { SupportedChainId } from 'constants/chains'

export const CloseIcon = styled.div`
position: absolute;
right: 1rem;
Expand Down Expand Up @@ -224,7 +226,7 @@ export default function WalletModal({
setWalletView(WALLET_VIEWS.PENDING)

// if the connector is walletconnect and the user has already tried to connect, manually reset the connector
if (connector instanceof WalletConnectConnector && connector.walletConnectProvider?.wc?.uri) {
if (connector instanceof WalletConnectConnector) {
connector.walletConnectProvider = undefined
}

Expand All @@ -234,6 +236,15 @@ export default function WalletModal({
// const walletAddress = await connector.getAccount()
// logMonitoringEvent({ walletAddress })
// })
.then(() => {
// Manually set the WalletConnectConnector http.connection.url to currently connected network url
// Fix for this https://github.com/gnosis/cowswap/issues/1930
if (connector instanceof WalletConnectConnector) {
Copy link
Contributor

@W3stside W3stside Apr 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this also be connector.isWalletConnect?

const { http, rpc, signer } = connector.walletConnectProvider
const chainId = signer.connection.chainId || SupportedChainId.MAINNET
http.connection.url = rpc.custom[chainId]
}
})
.catch((error) => {
if (error instanceof UnsupportedChainIdError) {
activate(connector) // a little janky...can't use setError because the connector isn't set
Expand Down
13 changes: 6 additions & 7 deletions src/custom/hooks/useWalletInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ function checkIsSupportedWallet(params: {
async function getWcPeerMetadata(connector: WalletConnectConnector): Promise<{ walletName?: string; icon?: string }> {
const provider = (await connector.getProvider()) as WalletConnectProvider

const meta = provider.walletMeta
// fix for this https://github.com/gnosis/cowswap/issues/1929
const meta = provider.walletMeta || provider.signer.connection.wc.peerMeta
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cant this just be provider.peerMeta? why the long chain in signer?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also from Nenad's original PR.
Anyway, I'll update with your suggestion

if (meta) {
return {
walletName: meta.name,
Expand Down Expand Up @@ -96,12 +97,10 @@ export function useWalletInfo(): ConnectedWalletInfo {
const walletType = getProviderType(connector)
switch (walletType) {
case WalletProvider.WALLET_CONNECT:
if (connector instanceof WalletConnectConnector) {
getWcPeerMetadata(connector).then(({ walletName, icon }) => {
setWalletName(walletName)
setIcon(icon)
})
}
getWcPeerMetadata(connector as WalletConnectConnector).then(({ walletName, icon }) => {
setWalletName(walletName)
setIcon(icon)
})
break
case WalletProvider.GNOSIS_SAFE:
setWalletName(GNOSIS_SAFE_APP_NAME)
Expand Down
37 changes: 28 additions & 9 deletions src/custom/hooks/web3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import { isMobile } from 'utils/userAgent'
// MOD imports
import { STORAGE_KEY_LAST_PROVIDER, WAITING_TIME_RECONNECT_LAST_PROVIDER } from 'constants/index'
import { AbstractConnector } from '@web3-react/abstract-connector'
import { WalletConnectConnector } from '@web3-react/walletconnect-connector'

// exports from the original file
// Exports from the original file
export { useInactiveListener } from '@src/hooks/web3'
export { default as useActiveWeb3React } from 'hooks/useActiveWeb3React'

Expand All @@ -19,16 +20,15 @@ enum DefaultProvidersInjected {
COINBASE_WALLET = WalletProvider.WALLET_LINK,
}

// TODO: original from uniswap has gnosis-safe connection details, could be re-used
export function useEagerConnect() {
const { activate, active, connector } = useWeb3React()
const [tried, setTried] = useState(false)

// gnosisSafe.isSafeApp() races a timeout against postMessage, so it delays pageload if we are not in a safe app;
// if we are not embedded in an iframe, it is not worth checking
// GnosisSafe.isSafeApp() races a timeout against postMessage, so it delays pageload if we are not in a safe app;
// If we are not embedded in an iframe, it is not worth checking
const [triedSafe, setTriedSafe] = useState(!IS_IN_IFRAME)

// handle setting/removing wallet provider in local storage
// Handle setting/removing wallet provider in local storage
const handleBeforeUnload = useCallback(() => {
const walletType = getProviderType(connector)

Expand All @@ -41,7 +41,7 @@ export function useEagerConnect() {

const connectInjected = useCallback(
(providerName = DefaultProvidersInjected.METAMASK) => {
// check if the our application is authorized/connected with Metamask
// Check if our application is authorized/connected with Metamask
injected.isAuthorized().then((isAuthorized) => {
if (isAuthorized) {
setDefaultInjected(providerName)
Expand Down Expand Up @@ -88,7 +88,7 @@ export function useEagerConnect() {
if (!active) {
const latestProvider = localStorage.getItem(STORAGE_KEY_LAST_PROVIDER)

// if there is no last saved provider set tried state to true
// If there is no last saved provider set tried state to true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wut

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not my changes.

For context: When reviewing Nenad's original PR, there were comments about the case of the comments.
Keep in mind these were all coming from Uniswap.
Anyway, @nenadV91 was very kind and did abide by the requests.

if (!latestProvider) {
if (!triedSafe) {
// First try to connect using Gnosis Safe
Expand Down Expand Up @@ -130,10 +130,29 @@ export function useEagerConnect() {
}, [active])

useEffect(() => {
// add beforeunload event listener on initial component mount
// Check if current connector is of type WalletConnect
// Fix for this https://github.com/gnosis/cowswap/issues/1923
if (connector instanceof WalletConnectConnector) {
const walletConnect = connector.walletConnectProvider.signer.connection.wc

// Listen on disconnect events directly on WalletConnect client and close the connection
// Important in case the connection is closed from the wallet side after the page is refreshed
walletConnect.on('disconnect', (error: Error) => {
if (error) {
console.error('[WalletConnectConnector] Error during disconnect:', error)
} else {
connector.close()
localStorage.removeItem(STORAGE_KEY_LAST_PROVIDER)
}
})
}
}, [connector])

useEffect(() => {
// Add beforeunload event listener on initial component mount
window.addEventListener('beforeunload', handleBeforeUnload)

// remove beforeunload event listener on component unmount
// Remove beforeunload event listener on component unmount
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this some new grammarnazi lint rule or is this custom? seems very unnecessary

return () => {
window.removeEventListener('beforeunload', handleBeforeUnload)
}
Expand Down
10 changes: 9 additions & 1 deletion src/custom/utils/signatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import { registerOnWindow } from 'utils/misc'
// - https://www.jsonrpc.org/specification#error_object
const METAMASK_SIGNATURE_ERROR_CODE = -32603
const METHOD_NOT_FOUND_ERROR_CODE = -32601
// Added the following because of 1Inch walet who doesn't send the error code
// So we will check the actual error text
const METHOD_NOT_FOUND_ERROR_MSG_REGEX = /Method not found/i
const V4_ERROR_MSG_REGEX = /eth_signTypedData_v4 does not exist/i
const V3_ERROR_MSG_REGEX = /eth_signTypedData_v3 does not exist/i
const RPC_REQUEST_FAILED_REGEX = /RPC request failed/i
Expand Down Expand Up @@ -159,7 +162,12 @@ async function _signPayload(
try {
signature = (await signFn({ ...payload, signer: _signer, signingScheme })) as EcdsaSignature // Only ECDSA signing supported for now
} catch (e) {
if (e.code === METHOD_NOT_FOUND_ERROR_CODE || RPC_REQUEST_FAILED_REGEX.test(e.message)) {
const regexErrorCheck = [METHOD_NOT_FOUND_ERROR_MSG_REGEX, RPC_REQUEST_FAILED_REGEX].some((regex) =>
// for example 1Inch error doesn't have e.message so we will check the output of toString()
[e.message, e.toString()].some((msg) => regex.test(msg))
)

if (e.code === METHOD_NOT_FOUND_ERROR_CODE || regexErrorCheck) {
// Maybe the wallet returns the proper error code? We can only hope 🤞
// OR it failed with a generic message, there's no error code set, and we also hope it'll work
// with other methods...
Expand Down
130 changes: 99 additions & 31 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5504,6 +5504,27 @@
"@walletconnect/window-metadata" "1.0.0"
detect-browser "5.2.0"

"@walletconnect/browser-utils@^1.7.8":
version "1.7.8"
resolved "https://registry.yarnpkg.com/@walletconnect/browser-utils/-/browser-utils-1.7.8.tgz#c9e27f69d838442d69ccf53cb38ffc3c554baee2"
integrity sha512-iCL0XCWOZaABIc0lqA79Vyaybr3z26nt8mxiwvfrG8oaKUf5Y21Of4dj+wIXQ4Hhblre6SgDlU0Ffb39+1THOw==
dependencies:
"@walletconnect/safe-json" "1.0.0"
"@walletconnect/types" "^1.7.8"
"@walletconnect/window-getters" "1.0.0"
"@walletconnect/window-metadata" "1.0.0"
detect-browser "5.2.0"

"@walletconnect/client@^1.7.0":
version "1.7.8"
resolved "https://registry.yarnpkg.com/@walletconnect/client/-/client-1.7.8.tgz#62c2d7114e59495d90772ea8033831ceb29c6a78"
integrity sha512-pBroM6jZAaUM0SoXJZg5U7aPTiU3ljQAw3Xh/i2pxFDeN/oPKao7husZ5rdxS5xuGSV6YpqqRb0RxW1IeoR2Pg==
dependencies:
"@walletconnect/core" "^1.7.8"
"@walletconnect/iso-crypto" "^1.7.8"
"@walletconnect/types" "^1.7.8"
"@walletconnect/utils" "^1.7.8"

"@walletconnect/client@^1.7.1", "@walletconnect/client@^1.7.7":
version "1.7.7"
resolved "https://registry.yarnpkg.com/@walletconnect/client/-/client-1.7.7.tgz#4570475b0aeed05e53b0c7b01a352a895c0b455b"
Expand All @@ -5523,6 +5544,15 @@
"@walletconnect/types" "^1.7.7"
"@walletconnect/utils" "^1.7.7"

"@walletconnect/core@^1.7.8":
version "1.7.8"
resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-1.7.8.tgz#97c52ea7d00126863cd37bf19bd3e87d4f30de1e"
integrity sha512-9xcQ0YNf9JUFb0YOX1Mpy4Yojt+6w2yQz/0aIEyj2X/s9D71NW0fTYsMcdhkLOI7mn2cqVbx2t1lRvdgqsbrSQ==
dependencies:
"@walletconnect/socket-transport" "^1.7.8"
"@walletconnect/types" "^1.7.8"
"@walletconnect/utils" "^1.7.8"

"@walletconnect/crypto@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@walletconnect/crypto/-/crypto-1.0.2.tgz#3fcc2b2cde6f529a19eadd883dc555cd0e861992"
Expand Down Expand Up @@ -5561,13 +5591,13 @@
eip1193-provider "1.0.1"
eventemitter3 "4.0.7"

"@walletconnect/http-connection@^1.7.7":
version "1.7.7"
resolved "https://registry.yarnpkg.com/@walletconnect/http-connection/-/http-connection-1.7.7.tgz#77bcce7c4c067e1e745da1a1c6174ff0daa76018"
integrity sha512-MmhVJfBjvjYcZnrJYnDx4VBwpYM6yh7eX4/8szo6sGwzG4E+8V4YBKU2NF6fA6YXpdAyEYayElqXhB0YBEZFJA==
"@walletconnect/http-connection@^1.7.0":
version "1.7.8"
resolved "https://registry.yarnpkg.com/@walletconnect/http-connection/-/http-connection-1.7.8.tgz#98bcc726c29751ddc791ff151742aa712bf679a7"
integrity sha512-31gjBw46MRU9hFMTNXAqh+f8qpDNzVeV9BJehzVWKiNC3ciL1JCZkbvsY0djwajduE6TB2ujaML0XDXS9HgBRA==
dependencies:
"@walletconnect/types" "^1.7.7"
"@walletconnect/utils" "^1.7.7"
"@walletconnect/types" "^1.7.8"
"@walletconnect/utils" "^1.7.8"
eventemitter3 "4.0.7"
xhr2-cookies "1.1.0"

Expand All @@ -5580,6 +5610,15 @@
"@walletconnect/types" "^1.7.7"
"@walletconnect/utils" "^1.7.7"

"@walletconnect/iso-crypto@^1.7.8":
version "1.7.8"
resolved "https://registry.yarnpkg.com/@walletconnect/iso-crypto/-/iso-crypto-1.7.8.tgz#41f09326d129faa09beae213e78614c19d90bbd6"
integrity sha512-Qo6qDcMG0Ac+9fpWE0h/oE55NHLk6eM2vlXpWlQDN/me7RZGrkvk+LXsAkQ3UiYPEiPfq4eswcyRWC9AcrAscg==
dependencies:
"@walletconnect/crypto" "^1.0.2"
"@walletconnect/types" "^1.7.8"
"@walletconnect/utils" "^1.7.8"

"@walletconnect/jsonrpc-http-connection@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@walletconnect/jsonrpc-http-connection/-/jsonrpc-http-connection-1.0.0.tgz#5bbdfbaf6d6519b3c08e492a6badb7460ab5ecd0"
Expand Down Expand Up @@ -5617,6 +5656,18 @@
resolved "https://registry.yarnpkg.com/@walletconnect/mobile-registry/-/mobile-registry-1.4.0.tgz#502cf8ab87330841d794819081e748ebdef7aee5"
integrity sha512-ZtKRio4uCZ1JUF7LIdecmZt7FOLnX72RPSY7aUVu7mj7CSfxDwUn6gBuK6WGtH+NZCldBqDl5DenI5fFSvkKYw==

"@walletconnect/qrcode-modal@^1.7.0":
version "1.7.8"
resolved "https://registry.yarnpkg.com/@walletconnect/qrcode-modal/-/qrcode-modal-1.7.8.tgz#52b3d15922f3e371ddc92fd0f49f93ff40241365"
integrity sha512-LqNJMLWO+ljvoRSdq8tcEslW0imKrrb+ugs3bw4w/jEI1FSJzVeinEsgVpyaMv8wsUcyTcSCXSkXpT1SXHtcpw==
dependencies:
"@walletconnect/browser-utils" "^1.7.8"
"@walletconnect/mobile-registry" "^1.4.0"
"@walletconnect/types" "^1.7.8"
copy-to-clipboard "^3.3.1"
preact "10.4.1"
qrcode "1.4.4"

"@walletconnect/qrcode-modal@^1.7.7":
version "1.7.7"
resolved "https://registry.yarnpkg.com/@walletconnect/qrcode-modal/-/qrcode-modal-1.7.7.tgz#a7567370bf915a50fb8edc99f6ceb70ce9be2bfc"
Expand Down Expand Up @@ -5664,11 +5715,38 @@
"@walletconnect/utils" "^1.7.7"
ws "7.5.3"

"@walletconnect/socket-transport@^1.7.8":
version "1.7.8"
resolved "https://registry.yarnpkg.com/@walletconnect/socket-transport/-/socket-transport-1.7.8.tgz#a4ef50d8054293991dbfde7f9c66788030182ec3"
integrity sha512-bqEjLxfSzG79v2OT7XVOZoyUkg6g3yng0fURrdLusWs42fYHWnrSrIZDejFn8N5PiZk5R2edrggkQ7w0VUUAfw==
dependencies:
"@walletconnect/types" "^1.7.8"
"@walletconnect/utils" "^1.7.8"
ws "7.5.3"

"@walletconnect/types@^1.7.0", "@walletconnect/types@^1.7.8":
version "1.7.8"
resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-1.7.8.tgz#ec397e6fbdc8147bccc17029edfeb41c50a5ca09"
integrity sha512-0oSZhKIrtXRJVP1jQ0EDTRtotQY6kggGjDcmm/LLQBKnOZXdPeo0sPkV/7DjT5plT3O7Cjc6JvuXt9WOY0hlCA==

"@walletconnect/types@^1.7.1", "@walletconnect/types@^1.7.7":
version "1.7.7"
resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-1.7.7.tgz#71c623b36a93e373370b1772e82fea2d801adf54"
integrity sha512-yXJrLxwLLCXtWgd/e8FjfY9v5DKds12Z7EEPzUrPSq6v7WtXpqate577KwlFQ6UYzioQzIEDE8+98j+0aiZbsw==

"@walletconnect/utils@^1.7.0", "@walletconnect/utils@^1.7.8":
version "1.7.8"
resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-1.7.8.tgz#f94572bca5eb6b5f81daf8a35268f249f9c6b1ec"
integrity sha512-DSpfH6Do0TQmdrgzu+SyjVhupVjN0WEMvNWGK9K4VlSmLFpCWfme7qxzrvuxBZ47gDqs1kGWvjyJmviWqvOnAg==
dependencies:
"@walletconnect/browser-utils" "^1.7.8"
"@walletconnect/encoding" "^1.0.1"
"@walletconnect/jsonrpc-utils" "^1.0.0"
"@walletconnect/types" "^1.7.8"
bn.js "4.11.8"
js-sha3 "0.8.0"
query-string "6.13.5"

"@walletconnect/utils@^1.7.1", "@walletconnect/utils@^1.7.7":
version "1.7.7"
resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-1.7.7.tgz#684522fa20ccf9ec2944f0497ca70254cb6d4729"
Expand All @@ -5682,16 +5760,16 @@
js-sha3 "0.8.0"
query-string "6.13.5"

"@walletconnect/web3-provider@^1.5.0", "@walletconnect/web3-provider@^1.6.6":
version "1.7.7"
resolved "https://registry.yarnpkg.com/@walletconnect/web3-provider/-/web3-provider-1.7.7.tgz#3d2f8d7a0fcdc118615283978c6a7c9f2a852b71"
integrity sha512-hUhDyaMu93e7e82OVCu3KnYOn6m6wQO9YObbhi3PexppCANe/Y9eDrw/37S+7jbjLIx5nS1et2JU+taKR7OSOw==
dependencies:
"@walletconnect/client" "^1.7.7"
"@walletconnect/http-connection" "^1.7.7"
"@walletconnect/qrcode-modal" "^1.7.7"
"@walletconnect/types" "^1.7.7"
"@walletconnect/utils" "^1.7.7"
"@walletconnect/web3-provider@^1.6.6":
version "1.7.0"
resolved "https://registry.yarnpkg.com/@walletconnect/web3-provider/-/web3-provider-1.7.0.tgz#14d67a75555547205e9fd916a7142b1f951a143e"
integrity sha512-tX0nKVJAs1jrRmFAP8nv1h27ZttWQ/4pOW8hkH32JIB76zbsncdUOjHpnzpzgXrzMyrCzfeXeZ8dbdW4jS1KDw==
dependencies:
"@walletconnect/client" "^1.7.0"
"@walletconnect/http-connection" "^1.7.0"
"@walletconnect/qrcode-modal" "^1.7.0"
"@walletconnect/types" "^1.7.0"
"@walletconnect/utils" "^1.7.0"
web3-provider-engine "16.0.1"

"@walletconnect/[email protected]", "@walletconnect/window-getters@^1.0.0":
Expand Down Expand Up @@ -5805,12 +5883,12 @@
"@ethersproject/providers" "^5.5.1"
"@web3-react/types" "^8.0.7-beta.0"

"@web3-react/walletconnect-connector@6.2.4":
version "6.2.4"
resolved "https://registry.yarnpkg.com/@web3-react/walletconnect-connector/-/walletconnect-connector-6.2.4.tgz#0a128699fc93ddac885935f4aeca32925f6285f0"
integrity sha512-IEVjCXrlcfVa6ggUBEyKtLRaLQuZGtT2lGuzOFtdbJJkN84u1++pzzeDrcsVhKAoS5wq33zyJT9baEbG1Aed8g==
"@web3-react/walletconnect-connector@^7.0.2-alpha.0", "web3-react-walletconnect-connector@npm:@web3-react/walletconnect-connector@^7.0.2-alpha.0":
version "7.0.2-alpha.0"
resolved "https://registry.yarnpkg.com/@web3-react/walletconnect-connector/-/walletconnect-connector-7.0.2-alpha.0.tgz#dacd59db626b42137a1e4f34ea23ef1f04cc8b99"
integrity sha512-Qr+AecDt5/gbAb8sFfW5kbMo0nberCAU/6AB9KmmwCm2YGEEqJrj8fW3Kin7SGxv8pgDxgXwPYsW7qMUzayXEQ==
dependencies:
"@walletconnect/web3-provider" "^1.5.0"
"@walletconnect/ethereum-provider" "^1.5.4"
"@web3-react/abstract-connector" "^6.0.7"
"@web3-react/types" "^6.0.7"
tiny-invariant "^1.0.6"
Expand Down Expand Up @@ -23776,16 +23854,6 @@ [email protected]:
tiny-invariant "^1.0.6"
tiny-warning "^1.0.3"

"web3-react-walletconnect-connector@npm:@web3-react/walletconnect-connector@^7.0.2-alpha.0":
version "7.0.2-alpha.0"
resolved "https://registry.yarnpkg.com/@web3-react/walletconnect-connector/-/walletconnect-connector-7.0.2-alpha.0.tgz#dacd59db626b42137a1e4f34ea23ef1f04cc8b99"
integrity sha512-Qr+AecDt5/gbAb8sFfW5kbMo0nberCAU/6AB9KmmwCm2YGEEqJrj8fW3Kin7SGxv8pgDxgXwPYsW7qMUzayXEQ==
dependencies:
"@walletconnect/ethereum-provider" "^1.5.4"
"@web3-react/abstract-connector" "^6.0.7"
"@web3-react/types" "^6.0.7"
tiny-invariant "^1.0.6"

[email protected]:
version "1.7.1"
resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.7.1.tgz#c6a0fc67321dd585085e3e3be8f2c1c8d61636ef"
Expand Down