diff --git a/.vscode/settings.json b/.vscode/settings.json index 25fa6215..554e4021 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "typescript.tsdk": "node_modules/typescript/lib" + "typescript.tsdk": "node_modules/typescript/lib", + "editor.formatOnSave": false } diff --git a/packages/snap/.gitignore b/packages/snap/.gitignore index fe0c26b5..acd6367a 100644 --- a/packages/snap/.gitignore +++ b/packages/snap/.gitignore @@ -1,3 +1,4 @@ build/ dist/ coverage/ +yarn-error.log \ No newline at end of file diff --git a/packages/snap/index.html b/packages/snap/index.html index 612e96f1..419c7920 100644 --- a/packages/snap/index.html +++ b/packages/snap/index.html @@ -33,6 +33,7 @@

Hello, BTC Snaps!

+
@@ -51,6 +52,7 @@

Hello, BTC Snaps!

const saveDataButton = document.querySelector('button.saveData') const getDataButton = document.querySelector('button.getData') const signLNInvoiceButton = document.querySelector('button.signLNInvoice') + const signMessageButton = document.querySelector('button.signMessage') connectButton.addEventListener('click', connect) sendButton.addEventListener('click', getPubKey) @@ -60,6 +62,8 @@

Hello, BTC Snaps!

saveDataButton.addEventListener('click', saveData) getDataButton.addEventListener('click', getData) signLNInvoiceButton.addEventListener('click', signLNInvoice) + signMessageButton.addEventListener('click', signMessage); + // here we get permissions to interact with and install the snap async function connect () { @@ -232,5 +236,28 @@

Hello, BTC Snaps!

