add solana name service (#320)

This commit is contained in:
DR497 2021-07-01 23:50:16 +08:00 committed by GitHub
parent 914a1d420d
commit 0909629d44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 239 additions and 17 deletions

View File

@ -11,6 +11,7 @@
"@project-serum/serum": "^0.13.33",
"@project-serum/swap-ui": "^0.1.11",
"@project-serum/token": "^0.0.1-alpha.3",
"@solana/spl-name-service": "^0.1.2",
"@solana/spl-token-registry": "^0.2.102",
"@solana/web3.js": "^1.17.0",
"@testing-library/jest-dom": "^5.11.6",

View File

@ -36,6 +36,7 @@ import {
import { parseTokenAccountData } from '../utils/tokens/data';
import { Switch, Tooltip } from '@material-ui/core';
import { EthFeeEstimate } from './EthFeeEstimate';
import { resolveDomainName, resolveTwitterHandle } from '../utils/name-service';
const WUSDC_MINT = new PublicKey(
'BXXkv6z8ykpG1yuvUDPgh732wzVHB69RnB9YgSYh3itW',
@ -50,7 +51,7 @@ const USDT_MINT = new PublicKey('Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB');
const DISABLED_ERC20_MINTS = new Set([
'kinXdEcpDQeHPEuQnqmUgtYykqKGVFq6CeVX5iAHJq6',
'ABE7D8RU1eHfCJWzHYZZeymeE8k9nPPXfqge2NQYyKoL',
])
]);
export default function SendDialog({ open, onClose, publicKey, balanceInfo }) {
const isProdNetwork = useIsProdNetwork();
@ -79,7 +80,10 @@ export default function SendDialog({ open, onClose, publicKey, balanceInfo }) {
<Tab label="SPL USDT" key="wusdtToSplUsdt" value="wusdtToSplUsdt" />,
<Tab label="ERC20 USDT" key="swap" value="swap" />,
];
} else if (localStorage.getItem('sollet-private') && mint?.equals(USDC_MINT)) {
} else if (
localStorage.getItem('sollet-private') &&
mint?.equals(USDC_MINT)
) {
return [
<Tab label="SPL USDC" key="spl" value="spl" />,
<Tab label="SPL WUSDC" key="usdcToSplWUsdc" value="usdcToSplWUsdc" />,
@ -98,7 +102,10 @@ export default function SendDialog({ open, onClose, publicKey, balanceInfo }) {
const tabs = [
<Tab label={`SPL ${swapCoinInfo.ticker}`} key="spl" value="spl" />,
];
if (!DISABLED_ERC20_MINTS.has(mint.toString()) || localStorage.getItem('sollet-private')) {
if (
!DISABLED_ERC20_MINTS.has(mint.toString()) ||
localStorage.getItem('sollet-private')
) {
tabs.push(erc20Tab);
}
return tabs;
@ -207,7 +214,7 @@ function SendSplDialog({ onClose, publicKey, balanceInfo, onSubmitRef }) {
false,
);
const [shouldShowOverride, setShouldShowOverride] = useState();
const {
let {
fields,
destinationAddress,
transferAmountString,
@ -215,9 +222,39 @@ function SendSplDialog({ onClose, publicKey, balanceInfo, onSubmitRef }) {
} = useForm(balanceInfo, addressHelperText, passValidation);
const { decimals, mint } = balanceInfo;
const mintString = mint && mint.toBase58();
const [isDomainName, setIsDomainName] = useState(false);
const [domainOwner, setDomainOwner] = useState();
useEffect(() => {
(async () => {
if (destinationAddress.startsWith('@')) {
const twitterOwner = await resolveTwitterHandle(
wallet.connection,
destinationAddress.slice(1),
);
if (!twitterOwner) {
setAddressHelperText(`This Twitter handle is not registered`);
setPassValidation(undefined);
setShouldShowOverride(undefined);
return;
}
setIsDomainName(true);
setDomainOwner(twitterOwner);
}
if (destinationAddress.endsWith('.sol')) {
const domainOwner = await resolveDomainName(
wallet.connection,
destinationAddress.slice(0, -4),
);
if (!domainOwner) {
setAddressHelperText(`This domain name is not registered`);
setPassValidation(undefined);
setShouldShowOverride(undefined);
return;
}
setIsDomainName(true);
setDomainOwner(domainOwner);
}
if (!destinationAddress) {
setAddressHelperText(defaultAddressHelperText);
setPassValidation(undefined);
@ -226,7 +263,7 @@ function SendSplDialog({ onClose, publicKey, balanceInfo, onSubmitRef }) {
}
try {
const destinationAccountInfo = await wallet.connection.getAccountInfo(
new PublicKey(destinationAddress),
new PublicKey(isDomainName ? domainOwner : destinationAddress),
);
setShouldShowOverride(false);
@ -243,7 +280,11 @@ function SendSplDialog({ onClose, publicKey, balanceInfo, onSubmitRef }) {
}
} else {
setPassValidation(true);
setAddressHelperText('Destination is a Solana address');
setAddressHelperText(
`Destination is a Solana address: ${
isDomainName ? domainOwner : destinationAddress
}`,
);
}
} catch (e) {
console.log(`Received error validating address ${e}`);
@ -253,14 +294,12 @@ function SendSplDialog({ onClose, publicKey, balanceInfo, onSubmitRef }) {
}
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [destinationAddress, wallet, mintString]);
}, [destinationAddress, wallet, mintString, isDomainName, domainOwner]);
useEffect(() => {
return () => {
setOverrideDestinationCheck(false);
};
}, [setOverrideDestinationCheck]);
async function makeTransaction() {
let amount = Math.round(parseFloat(transferAmountString) * 10 ** decimals);
if (!amount || amount <= 0) {
@ -268,7 +307,7 @@ function SendSplDialog({ onClose, publicKey, balanceInfo, onSubmitRef }) {
}
return wallet.transferToken(
publicKey,
new PublicKey(destinationAddress),
new PublicKey(isDomainName ? domainOwner : destinationAddress),
amount,
balanceInfo.mint,
decimals,
@ -452,7 +491,8 @@ function SendSwapDialog({
/>
);
}
const bitcoinDisable = (blockchain === 'btc' ? parseFloat(transferAmountString) < 0.001 : false);
const bitcoinDisable =
blockchain === 'btc' ? parseFloat(transferAmountString) < 0.001 : false;
let sendButton = (
<Button
type="submit"

View File

@ -0,0 +1,47 @@
import { PublicKey, Connection } from '@solana/web3.js';
import {
getTwitterRegistry,
getHashedName,
getNameAccountKey,
NameRegistryState,
} from '@solana/spl-name-service';
// Address of the SOL TLD
export const SOL_TLD_AUTHORITY = new PublicKey(
'58PwtjSDuFHuUkYjH9BYnnQKHfwo9reZhC2zMJv9JPkx',
);
export const resolveTwitterHandle = async (
connection: Connection,
twitterHandle: string,
): Promise<string | undefined> => {
try {
const registry = await getTwitterRegistry(connection, twitterHandle);
return registry.owner.toBase58();
} catch (err) {
console.warn(`err`);
return undefined;
}
};
export const resolveDomainName = async (
connection: Connection,
domainName: string,
): Promise<string | undefined> => {
let hashedDomainName = await getHashedName(domainName);
let inputDomainKey = await getNameAccountKey(
hashedDomainName,
undefined,
SOL_TLD_AUTHORITY,
);
try {
const registry = await NameRegistryState.retrieve(
connection,
inputDomainKey,
);
return registry.owner.toBase58();
} catch (err) {
console.warn(err);
return undefined;
}
};

146
yarn.lock
View File

@ -2021,6 +2021,24 @@
dependencies:
"@sinonjs/commons" "^1.7.0"
"@solana/spl-name-service@^0.1.2":
version "0.1.2"
resolved "https://registry.yarnpkg.com/@solana/spl-name-service/-/spl-name-service-0.1.2.tgz#d49240a692bbf05ef56d79db0916b17a4829b99d"
integrity sha512-rmVYzhmZgaYmaANzmuxemoJWMnkuM59zitJpdkJejn/oTPYc+SezhQk52fN4dqjCz1cTBnzDJ+0AUJIwgNWnNQ==
dependencies:
"@solana/spl-token" "0.1.4"
"@solana/web3.js" "^1.11.0"
bip32 "^2.0.6"
bn.js "^5.1.3"
bs58 "4.0.1"
buffer-layout "^1.2.0"
core-util-is "^1.0.2"
crypto "^1.0.1"
crypto-ts "^1.0.2"
fs "^0.0.1-security"
tweetnacl "^1.0.3"
webpack-dev-server "^3.11.2"
"@solana/spl-token-registry@^0.2.102":
version "0.2.102"
resolved "https://registry.yarnpkg.com/@solana/spl-token-registry/-/spl-token-registry-0.2.102.tgz#e313a098474bb8b1fab5af74fce2644a046dbf01"
@ -2028,7 +2046,7 @@
dependencies:
cross-fetch "3.0.6"
"@solana/spl-token@^0.1.3", "@solana/spl-token@^0.1.4":
"@solana/spl-token@0.1.4", "@solana/spl-token@^0.1.3", "@solana/spl-token@^0.1.4":
version "0.1.4"
resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.4.tgz#7fc6ba82a7fbb2b0907f7ffc87709488db83ed2a"
integrity sha512-W8uSC4ysWVjbKK7lvsIHFxdMIkOCEoOm9tYY9VVpBlUIp4+K5bpPxHXUlxMiHfkKPWAxab6IGUn71VVLg8uq5Q==
@ -3550,7 +3568,7 @@ bindings@^1.3.0, bindings@^1.5.0:
dependencies:
file-uri-to-path "1.0.0"
bip32@^2.0.5:
bip32@^2.0.5, bip32@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/bip32/-/bip32-2.0.6.tgz#6a81d9f98c4cd57d05150c60d8f9e75121635134"
integrity sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==
@ -3789,7 +3807,7 @@ browserslist@^4.14.5, browserslist@^4.15.0:
escalade "^3.1.1"
node-releases "^1.1.67"
bs58@^4.0.0, bs58@^4.0.1:
bs58@4.0.1, bs58@^4.0.0, bs58@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo=
@ -4546,7 +4564,7 @@ core-js@^3.6.5:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.8.1.tgz#f51523668ac8a294d1285c3b9db44025fda66d47"
integrity sha512-9Id2xHY1W7m8hCl8NkhQn5CufmF/WuR30BTRewvCXc1aZd3kMECwNZ69ndLbekKfakw9Rf2Xyc+QR6E7Gg+obg==
core-util-is@1.0.2, core-util-is@~1.0.0:
core-util-is@1.0.2, core-util-is@^1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
@ -4676,6 +4694,18 @@ crypto-random-string@^1.0.0:
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=
crypto-ts@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/crypto-ts/-/crypto-ts-1.0.2.tgz#c2ae47b42f1a8affcf33bddbe3283983150c8a05"
integrity sha1-wq5HtC8aiv/PM73b4yg5gxUMigU=
dependencies:
tslib "^1.7.1"
crypto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037"
integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==
css-blank-pseudo@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz#dfdefd3254bf8a82027993674ccf35483bfcb3c5"
@ -4977,6 +5007,13 @@ debug@^3.1.1, debug@^3.2.5:
dependencies:
ms "^2.1.1"
debug@^3.2.6:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
dependencies:
ms "^2.1.1"
debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
@ -6219,6 +6256,13 @@ faye-websocket@^0.10.0:
dependencies:
websocket-driver ">=0.5.1"
faye-websocket@^0.11.3:
version "0.11.4"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da"
integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==
dependencies:
websocket-driver ">=0.5.1"
faye-websocket@~0.11.1:
version "0.11.3"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e"
@ -6532,6 +6576,11 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
fs@^0.0.1-security:
version "0.0.1-security"
resolved "https://registry.yarnpkg.com/fs/-/fs-0.0.1-security.tgz#8a7bd37186b6dddf3813f23858b57ecaaf5e41d4"
integrity sha1-invTcYa23d84E/I4WLV+yq9eQdQ=
fsevents@^1.2.7:
version "1.2.13"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38"
@ -8343,7 +8392,7 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
json3@^3.3.2:
json3@^3.3.2, json3@^3.3.3:
version "3.3.3"
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81"
integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==
@ -9338,6 +9387,11 @@ node-forge@0.9.0:
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579"
integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==
node-forge@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==
node-gyp-build@^4.2.0:
version "4.2.3"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739"
@ -11874,6 +11928,13 @@ selfsigned@^1.10.7:
dependencies:
node-forge "0.9.0"
selfsigned@^1.10.8:
version "1.10.11"
resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.11.tgz#24929cd906fe0f44b6d01fb23999a739537acbe9"
integrity sha512-aVmbPOfViZqOZPgRBT0+3u4yZFHpmnIghLMlAcb5/xhp5ZtB/RVnKhz5vl2M32CLXAqR4kha9zfhNg0Lf/sxKA==
dependencies:
node-forge "^0.10.0"
semver-compare@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
@ -12167,6 +12228,18 @@ sockjs-client@1.4.0:
json3 "^3.3.2"
url-parse "^1.4.3"
sockjs-client@^1.5.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.5.1.tgz#256908f6d5adfb94dabbdbd02c66362cca0f9ea6"
integrity sha512-VnVAb663fosipI/m6pqRXakEOw7nvd7TUgdr3PlR/8V2I95QIdwT8L4nMxhyU8SmDBHYXU1TOElaKOmKLfYzeQ==
dependencies:
debug "^3.2.6"
eventsource "^1.0.7"
faye-websocket "^0.11.3"
inherits "^2.0.4"
json3 "^3.3.3"
url-parse "^1.5.1"
sockjs@0.3.20:
version "0.3.20"
resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.20.tgz#b26a283ec562ef8b2687b44033a4eeceac75d855"
@ -12176,6 +12249,15 @@ sockjs@0.3.20:
uuid "^3.4.0"
websocket-driver "0.6.5"
sockjs@^0.3.21:
version "0.3.21"
resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.21.tgz#b34ffb98e796930b60a0cfa11904d6a339a7d417"
integrity sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw==
dependencies:
faye-websocket "^0.11.3"
uuid "^3.4.0"
websocket-driver "^0.7.4"
sort-keys@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
@ -13009,6 +13091,11 @@ tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
tslib@^1.7.1:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.3:
version "2.2.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
@ -13282,6 +13369,14 @@ url-parse@^1.4.3:
querystringify "^2.1.1"
requires-port "^1.0.0"
url-parse@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.1.tgz#d5fa9890af8a5e1f274a2c98376510f6425f6e3b"
integrity sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q==
dependencies:
querystringify "^2.1.1"
requires-port "^1.0.0"
url-set-query@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/url-set-query/-/url-set-query-1.0.0.tgz#016e8cfd7c20ee05cafe7795e892bd0702faa339"
@ -13775,6 +13870,45 @@ webpack-dev-server@3.11.0:
ws "^6.2.1"
yargs "^13.3.2"
webpack-dev-server@^3.11.2:
version "3.11.2"
resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.2.tgz#695ebced76a4929f0d5de7fd73fafe185fe33708"
integrity sha512-A80BkuHRQfCiNtGBS1EMf2ChTUs0x+B3wGDFmOeT4rmJOHhHTCH2naNxIHhmkr0/UillP4U3yeIyv1pNp+QDLQ==
dependencies:
ansi-html "0.0.7"
bonjour "^3.5.0"
chokidar "^2.1.8"
compression "^1.7.4"
connect-history-api-fallback "^1.6.0"
debug "^4.1.1"
del "^4.1.1"
express "^4.17.1"
html-entities "^1.3.1"
http-proxy-middleware "0.19.1"
import-local "^2.0.0"
internal-ip "^4.3.0"
ip "^1.1.5"
is-absolute-url "^3.0.3"
killable "^1.0.1"
loglevel "^1.6.8"
opn "^5.5.0"
p-retry "^3.0.1"
portfinder "^1.0.26"
schema-utils "^1.0.0"
selfsigned "^1.10.8"
semver "^6.3.0"
serve-index "^1.9.1"
sockjs "^0.3.21"
sockjs-client "^1.5.0"
spdy "^4.0.2"
strip-ansi "^3.0.1"
supports-color "^6.1.0"
url "^0.11.0"
webpack-dev-middleware "^3.7.2"
webpack-log "^2.0.0"
ws "^6.2.1"
yargs "^13.3.2"
webpack-log@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f"
@ -13837,7 +13971,7 @@ websocket-driver@0.6.5:
dependencies:
websocket-extensions ">=0.1.1"
websocket-driver@>=0.5.1:
websocket-driver@>=0.5.1, websocket-driver@^0.7.4:
version "0.7.4"
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760"
integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==