diff --git a/address-poisoning-py/Dockerfile b/address-poisoning-py/Dockerfile index 89bb5e1a..9e8f7e55 100644 --- a/address-poisoning-py/Dockerfile +++ b/address-poisoning-py/Dockerfile @@ -1,23 +1,19 @@ # Build stage: compile Python dependencies -FROM python:3.9-alpine as builder +FROM python:3.10-alpine as builder RUN apk update RUN apk add alpine-sdk RUN python3 -m pip install --upgrade pip COPY requirements.txt ./ RUN python3 -m pip install --user -r requirements.txt -# Final stage: copy over Python dependencies and install production Node dependencies -FROM node:12-alpine -# this python version should match the build stage python version -RUN apk add python3 +# Final stage: copy over Python dependencies +FROM python:3.10-alpine COPY --from=builder /root/.local /root/.local ENV PATH=/root/.local:$PATH -ENV NODE_ENV=production -# Uncomment the following line to enable agent logging +ENV FORTA_ENV=production +# Uncomment the following line to enable logging LABEL "network.forta.settings.agent-logs.enable"="true" WORKDIR /app COPY ./src ./src -COPY ./LICENSE.md ./ -COPY package*.json ./ -RUN npm ci --production -CMD [ "npm", "run", "start:prod" ] \ No newline at end of file +COPY LICENSE.md ./ +CMD [ "python3", "./src/agent.py" ] \ No newline at end of file diff --git a/address-poisoning-py/README.md b/address-poisoning-py/README.md index 50fea0bf..686477e9 100644 --- a/address-poisoning-py/README.md +++ b/address-poisoning-py/README.md @@ -13,6 +13,7 @@ This agent detects address poisoning, phishing transactions. - Arbitrum - Optimism - Fantom +- Linea ## Alerts diff --git a/address-poisoning-py/package-lock.json b/address-poisoning-py/package-lock.json index d27c1e7a..15a7636a 100644 --- a/address-poisoning-py/package-lock.json +++ b/address-poisoning-py/package-lock.json @@ -1,682 +1,363 @@ { "name": "address-poisoning-detector", - "version": "0.4.3", - "lockfileVersion": 1, + "version": "0.5.0", + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@ethersproject/abi": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", - "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", - "requires": { - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@ethersproject/abstract-provider": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", - "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", - "requires": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/networks": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/web": "^5.7.0" - } - }, - "@ethersproject/abstract-signer": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", - "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", - "requires": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0" - } - }, - "@ethersproject/address": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", - "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", - "requires": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/rlp": "^5.7.0" - } - }, - "@ethersproject/base64": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", - "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", - "requires": { - "@ethersproject/bytes": "^5.7.0" - } - }, - "@ethersproject/basex": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", - "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/properties": "^5.7.0" - } - }, - "@ethersproject/bignumber": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", - "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "bn.js": "^5.2.1" - } - }, - "@ethersproject/bytes": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", - "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", - "requires": { - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/constants": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", - "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", - "requires": { - "@ethersproject/bignumber": "^5.7.0" - } - }, - "@ethersproject/contracts": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", - "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", - "requires": { - "@ethersproject/abi": "^5.7.0", - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/transactions": "^5.7.0" - } - }, - "@ethersproject/hash": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", - "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", - "requires": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/base64": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@ethersproject/hdnode": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", - "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", - "requires": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/basex": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/pbkdf2": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/sha2": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0", - "@ethersproject/strings": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/wordlists": "^5.7.0" - } - }, - "@ethersproject/json-wallets": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", - "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", - "requires": { - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/hdnode": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/pbkdf2": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/random": "^5.7.0", - "@ethersproject/strings": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "aes-js": "3.0.0", - "scrypt-js": "3.0.1" - } - }, - "@ethersproject/keccak256": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", - "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "js-sha3": "0.8.0" - } - }, - "@ethersproject/logger": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", - "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==" - }, - "@ethersproject/networks": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", - "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", - "requires": { - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/pbkdf2": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", - "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/sha2": "^5.7.0" - } - }, - "@ethersproject/properties": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", - "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", - "requires": { - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/providers": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", - "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", - "requires": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/base64": "^5.7.0", - "@ethersproject/basex": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/networks": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/random": "^5.7.0", - "@ethersproject/rlp": "^5.7.0", - "@ethersproject/sha2": "^5.7.0", - "@ethersproject/strings": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/web": "^5.7.0", - "bech32": "1.1.4", - "ws": "7.4.6" - } - }, - "@ethersproject/random": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", - "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/rlp": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", - "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/sha2": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", - "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "hash.js": "1.1.7" - } - }, - "@ethersproject/signing-key": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", - "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "bn.js": "^5.2.1", - "elliptic": "6.5.4", - "hash.js": "1.1.7" - } - }, - "@ethersproject/solidity": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", - "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", - "requires": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/sha2": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@ethersproject/strings": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", - "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/transactions": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", - "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", - "requires": { - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/rlp": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0" - } - }, - "@ethersproject/units": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", - "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", - "requires": { - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/constants": "^5.7.0", - "@ethersproject/logger": "^5.7.0" - } - }, - "@ethersproject/wallet": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", - "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", - "requires": { - "@ethersproject/abstract-provider": "^5.7.0", - "@ethersproject/abstract-signer": "^5.7.0", - "@ethersproject/address": "^5.7.0", - "@ethersproject/bignumber": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/hdnode": "^5.7.0", - "@ethersproject/json-wallets": "^5.7.0", - "@ethersproject/keccak256": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/random": "^5.7.0", - "@ethersproject/signing-key": "^5.7.0", - "@ethersproject/transactions": "^5.7.0", - "@ethersproject/wordlists": "^5.7.0" - } - }, - "@ethersproject/web": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", - "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", - "requires": { - "@ethersproject/base64": "^5.7.0", - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@ethersproject/wordlists": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", - "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", - "requires": { - "@ethersproject/bytes": "^5.7.0", - "@ethersproject/hash": "^5.7.0", - "@ethersproject/logger": "^5.7.0", - "@ethersproject/properties": "^5.7.0", - "@ethersproject/strings": "^5.7.0" - } - }, - "@grpc/grpc-js": { - "version": "1.8.17", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.8.17.tgz", - "integrity": "sha512-DGuSbtMFbaRsyffMf+VEkVu8HkSXEUfO3UyGJNtqxW9ABdtTIA+2UXAJpwbJS+xfQxuwqLUeELmL6FuZkOqPxw==", - "requires": { - "@grpc/proto-loader": "^0.7.0", - "@types/node": ">=12.12.47" - }, - "dependencies": { - "@grpc/proto-loader": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.7.tgz", - "integrity": "sha512-1TIeXOi8TuSCQprPItwoMymZXxWT0CPxUhkrkeCUH+D8U7QDwQ6b7SUz2MaLuWM2llT+J/TVFLmQI5KtML3BhQ==", - "requires": { - "@types/long": "^4.0.1", - "lodash.camelcase": "^4.3.0", - "long": "^4.0.0", - "protobufjs": "^7.0.0", - "yargs": "^17.7.2" - } - }, - "protobufjs": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.4.tgz", - "integrity": "sha512-AT+RJgD2sH8phPmCf7OUZR8xGdcJRga4+1cOaXJ64hvcSkVhNcRHOwIxUatPH15+nj59WAGTDv3LSGZPEQbJaQ==", - "requires": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/node": ">=13.7.0", - "long": "^5.0.0" - }, - "dependencies": { - "long": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", - "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" - } - } - } - } + "packages": { + "": { + "name": "address-poisoning-detector", + "version": "0.5.0", + "hasInstallScript": true, + "devDependencies": { + "@fortanetwork/forta-bot-cli": "^0.2.2", + "nodemon": "^3.0.2" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "dev": true }, - "@grpc/proto-loader": { - "version": "0.6.13", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.6.13.tgz", - "integrity": "sha512-FjxPYDRTn6Ec3V0arm1FtSpmP6V50wuph2yILpyvTKzjc76oDdoihXqM1DzOW5ubvCC8GivfCnNtfaRE8myJ7g==", - "requires": { - "@types/long": "^4.0.1", - "lodash.camelcase": "^4.3.0", - "long": "^4.0.0", - "protobufjs": "^6.11.3", - "yargs": "^16.2.0" - }, - "dependencies": { - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==" - } + "node_modules/@fortanetwork/forta-bot": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@fortanetwork/forta-bot/-/forta-bot-0.2.3.tgz", + "integrity": "sha512-kGPAYZFyAFzB9IvZ+jn6OQ3+ASDeKWEyL1Ltzi8wNL57kfVF+C+WiKXhDk77BA1GwP8qyYBRztCxF73k8kA7vQ==", + "dev": true, + "dependencies": { + "awilix": "^9.0.0", + "axios": "^1.6.2", + "base64-arraybuffer": "^1.0.2", + "ethers": "^6.9.0", + "flat-cache": "^3.2.0", + "jsonc": "^2.0.0", + "lodash": "^4.17.21", + "lru-cache": "^10.2.0", + "murmurhash3js": "^3.0.1", + "sha3": "^2.1.4" + }, + "engines": { + "node": ">=20" } }, - "@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==" + "node_modules/@fortanetwork/forta-bot-cli": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@fortanetwork/forta-bot-cli/-/forta-bot-cli-0.2.4.tgz", + "integrity": "sha512-QJDSZTLkkG6rUXU/H2cTfEQCOOsKBnK0eZ5uvFNG7flHZqZoj7/Djp+bQrFC+OrbrhQZHnrbRIOfAS4T0odbHw==", + "dev": true, + "dependencies": { + "@fortanetwork/forta-bot": "^0.2.3", + "awilix": "^9.0.0", + "axios": "^1.6.2", + "ethers": "^6.9.0", + "flat-cache": "^3.2.0", + "keythereum": "^2.0.0", + "prompts": "^2.4.2", + "shelljs": "^0.8.5", + "uuid": "^9.0.1", + "yargs": "^17.7.2" + }, + "bin": { + "forta-bot": "dist/index.js" + }, + "engines": { + "node": ">=20" + } }, - "@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dev": true, + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, - "@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "dev": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, - "@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==" + "node_modules/@noble/secp256k1": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.6.3.tgz", + "integrity": "sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] }, - "@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", - "requires": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" } }, - "@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==" + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } }, - "@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==" + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } }, - "@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==" + "node_modules/@scure/base": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.6.tgz", + "integrity": "sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==", + "dev": true, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, - "@protobufjs/pool": { + "node_modules/@scure/bip32": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==" + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.0.tgz", + "integrity": "sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.1.1", + "@noble/secp256k1": "~1.6.0", + "@scure/base": "~1.1.0" + } }, - "@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.5.tgz", + "integrity": "sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] }, - "@types/long": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", - "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==" + "node_modules/@scure/bip39": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz", + "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.1.1", + "@scure/base": "~1.1.0" + } }, - "@types/node": { - "version": "20.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz", - "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==" + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.5.tgz", + "integrity": "sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] }, - "@types/uuid": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", - "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==" + "node_modules/@types/node": { + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==", + "dev": true }, - "abbrev": { + "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, - "aes-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", - "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==" + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "dev": true }, - "ansi-regex": { + "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "ansi-styles": { + "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { + "dev": true, + "dependencies": { "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "anymatch": { + "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, - "requires": { + "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" - } - }, - "asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", - "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - } + "engines": { + "node": ">= 8" } }, - "async-retry": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", - "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", - "requires": { - "retry": "0.13.1" - } - }, - "asynckit": { + "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true }, - "awilix": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/awilix/-/awilix-4.3.4.tgz", - "integrity": "sha512-NgRwUPxUnoK+OTRa2fXcRQVFPOPQXlwCN1FJPkhO3IHKQJHokhdVpDfgz9L3VZTcA1iSaOFE3N/Q/5P7lIDqig==", - "requires": { + "node_modules/awilix": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/awilix/-/awilix-9.0.0.tgz", + "integrity": "sha512-DVhdT1sbCCjGBvJbNKJaPSh+JVvgzUV0Rbdq3r3/MqxDgm7e/zs8aAhWI8O8nFNFvUYFtJPqWsFldyzC2rpMnA==", + "dev": true, + "dependencies": { "camel-case": "^4.1.2", - "glob": "^7.1.6" + "fast-glob": "^3.3.1" + }, + "engines": { + "node": ">=14.0.0" } }, - "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "requires": { - "follow-redirects": "^1.14.0" + "node_modules/axios": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.0.tgz", + "integrity": "sha512-IiB0wQeKyPRdsFVhBgIo31FbzOyf2M6wYl7/NVutFwFBRMiAbjNiydJIHKeLmPugF4kJLfA1uWZ82Is2QzqqFA==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, - "balanced-match": { + "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, - "base64-arraybuffer": { + "node_modules/base64-arraybuffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", - "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==" + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } }, - "base64-js": { + "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" - }, - "bech32": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", - "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "binary-extensions": { + "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true - }, - "bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + "dev": true, + "engines": { + "node": ">=8" + } }, - "brace-expansion": { + "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { + "dev": true, + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "requires": { - "fill-range": "^7.0.1" + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" } }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" - }, - "browserify-aes": { + "node_modules/browserify-aes": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", - "requires": { + "dev": true, + "dependencies": { "buffer-xor": "^1.0.3", "cipher-base": "^1.0.0", "create-hash": "^1.1.0", @@ -685,157 +366,139 @@ "safe-buffer": "^5.0.1" } }, - "browserify-cipher": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" - } - }, - "browserify-des": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "browserify-rsa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.1.0.tgz", - "integrity": "sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==", - "requires": { - "bn.js": "^5.0.0", - "randombytes": "^2.0.1" - } - }, - "browserify-sign": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.2.tgz", - "integrity": "sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==", - "requires": { - "bn.js": "^5.2.1", - "browserify-rsa": "^4.1.0", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.4", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.6", - "readable-stream": "^3.6.2", - "safe-buffer": "^5.2.1" - } - }, - "buffer": { + "node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "requires": { + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" } }, - "buffer-xor": { + "node_modules/buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==", + "dev": true }, - "camel-case": { + "node_modules/camel-case": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "requires": { + "dev": true, + "dependencies": { "pascal-case": "^3.1.2", "tslib": "^2.0.3" } }, - "chokidar": { + "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, - "requires": { + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "cipher-base": { + "node_modules/cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", - "requires": { + "dev": true, + "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" } }, - "cliui": { + "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "requires": { + "dev": true, + "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "color-convert": { + "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { + "dev": true, + "dependencies": { "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "color-name": { + "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, - "combined-stream": { + "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { + "dev": true, + "dependencies": { "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "concat-map": { + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, - "create-ecdh": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", - "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.5.3" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - } - } + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, - "create-hash": { + "node_modules/create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "requires": { + "dev": true, + "dependencies": { "cipher-base": "^1.0.1", "inherits": "^2.0.1", "md5.js": "^1.3.4", @@ -843,1032 +506,1216 @@ "sha.js": "^2.4.0" } }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "des.js": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", - "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", - "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - }, "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true } } }, - "elliptic": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", - "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", - "requires": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - } + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" } }, - "emoji-regex": { + "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, - "error-ex": { + "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { + "dev": true, + "dependencies": { "is-arrayish": "^0.2.1" } }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" - }, - "ethers": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", - "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", - "requires": { - "@ethersproject/abi": "5.7.0", - "@ethersproject/abstract-provider": "5.7.0", - "@ethersproject/abstract-signer": "5.7.0", - "@ethersproject/address": "5.7.0", - "@ethersproject/base64": "5.7.0", - "@ethersproject/basex": "5.7.0", - "@ethersproject/bignumber": "5.7.0", - "@ethersproject/bytes": "5.7.0", - "@ethersproject/constants": "5.7.0", - "@ethersproject/contracts": "5.7.0", - "@ethersproject/hash": "5.7.0", - "@ethersproject/hdnode": "5.7.0", - "@ethersproject/json-wallets": "5.7.0", - "@ethersproject/keccak256": "5.7.0", - "@ethersproject/logger": "5.7.0", - "@ethersproject/networks": "5.7.1", - "@ethersproject/pbkdf2": "5.7.0", - "@ethersproject/properties": "5.7.0", - "@ethersproject/providers": "5.7.2", - "@ethersproject/random": "5.7.0", - "@ethersproject/rlp": "5.7.0", - "@ethersproject/sha2": "5.7.0", - "@ethersproject/signing-key": "5.7.0", - "@ethersproject/solidity": "5.7.0", - "@ethersproject/strings": "5.7.0", - "@ethersproject/transactions": "5.7.0", - "@ethersproject/units": "5.7.0", - "@ethersproject/wallet": "5.7.0", - "@ethersproject/web": "5.7.1", - "@ethersproject/wordlists": "5.7.0" - } - }, - "evp_bytestokey": { + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ethereum-cryptography": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz", + "integrity": "sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ==", + "dev": true, + "dependencies": { + "@noble/hashes": "1.1.2", + "@noble/secp256k1": "1.6.3", + "@scure/bip32": "1.1.0", + "@scure/bip39": "1.1.0" + } + }, + "node_modules/ethereum-cryptography/node_modules/@noble/hashes": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", + "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/ethers": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.1.tgz", + "integrity": "sha512-hdJ2HOxg/xx97Lm9HdCWk949BfYqYWpyw4//78SiwOLgASyfrNszfMUNB2joKjvGUdwhHfaiMMFFwacVVoLR9A==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "18.15.13", + "aes-js": "4.0.0-beta.5", + "tslib": "2.4.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "requires": { + "dev": true, + "dependencies": { "md5.js": "^1.3.4", "safe-buffer": "^5.1.1" } }, - "fast-safe-stringify": { + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-safe-stringify": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "requires": { + "dependencies": { "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "requires": { - "flatted": "^3.1.0", + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" } }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true }, - "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } }, - "form-data": { + "node_modules/form-data": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { + "dev": true, + "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" } }, - "forta-agent": { - "version": "0.1.45", - "resolved": "https://registry.npmjs.org/forta-agent/-/forta-agent-0.1.45.tgz", - "integrity": "sha512-QP+qsWPmA1kvyHVFSpGSnXxTODOpeoxfGil0HmcpO6dOuH+f6FY54oPyjrJ2Eg+kiB1skO9a1SIdW16N1UmELg==", - "requires": { - "@grpc/grpc-js": "^1.3.6", - "@grpc/proto-loader": "^0.6.4", - "@types/uuid": "^8.3.4", - "async-retry": "^1.3.3", - "awilix": "^4.3.4", - "axios": "^0.21.1", - "base64-arraybuffer": "^1.0.2", - "ethers": "^5.5.1", - "flat-cache": "^3.0.4", - "form-data": "^4.0.0", - "jsonc": "^2.0.0", - "keythereum": "^1.2.0", - "lodash": "^4.17.21", - "murmurhash3js": "^3.0.1", - "n-readlines": "^1.0.1", - "prompts": "^2.4.1", - "python-shell": "^3.0.0", - "sha3": "^2.1.4", - "shelljs": "^0.8.4", - "uuid": "^8.3.2", - "yargs": "^17.0.1" - } - }, - "fs.realpath": { + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true }, - "fsevents": { + "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "optional": true + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "get-caller-file": { + "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } }, - "glob": { + "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "requires": { + "dev": true, + "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "glob-parent": { + "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "requires": { + "dependencies": { "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, - "graceful-fs": { + "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true }, - "has-flag": { + "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "hash-base": { + "node_modules/hash-base": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "requires": { + "dev": true, + "dependencies": { "inherits": "^2.0.4", "readable-stream": "^3.6.0", "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" } }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "ieee754": { + "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "ignore-by-default": { + "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true }, - "inflight": { + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "requires": { + "dev": true, + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true }, - "interpret": { + "node_modules/interpret": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } }, - "is-arrayish": { + "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true }, - "is-binary-path": { + "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, - "requires": { + "dependencies": { "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", - "requires": { - "has": "^1.0.3" + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-extglob": { + "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-fullwidth-code-point": { + "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "is-glob": { + "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "requires": { + "dependencies": { "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-number": { + "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.12.0" + } }, - "js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true }, - "json-parse-better-errors": { + "node_modules/json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true }, - "jsonc": { + "node_modules/jsonc": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/jsonc/-/jsonc-2.0.0.tgz", "integrity": "sha512-B281bLCT2TRMQa+AQUQY5AGcqSOXBOKaYGP4wDzoA/+QswUfN8sODektbPEs9Baq7LGKun5jQbNFpzwGuVYKhw==", - "requires": { + "dev": true, + "dependencies": { "fast-safe-stringify": "^2.0.6", "graceful-fs": "^4.1.15", "mkdirp": "^0.5.1", "parse-json": "^4.0.0", "strip-bom": "^4.0.0", "strip-json-comments": "^3.0.1" + }, + "engines": { + "node": ">=8" } }, - "keccak": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.1.tgz", - "integrity": "sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA==", - "requires": { - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0" + "node_modules/keythereum": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/keythereum/-/keythereum-2.0.0.tgz", + "integrity": "sha512-1l5DMKJXWb15+1PdRnv4s7lUWGuvZV2MLlIUc7bHuxO2u9Y+QAqeYYi8g3lcER23nK+Mk+icNS09PWj3UUOAlw==", + "dev": true, + "dependencies": { + "browserify-aes": "1.2.0", + "ethereum-cryptography": "1.1.2", + "uuid": "8.3.2" } }, - "keythereum": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/keythereum/-/keythereum-1.2.0.tgz", - "integrity": "sha512-u3XnjIruOmjIvJ4tH1Wdr2y0X8+z8BZTQ+dqJuDMyLvNWw6VnH9XKtt0yauSE+96Bq97h6CPm4w5LbW3i28x0g==", - "requires": { - "crypto-browserify": "3.12.0", - "keccak": "3.0.1", - "scrypt-js": "3.0.1", - "secp256k1": "4.0.2", - "sjcl": "1.0.6", - "uuid": "3.0.0" - }, - "dependencies": { - "uuid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz", - "integrity": "sha512-rqE1LoOVLv3QrZMjb4NkF5UWlkurCfPyItVnFPNKDDGkHw4dQUdE4zMcLqx28+0Kcf3+bnUk4PisaiRJT4aiaQ==" - } + "node_modules/keythereum/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" } }, - "kleur": { + "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "lodash": { + "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" - }, - "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, - "lower-case": { + "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "requires": { + "dev": true, + "dependencies": { "tslib": "^2.0.3" } }, - "md5.js": { + "node_modules/lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "requires": { + "dev": true, + "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1", "safe-buffer": "^5.1.2" } }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" - } + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "mime-db": { + "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } }, - "mime-types": { + "node_modules/mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { + "dev": true, + "dependencies": { "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" - }, - "minimatch": { + "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { + "dev": true, + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "minimist": { + "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "mkdirp": { + "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "requires": { + "dev": true, + "dependencies": { "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" } }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "murmurhash3js": { + "node_modules/murmurhash3js": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/murmurhash3js/-/murmurhash3js-3.0.1.tgz", - "integrity": "sha512-KL8QYUaxq7kUbcl0Yto51rMcYt7E/4N4BG3/c96Iqw1PQrTRspu8Cpx4TZ4Nunib1d4bEkIH3gjCYlP2RLBdow==" - }, - "n-readlines": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/n-readlines/-/n-readlines-1.0.1.tgz", - "integrity": "sha512-z4SyAIVgMy7CkgsoNw7YVz40v0g4+WWvvqy8+ZdHrCtgevcEO758WQyrYcw3XPxcLxF+//RszTz/rO48nzD0wQ==" + "integrity": "sha512-KL8QYUaxq7kUbcl0Yto51rMcYt7E/4N4BG3/c96Iqw1PQrTRspu8Cpx4TZ4Nunib1d4bEkIH3gjCYlP2RLBdow==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "no-case": { + "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "requires": { + "dev": true, + "dependencies": { "lower-case": "^2.0.2", "tslib": "^2.0.3" } }, - "node-addon-api": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", - "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" - }, - "node-gyp-build": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", - "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==" - }, - "nodemon": { - "version": "2.0.22", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", - "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", + "node_modules/nodemon": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz", + "integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==", "dev": true, - "requires": { + "dependencies": { "chokidar": "^3.5.2", - "debug": "^3.2.7", + "debug": "^4", "ignore-by-default": "^1.0.1", "minimatch": "^3.1.2", "pstree.remy": "^1.1.8", - "semver": "^5.7.1", - "simple-update-notifier": "^1.0.7", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", "supports-color": "^5.5.0", "touch": "^3.1.0", "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" } }, - "nopt": { + "node_modules/nopt": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", "dev": true, - "requires": { + "dependencies": { "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" } }, - "normalize-path": { + "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "requires": { + "dev": true, + "dependencies": { "wrappy": "1" } }, - "parse-asn1": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", - "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", - "requires": { - "asn1.js": "^5.2.0", - "browserify-aes": "^1.0.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "parse-json": { + "node_modules/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", - "requires": { + "dev": true, + "dependencies": { "error-ex": "^1.3.1", "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" } }, - "pascal-case": { + "node_modules/pascal-case": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "requires": { + "dev": true, + "dependencies": { "no-case": "^3.0.4", "tslib": "^2.0.3" } }, - "path-is-absolute": { + "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-parse": { + "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" - }, - "pbkdf2": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", - "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, - "picomatch": { + "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "prompts": { + "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "requires": { + "dev": true, + "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" } }, - "protobufjs": { - "version": "6.11.4", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz", - "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==", - "requires": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.1", - "@types/node": ">=13.7.0", - "long": "^4.0.0" - } - }, - "pstree.remy": { + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - }, - "dependencies": { - "bn.js": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", - "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" } - } - }, - "python-shell": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/python-shell/-/python-shell-3.0.1.tgz", - "integrity": "sha512-TWeotuxe1auhXa5bGRScxnc2J+0r41NBntSa6RYZtMBLtAEsvCboKrEbW6DvASosWQepVkhZZlT3B5Ei766G+Q==" - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } + ] }, - "readable-stream": { + "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "requires": { + "dev": true, + "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" } }, - "readdirp": { + "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, - "requires": { + "dependencies": { "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "rechoir": { + "node_modules/rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "requires": { + "dev": true, + "dependencies": { "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" } }, - "require-directory": { + "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" - }, - "resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "requires": { - "is-core-module": "^2.11.0", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } }, - "rimraf": { + "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { + "dev": true, + "dependencies": { "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "ripemd160": { + "node_modules/ripemd160": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "requires": { + "dev": true, + "dependencies": { "hash-base": "^3.0.0", "inherits": "^2.0.1" } }, - "safe-buffer": { + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "scrypt-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", - "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" - }, - "secp256k1": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz", - "integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==", - "requires": { - "elliptic": "^6.5.2", - "node-addon-api": "^2.0.0", - "node-gyp-build": "^4.2.0" - } - }, - "semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", - "dev": true + "node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } }, - "sha.js": { + "node_modules/sha.js": { "version": "2.4.11", "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "requires": { + "dev": true, + "dependencies": { "inherits": "^2.0.1", "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" } }, - "sha3": { + "node_modules/sha3": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.4.tgz", "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==", - "requires": { + "dev": true, + "dependencies": { "buffer": "6.0.3" } }, - "shelljs": { + "node_modules/shelljs": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", - "requires": { + "dev": true, + "dependencies": { "glob": "^7.0.0", "interpret": "^1.0.0", "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" } }, - "simple-update-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", - "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", "dev": true, - "requires": { - "semver": "~7.0.0" - }, "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" } }, - "sisteransi": { + "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true }, - "sjcl": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/sjcl/-/sjcl-1.0.6.tgz", - "integrity": "sha512-oUVs+hzMSWEZ3rdeDL461QilvvEU2OL9q6T42lpVi2C5Proej9obVZ1nQeY9T96NxoMy/dqw82m33MfNNEmYJg==" + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } }, - "string-width": { + "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { + "dev": true, + "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "strip-ansi": { + "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { + "dev": true, + "dependencies": { "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-bom": { + "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "strip-json-comments": { + "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "supports-color": { + "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "requires": { + "dependencies": { "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "supports-preserve-symlinks-flag": { + "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==" + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "to-regex-range": { + "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "requires": { + "dependencies": { "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "touch": { + "node_modules/touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", "dev": true, - "requires": { + "dependencies": { "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" } }, - "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true }, - "undefsafe": { + "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } }, - "wrap-ansi": { + "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "requires": { + "dev": true, + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, - "ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==" + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } }, - "y18n": { + "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } }, - "yargs": { + "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "requires": { + "dev": true, + "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", @@ -1876,12 +1723,19 @@ "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" } }, - "yargs-parser": { + "node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } } } } diff --git a/address-poisoning-py/package.json b/address-poisoning-py/package.json index eeb4ab58..c5589d81 100644 --- a/address-poisoning-py/package.json +++ b/address-poisoning-py/package.json @@ -1,7 +1,7 @@ { "name": "address-poisoning-detector", - "displayName": "Address Poisoning Detector", - "version": "0.4.3", + "displayName": "Address Poisoning Detector - Linea", + "version": "0.5.0", "description": "Bot detecting address poisoning - zero value, low value, and fake token phishing transfers", "repository": "https://github.com/forta-network/starter-kits/tree/main/address-poisoning-py", "chainIds": [ @@ -11,7 +11,8 @@ 42161, 10, 250, - 43114 + 43114, + 59144 ], "chainSettings": { "1": { @@ -34,27 +35,26 @@ "scripts": { "postinstall": "python3 -m pip install -r requirements_dev.txt", "start": "npm run start:dev", - "start:dev": "nodemon --watch src --watch forta.config.json -e py --exec \"forta-agent run\"", - "start:prod": "forta-agent run --prod", - "tx": "forta-agent run --tx", - "block": "forta-agent run --block", - "range": "forta-agent run --range", - "alert": "forta-agent run --alert", - "sequence": "forta-agent run --sequence", - "file": "forta-agent run --file", - "publish": "forta-agent publish", - "info": "forta-agent info", - "logs": "forta-agent logs", - "push": "forta-agent push", - "disable": "forta-agent disable", - "enable": "forta-agent enable", - "keyfile": "forta-agent keyfile", + "start:dev": "nodemon --watch src --watch forta.config.json -e py --exec \"python3 ./src/agent.py\"", + "start:prod": "node ./src/agent.js", + "tx": "forta-bot run --tx", + "block": "forta-bot run --block", + "range": "forta-bot run --range", + "alert": "forta-bot run --alert", + "sequence": "forta-bot run --sequence", + "file": "forta-bot run --file", + "publish": "forta-bot publish", + "info": "forta-bot info", + "logs": "forta-bot logs", + "push": "forta-bot push", + "disable": "forta-bot disable", + "enable": "forta-bot enable", + "keyfile": "forta-bot keyfile", + "stake": "forta-bot stake", "test": "python3 -m pytest" }, - "dependencies": { - "forta-agent": "^0.1.45" - }, "devDependencies": { - "nodemon": "^2.0.8" + "@fortanetwork/forta-bot-cli": "^0.2.2", + "nodemon": "^3.0.2" } } diff --git a/address-poisoning-py/publish.log b/address-poisoning-py/publish.log index b157ce9f..5facefe4 100644 --- a/address-poisoning-py/publish.log +++ b/address-poisoning-py/publish.log @@ -23,3 +23,18 @@ Wed, 27 Sep 2023 08:09:15 GMT: successfully updated agent id 0x98b87a29ecb6c8c0f Thu, 09 Nov 2023 16:14:56 GMT: successfully updated agent id 0x98b87a29ecb6c8c0f8e6ea83598817ec91e01c15d379f03c7ff781fd1141e502 with manifest QmeeG27yo2Kf5v71EPWw2bsfWZ6ubNL2hDHWRgGbxNByK8 Fri, 17 Nov 2023 13:51:31 GMT: successfully updated agent id 0xf7bdbc51869b0a74aba61ade31adac96622a48108af5fb50a1d5d197bfdb6b9e with manifest QmY1aebYQXZavPH1kEA2adYjjGXCZg8WCifoyDWLnBVJun Mon, 12 Feb 2024 11:43:31 GMT: successfully updated agent id 0x98b87a29ecb6c8c0f8e6ea83598817ec91e01c15d379f03c7ff781fd1141e502 with manifest QmXVj7LCEmGVJtHF24HD3Hn11Z9S4sDj8MQF476DLsG7gB +Tue, 21 May 2024 14:42:01 GMT: successfully added bot id 0xcd233b284c8340ff12d38fdd594d7f3b8f4449571c0fd6d2fdb8db8a7e45991b with manifest QmcA6Zc5VAtAzHmZTfbzGR2aTS5Gbwbq1u1FHhnbrgoHNY +Tue, 21 May 2024 15:14:57 GMT: successfully updated bot id 0xcd233b284c8340ff12d38fdd594d7f3b8f4449571c0fd6d2fdb8db8a7e45991b with manifest QmRpzUfcz7WLKpc2gSFULTGr7ja4xt3yxbYCLseyk81Z1R +Tue, 21 May 2024 15:21:03 GMT: successfully updated bot id 0xcd233b284c8340ff12d38fdd594d7f3b8f4449571c0fd6d2fdb8db8a7e45991b with manifest QmPNUWTf7yt1ZaxBxH5DPha1zPxwkE9zDt19szL4U4sn8t +Tue, 21 May 2024 15:56:06 GMT: successfully updated bot id 0xcd233b284c8340ff12d38fdd594d7f3b8f4449571c0fd6d2fdb8db8a7e45991b with manifest QmQ7zpUp2onLCCKe429Vcymh4LedignbLRRtg5qQhLyt49 +Tue, 21 May 2024 16:03:38 GMT: successfully updated bot id 0xcd233b284c8340ff12d38fdd594d7f3b8f4449571c0fd6d2fdb8db8a7e45991b with manifest QmfLXuV9zYoh2Y6tNy6ZBekmT1qjeCEVuNjjGoMfJuzXss +Tue, 21 May 2024 16:05:04 GMT: successfully disabled bot id 0xcd233b284c8340ff12d38fdd594d7f3b8f4449571c0fd6d2fdb8db8a7e45991b +Tue, 21 May 2024 16:06:01 GMT: successfully enabled bot id 0xcd233b284c8340ff12d38fdd594d7f3b8f4449571c0fd6d2fdb8db8a7e45991b +Tue, 21 May 2024 16:22:52 GMT: successfully updated bot id 0xcd233b284c8340ff12d38fdd594d7f3b8f4449571c0fd6d2fdb8db8a7e45991b with manifest QmfFyYHTJo5FNdhJQybmNyTcHzzkb5pDiUc4BuXXAgVdns +Tue, 21 May 2024 16:27:39 GMT: successfully disabled bot id 0xcd233b284c8340ff12d38fdd594d7f3b8f4449571c0fd6d2fdb8db8a7e45991b +Tue, 21 May 2024 16:32:08 GMT: successfully updated bot id 0xcd233b284c8340ff12d38fdd594d7f3b8f4449571c0fd6d2fdb8db8a7e45991b with manifest Qmbj3bUtyLCdoTBbH2ZaM3BbNyPf57wZyEQzAe2uXLcHKy +Tue, 21 May 2024 16:32:33 GMT: successfully enabled bot id 0xcd233b284c8340ff12d38fdd594d7f3b8f4449571c0fd6d2fdb8db8a7e45991b +Tue, 21 May 2024 16:38:51 GMT: successfully disabled bot id 0xcd233b284c8340ff12d38fdd594d7f3b8f4449571c0fd6d2fdb8db8a7e45991b +Tue, 21 May 2024 16:40:31 GMT: successfully enabled bot id 0xcd233b284c8340ff12d38fdd594d7f3b8f4449571c0fd6d2fdb8db8a7e45991b +Tue, 21 May 2024 16:51:37 GMT: successfully updated bot id 0xcd233b284c8340ff12d38fdd594d7f3b8f4449571c0fd6d2fdb8db8a7e45991b with manifest QmbQ7shbRPYCqP6dsx9AqRiavYdxr8srf7BF4F2r7xbRhF +Tue, 25 Jun 2024 13:35:40 GMT: successfully updated bot id 0xcd233b284c8340ff12d38fdd594d7f3b8f4449571c0fd6d2fdb8db8a7e45991b with manifest QmTa3DeRwCiL68QXtyqaaxMmtMyUpjCrkQU9cE2ykq66bf diff --git a/address-poisoning-py/requirements.txt b/address-poisoning-py/requirements.txt index d46f9827..9326c403 100644 --- a/address-poisoning-py/requirements.txt +++ b/address-poisoning-py/requirements.txt @@ -1,2 +1,2 @@ -forta_agent>=0.1.25 +forta_bot_sdk>=0.2.3 setuptools>=61.3.1 \ No newline at end of file diff --git a/address-poisoning-py/requirements_dev.txt b/address-poisoning-py/requirements_dev.txt index ec99dbbc..c95d891b 100644 --- a/address-poisoning-py/requirements_dev.txt +++ b/address-poisoning-py/requirements_dev.txt @@ -1,3 +1,4 @@ -r requirements.txt -pytest==6.2.5 -pytest-env==0.6.2 \ No newline at end of file +pytest==8.2.0 +pytest-env==1.1.3 +pytest-asyncio==0.23.6 \ No newline at end of file diff --git a/address-poisoning-py/src/agent.py b/address-poisoning-py/src/agent.py index 9a824bbe..d40e3e19 100644 --- a/address-poisoning-py/src/agent.py +++ b/address-poisoning-py/src/agent.py @@ -1,16 +1,16 @@ -from forta_agent import get_json_rpc_url, Web3 -from src.findings import AddressPoisoningFinding -from src.rules import AddressPoisoningRules -from src.constants import * -from src.blockexplorer import BlockExplorer -from src.storage import get_secrets +from forta_bot_sdk import scan_ethereum, get_chain_id, TransactionEvent +from web3 import AsyncWeb3 +from findings import AddressPoisoningFinding +from rules import AddressPoisoningRules +from constants import * +from blockexplorer import BlockExplorer +from storage import get_secrets import logging import sys +import asyncio -# Initialize web3 -web3 = Web3(Web3.HTTPProvider(get_json_rpc_url())) +# Initialize heuristic = AddressPoisoningRules() -blockexplorer = BlockExplorer(web3.eth.chain_id) CHAIN_ID = -1 # Logging set up. @@ -18,7 +18,8 @@ root.setLevel(logging.INFO) handler = logging.StreamHandler(sys.stdout) handler.setLevel(logging.INFO) -formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') +formatter = logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) root.addHandler(handler) @@ -35,7 +36,10 @@ secrets = None -def initialize(): + +async def initialize(): + logging.info('Initializing...') + """ Global variables for anomaly score initialized here, but also tracking known phishing contracts to improve efficiency. @@ -62,17 +66,22 @@ def initialize(): fake_value_phishing_contracts = set() global CHAIN_ID - CHAIN_ID = web3.eth.chain_id + CHAIN_ID = get_chain_id() + + global blockexplorer + blockexplorer = BlockExplorer(get_chain_id()) global secrets - secrets = get_secrets() + secrets = await get_secrets() + def parse_logs_for_transfer_and_approval_info(transaction_event, contracts): transfer_logs = [] for contract in contracts: try: - token_transfer_logs = transaction_event.filter_log(TRANSFER_EVENT_ABI, contract) + token_transfer_logs = transaction_event.filter_log( + TRANSFER_EVENT_ABI, contract) if len(token_transfer_logs) > 0: for log in token_transfer_logs: transfer_logs.append(log) @@ -82,7 +91,7 @@ def parse_logs_for_transfer_and_approval_info(transaction_event, contracts): return transfer_logs -def get_attacker_victim_lists(w3, decoded_logs, alert_type): +async def get_attacker_victim_lists(w3, decoded_logs, alert_type): log_args = [log['args'] for log in decoded_logs] attackers = [] victims = [] @@ -98,9 +107,11 @@ def get_attacker_victim_lists(w3, decoded_logs, alert_type): # Retrieve or calculate transaction counts and store them if from_address not in tx_counts: - tx_counts[from_address] = w3.eth.get_transaction_count(from_address) + tx_counts[from_address] = await w3.eth.get_transaction_count( + from_address) if to_address not in tx_counts: - tx_counts[to_address] = w3.eth.get_transaction_count(to_address) + tx_counts[to_address] = await w3.eth.get_transaction_count( + to_address) from_tx_count = tx_counts[from_address] to_tx_count = tx_counts[to_address] @@ -121,10 +132,11 @@ def get_attacker_victim_lists(w3, decoded_logs, alert_type): continue if (from_as_attacker_count > to_as_attacker_count and to_address in attackers) or (to_as_attacker_count > from_as_attacker_count and from_address in attackers): - index = attackers.index(from_address if from_address in attackers else to_address) + index = attackers.index( + from_address if from_address in attackers else to_address) attackers[index], victims[index] = victims[index], attackers[index] attackers = list(set(attackers)) - victims = list(set(victims)) + victims = list(set(victims)) elif alert_type == "ADDRESS-POISONING-LOW-VALUE": senders = [str.lower(log['from']) for log in log_args] receivers = [str.lower(log['to']) for log in log_args] @@ -136,14 +148,15 @@ def get_attacker_victim_lists(w3, decoded_logs, alert_type): def check_for_similar_transfer(blockexplorer, decoded_logs, victims): - address_token_value_data = [(log['args']['to'], log['address'], log['args']['value']) for log in decoded_logs \ - if str.lower(log['args']['to']) in victims] + address_token_value_data = [(log['args']['to'], log['address'], log['args']['value']) for log in decoded_logs + if str.lower(log['args']['to']) in victims] def process_entry(entry): try: transfer_history = blockexplorer.make_token_history_query(entry) except Exception as e: - logging.info(f"Failed to retrieve transaction history from blockexplorer: {e}") + logging.info( + f"Failed to retrieve transaction history from blockexplorer: {e}") transfer_history = None return (transfer_history is None, str(entry[2])[:3] not in str(transfer_history) if transfer_history is not None else False) @@ -164,8 +177,7 @@ def process_entry(entry): return True - -def detect_address_poisoning(w3, blockexplorer, heuristic, transaction_event): +async def detect_address_poisoning(w3, blockexplorer, heuristic, transaction_event): """ Expanded to check for zero value phishing, low value phishing, and fake token phishing :return: detect_address_poisoning: list(Finding) @@ -188,7 +200,8 @@ def detect_address_poisoning(w3, blockexplorer, heuristic, transaction_event): return [] findings = [] - logs = w3.eth.get_transaction_receipt(transaction_event.hash)['logs'] + receipt = await w3.eth.get_transaction_receipt(transaction_event.hash) + logs = receipt['logs'] # Return alert type if previously detected, but if not return empty string ALERT_TYPE = heuristic.have_addresses_been_detected( @@ -214,62 +227,73 @@ def detect_address_poisoning(w3, blockexplorer, heuristic, transaction_event): else: contracts = STABLECOIN_CONTRACTS[CHAIN_ID] - transfer_logs = parse_logs_for_transfer_and_approval_info(transaction_event, contracts) - attackers, victims = get_attacker_victim_lists(w3, transfer_logs, ALERT_TYPE) + transfer_logs = parse_logs_for_transfer_and_approval_info( + transaction_event, contracts) + attackers, victims = await get_attacker_victim_lists( + w3, transfer_logs, ALERT_TYPE) findings.append( - AddressPoisoningFinding.create_finding(transaction_event, score, len(logs), attackers, victims, ALERT_TYPE) + AddressPoisoningFinding.create_finding( + transaction_event, score, len(logs), attackers, victims, ALERT_TYPE, CHAIN_ID) ) return findings - - elif (heuristic.is_contract(w3, transaction_event.to) - # and not blockexplorer.is_verified(transaction_event.to) - and not heuristic.are_tokens_minted(logs)): + + elif ((await heuristic.is_contract(w3, transaction_event.to)) + # and not blockexplorer.is_verified(transaction_event.to) + and not heuristic.are_tokens_minted(logs)): denominator_count += 1 - + if CHAIN_ID == 137: logs = logs[:-1] - + log_length = len(logs) - + # Zero value address poisoning heuristic -> - if (log_length >= 3 - and heuristic.are_all_logs_stablecoins(logs, CHAIN_ID) >= 0.4 - and heuristic.are_all_logs_transfers_or_approvals(logs) - and heuristic.is_zero_value_tx(logs, CHAIN_ID)): + if (log_length >= 3 + and heuristic.are_all_logs_stablecoins(logs, CHAIN_ID) >= 0.4 + and heuristic.are_all_logs_transfers_or_approvals(logs) + and heuristic.is_zero_value_tx(logs, CHAIN_ID)): logging.info(f"Detected phishing transaction from EOA: {transaction_event.from_}, and Contract: {transaction_event.to}") ALERT_TYPE = "ADDRESS-POISONING-ZERO-VALUE" zero_value_alert_count += 1 score = (1.0 * zero_value_alert_count) / denominator_count zero_value_phishing_contracts.update([transaction_event.to]) - transfer_logs = parse_logs_for_transfer_and_approval_info(transaction_event, STABLECOIN_CONTRACTS[CHAIN_ID]) - attackers, victims = get_attacker_victim_lists(w3, transfer_logs, ALERT_TYPE) + transfer_logs = parse_logs_for_transfer_and_approval_info( + transaction_event, STABLECOIN_CONTRACTS[CHAIN_ID]) + attackers, victims = await get_attacker_victim_lists( + w3, transfer_logs, ALERT_TYPE) attackers.extend([transaction_event.from_, transaction_event.to]) # Fake token address poisoning heuristic -> elif (log_length >= 5 - and heuristic.are_all_logs_transfers_or_approvals(logs) - and heuristic.are_tokens_using_known_symbols(w3, logs, CHAIN_ID)): + and heuristic.are_all_logs_transfers_or_approvals(logs) + and (await heuristic.are_tokens_using_known_symbols(w3, logs, CHAIN_ID))): logging.info(f"Detected phishing transaction from EOA: {transaction_event.from_}, and Contract: {transaction_event.to}") ALERT_TYPE = "ADDRESS-POISONING-FAKE-TOKEN" fake_token_alert_count += 1 score = (1.0 * fake_token_alert_count) / denominator_count fake_value_phishing_contracts.update([transaction_event.to]) fake_contracts = set([log['address'] for log in logs]) - transfer_logs = parse_logs_for_transfer_and_approval_info(transaction_event, fake_contracts) - attackers, victims = get_attacker_victim_lists(w3, transfer_logs, ALERT_TYPE) + transfer_logs = parse_logs_for_transfer_and_approval_info( + transaction_event, fake_contracts) + attackers, victims = await get_attacker_victim_lists( + w3, transfer_logs, ALERT_TYPE) attackers.extend([transaction_event.from_, transaction_event.to]) - + # Low value address poisoning heuristic -> elif (log_length >= 10 - and heuristic.are_all_logs_stablecoins(logs, CHAIN_ID) >= 0.9 - and heuristic.are_all_logs_transfers_or_approvals(logs) - and heuristic.is_data_field_repeated(logs)): - logging.info(f"Possible low-value address poisoning - making additional checks...") + and heuristic.are_all_logs_stablecoins(logs, CHAIN_ID) >= 0.9 + and heuristic.are_all_logs_transfers_or_approvals(logs) + and heuristic.is_data_field_repeated(logs)): + logging.info( + f"Possible low-value address poisoning - making additional checks...") PENDING_ALERT_TYPE = "ADDRESS-POISONING-LOW-VALUE" - transfer_logs = parse_logs_for_transfer_and_approval_info(transaction_event, STABLECOIN_CONTRACTS[CHAIN_ID]) - attackers, victims = get_attacker_victim_lists(w3, transfer_logs, PENDING_ALERT_TYPE) + transfer_logs = parse_logs_for_transfer_and_approval_info( + transaction_event, STABLECOIN_CONTRACTS[CHAIN_ID]) + attackers, victims = await get_attacker_victim_lists( + w3, transfer_logs, PENDING_ALERT_TYPE) + if ((len(attackers) - len(victims)) == 1 - and check_for_similar_transfer(blockexplorer, transfer_logs, victims)): + and check_for_similar_transfer(blockexplorer, transfer_logs, victims)): logging.info(f"Detected phishing transaction from EOA: {transaction_event.from_}, and Contract: {transaction_event.to}") ALERT_TYPE = PENDING_ALERT_TYPE low_value_alert_count += 1 @@ -280,22 +304,29 @@ def detect_address_poisoning(w3, blockexplorer, heuristic, transaction_event): if ALERT_TYPE != "": logging.info("Appending finding...") findings.append( - AddressPoisoningFinding.create_finding(transaction_event, score, log_length, attackers, victims, ALERT_TYPE) + AddressPoisoningFinding.create_finding( + transaction_event, score, log_length, attackers, victims, ALERT_TYPE, CHAIN_ID) ) logging.info(f"Alert counts: {zero_value_alert_count, low_value_alert_count, fake_token_alert_count}") return findings -def provide_handle_transaction(w3, blockexplorer, heuristic): - def handle_transaction(transaction_event): - return detect_address_poisoning(w3, blockexplorer, heuristic, transaction_event) +async def handle_transaction(transaction_event: TransactionEvent, web3: AsyncWeb3.AsyncHTTPProvider): + return await detect_address_poisoning(web3, blockexplorer, heuristic, transaction_event) - return handle_transaction +async def main(): + await initialize() -real_handle_transaction = provide_handle_transaction(web3, blockexplorer, heuristic) + await asyncio.gather( + scan_ethereum({ + 'rpc_url': 'https://rpc.linea.build', + 'local_rpc_url': "59144", + 'handle_transaction': handle_transaction + }), + ) -def handle_transaction(transaction_event): - return real_handle_transaction(transaction_event) +if __name__ == "__main__": + asyncio.run(main()) diff --git a/address-poisoning-py/src/agent_test.py b/address-poisoning-py/src/agent_test.py index cff03f21..cf9861a4 100644 --- a/address-poisoning-py/src/agent_test.py +++ b/address-poisoning-py/src/agent_test.py @@ -1,64 +1,82 @@ from unittest.mock import MagicMock import agent -from forta_agent import create_transaction_event, TransactionEvent, Web3, get_json_rpc_url +from forta_bot_sdk import create_transaction_event, TransactionEvent, get_chain_id from web3_mock import * from rules import AddressPoisoningRules from blockexplorer_mock import BlockExplorerMock from blockexplorer import BlockExplorer import timeit +import pytest +from unittest.mock import patch +from web3 import AsyncWeb3 w3 = Web3Mock() blockexplorer = BlockExplorerMock(w3.eth.chain_id) heuristic = AddressPoisoningRules() -real_w3 = Web3(Web3.HTTPProvider(get_json_rpc_url())) -real_blockexplorer = BlockExplorer(real_w3.eth.chain_id) +w3.to_checksum_address = AsyncWeb3.to_checksum_address +real_w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider("https://rpc.ankr.com/eth")) + + +@pytest.fixture(scope="module") +async def real_blockexplorer(): + yield BlockExplorer(get_chain_id()) + class TestAddressPoisoningAgent: - def test_transaction_processing_speed(self): - agent.initialize() + @pytest.mark.asyncio + async def test_transaction_processing_speed(self, real_blockexplorer): + agent.CHAIN_ID = 1 global transaction_to_eoa - transaction_to_eoa = create_transaction_event({ - 'transaction': { + transaction_to_eoa = create_transaction_event( + { 'hash': "0x4a419b16152cc6513db84bdfb94818827ef8e64c2bc52f9a95f960c47ef02817", 'from': "0x5b7d833a4aa182bddd754db797d43d3c29c171a0", 'value': 100, 'to': "0xb2880739e3bd3f535d38760cd8f2ee058737341b", - } - }) + }, + {}, + 1 + ) global transaction_to_contract - transaction_to_contract = create_transaction_event({ - 'transaction': { + transaction_to_contract = create_transaction_event( + { 'hash': "0x00159936a7412553e0736c6f4d6cf91028ddd2defb8a3427318f7599b941f42d", 'from': "0xb2880739e3bd3f535d38760cd8f2ee058737341b", 'value': 0, 'to': "0xa9d7c7466b9d0a76397d5a226da0024fdcff0ee1", - } - }) + }, + {}, + 1 + ) global transaction_to_verified_contract - transaction_to_verified_contract = create_transaction_event({ - 'transaction': { + transaction_to_verified_contract = create_transaction_event( + { 'hash': "0x0af2f2d106bff1950805d610e927344a2477b8be138ab4d4269e72b9aab5ddec", 'from': "0x2a038e100f8b85df21e4d44121bdbfe0c288a869", 'value': 0, 'to': "0xba8da9dcf11b50b03fd5284f164ef5cdef910705", - } - }) + }, + {}, + 1 + ) global fake_token_phishing_tx - fake_token_phishing_tx = create_transaction_event({ - 'transaction': { + fake_token_phishing_tx = create_transaction_event( + { 'hash': "0xae818f01e8e911da67d87a577ecfc04443cad96ac3e099b2dac44633594d7311", 'from': "0xb2880739e3bd3f535d38760cd8f2ee058737341b", 'value': 0, 'to': "0xa9d7c7466b9d0a76397d5a226da0024fdcff0ee1", - } - }) + }, + {}, + 1 + ) # Chain: Blocktime, Number of Tx -> Avg processing time in ms target # Ethereum: 12s, 150 -> 80ms @@ -71,32 +89,56 @@ def test_transaction_processing_speed(self): processing_runs = 10 - processing_time_to_eoa = timeit.timeit('agent.detect_address_poisoning(real_w3, real_blockexplorer, heuristic, transaction_to_eoa)', number=processing_runs, globals=globals()) * 1000 / processing_runs - processing_time_to_contract = timeit.timeit('agent.detect_address_poisoning(real_w3, real_blockexplorer, heuristic, transaction_to_contract)', number=processing_runs, globals=globals()) * 1000 / processing_runs - processing_time_to_verified_contract = timeit.timeit('agent.detect_address_poisoning(real_w3, real_blockexplorer, heuristic, transaction_to_verified_contract)', number=processing_runs, globals=globals()) * 1000 / processing_runs - processing_time_fake_token = timeit.timeit('agent.detect_address_poisoning(real_w3, real_blockexplorer, heuristic, fake_token_phishing_tx)', number=processing_runs, globals=globals()) * 1000 / processing_runs - - assert (processing_time_to_eoa * 0.33 + processing_time_to_contract * 0.33 + processing_time_to_verified_contract * 0.33 + processing_time_fake_token * 0.01)/8 < 160, f"Time is {(processing_time_to_eoa * 0.33 + processing_time_to_contract * 0.33 + processing_time_to_verified_contract * 0.33 + processing_time_fake_token * 0.01)/8}, normal: {processing_time_to_eoa} - {processing_time_to_contract} - {processing_time_to_verified_contract} - {processing_time_fake_token}" - - - def test_transfer_to_eoa(self): - agent.initialize() - + processing_time_to_eoa = 0 + for _ in range(processing_runs): + start_time = asyncio.get_event_loop().time() + await agent.detect_address_poisoning(real_w3, real_blockexplorer, heuristic, transaction_to_eoa) + end_time = asyncio.get_event_loop().time() + processing_time_to_eoa += (end_time - start_time) * 1000 + processing_time_to_eoa /= processing_runs + + processing_time_to_contract = 0 + for _ in range(processing_runs): + start_time = asyncio.get_event_loop().time() + await agent.detect_address_poisoning(real_w3, real_blockexplorer, heuristic, transaction_to_contract) + end_time = asyncio.get_event_loop().time() + processing_time_to_contract += (end_time - start_time) * 1000 + processing_time_to_contract /= processing_runs + + processing_time_to_verified_contract = 0 + for _ in range(processing_runs): + start_time = asyncio.get_event_loop().time() + await agent.detect_address_poisoning(real_w3, real_blockexplorer, heuristic, transaction_to_verified_contract) + end_time = asyncio.get_event_loop().time() + processing_time_to_verified_contract += (end_time - start_time) * 1000 + processing_time_to_verified_contract /= processing_runs + + processing_time_fake_token = 0 + for _ in range(processing_runs): + start_time = asyncio.get_event_loop().time() + await agent.detect_address_poisoning(real_w3, real_blockexplorer, heuristic, fake_token_phishing_tx) + end_time = asyncio.get_event_loop().time() + processing_time_fake_token += (end_time - start_time) * 1000 + processing_time_fake_token /= processing_runs + + assert (processing_time_to_eoa * 0.33 + processing_time_to_contract * 0.33 + processing_time_to_verified_contract * 0.33 + processing_time_fake_token * 0.01) / 8 < 160, f"Time is {(processing_time_to_eoa * 0.33 + processing_time_to_contract * 0.33 + processing_time_to_verified_contract * 0.33 + processing_time_fake_token * 0.01) / 8}, normal: {processing_time_to_eoa} - {processing_time_to_contract} - {processing_time_to_verified_contract} - {processing_time_fake_token} " + + @pytest.mark.asyncio + async def test_transfer_to_eoa(self): tx_event = create_transaction_event({ - 'transaction': { - 'to': NEW_EOA, - 'from': OLD_EOA, - 'hash': "0xpositive_zero" - } - }) - - findings = agent.detect_address_poisoning(w3, blockexplorer, heuristic, tx_event) + 'to': NEW_EOA, + 'from': OLD_EOA, + 'hash': "0xpositive_zero" + }, + {}, + 1 + ) + + findings = await agent.detect_address_poisoning(w3, blockexplorer, heuristic, tx_event) assert len(findings) == 0, "This should not have triggered a finding - not to a contract" - - def test_get_attacker_victim_lists_for_zero_value(self): - agent.initialize() - + @pytest.mark.asyncio + async def test_get_attacker_victim_lists_for_zero_value(self): alert_type = "ZERO-VALUE-ADDRESS-POISONING" logs = [ { @@ -117,14 +159,12 @@ def test_get_attacker_victim_lists_for_zero_value(self): } ] - attackers, victims = agent.get_attacker_victim_lists(w3, logs, alert_type) + attackers, victims = await agent.get_attacker_victim_lists(w3, logs, alert_type) assert len([a for a in attackers if "attacker" in a]) == len(attackers) assert len([v for v in victims if v == "victim"]) == len(victims) - - def test_get_attacker_victim_lists_for_low_value(self): - agent.initialize() - + @pytest.mark.asyncio + async def test_get_attacker_victim_lists_for_low_value(self): alert_type = "ADDRESS-POISONING-LOW-VALUE" logs = [ { @@ -145,15 +185,12 @@ def test_get_attacker_victim_lists_for_low_value(self): } ] - attackers, victims = agent.get_attacker_victim_lists(w3, logs, alert_type) + attackers, victims = await agent.get_attacker_victim_lists(w3, logs, alert_type) assert len([a for a in attackers if "attacker" in a]) == len(attackers) assert len([v for v in victims if v == "victim"]) == len(victims) assert len(attackers) - len(victims) == 1 - def test_positive_check_for_similar_transfer(self): - agent.initialize() - victims = ["victim"] logs = [ { @@ -176,106 +213,108 @@ def test_positive_check_for_similar_transfer(self): check_result = agent.check_for_similar_transfer(blockexplorer, logs, victims) assert check_result, "This should find a matching value" - - - def test_negative_check_for_similar_transfer(self): - agent.initialize() - victims = ["user_one"] - logs = [ - { - "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "args": { - "to": "user_one", - "from": "user_two", - "value": "82300" - } - }, - { - "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "args": { - "to": "user_three", - "from": "user_four", - "value": "82300" + def test_negative_check_for_similar_transfer(self): + victims = ["user_one"] + logs = [ + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "args": { + "to": "user_one", + "from": "user_two", + "value": "82300" + } + }, + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "args": { + "to": "user_three", + "from": "user_four", + "value": "82300" + } } - } - ] - - check_result = agent.check_for_similar_transfer(blockexplorer, logs, victims) - assert not check_result, "This should not find a matching value" + ] + check_result = agent.check_for_similar_transfer(blockexplorer, logs, victims) + assert not check_result, "This should not find a matching value" - def test_is_zero_value_address_poisoning(self): - agent.initialize() + @pytest.mark.asyncio + async def test_is_zero_value_address_poisoning(self): + await agent.initialize() + agent.CHAIN_ID = 1 - tx_event = MagicMock(spec=TransactionEvent) - tx_event.transaction = {} - tx_event.to = CONTRACT - tx_event.from_ = NEW_EOA - tx_event.hash = "0xpositive_zero" - tx_event.filter_log.return_value = [ - { - "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "args": { - "to": "attacker", - "from": "victim", - "value": "0" - } + tx_event = TransactionEvent({ + 'transaction': { + 'hash': "0xpositive_zero", + 'from': NEW_EOA, + 'to': CONTRACT }, - { - "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "args": { - "to": "attacker", - "from": "victim", - "value": "0" + 'logs': [ + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "args": { + "to": "attacker", + "from": "victim", + "value": "0" + } + }, + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "args": { + "to": "attacker", + "from": "victim", + "value": "0"} } - } - ] - - findings = agent.detect_address_poisoning(w3, blockexplorer, heuristic, tx_event) + ] + }) + + findings = await agent.detect_address_poisoning(w3, blockexplorer, heuristic, tx_event) assert len(findings) == 1, "This should have triggered an alert - positive case" assert findings[0].alert_id == "ADDRESS-POISONING-ZERO-VALUE" + @pytest.mark.asyncio + async def test_is_not_zero_value_address_poisoning(self): + await agent.initialize() + agent.CHAIN_ID = 1 - def test_is_not_zero_value_address_poisoning(self): - agent.initialize() - - tx_event = MagicMock(spec=TransactionEvent) - tx_event.transaction = {} - tx_event.to = CONTRACT - tx_event.from_ = NEW_EOA - tx_event.hash = "0xnegative_zero" - tx_event.filter_log.return_value = [ - { - "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "args": { - "to": "attacker", - "from": "victim", - "value": "0" - } + tx_event = TransactionEvent({ + 'transaction': { + 'hash': "0xnegative_zero", + 'from': NEW_EOA, + 'to': CONTRACT }, - { - "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "args": { - "to": "attacker", - "from": "victim", - "value": "0" + 'logs': [ + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "args": { + "to": "attacker", + "from": "victim", + "value": "0" + } + }, + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "args": { + "to": "attacker", + "from": "victim", + "value": "0" + } } - } - ] + ] + }) - findings = agent.detect_address_poisoning(w3, blockexplorer, heuristic, tx_event) + findings = await agent.detect_address_poisoning(w3, blockexplorer, heuristic, tx_event) assert len(findings) == 0, "This should not have triggered an alert - negative case" - - def test_is_low_value_address_poisoning(self): - agent.initialize() + @pytest.mark.asyncio + async def test_is_low_value_address_poisoning(self): + await agent.initialize() + agent.CHAIN_ID = 1 tx_event = MagicMock(spec=TransactionEvent) - tx_event.transaction = {} - tx_event.to = CONTRACT - tx_event.from_ = NEW_EOA tx_event.hash = "0xpositive_low" + tx_event.from_ = NEW_EOA + tx_event.to = CONTRACT tx_event.filter_log.return_value = [ { "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", @@ -295,75 +334,81 @@ def test_is_low_value_address_poisoning(self): } ] - findings = agent.detect_address_poisoning(w3, blockexplorer, heuristic, tx_event) + findings = await agent.detect_address_poisoning(w3, blockexplorer, heuristic, tx_event) assert len(findings) == 1 assert findings[0].alert_id == "ADDRESS-POISONING-LOW-VALUE" - - def test_is_not_low_value_address_poisoning(self): - agent.initialize() + @pytest.mark.asyncio + async def test_is_not_low_value_address_poisoning(self): + await agent.initialize() + agent.CHAIN_ID = 1 - tx_event = MagicMock(spec=TransactionEvent) - tx_event.transaction = {} - tx_event.to = CONTRACT - tx_event.from_ = NEW_EOA - tx_event.hash = "0xnegative_low" - tx_event.filter_log.return_value = [ - { - "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "args": { - "to": "user_one", - "from": "user_two", - "value": "1600" - } + tx_event = TransactionEvent({ + 'transaction': { + 'hash': "0xnegative_low", + 'from': NEW_EOA, + 'to': CONTRACT }, - { - "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", - "args": { - "to": "user_three", - "from": "user_four", - "value": "15000" + 'logs': [ + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "args": { + "to": "user_one", + "from": "user_two", + "value": "1600" + } + }, + { + "address": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "args": { + "to": "user_three", + "from": "user_four", + "value": "15000" + } } - } - ] + ] + }) - findings = agent.detect_address_poisoning(w3, blockexplorer, heuristic, tx_event) + findings = await agent.detect_address_poisoning(w3, blockexplorer, heuristic, tx_event) assert len(findings) == 0, "This should not have triggered an alert - negative case" + @pytest.mark.asyncio + async def test_is_fake_token_address_poisoning(self): + agent.CHAIN_ID = 1 - def test_is_fake_token_address_poisoning(self): - agent.initialize() - - tx_event = MagicMock(spec=TransactionEvent) - tx_event.transaction = {} - tx_event.to = CONTRACT - tx_event.from_ = NEW_EOA - tx_event.hash = "0xpositive_fake_token" - tx_event.filter_log.return_value = [ - { - "address": "0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F", - "args": { - "to": "attacker", - "from": "victim", - "value": "3000" - } + tx_event = TransactionEvent({ + 'transaction': { + 'hash': "0xpositive_fake_token", + 'from': NEW_EOA, + 'to': CONTRACT }, - { - "address": "0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F", - "args": { - "to": "attacker", - "from": "victim", - "value": "4000" + 'logs': [ + { + "address": "0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F", + "args": { + "to": "attacker", + "from": "victim", + "value": "3000" + } + }, + { + "address": "0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F", + "args": { + "to": "attacker", + "from": "victim", + "value": "4000" + } } - } - ] + ] + }) - findings = agent.detect_address_poisoning(w3, blockexplorer, heuristic, tx_event) + findings = await agent.detect_address_poisoning(w3, blockexplorer, heuristic, tx_event) assert len(findings) == 1, "This should have triggered an alert - positive case" - - def test_is_not_fake_token_address_poisoning(self): - agent.initialize() + @pytest.mark.asyncio + async def test_is_not_fake_token_address_poisoning(self): + await agent.initialize() + agent.CHAIN_ID = 1 tx_event = MagicMock(spec=TransactionEvent) tx_event.transaction = {} @@ -389,12 +434,12 @@ def test_is_not_fake_token_address_poisoning(self): } ] - findings = agent.detect_address_poisoning(w3, blockexplorer, heuristic, tx_event) + findings = await agent.detect_address_poisoning(w3, blockexplorer, heuristic, tx_event) assert len(findings) == 0, "This should not have triggered an alert - negative case" - - def test_is_null_address_in_logs(self): - agent.initialize() + @pytest.mark.asyncio + async def test_is_null_address_in_logs(self): + await agent.initialize() tx_event = MagicMock(spec=TransactionEvent) tx_event.transaction = {} @@ -402,5 +447,5 @@ def test_is_null_address_in_logs(self): tx_event.from_ = NEW_EOA tx_event.hash = "0x_token_mint" - findings = agent.detect_address_poisoning(w3, blockexplorer, heuristic, tx_event) - assert len(findings) == 0, "This is a minting transaction, so should not have triggered." \ No newline at end of file + findings = await agent.detect_address_poisoning(w3, blockexplorer, heuristic, tx_event) + assert len(findings) == 0, "This is a minting transaction, so should not have triggered." diff --git a/address-poisoning-py/src/blockexplorer.py b/address-poisoning-py/src/blockexplorer.py index 89c8b4ca..fb78b936 100644 --- a/address-poisoning-py/src/blockexplorer.py +++ b/address-poisoning-py/src/blockexplorer.py @@ -1,14 +1,18 @@ -from src.storage import get_secrets +from storage import get_secrets from functools import lru_cache import requests import logging import json +import asyncio + class BlockExplorer(): def __init__(self, chain_id): - self.api_keys = get_secrets() + self.initialize_task = asyncio.create_task(self.initialize(chain_id)) + async def initialize(self, chain_id): + self.api_keys = await get_secrets() if chain_id == 1: self.host = "https://api.etherscan.io/api" self.api_key = self.api_keys['apiKeys']['ETHERSCAN_TOKEN'] @@ -30,7 +34,9 @@ def __init__(self, chain_id): elif chain_id == 43114: self.host = "https://api.snowtrace.io/api" self.api_key = self.api_keys['apiKeys']['SNOWTRACE_TOKEN'] - + elif chain_id == 59144: + self.host = "https://api.lineascan.build/api" + self.api_key = self.api_keys['apiKeys']['LINEASCAN_TOKEN'] @lru_cache(maxsize=100) def make_token_history_query(self, address_info): @@ -44,7 +50,7 @@ def make_token_history_query(self, address_info): response = requests.get(self.host, params=params) values = [transfer['value'] for transfer in response.json()['result'] if transfer['from'] == str.lower(address_info[0])] - + return values[-5:] def is_verified(self, address): @@ -56,6 +62,6 @@ def is_verified(self, address): logging.info("Contract is verified...exiting") return True else: - logging.warn("Unable to check if contract is verified. Etherscan returned status code " + str(response.status_code)) + logging.warning("Unable to check if contract is verified. Etherscan returned status code " + str(response.status_code)) logging.info("Contract is not verified") - return False \ No newline at end of file + return False diff --git a/address-poisoning-py/src/constants.py b/address-poisoning-py/src/constants.py index 96e6a351..85a949b9 100644 --- a/address-poisoning-py/src/constants.py +++ b/address-poisoning-py/src/constants.py @@ -42,14 +42,15 @@ '0x19860ccb0a68fd4213ab9d8266f7bbf05a8dde98', '0x9c9e5fd8bbc25984b178fdce6117defa39d2db39', '0xd586e7f844cea2f87f50152665bcbc2c279d8d70' - ] + ], + 59144: ["0xa219439258ca9da29e9cc4ce5596924745e12b93", "0x176211869ca2b568f2a7d4ee941e073a821ee1ff", "0x4af15ec2a0bd43db75dd04e62faa3b8ef36b00d5", "0xd2bc272ea0154a93bf00191c8a1db23e67643ec5", "0xb79dd08ea68a908a97220c76d19a6aa9cbde4376", "0xf3b001d64c656e30a62fbaaca003b1336b4ce12a"] } BASE_TOKENS = [ - '0x0000000000000000000000000000000000001010', # MATIC on Polygon - '0x4200000000000000000000000000000000000042', # OP on Optimism - '0x912ce59144191c1204e64559fe8253a0e49e6548', # ARB on Arbitrum - '0x658b0c7613e890ee50b8c4bc6a3f41ef411208ad' # FETH on Fantom + '0x0000000000000000000000000000000000001010', # MATIC on Polygon + '0x4200000000000000000000000000000000000042', # OP on Optimism + '0x912ce59144191c1204e64559fe8253a0e49e6548', # ARB on Arbitrum + '0x658b0c7613e890ee50b8c4bc6a3f41ef411208ad' # FETH on Fantom ] # ABIs for decoding relevant log events @@ -59,17 +60,17 @@ # ABI for symbol function SYMBOL_CALL_ABI = [ { - "constant": True, - "inputs": [], - "name": "symbol", - "outputs": [ - {"name": "", - "type": "string" - } - ], - "payable": False, - "stateMutability": "view", - "type": "function" + "constant": True, + "inputs": [], + "name": "symbol", + "outputs": [ + {"name": "", + "type": "string" + } + ], + "payable": False, + "stateMutability": "view", + "type": "function" } ] @@ -81,23 +82,27 @@ 42161: ['USDT', 'USDC', 'DAI', 'TUSD', 'ARB'], 10: ['USDT', 'USDC', 'DAI', 'OP'], 250: ['TUSD', 'USDC', 'DAI', 'FETH'], - 43114: ['USDT.e', 'USDt', 'USDC', 'USDC.e', 'BUSD.e', 'BUSD', 'DAI.e', 'AVAX'] + 43114: ['USDT.e', 'USDt', 'USDC', 'USDC.e', 'BUSD.e', 'BUSD', 'DAI.e', 'AVAX'], + 59144: ['USDT', 'USDC', 'DAI', 'USDP', 'USD+', 'MAI'] } CHAIN_ORDINAL_SYMBOL_MAP = { 1: [ - [85, 83, 68, 84], # USDT - [85, 83, 68, 1058], # USDΠ’ -> Cyrillic T - [85, 83, 68, 67], # USDC - [85, 83, 68, 1057], # USDΠ‘ -> Cyrillic C - [69, 84, 72], # ETH - [1045, 84, 72], # Π•TH -> Cyrillic E - [68, 65, 73], # DAI - [68, 1040, 73], # DАI -> Cyrillic A - [119828, 119826, 119811, 119810], # USDC -> 𝐔𝐒𝐃𝐂 - [119828, 119826, 119811, 119827], # USDΠ’ -> 𝐔𝐒𝐃𝐓 - [119811, 119808, 119816] # DАI -> πƒπ€πˆ + [85, 83, 68, 84], # USDT + [85, 83, 68, 1058], # USDΠ’ -> Cyrillic T + [85, 83, 68, 67], # USDC + [85, 83, 68, 1057], # USDΠ‘ -> Cyrillic C + [85, 83, 68, 80], # USDP + [85, 83, 68, 43], # USD+ + [69, 84, 72], # ETH + [1045, 84, 72], # Π•TH -> Cyrillic E + [77, 65, 73], # MAI + [68, 65, 73], # DAI + [68, 1040, 73], # DАI -> Cyrillic A + [119828, 119826, 119811, 119810], # USDC -> 𝐔𝐒𝐃𝐂 + [119828, 119826, 119811, 119827], # USDΠ’ -> 𝐔𝐒𝐃𝐓 + [119811, 119808, 119816] # DАI -> πƒπ€πˆ ] } -OPENSEA_TRANSFER_HELPER = "0x0000000000c2d145a2526bd8c716263bfebe1a72" \ No newline at end of file +OPENSEA_TRANSFER_HELPER = "0x0000000000c2d145a2526bd8c716263bfebe1a72" diff --git a/address-poisoning-py/src/findings.py b/address-poisoning-py/src/findings.py index 7226c5db..d8cd60cb 100644 --- a/address-poisoning-py/src/findings.py +++ b/address-poisoning-py/src/findings.py @@ -1,10 +1,9 @@ -from forta_agent import Finding, FindingSeverity, FindingType, EntityType +from forta_bot_sdk import Finding, FindingSeverity, FindingType, EntityType - -class AddressPoisoningFinding: - def create_finding(transaction_event, anomaly_score, log_length, attackers, victims, alert_type): +class AddressPoisoningFinding: + def create_finding(transaction_event, anomaly_score, log_length, attackers, victims, alert_type, chain_id): alert_description = { "ADDRESS-POISONING-ZERO-VALUE": "zero value", "ADDRESS-POISONING-LOW-VALUE": "low value", @@ -12,41 +11,46 @@ def create_finding(transaction_event, anomaly_score, log_length, attackers, vict } finding = Finding( + { + "name": "Possible Address Poisoning", + "description": f"Possible {alert_description[alert_type]} address poisoning transaction triggered by eoa - {transaction_event.from_} calling contract - {transaction_event.to}", + "alert_id": alert_type, + "type": FindingType.Suspicious, + "severity": FindingSeverity.Medium, + "metadata": { + "phishing_eoa": transaction_event.from_, + "phishing_contract": transaction_event.to, + "logs_length": log_length, + "attacker_addresses": attackers, + "victim_addresses": victims, + "anomaly_score": str(anomaly_score) + }, + "metadata": {}, + "labels": [ + { + "entityType": EntityType.Address, + "entity": transaction_event.from_, + "label": "attacker-eoa", + "confidence": 0.7 + }, + { + "entityType": EntityType.Address, + "entity": transaction_event.to, + "label": "attacker-contract", + "confidence": 0.7 + }, { - "name": "Possible Address Poisoning", - "description": f"Possible {alert_description[alert_type]} address poisoning transaction triggered by eoa - {transaction_event.from_} calling contract - {transaction_event.to}", - "alert_id": alert_type, - "type": FindingType.Suspicious, - "severity": FindingSeverity.Medium, - "metadata": { - "phishing_eoa": transaction_event.from_, - "phishing_contract": transaction_event.to, - "logs_length": log_length, - "attacker_addresses": attackers, - "victim_addresses": victims, - "anomaly_score": anomaly_score - }, - "labels": [ - { - "entityType": EntityType.Address, - "entity": transaction_event.from_, - "label": "attacker-eoa", - "confidence": 0.7 - }, - { - "entityType": EntityType.Address, - "entity": transaction_event.to, - "label": "attacker-contract", - "confidence": 0.7 - }, - { - "entityType": EntityType.Transaction, - "entity": transaction_event.hash, - "label": "address-poisoning", - "confidence": 0.7 - }, - ] - } - ) + "entityType": EntityType.Transaction, + "entity": transaction_event.hash, + "label": "address-poisoning", + "confidence": 0.7 + }, + ], + "source": { + 'chains': [{'chainId': chain_id}], + 'transactions': [{'chainId': chain_id, 'hash': transaction_event.hash}] + } + } + ) - return finding \ No newline at end of file + return finding diff --git a/address-poisoning-py/src/rules.py b/address-poisoning-py/src/rules.py index a0707478..3285db89 100644 --- a/address-poisoning-py/src/rules.py +++ b/address-poisoning-py/src/rules.py @@ -1,23 +1,23 @@ from hexbytes import HexBytes -from forta_agent import Web3 -from src.constants import * +from web3 import Web3 +from constants import * +from constants import STABLECOIN_CONTRACTS import logging class AddressPoisoningRules: @staticmethod - def is_contract(w3, address): + async def is_contract(w3, address): """ this function determines whether address is a contract :return: is_contract: bool """ if address is None: return True - code = w3.eth.get_code(Web3.toChecksumAddress(address)) + code = await w3.eth.get_code(Web3.to_checksum_address(address)) return code != HexBytes('0x') - @staticmethod def have_addresses_been_detected(transaction_event, zero_value_contracts, low_value_contracts, fake_token_contracts): """ @@ -33,7 +33,6 @@ def have_addresses_been_detected(transaction_event, zero_value_contracts, low_va else: return "" - @staticmethod def are_all_logs_stablecoins(logs, chain_id): stablecoin_count = 0 @@ -47,14 +46,17 @@ def are_all_logs_stablecoins(logs, chain_id): return (1.0 * stablecoin_count) / len(logs) - - @staticmethod + @staticmethod def are_all_logs_transfers_or_approvals(logs): - approval_hash = HexBytes("0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925") - transfer_hash = HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef") + approval_hash = HexBytes( + "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925") + transfer_hash = HexBytes( + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef") - approval_logs = [log for log in logs if log['topics'][0] == approval_hash] - transfer_logs = [log for log in logs if log['topics'][0] == transfer_hash] + approval_logs = [ + log for log in logs if log['topics'][0] == approval_hash] + transfer_logs = [ + log for log in logs if log['topics'][0] == transfer_hash] if ( len(approval_logs) + len(transfer_logs) == len(logs) @@ -64,32 +66,29 @@ def are_all_logs_transfers_or_approvals(logs): else: return False - @staticmethod def is_zero_value_tx(logs, chain_id): for log in logs: if (str.lower(log['address']) in STABLECOIN_CONTRACTS[chain_id] - and log['data'] == "0x0000000000000000000000000000000000000000000000000000000000000000"): + and HexBytes(log['data']) == HexBytes("0x0000000000000000000000000000000000000000000000000000000000000000")): continue else: return False return True - @staticmethod def is_data_field_repeated(logs): data_fields = [log['data'] for log in logs] - if (len(set(data_fields)) > (len(data_fields)/2) - or "0x0000000000000000000000000000000000000000000000000000000000000000" in data_fields): + if (len(set(data_fields)) > (len(data_fields) / 2) + or "0x0000000000000000000000000000000000000000000000000000000000000000" in data_fields): return False - - return True + return True @staticmethod - def are_tokens_using_known_symbols(w3, logs, chain_id): + async def are_tokens_using_known_symbols(w3, logs, chain_id): contracts = set([log['address'] for log in logs]) valid_contracts = 0 @@ -98,36 +97,38 @@ def are_tokens_using_known_symbols(w3, logs, chain_id): valid_contracts += 1 else: try: - contract = w3.eth.contract(address=Web3.toChecksumAddress(address), abi=SYMBOL_CALL_ABI) - symbol = contract.functions.symbol().call() + contract = w3.eth.contract( + address=Web3.to_checksum_address(address), abi=SYMBOL_CALL_ABI) + symbol = await contract.functions.symbol().call() if chain_id == 1: ord_symbol = [ord(char) for char in symbol] if ord_symbol in CHAIN_ORDINAL_SYMBOL_MAP[1]: continue else: - logging.info("Exiting because failed to match ordinal") + logging.info( + "Exiting because failed to match ordinal") return False else: if symbol in OFFICIAL_SYMBOLS[chain_id]: continue else: - logging.info("Exiting because failed to match symbol") + logging.info( + "Exiting because failed to match symbol") return False except Exception as e: - logging.warn(f"Failed to retrieve symbol info for {address} with exception {e}") + logging.warning(f"Failed to retrieve symbol info for {address} with exception {e}") return False - if valid_contracts == len(contracts): logging.info("Exiting because all contracts are valid") return False return True - @staticmethod def are_tokens_minted(logs): - null_hash = HexBytes('0x0000000000000000000000000000000000000000000000000000000000000000') + null_hash = HexBytes( + '0x0000000000000000000000000000000000000000000000000000000000000000') for log in logs: if null_hash in log["topics"]: @@ -135,5 +136,5 @@ def are_tokens_minted(logs): return True else: continue - - return False \ No newline at end of file + + return False diff --git a/address-poisoning-py/src/rules_test.py b/address-poisoning-py/src/rules_test.py index 1b93d573..c05da72a 100644 --- a/address-poisoning-py/src/rules_test.py +++ b/address-poisoning-py/src/rules_test.py @@ -1,8 +1,9 @@ from web3_mock import * from rules import AddressPoisoningRules -from forta_agent import create_transaction_event +from forta_bot_sdk import create_transaction_event from web3_constants_mock import * -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch +import pytest w3 = Web3Mock() heuristic = AddressPoisoningRules() @@ -10,108 +11,104 @@ class TestAddressPoisoningRules: - def test_is_contract_contract(self): - assert heuristic.is_contract(w3, CONTRACT) + @pytest.mark.asyncio + async def test_is_contract_contract(self): + assert (await heuristic.is_contract(w3, CONTRACT)) is True + @pytest.mark.asyncio + async def test_is_contract_eoa(self): + assert (await heuristic.is_contract(w3, NEW_EOA)) is False - def test_is_contract_eoa(self): - assert heuristic.is_contract(w3, NEW_EOA) is False - - - def test_have_addresses_been_detected_positive(self): + @pytest.mark.asyncio + async def test_have_addresses_been_detected_positive(self): zero_value_contracts = set([NEW_EOA]) low_value_contracts = set([]) fake_token_contracts = set([]) - - positive_case = create_transaction_event({ - 'transaction': { + positive_case = create_transaction_event( + { 'to': NEW_EOA, 'from': CONTRACT - } - }) + }, + {}, + w3.eth.chain_id + ) assert heuristic.have_addresses_been_detected( positive_case, zero_value_contracts, low_value_contracts, fake_token_contracts ) == "ADDRESS-POISONING-ZERO-VALUE", "Address should be flagged as being in the zero_value_contracts" - - def test_have_addresses_been_detected_negative(self): + @pytest.mark.asyncio + async def test_have_addresses_been_detected_negative(self): zero_value_contracts = set([NEW_EOA, CONTRACT]) low_value_contracts = set([]) fake_token_contracts = set([]) - negative_case = create_transaction_event({ - 'transaction': { + negative_case = create_transaction_event( + { 'to': OLD_EOA, 'from': CONTRACT - } - }) + }, + {}, + w3.eth.chain_id + ) assert heuristic.have_addresses_been_detected( negative_case, zero_value_contracts, low_value_contracts, fake_token_contracts ) == "", "Address should be flagged as being in the zero_value_contracts" - def test_are_all_logs_stablecoins_positive(self): assert heuristic.are_all_logs_stablecoins( MOCK_TX_HASH_LOGS_MAPPING['0xpositive_zero']['logs'], w3.eth.chain_id - ) >= 0.8 - + ) >= 0.8 def test_are_all_logs_stablecoins_negative(self): assert (heuristic.are_all_logs_stablecoins( - MOCK_TX_HASH_LOGS_MAPPING['0xnegative_zero']['logs'], + MOCK_TX_HASH_LOGS_MAPPING['0xnegative_zero']['logs'], w3.eth.chain_id - ) >= 0.8) is False - + ) >= 0.8) is False def test_are_all_logs_transfers_positive(self): assert heuristic.are_all_logs_transfers_or_approvals( MOCK_TX_HASH_LOGS_MAPPING['0xpositive_zero']['logs'] ) is True - def test_are_all_logs_transfers_negative(self): assert heuristic.are_all_logs_transfers_or_approvals( MOCK_TX_HASH_LOGS_MAPPING['0xnegative_zero']['logs'] ) is False - def test_is_zero_value_tx_positive(self): assert heuristic.is_zero_value_tx( MOCK_TX_HASH_LOGS_MAPPING['0xpositive_zero']['logs'], w3.eth.chain_id ) is True - def test_is_zero_value_tx_negative(self): assert heuristic.is_zero_value_tx( MOCK_TX_HASH_LOGS_MAPPING['0xnegative_zero']['logs'], w3.eth.chain_id ) is False - - def test_are_tokens_using_known_symbols_positive(self): - assert heuristic.are_tokens_using_known_symbols( - w3, - MOCK_TX_HASH_LOGS_MAPPING['0xpositive_fake_token']['logs'], + @pytest.mark.asyncio + async def test_are_tokens_using_known_symbols_positive(self): + assert (await heuristic.are_tokens_using_known_symbols( + w3, + MOCK_TX_HASH_LOGS_MAPPING['0xpositive_fake_token']['logs'], w3.eth.chain_id - ) is True + )) is True - - def test_are_tokens_using_known_symbols_negative(self): - assert heuristic.are_tokens_using_known_symbols( + @pytest.mark.asyncio + async def test_are_tokens_using_known_symbols_negative(self): + assert (await heuristic.are_tokens_using_known_symbols( w3, MOCK_TX_HASH_LOGS_MAPPING['0xnegative_fake_token']['logs'], w3.eth.chain_id - ) is False - + )) is False def test_are_tokens_minted_positive(self): assert heuristic.are_tokens_minted( MOCK_TX_HASH_LOGS_MAPPING['0x_token_mint']['logs'] ) is True - def test_are_tokens_minted(self): assert heuristic.are_tokens_minted( MOCK_TX_HASH_LOGS_MAPPING['0xnegative_fake_token']['logs'] - ) is False \ No newline at end of file + ) is False diff --git a/address-poisoning-py/src/storage.py b/address-poisoning-py/src/storage.py index bf318d0a..58b2048c 100644 --- a/address-poisoning-py/src/storage.py +++ b/address-poisoning-py/src/storage.py @@ -1,30 +1,43 @@ -import forta_agent +import forta_bot_sdk import json -import requests +import aiohttp import os owner_db = "https://research.forta.network/database/owner/" -test_mode = "main" if 'NODE_ENV' in os.environ and 'production' in os.environ.get('NODE_ENV') else "test" +test_mode = "main" if 'FORTA_ENV' in os.environ and 'production' in os.environ.get( + 'FORTA_ENV') else "test" -def _token(): - tk = forta_agent.fetch_jwt({}) + +async def _token(): + tk = await forta_bot_sdk.fetch_jwt() return {"Authorization": f"Bearer {tk}"} -def _load_json(key: str) -> object: +async def _load_json_from_file(key: str) -> object: + with open(key) as f: + return json.load(f) + + +async def _load_json_from_db(key: str) -> object: + async with aiohttp.ClientSession() as session: + async with session.get(f"{owner_db}{key}", headers=await _token()) as res: + if res.status == 200: + try: + return await res.json(content_type=None) + except json.JSONDecodeError: + raise Exception("Failed to decode JSON response") + else: + raise Exception( + f"error loading json from owner db: {res.status}, {await res.text()}") + + +async def _load_json(key: str) -> object: if test_mode == "test": - # loading json from local file secrets.json - with open("secrets.json") as f: - return json.load(f) + return await _load_json_from_file(key) else: - res = requests.get(f"{owner_db}{key}", headers=_token()) - if res.status_code == 200: - return res.json() - else: - raise Exception(f"error loading json from owner db: {res.status_code}, {res.text}") - + return await _load_json_from_db(key) -def get_secrets(): - return _load_json("secrets.json") +async def get_secrets(): + return await _load_json("secrets.json") diff --git a/address-poisoning-py/src/web3_constants_mock.py b/address-poisoning-py/src/web3_constants_mock.py index f00d1abf..a39f039f 100644 --- a/address-poisoning-py/src/web3_constants_mock.py +++ b/address-poisoning-py/src/web3_constants_mock.py @@ -1,194 +1,194 @@ from hexbytes import HexBytes -NEW_EOA = '0x49A9deCA3DcA86aB3A029C2ed629EC8477009Fee' -OLD_EOA = '0x4e5b2E1Dc63f6B91cb6cD759936495434c7E0000' -CONTRACT = '0x2320A28f52334d62622cc2EaFa15DE55F9987eD0' +NEW_EOA = '0x49A9deCA3DcA86aB3A029C2ed629EC8477009Fee'.lower() +OLD_EOA = '0x4e5b2E1Dc63f6B91cb6cD759936495434c7E0000'.lower() +CONTRACT = '0x2320A28f52334d62622cc2EaFa15DE55F9987eD0'.lower() MOCK_TX_HASH_LOGS_MAPPING = { "0xpositive_zero": {'logs': [ - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x0000000000000000000000000000000000000000000000000000000000000000', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x0000000000000000000000000000000000000000000000000000000000000000', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x0000000000000000000000000000000000000000000000000000000000000000', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x0000000000000000000000000000000000000000000000000000000000000000', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x0000000000000000000000000000000000000000000000000000000000000000', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - ] + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x0000000000000000000000000000000000000000000000000000000000000000', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x0000000000000000000000000000000000000000000000000000000000000000', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x0000000000000000000000000000000000000000000000000000000000000000', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x0000000000000000000000000000000000000000000000000000000000000000', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x0000000000000000000000000000000000000000000000000000000000000000', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + ] }, - "0xnegative_zero": {'logs':[ - { - 'address': NEW_EOA, - 'data': '0x0000000000000000000000000000000000000000000000000000000000100000', - 'topics':[HexBytes("0x0000000000000000000000003e02bc40db6c236d12f07a2e78db4e08f9aa4561")] - }, - { - 'address': NEW_EOA, - 'data': '0x0000000000000000000000000000000000000000000000000000000000100000', - 'topics':[HexBytes("0x0000000000000000000000003e02bc40db6c236d12f07a2e78db4e08f9aa4561")] - } - ] + "0xnegative_zero": {'logs': [ + { + 'address': NEW_EOA, + 'data': '0x0000000000000000000000000000000000000000000000000000000000100000', + 'topics': [HexBytes("0x0000000000000000000000003e02bc40db6c236d12f07a2e78db4e08f9aa4561")] + }, + { + 'address': NEW_EOA, + 'data': '0x0000000000000000000000000000000000000000000000000000000000100000', + 'topics': [HexBytes("0x0000000000000000000000003e02bc40db6c236d12f07a2e78db4e08f9aa4561")] + } + ] }, "0xpositive_low": {'logs': [ - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x00000000000000000000000000000000000000000000000000000000000186a0', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x00000000000000000000000000000000000000000000000000000000000186a0', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x0000000000000000000000000000000000000000000000000000000000019a28', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x0000000000000000000000000000000000000000000000000000000000019a28', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x00000000000000000000000000000000000000000000000000000000000249f0', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x00000000000000000000000000000000000000000000000000000000000249f0', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x00000000000000000000000000000000000000000000000000000000000249f1', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x00000000000000000000000000000000000000000000000000000000000249f1', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - ] + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x00000000000000000000000000000000000000000000000000000000000186a0', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x00000000000000000000000000000000000000000000000000000000000186a0', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x0000000000000000000000000000000000000000000000000000000000019a28', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x0000000000000000000000000000000000000000000000000000000000019a28', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x00000000000000000000000000000000000000000000000000000000000249f0', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x00000000000000000000000000000000000000000000000000000000000249f0', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x00000000000000000000000000000000000000000000000000000000000249f1', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x00000000000000000000000000000000000000000000000000000000000249f1', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + ] }, - "0xnegative_low": { "logs": [ - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x00000000000000000000000000000000000000000000000000000000000549f2', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - } - ] + "0xnegative_low": {"logs": [ + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x00000000000000000000000000000000000000000000000000000000000549f2', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + } + ] }, - "0xpositive_fake_token": { "logs": [ - { - 'address': '0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F', - 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xCf117403474eEaC230DaCcB3b54c0dABeB94Ae22', - 'data': '0x00000000000000000000000000000000000000000000000000000000000549f2', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F', - 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xCf117403474eEaC230DaCcB3b54c0dABeB94Ae22', - 'data': '0x00000000000000000000000000000000000000000000000000000000000549f2', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F', - 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xCf117403474eEaC230DaCcB3b54c0dABeB94Ae22', - 'data': '0x00000000000000000000000000000000000000000000000000000000000549f2', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F', - 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xCf117403474eEaC230DaCcB3b54c0dABeB94Ae22', - 'data': '0x00000000000000000000000000000000000000000000000000000000000549f2', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F', - 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xCf117403474eEaC230DaCcB3b54c0dABeB94Ae22', - 'data': '0x00000000000000000000000000000000000000000000000000000000000549f2', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - } - ] + "0xpositive_fake_token": {"logs": [ + { + 'address': '0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F', + 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xCf117403474eEaC230DaCcB3b54c0dABeB94Ae22', + 'data': '0x00000000000000000000000000000000000000000000000000000000000549f2', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F', + 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xCf117403474eEaC230DaCcB3b54c0dABeB94Ae22', + 'data': '0x00000000000000000000000000000000000000000000000000000000000549f2', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F', + 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xCf117403474eEaC230DaCcB3b54c0dABeB94Ae22', + 'data': '0x00000000000000000000000000000000000000000000000000000000000549f2', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F', + 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xCf117403474eEaC230DaCcB3b54c0dABeB94Ae22', + 'data': '0x00000000000000000000000000000000000000000000000000000000000549f2', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F', + 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xCf117403474eEaC230DaCcB3b54c0dABeB94Ae22', + 'data': '0x00000000000000000000000000000000000000000000000000000000000549f2', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + } + ] }, - "0xnegative_fake_token": { "logs": [ - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - }, - { - 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', - 'data': '0x00000000000000000000000000000000000000000000000000000000000549f2', - 'topics':[HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] - } - ] + "0xnegative_fake_token": {"logs": [ + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x00000000000000000000000000000000000000000000000000000000000249f2', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + }, + { + 'address': '0xdAC17F958D2ee523a2206206994597C13D831ec7', + 'data': '0x00000000000000000000000000000000000000000000000000000000000549f2', + 'topics': [HexBytes("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef")] + } + ] }, - "0x_token_mint": { "logs": [ - { - 'address': '0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F', - 'data': "0x", - 'topics': [ + "0x_token_mint": {"logs": [ + { + 'address': '0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F', + 'data': "0x", + 'topics': [ HexBytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'), HexBytes('0x0000000000000000000000000000000000000000000000000000000000000000'), HexBytes('0x000000000000000000000000302f442c5aa90177684b551883fc32b151178e7b'), HexBytes('0x0000000000000000000000000000000000000000000000000000000000000006') - ] - } - ] + ] + } + ] } -} \ No newline at end of file +} diff --git a/address-poisoning-py/src/web3_mock.py b/address-poisoning-py/src/web3_mock.py index ba61084a..f363c08b 100644 --- a/address-poisoning-py/src/web3_mock.py +++ b/address-poisoning-py/src/web3_mock.py @@ -1,38 +1,41 @@ from web3 import Web3 from hexbytes import HexBytes -from src.web3_constants_mock import * +from web3_constants_mock import * from unittest.mock import MagicMock +import asyncio class Web3Mock: def __init__(self): self.eth = EthMock() + @staticmethod + def to_checksum_address(address): + return Web3.to_checksum_address(address) + class EthMock: def __init__(self): - pass - - chain_id = 1 + self.chain_id = 1 def contract(self, address, abi): return ContractMock(address=address, abi=abi) - def get_code(self, address): - if address == Web3.toChecksumAddress(CONTRACT): + async def get_code(self, address): + if address == Web3.to_checksum_address(CONTRACT): return HexBytes( '0x608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100d9565b60405180910390f35b610073600480360381019061006e919061009d565b61007e565b005b60008054905090565b8060008190555050565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea26469706673582212209a159a4f3847890f10bfb87871a61eba91c5dbf5ee3cf6398207e292eee22a1664736f6c63430008070033' ) - elif address == Web3.toChecksumAddress(NEW_EOA) or address == Web3.toChecksumAddress(OLD_EOA): + elif address == Web3.to_checksum_address(NEW_EOA) or address == Web3.to_checksum_address(OLD_EOA): return HexBytes('0x') - + return HexBytes('0x') - def get_transaction_receipt(self, transaction_hash): + async def get_transaction_receipt(self, transaction_hash): transaction_receipt = MOCK_TX_HASH_LOGS_MAPPING[transaction_hash] return transaction_receipt - def get_transaction_count(self, address): + async def get_transaction_count(self, address): if address == "attacker": return 1 else: @@ -40,23 +43,29 @@ def get_transaction_count(self, address): class ContractMock: - def __init__(self, address, abi, functions=None): + def __init__(self, address, abi): self.address = address - self.functions = functions if functions is not None else FunctionsMock() - - if self.address == "0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F": - self.functions.symbol.return_value.call.return_value = "USDC" - elif self.address == "0xCf117403474eEaC230DaCcB3b54c0dABeB94Ae22": - self.functions.symbol.return_value.call.return_value = "USDT" - else: - self.functions.symbol.return_value.call.return_value = "NULL" + self.abi = abi + self.functions = FunctionsMock(address) - def __call__(self, address, *args, **kwargs): + async def __call__(self, address, *args, **kwargs): return self def __getattr__(self, name): return getattr(self.functions, name) + class FunctionsMock: - def __init__(self): - self.symbol = MagicMock() \ No newline at end of file + def __init__(self, address): + self.address = address + self.symbol_return_values = {} + self._set_symbol_return_value("0x4f06229a42e344b361D8dc9cA58D73e2597a9f1F", "USDC") + self._set_symbol_return_value("0xCf117403474eEaC230DaCcB3b54c0dABeB94Ae22", "USDT") + + def _set_symbol_return_value(self, address, symbol): + self.symbol_return_values[address.lower()] = symbol + + def symbol(self): + async def call(): + return self.symbol_return_values.get(self.address.lower(), "NULL") + return MagicMock(call=call)