remove unused functions, typescript (#725)
* TypeScript in the utils directory, README fixes * Remove unused methods * Added unnecessary space * Some env variable cleanup * Bug in change * Another file to ts
This commit is contained in:
parent
49462fdb3b
commit
b6b094acb3
@ -1,5 +1,4 @@
|
||||
REACT_APP_CHAIN_ID="1"
|
||||
REACT_APP_NETWORK_URL=""
|
||||
REACT_APP_PORTIS_ID=""
|
||||
REACT_APP_FORTMATIC_KEY=""
|
||||
REACT_APP_IS_PRODUCTION_DEPLOY="false"
|
||||
REACT_APP_FORTMATIC_KEY=""
|
10
README.md
10
README.md
@ -6,11 +6,11 @@
|
||||
|
||||
This an an open source interface for Uniswap - a protocol for decentralized exchange of Ethereum tokens.
|
||||
|
||||
- Website: [uniswap.io](https://uniswap.io/)
|
||||
- Docs: [docs.uniswap.io](https://docs.uniswap.io/)
|
||||
- Twitter: [@UniswapExchange](https://twitter.com/UniswapExchange)
|
||||
- Reddit: [/r/Uniswap](https://www.reddit.com/r/UniSwap/)
|
||||
- Email: [contact@uniswap.io](mailto:contact@uniswap.io)
|
||||
- Website: [uniswap.org](https://uniswap.org/)
|
||||
- Docs: [uniswap.org/docs/](https://uniswap.org/docs/)
|
||||
- Twitter: [@UniswapProtocol](https://twitter.com/UniswapProtocol)
|
||||
- Reddit: [/r/Uniswap](https://www.reddit.com/r/Uniswap/)
|
||||
- Email: [contact@uniswap.org](mailto:contact@uniswap.org)
|
||||
- Discord: [Uniswap](https://discord.gg/Y7TF6QA)
|
||||
- Whitepaper: [Link](https://hackmd.io/C-DvwDSfSxuh-Gd4WKE_ig)
|
||||
|
||||
|
@ -14,7 +14,11 @@
|
||||
status = 200
|
||||
|
||||
[build.environment]
|
||||
REACT_APP_IS_PRODUCTION_DEPLOY = "false"
|
||||
REACT_APP_GOOGLE_ANALYTICS_ID = "UA-128182339-1"
|
||||
REACT_APP_NETWORK_URL = "https://mainnet.infura.io/v3/b8800ce81b8c451698081d269b86692b"
|
||||
|
||||
[context.production.environment]
|
||||
REACT_APP_IS_PRODUCTION_DEPLOY = "true"
|
||||
REACT_APP_CHAIN_ID = "1"
|
||||
REACT_APP_NETWORK_URL = "https://mainnet.infura.io/v3/2acb2baa4c06402792e0c701a3697d10"
|
||||
REACT_APP_FORTMATIC_KEY = "pk_live_F937DF033A1666BF"
|
||||
REACT_APP_PORTIS_ID = "c0e2bf01-4b08-4fd5-ac7b-8e26b58cd236"
|
||||
|
10
package.json
10
package.json
@ -13,7 +13,7 @@
|
||||
"@types/node": "^13.7.4",
|
||||
"@types/react": "^16.9.21",
|
||||
"@types/react-dom": "^16.9.5",
|
||||
"@uniswap/sdk": "^2.0.1",
|
||||
"@uniswap/sdk": "^2.0.3",
|
||||
"@uniswap/v2-core": "1.0.0",
|
||||
"@uniswap/v2-periphery": "1.0.0-beta.0",
|
||||
"@web3-react/core": "^6.0.2",
|
||||
@ -32,6 +32,7 @@
|
||||
"i18next-xhr-backend": "^2.0.1",
|
||||
"jazzicon": "^1.5.0",
|
||||
"polished": "^3.3.2",
|
||||
"prettier": "^1.17.0",
|
||||
"qrcode.react": "^0.9.3",
|
||||
"react": "^16.8.6",
|
||||
"react-device-detect": "^1.6.2",
|
||||
@ -78,8 +79,5 @@
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"license": "GPL-3.0-or-later",
|
||||
"devDependencies": {
|
||||
"prettier": "^1.17.0"
|
||||
}
|
||||
}
|
||||
"license": "GPL-3.0-or-later"
|
||||
}
|
@ -595,7 +595,7 @@ function ExchangePage({ sendingInput = false, history, params }) {
|
||||
// get token contract if needed
|
||||
let estimate: Function, method: Function, args, value
|
||||
if (tokens[Field.INPUT] === WETH[chainId]) {
|
||||
signer
|
||||
(signer as any)
|
||||
.sendTransaction({ to: recipient.toString(), value: hex(parsedAmounts[Field.INPUT].raw) })
|
||||
.then(response => {
|
||||
setTxHash(response.hash)
|
||||
|
@ -7,10 +7,7 @@ import { NetworkConnector } from './Network'
|
||||
import { FortmaticConnector } from './Fortmatic'
|
||||
|
||||
const POLLING_INTERVAL = 10000
|
||||
const NETWORK_URL =
|
||||
process.env.REACT_APP_IS_PRODUCTION_DEPLOY === 'true'
|
||||
? process.env.REACT_APP_NETWORK_URL_PROD
|
||||
: process.env.REACT_APP_NETWORK_URL
|
||||
const NETWORK_URL = process.env.REACT_APP_NETWORK_URL
|
||||
|
||||
export const network = new NetworkConnector({
|
||||
urls: { [Number(process.env.REACT_APP_CHAIN_ID)]: NETWORK_URL },
|
||||
|
@ -3,6 +3,7 @@ import ReactDOM from 'react-dom'
|
||||
import ReactGA from 'react-ga'
|
||||
import { Web3ReactProvider, createWeb3ReactRoot } from '@web3-react/core'
|
||||
import { ethers } from 'ethers'
|
||||
import { Web3Provider } from 'ethers/providers'
|
||||
|
||||
import { NetworkContextName } from './constants'
|
||||
import { isMobile } from 'react-device-detect'
|
||||
@ -19,19 +20,19 @@ import './i18n'
|
||||
|
||||
const Web3ProviderNetwork = createWeb3ReactRoot(NetworkContextName)
|
||||
|
||||
function getLibrary(provider) {
|
||||
function getLibrary(provider): Web3Provider {
|
||||
const library = new ethers.providers.Web3Provider(provider)
|
||||
library.pollingInterval = 10000
|
||||
return library
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
ReactGA.initialize('UA-128182339-1')
|
||||
ReactGA.initialize(process.env.REACT_APP_GOOGLE_ANALYTICS_ID)
|
||||
ReactGA.set({
|
||||
customBrowserType: !isMobile ? 'desktop' : window.web3 || window.ethereum ? 'mobileWeb3' : 'mobileRegular'
|
||||
customBrowserType: !isMobile ? 'desktop' : 'web3' in window || 'ethereum' in window ? 'mobileWeb3' : 'mobileRegular'
|
||||
})
|
||||
} else {
|
||||
ReactGA.initialize('test', { testMode: true })
|
||||
ReactGA.initialize('test', { testMode: true, debug: true })
|
||||
}
|
||||
|
||||
ReactGA.pageview(window.location.pathname + window.location.search)
|
||||
@ -57,9 +58,9 @@ function ContextProviders({ children }) {
|
||||
function Updaters() {
|
||||
return (
|
||||
<>
|
||||
<ApplicationContextUpdater />
|
||||
<TransactionContextUpdater />
|
||||
<BalancesContextUpdater />
|
||||
<ApplicationContextUpdater/>
|
||||
<TransactionContextUpdater/>
|
||||
<BalancesContextUpdater/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -68,11 +69,11 @@ ReactDOM.render(
|
||||
<Web3ReactProvider getLibrary={getLibrary}>
|
||||
<Web3ProviderNetwork getLibrary={getLibrary}>
|
||||
<ContextProviders>
|
||||
<Updaters />
|
||||
<Updaters/>
|
||||
<ThemeProvider>
|
||||
<>
|
||||
<GlobalStyle />
|
||||
<App />
|
||||
<GlobalStyle/>
|
||||
<App/>
|
||||
</>
|
||||
</ThemeProvider>
|
||||
</ContextProviders>
|
@ -1,30 +1,27 @@
|
||||
import { ethers } from 'ethers'
|
||||
|
||||
import IUniswapV2Pair from '@uniswap/v2-core/build/IUniswapV2Pair.json'
|
||||
import IUniswapV2Router01 from '@uniswap/v2-periphery/build/IUniswapV2Router01.json'
|
||||
|
||||
import ERC20_ABI from '../constants/abis/erc20'
|
||||
import ERC20_BYTES32_ABI from '../constants/abis/erc20_bytes32'
|
||||
import { SUPPORTED_THEMES, ROUTER_ADDRESS } from '../constants'
|
||||
import { bigNumberify, keccak256, defaultAbiCoder, toUtf8Bytes, solidityPack } from 'ethers/utils'
|
||||
|
||||
import UncheckedJsonRpcSigner from './signer'
|
||||
import { WETH } from '@uniswap/sdk'
|
||||
|
||||
export const ERROR_CODES = ['TOKEN_NAME', 'TOKEN_SYMBOL', 'TOKEN_DECIMALS'].reduce(
|
||||
(accumulator, currentValue, currentIndex) => {
|
||||
accumulator[currentValue] = currentIndex
|
||||
return accumulator
|
||||
},
|
||||
{}
|
||||
)
|
||||
import {abi as IUniswapV2PairABI} from '@uniswap/v2-core/build/IUniswapV2Pair.json'
|
||||
import { abi as IUniswapV2Router01ABI } from '@uniswap/v2-periphery/build/IUniswapV2Router01.json'
|
||||
import { Contract, ethers, Signer } from 'ethers'
|
||||
import { JsonRpcProvider, Provider } from 'ethers/providers'
|
||||
import { ROUTER_ADDRESS, SUPPORTED_THEMES } from '../constants'
|
||||
|
||||
import ERC20_ABI from '../constants/abis/erc20.json'
|
||||
import ERC20_BYTES32_ABI from '../constants/abis/erc20_bytes32.json'
|
||||
|
||||
import UncheckedJsonRpcSigner from './signer'
|
||||
|
||||
export enum ERROR_CODES {
|
||||
TOKEN_SYMBOL = 1,
|
||||
TOKEN_DECIMALS = 2
|
||||
}
|
||||
|
||||
export function safeAccess(object, path) {
|
||||
return object
|
||||
? path.reduce(
|
||||
(accumulator, currentValue) => (accumulator && accumulator[currentValue] ? accumulator[currentValue] : null),
|
||||
object
|
||||
)
|
||||
(accumulator, currentValue) => (accumulator && accumulator[currentValue] ? accumulator[currentValue] : null),
|
||||
object
|
||||
)
|
||||
: null
|
||||
}
|
||||
|
||||
@ -55,26 +52,36 @@ export function getQueryParam(windowLocation, name) {
|
||||
return q && q[1]
|
||||
}
|
||||
|
||||
export function getAllQueryParams() {
|
||||
let params = {}
|
||||
function parseUrlAddress(param: string): string {
|
||||
const addr = isAddress(getQueryParam(window.location, param))
|
||||
if (addr === false) {
|
||||
return ''
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
params.inputTokenAddress = isAddress(getQueryParam(window.location, 'inputTokenAddress'))
|
||||
? isAddress(getQueryParam(window.location, 'inputTokenAddress'))
|
||||
: ''
|
||||
function parseUrlTokenAmount(paramName: string): string {
|
||||
const value = getQueryParam(window.location, paramName)
|
||||
if (!isNaN(Number(value))) {
|
||||
return ''
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
params.outputTokenAddress = isAddress(getQueryParam(window.location, 'outputTokenAddress'))
|
||||
? isAddress(getQueryParam(window.location, 'outputTokenAddress'))
|
||||
: ''
|
||||
interface QueryParams {
|
||||
readonly inputTokenAddress: string
|
||||
readonly outputTokenAddress: string
|
||||
readonly inputTokenAmount: string
|
||||
readonly outputTokenAmount: string
|
||||
}
|
||||
|
||||
params.inputTokenAmount = !isNaN(Number(getQueryParam(window.location, 'inputTokenAmount')))
|
||||
? getQueryParam(window.location, 'inputTokenAmount')
|
||||
: ''
|
||||
|
||||
params.outputTokenAmount = !isNaN(Number(getQueryParam(window.location, 'outputTokenAmount')))
|
||||
? getQueryParam(window.location, 'outputTokenAmount')
|
||||
: ''
|
||||
|
||||
return params
|
||||
export function getAllQueryParams(): QueryParams {
|
||||
return {
|
||||
inputTokenAddress: parseUrlAddress('inputTokenAddress'),
|
||||
outputTokenAddress: parseUrlAddress('outputTokenAddress'),
|
||||
inputTokenAmount: parseUrlTokenAmount('inputTokenAmount'),
|
||||
outputTokenAmount: parseUrlTokenAmount('outputTokenAmount'),
|
||||
}
|
||||
}
|
||||
|
||||
export function checkSupportedTheme(themeName) {
|
||||
@ -118,7 +125,7 @@ export function shortenTransactionHash(hash, digits = 4) {
|
||||
return `${hash.substring(0, digits + 2)}...${hash.substring(66 - digits)}`
|
||||
}
|
||||
|
||||
export function isAddress(value) {
|
||||
export function isAddress(value): string | false {
|
||||
try {
|
||||
return ethers.utils.getAddress(value.toLowerCase())
|
||||
} catch {
|
||||
@ -135,12 +142,12 @@ export function calculateGasMargin(value, margin) {
|
||||
}
|
||||
|
||||
// account is optional
|
||||
export function getProviderOrSigner(library, account) {
|
||||
export function getProviderOrSigner(library: JsonRpcProvider, account?: string): Signer | Provider {
|
||||
return account ? new UncheckedJsonRpcSigner(library.getSigner(account)) : library
|
||||
}
|
||||
|
||||
// account is optional
|
||||
export function getContract(address, ABI, library, account) {
|
||||
export function getContract(address: string, ABI: any, library: JsonRpcProvider, account?: string): Contract {
|
||||
if (!isAddress(address) || address === ethers.constants.AddressZero) {
|
||||
throw Error(`Invalid 'address' parameter '${address}'.`)
|
||||
}
|
||||
@ -150,13 +157,12 @@ export function getContract(address, ABI, library, account) {
|
||||
|
||||
// account is optional
|
||||
export function getRouterContract(chainId, library, account) {
|
||||
const router = getContract(ROUTER_ADDRESS, IUniswapV2Router01.abi, library, account)
|
||||
return router
|
||||
return getContract(ROUTER_ADDRESS, IUniswapV2Router01ABI, library, account)
|
||||
}
|
||||
|
||||
// account is optional
|
||||
export function getExchangeContract(pairAddress, library, account) {
|
||||
return getContract(pairAddress, IUniswapV2Pair.abi, library, account)
|
||||
return getContract(pairAddress, IUniswapV2PairABI, library, account)
|
||||
}
|
||||
|
||||
// get token name
|
||||
@ -231,58 +237,14 @@ export async function getTokenBalance(tokenAddress, address, library) {
|
||||
export async function getTokenAllowance(address, tokenAddress, spenderAddress, library) {
|
||||
if (!isAddress(address) || !isAddress(tokenAddress) || !isAddress(spenderAddress)) {
|
||||
throw Error(
|
||||
"Invalid 'address' or 'tokenAddress' or 'spenderAddress' parameter" +
|
||||
`'${address}' or '${tokenAddress}' or '${spenderAddress}'.`
|
||||
'Invalid \'address\' or \'tokenAddress\' or \'spenderAddress\' parameter' +
|
||||
`'${address}' or '${tokenAddress}' or '${spenderAddress}'.`
|
||||
)
|
||||
}
|
||||
|
||||
return getContract(tokenAddress, ERC20_ABI, library).allowance(address, spenderAddress)
|
||||
}
|
||||
|
||||
const PERMIT_TYPEHASH = keccak256(
|
||||
toUtf8Bytes('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)')
|
||||
)
|
||||
|
||||
export function expandTo18Decimals(n) {
|
||||
return bigNumberify(n).mul(bigNumberify(10).pow(18))
|
||||
}
|
||||
|
||||
function getDomainSeparator(name, tokenAddress) {
|
||||
return keccak256(
|
||||
defaultAbiCoder.encode(
|
||||
['bytes32', 'bytes32', 'bytes32', 'uint256', 'address'],
|
||||
[
|
||||
keccak256(toUtf8Bytes('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)')),
|
||||
keccak256(toUtf8Bytes(name)),
|
||||
keccak256(toUtf8Bytes('1')),
|
||||
1,
|
||||
tokenAddress
|
||||
]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export async function getApprovalDigest(token, approve, nonce, deadline) {
|
||||
const name = await token.name()
|
||||
const DOMAIN_SEPARATOR = getDomainSeparator(name, token.address)
|
||||
return keccak256(
|
||||
solidityPack(
|
||||
['bytes1', 'bytes1', 'bytes32', 'bytes32'],
|
||||
[
|
||||
'0x19',
|
||||
'0x01',
|
||||
DOMAIN_SEPARATOR,
|
||||
keccak256(
|
||||
defaultAbiCoder.encode(
|
||||
['bytes32', 'address', 'address', 'uint256', 'uint256', 'uint256'],
|
||||
[PERMIT_TYPEHASH, approve.owner, approve.spender, approve.value, nonce, deadline]
|
||||
)
|
||||
)
|
||||
]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export function isWETH(token) {
|
||||
if (token && token.address === WETH[token.chainId].address) {
|
||||
return true
|
@ -1,49 +0,0 @@
|
||||
import { BigNumber } from '@uniswap/sdk'
|
||||
|
||||
// returns a deep copied + sorted list of values, as well as a sortmap
|
||||
export function sortBigNumbers(values) {
|
||||
const valueMap = values.map((value, i) => ({ value, i }))
|
||||
|
||||
valueMap.sort((a, b) => {
|
||||
if (a.value.isGreaterThan(b.value)) {
|
||||
return 1
|
||||
} else if (a.value.isLessThan(b.value)) {
|
||||
return -1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
})
|
||||
|
||||
return [
|
||||
valueMap.map(element => values[element.i]),
|
||||
values.map((_, i) => valueMap.findIndex(element => element.i === i))
|
||||
]
|
||||
}
|
||||
|
||||
export function getMedian(values) {
|
||||
const [sortedValues, sortMap] = sortBigNumbers(values)
|
||||
if (values.length % 2 === 0) {
|
||||
const middle = values.length / 2
|
||||
const indices = [middle - 1, middle]
|
||||
return [
|
||||
sortedValues[middle - 1].plus(sortedValues[middle]).dividedBy(2),
|
||||
sortMap.map(element => (indices.includes(element) ? new BigNumber(0.5) : new BigNumber(0)))
|
||||
]
|
||||
} else {
|
||||
const middle = Math.floor(values.length / 2)
|
||||
return [sortedValues[middle], sortMap.map(element => (element === middle ? new BigNumber(1) : new BigNumber(0)))]
|
||||
}
|
||||
}
|
||||
|
||||
export function getMean(values, _weights) {
|
||||
const weights = _weights ? _weights : values.map(() => new BigNumber(1))
|
||||
|
||||
const weightedValues = values.map((value, i) => value.multipliedBy(weights[i]))
|
||||
const numerator = weightedValues.reduce(
|
||||
(accumulator, currentValue) => accumulator.plus(currentValue),
|
||||
new BigNumber(0)
|
||||
)
|
||||
const denominator = weights.reduce((accumulator, currentValue) => accumulator.plus(currentValue), new BigNumber(0))
|
||||
|
||||
return [numerator.dividedBy(denominator), weights.map(weight => weight.dividedBy(denominator))]
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
import * as ethers from 'ethers'
|
||||
|
||||
export default class UncheckedJsonRpcSigner extends ethers.Signer {
|
||||
constructor(signer) {
|
||||
super()
|
||||
ethers.utils.defineReadOnly(this, 'signer', signer)
|
||||
ethers.utils.defineReadOnly(this, 'provider', signer.provider)
|
||||
}
|
||||
|
||||
getAddress() {
|
||||
return this.signer.getAddress()
|
||||
}
|
||||
|
||||
sendTransaction(transaction) {
|
||||
return this.signer.sendUncheckedTransaction(transaction).then(hash => {
|
||||
return {
|
||||
hash: hash,
|
||||
nonce: null,
|
||||
gasLimit: null,
|
||||
gasPrice: null,
|
||||
data: null,
|
||||
value: null,
|
||||
chainId: null,
|
||||
confirmations: 0,
|
||||
from: null,
|
||||
wait: confirmations => {
|
||||
return this.signer.provider.waitForTransaction(hash, confirmations)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
signMessage(message) {
|
||||
return this.signer.signMessage(message)
|
||||
}
|
||||
}
|
44
src/utils/signer.ts
Normal file
44
src/utils/signer.ts
Normal file
@ -0,0 +1,44 @@
|
||||
import { Signer } from 'ethers'
|
||||
import { JsonRpcSigner, Provider } from 'ethers/providers'
|
||||
import { TransactionResponse } from 'ethers/providers/abstract-provider'
|
||||
|
||||
/**
|
||||
* Wraps a JsonRpcSigner and replaces `sendTransaction` with `sendUncheckedTransaction`
|
||||
*/
|
||||
export default class UncheckedJsonRpcSigner extends Signer {
|
||||
private readonly signer: JsonRpcSigner
|
||||
public readonly provider: Provider
|
||||
|
||||
constructor(signer: JsonRpcSigner) {
|
||||
super()
|
||||
this.signer = signer
|
||||
this.provider = signer.provider
|
||||
}
|
||||
|
||||
getAddress(): Promise<string> {
|
||||
return this.signer.getAddress()
|
||||
}
|
||||
|
||||
sendTransaction(transaction): Promise<TransactionResponse> {
|
||||
return this.signer.sendUncheckedTransaction(transaction).then(hash => {
|
||||
return {
|
||||
hash,
|
||||
nonce: null,
|
||||
gasLimit: null,
|
||||
gasPrice: null,
|
||||
data: null,
|
||||
value: null,
|
||||
chainId: null,
|
||||
confirmations: 0,
|
||||
from: null,
|
||||
wait: confirmations => {
|
||||
return this.signer.provider.waitForTransaction(hash, confirmations)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
signMessage(message) {
|
||||
return this.signer.signMessage(message)
|
||||
}
|
||||
}
|
@ -2455,10 +2455,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/lib/-/lib-1.1.1.tgz#0afd29601846c16e5d082866cbb24a9e0758e6bc"
|
||||
integrity sha512-2yK7sLpKIT91TiS5sewHtOa7YuM8IuBXVl4GZv2jZFys4D2sY7K5vZh6MqD25TPA95Od+0YzCVq6cTF2IKrOmg==
|
||||
|
||||
"@uniswap/sdk@^2.0.1":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/sdk/-/sdk-2.0.1.tgz#191fdf0a49bc31c44ae429a1757380b83e22dd5f"
|
||||
integrity sha512-F5SAWhGPPi4tgFTL0Gl3mtLSFkR/Tz/w35Aib5jpC1ti4AHwPWt4ReXMm7ucrvtXZlWmiBHPplNxsIIm1Z5aqQ==
|
||||
"@uniswap/sdk@^2.0.3":
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@uniswap/sdk/-/sdk-2.0.3.tgz#a9ec8059d40d8ccd49183938bec5f194018336af"
|
||||
integrity sha512-Re2fFehHtA8KBAD+PxUEah94Rgtm4A516jP+AcGiJ5dVUp8MzkR59RMqoEW6NX4lR8calwl9YwKOhnmLiUY9jQ==
|
||||
dependencies:
|
||||
"@ethersproject/address" "^5.0.0-beta.134"
|
||||
"@ethersproject/contracts" "^5.0.0-beta.143"
|
||||
|
Loading…
Reference in New Issue
Block a user