diff --git a/craco.config.cjs b/craco.config.cjs new file mode 100644 index 0000000000..a139263690 --- /dev/null +++ b/craco.config.cjs @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +const { VanillaExtractPlugin } = require('@vanilla-extract/webpack-plugin') +const MiniCssExtractPlugin = require('mini-css-extract-plugin') + +module.exports = { + babel: { + plugins: ['@vanilla-extract/babel-plugin'], + }, + webpack: { + plugins: [new VanillaExtractPlugin()], + configure: (webpackConfig) => { + const instanceOfMiniCssExtractPlugin = webpackConfig.plugins.find( + (plugin) => plugin instanceof MiniCssExtractPlugin + ) + if (instanceOfMiniCssExtractPlugin !== undefined) instanceOfMiniCssExtractPlugin.options.ignoreOrder = true + return webpackConfig + }, + }, +} diff --git a/package.json b/package.json index 9abcbcc1df..6627181b73 100644 --- a/package.json +++ b/package.json @@ -14,11 +14,11 @@ "i18n:compile": "yarn i18n:extract && lingui compile", "i18n:pseudo": "lingui extract --locale pseudo && lingui compile", "prepare": "yarn contracts:compile && yarn graphql:generate && yarn i18n:compile", - "start": "react-scripts start", - "build": "react-scripts build", + "start": "craco start", + "build": "craco build", "serve": "serve build -l 3000", "lint": "yarn eslint .", - "test": "react-scripts test --coverage", + "test": "craco test --coverage", "cypress:open": "cypress open --browser chrome --e2e", "cypress:run": "cypress run --browser chrome --e2e" }, @@ -55,6 +55,7 @@ ] }, "devDependencies": { + "@craco/craco": "6.4.3", "@ethersproject/experimental": "^5.4.0", "@graphql-codegen/cli": "1.21.5", "@graphql-codegen/typescript": "1.22.3", @@ -85,6 +86,7 @@ "@types/styled-components": "^5.1.25", "@types/testing-library__cypress": "^5.0.5", "@types/ua-parser-js": "^0.7.35", + "@types/uuid": "^8.3.4", "@types/wcag-contrast": "^3.0.0", "@typescript-eslint/eslint-plugin": "^4", "@typescript-eslint/parser": "^4", @@ -104,7 +106,9 @@ "react-scripts": "^4.0.3", "serve": "^11.3.2", "typechain": "^5.0.0", - "typescript": "^4.4.3" + "typescript": "^4.4.3", + "@vanilla-extract/babel-plugin": "^1.1.7", + "@vanilla-extract/webpack-plugin": "^2.1.11" }, "dependencies": { "@amplitude/analytics-browser": "^0.5.1", @@ -115,7 +119,9 @@ "@lingui/core": "^3.14.0", "@lingui/macro": "^3.14.0", "@lingui/react": "^3.14.0", + "@looksrare/sdk": "^0.7.1", "@metamask/jazzicon": "^2.0.0", + "@opensea/seaport-js": "^1.0.2", "@popperjs/core": "^2.4.4", "@reach/dialog": "^0.10.3", "@reach/portal": "^0.10.3", @@ -135,6 +141,10 @@ "@uniswap/v3-core": "1.0.0", "@uniswap/v3-periphery": "^1.1.1", "@uniswap/v3-sdk": "^3.9.0", + "@vanilla-extract/css": "^1.7.2", + "@vanilla-extract/css-utils": "^0.1.2", + "@vanilla-extract/dynamic": "^2.0.2", + "@vanilla-extract/sprinkles": "^1.4.1", "@visx/axis": "^2.12.2", "@visx/event": "^2.6.0", "@visx/glyph": "^2.10.0", @@ -156,11 +166,13 @@ "array.prototype.flat": "^1.2.4", "array.prototype.flatmap": "^1.2.4", "cids": "^1.0.0", + "clsx": "^1.1.1", "copy-to-clipboard": "^3.2.0", "d3": "^7.6.1", "d3-curve-circlecorners": "^0.1.6", "ethers": "^5.1.4", "firebase": "^9.1.3", + "focus-visible": "^5.2.0", "fortmatic": "^2.4.0", "graphql": "^15.5.0", "graphql-request": "^3.4.0", @@ -185,9 +197,11 @@ "react-is": "^17.0.2", "react-markdown": "^4.3.1", "react-popper": "^2.2.3", + "react-query": "^3.39.1", "react-redux": "^8.0.2", "react-router-dom": "^6.3.0", "react-spring": "^8.0.27", + "react-table": "^7.8.0", "react-use-gesture": "^6.0.14", "react-virtualized-auto-sizer": "^1.0.2", "react-window": "^1.8.5", @@ -200,11 +214,14 @@ "ua-parser-js": "^0.7.28", "use-count-up": "^2.2.5", "use-resize-observer": "^9.0.2", + "uuid": "^8.3.2", + "video-extensions": "^1.2.0", "wcag-contrast": "^3.0.0", "web-vitals": "^2.1.0", "workbox-core": "^6.1.0", "workbox-navigation-preload": "^6.1.0", "workbox-precaching": "^6.1.0", - "workbox-routing": "^6.1.0" + "workbox-routing": "^6.1.0", + "zustand": "^4.0.0-rc.1" } } diff --git a/src/abis/erc1155.json b/src/abis/erc1155.json index d14e0e18bc..f9c904723e 100644 --- a/src/abis/erc1155.json +++ b/src/abis/erc1155.json @@ -1,15 +1,143 @@ [ { - "constant": true, + "inputs": [ + { + "internalType": "string", + "name": "uri_", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "indexed": false, + "internalType": "uint256[]", + "name": "values", + "type": "uint256[]" + } + ], + "name": "TransferBatch", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "TransferSingle", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "value", + "type": "string" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "URI", + "type": "event" + }, + { "inputs": [ { "internalType": "address", - "name": "_owner", + "name": "account", "type": "address" }, { "internalType": "uint256", - "name": "_id", + "name": "id", "type": "uint256" } ], @@ -21,16 +149,165 @@ "type": "uint256" } ], - "payable": false, "stateMutability": "view", "type": "function" }, { - "constant": true, + "inputs": [ + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + } + ], + "name": "balanceOfBatch", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeBatchTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { "inputs": [ { "internalType": "uint256", - "name": "_id", + "name": "", "type": "uint256" } ], @@ -42,7 +319,6 @@ "type": "string" } ], - "payable": false, "stateMutability": "view", "type": "function" } diff --git a/src/abis/erc721.json b/src/abis/erc721.json index 76339c6a0b..1852b1df52 100644 --- a/src/abis/erc721.json +++ b/src/abis/erc721.json @@ -1,5 +1,320 @@ [ { + "inputs": [ + { + "internalType": "address", + "name": "punkContract", + "type": "address" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "approved", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "ApprovalForAll", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "proxy", + "type": "address" + } + ], + "name": "ProxyRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "baseURI", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "punkIndex", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "getApproved", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + } + ], + "name": "isApprovedForAll", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "uint256", + "name": "punkIndex", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, "inputs": [ { "internalType": "uint256", @@ -15,10 +330,263 @@ "type": "address" } ], + "payable": false, "stateMutability": "view", "type": "function" }, { + "constant": false, + "inputs": [], + "name": "pause", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "proxyInfo", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "punkContract", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "registerProxy", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "name": "safeTransferFrom", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "bool", + "name": "approved", + "type": "bool" + } + ], + "name": "setApprovalForAll", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "string", + "name": "baseUri", + "type": "string" + } + ], + "name": "setBaseURI", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "tokenOfOwnerByIndex", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, "inputs": [ { "internalType": "uint256", @@ -34,7 +602,72 @@ "type": "string" } ], + "payable": false, "stateMutability": "view", "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "unpause", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" } ] diff --git a/src/hooks/useDebounce.ts b/src/hooks/useDebounce.ts index c03ee2bc68..11161319c8 100644 --- a/src/hooks/useDebounce.ts +++ b/src/hooks/useDebounce.ts @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useEffect, useRef, useState } from 'react' /** * Debounces updates to a value. @@ -24,3 +24,34 @@ export default function useDebounce(value: T, delay: number): T { return debouncedValue } + +export function useDebouncedCallback(callback: (...args: A) => void, wait: number) { + // track args & timeout handle between calls + const argsRef = useRef() + const timeout = useRef>() + + function cleanup() { + if (timeout.current) { + clearTimeout(timeout.current) + } + } + + // make sure our timeout gets cleared if + // our consuming component gets unmounted + useEffect(() => cleanup, []) + + return function debouncedCallback(...args: A) { + // capture latest args + argsRef.current = args + + // clear debounce timer + cleanup() + + // start waiting again + timeout.current = setTimeout(() => { + if (argsRef.current) { + callback(...argsRef.current) + } + }, wait) + } +} diff --git a/src/hooks/useImperativeDisableScroll.ts b/src/hooks/useImperativeDisableScroll.ts new file mode 100644 index 0000000000..7e7dcfacfc --- /dev/null +++ b/src/hooks/useImperativeDisableScroll.ts @@ -0,0 +1,17 @@ +import { useEffect } from 'react' + +/** + * Disable scroll of an element based on condition + */ +export function useImperativeDisableScroll({ element, disabled }: { element?: HTMLElement; disabled?: boolean }) { + useEffect(() => { + if (!element) return + + element.style.overflowY = disabled ? 'hidden' : 'auto' + + return () => { + element.style.overflowY = 'auto' + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [disabled]) +} diff --git a/src/hooks/useOnClickOutside.ts b/src/hooks/useOnClickOutside.ts new file mode 100644 index 0000000000..0307fe9d02 --- /dev/null +++ b/src/hooks/useOnClickOutside.ts @@ -0,0 +1,26 @@ +import { RefObject, useEffect, useRef } from 'react' + +export function useOnClickOutside( + node: RefObject, + handler: undefined | (() => void) +) { + const handlerRef = useRef void)>(handler) + useEffect(() => { + handlerRef.current = handler + }, [handler]) + + useEffect(() => { + const handleClickOutside = (e: MouseEvent) => { + if (node.current?.contains(e.target as Node) ?? false) { + return + } + if (handlerRef.current) handlerRef.current() + } + + document.addEventListener('mousedown', handleClickOutside) + + return () => { + document.removeEventListener('mousedown', handleClickOutside) + } + }, [node]) +} diff --git a/src/hooks/useTimeout.ts b/src/hooks/useTimeout.ts new file mode 100644 index 0000000000..34e700fd41 --- /dev/null +++ b/src/hooks/useTimeout.ts @@ -0,0 +1,27 @@ +import { useEffect, useState } from 'react' + +const getReturnValues = (countDown: number): [number, number, number, number] => { + // calculate time left + const days = Math.floor(countDown / (1000 * 60 * 60 * 24)) + const hours = Math.floor((countDown % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)) + const minutes = Math.floor((countDown % (1000 * 60 * 60)) / (1000 * 60)) + const seconds = Math.floor((countDown % (1000 * 60)) / 1000) + + return [days, hours, minutes, seconds] +} + +export const useTimeout = (targetDate: Date) => { + const countDownDate = new Date(targetDate).getTime() + + const [countDown, setCountDown] = useState(countDownDate - new Date().getTime()) + + useEffect(() => { + const interval = setInterval(() => { + setCountDown(countDownDate - new Date().getTime()) + }, 1000) + + return () => clearInterval(interval) + }, [countDownDate]) + + return getReturnValues(countDown) +} diff --git a/src/index.tsx b/src/index.tsx index 68402a41dd..ebb4990477 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -8,6 +8,7 @@ import { BlockNumberProvider } from 'lib/hooks/useBlockNumber' import { MulticallUpdater } from 'lib/state/multicall' import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' +import { QueryClient, QueryClientProvider } from 'react-query' import { Provider } from 'react-redux' import { HashRouter } from 'react-router-dom' @@ -25,6 +26,8 @@ import UserUpdater from './state/user/updater' import ThemeProvider, { ThemedGlobalStyle } from './theme' import RadialGradientByChainUpdater from './theme/RadialGradientByChainUpdater' +const queryClient = new QueryClient() + if (!!window.ethereum) { window.ethereum.autoRefreshOnNetworkChange = false } @@ -49,21 +52,23 @@ createRoot(container).render( - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/src/lib/hooks/useCurrencyBalance.ts b/src/lib/hooks/useCurrencyBalance.ts index 5990d293c4..09ee7ca9a2 100644 --- a/src/lib/hooks/useCurrencyBalance.ts +++ b/src/lib/hooks/useCurrencyBalance.ts @@ -141,3 +141,7 @@ export default function useCurrencyBalance( useMemo(() => [currency], [currency]) )[0] } + +export function useCurrencyBalanceString(account: string): string { + return useNativeCurrencyBalances(account ? [account] : [])?.[account ?? '']?.toSignificant(3) ?? '' +} diff --git a/src/nft/abis/CrossAssetSwap.json b/src/nft/abis/CrossAssetSwap.json new file mode 100644 index 0000000000..f881750d7b --- /dev/null +++ b/src/nft/abis/CrossAssetSwap.json @@ -0,0 +1,886 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_marketRegistry", + "type": "address" + }, + { + "internalType": "address", + "name": "_converter", + "type": "address" + }, + { + "internalType": "address", + "name": "_guardian", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "inputs": [], + "name": "GOV", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_affiliate", + "type": "address" + } + ], + "name": "addAffiliate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_marketId", + "type": "uint256" + } + ], + "name": "addSponsoredMarket", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "affiliates", + "outputs": [ + { + "internalType": "address", + "name": "affiliate", + "type": "address" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "baseFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "closeAllTrades", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "converter", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "guardian", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "marketRegistry", + "outputs": [ + { + "internalType": "contract MarketRegistry", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address[]", + "name": "tokenAddrs", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "internalType": "struct GenieSwap.ERC20Details", + "name": "erc20Details", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "tokenAddr", + "type": "address" + }, + { + "internalType": "address[]", + "name": "to", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + } + ], + "internalType": "struct SpecialTransferHelper.ERC721Details[]", + "name": "erc721Details", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "tokenAddr", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "internalType": "struct GenieSwap.ERC1155Details[]", + "name": "erc1155Details", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "bytes", + "name": "conversionData", + "type": "bytes" + } + ], + "internalType": "struct GenieSwap.ConverstionDetails[]", + "name": "converstionDetails", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "marketId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "tradeData", + "type": "bytes" + } + ], + "internalType": "struct MarketRegistry.TradeDetails[]", + "name": "tradeDetails", + "type": "tuple[]" + }, + { + "internalType": "address[]", + "name": "dustTokens", + "type": "address[]" + }, + { + "internalType": "uint256[2]", + "name": "feeDetails", + "type": "uint256[2]" + } + ], + "name": "multiAssetSwap", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "address[]", + "name": "tokenAddrs", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "internalType": "struct GenieSwap.ERC20Details", + "name": "erc20Details", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "address", + "name": "tokenAddr", + "type": "address" + }, + { + "internalType": "address[]", + "name": "to", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + } + ], + "internalType": "struct SpecialTransferHelper.ERC721Details[]", + "name": "erc721Details", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "address", + "name": "tokenAddr", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "internalType": "struct GenieSwap.ERC1155Details[]", + "name": "erc1155Details", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "bytes", + "name": "conversionData", + "type": "bytes" + } + ], + "internalType": "struct GenieSwap.ConverstionDetails[]", + "name": "converstionDetails", + "type": "tuple[]" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "marketId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "tradeData", + "type": "bytes" + } + ], + "internalType": "struct MarketRegistry.TradeDetails[]", + "name": "tradeDetails", + "type": "tuple[]" + }, + { + "internalType": "address[]", + "name": "dustTokens", + "type": "address[]" + }, + { + "internalType": "uint256", + "name": "sponsoredMarketIndex", + "type": "uint256" + } + ], + "name": "multiAssetSwapWithoutFee", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155BatchReceived", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC1155Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "onERC721Received", + "outputs": [ + { + "internalType": "bytes4", + "name": "", + "type": "bytes4" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "openForFreeTrades", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "openForTrades", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "punkProxy", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "rescueERC1155", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "rescueERC20", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "internalType": "uint256[]", + "name": "ids", + "type": "uint256[]" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "rescueERC721", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "rescueETH", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_baseFees", + "type": "uint256" + } + ], + "name": "setBaseFees", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_converter", + "type": "address" + } + ], + "name": "setConverter", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract MarketRegistry", + "name": "_marketRegistry", + "type": "address" + } + ], + "name": "setMarketRegistry", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IERC20", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "operator", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "setOneTimeApproval", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_openForFreeTrades", + "type": "bool" + } + ], + "name": "setOpenForFreeTrades", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bool", + "name": "_openForTrades", + "type": "bool" + } + ], + "name": "setOpenForTrades", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "setUp", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "sponsoredMarkets", + "outputs": [ + { + "internalType": "uint256", + "name": "marketId", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_affiliateIndex", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_affiliate", + "type": "address" + }, + { + "internalType": "bool", + "name": "_IsActive", + "type": "bool" + } + ], + "name": "updateAffiliate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_guardian", + "type": "address" + } + ], + "name": "updateGuardian", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_marketIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_marketId", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "_isActive", + "type": "bool" + } + ], + "name": "updateSponsoredMarket", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/src/nft/abis/CryptoPunksMarket.json b/src/nft/abis/CryptoPunksMarket.json new file mode 100644 index 0000000000..edb8925cc4 --- /dev/null +++ b/src/nft/abis/CryptoPunksMarket.json @@ -0,0 +1,595 @@ +[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + } + ], + "name": "punksOfferedForSale", + "outputs": [ + { + "name": "isForSale", + "type": "bool" + }, + { + "name": "punkIndex", + "type": "uint256" + }, + { + "name": "seller", + "type": "address" + }, + { + "name": "minValue", + "type": "uint256" + }, + { + "name": "onlySellTo", + "type": "address" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "punkIndex", + "type": "uint256" + } + ], + "name": "enterBidForPunk", + "outputs": [], + "payable": true, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "punkIndex", + "type": "uint256" + }, + { + "name": "minPrice", + "type": "uint256" + } + ], + "name": "acceptBidForPunk", + "outputs": [], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "addresses", + "type": "address[]" + }, + { + "name": "indices", + "type": "uint256[]" + } + ], + "name": "setInitialOwners", + "outputs": [], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "withdraw", + "outputs": [], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "imageHash", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "nextPunkIndexToAssign", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + } + ], + "name": "punkIndexToAddress", + "outputs": [ + { + "name": "", + "type": "address" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "standard", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "uint256" + } + ], + "name": "punkBids", + "outputs": [ + { + "name": "hasBid", + "type": "bool" + }, + { + "name": "punkIndex", + "type": "uint256" + }, + { + "name": "bidder", + "type": "address" + }, + { + "name": "value", + "type": "uint256" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "allInitialOwnersAssigned", + "outputs": [], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "allPunksAssigned", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "punkIndex", + "type": "uint256" + } + ], + "name": "buyPunk", + "outputs": [], + "payable": true, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "punkIndex", + "type": "uint256" + } + ], + "name": "transferPunk", + "outputs": [], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "punkIndex", + "type": "uint256" + } + ], + "name": "withdrawBidForPunk", + "outputs": [], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "to", + "type": "address" + }, + { + "name": "punkIndex", + "type": "uint256" + } + ], + "name": "setInitialOwner", + "outputs": [], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "punkIndex", + "type": "uint256" + }, + { + "name": "minSalePriceInWei", + "type": "uint256" + }, + { + "name": "toAddress", + "type": "address" + } + ], + "name": "offerPunkForSaleToAddress", + "outputs": [], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "punksRemainingToAssign", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "punkIndex", + "type": "uint256" + }, + { + "name": "minSalePriceInWei", + "type": "uint256" + } + ], + "name": "offerPunkForSale", + "outputs": [], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "punkIndex", + "type": "uint256" + } + ], + "name": "getPunk", + "outputs": [], + "payable": false, + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "", + "type": "address" + } + ], + "name": "pendingWithdrawals", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "punkIndex", + "type": "uint256" + } + ], + "name": "punkNoLongerForSale", + "outputs": [], + "payable": false, + "type": "function" + }, + { + "inputs": [], + "payable": true, + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "punkIndex", + "type": "uint256" + } + ], + "name": "Assign", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "punkIndex", + "type": "uint256" + } + ], + "name": "PunkTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "punkIndex", + "type": "uint256" + }, + { + "indexed": false, + "name": "minValue", + "type": "uint256" + }, + { + "indexed": true, + "name": "toAddress", + "type": "address" + } + ], + "name": "PunkOffered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "punkIndex", + "type": "uint256" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + }, + { + "indexed": true, + "name": "fromAddress", + "type": "address" + } + ], + "name": "PunkBidEntered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "punkIndex", + "type": "uint256" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + }, + { + "indexed": true, + "name": "fromAddress", + "type": "address" + } + ], + "name": "PunkBidWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "punkIndex", + "type": "uint256" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + }, + { + "indexed": true, + "name": "fromAddress", + "type": "address" + }, + { + "indexed": true, + "name": "toAddress", + "type": "address" + } + ], + "name": "PunkBought", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "punkIndex", + "type": "uint256" + } + ], + "name": "PunkNoLongerForSale", + "type": "event" + } +] diff --git a/src/nft/abis/WyvernProxyRegistry.json b/src/nft/abis/WyvernProxyRegistry.json new file mode 100644 index 0000000000..696e51f3e6 --- /dev/null +++ b/src/nft/abis/WyvernProxyRegistry.json @@ -0,0 +1,153 @@ +[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [{ "name": "", "type": "string" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "initialAddressSet", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "addr", "type": "address" }], + "name": "endGrantAuthentication", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "addr", "type": "address" }], + "name": "revokeAuthentication", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "", "type": "address" }], + "name": "pending", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "", "type": "address" }], + "name": "contracts", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "owner", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "delegateProxyImplementation", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "", "type": "address" }], + "name": "proxies", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "addr", "type": "address" }], + "name": "startGrantAuthentication", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "registerProxy", + "outputs": [{ "name": "proxy", "type": "address" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "DELAY_PERIOD", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "authAddress", "type": "address" }], + "name": "grantInitialAuthentication", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "newOwner", "type": "address" }], + "name": "transferOwnership", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { "inputs": [], "payable": false, "stateMutability": "nonpayable", "type": "constructor" }, + { + "anonymous": false, + "inputs": [{ "indexed": true, "name": "previousOwner", "type": "address" }], + "name": "OwnershipRenounced", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "previousOwner", "type": "address" }, + { "indexed": true, "name": "newOwner", "type": "address" } + ], + "name": "OwnershipTransferred", + "type": "event" + } +] diff --git a/src/nft/css/atoms.ts b/src/nft/css/atoms.ts new file mode 100644 index 0000000000..35f03c201b --- /dev/null +++ b/src/nft/css/atoms.ts @@ -0,0 +1,19 @@ +import clsx from 'clsx' + +import * as resetStyles from './reset.css' +import { Sprinkles, sprinkles } from './sprinkles.css' + +export interface Atoms extends Sprinkles { + // reset is used by the Box component when its expected to behave as something other than a div, ie button, a, or span + reset?: keyof JSX.IntrinsicElements +} + +export const atoms = ({ reset, ...rest }: Atoms) => { + if (!reset) return sprinkles(rest) + + const elementReset = resetStyles.element[reset as keyof typeof resetStyles.element] + + const sprinklesClasses = sprinkles(rest) + + return clsx(resetStyles.base, elementReset, sprinklesClasses) +} diff --git a/src/nft/css/common.css.ts b/src/nft/css/common.css.ts new file mode 100644 index 0000000000..d378f611a7 --- /dev/null +++ b/src/nft/css/common.css.ts @@ -0,0 +1,119 @@ +import { style } from '@vanilla-extract/css' + +import { sprinkles, themeVars } from './sprinkles.css' + +export const center = sprinkles({ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', +}) + +export const row = sprinkles({ + display: 'flex', + flexDirection: 'row', + alignItems: 'center', +}) + +export const column = sprinkles({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', +}) + +export const section = style([ + sprinkles({ + paddingLeft: { mobile: '16', desktopL: '0' }, + paddingRight: { mobile: '16', desktopL: '0' }, + }), + { maxWidth: '1000px', margin: '0 auto' }, +]) + +// TYPOGRAPHY +export const header1 = sprinkles({ fontWeight: 'normal', fontSize: '36' }) +export const header2 = sprinkles({ fontWeight: 'normal', fontSize: '28' }) +export const headlineSmall = sprinkles({ fontWeight: 'normal', fontSize: '20' }) +export const subhead = sprinkles({ fontWeight: 'medium', fontSize: '16' }) +export const subheadSmall = sprinkles({ fontWeight: 'medium', fontSize: '14' }) +export const body = sprinkles({ fontSize: '16' }) +export const bodySmall = sprinkles({ + fontSize: '14', +}) +export const caption = sprinkles({ fontWeight: 'bold', fontSize: '12' }) +export const badge = style([sprinkles({ fontWeight: 'semibold', fontSize: '10' }), { letterSpacing: '0.5px' }]) +export const buttonTextLarge = sprinkles({ fontWeight: 'medium', fontSize: '28' }) + +export const buttonTextMedium = sprinkles({ fontWeight: 'medium', fontSize: '16' }) +export const buttonMedium = style([ + buttonTextMedium, + sprinkles({ + backgroundColor: 'blue', + borderRadius: '12', + color: 'explicitWhite', + transition: '250', + boxShadow: { hover: 'elevation' }, + }), + { + cursor: 'pointer', + padding: '14px 18px', + border: 'none', + ':hover': { + cursor: 'pointer', + }, + ':disabled': { + cursor: 'auto', + opacity: '0.3', + }, + }, +]) + +export const buttonReset = sprinkles({ + border: 'none', + background: 'none', + cursor: 'pointer', +}) + +export const disabled = style([ + { + padding: '19px 17px', + boxSizing: 'border-box', + textAlign: 'left', + }, + sprinkles({ + color: 'placeholder', + fontWeight: 'medium', + background: 'whitesmoke', + borderRadius: '14', + borderStyle: 'none', + width: 'full', + fontSize: '16', + }), +]) + +export const buttonTextSmall = sprinkles({ fontWeight: 'normal', fontSize: '14' }) +export const buttonSmall = style([ + buttonTextSmall, + sprinkles({ + background: 'lightGray', + borderRadius: '12', + fontSize: '12', + color: 'genieBlue', + transition: '250', + boxShadow: { hover: 'elevation' }, + }), + { + padding: '2px 8px', + border: 'none', + ':hover': { + cursor: 'pointer', + }, + ':disabled': { + cursor: 'auto', + color: themeVars.colors.white, + backgroundColor: themeVars.colors.medGray, + }, + }, +]) + +export const imageHover = style({ + transform: 'scale(1.25)', +}) diff --git a/src/nft/css/cssObjectFromTheme.ts b/src/nft/css/cssObjectFromTheme.ts new file mode 100644 index 0000000000..95193190f9 --- /dev/null +++ b/src/nft/css/cssObjectFromTheme.ts @@ -0,0 +1,26 @@ +import { assignInlineVars } from '@vanilla-extract/dynamic' + +import { Theme, themeVars } from './sprinkles.css' + +const resolveTheme = (theme: Theme | (() => Theme)) => (typeof theme === 'function' ? theme() : theme) + +export function cssObjectFromTheme( + theme: Theme | (() => Theme), + { extends: baseTheme }: { extends?: Theme | (() => Theme) } = {} +) { + const resolvedThemeVars = { + ...assignInlineVars(themeVars, resolveTheme(theme)), + } + + if (!baseTheme) { + return resolvedThemeVars + } + + const resolvedBaseThemeVars = assignInlineVars(themeVars, resolveTheme(baseTheme)) + + const filteredVars = Object.fromEntries( + Object.entries(resolvedThemeVars).filter(([varName, value]) => value !== resolvedBaseThemeVars[varName]) + ) + + return filteredVars +} diff --git a/src/nft/css/cssStringFromTheme.ts b/src/nft/css/cssStringFromTheme.ts new file mode 100644 index 0000000000..22b42bab25 --- /dev/null +++ b/src/nft/css/cssStringFromTheme.ts @@ -0,0 +1,8 @@ +import { cssObjectFromTheme } from './cssObjectFromTheme' +import { Theme } from './sprinkles.css' + +export function cssStringFromTheme(theme: Theme | (() => Theme), options: { extends?: Theme | (() => Theme) } = {}) { + return Object.entries(cssObjectFromTheme(theme, options)) + .map(([key, value]) => `${key}:${value};`) + .join('') +} diff --git a/src/nft/css/loading.css.ts b/src/nft/css/loading.css.ts new file mode 100644 index 0000000000..616672f517 --- /dev/null +++ b/src/nft/css/loading.css.ts @@ -0,0 +1,21 @@ +import { keyframes, style } from '@vanilla-extract/css' +import { darken } from 'polished' + +export const loadingAnimation = keyframes({ + '0%': { + backgroundPosition: '100% 50%', + }, + '100%': { + backgroundPosition: '0% 50%', + }, +}) + +export const loadingBlock = style([ + { + animation: `${loadingAnimation} 1.5s infinite`, + animationFillMode: 'both', + background: `linear-gradient(to left, #7C85A24D 25%, ${darken(0.8, '#7C85A24D')} 50%, #7C85A24D 75%)`, + backgroundSize: '400%', + willChange: 'background-position', + }, +]) diff --git a/src/nft/css/reset.css.ts b/src/nft/css/reset.css.ts new file mode 100644 index 0000000000..167daf1b76 --- /dev/null +++ b/src/nft/css/reset.css.ts @@ -0,0 +1,106 @@ +import 'focus-visible' + +import { style } from '@vanilla-extract/css' + +const hideFocusRingsDataAttribute = '[data-js-focus-visible] &:focus:not([data-focus-visible-added])' + +export const base = style({ + boxSizing: 'border-box', + selectors: { + [`${hideFocusRingsDataAttribute}`]: { + outline: 'none', + }, + }, + verticalAlign: 'baseline', + WebkitTapHighlightColor: 'transparent', +}) + +const list = style({ + listStyle: 'none', +}) + +const quote = style({ + quotes: 'none', + selectors: { + '&:before, &:after': { + content: "''", + }, + }, +}) + +const table = style({ + borderCollapse: 'collapse', + borderSpacing: 0, +}) + +const appearance = style({ + appearance: 'none', +}) + +const field = style([ + appearance, + { + '::placeholder': { + opacity: 1, + }, + outline: 'none', + }, +]) + +const mark = style({ + backgroundColor: 'transparent', + color: 'inherit', +}) + +const select = style([ + field, + { + ':disabled': { + opacity: 1, + }, + selectors: { + '&::-ms-expand': { + display: 'none', + }, + }, + }, +]) + +const input = style([ + field, + { + selectors: { + '&::-ms-clear': { + display: 'none', + }, + '&::-webkit-search-cancel-button': { + WebkitAppearance: 'none', + }, + '&::-webkit-inner-spin-button, &::-webkit-inner-spin-button ': { + WebkitAppearance: 'none', + }, + }, + WebkitAppearance: 'none', + MozAppearance: 'textfield', + ':focus': { + outline: 'none', + }, + }, +]) + +const a = style({ + textDecoration: 'none', +}) + +export const element = { + a, + blockquote: quote, + input, + mark, + ol: list, + q: quote, + select, + table, + textarea: field, + ul: list, +} diff --git a/src/nft/css/sprinkles.css.ts b/src/nft/css/sprinkles.css.ts new file mode 100644 index 0000000000..3e14cf542e --- /dev/null +++ b/src/nft/css/sprinkles.css.ts @@ -0,0 +1,366 @@ +import { createGlobalTheme } from '@vanilla-extract/css' +import { createGlobalThemeContract } from '@vanilla-extract/css' +import { createSprinkles, defineProperties } from '@vanilla-extract/sprinkles' + +const themeContractValues = { + colors: { + // Pavel's colors, mostly used for the wallet connection. TODO Some may need to be changed / removed + error: '', + textDisconnect: '', + modalBackdrop: '', + backgroundSecondary: '', + modalClose: '', + text: '', + modalTextSecondary: '', + + // Bryan's colors from Figma that vary dark vs light + blackBlue: '', + darkGray: '', + medGray: '', + lightGray: '', + white: '', + darkGray10: '', + blackBlue20: '', + explicitWhite: '', + magicGradient: '', + placeholder: '', + lightGrayButton: '', + lightGrayContainer: ',', + lightGrayOverlay: '', + + // Opacities of black and whit + white95: '', + white90: '', + white80: '', + }, + + shadows: { + menu: '', + genieBlue: '', + elevation: '', + tooltip: '', + }, +} + +export type Theme = typeof themeContractValues + +type DeepPartial = { + [P in keyof T]?: DeepPartial +} +export type ThemePartial = DeepPartial + +export const themeVars = createGlobalThemeContract(themeContractValues, (_, path) => `genie-${path.join('-')}`) + +const dimensions = { + '0': '0', + '2': '2', + '4': '4px', + '8': '8px', + '16': '16px', + '18': '18px', + '20': '20px', + '24': '24px', + '26': '26px', + '28': '28px', + '32': '32px', + '36': '36px', + '40': '40px', + '42': '42px', + '44': '44px', + '48': '48px', + '52': '52px', + '54': '54px', + '56': '56px', + '60': '60px', + '64': '64px', + '72': '72px', + '100': '100px', + '120': '120px', + '160': '160px', + '276': '276px', + '288': '288px', + '292': '292px', + '386': '386px', + half: '50%', + full: '100%', + min: 'min-content', + max: 'max-content', + viewHeight: '100vh', + viewWidth: '100vw', + auto: 'auto', + inherit: 'inherit', +} + +const spacing = { + '0': '0', + '1': '1px', + '2': '2px', + '4': '4px', + '6': '6px', + '8': '8px', + '10': '10px', + '12': '12px', + '14': '14px', + '16': '16px', + '18': '18px', + '20': '20px', + '24': '24px', + '28': '28px', + '32': '32px', + '36': '36px', + '40': '40px', + '48': '48px', + '50': '50px', + '52': '52px', + '60': '60px', + '64': '64px', + '82': '82px', + '72': '72px', + '88': '88px', + '100': '100px', + '104': '104px', + '136': '136px', + '150': '150px', + '1/2': '50%', + auto: 'auto', + unset: 'unset', +} + +export const vars = createGlobalTheme(':root', { + color: { + ...themeVars.colors, + genieBlue: '#4C82FB', + fallbackGradient: 'linear-gradient(270deg, #D1D5DB 0%, #F6F6F6 100%)', + dropShadow: '0px 4px 16px rgba(70, 115, 250, 0.4)', + green: '#209853', + orange: '#FA2C38', + // Pavel's colors, TODO probably remove them after Pavel continues Genie List + black: 'black', + whitesmoke: '#F5F5F5', + blue: '#4C82FB', + explicitBlackBlue: '#0E111A', + gray: '#CBCEDC', + transculent: '#7F7F7F', + transparent: 'transparent', + none: 'none', + + // new uniswap colors: + blue400: '#4C82FB', + pink400: '#FB118E', + red700: '#530f10', + red400: '#FA2C38', + green200: '#5CFE9D', + green400: '#1A9550', + grey900: '#0E111A', + grey700: '#293249', + grey500: '#5E6887', + grey400: '#7C85A2', + grey300: '#99A1BD', + grey200: '#B7BED4', + grey100: '#DDE3F7', + grey50: '#EDEFF7', + }, + border: { + transculent: '1.5px solid rgba(0, 0, 0, 0.1)', + none: 'none', + }, + radii: { + menu: '16px', + modal: '24px', + '0': '0px', + '4': '4px', + '8': '8px', + '10': '10px', + '12': '12px', + '14': '14px', + '16': '16px', + '20': '20px', + '30': '30px', + '40': '40px', + '100': '100px', + round: '9999px', + }, + fontSize: { + '0': '0', + '10': '10px', + '12': '12px', + '14': '14px', + '16': '16px', + '20': '20px', + '24': '24px', + '28': '28px', + '34': '34px', + '36': '36px', + '40': '40px', + '48': '48px', + '60': '60px', + '96': '96px', + }, + fontWeight: { + normal: '400', + medium: '500', + semibold: '600', + bold: '700', + black: '900', + }, + time: { + '250': '250ms', + '500': '500ms', + }, + fonts: { + body: 'Inter, sans-serif', + heading: 'Adieu, sans-serif', + }, +}) + +const flexAlignment = [ + 'flex-start', + 'center', + 'flex-end', + 'stretch', + 'baseline', + 'space-around', + 'space-between', +] as const + +const overflow = ['hidden', 'inherit', 'scroll', 'visible', 'auto'] as const + +const borderWidth = ['1px', '1.5px', '2px', '4px'] + +const borderStyle = ['none', 'solid'] as const + +export const breakpoints = { + tabletSm: 656, + tablet: 708, + tabletL: 784, + tabletXl: 830, + desktop: 948, + desktopL: 1030, + desktopXl: 1260, +} + +const layoutStyles = defineProperties({ + conditions: { + mobile: {}, + tabletSm: { '@media': `screen and (min-width: ${breakpoints.tabletSm}px)` }, + tablet: { '@media': `screen and (min-width: ${breakpoints.tablet})` }, + tabletL: { '@media': `screen and (min-width: ${breakpoints.tabletL}px)` }, + tabletXl: { '@media': `screen and (min-width: ${breakpoints.tabletXl}px)` }, + desktop: { '@media': `screen and (min-width: ${breakpoints.desktop}px)` }, + desktopL: { '@media': `screen and (min-width: ${breakpoints.desktopL}px)` }, + desktopXl: { '@media': `screen and (min-width: ${breakpoints.desktopXl}px)` }, + }, + defaultCondition: 'mobile', + properties: { + alignItems: flexAlignment, + alignSelf: flexAlignment, + justifyItems: flexAlignment, + justifySelf: flexAlignment, + placeItems: flexAlignment, + placeContent: flexAlignment, + fontSize: vars.fontSize, + fontWeight: vars.fontWeight, + marginBottom: spacing, + marginLeft: spacing, + marginRight: spacing, + marginTop: spacing, + width: dimensions, + height: dimensions, + maxWidth: dimensions, + minWidth: dimensions, + maxHeight: dimensions, + minHeight: dimensions, + paddingBottom: spacing, + paddingLeft: spacing, + paddingRight: spacing, + paddingTop: spacing, + padding: spacing, + bottom: spacing, + left: spacing, + right: spacing, + top: spacing, + margin: spacing, + zIndex: ['auto', '0', '1', '2', '3'], + gap: spacing, + flexShrink: spacing, + flex: ['1', '2', '3'], + flexWrap: ['nowrap', 'wrap', 'wrap-reverse'], + display: ['none', 'block', 'flex', 'inline-flex', 'inline-block', 'grid', 'inline'], + whiteSpace: ['nowrap'], + textOverflow: ['ellipsis'], + textAlign: ['left', 'right', 'center', 'justify'], + visibility: ['visible', 'hidden'], + flexDirection: ['row', 'column', 'column-reverse'], + justifyContent: flexAlignment, + position: ['absolute', 'fixed', 'relative', 'sticky', 'static'], + objectFit: ['contain', 'cover'], + order: [0, 1], + } as const, + shorthands: { + paddingX: ['paddingLeft', 'paddingRight'], + paddingY: ['paddingTop', 'paddingBottom'], + marginX: ['marginLeft', 'marginRight'], + marginY: ['marginTop', 'marginBottom'], + }, +}) + +const colorStyles = defineProperties({ + conditions: { + default: {}, + hover: { selector: '&:hover' }, + active: { selector: '&:active' }, + focus: { selector: '&:focus' }, + before: { selector: '&:before' }, + placeholder: { selector: '&::placeholder' }, + }, + defaultCondition: 'default', + properties: { + color: vars.color, + background: vars.color, + borderColor: vars.color, + borderBottomColor: vars.color, + borderTopColor: vars.color, + backgroundColor: vars.color, + outlineColor: vars.color, + fill: vars.color, + }, +}) + +const unresponsiveProperties = defineProperties({ + conditions: { + default: {}, + hover: { selector: '&:hover' }, + active: { selector: '&:active' }, + before: { selector: '&:before' }, + }, + defaultCondition: 'default', + properties: { + cursor: ['default', 'pointer', 'auto'], + borderStyle, + borderBottomStyle: borderStyle, + borderRadius: vars.radii, + borderTopLeftRadius: vars.radii, + borderTopRightRadius: vars.radii, + borderBottomLeftRadius: vars.radii, + borderBottomRightRadius: vars.radii, + border: vars.border, + borderBottom: vars.border, + borderTop: vars.border, + borderWidth, + borderBottomWidth: borderWidth, + fontFamily: vars.fonts, + overflow, + overflowX: overflow, + overflowY: overflow, + boxShadow: { ...themeVars.shadows, none: 'none', dropShadow: vars.color.dropShadow }, + lineHeight: ['1', 'auto'], + transition: vars.time, + transitionDuration: vars.time, + animationDuration: vars.time, + }, +}) + +export type UnresponsiveProperties = keyof typeof unresponsiveProperties + +export const sprinkles = createSprinkles(layoutStyles, colorStyles, unresponsiveProperties) +export type Sprinkles = Parameters[0] diff --git a/src/nft/hooks/index.ts b/src/nft/hooks/index.ts new file mode 100644 index 0000000000..3d8efc1c7b --- /dev/null +++ b/src/nft/hooks/index.ts @@ -0,0 +1,14 @@ +export * from './useCart' +export * from './useCollectionFilters' +export * from './useFiltersExpanded' +export * from './useGenieList' +export * from './useIsMobile' +export * from './useMarketplaceSelect' +export * from './useNFTSelect' +export * from './useSearchHistory' +export * from './useSelectAsset' +export * from './useSellAsset' +export * from './useSellPageState' +export * from './useSweep' +export * from './useWalletBalance' +export * from './useWalletCollections' diff --git a/src/nft/hooks/useCart.ts b/src/nft/hooks/useCart.ts new file mode 100644 index 0000000000..5ade7a7b53 --- /dev/null +++ b/src/nft/hooks/useCart.ts @@ -0,0 +1,20 @@ +import create from 'zustand' +import { devtools } from 'zustand/middleware' + +interface CartState { + cartExpanded: boolean + toggleCart: () => void +} + +export const useCart = create()( + devtools( + (set) => ({ + cartExpanded: false, + toggleCart: () => + set(({ cartExpanded }) => ({ + cartExpanded: !cartExpanded, + })), + }), + { name: 'useCart' } + ) +) diff --git a/src/nft/hooks/useCollectionFilters.ts b/src/nft/hooks/useCollectionFilters.ts new file mode 100644 index 0000000000..f01d9c6a5a --- /dev/null +++ b/src/nft/hooks/useCollectionFilters.ts @@ -0,0 +1,97 @@ +import create from 'zustand' +import { devtools } from 'zustand/middleware' + +export enum SortBy { + LowToHigh, + HighToLow, + RareToCommon, + CommonToRare, +} + +export const SortByPointers = { + [SortBy.HighToLow]: 'highest', + [SortBy.LowToHigh]: 'lowest', + [SortBy.RareToCommon]: 'rare', + [SortBy.CommonToRare]: 'common', +} + +export type Trait = { + trait_type: string + trait_value: string + trait_count: number + floorPrice?: number +} + +interface State { + traits: Trait[] + markets: string[] + minPrice: number | '' + maxPrice: number | '' + minRarity: number | '' + maxRarity: number | '' + marketCount: Record + buyNow: boolean + search: string + sortBy: SortBy + showFullTraitName: { shouldShow: boolean; trait_value?: string; trait_type: string } +} + +type Actions = { + setMarketCount: (_: Record) => void + addMarket: (market: string) => void + removeMarket: (market: string) => void + addTrait: (trait: Trait) => void + removeTrait: (trait: Trait) => void + reset: () => void + setMinPrice: (price: number | '') => void + setMaxPrice: (price: number | '') => void + setMinRarity: (range: number | '') => void + setMaxRarity: (range: number | '') => void + setBuyNow: (bool: boolean) => void + setSearch: (term: string) => void + setSortBy: (sortBy: SortBy) => void + toggleShowFullTraitName: (show: { shouldShow: boolean; trait_value: string; trait_type: string }) => void +} + +export type CollectionFilters = State & Actions + +export const initialCollectionFilterState: State = { + minPrice: '', + maxPrice: '', + minRarity: '', + maxRarity: '', + traits: [], + markets: [], + marketCount: {}, + buyNow: true, + search: '', + sortBy: SortBy.LowToHigh, + showFullTraitName: { shouldShow: false, trait_value: '', trait_type: '' }, +} + +export const useCollectionFilters = create()( + devtools( + (set) => ({ + ...initialCollectionFilterState, + setSortBy: (sortBy) => set({ sortBy }), + setSearch: (search) => set({ search }), + setBuyNow: (buyNow) => set({ buyNow }), + setMarketCount: (marketCount) => set({ marketCount }), + addMarket: (market) => set(({ markets }) => ({ markets: [...markets, market] })), + removeMarket: (market) => set(({ markets }) => ({ markets: markets.filter((_market) => market !== _market) })), + addTrait: (trait) => set(({ traits }) => ({ traits: [...traits, trait] })), + removeTrait: (trait) => + set(({ traits }) => ({ + traits: traits.filter((x) => JSON.stringify(x) !== JSON.stringify(trait)), + })), + reset: () => set(() => ({ traits: [], minRarity: '', maxRarity: '', markets: [] })), + setMinPrice: (price) => set(() => ({ minPrice: price })), + setMaxPrice: (price) => set(() => ({ maxPrice: price })), + setMinRarity: (range) => set(() => ({ minRarity: range })), + setMaxRarity: (range) => set(() => ({ maxRarity: range })), + toggleShowFullTraitName: ({ shouldShow, trait_value, trait_type }) => + set(() => ({ showFullTraitName: { shouldShow, trait_value, trait_type } })), + }), + { name: 'useCollectionTraits' } + ) +) diff --git a/src/nft/hooks/useFiltersExpanded.ts b/src/nft/hooks/useFiltersExpanded.ts new file mode 100644 index 0000000000..24f86669af --- /dev/null +++ b/src/nft/hooks/useFiltersExpanded.ts @@ -0,0 +1,30 @@ +import create from 'zustand' +import { devtools, persist } from 'zustand/middleware' + +interface State { + isExpanded: boolean + setExpanded: (expanded: boolean) => void +} + +const useFiltersExpandedStore = create()( + persist( + devtools( + (set) => ({ + isExpanded: false, + setExpanded: (expanded) => + set(() => ({ + isExpanded: expanded, + })), + }), + { name: 'useFiltersExpanded' } + ), + { name: 'useFiltersExpanded' } + ) +) + +export const useFiltersExpanded = (): [boolean, (expanded: boolean) => void] => { + const isExpanded = useFiltersExpandedStore((s) => s.isExpanded) + const setExpanded = useFiltersExpandedStore((s) => s.setExpanded) + + return [isExpanded, setExpanded] +} diff --git a/src/nft/hooks/useGenieList.ts b/src/nft/hooks/useGenieList.ts new file mode 100644 index 0000000000..59169a0b96 --- /dev/null +++ b/src/nft/hooks/useGenieList.ts @@ -0,0 +1,21 @@ +import create from 'zustand' +import { devtools } from 'zustand/middleware' + +interface GenieListState { + looksRareNonce: number + setLooksRareNonce: (nonce: number) => void + getLooksRareNonce: () => number +} + +export const useGenieList = create()( + devtools((set, get) => ({ + looksRareNonce: 0, + setLooksRareNonce: (nonce) => + set(() => { + return { looksRareNonce: nonce } + }), + getLooksRareNonce: () => { + return get().looksRareNonce + }, + })) +) diff --git a/src/nft/hooks/useIsMobile.ts b/src/nft/hooks/useIsMobile.ts new file mode 100644 index 0000000000..e97ccb2fb5 --- /dev/null +++ b/src/nft/hooks/useIsMobile.ts @@ -0,0 +1,25 @@ +import create from 'zustand' +import { devtools } from 'zustand/middleware' + +import { breakpoints } from '../css/sprinkles.css' + +interface IsMobileState { + isMobile: boolean + width: number + setMobileWidth: (width: number) => void +} + +export const useIsMobile = create()( + devtools( + (set) => ({ + isMobile: true, + width: 800, + setMobileWidth: (width: number) => + set(() => ({ + width, + isMobile: width < breakpoints.tabletSm, + })), + }), + { name: 'isMobile' } + ) +) diff --git a/src/nft/hooks/useMarketplaceSelect.ts b/src/nft/hooks/useMarketplaceSelect.ts new file mode 100644 index 0000000000..b5d56d9d0d --- /dev/null +++ b/src/nft/hooks/useMarketplaceSelect.ts @@ -0,0 +1,24 @@ +import create from 'zustand' +import { devtools } from 'zustand/middleware' + +export type MarketplaceOption = { name: string; icon: string } + +interface State { + options: MarketplaceOption[] + select: (o: MarketplaceOption) => void +} + +export const useMarketplaceSelect = create()( + devtools( + (set) => ({ + options: [], + select: (option) => + set(({ options }) => { + if (options.find((o) => option.name === o.name)) + return { options: options.filter((x) => x.name !== option.name) } + else return { options: [...options, option] } + }), + }), + { name: 'useMarketplaceSelect' } + ) +) diff --git a/src/nft/hooks/useNFTSelect.ts b/src/nft/hooks/useNFTSelect.ts new file mode 100644 index 0000000000..07d56ea2b0 --- /dev/null +++ b/src/nft/hooks/useNFTSelect.ts @@ -0,0 +1,52 @@ +import create from 'zustand' +import { devtools } from 'zustand/middleware' + +import { OpenSeaAsset } from '../types' + +interface SelectNFTState { + /** + * NFTs selected by a user + */ + selectedNFTs: (OpenSeaAsset & { price?: number })[] + + selectNFT: (nft: OpenSeaAsset & { price?: number }) => void + reset: () => void + setUniversalPrice: (price: number) => void + toggleUniversalPrice: (v: boolean) => void + setSingleNFTPrice: (id: number, price: number) => void + isUniversalPrice: boolean +} + +export const useNFTSelect = create()( + devtools( + (set) => ({ + selectedNFTs: [], + isUniversalPrice: false, + selectNFT: (nft) => + set(({ selectedNFTs }) => { + if (selectedNFTs.length === 0) return { selectedNFTs: [nft] } + else if (!!selectedNFTs.find((x) => x.id === nft.id)) + return { selectedNFTs: selectedNFTs.filter((n) => n.id !== nft.id) } + else return { selectedNFTs: [...selectedNFTs, nft] } + }), + reset: () => set(() => ({ selectedNFTs: [] })), + toggleUniversalPrice: (v) => set(() => ({ isUniversalPrice: v })), + setUniversalPrice: (price) => + set(({ selectedNFTs }) => { + return { + selectedNFTs: selectedNFTs.map((n) => ({ ...n, price })), + isUniversalPrice: true, + } + }), + setSingleNFTPrice: (id, price) => + set(({ selectedNFTs }) => { + const found = selectedNFTs.find((i) => i.id === id) + + return { + selectedNFTs: [...selectedNFTs.filter((n) => n.id !== id), { ...found, price }], + } + }), + }), + { name: 'useNFTSelect' } + ) +) diff --git a/src/nft/hooks/useSearchHistory.ts b/src/nft/hooks/useSearchHistory.ts new file mode 100644 index 0000000000..b01619003c --- /dev/null +++ b/src/nft/hooks/useSearchHistory.ts @@ -0,0 +1,24 @@ +import { FungibleToken, GenieCollection } from 'nft/types' +import create from 'zustand' +import { devtools, persist } from 'zustand/middleware' + +interface SearchHistoryProps { + history: (FungibleToken | GenieCollection)[] + addItem: (item: FungibleToken | GenieCollection) => void +} + +export const useSearchHistory = create()( + persist( + devtools((set) => ({ + history: [], + addItem: (item: FungibleToken | GenieCollection) => { + set(({ history }) => { + const historyCopy = [...history] + if (historyCopy.length === 0 || historyCopy[0].address !== item.address) historyCopy.unshift(item) + return { history: historyCopy } + }) + }, + })), + { name: 'useSearchHistory' } + ) +) diff --git a/src/nft/hooks/useSelectAsset.ts b/src/nft/hooks/useSelectAsset.ts new file mode 100644 index 0000000000..bcab3470a4 --- /dev/null +++ b/src/nft/hooks/useSelectAsset.ts @@ -0,0 +1,37 @@ +import { v4 as uuidv4 } from 'uuid' +import create from 'zustand' +import { devtools } from 'zustand/middleware' + +import { GenieAsset } from '../types' + +interface SelectAssetState { + selectedAssets: GenieAsset[] + selectAsset: (asset: GenieAsset) => void + removeAsset: (asset: GenieAsset) => void + reset: () => void +} + +export const useSelectAsset = create()( + devtools((set) => ({ + selectedAssets: [], + selectAsset: (asset) => + set(({ selectedAssets }) => { + const assetWithId = { id: uuidv4(), ...asset } + if (selectedAssets.length === 0) return { selectedAssets: [assetWithId] } + else return { selectedAssets: [...selectedAssets, assetWithId] } + }), + removeAsset: (asset) => { + set(({ selectedAssets }) => { + if (selectedAssets.length === 0) return { selectedAssets: [] } + else selectedAssets.find((x) => x.tokenId === asset.tokenId && x.address === asset.address) + const assetsCopy = [...selectedAssets] + assetsCopy.splice( + selectedAssets.findIndex((n) => n.tokenId === asset.tokenId && n.address === asset.address), + 1 + ) + return { selectedAssets: assetsCopy } + }) + }, + reset: () => set(() => ({ selectedAssets: [] })), + })) +) diff --git a/src/nft/hooks/useSellAsset.ts b/src/nft/hooks/useSellAsset.ts new file mode 100644 index 0000000000..b50773d842 --- /dev/null +++ b/src/nft/hooks/useSellAsset.ts @@ -0,0 +1,149 @@ +import { v4 as uuidv4 } from 'uuid' +import create from 'zustand' +import { devtools } from 'zustand/middleware' + +import { ListingMarket, ListingWarning, WalletAsset } from '../types' + +interface SellAssetState { + sellAssets: WalletAsset[] + selectSellAsset: (asset: WalletAsset) => void + removeSellAsset: (asset: WalletAsset) => void + reset: () => void + setGlobalExpiration: (expirationTime: number) => void + setAssetListPrice: (asset: WalletAsset, price: string, marketplace?: ListingMarket) => void + setGlobalMarketplaces: (marketplaces: ListingMarket[]) => void + removeAssetMarketplace: (asset: WalletAsset, marketplace: ListingMarket) => void + addMarketplaceWarning: (asset: WalletAsset, warning: ListingWarning) => void + removeMarketplaceWarning: (asset: WalletAsset, warning: ListingWarning, setGlobalOverride?: boolean) => void + removeAllMarketplaceWarnings: () => void +} + +export const useSellAsset = create()( + devtools( + (set) => ({ + sellAssets: [], + selectSellAsset: (asset) => + set(({ sellAssets }) => { + const assetWithId = { id: uuidv4(), ...asset } + if (sellAssets.length === 0) return { sellAssets: [assetWithId] } + else return { sellAssets: [...sellAssets, assetWithId] } + }), + removeSellAsset: (asset) => { + set(({ sellAssets }) => { + if (sellAssets.length === 0) return { sellAssets: [] } + else sellAssets.find((x) => x.id === asset.id) + const assetsCopy = [...sellAssets] + assetsCopy.splice( + sellAssets.findIndex((n) => n.id === asset.id), + 1 + ) + return { sellAssets: assetsCopy } + }) + }, + reset: () => set(() => ({ sellAssets: [] })), + setGlobalExpiration: (expirationTime) => { + set(({ sellAssets }) => { + const assetsCopy = [...sellAssets] + assetsCopy.map((asset) => { + asset.expirationTime = expirationTime + return asset + }) + return { sellAssets: assetsCopy } + }) + }, + setAssetListPrice: (asset, price, marketplace?) => { + set(({ sellAssets }) => { + const assetsCopy = [...sellAssets] + if (marketplace) { + const listingIndex = asset.newListings?.findIndex( + (listing) => listing.marketplace.name === marketplace.name + ) + if (asset.newListings && listingIndex != null && listingIndex > -1) { + asset.newListings[listingIndex] = { price, marketplace, overrideFloorPrice: false } + if (listingIndex === 0) asset.marketAgnosticPrice = price + } else asset.newListings?.push({ price, marketplace, overrideFloorPrice: false }) + } else asset.marketAgnosticPrice = price + const index = sellAssets.findIndex((n) => n.id === asset.id) + assetsCopy[index] = asset + return { sellAssets: assetsCopy } + }) + }, + setGlobalMarketplaces: (marketplaces) => { + set(({ sellAssets }) => { + const assetsCopy = [...sellAssets] + assetsCopy.map((asset) => { + asset.marketplaces = marketplaces + asset.newListings = [] + for (const marketplace of marketplaces) { + const listingIndex = asset.newListings.findIndex( + (listing) => listing.marketplace.name === marketplace.name + ) + const newListing = { + price: asset.marketAgnosticPrice, + marketplace, + overrideFloorPrice: false, + } + listingIndex > -1 ? (asset.newListings[listingIndex] = newListing) : asset.newListings.push(newListing) + } + return asset + }) + return { sellAssets: assetsCopy } + }) + }, + removeAssetMarketplace: (asset, marketplace) => { + set(({ sellAssets }) => { + const assetsCopy = [...sellAssets] + const assetIndex = sellAssets.indexOf(asset) + const marketplaceIndex = + asset.marketplaces?.findIndex((oldMarket) => oldMarket.name === marketplace.name) ?? -1 + const listingIndex = asset.newListings?.findIndex((listing) => listing.marketplace.name === marketplace.name) + const assetCopy = JSON.parse(JSON.stringify(asset)) + if (marketplaceIndex > -1) { + assetCopy.marketplaces.splice(marketplaceIndex, 1) + assetCopy.newListings.splice(listingIndex, 1) + } + assetsCopy.splice(assetIndex, 1, assetCopy) + return { sellAssets: assetsCopy } + }) + }, + addMarketplaceWarning: (asset, warning) => { + set(({ sellAssets }) => { + const assetsCopy = [...sellAssets] + asset.listingWarnings?.push(warning) + const index = sellAssets.findIndex((n) => n.id === asset.id) + assetsCopy[index] = asset + return { sellAssets: assetsCopy } + }) + }, + removeMarketplaceWarning: (asset, warning, setGlobalOverride?) => { + set(({ sellAssets }) => { + const assetsCopy = [...sellAssets] + if (asset.listingWarnings === undefined || asset.newListings === undefined) return { sellAssets: assetsCopy } + const warningIndex = + asset.listingWarnings?.findIndex((n) => n.marketplace.name === warning.marketplace.name) ?? -1 + asset.listingWarnings?.splice(warningIndex, 1) + if (warning?.message?.includes('LISTING BELOW FLOOR')) { + if (setGlobalOverride) { + asset.newListings?.forEach((listing) => (listing.overrideFloorPrice = true)) + } else { + const listingIndex = + asset.newListings?.findIndex((n) => n.marketplace.name === warning.marketplace.name) ?? -1 + asset.newListings[listingIndex].overrideFloorPrice = true + } + } + const index = sellAssets.findIndex((n) => n.id === asset.id) + assetsCopy[index] = asset + return { sellAssets: assetsCopy } + }) + }, + removeAllMarketplaceWarnings: () => { + set(({ sellAssets }) => { + const assetsCopy = [...sellAssets] + assetsCopy.map((asset) => (asset.listingWarnings = [])) + return { sellAssets: assetsCopy } + }) + }, + }), + { name: 'useSelectAsset' } + ) +) diff --git a/src/nft/hooks/useSellPageState.ts b/src/nft/hooks/useSellPageState.ts new file mode 100644 index 0000000000..f695499ed8 --- /dev/null +++ b/src/nft/hooks/useSellPageState.ts @@ -0,0 +1,25 @@ +import create from 'zustand' +import { devtools } from 'zustand/middleware' + +import { SellPageStateType } from '../types' + +interface sellPageState { + /** + * State of user settings + */ + state: SellPageStateType + setSellPageState: (state: SellPageStateType) => void +} + +export const useSellPageState = create()( + devtools( + (set) => ({ + state: SellPageStateType.SELECTING, + setSellPageState: (newState) => + set(() => ({ + state: newState, + })), + }), + { name: 'useSellPageState' } + ) +) diff --git a/src/nft/hooks/useSendTransaction.ts b/src/nft/hooks/useSendTransaction.ts new file mode 100644 index 0000000000..9479e91316 --- /dev/null +++ b/src/nft/hooks/useSendTransaction.ts @@ -0,0 +1,165 @@ +import { Interface } from '@ethersproject/abi' +import { BigNumber } from '@ethersproject/bignumber' +import { hexStripZeros } from '@ethersproject/bytes' +import { ContractReceipt } from '@ethersproject/contracts' +import { JsonRpcSigner } from '@ethersproject/providers' +import create from 'zustand' +import { devtools } from 'zustand/middleware' + +import ERC721 from '../../abis/erc721.json' +import ERC1155 from '../../abis/erc1155.json' +import CryptoPunksMarket from '../abis/CryptoPunksMarket.json' +import { GenieAsset, RouteResponse, RoutingItem, TxResponse, TxStateType, UpdatedGenieAsset } from '../types' +import { combineBuyItemsWithTxRoute } from '../utils/txRoute/combineItemsWithTxRoute' + +// Shortens a given txHash. With standard charsToShorten var of 4, a hash will become 0x1234...1234 +export const shortenTxHash = (txHash: string, charsToShorten = 4, addCharsToBack = 0): string => { + return `${txHash.substring(0, charsToShorten + 2)}...${txHash.substring( + txHash.length - charsToShorten, + txHash.length - (charsToShorten + addCharsToBack) + )}` +} + +interface TxState { + state: TxStateType + setState: (state: TxStateType) => void + txHash: string + clearTxHash: () => void + sendTransaction: ( + signer: JsonRpcSigner, + selectedAssets: UpdatedGenieAsset[], + transactionData: RouteResponse + ) => Promise +} + +export const useSendTransaction = create()( + devtools( + (set) => ({ + state: TxStateType.New, + txHash: '', + clearTxHash: () => set({ txHash: '' }), + setState: (newState) => set(() => ({ state: newState })), + sendTransaction: async (signer, selectedAssets, transactionData) => { + const address = await signer.getAddress() + try { + const txNoGasLimit = { + to: transactionData.to, + value: BigNumber.from(transactionData.valueToSend), + data: transactionData.data, + } + + const gasLimit = (await signer.estimateGas(txNoGasLimit)).mul(105).div(100) + // tx['gasLimit'] = gasLimit + const tx = { ...txNoGasLimit, gasLimit } // TODO test this works when firing off tx + + set({ state: TxStateType.Signing }) + const res = await signer.sendTransaction(tx) + set({ state: TxStateType.Confirming }) + set({ txHash: res.hash }) + + const txReceipt = await res.wait() + + //tx was mined successfully + if (txReceipt.status === 1) { + const nftsPurchased = findNFTsPurchased(txReceipt, address, selectedAssets, transactionData.route) + const nftsNotPurchased = findNFTsNotPurchased(selectedAssets, nftsPurchased) + set({ state: TxStateType.Success }) + return { + nftsPurchased, + nftsNotPurchased, + txReceipt, + } + } else { + set({ state: TxStateType.Failed }) + return { + nftsPurchased: [], + nftsNotPurchased: selectedAssets, + txReceipt, + } + } + } catch (e) { + console.log('Error creating multiAssetSwap Transaction', e) + if (e.code === 4001) { + set({ state: TxStateType.Denied }) + } else { + set({ state: TxStateType.Invalid }) + } + return + } + }, + }), + { name: 'useSendTransactionState' } + ) +) + +const findNFTsPurchased = ( + txReceipt: ContractReceipt, + signerAddress: string, + toBuy: GenieAsset[], + txRoute: RoutingItem[] +): UpdatedGenieAsset[] => { + if (!txReceipt.logs) { + return [] + } + const erc721Interface = new Interface(ERC721) + const erc1155Interface = new Interface(ERC1155) + const cryptopunksMarketInterface = new Interface(CryptoPunksMarket) + + // Find successfully purchased NFTs (and assign to state nftsPurchased) by parsing events + const transferErc721BuyEvents = txReceipt.logs.filter( + (x) => + x.topics[0] === erc721Interface.getEventTopic('Transfer') && + hexStripZeros(x.topics[2]).toLowerCase() === signerAddress.toLowerCase() + ) + + const transferredErc721 = transferErc721BuyEvents.map((x) => ({ + address: x.address, + tokenId: parseInt(x.topics[3]).toString(), + })) + const transferErc1155BuyEvents = txReceipt.logs.filter( + (x) => + x.topics[0] === erc1155Interface.getEventTopic('TransferSingle') && + hexStripZeros(x.topics[3]).toLowerCase() === signerAddress.toLowerCase() + ) + + const transferredErc1155 = transferErc1155BuyEvents.map((x) => ({ + address: x.address, + tokenId: erc1155Interface.parseLog(x).args[3].toString(), + })) + + // Find transferred CryptoPunks + const transferCryptopunkEvents = txReceipt.logs.filter( + (x) => + x.topics[0] === cryptopunksMarketInterface.getEventTopic('PunkTransfer') && + hexStripZeros(x.topics[2]).toLowerCase() === signerAddress.toLowerCase() + ) + const transferredCryptopunks = transferCryptopunkEvents.map((x) => ({ + address: x.address, + tokenId: cryptopunksMarketInterface.parseLog(x).args[2].toString(), + })) + + const allTransferred = [...transferredErc721, ...transferredErc1155, ...transferredCryptopunks] + + const transferredItems = toBuy.filter((assetToBuy) => { + return allTransferred.some( + (purchasedNft) => + assetToBuy.address.toLowerCase() === purchasedNft.address.toLowerCase() && + parseInt(assetToBuy.tokenId).toString() === purchasedNft.tokenId + ) + }) + + return combineBuyItemsWithTxRoute(transferredItems, txRoute) +} + +const findNFTsNotPurchased = (toBuy: GenieAsset[], nftsPurchased: UpdatedGenieAsset[]) => { + const nftsNotPurchased: Array = [] + toBuy.forEach((selectedAsset) => { + const purchasedNft = nftsPurchased.find( + (x) => x.address.toLowerCase() === selectedAsset.address.toLowerCase() && x.tokenId === selectedAsset.tokenId + ) + if (!purchasedNft) { + nftsNotPurchased.push(selectedAsset) + } + }) + return nftsNotPurchased +} diff --git a/src/nft/hooks/useSweep.ts b/src/nft/hooks/useSweep.ts new file mode 100644 index 0000000000..dd83286329 --- /dev/null +++ b/src/nft/hooks/useSweep.ts @@ -0,0 +1,37 @@ +import create from 'zustand' +import { devtools, persist } from 'zustand/middleware' + +import { GenieAsset } from '../types' + +interface SweepState { + sweepAssets: GenieAsset[] + setSweepAssets: (assets: GenieAsset[]) => void + removeSweepAsset: (asset: GenieAsset) => void + reset: () => void +} + +export const useSweep = create()( + persist( + devtools((set) => ({ + sweepAssets: [], + setSweepAssets: (assets) => + set(() => { + return { sweepAssets: assets } + }), + removeSweepAsset: (asset) => { + set(({ sweepAssets }) => { + if (sweepAssets.length === 0) return { sweepAssets: [] } + else sweepAssets.find((x) => x.tokenId === asset.tokenId && x.address === asset.address) + const assetsCopy = [...sweepAssets] + assetsCopy.splice( + sweepAssets.findIndex((n) => n.tokenId === asset.tokenId && n.address === asset.address), + 1 + ) + return { sweepAssets: assetsCopy } + }) + }, + reset: () => set(() => ({ sweepAssets: [] })), + })), + { name: 'useSweep' } + ) +) diff --git a/src/nft/hooks/useWalletBalance.ts b/src/nft/hooks/useWalletBalance.ts new file mode 100644 index 0000000000..f822793ec8 --- /dev/null +++ b/src/nft/hooks/useWalletBalance.ts @@ -0,0 +1,31 @@ +import { BigNumber } from '@ethersproject/bignumber' +import { Web3Provider } from '@ethersproject/providers' +import { parseEther } from '@ethersproject/units' +import { useWeb3React } from '@web3-react/core' +import { useNativeCurrencyBalances } from 'state/connection/hooks' + +interface WalletBalanceProps { + address: string + balance: string + weiBalance: BigNumber + provider: Web3Provider | undefined +} + +export function useWalletBalance(): WalletBalanceProps { + const { account: address, provider } = useWeb3React() + const balanceString = useNativeCurrencyBalances(address ? [address] : [])?.[address ?? '']?.toSignificant(3) || '0' + + return address == null + ? { + address: '', + balance: '0', + weiBalance: parseEther('0'), + provider: undefined, + } + : { + address, + balance: balanceString, + weiBalance: parseEther(balanceString), + provider, + } +} diff --git a/src/nft/hooks/useWalletCollections.ts b/src/nft/hooks/useWalletCollections.ts new file mode 100644 index 0000000000..bc5ae54be1 --- /dev/null +++ b/src/nft/hooks/useWalletCollections.ts @@ -0,0 +1,73 @@ +import create from 'zustand' +import { devtools } from 'zustand/middleware' + +import { WalletAsset, WalletCollection } from '../types' + +interface WalletCollectionState { + walletAssets: WalletAsset[] + walletCollections: WalletCollection[] + displayAssets: WalletAsset[] + collectionFilters: string[] + listFilter: string + setWalletAssets: (assets: WalletAsset[]) => void + setWalletCollections: (collections: WalletCollection[]) => void + setCollectionFilters: (address: string) => void + clearCollectionFilters: () => void + setListFilter: (value: string) => void + setDisplayAssets: (walletAssets: WalletAsset[], listFilter: string) => void +} + +export const useWalletCollections = create()( + devtools( + (set) => ({ + walletAssets: [], + walletCollections: [], + displayAssets: [], + collectionFilters: [], + listFilter: 'All', + setWalletAssets: (assets) => + set(() => { + return { + walletAssets: assets?.filter((asset) => asset.asset_contract?.schema_name === 'ERC721'), + } + }), + setWalletCollections: (collections) => + set(() => { + return { walletCollections: collections } + }), + setCollectionFilters: (address) => + set(({ collectionFilters }) => { + if (collectionFilters.length === 0) return { collectionFilters: [address] } + else if (!!collectionFilters.find((x) => x === address)) + return { collectionFilters: collectionFilters.filter((n) => n !== address) } + else return { collectionFilters: [...collectionFilters, address] } + }), + clearCollectionFilters: () => + set(() => { + return { collectionFilters: [] } + }), + setListFilter: (value) => + set(() => { + return { listFilter: value } + }), + setDisplayAssets: (walletAssets, listFilter) => + set(() => { + return { displayAssets: filterWalletAssets(walletAssets, listFilter) } + }), + }), + { name: 'useWalletCollections' } + ) +) + +const filterWalletAssets = (walletAssets: WalletAsset[], listFilter: string) => { + let displayAssets = walletAssets + if (listFilter === 'Listed') + displayAssets = displayAssets?.filter((x) => { + return x.listing_date !== null + }) + if (listFilter === 'Unlisted') + displayAssets = displayAssets?.filter((x) => { + return x.listing_date === null + }) + return displayAssets +} diff --git a/src/nft/queries/genie/ActivityFetcher.ts b/src/nft/queries/genie/ActivityFetcher.ts new file mode 100644 index 0000000000..7fc120aadf --- /dev/null +++ b/src/nft/queries/genie/ActivityFetcher.ts @@ -0,0 +1,24 @@ +import { ActivityEventResponse, ActivityFilter } from '../../types' + +export const ActivityFetcher = async ( + contractAddress: string, + filters?: ActivityFilter, + cursor?: string +): Promise => { + const filterParam = + filters && filters.eventTypes + ? `&${filters.eventTypes?.map((eventType) => `event_types[]=${eventType}`).join('&')}` + : '' + const url = `${ + process.env.REACT_APP_GENIE_V3_API_URL + }/collections/${contractAddress}/activity?limit=25${filterParam}${cursor ? `&cursor=${cursor}` : ''}` + + const r = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + const data = await r.json() + return data.data +} diff --git a/src/nft/queries/genie/AssetsFetcher.ts b/src/nft/queries/genie/AssetsFetcher.ts new file mode 100644 index 0000000000..f435029a6e --- /dev/null +++ b/src/nft/queries/genie/AssetsFetcher.ts @@ -0,0 +1,126 @@ +import { parseEther } from '@ethersproject/units' + +import { Trait } from '../../hooks/useCollectionFilters' +import { AssetPayload, CollectionSort, GenieAsset } from '../../types' + +export const formatTraits = (traits: Trait[]) => { + const traitObj: Record = {} + const nonMetaTraits = traits.filter((el) => el.trait_type !== 'Number of traits') + for (const trait of nonMetaTraits) { + if (!traitObj[trait.trait_type]) traitObj[trait.trait_type] = [trait.trait_value] + else traitObj[trait.trait_type].push(trait.trait_value) + } + + return traitObj +} + +const formatPrice = (x: number | string) => parseEther(x.toString()).toString() + +export const AssetsFetcher = async ({ + contractAddress, + tokenId, + sort, + markets, + price, + rarityRange, + traits, + searchText, + notForSale, + pageParam, +}: { + contractAddress: string + tokenId?: string + offset?: number + sort?: CollectionSort + markets?: string[] + price?: { high?: number | string; low?: number | string; symbol: string } + rarityRange?: Record + traits?: Trait[] + searchText?: string + notForSale?: boolean + pageParam: number +}): Promise => { + const url = `${process.env.REACT_APP_GENIE_API_URL}/assets` + const payload: AssetPayload = { + filters: { + address: contractAddress.toLowerCase(), + traits: {}, + searchText, + notForSale, + tokenId, + ...rarityRange, + }, + fields: { + address: 1, + name: 1, + id: 1, + imageUrl: 1, + currentPrice: 1, + currentUsdPrice: 1, + paymentToken: 1, + animationUrl: 1, + notForSale: 1, + rarity: 1, + tokenId: 1, + }, + limit: 25, + offset: pageParam * 25, + } + if (sort) { + payload.sort = sort + } + if (markets) { + payload.markets = markets + } + const numberOfTraits = traits?.filter((trait) => trait.trait_type === 'Number of traits') + if (numberOfTraits) { + payload.filters.numTraits = numberOfTraits.map((el) => ({ traitCount: el.trait_value })) + } + if (traits) { + payload.filters.traits = formatTraits(traits) + } + + const low = price?.low ? parseFloat(formatPrice(price.low)) : undefined + const high = price?.high ? parseFloat(formatPrice(price.high)) : undefined + + // Only consider sending eth price filters when searching + // across listed assets + if (!notForSale) { + if (low || high) { + payload.filters.currentEthPrice = {} + } + + if (low && payload.filters.currentEthPrice) { + payload.filters.currentEthPrice.$gte = low + } + + if (high && payload.filters.currentEthPrice) { + payload.filters.currentEthPrice.$lte = high + } + } + + try { + const r = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }) + const data = await r.json() + // Unfortunately have to include totalCount into each element. The fetcher + // for swr infinite must return an array. + for (const x of data.data) { + x.totalCount = data.totalCount + x.numTraitsByAmount = data.numTraitsByAmount + } + + // Uncomment the lines belo if you want to simulate a delay + // await (async () => await new Promise((resolve) => setTimeout(resolve, 50000)))(); + + return data.data + } catch (e) { + console.log(e) + return + } +} diff --git a/src/nft/queries/genie/CollectionPreviewFetcher.ts b/src/nft/queries/genie/CollectionPreviewFetcher.ts new file mode 100644 index 0000000000..e242961f97 --- /dev/null +++ b/src/nft/queries/genie/CollectionPreviewFetcher.ts @@ -0,0 +1,32 @@ +export const CollectionPreviewFetcher = async ( + address: string +): Promise< + [ + { + name: string + bannerImageUrl?: string + } + ] +> => { + const url = `${process.env.REACT_APP_GENIE_API_URL}/collectionPreview?address=${address}` + + const controller = new AbortController() + + const timeoutId = setTimeout(() => controller.abort(), 3000) + + const r = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + clearInterval(timeoutId) + const data = await r.json() + + return [ + { + name: data.data.collectionName, + bannerImageUrl: data.data.bannerImageUrl, + }, + ] +} diff --git a/src/nft/queries/genie/CollectionStatsFetcher.ts b/src/nft/queries/genie/CollectionStatsFetcher.ts new file mode 100644 index 0000000000..ba7484d95b --- /dev/null +++ b/src/nft/queries/genie/CollectionStatsFetcher.ts @@ -0,0 +1,59 @@ +import { isAddress } from '@ethersproject/address' + +import { GenieCollection } from '../../types' + +export const CollectionStatsFetcher = async (addressOrName: string, recursive = false): Promise => { + const isName = !isAddress(addressOrName.toLowerCase()) + const url = `${process.env.REACT_APP_GENIE_API_URL}/collections` + + if (!isName && !recursive) { + try { + return await CollectionStatsFetcher(addressOrName.toLowerCase(), true) + } catch { + // Handle Error + } + } + + const filters = isName + ? { + $or: [{ name: { $regex: addressOrName, $options: 'i' } }], + } + : { address: addressOrName } + + const payload = { + filters, + limit: isName ? 6 : 1, + fields: isName + ? { + name: 1, + imageUrl: 1, + address: 1, + stats: 1, + floorPrice: 1, + } + : { + traits: 1, + stats: 1, + 'indexingStats.openSea': 1, + imageUrl: 1, + bannerImageUrl: 1, + twitter: 1, + externalUrl: 1, + instagram: 1, + discordUrl: 1, + marketplaceCount: 1, + floorPrice: 1, + }, + offset: 0, + } + const r = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }) + + const data = await r.json() + return data?.data ? data.data[0] : {} +} diff --git a/src/nft/queries/genie/LooksRareRewardsFetcher.ts b/src/nft/queries/genie/LooksRareRewardsFetcher.ts new file mode 100644 index 0000000000..421f504d9d --- /dev/null +++ b/src/nft/queries/genie/LooksRareRewardsFetcher.ts @@ -0,0 +1,13 @@ +import { LooksRareRewardsData } from '../../types' + +const looksRareApiAddress = 'https://api.looksrare.org/api/v1' + +export const fetchLooksRareRewards = async (address: string): Promise => { + const res = await fetch(`${looksRareApiAddress}/rewards?address=${address}`) + + if (res.status !== 200) throw new Error(`LooksRare rewards API errored with status ${res.statusText}`) + + const json = await res.json() + + return json.data +} diff --git a/src/nft/queries/genie/MultipleCollectionStatsFetcher.ts b/src/nft/queries/genie/MultipleCollectionStatsFetcher.ts new file mode 100644 index 0000000000..78926f0d32 --- /dev/null +++ b/src/nft/queries/genie/MultipleCollectionStatsFetcher.ts @@ -0,0 +1,31 @@ +import { GenieCollection } from '../../types' + +export const fetchMultipleCollectionStats = async ({ + addresses, +}: { + addresses: string[] +}): Promise => { + const url = `${process.env.REACT_APP_GENIE_API_URL}/searchCollections` + const filters = { + address: { $in: addresses }, + } + const payload = { + filters, + fields: { + stats: 1, + imageUrl: 1, + address: 1, + name: 1, + }, + } + + const r = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }) + const data = await r.json() + return data.data +} diff --git a/src/nft/queries/genie/RouteFetcher.ts b/src/nft/queries/genie/RouteFetcher.ts new file mode 100644 index 0000000000..1a17aa0db7 --- /dev/null +++ b/src/nft/queries/genie/RouteFetcher.ts @@ -0,0 +1,69 @@ +import { GenieAsset, RouteResponse, TokenType } from '../../types' + +export const fetchRoute = async ({ + toSell, + toBuy, + senderAddress, +}: { + toSell: any + toBuy: any + senderAddress: string +}): Promise => { + const url = `${process.env.REACT_APP_GENIE_API_URL}/route` + const payload = { + sell: [...toSell].map((x) => buildRouteItem(x)), + buy: [...toBuy].filter((x) => x.tokenType !== 'Dust').map((x) => buildRouteItem(x)), + sender: senderAddress, + } + + const r = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }) + const data = await r.json() + + return data +} + +type ApiPriceInfo = { + basePrice: string + baseAsset: string + ETHPrice: string +} + +type RouteItem = { + id?: string + symbol?: string + name: string + decimals: number + address: string + priceInfo: ApiPriceInfo + tokenType: TokenType + tokenId: string + amount: number + marketplace?: string + collectionName?: string +} + +const buildRouteItem = (item: GenieAsset): RouteItem => { + return { + id: item.id, + symbol: item.priceInfo.baseAsset, + name: item.name, + decimals: item.decimals || 0, // 0 for fungible items + address: item.address, + tokenType: item.tokenType, + tokenId: item.tokenId, + marketplace: item.marketplace, + collectionName: item.collectionName, + amount: item.amount || 1, // default 1 for a single asset + priceInfo: { + basePrice: item.priceInfo.basePrice, + baseAsset: item.priceInfo.baseAsset, + ETHPrice: item.priceInfo.ETHPrice, + }, + } +} diff --git a/src/nft/queries/genie/SearchCollectionsFetcher.ts b/src/nft/queries/genie/SearchCollectionsFetcher.ts new file mode 100644 index 0000000000..45464dd584 --- /dev/null +++ b/src/nft/queries/genie/SearchCollectionsFetcher.ts @@ -0,0 +1,48 @@ +import { isAddress } from '@ethersproject/address' + +import { GenieCollection } from '../../types' + +export const fetchSearchCollections = async (addressOrName: string, recursive = false): Promise => { + const url = `${process.env.REACT_APP_GENIE_V3_API_URL}/searchCollections` + const isName = !isAddress(addressOrName.toLowerCase()) + + if (!isName && !recursive) { + try { + return await fetchSearchCollections(addressOrName.toLowerCase(), true) + } catch { + return [] + } + } + + const filters = isName + ? { + $or: [{ name: { $regex: addressOrName, $options: 'i' } }], + } + : { address: addressOrName } + + const payload = { + filters, + limit: 6, + fields: { + name: 1, + imageUrl: 1, + address: 1, + floorPrice: 1, + }, + offset: 0, + } + const r = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }) + if (isName) { + const data = (await r.json()) as { data: GenieCollection[] } + return data?.data ? data.data.slice(0, 6) : [] + } + const data = await r.json() + + return data.data ? [data.data[0]] : [] +} diff --git a/src/nft/queries/genie/SearchTokensFetcher.ts b/src/nft/queries/genie/SearchTokensFetcher.ts new file mode 100644 index 0000000000..c897f62822 --- /dev/null +++ b/src/nft/queries/genie/SearchTokensFetcher.ts @@ -0,0 +1,20 @@ +import { FungibleToken } from '../../types' + +export const fetchSearchTokens = async (tokenQuery: string): Promise => { + const url = `${process.env.REACT_APP_GENIE_V3_API_URL}/searchTokens?tokenQuery=${tokenQuery}` + + const r = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + + const data = await r.json() + + // TODO Undo favoritism + return ( + data.data && + data.data.sort((a: FungibleToken, b: FungibleToken) => (b.name === 'Uniswap' ? 1 : b.volume24h - a.volume24h)) + ) +} diff --git a/src/nft/queries/genie/SingleAssetFetcher.ts b/src/nft/queries/genie/SingleAssetFetcher.ts new file mode 100644 index 0000000000..8835b932d4 --- /dev/null +++ b/src/nft/queries/genie/SingleAssetFetcher.ts @@ -0,0 +1,14 @@ +import { CollectionInfoForAsset, GenieAsset } from '../../types' + +export const fetchSingleAsset = async ({ + contractAddress, + tokenId, +}: { + contractAddress: string + tokenId?: string +}): Promise<[GenieAsset, CollectionInfoForAsset]> => { + const url = `${process.env.REACT_APP_GENIE_API_URL}/assetDetails?address=${contractAddress}&tokenId=${tokenId}` + const r = await fetch(url) + const data = await r.json() + return [data.asset[0], data.collection] +} diff --git a/src/nft/queries/genie/SweepFetcher.ts b/src/nft/queries/genie/SweepFetcher.ts new file mode 100644 index 0000000000..3e007efece --- /dev/null +++ b/src/nft/queries/genie/SweepFetcher.ts @@ -0,0 +1,55 @@ +import { Trait } from '../../hooks/useCollectionFilters' +import { AssetPayload, GenieAsset } from '../../types' +import { formatTraits } from './AssetsFetcher' + +export const fetchSweep = async ({ + contractAddress, + markets, + traits = [], +}: { + contractAddress: string + markets?: string[] + traits?: Trait[] +}): Promise => { + const url = `${process.env.REACT_APP_GENIE_API_URL}/assets` + const payload: AssetPayload = { + filters: { address: contractAddress.toLowerCase(), traits: {}, notForSale: false }, + fields: { + address: 1, + name: 1, + id: 1, + imageUrl: 1, + currentPrice: 1, + currentUsdPrice: 1, + paymentToken: 1, + animationUrl: 1, + notForSale: 1, + }, + limit: 99, + offset: 0, + } + + if (markets) { + payload.markets = markets + } + + if (traits) { + payload.filters.traits = formatTraits(traits) + } + + const numberOfTraits = traits.filter((trait) => trait.trait_type === 'Number of traits') + if (numberOfTraits) { + payload.filters.numTraits = numberOfTraits.map((el) => ({ traitCount: el.trait_value })) + } + + const r = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }) + + const data = await r.json() + return data.data +} diff --git a/src/nft/queries/genie/TransactionsFetcher.ts b/src/nft/queries/genie/TransactionsFetcher.ts new file mode 100644 index 0000000000..ab1d4ff64d --- /dev/null +++ b/src/nft/queries/genie/TransactionsFetcher.ts @@ -0,0 +1,19 @@ +import { TransactionsResponse } from '../../types' + +export const fetchTransactions = async (payload: { sweep?: boolean }): Promise => { + const url = `${process.env.REACT_APP_GENIE_API_URL}/transactions` + + const r = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }) + + const data = (await r.json()) as TransactionsResponse[] + + return data.filter( + (x) => x.bannerImage && (payload.sweep ? x.nftCount >= 3 && Math.floor(x.ethValue / 10 ** 18) >= 1 : true) + ) +} diff --git a/src/nft/queries/genie/TrendingCollectionsFetcher.ts b/src/nft/queries/genie/TrendingCollectionsFetcher.ts new file mode 100644 index 0000000000..0c2ea3bf13 --- /dev/null +++ b/src/nft/queries/genie/TrendingCollectionsFetcher.ts @@ -0,0 +1,20 @@ +import { TimePeriod, TrendingCollection } from '../../types' + +export const fetchTrendingCollections = async (payload: { + volumeType: 'eth' | 'nft' + timePeriod: TimePeriod + size: number +}): Promise => { + const url = `${process.env.REACT_APP_GENIE_V3_API_URL}/collections/trending` + const r = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }) + + const data = await r.json() + + return data +} diff --git a/src/nft/queries/genie/TrendingTokensFetcher.ts b/src/nft/queries/genie/TrendingTokensFetcher.ts new file mode 100644 index 0000000000..adac6edaff --- /dev/null +++ b/src/nft/queries/genie/TrendingTokensFetcher.ts @@ -0,0 +1,16 @@ +import { FungibleToken } from '../../types' + +export const fetchTrendingTokens = async (numTokens?: number): Promise => { + const url = `${process.env.REACT_APP_GENIE_V3_API_URL}/tokens/trending${numTokens ? `?numTokens=${numTokens}` : ''}` + + const r = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + + const data = await r.json() + + return data.data +} diff --git a/src/nft/queries/genie/WalletAssetsFetcher.ts b/src/nft/queries/genie/WalletAssetsFetcher.ts new file mode 100644 index 0000000000..1642e6b50f --- /dev/null +++ b/src/nft/queries/genie/WalletAssetsFetcher.ts @@ -0,0 +1,64 @@ +import { BigNumber } from '@ethersproject/bignumber' +import { formatEther } from '@ethersproject/units' + +import { WalletAsset } from '../../types' + +const getEthPrice = (price: any) => { + if (price.toString().includes('e')) { + return BigNumber.from(10).pow(price.toString().split('e+')[1]).toString() + } + + return Math.round(price).toString() +} + +export const fetchWalletAssets = async ({ + ownerAddress, + collectionAddresses, + pageParam, +}: { + ownerAddress: string + collectionAddresses?: string[] + pageParam: number +}): Promise => { + const collectionAddressesString = collectionAddresses + ? collectionAddresses.reduce((str, collectionAddress) => str + `&assetContractAddresses=${collectionAddress}`, '') + : '' + const url = `${ + process.env.REACT_APP_GENIE_API_URL + }/walletAssets?address=${ownerAddress}${collectionAddressesString}&limit=25&offset=${pageParam * 25}` + + const r = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + const data = await r.json() + return data.data.assets.map((asset: any) => { + return { + ...asset, + collectionIsVerified: asset.asset_contract.isVerified, + lastPrice: asset.last_sale && formatEther(asset.last_sale.total_price), + floorPrice: asset.collection?.floorPrice, + creatorPercentage: parseFloat(asset.asset_contract.dev_seller_fee_basis_points) / 10000, + date_acquired: asset.last_sale ? asset.last_sale.event_timestamp : asset.asset_contract.created_date, + listing_date: asset.sellOrders.length + ? Math.max + .apply( + null, + asset.sellOrders.map(function (order: any) { + return new Date(order.orderCreatedDate) + }) + ) + .toString() + : null, + floor_sell_order_price: asset?.sellOrders?.length + ? Math.min( + ...asset.sellOrders.map((order: any) => { + return parseFloat(formatEther(getEthPrice(order.ethPrice))) + }) + ) + : null, + } + }) +} diff --git a/src/nft/queries/genie/index.ts b/src/nft/queries/genie/index.ts new file mode 100644 index 0000000000..303229a2a8 --- /dev/null +++ b/src/nft/queries/genie/index.ts @@ -0,0 +1,14 @@ +export * from './AssetsFetcher' +export * from './CollectionPreviewFetcher' +export * from './CollectionStatsFetcher' +export * from './logListing' +export * from './LooksRareRewardsFetcher' +export * from './MultipleCollectionStatsFetcher' +export * from './RouteFetcher' +export * from './SearchCollectionsFetcher' +export * from './SingleAssetFetcher' +export * from './SweepFetcher' +export * from './TransactionsFetcher' +export * from './TrendingCollectionsFetcher' +export * from './triggerPriceUpdatesForCollection' +export * from './WalletAssetsFetcher' diff --git a/src/nft/queries/genie/logListing.ts b/src/nft/queries/genie/logListing.ts new file mode 100644 index 0000000000..3031016032 --- /dev/null +++ b/src/nft/queries/genie/logListing.ts @@ -0,0 +1,36 @@ +import { AssetRow, ListingMarket } from '../../types' + +interface Listing extends AssetRow { + marketplaces: ListingMarket[] +} + +export const logListing = async (listings: AssetRow[], userAddress: string): Promise => { + const url = `${process.env.REACT_APP_GENIE_API_URL}/logGenieList` + const listingsConsolidated: Listing[] = listings.map((el) => ({ ...el, marketplaces: [] })) + const marketplacesById: Record = {} + const listingsWithMarketsConsolidated = listingsConsolidated.reduce((uniqueListings, curr) => { + const key = `${curr.asset.asset_contract.address}-${curr.asset.tokenId}` + if (marketplacesById[key]) { + marketplacesById[key].push(curr.marketplace) + } else { + marketplacesById[key] = [curr.marketplace] + } + if (!uniqueListings.some((listing) => `${listing.asset.asset_contract.address}-${listing.asset.tokenId}` === key)) { + curr.marketplaces = marketplacesById[key] + uniqueListings.push(curr) + } + return uniqueListings + }, [] as Listing[]) + const payload = { + listings: listingsWithMarketsConsolidated, + userAddress, + } + const r = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }) + return r.status === 200 +} diff --git a/src/nft/queries/genie/triggerPriceUpdatesForCollection.ts b/src/nft/queries/genie/triggerPriceUpdatesForCollection.ts new file mode 100644 index 0000000000..e52a0910a8 --- /dev/null +++ b/src/nft/queries/genie/triggerPriceUpdatesForCollection.ts @@ -0,0 +1,13 @@ +export const triggerPriceUpdatesForCollection = async (address: string) => { + const url = `${process.env.REACT_APP_GENIE_API_URL}/collections/refresh` + const r = await fetch(url, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + address, + }), + }) + return r.json() +} diff --git a/src/nft/queries/index.ts b/src/nft/queries/index.ts new file mode 100644 index 0000000000..0d6a08e506 --- /dev/null +++ b/src/nft/queries/index.ts @@ -0,0 +1,4 @@ +export * from './genie' +export * from './looksRare' +export * from './openSea' +export * from './x2y2' diff --git a/src/nft/queries/looksRare/createLooksRareOrder.ts b/src/nft/queries/looksRare/createLooksRareOrder.ts new file mode 100644 index 0000000000..0e464820f0 --- /dev/null +++ b/src/nft/queries/looksRare/createLooksRareOrder.ts @@ -0,0 +1,16 @@ +export const createLooksRareOrder = async (payload: any): Promise => { + const url = `${process.env.REACT_APP_GENIE_API_URL}/createLooksRareOrder` + const res = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }) + try { + const data = await res.json() + return data.code === 200 + } catch (e) { + return false + } +} diff --git a/src/nft/queries/looksRare/index.ts b/src/nft/queries/looksRare/index.ts new file mode 100644 index 0000000000..9938fa98f9 --- /dev/null +++ b/src/nft/queries/looksRare/index.ts @@ -0,0 +1,3 @@ +export * from './createLooksRareOrder' +export * from './looksRareNonceFetcher' +export * from './looksRareRewardsFetcher' diff --git a/src/nft/queries/looksRare/looksRareNonceFetcher.ts b/src/nft/queries/looksRare/looksRareNonceFetcher.ts new file mode 100644 index 0000000000..75866b3fdb --- /dev/null +++ b/src/nft/queries/looksRare/looksRareNonceFetcher.ts @@ -0,0 +1,14 @@ +const looksRareApiAddress = 'https://api.looksrare.org/api/v1' + +export const looksRareNonceFetcher = async (address: any): Promise => { + const res = await fetch(`${looksRareApiAddress}/orders/nonce?address=${address}`) + + if (res.status !== 200) { + console.log(`LooksRare nonce API errored with status ${res.statusText}`) + return + } + + const json = await res.json() + + return parseFloat(json.data) +} diff --git a/src/nft/queries/looksRare/looksRareRewardsFetcher.ts b/src/nft/queries/looksRare/looksRareRewardsFetcher.ts new file mode 100644 index 0000000000..9acffc0086 --- /dev/null +++ b/src/nft/queries/looksRare/looksRareRewardsFetcher.ts @@ -0,0 +1,13 @@ +import { LooksRareRewardsData } from '../../types' + +const looksRareApiAddress = 'https://api.looksrare.org/api/v1' + +export const looksRareRewardsFetcher = async (address: any): Promise => { + const res = await fetch(`${looksRareApiAddress}/rewards?address=${address}`) + + if (res.status !== 200) throw new Error(`LooksRare rewards API errored with status ${res.statusText}`) + + const json = await res.json() + + return json.data +} diff --git a/src/nft/queries/openSea/OSCollectionsFetcher.ts b/src/nft/queries/openSea/OSCollectionsFetcher.ts new file mode 100644 index 0000000000..224b4de8b5 --- /dev/null +++ b/src/nft/queries/openSea/OSCollectionsFetcher.ts @@ -0,0 +1,30 @@ +import { WalletCollection } from '../../types' + +export const OSCollectionsFetcher = async ({ params }: any): Promise => { + let hasEmptyFields = false + + for (const v of Object.values(params)) { + if (v === undefined) { + hasEmptyFields = true + } + } + if (hasEmptyFields) return [] + + const r = await fetch(`https://api.opensea.io/api/v1/collections?${new URLSearchParams(params).toString()}`) + const walletCollections = await r.json() + if (walletCollections) { + return walletCollections + .filter( + (collection: any) => + collection.primary_asset_contracts.length && collection.primary_asset_contracts[0].schema_name === 'ERC721' + ) + .map((collection: any) => ({ + address: collection.primary_asset_contracts[0].address, + name: collection.name, + image: collection.image_url, + count: collection.owned_asset_count, + })) + } else { + return [] + } +} diff --git a/src/nft/queries/openSea/PostOpenSeaSellOrder.ts b/src/nft/queries/openSea/PostOpenSeaSellOrder.ts new file mode 100644 index 0000000000..bed3520d49 --- /dev/null +++ b/src/nft/queries/openSea/PostOpenSeaSellOrder.ts @@ -0,0 +1,76 @@ +import { OPENSEA_BASE_API_PATH } from 'nft/queries/openSea' + +export async function PostOpenSeaSellOrder( + apiPath: string, + body?: Record, + opts: RequestInit = {} +): Promise { + const fetchOpts = { + method: 'POST', + body: body ? JSON.stringify(body) : undefined, + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + ...opts, + } + + const response = await _fetch(apiPath, fetchOpts) + return response.json() +} + +async function _fetch(apiPath: string, opts: RequestInit = {}) { + const apiBase = OPENSEA_BASE_API_PATH + const finalUrl = apiBase + apiPath + const finalOpts = { + ...opts, + headers: { + ...(opts.headers || {}), + }, + } + + return fetch(finalUrl, finalOpts).then(async (res) => _handleApiResponse(res)) +} + +async function _handleApiResponse(response: Response) { + if (response.ok) { + return response + } + + let result + let errorMessage + try { + result = await response.text() + result = JSON.parse(result) + } catch { + // Result will be undefined or text + } + + switch (response.status) { + case 400: + errorMessage = result && result.errors ? result.errors.join(', ') : `Invalid request: ${JSON.stringify(result)}` + break + case 401: + case 403: + errorMessage = `Unauthorized. Full message was '${JSON.stringify(result)}'` + break + case 404: + errorMessage = `Not found. Full message was '${JSON.stringify(result)}'` + break + case 500: + errorMessage = `Internal server error. OpenSea has been alerted, but if the problem persists please contact us via Discord: https://discord.gg/ga8EJbv - full message was ${JSON.stringify( + result + )}` + break + case 503: + errorMessage = `Service unavailable. Please try again in a few minutes. If the problem persists please contact us via Discord: https://discord.gg/ga8EJbv - full message was ${JSON.stringify( + result + )}` + break + default: + errorMessage = `Message: ${JSON.stringify(result)}` + break + } + + throw new Error(`API Error ${response.status}: ${errorMessage}`) +} diff --git a/src/nft/queries/openSea/constants.ts b/src/nft/queries/openSea/constants.ts new file mode 100644 index 0000000000..270d0e89b3 --- /dev/null +++ b/src/nft/queries/openSea/constants.ts @@ -0,0 +1,10 @@ +export const OPENSEA_BASE_API_PATH = 'https://api.opensea.io' +export const OPENSEA_FEE_ADDRESS = '0x8de9c5a032463c561423387a9648c5c7bcc5bc90' +export const OPENSEA_DEFAULT_ZONE = '0x004c00500000ad104d7dbd00e3ae0a5c00560c00' +export const OPENSEA_LISTINGS_API_PATH = '/v2/orders/ethereum/seaport/listings' +export const OPENSEA_DEFAULT_CROSS_CHAIN_CONDUIT_KEY = + '0x0000007b02230091a7ed01230072f7006a004d60a8d4e71d599b8104250f0000' +export const OPENSEA_CROSS_CHAIN_CONDUIT = '0x1e0049783f008a0085193e00003d00cd54003c71' +export const OPENSEA_KEY_TO_CONDUIT = { [OPENSEA_DEFAULT_CROSS_CHAIN_CONDUIT_KEY]: OPENSEA_CROSS_CHAIN_CONDUIT } +export const OPENSEA_DEFAULT_FEE = 0.025 +export const INVERSE_BASIS_POINTS = 10000 diff --git a/src/nft/queries/openSea/index.ts b/src/nft/queries/openSea/index.ts new file mode 100644 index 0000000000..74c1babd0d --- /dev/null +++ b/src/nft/queries/openSea/index.ts @@ -0,0 +1,3 @@ +export * from './constants' +export * from './OSCollectionsFetcher' +export * from './PostOpenSeaSellOrder' diff --git a/src/nft/queries/x2y2/index.ts b/src/nft/queries/x2y2/index.ts new file mode 100644 index 0000000000..faf1f252e4 --- /dev/null +++ b/src/nft/queries/x2y2/index.ts @@ -0,0 +1,25 @@ +import { OrderPayload } from '../../utils/x2y2' + +export const newX2Y2Order = async (payload: OrderPayload): Promise => { + const body = JSON.stringify(payload) + const url = `${process.env.REACT_APP_GENIE_API_URL}/postX2Y2SellOrderWithApiKey` + const ac = new AbortController() + const req = new Request(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json; charset=utf-8', + }, + body, + signal: ac.signal, + }) + const timeout = setTimeout(() => ac.abort(), 60 * 1000) + try { + const res = await fetch(req) + const data = await res.json() + return data.code === 200 + } catch (e) { + return false + } finally { + clearTimeout(timeout) + } +} diff --git a/src/nft/themes/darkTheme.ts b/src/nft/themes/darkTheme.ts new file mode 100644 index 0000000000..69c9bcd661 --- /dev/null +++ b/src/nft/themes/darkTheme.ts @@ -0,0 +1,39 @@ +import { Theme, vars } from '../css/sprinkles.css' + +export const darkTheme: Theme = { + colors: { + error: '#FF494A', + textDisconnect: '#FF494A', + modalBackdrop: 'linear-gradient(0deg, rgba(0, 0, 0, 0.7), rgba(0, 0, 0, 0.7))', + backgroundSecondary: '#23262F', + modalClose: 'rgba(255, 255, 255, 0.08)', + text: '#fff', + modalTextSecondary: 'rgba(255, 255, 255, 0.6)', + + // Bryan's colors from Figma that vary dark vs light + blackBlue: '#FFFFFF', + blackBlue20: '#FFFFFF20', + darkGray: vars.color.grey300, + medGray: `#99A1BD3D`, + lightGray: vars.color.grey900, + white: '#000000', + darkGray10: `#99A1BD1A`, + explicitWhite: '#FFFFFF', + magicGradient: vars.color.blue400, + placeholder: vars.color.grey400, + lightGrayButton: vars.color.grey700, + lightGrayContainer: `#99A1BD14`, + lightGrayOverlay: '#35373F', + + // Opacities of black and white + white95: '#0E111AF2', + white90: '#000000E5', + white80: '#000000CC', + }, + shadows: { + menu: '0px 10px 30px rgba(0, 0, 0, 0.1)', + genieBlue: '0 4px 16px 0 rgba(70, 115, 250, 0.4)', + elevation: '0px 4px 16px rgba(70, 115, 250, 0.4)', + tooltip: '0px 4px 16px rgba(255, 255, 255, 0.2)', + }, +} diff --git a/src/nft/themes/lightTheme.ts b/src/nft/themes/lightTheme.ts new file mode 100644 index 0000000000..0515a2ad41 --- /dev/null +++ b/src/nft/themes/lightTheme.ts @@ -0,0 +1,39 @@ +import { Theme, vars } from '../css/sprinkles.css' + +export const lightTheme: Theme = { + colors: { + error: '#FF494A', + textDisconnect: '#FF494A', + modalBackdrop: 'rgba(0, 0, 0, 0.3)', + backgroundSecondary: '#FCFCFD', + modalClose: 'rgba(60, 66, 82, 0.06)', + text: '#25292E', + modalTextSecondary: 'rgba(60, 66, 82, 0.6)', + + // Bryan's colors from Figma that vary dark vs light + blackBlue: vars.color.grey900, + blackBlue20: `#0E111A33`, + darkGray: vars.color.grey500, + medGray: `#5E68873D`, + lightGray: vars.color.grey50, + white: '#FFFFFF', + darkGray10: `#5E68871A`, + explicitWhite: '#FFFFFF', + magicGradient: vars.color.pink400, + placeholder: vars.color.grey300, + lightGrayButton: vars.color.grey100, + lightGrayContainer: vars.color.grey100, + lightGrayOverlay: '#E6E8F0', + + // Opacities of black and white + white95: '#EDEFF7F2', + white90: '#FFFFFFE5', + white80: '#FFFFFFCC', + }, + shadows: { + menu: '0px 10px 30px rgba(0, 0, 0, 0.1)', + genieBlue: '0 4px 16px 0 rgba(251, 17, 142)', + elevation: '0px 4px 16px rgba(70, 115, 250, 0.4)', + tooltip: '0px 4px 16px rgba(10, 10, 59, 0.2)', + }, +} diff --git a/src/nft/types/checkout/checkout.ts b/src/nft/types/checkout/checkout.ts new file mode 100644 index 0000000000..b1b2e09104 --- /dev/null +++ b/src/nft/types/checkout/checkout.ts @@ -0,0 +1,75 @@ +import { ContractReceipt } from '@ethersproject/contracts' + +import { GenieAsset, Markets, PriceInfo, TokenType } from '../common' + +export interface UpdatedGenieAsset extends GenieAsset { + updatedPriceInfo?: PriceInfo + isUnavailable?: boolean + orderSource?: 'api' | 'stored' | string +} + +export enum RoutingActions { + Buy = 'Buy', + Sell = 'Sell', + Swap = 'Swap', +} + +export type SellItem = { + id?: string + symbol?: string + name: string + decimals: number + address: string + priceInfo: PriceInfo + tokenType: TokenType + tokenId: string + amount: string // convert to BigNumber + marketplace?: Markets +} + +export type BuyItem = { + id?: string + symbol?: string + name: string + decimals: number + address: string + priceInfo: PriceInfo + tokenType: TokenType + tokenId: string + amount: string // convert to BigNumber + marketplace: Markets + collectionName?: string + orderSource?: 'api' | 'stored' | string +} + +export type RoutingItem = { + action: RoutingActions + marketplace: string + amountIn: string + assetIn: SellItem | PriceInfo + amountOut: string + assetOut: BuyItem | PriceInfo +} + +export interface RouteResponse { + valueToSend: string + route: RoutingItem[] + data: any + to: any +} + +export interface TxResponse { + nftsPurchased: UpdatedGenieAsset[] + nftsNotPurchased: UpdatedGenieAsset[] + txReceipt: ContractReceipt +} + +export enum TxStateType { + Success = 'Success', + Denied = 'Denied', + Invalid = 'Invalid', + Failed = 'Failed', + New = 'New', + Signing = 'Signing', + Confirming = 'Confirming', +} diff --git a/src/nft/types/checkout/index.ts b/src/nft/types/checkout/index.ts new file mode 100644 index 0000000000..1b77a49458 --- /dev/null +++ b/src/nft/types/checkout/index.ts @@ -0,0 +1 @@ +export * from './checkout' diff --git a/src/nft/types/collection/collection.ts b/src/nft/types/collection/collection.ts new file mode 100644 index 0000000000..43f9eb5bb9 --- /dev/null +++ b/src/nft/types/collection/collection.ts @@ -0,0 +1,101 @@ +import { Markets, TokenType } from '../common' +export interface AssetPayload { + filters: { + traits?: Record + address: string + currentEthPrice?: { + $gte?: number + $lte?: number + } + numTraits?: { traitCount: string }[] + name?: string + searchText?: string + notForSale?: boolean + tokenId?: string + } + fields?: Record + limit: number + offset?: number + sort?: CollectionSort + markets?: string[] +} + +export interface CollectionInfoForAsset { + collectionSymbol: string + collectionDescription: string | null + collectionImageUrl: string + collectionName: string + isVerified: boolean + totalSupply: number +} + +export type CollectionSort = Record< + string, + 'asc' | 'desc' | 1 | -1 | { $gte?: string | number; $lte?: string | number } | string | number +> + +export enum UniformHeight { + unset, + notUniform, +} + +export enum ActivityEventType { + Listing = 'LISTING', + Sale = 'SALE', + CancelListing = 'CANCEL_LISTING', + Transfer = 'TRANSFER', +} + +export enum OrderStatus { + VALID = 'VALID', + EXECUTED = 'EXECUTED', + CANCELLED = 'CANCELLED', + EXPIRED = 'EXPIRED', +} + +export interface ActivityFilter { + collectionAddress?: string + eventTypes?: ActivityEventType[] + marketplaces?: Markets[] +} + +export interface ActivityEventResponse { + events: ActivityEvent[] + cursor?: string +} + +export interface TokenRarity { + rank: number + score: number + source: string +} + +export interface TokenMetadata { + name: string + imageUrl: string + smallImageUrl: string + metadataUrl: string + rarity: TokenRarity + suspiciousFlag: boolean + suspiciousFlaggedBy: string + standard: TokenType +} + +export interface ActivityEvent { + collectionAddress: string + tokenId?: string + tokenMetadata?: TokenMetadata + eventType: ActivityEventType + marketplace?: Markets + fromAddress: string + toAddress?: string + transactionHash?: string + orderHash?: string + orderStatus?: OrderStatus + price?: string + symbol?: string + quantity?: number + auctionType?: string + url?: string + eventTimestamp?: number +} diff --git a/src/nft/types/collection/index.ts b/src/nft/types/collection/index.ts new file mode 100644 index 0000000000..b9959d9f39 --- /dev/null +++ b/src/nft/types/collection/index.ts @@ -0,0 +1 @@ +export * from './collection' diff --git a/src/nft/types/common/common.ts b/src/nft/types/common/common.ts new file mode 100644 index 0000000000..9f2f28d2c5 --- /dev/null +++ b/src/nft/types/common/common.ts @@ -0,0 +1,183 @@ +import { SellOrder } from '../sell' + +export interface OpenSeaCollection { + name: string + slug: string + image_url: string + description: string + external_url: string + featured: boolean + hidden: boolean + safelist_request_status: string + is_subject_to_whitelist: boolean + large_image_url: string + only_proxied_transfers: boolean + payout_address: string +} + +export interface OpenSeaAsset { + id?: number + image_url?: string + image_preview_url?: string + name?: string + token_id?: string + last_sale?: { + total_price: string + } + asset_contract?: { + address: string + schema_name: 'ERC1155' | 'ERC721' | string + asset_contract_type: string + created_date: string + name: string + symbol: string + description: string + external_link: string + image_url: string + default_to_fiat: boolean + only_proxied_transfers: boolean + payout_address: string + } + collection?: OpenSeaCollection +} + +interface OpenSeaUser { + user?: null + profile_img_url: string + address: string + config: string +} + +export enum TokenType { + ERC20 = 'ERC20', + ERC721 = 'ERC721', + ERC1155 = 'ERC1155', + Dust = 'Dust', + Cryptopunk = 'Cryptopunk', +} + +export interface PriceInfo { + ETHPrice: string + USDPrice: string + baseAsset: string + baseDecimals: string + basePrice: string +} + +export interface AssetSellOrder { + ammFeePercent: number + ethReserves: number + tokenReserves: number +} + +export interface Rarity { + primaryProvider: string + providers: { provider: string; rank: number; url: string; score: number }[] +} + +export interface GenieAsset { + id?: string // This would be a random id created and assigned by front end + address: string + notForSale: boolean + collectionName: string + collectionSymbol: string + currentEthPrice: string + currentUsdPrice: string + imageUrl: string + animationUrl: string + marketplace: string + name: string + priceInfo: PriceInfo + openseaSusFlag: boolean + sellorders: SellOrder[] + smallImageUrl: string + tokenId: string + tokenType: TokenType + url: string + totalCount?: number // The totalCount from the query to /assets + amount?: number + decimals?: number + collectionIsVerified?: boolean + rarity?: Rarity + owner: OpenSeaUser + creator: OpenSeaUser + externalLink: string + traits?: { + trait_type: string + value: string + display_type?: any + max_value?: any + trait_count: number + order?: any + }[] +} + +export interface GenieCollection { + collectionAddress: string + address: string + indexingStatus: string + isVerified: boolean + name: string + description: string + standard: string + bannerImageUrl?: string + floorPrice: number + stats: { + num_owners: number + floor_price: number + one_day_volume: number + one_day_change: number + one_day_floor_change: number + banner_image_url: string + total_supply: number + total_listings: number + total_volume: number + } + symbol: string + traits: { + trait_type: string + trait_value: string + trait_count: number + floorSellOrder: PriceInfo + floorPrice: number + }[] + numTraitsByAmount: { traitCount: number; numWithTrait: number }[] + indexingStats: { openSea: { successfulExecutionDate: string; lastRequestedAt: string } } + marketplaceCount?: { marketplace: string; count: number }[] + imageUrl: string + twitter?: string + instagram?: string + discordUrl?: string + externalUrl?: string + rarityVerified?: boolean + isFoundation?: boolean +} + +export enum Markets { + NFT20 = 'nft20', + NFTX = 'nftx', + Opensea = 'opensea', + Rarible = 'rarible', + Uniswap = 'Uniswap', + Uniswap_V2 = 'Uniswap_V2', + SushiSwap = 'SushiSwap', + SuperRare = 'superrare', + KnownOrigin = 'knownorigin', + WETH = 'weth', + Cryptopunks = 'cryptopunks', + CryptoPhunks = 'cryptophunks', +} + +export enum ToolTipType { + pool, + sus, +} + +// index starts at 1 for boolean reasons +export interface DropDownOption { + displayText: string + icon?: JSX.Element + onClick: () => void + reverseIndex?: number + reverseOnClick?: () => void +} diff --git a/src/nft/types/common/index.ts b/src/nft/types/common/index.ts new file mode 100644 index 0000000000..89a3196b12 --- /dev/null +++ b/src/nft/types/common/index.ts @@ -0,0 +1 @@ +export * from './common' diff --git a/src/nft/types/discover/discover.ts b/src/nft/types/discover/discover.ts new file mode 100644 index 0000000000..357d00761a --- /dev/null +++ b/src/nft/types/discover/discover.ts @@ -0,0 +1,77 @@ +export enum TimePeriod { + OneDay = 'ONE_DAY', + SevenDays = 'SEVEN_DAYS', + ThirtyDays = 'THIRTY_DAYS', + AllTime = 'ALL_TIME', +} + +export type VolumeType = 'nft' | 'eth' +export interface TransactionsResponse { + __v: number + _id: string + bannerImage: string + blockNumber: string + blockTimestamp: string + collections: [string] + createdAt: string + ethValue: number + from_address: string + gas: string + gasPrice: string + hash: string + isVerified: boolean + nftCount: number + profileImage: string + receiptContractAddress: string | null + receiptCumulatioveGasUsed: string + receiptGasUsed: string + receiptStatus: string + sweep: boolean + timestamp: string + to_address: string + updatedAt: string + usdValue: number + title: string +} + +export interface TrendingCollection { + name: string + address: string + imageUrl: string + bannerImageUrl: string + isVerified: boolean + volume: number + volumeChange: number + floor: number + floorChange: number + marketCap: number + percentListed: number + owners: number + ownersChange: number + totalSupply: number + sales: number +} + +export interface CollectionTableColumn { + collection: { + name: string + address: string + logo: string + isVerified: boolean + } + volume: { + value: number + change: number + type: VolumeType + } + floor: { + value: number + change: number + } + owners: { + value: number + change: number + } + sales: number + totalSupply: number +} diff --git a/src/nft/types/discover/index.ts b/src/nft/types/discover/index.ts new file mode 100644 index 0000000000..840a50ae34 --- /dev/null +++ b/src/nft/types/discover/index.ts @@ -0,0 +1 @@ +export * from './discover' diff --git a/src/nft/types/discover/react-table-config.d.ts b/src/nft/types/discover/react-table-config.d.ts new file mode 100644 index 0000000000..d07c686fba --- /dev/null +++ b/src/nft/types/discover/react-table-config.d.ts @@ -0,0 +1,21 @@ +import { UseSortByColumnOptions, UseSortByColumnProps, UseSortByOptions, UseSortByState } from 'react-table' + +/* https://github.com/TanStack/table/issues/2970 */ +declare module 'react-table' { + export interface TableOptions> + extends UseExpandedOptions, + UseSortByOptions, + Record {} + + export interface TableState = Record> + extends UseColumnOrderState, + UseSortByState {} + + export interface ColumnInterface = Record> + extends UseFiltersColumnOptions, + UseSortByColumnOptions {} + + export interface ColumnInstance = Record> + extends UseFiltersColumnProps, + UseSortByColumnProps {} +} diff --git a/src/nft/types/index.ts b/src/nft/types/index.ts new file mode 100644 index 0000000000..b6ebd2dbfc --- /dev/null +++ b/src/nft/types/index.ts @@ -0,0 +1,6 @@ +export * from './checkout' +export * from './collection' +export * from './common' +export * from './discover' +export * from './navbar' +export * from './sell' diff --git a/src/nft/types/navbar/index.ts b/src/nft/types/navbar/index.ts new file mode 100644 index 0000000000..06c22c60c5 --- /dev/null +++ b/src/nft/types/navbar/index.ts @@ -0,0 +1 @@ +export * from './navbar' diff --git a/src/nft/types/navbar/navbar.ts b/src/nft/types/navbar/navbar.ts new file mode 100644 index 0000000000..278f427089 --- /dev/null +++ b/src/nft/types/navbar/navbar.ts @@ -0,0 +1,20 @@ +export interface LooksRareRewardsData { + address: string + cumulativeLooksAmount: string + cumulativeLooksProof: string[] +} + +export interface FungibleToken { + name: string + address: string + symbol: string + decimals: number + chainId: number + logoURI: string + coinGeckoId: string + priceUsd: number + price24hChange: number + volume24h: number + onDefaultList?: boolean + marketCap: number +} diff --git a/src/nft/types/sell/index.ts b/src/nft/types/sell/index.ts new file mode 100644 index 0000000000..9e80fb7083 --- /dev/null +++ b/src/nft/types/sell/index.ts @@ -0,0 +1 @@ +export * from './sell' diff --git a/src/nft/types/sell/sell.ts b/src/nft/types/sell/sell.ts new file mode 100644 index 0000000000..dd00d564f7 --- /dev/null +++ b/src/nft/types/sell/sell.ts @@ -0,0 +1,117 @@ +import { GenieCollection } from '../common' + +export interface ListingMarket { + name: string + fee: number + icon: string +} +export interface ListingWarning { + marketplace: ListingMarket + message: string +} + +export interface SellOrder { + assetId: string + ethPrice: number + basePrice: number + baseCurrency: string + baseCurrencyDecimal: number + orderCreatedDate: string + orderClosingDate: string + quantity: number + timestamp: string + marketplace: string + marketplaceUrl: string + orderHash: string + ammFeePercent?: number + ethReserves?: number + tokenReserves?: number +} + +export interface WalletAsset { + id?: string + image_url: string + image_preview_url: string + name: string + tokenId: string + asset_contract: { + address: string + schema_name: 'ERC1155' | 'ERC721' | string + asset_contract_type: string + created_date: string + name: string + symbol: string + description: string + external_link: string + image_url: string + default_to_fiat: boolean + only_proxied_transfers: boolean + payout_address: string + } + collection: GenieCollection + collectionIsVerified: boolean + lastPrice: number + floorPrice: number + creatorPercentage: number + listing_date: string + date_acquired: string + sellOrders: SellOrder[] + floor_sell_order_price: number + // Used for creating new listings + expirationTime?: number + marketAgnosticPrice?: string + newListings?: { + price?: string + marketplace: ListingMarket + overrideFloorPrice?: boolean + }[] + marketplaces?: ListingMarket[] + listingWarnings?: ListingWarning[] +} + +export interface WalletCollection { + address: string + name: string + image: string + floorPrice: number + count: number +} + +export enum ListingStatus { + APPROVED = 'Approved', + CONTINUE = 'Continue', + DEFINED = 'Defined', + FAILED = 'Failed', + PAUSED = 'Paused', + PENDING = 'Pending', + REJECTED = 'Rejected', + SIGNING = 'Signing', +} + +export interface ListingRow { + images: string[] + name: string + status: ListingStatus + callback?: () => Promise +} + +export interface AssetRow extends ListingRow { + asset: WalletAsset + marketplace: ListingMarket +} + +export interface CollectionRow extends ListingRow { + collectionAddress: string + marketplace: ListingMarket +} + +// Creating this as an enum and not boolean as we will likely have a success screen state to show +export enum SellPageStateType { + SELECTING, + LISTING, +} + +export enum ListingResponse { + TRY_AGAIN, + SUCCESS, +} diff --git a/src/nft/utils/address.ts b/src/nft/utils/address.ts new file mode 100644 index 0000000000..8c2056ec5c --- /dev/null +++ b/src/nft/utils/address.ts @@ -0,0 +1,19 @@ +import { isAddress } from '@ethersproject/address' + +/** + * Shortens an Ethereum address by N characters + * @param address blockchain address + * @param charsStart amount of character to shorten (from both ends / in the beginning) + * @param charsEnd amount of characters to shorten in the end + * @returns formatted string + */ +export function shortenAddress(address: string, charsStart = 4, charsEnd?: number): string { + const parsed = isAddress(address) + if (!parsed) throw Error(`Invalid 'address' parameter '${address}'.`) + + return `${address.substring(0, charsStart + 2)}...${address.substring(42 - (charsEnd || charsStart))}` +} + +export function shortenEnsName(name?: string): string | undefined { + return !name || name.length <= 12 ? name : `${name.substring(0, 6)}...eth` +} diff --git a/src/nft/utils/buildActivityAsset.ts b/src/nft/utils/buildActivityAsset.ts new file mode 100644 index 0000000000..3273b78e6b --- /dev/null +++ b/src/nft/utils/buildActivityAsset.ts @@ -0,0 +1,34 @@ +import { BigNumber } from '@ethersproject/bignumber' +import { formatEther } from '@ethersproject/units' +import { ActivityEvent, GenieAsset } from 'nft/types' + +export const buildActivityAsset = (event: ActivityEvent, collectionName: string, ethPriceInUSD: number): GenieAsset => { + const assetUsdPrice = event.price + ? formatEther( + BigNumber.from(event.price) + .mul(BigNumber.from(Math.trunc(ethPriceInUSD * 100))) + .div(100) + ) + : '0' + + return { + address: event.collectionAddress, + collectionName, + currentEthPrice: event.price, + imageUrl: event.tokenMetadata?.imageUrl, + marketplace: event.marketplace, + name: event.tokenMetadata?.name, + tokenId: event.tokenId, + openseaSusFlag: event.tokenMetadata?.suspiciousFlag, + smallImageUrl: event.tokenMetadata?.smallImageUrl, + collectionSymbol: event.symbol, + currentUsdPrice: assetUsdPrice, + priceInfo: { + USDPrice: assetUsdPrice, + ETHPrice: event.price, + basePrice: event.price, + baseAsset: 'ETH', + }, + tokenType: event.tokenMetadata?.standard, + } as GenieAsset +} diff --git a/src/nft/utils/buildSellObject.ts b/src/nft/utils/buildSellObject.ts new file mode 100644 index 0000000000..1b4c7466b5 --- /dev/null +++ b/src/nft/utils/buildSellObject.ts @@ -0,0 +1,16 @@ +export const buildSellObject = (amount: string) => { + return { + address: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + amount, + decimals: 18, + name: 'Ethereum', + priceInfo: { + baseAsset: 'ETH', + basePrice: amount, + ETHPrice: amount, + }, + symbol: 'ETH', + tokenId: 'ETH', + tokenType: 'ERC20', + } +} diff --git a/src/nft/utils/calcPoolPrice.ts b/src/nft/utils/calcPoolPrice.ts new file mode 100644 index 0000000000..8ca819d6ec --- /dev/null +++ b/src/nft/utils/calcPoolPrice.ts @@ -0,0 +1,45 @@ +import { BigNumber } from '@ethersproject/bignumber' + +import { GenieAsset, Markets } from '../types' + +export const calcPoolPrice = (asset: GenieAsset, position = 0) => { + let amountToBuy: BigNumber = BigNumber.from(0) + let marginalBuy: BigNumber = BigNumber.from(0) + const nft = asset.sellorders[0] + const decimals = BigNumber.from(1).mul(10).pow(18) + const ammFee = nft.ammFeePercent ? (100 + nft.ammFeePercent) * 100 : 110 * 100 + + if (asset.marketplace === Markets.NFTX) { + const sixteenmul = BigNumber.from(1).mul(10).pow(16) + amountToBuy = BigNumber.from(ammFee) + .div(100) + .mul(position + 1) + amountToBuy = amountToBuy.mul(sixteenmul) + + marginalBuy = BigNumber.from(ammFee).div(100).mul(position) + marginalBuy = marginalBuy.mul(sixteenmul) + } + if (asset.marketplace === Markets.NFT20) { + amountToBuy = BigNumber.from(100).mul(position + 1) + amountToBuy = amountToBuy.mul(decimals) + + marginalBuy = BigNumber.from(100).mul(position) + marginalBuy = marginalBuy.mul(decimals) + } + + const ethReserves = BigNumber.from(nft.ethReserves?.toLocaleString('fullwide', { useGrouping: false })) + const tokenReserves = BigNumber.from(nft.tokenReserves?.toLocaleString('fullwide', { useGrouping: false })) + const numerator = ethReserves.mul(amountToBuy).mul(1000) + const denominator = tokenReserves.sub(amountToBuy).mul(997) + + const marginalnumerator = ethReserves.mul(marginalBuy).mul(1000) + const marginaldenominator = tokenReserves.sub(marginalBuy).mul(997) + + let price = numerator.div(denominator) + const marginalprice = marginalnumerator.div(marginaldenominator) + + price = price.sub(marginalprice) + price = price.mul(101).div(100) + + return price.toString() +} diff --git a/src/nft/utils/claimLooks.ts b/src/nft/utils/claimLooks.ts new file mode 100644 index 0000000000..065367ec1c --- /dev/null +++ b/src/nft/utils/claimLooks.ts @@ -0,0 +1,48 @@ +import { Signer } from '@ethersproject/abstract-signer' +import { Contract } from '@ethersproject/contracts' +import { BaseProvider } from '@ethersproject/providers' + +const looksRareContract = new Contract('0xea37093ce161f090e443f304e1bf3a8f14d7bb40', [ + { + anonymous: false, + inputs: [ + { indexed: true, internalType: 'address', name: 'user', type: 'address' }, + { indexed: true, internalType: 'uint256', name: 'rewardRound', type: 'uint256' }, + { indexed: false, internalType: 'uint256', name: 'amount', type: 'uint256' }, + ], + name: 'RewardsClaim', + type: 'event', + }, + { + inputs: [ + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'bytes32[]', name: 'merkleProof', type: 'bytes32[]' }, + ], + name: 'claim', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: '', type: 'address' }], + name: 'amountClaimedByUser', + outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, +]) + +export const getClaimedAmount = async ({ address, provider }: { address: string; provider: BaseProvider }) => + provider && (await looksRareContract.connect(provider).amountClaimedByUser(address)) + +export const claimLooks = async ({ + signer, + looksTotal, + proof, +}: { + signer: Signer + looksTotal: string + proof: string[] +}) => { + await looksRareContract.connect(signer).functions.claim(looksTotal, proof) +} diff --git a/src/nft/utils/colors.ts b/src/nft/utils/colors.ts new file mode 100644 index 0000000000..593572464e --- /dev/null +++ b/src/nft/utils/colors.ts @@ -0,0 +1,20 @@ +export const foregrounds = ['#001FAA', '#5D31FF', '#8EC3E4', '#F10B00', '#E843D3', '#C4B5FC', '#F88DD5'] + +export const backgrounds = ['#5DCCB9', '#9AFBCF', '#D1F8E7', '#73F54B', '#D3FB51', '#FCF958'] + +export function hashCode(text: string) { + let hash = 0 + if (text.length === 0) return hash + for (let i = 0; i < text.length; i++) { + const chr = text.charCodeAt(i) + hash = (hash << 3) - hash + chr + hash |= 0 + } + return hash +} + +export function addressToHashedColor(colors: string[], address: string | null): string | undefined { + if (address == null) return undefined + + return colors[Math.abs(hashCode(address.toLowerCase()) % colors.length)] +} diff --git a/src/nft/utils/currency.ts b/src/nft/utils/currency.ts new file mode 100644 index 0000000000..b1508a9dca --- /dev/null +++ b/src/nft/utils/currency.ts @@ -0,0 +1,54 @@ +import { formatEther, parseEther } from '@ethersproject/units' + +export const formatUsdPrice = (price: number) => { + if (price > 1000000) { + return `$${(price / 1000000).toFixed(1)}M` + } else if (price > 1000) { + return `$${(price / 1000).toFixed(1)}K` + } else { + return `$${price.toFixed(2)}` + } +} + +export const formatUSDPriceWithCommas = (price: number) => { + return `$${Math.round(price) + .toString() + .replace(/\B(?=(\d{3})+(?!\d))/g, ',')}` +} + +export const formatEthPrice = (price: string) => { + if (!price) return 0 + + const formattedPrice = parseFloat(formatEther(String(price))) + return ( + Math.round(formattedPrice * (formattedPrice >= 1 ? 100 : 1000) + Number.EPSILON) / + (formattedPrice >= 1 ? 100 : 1000) + ) +} + +// Stringify the `price` anyway because the `price` is being passed as any in some places +export const numberToWei = (amount: number) => { + return parseEther(amount.toString()) +} + +export const ethNumberStandardFormatter = (amount: string | number | undefined, includeDollarSign = false): string => { + if (!amount) return '-' + + const amountInDecimals = parseFloat(amount.toString()) + const conditionalDollarSign = includeDollarSign ? '$' : '' + + if (amountInDecimals < 0.0001) return `< ${conditionalDollarSign}0.00001` + if (amountInDecimals < 1) return `${conditionalDollarSign}${amountInDecimals.toFixed(3)}` + return ( + conditionalDollarSign + + amountInDecimals + .toFixed(2) + .toString() + .replace(/\B(?=(\d{3})+(?!\d))/g, ',') + ) +} + +export const formatWeiToDecimal = (amount: string) => { + if (!amount) return '-' + return ethNumberStandardFormatter(formatEther(amount)) +} diff --git a/src/nft/utils/date.ts b/src/nft/utils/date.ts new file mode 100644 index 0000000000..8ea987ea53 --- /dev/null +++ b/src/nft/utils/date.ts @@ -0,0 +1,18 @@ +export const isValidDate = (date: number): boolean => { + const d = Date.parse(date.toString()) + return isNaN(d) ? false : true +} + +export const getTimeDifference = (eventTimestamp: string) => { + const date = new Date(eventTimestamp).getTime() + const diff = new Date().getTime() - date + + const days = Math.floor(diff / (1000 * 60 * 60 * 24)) + const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)) + const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)) + + if (days > 0) return `${days} day${days > 1 ? 's' : ''} ago` + if (hours > 0) return `${hours} hour${hours > 1 ? 's' : ''} ago` + if (minutes > 1) return `${minutes} minutes ago` + return 'Just now' +} diff --git a/src/nft/utils/eu-timezones.ts b/src/nft/utils/eu-timezones.ts new file mode 100644 index 0000000000..f82ad2b1fe --- /dev/null +++ b/src/nft/utils/eu-timezones.ts @@ -0,0 +1,72 @@ +export function consentRequired(tz: string): boolean { + switch (tz) { + case 'Europe/Vienna': + return true + case 'Europe/Brussels': + return true + case 'Europe/Sofia': + return true + case 'Europe/Zagreb': + return true + case 'Asia/Famagusta': + return true + case 'Asia/Nicosia': + return true + case 'Europe/Prague': + return true + case 'Europe/Copenhagen': + return true + case 'Europe/Tallinn': + return true + case 'Europe/Helsinki': + return true + case 'Europe/Paris': + return true + case 'Europe/Berlin': + return true + case 'Europe/Busingen': + return true + case 'Europe/Athens': + return true + case 'Europe/Budapest': + return true + case 'Europe/Dublin': + return true + case 'Europe/Rome': + return true + case 'Europe/Riga': + return true + case 'Europe/Vilnius': + return true + case 'Europe/Luxembourg': + return true + case 'Europe/Malta': + return true + case 'Europe/Amsterdam': + return true + case 'Europe/Warsaw': + return true + case 'Atlantic/Azores': + return true + case 'Atlantic/Madeira': + return true + case 'Europe/Lisbon': + return true + case 'Europe/Bucharest': + return true + case 'Europe/Bratislava': + return true + case 'Europe/Ljubljana': + return true + case 'Africa/Ceuta': + return true + case 'Atlantic/Canary': + return true + case 'Europe/Madrid': + return true + case 'Europe/Stockholm': + return true + default: + return false + } +} diff --git a/src/nft/utils/fetchPrice.ts b/src/nft/utils/fetchPrice.ts new file mode 100644 index 0000000000..dfbf8b336c --- /dev/null +++ b/src/nft/utils/fetchPrice.ts @@ -0,0 +1,15 @@ +export enum Currency { + ETH = 'ETH', + LOOKS = 'LOOKS', + MATIC = 'MATIC', +} + +export const fetchPrice = async (currency: Currency = Currency.ETH): Promise => { + try { + const response = await fetch(`https://api.coinbase.com/v2/exchange-rates?currency=${currency}`) + return response.json().then((j) => j.data.rates.USD) + } catch (e) { + console.error(e) + return + } +} diff --git a/src/nft/utils/groupBy.ts b/src/nft/utils/groupBy.ts new file mode 100644 index 0000000000..384a80f22b --- /dev/null +++ b/src/nft/utils/groupBy.ts @@ -0,0 +1,6 @@ +export const groupBy = (xs: T[], key: string) => { + return xs.reduce((rv: any, x: any) => { + ;(rv[x[key]] = rv[x[key]] || []).push(x) + return rv + }, {}) +} diff --git a/src/nft/utils/isAssetOwnedByUser.ts b/src/nft/utils/isAssetOwnedByUser.ts new file mode 100644 index 0000000000..70eb858002 --- /dev/null +++ b/src/nft/utils/isAssetOwnedByUser.ts @@ -0,0 +1,25 @@ +import { Provider } from '@ethersproject/abstract-provider' +import { Contract } from '@ethersproject/contracts' + +import ERC721 from '../../abis/erc721.json' +import { TokenType } from '../types' + +export const isAssetOwnedByUser = async ({ + tokenId, + assetAddress, + userAddress, + tokenType, + provider, +}: { + tokenId: string + assetAddress: string + userAddress: string + tokenType: TokenType + provider: Provider +}) => { + if (tokenType === TokenType.ERC721) { + const c = new Contract(assetAddress, ERC721, provider) + + return (await c.functions.ownerOf(tokenId)) === userAddress + } else return false +} diff --git a/src/nft/utils/isAudio.ts b/src/nft/utils/isAudio.ts new file mode 100644 index 0000000000..d07d3eb94e --- /dev/null +++ b/src/nft/utils/isAudio.ts @@ -0,0 +1,9 @@ +const set = new Set(['mp3', 'wav']) + +export const isAudio = (file: string) => { + if (!file) return false + + const fileType = file.substring(file.lastIndexOf('.') + 1) + + return set.has(fileType) +} diff --git a/src/nft/utils/isIPhoneOrSafari.ts b/src/nft/utils/isIPhoneOrSafari.ts new file mode 100644 index 0000000000..9093d63fd7 --- /dev/null +++ b/src/nft/utils/isIPhoneOrSafari.ts @@ -0,0 +1,11 @@ +const iOSDevices = ['iPhone', 'iPad', 'iPod', 'iPhone Simulator', 'iPod Simulator', 'iPad Simulator'] + +export const isIPhoneOrSafari = () => { + const uA = navigator.userAgent + const vendor = navigator.vendor + const platform = navigator.platform + + return ( + iOSDevices.includes(platform) || (/Safari/i.test(uA) && /Apple Computer/.test(vendor) && !/Mobi|Android/i.test(uA)) + ) +} diff --git a/src/nft/utils/isVideo.ts b/src/nft/utils/isVideo.ts new file mode 100644 index 0000000000..c439d9d4a9 --- /dev/null +++ b/src/nft/utils/isVideo.ts @@ -0,0 +1,3 @@ +import extensions from 'video-extensions' + +export const isVideo = (path: string | null) => extensions.find((ext) => path?.endsWith(`.${ext}`)) !== undefined diff --git a/src/nft/utils/listNfts.ts b/src/nft/utils/listNfts.ts new file mode 100644 index 0000000000..4303ad83dc --- /dev/null +++ b/src/nft/utils/listNfts.ts @@ -0,0 +1,268 @@ +import { Signer } from '@ethersproject/abstract-signer' +import { BigNumber } from '@ethersproject/bignumber' +import { Contract } from '@ethersproject/contracts' +import { JsonRpcSigner, Web3Provider } from '@ethersproject/providers' +import { parseEther } from '@ethersproject/units' +import { addressesByNetwork, MakerOrder, signMakerOrder, SupportedChainId } from '@looksrare/sdk' +import { Seaport } from '@opensea/seaport-js' +import { ItemType } from '@opensea/seaport-js/lib/constants' +import { ConsiderationInputItem } from '@opensea/seaport-js/lib/types' +import { + OPENSEA_DEFAULT_CROSS_CHAIN_CONDUIT_KEY, + OPENSEA_DEFAULT_ZONE, + OPENSEA_KEY_TO_CONDUIT, + OPENSEA_LISTINGS_API_PATH, +} from 'nft/queries/openSea' + +import ERC721 from '../../abis/erc721.json' +import { PostOpenSeaSellOrder } from '../queries' +import { createLooksRareOrder } from '../queries' +import { newX2Y2Order } from '../queries' +import { INVERSE_BASIS_POINTS, OPENSEA_DEFAULT_FEE, OPENSEA_FEE_ADDRESS } from '../queries/openSea' +import { ListingMarket, WalletAsset } from '../types' +import { ListingStatus } from '../types' +import { createSellOrder, encodeOrder, OfferItem, OrderPayload, signOrderData } from './x2y2' + +export const ListingMarkets: ListingMarket[] = [ + { + name: 'LooksRare', + fee: 2.0, + icon: '/nft/svgs/marketplaces/looksrare.svg', + }, + { + name: 'OpenSea', + fee: 2.5, + icon: '/nft/svgs/marketplaces/opensea.svg', + }, + { + name: 'X2Y2', + fee: 0.5, + icon: '/nft/svgs/marketplaces/x2y2.svg', + }, +] + +const createConsiderationItem = (basisPoints: string, recipient: string): ConsiderationInputItem => { + return { + amount: basisPoints, + recipient, + } +} + +const getConsiderationItems = ( + asset: WalletAsset, + price: BigNumber, + signerAddress: string +): { + sellerFee: ConsiderationInputItem + openseaFee: ConsiderationInputItem + creatorFee?: ConsiderationInputItem +} => { + const openSeaBasisPoints = OPENSEA_DEFAULT_FEE * INVERSE_BASIS_POINTS + const creatorFeeBasisPoints = asset.creatorPercentage * INVERSE_BASIS_POINTS + const sellerBasisPoints = INVERSE_BASIS_POINTS - openSeaBasisPoints - creatorFeeBasisPoints + + const openseaFee = price.mul(BigNumber.from(openSeaBasisPoints)).div(BigNumber.from(INVERSE_BASIS_POINTS)).toString() + const creatorFee = price + .mul(BigNumber.from(creatorFeeBasisPoints)) + .div(BigNumber.from(INVERSE_BASIS_POINTS)) + .toString() + const sellerFee = price.mul(BigNumber.from(sellerBasisPoints)).div(BigNumber.from(INVERSE_BASIS_POINTS)).toString() + + return { + sellerFee: createConsiderationItem(sellerFee, signerAddress), + openseaFee: createConsiderationItem(openseaFee, OPENSEA_FEE_ADDRESS), + creatorFee: + creatorFeeBasisPoints > 0 ? createConsiderationItem(creatorFee, asset.asset_contract.payout_address) : undefined, + } +} + +export async function approveCollection( + operator: string, + collectionAddress: string, + signer: Signer, + setStatus: (newStatus: ListingStatus) => void +): Promise { + // This will work for both 721s & 1155s because they both have the + // setApprovalForAll() method + const ERC721Contract = new Contract(collectionAddress, ERC721, signer) + const signerAddress = await signer.getAddress() + setStatus(ListingStatus.PENDING) + try { + const approved = await ERC721Contract.isApprovedForAll(signerAddress, operator) + if (approved) { + setStatus(ListingStatus.APPROVED) + return + } + + setStatus(ListingStatus.SIGNING) + const approvalTransaction = await ERC721Contract.setApprovalForAll(operator, true) + + setStatus(ListingStatus.PENDING) + const tx = await approvalTransaction.wait() + + tx.status === 1 ? setStatus(ListingStatus.APPROVED) : setStatus(ListingStatus.FAILED) + } catch (error) { + if (error.code === 4001) setStatus(ListingStatus.REJECTED) + else setStatus(ListingStatus.FAILED) + } +} + +export async function signListing( + marketplace: ListingMarket, + asset: WalletAsset, + signer: JsonRpcSigner, + provider: Web3Provider, + looksRareNonce = 0, + setStatus: (newStatus: ListingStatus) => void +): Promise { + const seaport = new Seaport(provider, { + conduitKeyToConduit: OPENSEA_KEY_TO_CONDUIT, + overrides: { + defaultConduitKey: OPENSEA_DEFAULT_CROSS_CHAIN_CONDUIT_KEY, + }, + }) + + const signerAddress = await signer.getAddress() + const listingPrice = asset.newListings?.find((listing) => listing.marketplace.name === marketplace.name)?.price + if (!listingPrice || !asset.expirationTime) return false + switch (marketplace.name) { + case 'OpenSea': + try { + const listingInWei = parseEther(listingPrice) + const { sellerFee, openseaFee, creatorFee } = getConsiderationItems(asset, listingInWei, signerAddress) + const considerationItems = [sellerFee, openseaFee, creatorFee].filter( + (item): item is ConsiderationInputItem => item !== undefined + ) + + const { executeAllActions } = await seaport.createOrder( + { + offer: [ + { + itemType: ItemType.ERC721, + token: asset.asset_contract.address, + identifier: asset.tokenId, + amount: '1', + }, + ], + consideration: considerationItems, + endTime: asset.expirationTime.toString(), + zone: OPENSEA_DEFAULT_ZONE, + restrictedByZone: true, + allowPartialFills: true, + }, + signerAddress + ) + + const order = await executeAllActions() + const res = await PostOpenSeaSellOrder(OPENSEA_LISTINGS_API_PATH, order) + if (res) setStatus(ListingStatus.APPROVED) + return true + } catch (error) { + if (error.code === 4001) setStatus(ListingStatus.REJECTED) + else setStatus(ListingStatus.FAILED) + return false + } + case 'LooksRare': + const addresses = addressesByNetwork[SupportedChainId.MAINNET] + const currentTime = Math.round(Date.now() / 1000) + const makerOrder: MakerOrder = { + // true --> ask / false --> bid + isOrderAsk: true, + // signer address of the maker order + signer: signerAddress, + // collection address + collection: asset.asset_contract.address, + // Price in WEI + price: parseEther(listingPrice.toString()), + // Token ID + tokenId: BigNumber.from(asset.tokenId), + // amount of tokens to sell/purchase (must be 1 for ERC721, 1+ for ERC1155) + amount: BigNumber.from(1), + // strategy for trade execution (e.g., DutchAuction, StandardSaleForFixedPrice), see addresses in the SDK + strategy: addresses.STRATEGY_STANDARD_SALE, + // currency address + currency: addresses.WETH, + // order nonce (must be unique unless new maker order is meant to override existing one e.g., lower ask price) + nonce: BigNumber.from(looksRareNonce), + // startTime timestamp in seconds + startTime: BigNumber.from(currentTime), + // endTime timestamp in seconds + endTime: BigNumber.from(asset.expirationTime), + // minimum ratio to be received by the user (per 10000) + minPercentageToAsk: BigNumber.from(10000) + .sub(BigNumber.from(200).add(BigNumber.from(asset.creatorPercentage * 10000))) + .toNumber(), + // params (e.g., price, target account for private sale) + params: [], + } + + try { + const signatureHash = await signMakerOrder( + signer, + SupportedChainId.MAINNET, + makerOrder, + process.env.REACT_APP_LOOKSRARE_MARKETPLACE_CONTRACT || '' + ) + setStatus(ListingStatus.PENDING) + const payload = { + signature: signatureHash, + tokenId: asset.tokenId, + collection: asset.asset_contract.address, + strategy: addresses.STRATEGY_STANDARD_SALE, + currency: addresses.WETH, + signer: signerAddress, + isOrderAsk: true, + nonce: looksRareNonce, + amount: 1, + price: parseEther(listingPrice.toString()).toString(), + startTime: currentTime, + endTime: asset.expirationTime, + minPercentageToAsk: 10000 - (200 + asset.creatorPercentage * 10000), + params: [], + } + const res = await createLooksRareOrder(payload) + if (res) setStatus(ListingStatus.APPROVED) + return res + } catch (error) { + if (error.code === 4001) setStatus(ListingStatus.REJECTED) + else setStatus(ListingStatus.FAILED) + return false + } + + case 'X2Y2': + const orderItem: OfferItem = { + price: parseEther(listingPrice.toString()), + tokens: [ + { + token: asset.asset_contract.address, + tokenId: BigNumber.from(parseFloat(asset.tokenId)), + }, + ], + } + const order = createSellOrder(signerAddress, asset.expirationTime, [orderItem]) + try { + await signOrderData(provider, order) + const payload: OrderPayload = { + order: encodeOrder(order), + isBundle: false, + bundleName: '', + bundleDesc: '', + orderIds: [], + changePrice: false, + isCollection: false, + } + setStatus(ListingStatus.PENDING) + // call server api + const resp = await newX2Y2Order(payload) + if (resp) setStatus(ListingStatus.APPROVED) + return resp + } catch (error) { + if (error.code === 4001) setStatus(ListingStatus.REJECTED) + else setStatus(ListingStatus.FAILED) + return false + } + + default: + return false + } +} diff --git a/src/nft/utils/numbers.ts b/src/nft/utils/numbers.ts new file mode 100644 index 0000000000..bcf4f1d432 --- /dev/null +++ b/src/nft/utils/numbers.ts @@ -0,0 +1,11 @@ +export const isNumber = (s: string): boolean => { + const reg = /^-?\d+\.?\d*$/ + return reg.test(s) && !isNaN(parseFloat(s)) && isFinite(parseFloat(s)) +} + +export const formatPercentage = (percentage: string): string => { + if (!percentage) return '-' + return `${parseFloat(percentage) + .toFixed(2) + .replace(/\B(?=(\d{3})+(?!\d))/g, ',')}%` +} diff --git a/src/nft/utils/putCommas.ts b/src/nft/utils/putCommas.ts new file mode 100644 index 0000000000..bd0b114aa0 --- /dev/null +++ b/src/nft/utils/putCommas.ts @@ -0,0 +1,8 @@ +export const putCommas = (value: number) => { + try { + if (!value) return value + return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') + } catch (err) { + return value + } +} diff --git a/src/nft/utils/rarity.ts b/src/nft/utils/rarity.ts new file mode 100644 index 0000000000..8ac5a710a0 --- /dev/null +++ b/src/nft/utils/rarity.ts @@ -0,0 +1,17 @@ +// change this if we change the fallback provider +export const fallbackProvider = 'PopRank' +export const shouldLinkToFallbackProvider = false +export const fallbackProviderLogo = '/nft/logos/poprank.png' + +/** + * Add provider mappings based on provider name returned from the backend here + */ +export const rarityProviderLogo: { [key: string]: string } = { + 'Rarity Sniper': '/nft/svgs/gem.svg', + Genie: fallbackProviderLogo, +} + +export const getRarityProviderLogo = (source?: string): string | null => { + if (!source) return null + return rarityProviderLogo[source] || fallbackProviderLogo +} diff --git a/src/nft/utils/roundAndPluralize.ts b/src/nft/utils/roundAndPluralize.ts new file mode 100644 index 0000000000..cbfc5dda9a --- /dev/null +++ b/src/nft/utils/roundAndPluralize.ts @@ -0,0 +1,5 @@ +export const roundAndPluralize = (i: number, word: string) => { + const rounded = Math.floor(i) + + return `${rounded} ${word}${rounded === 1 ? '' : 's'}` +} diff --git a/src/nft/utils/shortenId.ts b/src/nft/utils/shortenId.ts new file mode 100644 index 0000000000..130fbff3ff --- /dev/null +++ b/src/nft/utils/shortenId.ts @@ -0,0 +1,3 @@ +export const shortenId = (id: string, chars = 4) => { + return id.length > chars * 2 + 3 ? `${id.substring(0, chars)}...${id.substring(id.length - chars)}` : id +} diff --git a/src/nft/utils/timeSince.ts b/src/nft/utils/timeSince.ts new file mode 100644 index 0000000000..4d8180ad39 --- /dev/null +++ b/src/nft/utils/timeSince.ts @@ -0,0 +1,24 @@ +import { roundAndPluralize } from './roundAndPluralize' + +export function timeSince(date: Date, min?: boolean) { + const seconds = Math.floor((new Date().getTime() - date.getTime()) / 1000) + + let interval = seconds / 31536000 + + if (interval > 1) return roundAndPluralize(interval, min ? 'yr' : 'year') + + interval = seconds / 2592000 + if (interval > 1) return roundAndPluralize(interval, min ? 'mth' : 'month') + + interval = seconds / 86400 + if (interval > 1) return roundAndPluralize(interval, 'day') + + interval = seconds / 3600 + + if (interval > 1) return roundAndPluralize(interval, min ? 'hr' : 'hour') + + interval = seconds / 60 + if (interval > 1) return roundAndPluralize(interval, 'min') + + return roundAndPluralize(interval, 'sec') +} diff --git a/src/nft/utils/toSignificant.ts b/src/nft/utils/toSignificant.ts new file mode 100644 index 0000000000..d24c8b8cc8 --- /dev/null +++ b/src/nft/utils/toSignificant.ts @@ -0,0 +1,52 @@ +/** + * Format number in human-readable way + * @example + * ```js + * nFormat(134_256) // => 134K + * ``` + * @param num number to format + * @param digits digits after decimal point + * @returns formatted number string + */ +export function nFormat(num: number, digits = 0): string { + const lookup = [ + { value: 1, symbol: '' }, + //{ value: 1e3, symbol: 'K' }, + { value: 1e6, symbol: 'M' }, + { value: 1e9, symbol: 'B' }, + { value: 1e12, symbol: 'T' }, + { value: 1e15, symbol: 'Qa' }, + { value: 1e18, symbol: 'Qi' }, + ] + const rx = /\.0+$|(\.[0-9]*[1-9])0+$/ + const item = lookup + .slice() + .reverse() + .find((item) => num >= item.value) + return item ? (num / item.value).toFixed(digits).replace(rx, '$1') + item.symbol : '0' +} + +/** + * Rounds a number to significant 4-digit number + * @param n number + * @param precision + * @returns formatted number + */ +export const toSignificant = (n: string, precision = 4): string => { + const floatBal = parseFloat(n) + + if (floatBal > 9999) return nFormat(floatBal, 0) + + return floatBal.toPrecision(precision) +} + +/** + * Formats percent change values + * @param v number + * @returns formatted number + */ +export const formatChange = (v: number) => { + if (v >= 98) return nFormat(v, 2) + else if (v <= 0.1) return v.toFixed(2) + else return v.toPrecision(2) +} diff --git a/src/nft/utils/txRoute/combineItemsWithTxRoute.ts b/src/nft/utils/txRoute/combineItemsWithTxRoute.ts new file mode 100644 index 0000000000..af073760b0 --- /dev/null +++ b/src/nft/utils/txRoute/combineItemsWithTxRoute.ts @@ -0,0 +1,41 @@ +import { BuyItem, GenieAsset, PriceInfo, RoutingItem, UpdatedGenieAsset } from '../../types' + +const isTheSame = (item: GenieAsset, routeAsset: BuyItem | PriceInfo) => { + // if route asset has id, match by id + if ('id' in routeAsset && routeAsset.id) { + return routeAsset.id === item.id + } else { + return ( + 'address' in routeAsset && + routeAsset.address.toLowerCase() === item.address.toLowerCase() && + routeAsset.tokenId === item.tokenId + ) + } +} + +export const combineBuyItemsWithTxRoute = (items: GenieAsset[], txRoute?: RoutingItem[]): UpdatedGenieAsset[] => { + return items.map((item) => { + const route = txRoute && txRoute.find((r) => r.action === 'Buy' && isTheSame(item, r.assetOut)) + + // if the item is not found in txRoute, it means it's no longer for sale + if (txRoute && !route) { + return { + ...item, + isUnavailable: true, + } + } + + // if the price changed + if (route && 'priceInfo' in route.assetOut && item.priceInfo.basePrice !== route.assetOut.priceInfo.basePrice) { + return { + ...item, + updatedPriceInfo: route.assetOut.priceInfo, + } + } + + return { + ...item, + orderSource: route && 'orderSource' in route.assetOut ? route.assetOut.orderSource : undefined, + } + }) +} diff --git a/src/nft/utils/uniqBy.ts b/src/nft/utils/uniqBy.ts new file mode 100644 index 0000000000..d975bc359a --- /dev/null +++ b/src/nft/utils/uniqBy.ts @@ -0,0 +1,13 @@ +export function uniqBy(arr: T[], key: keyof T) { + const seen = new Set() + + return arr.filter((it) => { + const val = it[key] + if (seen.has(val)) { + return false + } else { + seen.add(val) + return true + } + }) +} diff --git a/src/nft/utils/x2y2.ts b/src/nft/utils/x2y2.ts new file mode 100644 index 0000000000..95aad540d8 --- /dev/null +++ b/src/nft/utils/x2y2.ts @@ -0,0 +1,138 @@ +import { defaultAbiCoder } from '@ethersproject/abi' +import { BigNumber, BigNumberish } from '@ethersproject/bignumber' +import { hexZeroPad } from '@ethersproject/bytes' +import { AddressZero } from '@ethersproject/constants' +import { keccak256 } from '@ethersproject/keccak256' +import { Web3Provider } from '@ethersproject/providers' +import { randomBytes } from '@ethersproject/random' + +const dataParamType = `tuple(address token, uint256 tokenId)[]` +const orderItemParamType = `tuple(uint256 price, bytes data)` +const orderParamTypes = [ + `uint256`, + `address`, + `uint256`, + `uint256`, + `uint256`, + `uint256`, + `address`, + `bytes`, + `uint256`, + `${orderItemParamType}[]`, +] +const orderParamType = `tuple(uint256 salt, address user, uint256 network, uint256 intent, uint256 delegateType, uint256 deadline, address currency, bytes dataMask, ${orderItemParamType}[] items, bytes32 r, bytes32 s, uint8 v, uint8 signVersion)` + +export type OfferItem = { + price: BigNumber + tokens: { + token: string + tokenId: BigNumberish + }[] +} + +type OrderItem = { + price: BigNumberish + data: string +} + +type Order = { + salt: BigNumberish + user: string + network: BigNumberish + intent: BigNumberish + delegateType: BigNumberish + deadline: BigNumberish + currency: string + dataMask: string + items: OrderItem[] + // signature + r: string + s: string + v: number + signVersion: number +} + +export type OrderPayload = { + order: string + isBundle: boolean + bundleName: string + bundleDesc: string + orderIds: number[] + changePrice: boolean + isCollection: boolean +} + +export type OrderResp = { + success: boolean + code: number + error?: string +} + +const randomSalt = () => { + const randomHex = BigNumber.from(randomBytes(16)).toHexString() + return hexZeroPad(randomHex, 64) +} + +const encodeItemData = (data: { token: string; tokenId: BigNumberish }[]) => { + return defaultAbiCoder.encode([dataParamType], [data]) +} + +export const signOrderData = async (web3Provider: Web3Provider, order: Order) => { + const orderData = defaultAbiCoder.encode(orderParamTypes, [ + order.salt, + order.user, + order.network, + order.intent, + order.delegateType, + order.deadline, + order.currency, + order.dataMask, + order.items.length, + order.items, + ]) + const orderHash = keccak256(orderData) + const orderSig = (await web3Provider.send('personal_sign', [orderHash, order.user])) as string + order.r = `0x${orderSig.slice(2, 66)}` + order.s = `0x${orderSig.slice(66, 130)}` + order.v = parseInt(orderSig.slice(130, 132), 16) + fixSignature(order) +} + +const fixSignature = (data: Order) => { + // in geth its always 27/28, in ganache its 0/1. Change to 27/28 to prevent + // signature malleability if version is 0/1 + // see https://github.com/ethereum/go-ethereum/blob/v1.8.23/internal/ethapi/api.go#L465 + if (data.v < 27) { + data.v = data.v + 27 + } +} + +export const encodeOrder = (order: Order): string => { + return defaultAbiCoder.encode([orderParamType], [order]) +} + +export const createSellOrder = (user: string, deadline: number, items: OfferItem[]): Order => { + const salt = randomSalt() + const network = 1 // mainnet + const intent = 1 // INTENT_SELL + const delegateType = 1 // DELEGATION_TYPE_ERC721 + const currency = AddressZero // ETH + return { + salt, + user, + network, + intent, + delegateType, + deadline, + currency, + dataMask: '0x', + items: items.map((item) => ({ + price: item.price, + data: encodeItemData(item.tokens), + })), + r: '', + s: '', + v: 0, + signVersion: 1, + } +} diff --git a/src/state/connection/hooks.ts b/src/state/connection/hooks.ts index 911ad7a9e7..f42c240570 100644 --- a/src/state/connection/hooks.ts +++ b/src/state/connection/hooks.ts @@ -12,6 +12,7 @@ import { useTotalUniEarned } from '../stake/hooks' export { default as useCurrencyBalance, useCurrencyBalances, + useCurrencyBalanceString, useNativeCurrencyBalances, useTokenBalance, useTokenBalances, diff --git a/src/theme/index.tsx b/src/theme/index.tsx index b1838c275c..4f77280568 100644 --- a/src/theme/index.tsx +++ b/src/theme/index.tsx @@ -8,6 +8,9 @@ import styled, { ThemeProvider as StyledComponentsThemeProvider, } from 'styled-components/macro' +import { cssStringFromTheme } from '../nft/css/cssStringFromTheme' +import { darkTheme } from '../nft/themes/darkTheme' +import { lightTheme } from '../nft/themes/lightTheme' import { useIsDarkMode } from '../state/user/hooks' import { colors as ColorsPalette, colorsDark, colorsLight } from './colors' import { Colors, ThemeColors } from './styled' @@ -341,4 +344,8 @@ html { a { color: ${({ theme }) => theme.deprecated_blue1}; } + +:root { + ${({ theme }) => (theme.darkMode ? cssStringFromTheme(darkTheme) : cssStringFromTheme(lightTheme))} +} ` diff --git a/yarn.lock b/yarn.lock index dd01034e0b..8566a05a00 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,41 @@ # yarn lockfile v1 +"@0xsequence/abi@^0.39.6": + version "0.39.6" + resolved "https://registry.yarnpkg.com/@0xsequence/abi/-/abi-0.39.6.tgz#97ebe35030ae1084f4cab13ed19aacf9baa090e2" + integrity sha512-ROHfAyQvN1N2G6DeVyWR4wxi0dd0HjJZQ+2UkxvYJrdywQZONmrUPD6MfT4Fd+JrQwdIxkcNu2/QfgkyZDerFw== + +"@0xsequence/multicall@^0.39.0": + version "0.39.6" + resolved "https://registry.yarnpkg.com/@0xsequence/multicall/-/multicall-0.39.6.tgz#ebf79eb3b080aa92ad4d3cdde2acdce75b22ba3b" + integrity sha512-oHgxAuYoanYXKArrzPF+a8f9CAzDX5xYp+U+bcCd3qOagIn1xeQCEW4zbnGDRD+ZguhlcP2I7wiW1eImmoOi1g== + dependencies: + "@0xsequence/abi" "^0.39.6" + "@0xsequence/network" "^0.39.6" + "@0xsequence/utils" "^0.39.6" + "@ethersproject/providers" "^5.5.1" + ethers "^5.5.2" + +"@0xsequence/network@^0.39.6": + version "0.39.6" + resolved "https://registry.yarnpkg.com/@0xsequence/network/-/network-0.39.6.tgz#50973de1953625220eaa447198d3c89e5451c00a" + integrity sha512-esmdTCT98tjjbYoiYjaOmJIG0aSgadg9wR1FB6lzPdAVuq+xOGvxygtTiqhTqXF+YI2Alc4b5EP7tUWC9OA10Q== + dependencies: + "@0xsequence/utils" "^0.39.6" + "@ethersproject/providers" "^5.5.1" + ethers "^5.5.2" + +"@0xsequence/utils@^0.39.6": + version "0.39.6" + resolved "https://registry.yarnpkg.com/@0xsequence/utils/-/utils-0.39.6.tgz#534b854a95814ec223f9ce77572b340beea503ea" + integrity sha512-Zf5NTRSzrOrc48SXsmtRDs1uaJ0gaOjwQYkANoIco54Rq+IA1Dlz9HWRebgyjeCUrXLnGCckljBemAUoZbVgIg== + dependencies: + "@ethersproject/abstract-signer" "^5.5.0" + "@ethersproject/properties" "^5.5.0" + ethers "^5.5.2" + js-base64 "^3.7.2" + "@amplitude/analytics-browser@^0.5.1": version "0.5.1" resolved "https://registry.yarnpkg.com/@amplitude/analytics-browser/-/analytics-browser-0.5.1.tgz#0d7304a64251b9b65e826948e5afd311d6118576" @@ -46,6 +81,14 @@ resolved "https://registry.yarnpkg.com/@amplitude/ua-parser-js/-/ua-parser-js-0.7.31.tgz#749bf7cb633cfcc7ff3c10805bad7c5f6fbdbc61" integrity sha512-+z8UGRaj13Pt5NDzOnkTBy49HE2CX64jeL0ArB86HAtilpnfkPB7oqkigN7Lf2LxscMg4QhFD7mmCfedh3rqTg== +"@ampproject/remapping@^2.1.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" + integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== + dependencies: + "@jridgewell/gen-mapping" "^0.1.0" + "@jridgewell/trace-mapping" "^0.3.9" + "@ardatan/aggregate-error@0.0.6": version "0.0.6" resolved "https://registry.yarnpkg.com/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz#fe6924771ea40fc98dc7a7045c2e872dc8527609" @@ -67,19 +110,19 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.5.5": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" - integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.5.5": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== dependencies: - "@babel/highlight" "^7.16.7" + "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.12.1", "@babel/compat-data@^7.16.4": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.8.tgz#31560f9f29fdf1868de8cb55049538a1b9732a60" - integrity sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q== +"@babel/compat-data@^7.12.1", "@babel/compat-data@^7.16.4", "@babel/compat-data@^7.18.8": + version "7.18.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" + integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== -"@babel/core@7.12.3", "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.7.5", "@babel/core@^7.8.4": +"@babel/core@7.12.3": version "7.12.3" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.3.tgz#1b436884e1e3bff6fb1328dc02b208759de92ad8" integrity sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g== @@ -101,14 +144,35 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/generator@^7.11.6", "@babel/generator@^7.12.1", "@babel/generator@^7.12.13", "@babel/generator@^7.16.8", "@babel/generator@^7.5.0": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.8.tgz#359d44d966b8cd059d543250ce79596f792f2ebe" - integrity sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw== +"@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.13.10", "@babel/core@^7.7.5", "@babel/core@^7.8.4": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.10.tgz#39ad504991d77f1f3da91be0b8b949a5bc466fb8" + integrity sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw== dependencies: - "@babel/types" "^7.16.8" + "@ampproject/remapping" "^2.1.0" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.10" + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-module-transforms" "^7.18.9" + "@babel/helpers" "^7.18.9" + "@babel/parser" "^7.18.10" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.18.10" + "@babel/types" "^7.18.10" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.1" + semver "^6.3.0" + +"@babel/generator@^7.11.6", "@babel/generator@^7.12.1", "@babel/generator@^7.12.13", "@babel/generator@^7.16.8", "@babel/generator@^7.18.10", "@babel/generator@^7.5.0": + version "7.18.12" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.12.tgz#fa58daa303757bd6f5e4bbca91b342040463d9f4" + integrity sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg== + dependencies: + "@babel/types" "^7.18.10" + "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" - source-map "^0.5.0" "@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.16.7": version "7.16.7" @@ -125,14 +189,14 @@ "@babel/helper-explode-assignable-expression" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-compilation-targets@^7.12.1", "@babel/helper-compilation-targets@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b" - integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA== +"@babel/helper-compilation-targets@^7.12.1", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" + integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== dependencies: - "@babel/compat-data" "^7.16.4" - "@babel/helper-validator-option" "^7.16.7" - browserslist "^4.17.5" + "@babel/compat-data" "^7.18.8" + "@babel/helper-validator-option" "^7.18.6" + browserslist "^4.20.2" semver "^6.3.0" "@babel/helper-create-class-features-plugin@^7.12.1", "@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.16.7": @@ -156,12 +220,10 @@ "@babel/helper-annotate-as-pure" "^7.16.7" regexpu-core "^4.7.1" -"@babel/helper-environment-visitor@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" - integrity sha512-SLLb0AAn6PkUeAfKJCCOl9e1R53pQlGAfc4y4XuMRZfqeMYLE0dM1LMhqbGAlGQY0lfw5/ohoYWAe9V1yibRag== - dependencies: - "@babel/types" "^7.16.7" +"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" + integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== "@babel/helper-explode-assignable-expression@^7.16.7": version "7.16.7" @@ -170,14 +232,13 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-function-name@^7.12.13", "@babel/helper-function-name@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f" - integrity sha512-QfDfEnIUyyBSR3HtrtGECuZ6DAyCkYFp7GHl75vFtTnn6pjKeK0T1DB5lLkFvBea8MdaiUABx3osbgLyInoejA== +"@babel/helper-function-name@^7.12.13", "@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" + integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A== dependencies: - "@babel/helper-get-function-arity" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/template" "^7.18.6" + "@babel/types" "^7.18.9" "@babel/helper-get-function-arity@^7.16.7": version "7.16.7" @@ -186,12 +247,12 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-hoist-variables@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246" - integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg== +"@babel/helper-hoist-variables@^7.16.7", "@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" "@babel/helper-member-expression-to-functions@^7.16.7": version "7.16.7" @@ -200,26 +261,26 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" - integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg== +"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" + integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" -"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz#7665faeb721a01ca5327ddc6bba15a5cb34b6a41" - integrity sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng== +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.16.7", "@babel/helper-module-transforms@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712" + integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g== dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-simple-access" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.18.6" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" "@babel/helper-optimise-call-expression@^7.16.7": version "7.16.7" @@ -253,12 +314,12 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-simple-access@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7" - integrity sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g== +"@babel/helper-simple-access@^7.16.7", "@babel/helper-simple-access@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" + integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" "@babel/helper-skip-transparent-expression-wrappers@^7.12.1", "@babel/helper-skip-transparent-expression-wrappers@^7.16.0": version "7.16.0" @@ -267,22 +328,27 @@ dependencies: "@babel/types" "^7.16.0" -"@babel/helper-split-export-declaration@^7.12.13", "@babel/helper-split-export-declaration@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b" - integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw== +"@babel/helper-split-export-declaration@^7.12.13", "@babel/helper-split-export-declaration@^7.16.7", "@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== dependencies: - "@babel/types" "^7.16.7" + "@babel/types" "^7.18.6" -"@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" - integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== +"@babel/helper-string-parser@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" + integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== -"@babel/helper-validator-option@^7.12.1", "@babel/helper-validator-option@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" - integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== +"@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.16.7", "@babel/helper-validator-identifier@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" + integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== + +"@babel/helper-validator-option@^7.12.1", "@babel/helper-validator-option@^7.16.7", "@babel/helper-validator-option@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" + integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== "@babel/helper-wrap-function@^7.16.8": version "7.16.8" @@ -294,21 +360,21 @@ "@babel/traverse" "^7.16.8" "@babel/types" "^7.16.8" -"@babel/helpers@^7.12.1": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.16.7.tgz#7e3504d708d50344112767c3542fc5e357fffefc" - integrity sha512-9ZDoqtfY7AuEOt3cxchfii6C7GDyyMBffktR5B2jvWv8u2+efwvpnVKXMWzNehqy68tKgAfSwfdw/lWpthS2bw== +"@babel/helpers@^7.12.1", "@babel/helpers@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9" + integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ== dependencies: - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/template" "^7.18.6" + "@babel/traverse" "^7.18.9" + "@babel/types" "^7.18.9" -"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.7.tgz#81a01d7d675046f0d96f82450d9d9578bdfd6b0b" - integrity sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw== +"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7", "@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-validator-identifier" "^7.18.6" chalk "^2.0.0" js-tokens "^4.0.0" @@ -317,10 +383,10 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.16.tgz#cc31257419d2c3189d394081635703f549fc1ed4" integrity sha512-c/+u9cqV6F0+4Hpq01jnJO+GLp2DdT63ppz9Xa+6cHaajM9VFzK/iDXiKK65YtpeVwu+ctfS6iqlMqRgQRzeCw== -"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.11.5", "@babel/parser@^7.12.13", "@babel/parser@^7.12.3", "@babel/parser@^7.16.7", "@babel/parser@^7.16.8", "@babel/parser@^7.7.0": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.8.tgz#61c243a3875f7d0b0962b0543a33ece6ff2f1f17" - integrity sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw== +"@babel/parser@^7.0.0", "@babel/parser@^7.1.0", "@babel/parser@^7.11.5", "@babel/parser@^7.12.13", "@babel/parser@^7.12.3", "@babel/parser@^7.16.7", "@babel/parser@^7.16.8", "@babel/parser@^7.18.10", "@babel/parser@^7.18.11", "@babel/parser@^7.7.0": + version "7.18.11" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9" + integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ== "@babel/plugin-proposal-async-generator-functions@^7.12.1": version "7.16.8" @@ -1035,21 +1101,21 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.10.4", "@babel/template@^7.16.7", "@babel/template@^7.3.3": - version "7.16.7" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" - integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w== +"@babel/template@^7.10.4", "@babel/template@^7.12.13", "@babel/template@^7.16.7", "@babel/template@^7.18.10", "@babel/template@^7.18.6", "@babel/template@^7.3.3": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" + integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/parser" "^7.16.7" - "@babel/types" "^7.16.7" + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.10" + "@babel/types" "^7.18.10" "@babel/traverse@7.12.13": version "7.12.13" @@ -1066,19 +1132,19 @@ globals "^11.1.0" lodash "^4.17.19" -"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.12.1", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.8.tgz#bab2f2b09a5fe8a8d9cad22cbfe3ba1d126fef9c" - integrity sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ== +"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.12.1", "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.18.10", "@babel/traverse@^7.18.9", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0": + version "7.18.11" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.11.tgz#3d51f2afbd83ecf9912bcbb5c4d94e3d2ddaa16f" + integrity sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ== dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.16.8" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.16.7" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.16.8" - "@babel/types" "^7.16.8" + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.10" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.18.11" + "@babel/types" "^7.18.10" debug "^4.1.0" globals "^11.1.0" @@ -1091,12 +1157,13 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" -"@babel/types@^7.0.0", "@babel/types@^7.11.5", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.12.6", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": - version "7.16.8" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.8.tgz#0ba5da91dd71e0a4e7781a30f22770831062e3c1" - integrity sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg== +"@babel/types@^7.0.0", "@babel/types@^7.11.5", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.12.6", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6" + integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ== dependencies: - "@babel/helper-validator-identifier" "^7.16.7" + "@babel/helper-string-parser" "^7.18.10" + "@babel/helper-validator-identifier" "^7.18.6" to-fast-properties "^2.0.0" "@bcoe/v8-coverage@^0.2.3": @@ -1139,6 +1206,25 @@ resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== +"@craco/craco@6.4.3": + version "6.4.3" + resolved "https://registry.yarnpkg.com/@craco/craco/-/craco-6.4.3.tgz#784395b6ebab764056550a2860494d24c3abd44e" + integrity sha512-RzkXYmNzRCGUyG7mM+IUMM+nvrpSfA34352sPSGQN76UivAmCAht3sI4v5JKgzO05oUK9Zwi6abCKD7iKXI8hQ== + dependencies: + cosmiconfig "^7.0.1" + cosmiconfig-typescript-loader "^1.0.0" + cross-spawn "^7.0.0" + lodash "^4.17.15" + semver "^7.3.2" + webpack-merge "^4.2.2" + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + "@csstools/convert-colors@^1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7" @@ -1212,9 +1298,9 @@ "@emotion/utils" "0.11.3" babel-plugin-emotion "^10.0.27" -"@emotion/hash@0.8.0": +"@emotion/hash@0.8.0", "@emotion/hash@^0.8.0": version "0.8.0" - resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== "@emotion/is-prop-valid@0.8.8", "@emotion/is-prop-valid@^0.8.1": @@ -1412,7 +1498,7 @@ "@ethersproject/transactions" "^5.6.2" "@ethersproject/web" "^5.6.1" -"@ethersproject/abstract-signer@5.6.2", "@ethersproject/abstract-signer@^5.6.2": +"@ethersproject/abstract-signer@5.6.2", "@ethersproject/abstract-signer@^5.5.0", "@ethersproject/abstract-signer@^5.6.2": version "5.6.2" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.6.2.tgz#491f07fc2cbd5da258f46ec539664713950b0b33" integrity sha512-n1r6lttFBG0t2vNiI3HoWaS/KdOt8xyDjzlP2cuevlWLG6EX0OwcKLyG/Kp/cuwNxdy/ous+R/DEMdTUwWQIjQ== @@ -1576,14 +1662,14 @@ "@ethersproject/bytes" "^5.6.1" "@ethersproject/sha2" "^5.6.1" -"@ethersproject/properties@5.6.0", "@ethersproject/properties@^5.6.0": +"@ethersproject/properties@5.6.0", "@ethersproject/properties@^5.5.0", "@ethersproject/properties@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.6.0.tgz#38904651713bc6bdd5bdd1b0a4287ecda920fa04" integrity sha512-szoOkHskajKePTJSZ46uHUWWkbv7TzP2ypdEK6jGMqJaEt2sb0jCgfBo0gH0m2HBpRixMuJ6TBRaQCF7a9DoCg== dependencies: "@ethersproject/logger" "^5.6.0" -"@ethersproject/providers@5.6.8", "@ethersproject/providers@^5": +"@ethersproject/providers@5.6.8", "@ethersproject/providers@^5", "@ethersproject/providers@^5.5.1": version "5.6.8" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.6.8.tgz#22e6c57be215ba5545d3a46cf759d265bb4e879d" integrity sha512-Wf+CseT/iOJjrGtAOf3ck9zS7AgPmr2fZ3N97r4+YXN3mBePTG2/bJ8DApl9mVwYL+RpYbNxMEkEp4mPGdwG/w== @@ -2835,6 +2921,54 @@ "@babel/runtime" "^7.7.2" regenerator-runtime "^0.13.3" +"@jridgewell/gen-mapping@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" + integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== + dependencies: + "@jridgewell/set-array" "^1.0.0" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/gen-mapping@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.14" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" + integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@json-rpc-tools/provider@^1.5.5": version "1.7.6" resolved "https://registry.yarnpkg.com/@json-rpc-tools/provider/-/provider-1.7.6.tgz#8a17c34c493fa892632e278fd9331104e8491ec6" @@ -2950,6 +3084,11 @@ "@babel/runtime" "^7.11.2" "@lingui/core" "^3.14.0" +"@looksrare/sdk@^0.7.1": + version "0.7.4" + resolved "https://registry.yarnpkg.com/@looksrare/sdk/-/sdk-0.7.4.tgz#f7b3d5d5a0d94bc5b12e8a29d380cb923f72ce9c" + integrity sha512-zmn0dM0Twe01CGriCNeAY2DV6K8xGZ2VcfSbDYjxbRvL74M0Ec0wgtqTt/6F6m7OwetcYzljhovF4TuZcvvhwA== + "@metamask/detect-provider@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@metamask/detect-provider/-/detect-provider-1.2.0.tgz#3667a7531f2a682e3c3a43eaf3a1958bdb42a696" @@ -3033,6 +3172,15 @@ mkdirp "^1.0.4" rimraf "^3.0.2" +"@opensea/seaport-js@^1.0.2": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@opensea/seaport-js/-/seaport-js-1.0.5.tgz#f48ebf5e3bc6ff5627502616598b401c6eaf352a" + integrity sha512-WgK3Grdqf5PEQWMlSFiSFaL/j8vlJ3feSjn+frFEO0QH2MuqwlFBRUGwqK4l09o5ZtZDRRTMafgrYC6BOgBY4A== + dependencies: + "@0xsequence/multicall" "^0.39.0" + ethers "^5.6.7" + merkletreejs "^0.2.31" + "@openzeppelin/contracts@3.4.1-solc-0.7-2": version "3.4.1-solc-0.7-2" resolved "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-3.4.1-solc-0.7-2.tgz" @@ -3614,6 +3762,26 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" + integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + "@typechain/ethers-v5@^7.0.0": version "7.0.1" resolved "https://registry.yarnpkg.com/@typechain/ethers-v5/-/ethers-v5-7.0.1.tgz#f9ae60ae5bd9e8ea8a996f66244147e8e74034ae" @@ -4356,6 +4524,11 @@ resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== +"@types/uuid@^8.3.4": + version "8.3.4" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" + integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== + "@types/warning@^3.0.0": version "3.0.0" resolved "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz" @@ -4746,6 +4919,77 @@ "@uniswap/v3-core" "1.0.0" "@uniswap/v3-periphery" "^1.0.1" +"@vanilla-extract/babel-plugin@^1.1.7": + version "1.1.7" + resolved "https://registry.yarnpkg.com/@vanilla-extract/babel-plugin/-/babel-plugin-1.1.7.tgz#b086875db3f66963223b7eb9e6a961796c041ef9" + integrity sha512-nTCOb1N/u1FUxACxV/jvpLm4xchiCdEHTUZrxCWjYOrIxqkfgpJXE4T7q/1VyEje/M929DFBUaD+YkPzaqGMzA== + dependencies: + "@babel/core" "^7.13.10" + "@babel/template" "^7.12.13" + "@vanilla-extract/integration" "^5.0.0" + +"@vanilla-extract/css-utils@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@vanilla-extract/css-utils/-/css-utils-0.1.2.tgz#6bcd3e45f187d56c319ce9bc36ae1814b373c581" + integrity sha512-qoxIu5E/UhJtoKsPL1JaU9nhTN0Xl5zYV0pScOgsvc3JN46TvTTt0L3GV8x3PQpZP7x3elw8BsC9czYbhJy9Gg== + +"@vanilla-extract/css@^1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@vanilla-extract/css/-/css-1.7.2.tgz#2317f391a1d912372742386c8f44f2fbde3232c3" + integrity sha512-wRsV9sSD4qN6+dbS1ywExw5kEU4Fk/UipNKFsGwwUsHLyuPKK8BaZUmzfyAQex6POHYQboYv6Ajbqpvhx2KhfQ== + dependencies: + "@emotion/hash" "^0.8.0" + "@vanilla-extract/private" "^1.0.3" + chalk "^4.1.1" + css-what "^5.0.1" + cssesc "^3.0.0" + csstype "^3.0.7" + deep-object-diff "^1.1.0" + deepmerge "^4.2.2" + escape-string-regexp "^4.0.0" + media-query-parser "^2.0.2" + outdent "^0.8.0" + +"@vanilla-extract/dynamic@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@vanilla-extract/dynamic/-/dynamic-2.0.2.tgz#13a3e461964c8029a52e6b6b631009ca6a8b27f5" + integrity sha512-U4nKaEQ8Kuz+exXEr51DUpyaOuzo24/S/k1YbDPQR06cYcNjQqvwFRnwWtZ+9ImocqM1wTKtzrdUgSTtLGIwAg== + dependencies: + "@vanilla-extract/private" "^1.0.3" + +"@vanilla-extract/integration@^5.0.0": + version "5.0.0" + resolved "https://registry.yarnpkg.com/@vanilla-extract/integration/-/integration-5.0.0.tgz#6652f5bf8effb8f29719f6df8cd754c2d16d5b04" + integrity sha512-HwglsIxGYtV4IXFfyQ6GzZLFoaWaW+QkNx8UhXbgsCWUoPqpSbioukCOA+SuSuzsIcEZ3hkD0Y5ixITQNtnzjQ== + dependencies: + "@vanilla-extract/css" "^1.7.2" + esbuild "^0.11.16" + eval "0.1.6" + find-up "^5.0.0" + javascript-stringify "^2.0.1" + lodash "^4.17.21" + outdent "^0.8.0" + +"@vanilla-extract/private@^1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@vanilla-extract/private/-/private-1.0.3.tgz#7ec72bc2ff6fe51f9d650f962e8d1989b073690f" + integrity sha512-17kVyLq3ePTKOkveHxXuIJZtGYs+cSoev7BlP+Lf4916qfDhk/HBjvlYDe8egrea7LNPHKwSZJK/bzZC+Q6AwQ== + +"@vanilla-extract/sprinkles@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@vanilla-extract/sprinkles/-/sprinkles-1.4.1.tgz#8c8703ddedaac355c1187db909119816c0fc771c" + integrity sha512-aW6CfMMToX4a+baLuVxwcT0FSACjX3xrNt8wdi/3LLRlLAfhyue8OK7kJxhcYNZfydBeWTP59aRy8p5FUTIeew== + +"@vanilla-extract/webpack-plugin@^2.1.11": + version "2.1.11" + resolved "https://registry.yarnpkg.com/@vanilla-extract/webpack-plugin/-/webpack-plugin-2.1.11.tgz#ca67aa95c0b1e3511e787dd715c0158dd4a6d1d3" + integrity sha512-jCm2TT1jSN/m0TZXYtByuHOsX+ZDimku1SJzvDWF1Eqs3f0If2W1zcd+gaXR6lSaVJ/1MSB/evmeutsT/D3MoA== + dependencies: + "@vanilla-extract/integration" "^5.0.0" + chalk "^4.1.1" + debug "^4.3.1" + loader-utils "^2.0.0" + "@vibrant/color@^3.2.1-alpha.1": version "3.2.1-alpha.1" resolved "https://registry.yarnpkg.com/@vibrant/color/-/color-3.2.1-alpha.1.tgz#1bcee4545d2276d36f9a1acb42ab3485a9b489ec" @@ -5443,6 +5687,11 @@ acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + acorn@^6.4.1: version "6.4.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" @@ -5453,10 +5702,10 @@ acorn@^7.1.0, acorn@^7.1.1, acorn@^7.4.0: resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.2.4: - version "8.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" - integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== +acorn@^8.2.4, acorn@^8.4.1: + version "8.8.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" + integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== address@1.1.2, address@^1.0.1: version "1.1.2" @@ -6292,6 +6541,11 @@ bfj@^7.0.2: hoopy "^0.1.4" tryer "^1.0.1" +big-integer@^1.6.16: + version "1.6.51" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -6302,6 +6556,11 @@ bignumber.js@^8.1.1: resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-8.1.1.tgz#4b072ae5aea9c20f6730e4e5d529df1271c4d885" integrity sha512-QD46ppGintwPGuL1KqmwhR0O+N2cZUg8JG/VzwI2e28sM9TqHjQB10lI4QAaMHVbLzwVLLAwEglpKPViWX+5NQ== +bignumber.js@^9.0.1: + version "9.1.0" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.0.tgz#8d340146107fe3a6cb8d40699643c302e8773b62" + integrity sha512-4LwHK4nfDOraBCtst+wOWIHbu1vhvAPJK8g8nROd4iuc3PSEjWif/qwbkh8jwCJz6yDBvtU4KPynETgrfh7y3A== + binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" @@ -6353,6 +6612,11 @@ bmp-js@^0.1.0: resolved "https://registry.npmjs.org/bmp-js/-/bmp-js-0.1.0.tgz" integrity sha1-4Fpj95amwf8l9Hcex62twUjAcjM= +bn.js@4.11.6: + version "4.11.6" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215" + integrity sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA== + bn.js@4.11.8: version "4.11.8" resolved "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz" @@ -6452,6 +6716,20 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" +broadcast-channel@^3.4.1: + version "3.7.0" + resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.7.0.tgz#2dfa5c7b4289547ac3f6705f9c00af8723889937" + integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg== + dependencies: + "@babel/runtime" "^7.7.2" + detect-node "^2.1.0" + js-sha3 "0.8.0" + microseconds "0.2.0" + nano-time "1.0.0" + oblivious-set "1.0.0" + rimraf "3.0.2" + unload "2.2.0" + brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" @@ -6538,16 +6816,15 @@ browserslist@4.14.2: escalade "^3.0.2" node-releases "^1.1.61" -browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.17.5, browserslist@^4.19.1, browserslist@^4.6.2, browserslist@^4.6.4: - version "4.19.1" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3" - integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A== +browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.17.5, browserslist@^4.19.1, browserslist@^4.20.2, browserslist@^4.6.2, browserslist@^4.6.4: + version "4.21.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a" + integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ== dependencies: - caniuse-lite "^1.0.30001286" - electron-to-chromium "^1.4.17" - escalade "^3.1.1" - node-releases "^2.0.1" - picocolors "^1.0.0" + caniuse-lite "^1.0.30001370" + electron-to-chromium "^1.4.202" + node-releases "^2.0.6" + update-browserslist-db "^1.0.5" bs58@^4.0.0: version "4.0.1" @@ -6620,6 +6897,11 @@ buffer-indexof@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== +buffer-reverse@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-reverse/-/buffer-reverse-1.0.1.tgz#49283c8efa6f901bc01fa3304d06027971ae2f60" + integrity sha512-M87YIUBsZ6N924W57vDwT/aOu8hw7ZgdByz6ijksLjmHJELBASmYTTlNHRgjE+pTsT9oJXGaDSgqqwfdHotDUg== + buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" @@ -6854,10 +7136,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001286: - version "1.0.30001366" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001366.tgz" - integrity sha512-yy7XLWCubDobokgzudpkKux8e0UOOnLHE6mlNJBzT3lZJz6s5atSEzjoL+fsCPkI0G8MP5uVdDx1ur/fXEWkZA== +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001286, caniuse-lite@^1.0.30001370: + version "1.0.30001375" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001375.tgz#8e73bc3d1a4c800beb39f3163bf0190d7e5d7672" + integrity sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw== capital-case@^1.0.4: version "1.0.4" @@ -6922,7 +7204,7 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^4.0.0, chalk@^4.1.0: +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -7236,10 +7518,10 @@ clone@^1.0.2: resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz" integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= -clsx@^1.1.0: - version "1.1.1" - resolved "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz" - integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA== +clsx@^1.1.0, clsx@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" + integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== co@^4.6.0: version "4.6.0" @@ -7597,7 +7879,15 @@ cosmiconfig-toml-loader@1.0.0: dependencies: "@iarna/toml" "^2.2.5" -cosmiconfig@7.0.0, cosmiconfig@^7.0.0: +cosmiconfig-typescript-loader@^1.0.0: + version "1.0.9" + resolved "https://registry.yarnpkg.com/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-1.0.9.tgz#69c523f7e8c3d9f27f563d02bbeadaf2f27212d3" + integrity sha512-tRuMRhxN4m1Y8hP9SNYfz7jRwt8lZdWxdjg/ohg5esKmsndJIn4yT96oJVcf5x0eA11taXl+sIp+ielu529k6g== + dependencies: + cosmiconfig "^7" + ts-node "^10.7.0" + +cosmiconfig@7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz" integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== @@ -7629,6 +7919,17 @@ cosmiconfig@^6.0.0: path-type "^4.0.0" yaml "^1.7.2" +cosmiconfig@^7, cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + crc-32@^1.2.0: version "1.2.2" resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff" @@ -7737,6 +8038,11 @@ crypto-browserify@^3.11.0: randombytes "^2.0.0" randomfill "^1.0.3" +crypto-js@^3.1.9-1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b" + integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q== + crypto-random-string@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" @@ -7856,10 +8162,10 @@ css-what@^3.2.1: resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== -css-what@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad" - integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg== +css-what@^5.0.0, css-what@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe" + integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw== css.escape@^1.5.1: version "1.5.1" @@ -7997,10 +8303,10 @@ csstype@^2.5.7: resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz" integrity sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A== -csstype@^3.0.2: - version "3.0.8" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340" - integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw== +csstype@^3.0.2, csstype@^3.0.7: + version "3.1.0" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" + integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA== cyclist@^1.0.1: version "1.0.1" @@ -8511,6 +8817,11 @@ deep-is@^0.1.3, deep-is@~0.1.3: resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +deep-object-diff@^1.1.0: + version "1.1.7" + resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.7.tgz#348b3246f426427dd633eaa50e1ed1fc2eafc7e4" + integrity sha512-QkgBca0mL08P6HiOjoqvmm6xOAl2W6CT2+34Ljhg0OeFan8cwlcdq8jrLKsBBuUFAZLsN5b6y491KdKEoSo9lg== + deepmerge@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" @@ -8647,7 +8958,7 @@ detect-node-es@^1.1.0: resolved "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz" integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ== -detect-node@^2.0.4: +detect-node@^2.0.4, detect-node@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== @@ -8911,10 +9222,10 @@ ejs@^2.6.1: resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba" integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA== -electron-to-chromium@^1.3.564, electron-to-chromium@^1.4.17: - version "1.4.44" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.44.tgz#8a41923afdd6ef5ddabe001626036ba5d1d64ae6" - integrity sha512-tHGWiUUmY7GABK8+DNcr474cnZDTzD8x1736SlDosVH8+/vRJeqfaIBAEHFtMjddz/0T4rKKYsxEc8BwQRdBpw== +electron-to-chromium@^1.3.564, electron-to-chromium@^1.4.17, electron-to-chromium@^1.4.202: + version "1.4.213" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.213.tgz#a0d0f535e4fbddc25196c91ff2964b5660932297" + integrity sha512-+3DbGHGOCHTVB/Ms63bGqbyC1b8y7Fk86+7ltssB8NQrZtSCvZG6eooSl9U2Q0yw++fL2DpHKOdTU0NVEkFObg== elegant-spinner@^1.0.1: version "1.0.1" @@ -9105,6 +9416,11 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.3: d "^1.0.1" ext "^1.1.2" +esbuild@^0.11.16: + version "0.11.23" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.11.23.tgz#c42534f632e165120671d64db67883634333b4b8" + integrity sha512-iaiZZ9vUF5wJV8ob1tl+5aJTrwDczlvGP0JoMmnpC2B0ppiMCu8n8gmy5ZTGl5bcG081XBVn+U+jP+mPFm5T5Q== + escalade@^3.0.2, escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -9519,6 +9835,13 @@ eth-sig-util@^1.4.2: ethereumjs-abi "git+https://github.com/ethereumjs/ethereumjs-abi.git" ethereumjs-util "^5.1.1" +ethereum-bloom-filters@^1.0.6: + version "1.0.10" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz#3ca07f4aed698e75bd134584850260246a5fed8a" + integrity sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA== + dependencies: + js-sha3 "^0.8.0" + ethereum-cryptography@^0.1.3: version "0.1.3" resolved "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz" @@ -9583,7 +9906,7 @@ ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1: ethjs-util "0.1.6" rlp "^2.2.3" -ethereumjs-util@^7.1.1, ethereumjs-util@^7.1.4, ethereumjs-util@^7.1.5: +ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.1, ethereumjs-util@^7.1.4, ethereumjs-util@^7.1.5: version "7.1.5" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== @@ -9594,7 +9917,7 @@ ethereumjs-util@^7.1.1, ethereumjs-util@^7.1.4, ethereumjs-util@^7.1.5: ethereum-cryptography "^0.1.3" rlp "^2.2.4" -ethers@^5.1.4, ethers@^5.6.0, ethers@^5.6.1, ethers@^5.6.8: +ethers@^5.1.4, ethers@^5.5.2, ethers@^5.6.0, ethers@^5.6.1, ethers@^5.6.7, ethers@^5.6.8: version "5.6.9" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.6.9.tgz#4e12f8dfcb67b88ae7a78a9519b384c23c576a4d" integrity sha512-lMGC2zv9HC5EC+8r429WaWu3uWJUCgUCt8xxKCFqkrFuBDZXDYIdzDUECxzjf2BMF8IVBByY1EBoGSL3RTm8RA== @@ -9630,6 +9953,14 @@ ethers@^5.1.4, ethers@^5.6.0, ethers@^5.6.1, ethers@^5.6.8: "@ethersproject/web" "5.6.1" "@ethersproject/wordlists" "5.6.1" +ethjs-unit@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" + integrity sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw== + dependencies: + bn.js "4.11.6" + number-to-bn "1.7.0" + ethjs-util@0.1.6, ethjs-util@^0.1.3, ethjs-util@^0.1.6: version "0.1.6" resolved "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz" @@ -9638,6 +9969,13 @@ ethjs-util@0.1.6, ethjs-util@^0.1.3, ethjs-util@^0.1.6: is-hex-prefixed "1.0.0" strip-hex-prefix "1.0.0" +eval@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/eval/-/eval-0.1.6.tgz#9620d7d8c85515e97e6b47c5814f46ae381cb3cc" + integrity sha512-o0XUw+5OGkXw4pJZzQoXUk+H87DHuC+7ZE//oSrRGtatTmr12oTnLfg6QOq9DyTt0c/p4TwzgmkKrBzWTSizyQ== + dependencies: + require-like ">= 0.1.1" + event-target-shim@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" @@ -10113,7 +10451,7 @@ find-up@4.1.0, find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -find-up@5.0.0: +find-up@5.0.0, find-up@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== @@ -10205,6 +10543,11 @@ focus-lock@^0.9.1: dependencies: tslib "^2.0.3" +focus-visible@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/focus-visible/-/focus-visible-5.2.0.tgz#3a9e41fccf587bd25dcc2ef045508284f0a4d6b3" + integrity sha512-Rwix9pBtC1Nuy5wysTmKy+UjbDJpIfg8eHjw0rjZ1mX4GNLz1Bmd16uDpI3Gk1i70Fgcs8Csg2lPm8HULFg9DQ== + follow-redirects@^1.0.0, follow-redirects@^1.10.0, follow-redirects@^1.12.1: version "1.15.1" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" @@ -10411,7 +10754,7 @@ fuzzaldrin@^2.1.0: resolved "https://registry.npmjs.org/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz" integrity sha1-kCBMPi/appQbso0WZF1BgGOpDps= -gensync@^1.0.0-beta.1: +gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== @@ -12008,6 +12351,11 @@ iterall@^1.2.1: resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.3.0.tgz#afcb08492e2915cbd8a0884eb93a8c94d0d72fea" integrity sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg== +javascript-stringify@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" + integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== + jest-changed-files@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" @@ -12475,6 +12823,11 @@ jpeg-js@0.4.2: resolved "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.2.tgz" integrity sha512-+az2gi/hvex7eLTMTlbRLOhH6P6WFdk2ITI8HJsaH2VqYO0I594zXSYEP+tf4FW+8Cy68ScDXoAsQdyQanv3sw== +js-base64@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.2.tgz#816d11d81a8aff241603d19ce5761e13e41d7745" + integrity sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ== + js-sha256@0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" @@ -12646,12 +12999,10 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.1.2, json5@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" +json5@^2.1.2, json5@^2.2.0, json5@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" + integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== jsonfile@^2.1.0: version "2.4.0" @@ -13356,6 +13707,14 @@ markdown-escapes@^1.0.0: resolved "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz" integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== +match-sorter@^6.0.2: + version "6.3.1" + resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.1.tgz#98cc37fda756093424ddf3cbc62bfe9c75b92bda" + integrity sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw== + dependencies: + "@babel/runtime" "^7.12.5" + remove-accents "0.4.2" + math-expression-evaluator@^1.2.14: version "1.4.0" resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.4.0.tgz#3d66031117fbb7b9715ea6c9c68c2cd2eebd37e2" @@ -13392,6 +13751,13 @@ mdn-data@2.0.4: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== +media-query-parser@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/media-query-parser/-/media-query-parser-2.0.2.tgz#ff79e56cee92615a304a1c2fa4f2bd056c0a1d29" + integrity sha512-1N4qp+jE0pL5Xv4uEcwVUhIkwdUO3S/9gML90nqKA7v7FcOS5vUtatfzok9S9U1EJU8dHWlcv95WLnKmmxZI9w== + dependencies: + "@babel/runtime" "^7.12.5" + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -13467,6 +13833,17 @@ merkle-patricia-tree@^4.2.4: readable-stream "^3.6.0" semaphore-async-await "^1.5.1" +merkletreejs@^0.2.31: + version "0.2.32" + resolved "https://registry.yarnpkg.com/merkletreejs/-/merkletreejs-0.2.32.tgz#cf1c0760e2904e4a1cc269108d6009459fd06223" + integrity sha512-TostQBiwYRIwSE5++jGmacu3ODcKAgqb0Y/pnIohXS7sWxh1gCkSptbmF1a43faehRDpcHf7J/kv0Ml2D/zblQ== + dependencies: + bignumber.js "^9.0.1" + buffer-reverse "^1.0.1" + crypto-js "^3.1.9-1" + treeify "^1.1.0" + web3-utils "^1.3.4" + meros@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/meros/-/meros-1.1.4.tgz#c17994d3133db8b23807f62bec7f0cb276cfd948" @@ -13527,6 +13904,11 @@ micromatch@^4.0.2, micromatch@^4.0.4: braces "^3.0.1" picomatch "^2.2.3" +microseconds@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39" + integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA== + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -13843,6 +14225,13 @@ nan@^2.12.1, nan@^2.14.0: resolved "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz" integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== +nano-time@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef" + integrity sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA== + dependencies: + big-integer "^1.6.16" + nanocolors@^0.2.2: version "0.2.12" resolved "https://registry.yarnpkg.com/nanocolors/-/nanocolors-0.2.12.tgz#4d05932e70116078673ea4cc6699a1c56cc77777" @@ -14019,10 +14408,10 @@ node-releases@^1.1.61: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.76.tgz#df245b062b0cafbd5282ab6792f7dccc2d97f36e" integrity sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA== -node-releases@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" - integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== +node-releases@^2.0.1, node-releases@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" + integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== node-vibrant@^3.2.1-alpha.1: version "3.2.1-alpha.1" @@ -14127,6 +14516,14 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= +number-to-bn@1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" + integrity sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig== + dependencies: + bn.js "4.11.6" + strip-hex-prefix "1.0.0" + numbro@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/numbro/-/numbro-2.3.6.tgz#4bd622ebe59ccbc49dad365c5b9eed200781fa21" @@ -14237,6 +14634,11 @@ obliterator@^2.0.0: resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ== +oblivious-set@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566" + integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw== + obuf@^1.0.0, obuf@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" @@ -14369,6 +14771,11 @@ ospath@^1.2.2: resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b" integrity sha1-EnZjl3Sj+O8lcvf+QoDg6kVQwHs= +outdent@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/outdent/-/outdent-0.8.0.tgz#2ebc3e77bf49912543f1008100ff8e7f44428eb0" + integrity sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A== + p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -16033,6 +16440,15 @@ react-popper@^2.2.3: react-fast-compare "^3.0.1" warning "^4.0.2" +react-query@^3.39.1: + version "3.39.2" + resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.39.2.tgz#9224140f0296f01e9664b78ed6e4f69a0cc9216f" + integrity sha512-F6hYDKyNgDQfQOuR1Rsp3VRzJnWHx6aRnnIZHMNGGgbL3SBgpZTDg8MQwmxOgpCAoqZJA+JSNCydF1xGJqKOCA== + dependencies: + "@babel/runtime" "^7.5.5" + broadcast-channel "^3.4.1" + match-sorter "^6.0.2" + react-redux@^8.0.2: version "8.0.2" resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.0.2.tgz#bc2a304bb21e79c6808e3e47c50fe1caf62f7aad" @@ -16167,6 +16583,11 @@ react-style-singleton@^2.1.0: invariant "^2.2.4" tslib "^1.0.0" +react-table@^7.8.0: + version "7.8.0" + resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.8.0.tgz#07858c01c1718c09f7f1aed7034fcfd7bda907d2" + integrity sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA== + react-use-gesture@^6.0.14: version "6.0.14" resolved "https://registry.npmjs.org/react-use-gesture/-/react-use-gesture-6.0.14.tgz" @@ -16518,6 +16939,11 @@ remedial@^1.0.7: resolved "https://registry.yarnpkg.com/remedial/-/remedial-1.0.8.tgz#a5e4fd52a0e4956adbaf62da63a5a46a78c578a0" integrity sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg== +remove-accents@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5" + integrity sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA== + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -16576,6 +17002,11 @@ require-from-string@^2.0.0, require-from-string@^2.0.2: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== +"require-like@>= 0.1.1": + version "0.1.2" + resolved "https://registry.yarnpkg.com/require-like/-/require-like-0.1.2.tgz#ad6f30c13becd797010c468afa775c0c0a6b47fa" + integrity sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A== + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -16743,6 +17174,13 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= +rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -16750,13 +17188,6 @@ rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.3: dependencies: glob "^7.1.3" -rimraf@^3.0.0, rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - rimraf@~2.4.0: version "2.4.5" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" @@ -18294,6 +18725,11 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= +treeify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" + integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== + trim-trailing-lines@^1.0.0: version "1.1.4" resolved "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz" @@ -18329,6 +18765,25 @@ ts-log@^2.2.3: resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.2.3.tgz#4da5640fe25a9fb52642cd32391c886721318efb" integrity sha512-XvB+OdKSJ708Dmf9ore4Uf/q62AYDTzFcAdxc8KNML1mmAWywRFVt/dn1KYJH8Agt5UJNujfM3znU5PxgAzA2w== +ts-node@^10.7.0: + version "10.9.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + ts-node@^9: version "9.1.1" resolved "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz" @@ -18703,6 +19158,14 @@ unixify@1.0.0: dependencies: normalize-path "^2.1.1" +unload@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7" + integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA== + dependencies: + "@babel/runtime" "^7.6.2" + detect-node "^2.0.4" + unpipe@1.0.0, unpipe@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -18731,6 +19194,14 @@ upath@^1.1.1, upath@^1.1.2, upath@^1.2.0: resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== +update-browserslist-db@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" + integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + update-check@1.5.2: version "1.5.2" resolved "https://registry.npmjs.org/update-check/-/update-check-1.5.2.tgz" @@ -18829,16 +19300,26 @@ use-sidecar@^1.0.1: detect-node-es "^1.1.0" tslib "^1.9.3" -use-sync-external-store@1.1.0, use-sync-external-store@^1.0.0: +use-sync-external-store@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.1.0.tgz#3343c3fe7f7e404db70f8c687adf5c1652d34e82" integrity sha512-SEnieB2FPKEVne66NpXPd1Np4R1lTNKfjuy3XdIoPQKYBAFdzbzSZlSn1KJZUiihQLQC5Znot4SBz1EOTBwQAQ== +use-sync-external-store@1.2.0, use-sync-external-store@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== +utf8@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1" + integrity sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ== + utif@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/utif/-/utif-2.0.1.tgz" @@ -18905,6 +19386,11 @@ uuid@^8.3.0, uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" @@ -18983,6 +19469,11 @@ vfile@^2.0.0: unist-util-stringify-position "^1.0.0" vfile-message "^1.0.0" +video-extensions@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/video-extensions/-/video-extensions-1.2.0.tgz#62f449f403b853f02da40964cbf34143f7d96731" + integrity sha512-TriMl18BHEsh2KuuSA065tbu4SNAC9fge7k8uKoTTofTq89+Xsg4K1BGbmSVETwUZhqSjd9KwRCNwXAW/buXMg== + vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" @@ -19060,6 +19551,19 @@ web-vitals@^2.1.0: resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-2.1.0.tgz#ebf5428875ab5bfc1056c2e80cd177001287de7b" integrity sha512-npEyJP8jHf3J71t1tRTEtz9FeKp8H2udWJUUq5ykfPhhstr//TUxiYhIEzLNwk4zv2ybAilMn7v7N6Mxmuitmg== +web3-utils@^1.3.4: + version "1.7.5" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.7.5.tgz#081a952ac6e0322e25ac97b37358a43c7372ef6a" + integrity sha512-9AqNOziQky4wNQadEwEfHiBdOZqopIHzQQVzmvvv6fJwDSMhP+khqmAZC7YTiGjs0MboyZ8tWNivqSO1699XQw== + dependencies: + bn.js "^5.2.1" + ethereum-bloom-filters "^1.0.6" + ethereumjs-util "^7.1.0" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -19143,6 +19647,13 @@ webpack-manifest-plugin@2.2.0: object.entries "^1.1.0" tapable "^1.0.0" +webpack-merge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + webpack-sources@^1.1.0, webpack-sources@^1.3.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" @@ -19767,9 +20278,9 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zustand@^4.0.0-rc.0: - version "4.0.0-rc.1" - resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.0.0-rc.1.tgz#ec30a3afc03728adec7e1bd7bcc3592176372201" - integrity sha512-qgcs7zLqBdHu0PuT3GW4WCIY5SgXdsv30GQMu9Qpp1BA2aS+sNS8l4x0hWuyEhjXkN+701aGWawhKDv6oWJAcw== +zustand@^4.0.0-rc.0, zustand@^4.0.0-rc.1: + version "4.0.0" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.0.0.tgz#739cba69209ffe67b31e7d6741c25b51496114a7" + integrity sha512-OrsfQTnRXF1LZ9/vR/IqN9ws5EXUhb149xmPjErZnUrkgxS/gAHGy2dPNIVkVvoxrVe1sIydn4JjF0dYHmGeeQ== dependencies: - use-sync-external-store "1.1.0" + use-sync-external-store "1.2.0"