alert('Problem happened: ' + err.message || err) } } + + async function signMessage () { + try { + const response = await ethereum.request({ + method: 'wallet_invokeSnap', + params: { + snapId, + request: { + method: 'btc_signMessage', + params: { + message: "Hello, world!", + derivationPath: "m/49'/0'/0'/0/0", + } + } + } + }) + + console.log(response); + } catch(err) { + console.error(err) + alert('Problem happened: ' + err.message || err) + } + } diff --git a/packages/snap/package.json b/packages/snap/package.json index 1d596128..482ce381 100644 --- a/packages/snap/package.json +++ b/packages/snap/package.json @@ -1,6 +1,6 @@ { "name": "btcsnap", - "version": "2.0.3", + "version": "2.0.4", "description": "btcsnap: Metamask snap to manage your bitcoin", "author": "aaronisme ", "homepage": "", @@ -67,6 +67,7 @@ "@types/bs58check": "^2.1.0", "@types/create-hash": "^1.2.2", "@types/crypto-js": "^4.1.1", + "@types/secp256k1": "4.0.6", "prettier": "^2.7.1", "through2": "^4.0.2" } diff --git a/packages/snap/snap.manifest.json b/packages/snap/snap.manifest.json index df63bbc8..ea87f07b 100644 --- a/packages/snap/snap.manifest.json +++ b/packages/snap/snap.manifest.json @@ -1,5 +1,5 @@ { - "version": "2.0.3", + "version": "2.0.4", "description": "Zion: Metamask snap to manage your Bitcoin", "proposedName": "Zion", "repository": { @@ -7,7 +7,7 @@ "url": "https://github.com/snapdao/btcsnap" }, "source": { - "shasum": "Ci79gfFXP8eVY9fnvOFuOHycz7QairyYhbzbcuCKrNY=", + "shasum": "OLkGL1oGOzEssDC2cHeBTOMq4HE3tbbqJs3hcGAw+8E=", "location": { "npm": { "filePath": "dist/bundle.js", diff --git a/packages/snap/src/errors/constant/SignMessageErrors.ts b/packages/snap/src/errors/constant/SignMessageErrors.ts new file mode 100644 index 00000000..b59b779b --- /dev/null +++ b/packages/snap/src/errors/constant/SignMessageErrors.ts @@ -0,0 +1,6 @@ +export const SignMessageErrors = { + DerivationPathNotSupported: { + code: 40000, + message: 'Derivation path not supported' + } +} \ No newline at end of file diff --git a/packages/snap/src/index.ts b/packages/snap/src/index.ts index d6fa78bc..be6af751 100644 --- a/packages/snap/src/index.ts +++ b/packages/snap/src/index.ts @@ -10,6 +10,7 @@ import { saveLNDataToSnap, getLNDataFromSnap, signLNInvoice, + signMessage, } from './rpc'; import { SnapError, RequestErrors } from './errors'; @@ -77,6 +78,16 @@ export const onRpcRequest = async ({origin, request}: RpcRequest) => { ); case 'btc_signLNInvoice': return signLNInvoice(origin, snap, request.params.invoice); + + case 'btc_signMessage': + return signMessage( + origin, + snap, + request.params.message, + request.params.derivationPath, + request.params.protocol, + ); + default: throw SnapError.of(RequestErrors.MethodNotSupport); } diff --git a/packages/snap/src/interface.ts b/packages/snap/src/interface.ts index c6c46d3a..221b3a84 100644 --- a/packages/snap/src/interface.ts +++ b/packages/snap/src/interface.ts @@ -57,6 +57,15 @@ export interface SignLNInvoice { }; } +export interface SignMessage { + method: 'btc_signMessage'; + params: { + message: string; + protocol: 'ecdsa' | 'bip322' + derivationPath?: string; + }; +} + export type MetamaskBTCRpcRequest = | GetAllXpubsRequest | GetPublicExtendedKeyRequest @@ -65,7 +74,8 @@ export type MetamaskBTCRpcRequest = | ManageNetwork | SaveLNDataToSnap | GetLNDataFromSnap - | SignLNInvoice; + | SignLNInvoice + | SignMessage; export type BTCMethodCallback = ( originString: string, diff --git a/packages/snap/src/rpc/__tests__/signMessage.test.ts b/packages/snap/src/rpc/__tests__/signMessage.test.ts new file mode 100644 index 00000000..d52e298a --- /dev/null +++ b/packages/snap/src/rpc/__tests__/signMessage.test.ts @@ -0,0 +1,65 @@ +import {SnapMock} from '../__mocks__/snap'; +import {signMessage} from '../signMessage'; +import * as bitcoinMessage from 'bitcoinjs-message'; +import {bip44} from './fixtures/bitcoinNode'; + +describe('signMessage', () => { + const snapStub = new SnapMock(); + const domain = 'www.justsnap.io'; + const message = 'test message'; + const derivationPath = ['m', "49'", "0'", "0'", '0', '0'].join('/'); + + beforeEach(() => { + snapStub.rpcStubs.snap_manageState.mockResolvedValue({network: 'test'}); + }); + + afterEach(() => { + snapStub.reset(); + }); + + it('should sign message using ECDSA', async () => { + snapStub.rpcStubs.snap_dialog.mockResolvedValue(true); + snapStub.rpcStubs.snap_getBip32Entropy.mockResolvedValue(bip44.slip10Node); + + const result = await signMessage(domain, snapStub, message, derivationPath); + + expect( + bitcoinMessage.verify(message, result.address, result.signature), + ).toBeTruthy(); + }); + + it('should reject the sign request and throw error if user reject the sign message request', async () => { + snapStub.rpcStubs.snap_dialog.mockResolvedValue(false); + + await expect( + signMessage(domain, snapStub, message, derivationPath, 'ecdsa'), + ).rejects.toThrowError('User reject the sign request'); + }); + + + it('should throw error if protocol is not supported', async () => { + await expect( + signMessage(domain, snapStub, message, derivationPath, 'bip322'), + ).rejects.toThrowError('Action not supported'); + }); + + it("should return valid address if network is mainnet", async () => { + snapStub.rpcStubs.snap_dialog.mockResolvedValue(true); + snapStub.rpcStubs.snap_getBip32Entropy.mockResolvedValue(bip44.slip10Node); + snapStub.rpcStubs.snap_manageState.mockResolvedValue({network: 'main'}); + + const result = await signMessage(domain, snapStub, message, derivationPath); + + expect(result.address.startsWith('3')).toBeTruthy(); + }); + + it("should return valid address if network is testnet", async () => { + snapStub.rpcStubs.snap_dialog.mockResolvedValue(true); + snapStub.rpcStubs.snap_getBip32Entropy.mockResolvedValue(bip44.slip10Node); + snapStub.rpcStubs.snap_manageState.mockResolvedValue({network: 'test'}); + + const result = await signMessage(domain, snapStub, message, derivationPath); + + expect(result.address.startsWith('2')).toBeTruthy(); + }); +}); diff --git a/packages/snap/src/rpc/index.ts b/packages/snap/src/rpc/index.ts index 360714b0..00649da4 100644 --- a/packages/snap/src/rpc/index.ts +++ b/packages/snap/src/rpc/index.ts @@ -7,3 +7,4 @@ export { manageNetwork } from './manageNetwork'; export { saveLNDataToSnap } from './saveLNDataToSnap'; export { getLNDataFromSnap } from './getLNDataFromSnap'; export { signLNInvoice } from './signLNInvoice'; +export { signMessage } from './signMessage'; \ No newline at end of file diff --git a/packages/snap/src/rpc/signMessage.ts b/packages/snap/src/rpc/signMessage.ts new file mode 100644 index 00000000..3de3b393 --- /dev/null +++ b/packages/snap/src/rpc/signMessage.ts @@ -0,0 +1,86 @@ +import bitcoinMessage from 'bitcoinjs-message'; +import {SnapError, RequestErrors} from '../errors'; +import {BitcoinNetwork, ScriptType, Snap} from '../interface'; +import {getPersistedData} from '../utils'; +import {heading, panel, text, divider} from '@metamask/snaps-ui'; +import {getNetwork} from '../bitcoin/getNetwork'; +import * as bitcoin from 'bitcoinjs-lib'; +import { getHDNode } from '../utils/getHDNode'; +import { getScriptType } from '../utils/getScriptType'; +import { SignMessageErrors } from '../errors/constant/SignMessageErrors'; + + +export const signMessage = async ( + domain: string, + snap: Snap, + message: string, + derivationPath: string, + // TODO: implement bip322 message signing + protocol: 'ecdsa' | 'bip322' = 'ecdsa', +) => { + if (protocol !== 'ecdsa') { + throw SnapError.of(RequestErrors.ActionNotSupport); + } + + const snapNetwork = await getPersistedData( + snap, + 'network', + '' as BitcoinNetwork, + ); + + const scriptType = getScriptType(derivationPath); + + if (!scriptType || ![ScriptType.P2PKH, ScriptType.P2SH_P2WPKH].includes(scriptType)) { + throw SnapError.of(SignMessageErrors.DerivationPathNotSupported); + } + + const path = derivationPath.split('/'); + const btcNetwork = getNetwork(snapNetwork); + + if (snapNetwork !== BitcoinNetwork.Main) { + path[2] = "1'"; + } + + const {publicKey, privateKey} = await getHDNode(snap, path.join('/')); + + const address = bitcoin.payments.p2sh({ + redeem: bitcoin.payments.p2wpkh({ + pubkey: publicKey, + network: btcNetwork, + }), + network: btcNetwork, + }).address as string; + + const result = await snap.request({ + method: 'snap_dialog', + params: { + type: 'confirmation', + content: panel([ + heading('Signature Request'), + text(`Please verify this sign message request from ${domain}`), + divider(), + text("Only confirm this message if you approve the content and trust the requesting site."), + panel([ + text(`**Address**:\n ${address}`), + text(`**Network**:\n ${snapNetwork === BitcoinNetwork.Main ? 'Mainnet' : 'Testnet'}`), + text(`**Message**:\n ${message}`), + ]), + divider(), + text(`By signing this message, you verify that you own the account without broadcasting any on-chain transactions. This signature does not allow transactions to be broadcast on your behalf. Only sign messages that you trust.`) + ]), + }, + }); + + if (!result) { + throw SnapError.of(RequestErrors.RejectSign); + } + + const signature = bitcoinMessage.sign(message, privateKey, true, { + segwitType: scriptType === ScriptType.P2WPKH ? "p2wpkh" : "p2sh(p2wpkh)", + }); + + return { + signature: signature.toString('base64'), + address, + }; + }; diff --git a/packages/snap/src/utils/__tests__/getScriptType.test.ts b/packages/snap/src/utils/__tests__/getScriptType.test.ts new file mode 100644 index 00000000..392cf1eb --- /dev/null +++ b/packages/snap/src/utils/__tests__/getScriptType.test.ts @@ -0,0 +1,14 @@ +import { ScriptType } from "../../interface"; +import { getScriptType } from "../getScriptType"; + +describe('isDerivationPathSupported', () => { + it('should return ScriptType for supported derivation paths', () => { + expect(getScriptType("m/44'/0'/0'/0/0")).toBe(ScriptType.P2PKH); + expect(getScriptType("m/49'/0'/0'/0/0")).toBe(ScriptType.P2SH_P2WPKH); + expect(getScriptType("m/84'/0'/0'/0/0")).toBe(ScriptType.P2WPKH); + }); + + it('should return null for unsupported derivation paths', () => { + expect(getScriptType("m/86'/0'/0'/0/0")).toBe(null); + }); +}); \ No newline at end of file diff --git a/packages/snap/src/utils/getScriptType.ts b/packages/snap/src/utils/getScriptType.ts new file mode 100644 index 00000000..c3dbcc02 --- /dev/null +++ b/packages/snap/src/utils/getScriptType.ts @@ -0,0 +1,13 @@ +import {ScriptType} from 'interface'; +import {pathMap} from '../rpc/getExtendedPublicKey'; + +export const getScriptType = (derivationPath: string) => { + const path = derivationPath.split('/'); + for (const scriptType in pathMap) { + if (pathMap[scriptType as ScriptType].every((v, i) => v === path[i])) { + return scriptType as ScriptType; + } + } + + return null; +}; diff --git a/yarn.lock b/yarn.lock index 41a9daa3..9210a682 100644 --- a/yarn.lock +++ b/yarn.lock @@ -26,13 +26,6 @@ jsonpointer "^5.0.0" leven "^3.1.0" -"@babel/code-frame@7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" - integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== - dependencies: - "@babel/highlight" "^7.10.4" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.5.5", "@babel/code-frame@^7.8.3": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" @@ -540,7 +533,7 @@ "@babel/traverse" "^7.21.5" "@babel/types" "^7.21.5" -"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": +"@babel/highlight@^7.16.7": version "7.17.9" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.9.tgz#61b2ee7f32ea0454612def4fccdae0de232b73e3" integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg== @@ -1590,21 +1583,6 @@ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== -"@eslint/eslintrc@^0.4.3": - version "0.4.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" - integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== - dependencies: - ajv "^6.12.4" - debug "^4.1.1" - espree "^7.3.0" - globals "^13.9.0" - ignore "^4.0.6" - import-fresh "^3.2.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" - strip-json-comments "^3.1.1" - "@eslint/eslintrc@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6" @@ -1679,15 +1657,6 @@ resolved "https://registry.yarnpkg.com/@gilbarbara/deep-equal/-/deep-equal-0.1.2.tgz#1a106721368dba5e7e9fb7e9a3a6f9efbd8df36d" integrity sha512-jk+qzItoEb0D0xSSmrKDDzf9sheQj/BAPxlgNxgmOaA3mxpUa6ndJLYGZKsJnIVEQSD8zcTbyILz7I0HcnBCRA== -"@humanwhocodes/config-array@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" - integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== - dependencies: - "@humanwhocodes/object-schema" "^1.2.0" - debug "^4.1.1" - minimatch "^3.0.4" - "@humanwhocodes/config-array@^0.9.2": version "0.9.5" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" @@ -1697,7 +1666,7 @@ debug "^4.1.1" minimatch "^3.0.4" -"@humanwhocodes/object-schema@^1.2.0", "@humanwhocodes/object-schema@^1.2.1": +"@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== @@ -4072,6 +4041,13 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== +"@types/secp256k1@4.0.6": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.6.tgz#d60ba2349a51c2cbc5e816dcd831a42029d376bf" + integrity sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ== + dependencies: + "@types/node" "*" + "@types/secp256k1@^4.0.1": version "4.0.3" resolved "https://registry.yarnpkg.com/@types/secp256k1/-/secp256k1-4.0.3.tgz#1b8e55d8e00f08ee7220b4d59a6abe89c37a901c" @@ -4580,7 +4556,7 @@ acorn@^6.0.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== -acorn@^7.0.0, acorn@^7.1.0, acorn@^7.1.1, acorn@^7.4.0: +acorn@^7.0.0, acorn@^7.1.0, acorn@^7.1.1: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== @@ -4661,7 +4637,7 @@ ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.0.1, ajv@^8.6.0, ajv@^8.8.0: +ajv@^8.0.0, ajv@^8.6.0, ajv@^8.8.0: version "8.11.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== @@ -4947,11 +4923,6 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - async@0.9.x: version "0.9.2" resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" @@ -7473,7 +7444,7 @@ enhanced-resolve@^5.9.2: graceful-fs "^4.2.4" tapable "^2.2.0" -enquirer@^2.3.4, enquirer@^2.3.5: +enquirer@^2.3.4: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== @@ -7791,7 +7762,7 @@ eslint-utils@^1.4.3: dependencies: eslint-visitor-keys "^1.1.0" -eslint-utils@^2.0.0, eslint-utils@^2.1.0: +eslint-utils@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== @@ -7805,7 +7776,7 @@ eslint-utils@^3.0.0: dependencies: eslint-visitor-keys "^2.0.0" -eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: +eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== @@ -7874,52 +7845,6 @@ eslint@^6.1.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -eslint@^7.11.0: - version "7.32.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" - integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== - dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.3" - "@humanwhocodes/config-array" "^0.5.0" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.0.1" - doctrine "^3.0.0" - enquirer "^2.3.5" - escape-string-regexp "^4.0.0" - eslint-scope "^5.1.1" - eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" - esquery "^1.4.0" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.1.2" - globals "^13.6.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - js-yaml "^3.13.1" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.0.4" - natural-compare "^1.4.0" - optionator "^0.9.1" - progress "^2.0.0" - regexpp "^3.1.0" - semver "^7.2.1" - strip-ansi "^6.0.0" - strip-json-comments "^3.1.0" - table "^6.0.9" - text-table "^0.2.0" - v8-compile-cache "^2.0.3" - eslint@^8.3.0: version "8.13.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.13.0.tgz#6fcea43b6811e655410f5626cfcf328016badcd7" @@ -7970,15 +7895,6 @@ espree@^6.1.2: acorn-jsx "^5.2.0" eslint-visitor-keys "^1.1.0" -espree@^7.3.0, espree@^7.3.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" - integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== - dependencies: - acorn "^7.4.0" - acorn-jsx "^5.3.1" - eslint-visitor-keys "^1.3.0" - espree@^9.3.1: version "9.3.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd" @@ -11373,11 +11289,6 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" -lodash.truncate@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" - integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= - lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -15096,7 +15007,7 @@ semver@^7.0.0, semver@^7.3.7: dependencies: lru-cache "^6.0.0" -semver@^7.1.1, semver@^7.1.3, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: +semver@^7.1.1, semver@^7.1.3, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== @@ -15345,15 +15256,6 @@ slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - slide@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" @@ -15751,7 +15653,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -16066,17 +15968,6 @@ table@^5.2.3: slice-ansi "^2.1.0" string-width "^3.0.0" -table@^6.0.9: - version "6.8.0" - resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca" - integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA== - dependencies: - ajv "^8.0.1" - lodash.truncate "^4.4.2" - slice-ansi "^4.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - tailwindcss@^3.0.2: version "3.0.24" resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.0.24.tgz#22e31e801a44a78a1d9a81ecc52e13b69d85704d"