From 2bd1872676869670aa0cb232aeaa1a21168e8c60 Mon Sep 17 00:00:00 2001 From: tate Date: Thu, 27 Apr 2023 10:18:39 +1000 Subject: [PATCH] checkpoint --- .vscode/settings.json | 2 +- package.json | 2 +- packages/ensjs/jest.config.ts | 2 +- packages/ensjs/package.json | 6 +- packages/ensjs/src/contracts/addContracts.ts | 93 +++++++ packages/ensjs/src/contracts/multicall.ts | 48 +++- .../ensjs/src/contracts/publicResolver.ts | 46 +++- .../ensjs/src/contracts/universalResolver.ts | 50 +++- packages/ensjs/src/errors.ts | 0 .../ensjs/src/functions/fetch/_getAddr.ts | 121 +++++++++ packages/ensjs/src/functions/fetch/batch.ts | 63 +++++ .../ensjs/src/functions/fetch/getAddr.test.ts | 26 ++ packages/ensjs/src/functions/fetch/getAddr.ts | 29 ++ .../ensjs/src/functions/fetch/getName.test.ts | 23 ++ packages/ensjs/src/functions/fetch/getName.ts | 41 +++ .../src/functions/fetch/multicallWrapper.ts | 62 +++++ .../src/functions/fetch/universalWrapper.ts | 29 ++ packages/ensjs/src/types.ts | 14 + packages/ensjs/src/utils/ccip.ts | 220 ++++++++------- packages/ensjs/src/utils/generateFunction.ts | 81 ++++++ packages/ensjs/src/utils/hexEncodedName.ts | 32 ++- packages/ensjs/src/utils/labels.ts | 7 +- packages/ensjs/src/utils/normalise.ts | 36 ++- pnpm-lock.yaml | 250 +++++++++++++----- 24 files changed, 1049 insertions(+), 234 deletions(-) create mode 100644 packages/ensjs/src/contracts/addContracts.ts create mode 100644 packages/ensjs/src/errors.ts create mode 100644 packages/ensjs/src/functions/fetch/_getAddr.ts create mode 100644 packages/ensjs/src/functions/fetch/batch.ts create mode 100644 packages/ensjs/src/functions/fetch/getAddr.test.ts create mode 100644 packages/ensjs/src/functions/fetch/getAddr.ts create mode 100644 packages/ensjs/src/functions/fetch/getName.test.ts create mode 100644 packages/ensjs/src/functions/fetch/getName.ts create mode 100644 packages/ensjs/src/functions/fetch/multicallWrapper.ts create mode 100644 packages/ensjs/src/functions/fetch/universalWrapper.ts create mode 100644 packages/ensjs/src/types.ts create mode 100644 packages/ensjs/src/utils/generateFunction.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 62787842..46b1ef28 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,6 @@ }, "eslint.nodePath": ".yarn/sdks", "prettier.prettierPath": ".yarn/sdks/prettier/index.js", - "typescript.tsdk": ".yarn/sdks/typescript/lib", + "typescript.tsdk": "node_modules/typescript/lib", "typescript.enablePromptUseWorkspaceTsdk": true } diff --git a/package.json b/package.json index dcafd3ce..0f239cf5 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "eslint-plugin-prettier": "^4.0.0", "prettier": "^2.6.0", "ts-node": "^10.7.0", - "typescript": "^4.7.4" + "typescript": "^5.0.4" }, "resolutions": { "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers@0.3.0-beta.13" diff --git a/packages/ensjs/jest.config.ts b/packages/ensjs/jest.config.ts index f4e37a98..11a51040 100644 --- a/packages/ensjs/jest.config.ts +++ b/packages/ensjs/jest.config.ts @@ -6,7 +6,7 @@ const config = { '^.+\\.(t|j)sx?$': ['@swc/jest'], }, setupFilesAfterEnv: ['/jest.setup.ts'], - transformIgnorePatterns: [] + transformIgnorePatterns: [], } export default config diff --git a/packages/ensjs/package.json b/packages/ensjs/package.json index 837d966e..14f2ab93 100644 --- a/packages/ensjs/package.json +++ b/packages/ensjs/package.json @@ -81,12 +81,14 @@ "@ensdomains/address-encoder": "^0.2.18", "@ensdomains/content-hash": "^2.5.7", "@ensdomains/dnssecoraclejs": "^0.2.8", + "abitype": "^0.8.0", "cbor": "^8.1.0", "dns-packet": "^5.3.1", "graphql": "^16.3.0", "graphql-request": "5.1.0", "pako": "^2.1.0", - "traverse": "^0.6.6" + "traverse": "^0.6.6", + "viem": "^0.3.11" }, "devDependencies": { "@ensdomains/buffer": "^0.0.13", @@ -134,7 +136,7 @@ "ts-jest": "^27.1.4", "ts-node": "^10.7.0", "typechain": "^8.1.0", - "typescript": "^4.7.4", + "typescript": "^5.0.4", "wait-on": "^6.0.1" }, "peerDependencies": { diff --git a/packages/ensjs/src/contracts/addContracts.ts b/packages/ensjs/src/contracts/addContracts.ts new file mode 100644 index 00000000..f9530a1e --- /dev/null +++ b/packages/ensjs/src/contracts/addContracts.ts @@ -0,0 +1,93 @@ +import type { Chain, PublicClient, Transport } from 'viem' +import type { ChainContract } from 'viem/src/types/chain' +import { Prettify } from '../types' + +const supportedChains = ['homestead', 'goerli'] + +const addresses = { + homestead: { + baseRegistrarImplementation: { + address: '0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85', + }, + dnsRegistrar: { + address: '0x58774Bb8acD458A640aF0B88238369A167546ef2', + }, + ethRegistrarController: { + address: '0x253553366Da8546fC250F225fe3d25d0C782303b', + }, + nameWrapper: { + address: '0xD4416b13d2b3a9aBae7AcD5D6C2BbDBE25686401', + }, + publicResolver: { + address: '0x231b0Ee14048e9dCcD1d247744d114a4EB5E8E63', + }, + reverseRegistrar: { + address: '0xa58E81fe9b61B5c3fE2AFD33CF304c454AbFc7Cb', + }, + bulkRenewal: { + address: '0xa12159e5131b1eEf6B4857EEE3e1954744b5033A', + }, + }, + goerli: { + baseRegistrarImplementation: { + address: '0x57f1887a8bf19b14fc0df6fd9b2acc9af147ea85', + }, + dnsRegistrar: { + address: '0x8edc487D26F6c8Fa76e032066A3D4F87E273515d', + }, + ethRegistrarController: { + address: '0xCc5e7dB10E65EED1BBD105359e7268aa660f6734', + }, + nameWrapper: { + address: '0x114D4603199df73e7D157787f8778E21fCd13066', + }, + publicResolver: { + address: '0xd7a4F6473f32aC2Af804B3686AE8F19E48B8fF5f', + }, + reverseRegistrar: { + address: '0x6d9F26FfBcF1c6f0bAe9F2C1f7fBe8eE6B1d8d4d', + }, + bulkRenewal: { + address: '0x6d9F26FfBcF1c6f0bAe9F2C1f7fBe8eE6B1d8d4d', + }, + }, +} as const + +export type ChainWithEns = Prettify< + TChain & { + contracts: { + ensjs: { + baseRegistrarImplementation: ChainContract + dnsRegistrar: ChainContract + ethRegistrarController: ChainContract + nameWrapper: ChainContract + publicResolver: ChainContract + reverseRegistrar: ChainContract + bulkRenewal: ChainContract + } + } + } +> + +export type ClientWithEns< + TTransport extends Transport = Transport, + TChain extends ChainWithEns = ChainWithEns, +> = PublicClient + +export const addContracts = ( + chains: TChain[], +): ChainWithEns[] => + chains + .filter((chain) => supportedChains.includes(chain.network)) + .map( + (chain) => + ({ + ...chain, + contracts: { + ...chain.contracts, + ensjs: { + ...addresses[chain.name as keyof typeof addresses], + }, + }, + } as ChainWithEns), + ) diff --git a/packages/ensjs/src/contracts/multicall.ts b/packages/ensjs/src/contracts/multicall.ts index cb6f4b5a..9cafa279 100644 --- a/packages/ensjs/src/contracts/multicall.ts +++ b/packages/ensjs/src/contracts/multicall.ts @@ -1,5 +1,43 @@ -import type { JsonRpcProvider } from '@ethersproject/providers' -import { Multicall__factory } from '../generated/factories/Multicall__factory' - -export default (provider: JsonRpcProvider, address: string) => - Multicall__factory.connect(address, provider) +export const tryAggregateSnippet = [ + { + inputs: [ + { + name: 'requireSuccess', + type: 'bool', + }, + { + components: [ + { + name: 'target', + type: 'address', + }, + { + name: 'callData', + type: 'bytes', + }, + ], + name: 'calls', + type: 'tuple[]', + }, + ], + name: 'tryAggregate', + outputs: [ + { + components: [ + { + name: 'success', + type: 'bool', + }, + { + name: 'returnData', + type: 'bytes', + }, + ], + name: 'returnData', + type: 'tuple[]', + }, + ], + stateMutability: 'payable', + type: 'function', + }, +] as const diff --git a/packages/ensjs/src/contracts/publicResolver.ts b/packages/ensjs/src/contracts/publicResolver.ts index 8eb2bce2..0d77c5ac 100644 --- a/packages/ensjs/src/contracts/publicResolver.ts +++ b/packages/ensjs/src/contracts/publicResolver.ts @@ -1,5 +1,43 @@ -import type { JsonRpcProvider } from '@ethersproject/providers' -import { PublicResolver__factory } from '../generated/factories/PublicResolver__factory' +export const singleAddrSnippet = [ + { + inputs: [ + { + name: 'node', + type: 'bytes32', + }, + ], + name: 'addr', + outputs: [ + { + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const -export default (provider: JsonRpcProvider, address: string) => - PublicResolver__factory.connect(address, provider) +export const multiAddrSnippet = [ + { + inputs: [ + { + name: 'node', + type: 'bytes32', + }, + { + name: 'coinType', + type: 'uint256', + }, + ], + name: 'addr', + outputs: [ + { + name: '', + type: 'bytes', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const diff --git a/packages/ensjs/src/contracts/universalResolver.ts b/packages/ensjs/src/contracts/universalResolver.ts index 5f568700..32fae105 100644 --- a/packages/ensjs/src/contracts/universalResolver.ts +++ b/packages/ensjs/src/contracts/universalResolver.ts @@ -1,5 +1,47 @@ -import type { JsonRpcProvider } from '@ethersproject/providers' -import { UniversalResolver__factory } from '../generated/factories/UniversalResolver__factory' +export const reverseSnippet = [ + { + inputs: [ + { + name: 'reverseName', + type: 'bytes', + }, + ], + name: 'reverse', + outputs: [ + { type: 'string', name: 'resolvedName' }, + { type: 'address', name: 'resolvedAddress' }, + { type: 'address', name: 'reverseResolver' }, + { type: 'address', name: 'resolver' }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const -export default (provider: JsonRpcProvider, address: string) => - UniversalResolver__factory.connect(address, provider) +export const resolveSnippet = [ + { + inputs: [ + { + name: 'name', + type: 'bytes', + }, + { + name: 'data', + type: 'bytes', + }, + ], + name: 'resolve', + outputs: [ + { + name: 'data', + type: 'bytes', + }, + { + name: 'resolver', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, +] as const diff --git a/packages/ensjs/src/errors.ts b/packages/ensjs/src/errors.ts new file mode 100644 index 00000000..e69de29b diff --git a/packages/ensjs/src/functions/fetch/_getAddr.ts b/packages/ensjs/src/functions/fetch/_getAddr.ts new file mode 100644 index 00000000..df7f5b37 --- /dev/null +++ b/packages/ensjs/src/functions/fetch/_getAddr.ts @@ -0,0 +1,121 @@ +import { formatsByCoinType, formatsByName } from '@ensdomains/address-encoder' +import { Hex, decodeFunctionResult, encodeFunctionData, trim } from 'viem' +import { namehash } from '../../utils/normalise' + +import { ClientWithEns } from '../../contracts/addContracts' +import { + multiAddrSnippet, + singleAddrSnippet, +} from '../../contracts/publicResolver' +import { TransactionRequest } from '../../types' +import { generateFunction } from '../../utils/generateFunction' + +const encode = async ( + client: ClientWithEns, + name: string, + coinType?: string | number, + bypassFormat?: boolean, +): Promise => { + if (!coinType) { + coinType = 60 + } + + if (coinType === 60 || coinType === '60') { + return { + to: '0x0000000000000000000000000000000000000000', + data: encodeFunctionData({ + abi: singleAddrSnippet, + functionName: 'addr', + args: [namehash(name)], + }), + } + } + + if (bypassFormat) { + return { + to: '0x0000000000000000000000000000000000000000', + data: encodeFunctionData({ + abi: multiAddrSnippet, + functionName: 'addr', + args: [namehash(name), BigInt(coinType)], + }), + } + } + const formatter = + typeof coinType === 'string' && Number.isNaN(parseInt(coinType)) + ? formatsByName[coinType] + : formatsByCoinType[ + typeof coinType === 'number' ? coinType : parseInt(coinType) + ] + + if (!formatter) { + throw new Error(`No formatter found for coin: ${coinType}`) + } + + return { + to: '0x0000000000000000000000000000000000000000', + data: encodeFunctionData({ + abi: multiAddrSnippet, + functionName: 'addr', + args: [namehash(name), BigInt(formatter.coinType)], + }), + } +} + +const decode = async ( + client: ClientWithEns, + data: Hex, + _name: string, + coinType?: string | number, +) => { + let returnCoinType = true + if (!coinType) { + coinType = 60 + returnCoinType = false + } + + const formatter = + typeof coinType === 'string' && Number.isNaN(parseInt(coinType)) + ? formatsByName[coinType] + : formatsByCoinType[ + typeof coinType === 'number' ? coinType : parseInt(coinType) + ] + + let response: Hex + + if (coinType === 60 || coinType === '60') { + response = decodeFunctionResult({ + abi: singleAddrSnippet, + functionName: 'addr', + data, + }) + } else { + response = decodeFunctionResult({ + abi: multiAddrSnippet, + functionName: 'addr', + data, + }) + } + + if (!response) return null + + if (trim(response) === '0x') { + return null + } + + const decodedAddr = formatter.encoder(Buffer.from(response.slice(2), 'hex')) + + if (!decodedAddr) { + return null + } + + if (!returnCoinType) { + return decodedAddr + } + + return { coin: formatter.name, addr: decodedAddr } +} + +const _getAddr = generateFunction({ encode, decode }) + +export default _getAddr diff --git a/packages/ensjs/src/functions/fetch/batch.ts b/packages/ensjs/src/functions/fetch/batch.ts new file mode 100644 index 00000000..2e68e6ec --- /dev/null +++ b/packages/ensjs/src/functions/fetch/batch.ts @@ -0,0 +1,63 @@ +import type { Hex } from 'viem' +import { ClientWithEns } from '../../contracts/addContracts' +import { + TransactionRequest, + TransactionRequestWithPassthrough, +} from '../../types' +import { + BatchFunctionResult, + RawFunction, + generateFunction, +} from '../../utils/generateFunction' +import multicallWrapper from './multicallWrapper' + +const encode = async ( + client: ClientWithEns, + ...items: BatchFunctionResult[] +) => { + const rawDataArr: TransactionRequest[] = await Promise.all( + items.map(({ args, encode: encodeRef }, i: number) => { + if (!encodeRef) { + throw new Error(`Function ${i} is not batchable`) + } + return encodeRef(...args) + }), + ) + const response = await multicallWrapper.encode(client, rawDataArr) + return { ...response, passthrough: rawDataArr } +} + +const decode = async []>( + client: ClientWithEns, + data: Hex, + passthrough: TransactionRequestWithPassthrough[], + ...items: I +): Promise< + | { + [N in keyof I]: I[N] extends BatchFunctionResult + ? Awaited> + : never + } + | undefined +> => { + const response = await multicallWrapper.decode(client, data, passthrough) + if (!response) return + + return Promise.all( + response.map((ret, i: number) => { + if (passthrough[i].passthrough) { + return items[i].decode( + client, + ret.returnData, + passthrough[i].passthrough, + ...items[i].args, + ) + } + return items[i].decode(client, ret.returnData, ...items[i].args) + }), + ) +} + +const batch = generateFunction({ encode, decode }) + +export default batch diff --git a/packages/ensjs/src/functions/fetch/getAddr.test.ts b/packages/ensjs/src/functions/fetch/getAddr.test.ts new file mode 100644 index 00000000..dcb5ff3e --- /dev/null +++ b/packages/ensjs/src/functions/fetch/getAddr.test.ts @@ -0,0 +1,26 @@ +import { createPublicClient, http } from 'viem' +import { mainnet } from 'viem/chains' +import { addContracts } from '../../contracts/addContracts' +import batch from './batch' +import getAddr from './getAddr' +import getName from './getName' + +const transport = http('https://web3.ens.domains/v1/mainnet') + +const chainsWithEns = addContracts([mainnet]) + +const publicClient = createPublicClient({ + chain: chainsWithEns[0], + transport, +}) + +const main = async () => { + const result = await batch( + publicClient, + getName.batch('0x8e8Db5CcEF88cca9d624701Db544989C996E3216'), + getAddr.batch('taytems.eth'), + ) + console.log(result) +} + +main() diff --git a/packages/ensjs/src/functions/fetch/getAddr.ts b/packages/ensjs/src/functions/fetch/getAddr.ts new file mode 100644 index 00000000..d3a0e22b --- /dev/null +++ b/packages/ensjs/src/functions/fetch/getAddr.ts @@ -0,0 +1,29 @@ +import { Hex } from 'viem' +import { ClientWithEns } from '../../contracts/addContracts' +import { generateFunction } from '../../utils/generateFunction' +import _getAddr from './_getAddr' +import universalWrapper from './universalWrapper' + +const encode = async ( + client: ClientWithEns, + name: string, + coinType?: string | number, +) => { + const prData = await _getAddr.encode(client, name, coinType) + return universalWrapper.encode(client, name, prData.data) +} + +const decode = async ( + client: ClientWithEns, + data: Hex, + _name: string, + coinType?: string | number, +) => { + const urData = await universalWrapper.decode(client, data) + if (!urData) return + return _getAddr.decode(client, urData.data, _name, coinType) +} + +const getAddr = generateFunction({ encode, decode }) + +export default getAddr diff --git a/packages/ensjs/src/functions/fetch/getName.test.ts b/packages/ensjs/src/functions/fetch/getName.test.ts new file mode 100644 index 00000000..78e2c13a --- /dev/null +++ b/packages/ensjs/src/functions/fetch/getName.test.ts @@ -0,0 +1,23 @@ +import { createPublicClient, http } from 'viem' +import { mainnet } from 'viem/chains' +import { addContracts } from '../../contracts/addContracts' +import getName from './getName' + +const transport = http('https://web3.ens.domains/v1/mainnet') + +const chainsWithEns = addContracts([mainnet]) + +const publicClient = createPublicClient({ + chain: chainsWithEns[0], + transport, +}) + +const main = async () => { + const result = await getName( + publicClient, + '0x8e8Db5CcEF88cca9d624701Db544989C996E3216', + ) + console.log(result) +} + +main() diff --git a/packages/ensjs/src/functions/fetch/getName.ts b/packages/ensjs/src/functions/fetch/getName.ts new file mode 100644 index 00000000..03d638ea --- /dev/null +++ b/packages/ensjs/src/functions/fetch/getName.ts @@ -0,0 +1,41 @@ +import { + Address, + Hex, + decodeFunctionResult, + encodeFunctionData, + toHex, +} from 'viem' +import { ClientWithEns } from '../../contracts/addContracts' +import { reverseSnippet } from '../../contracts/universalResolver' +import { generateFunction } from '../../utils/generateFunction' +import { packetToBytes } from '../../utils/hexEncodedName' + +const encode = async (client: ClientWithEns, address: string) => { + const reverseNode = `${address.toLowerCase().substring(2)}.addr.reverse` + return { + to: client.chain.contracts!.ensUniversalResolver!.address, + data: encodeFunctionData({ + abi: reverseSnippet, + functionName: 'reverse', + args: [toHex(packetToBytes(reverseNode))], + }), + } +} + +const decode = async (client: ClientWithEns, data: Hex, address: Address) => { + const result = decodeFunctionResult({ + abi: reverseSnippet, + functionName: 'reverse', + data, + }) + return { + name: result[0], + match: result[1].toLowerCase() === address.toLowerCase(), + reverseResolverAddress: result[2], + resolverAddress: result[3], + } +} + +const getName = generateFunction({ encode, decode }) + +export default getName diff --git a/packages/ensjs/src/functions/fetch/multicallWrapper.ts b/packages/ensjs/src/functions/fetch/multicallWrapper.ts new file mode 100644 index 00000000..ebc564da --- /dev/null +++ b/packages/ensjs/src/functions/fetch/multicallWrapper.ts @@ -0,0 +1,62 @@ +import { Hex, decodeFunctionResult, encodeFunctionData } from 'viem' +import { ClientWithEns } from '../../contracts/addContracts' +import { tryAggregateSnippet } from '../../contracts/multicall' +import { + TransactionRequest, + TransactionRequestWithPassthrough, +} from '../../types' +import ccipLookup from '../../utils/ccip' +import { generateFunction } from '../../utils/generateFunction' + +const encode = async ( + client: ClientWithEns, + transactions: TransactionRequest[], + requireSuccess: boolean = false, +) => { + return { + to: client.chain.contracts.multicall3!.address, + data: encodeFunctionData({ + abi: tryAggregateSnippet, + functionName: 'tryAggregate', + args: [ + requireSuccess, + transactions.map((tx) => ({ target: tx.to!, callData: tx.data! })), + ], + }), + } +} + +const decode = async ( + client: ClientWithEns, + data: Hex, + transactions: TransactionRequestWithPassthrough[], +) => { + const result = decodeFunctionResult({ + abi: tryAggregateSnippet, + functionName: 'tryAggregate', + data, + }) + const ccipChecked = await Promise.all( + result.map(async ({ success, returnData }, i) => { + let newObj: { success: boolean; returnData: Hex } = { + success, + returnData, + } + // OffchainLookup(address,string[],bytes,bytes4,bytes) + if (!success && returnData.startsWith('0x556f1830')) { + try { + const newData = await ccipLookup(client, transactions[i], returnData) + if (newData) { + newObj = { success: true, returnData: newData } + } + } catch {} + } + return newObj + }), + ) + return ccipChecked +} + +const multicallWrapper = generateFunction({ encode, decode }) + +export default multicallWrapper diff --git a/packages/ensjs/src/functions/fetch/universalWrapper.ts b/packages/ensjs/src/functions/fetch/universalWrapper.ts new file mode 100644 index 00000000..d1d97f45 --- /dev/null +++ b/packages/ensjs/src/functions/fetch/universalWrapper.ts @@ -0,0 +1,29 @@ +import { Hex, decodeFunctionResult, encodeFunctionData, toHex } from 'viem' +import { ClientWithEns } from '../../contracts/addContracts' +import { resolveSnippet } from '../../contracts/universalResolver' +import { generateFunction } from '../../utils/generateFunction' +import { packetToBytes } from '../../utils/hexEncodedName' + +const encode = async (client: ClientWithEns, name: string, data: Hex) => { + return { + to: client.chain.contracts!.ensUniversalResolver!.address, + data: encodeFunctionData({ + abi: resolveSnippet, + functionName: 'resolve', + args: [toHex(packetToBytes(name)), data], + }), + } +} + +const decode = async (client: ClientWithEns, data: Hex) => { + const result = decodeFunctionResult({ + abi: resolveSnippet, + functionName: 'resolve', + data, + }) + return { data: result[0], resolver: result[1] } +} + +const universalWrapper = generateFunction({ encode, decode }) + +export default universalWrapper diff --git a/packages/ensjs/src/types.ts b/packages/ensjs/src/types.ts new file mode 100644 index 00000000..41322d1a --- /dev/null +++ b/packages/ensjs/src/types.ts @@ -0,0 +1,14 @@ +import { Address, Hex } from 'viem' + +export type Prettify = { + [K in keyof T]: T[K] +} & {} + +export type TransactionRequest = { + to: Address + data: Hex +} + +export type TransactionRequestWithPassthrough = TransactionRequest & { + passthrough?: any +} diff --git a/packages/ensjs/src/utils/ccip.ts b/packages/ensjs/src/utils/ccip.ts index 7cf4533d..f454fb0f 100644 --- a/packages/ensjs/src/utils/ccip.ts +++ b/packages/ensjs/src/utils/ccip.ts @@ -1,149 +1,141 @@ import { BigNumber } from '@ethersproject/bignumber' import { - arrayify, - BytesLike, - hexConcat, - hexDataSlice, -} from '@ethersproject/bytes' -import type { BaseProvider, TransactionRequest } from '@ethersproject/providers' -import { toUtf8String } from '@ethersproject/strings' -import type { Transaction } from '@ethersproject/transactions' - -function bytesPad(value: Uint8Array): Uint8Array { - if (value.length % 32 === 0) { - return value + Hex, + PublicClient, + concat, + encodeAbiParameters, + hexToString, + slice, +} from 'viem' +import { TransactionRequest } from '../types' + +function _parseBytes(result: Hex, start: number): null | Hex { + if (result === '0x') { + return null } - const result = new Uint8Array(Math.ceil(value.length / 32) * 32) - result.set(value) - return result -} + const offset = parseInt(slice(result, start, start + 32)) + const length = parseInt(slice(result, offset, offset + 32)) -function numPad(value: number): Uint8Array { - const result = arrayify(value) - if (result.length > 32) { - throw new Error('internal; should not happen') - } + return slice(result, offset + 32, offset + 32 + length) +} - const padded = new Uint8Array(32) - padded.set(result, 32 - result.length) - return padded +function _parseString(result: Hex, start: number): null | string { + try { + const bytes = _parseBytes(result, start) + if (bytes == null) return null + return hexToString(bytes) + } catch (error) {} + return null } -// ABI Encodes a series of (bytes, bytes, ...) -function encodeBytes(datas: Array) { - const result: Array = [] +const ccipReadFetch = async ( + tx: TransactionRequest, + calldata: Hex, + urls: string[], +): Promise => { + if (urls.length === 0) { + return null + } - let byteCount = 0 + const sender = tx.to!.toLowerCase() + const data = calldata.toLowerCase() - // Add place-holders for pointers as we add items - for (let i = 0; i < datas.length; i += 1) { - result.push(new Uint8Array(0)) - byteCount += 32 - } + const errorMessages: Array = [] - for (let i = 0; i < datas.length; i += 1) { - const data = arrayify(datas[i]) + for (let i = 0; i < urls.length; i += 1) { + const url = urls[i] - // Update the bytes offset - result[i] = numPad(byteCount) + // URL expansion + const href = url.replace('{sender}', sender).replace('{data}', data) - // The length and padded value of data - result.push(numPad(data.length)) - result.push(bytesPad(data)) - byteCount += 32 + Math.ceil(data.length / 32) * 32 - } + // If no {data} is present, use POST; otherwise GET + const json: string | null = + url.indexOf('{data}') >= 0 ? null : JSON.stringify({ data, sender }) - return hexConcat(result) -} + /* eslint-disable no-await-in-loop */ + const result = await fetch(href, { + method: json === null ? 'GET' : 'POST', + body: json, + }) + const returnedData = await result.text() + /* eslint-enable no-await-in-loop */ -function _parseBytes(result: string, start: number): null | string { - if (result === '0x') { - return null - } + if (returnedData) return returnedData as Hex - const offset = BigNumber.from( - hexDataSlice(result, start, start + 32), - ).toNumber() - const length = BigNumber.from( - hexDataSlice(result, offset, offset + 32), - ).toNumber() + const errorMessage = result.statusText || 'unknown error' - return hexDataSlice(result, offset + 32, offset + 32 + length) -} + if (result.status >= 400 && result.status < 500) { + throw new Error(`response not found during CCIP fetch: ${errorMessage}`) + } -function _parseString(result: string, start: number): null | string { - try { - const bytes = _parseBytes(result, start) - if (bytes == null) return null - return toUtf8String(bytes) - } catch (error) {} - return null + // 5xx indicates server issue; try the next url + errorMessages.push(errorMessage) + } + + throw new Error( + `error encountered during CCIP fetch: ${errorMessages + .map((m) => JSON.stringify(m)) + .join(', ')}`, + ) } const ccipLookup = async ( - provider: BaseProvider, + client: PublicClient, transaction: TransactionRequest, - result: string, + result: Hex, ) => { const txSender = transaction.to! - try { - const data = hexDataSlice(result, 4) + const data = slice(result, 4) - // Check the sender of the OffchainLookup matches the transaction - const sender = hexDataSlice(data, 0, 32) - if (!BigNumber.from(sender).eq(txSender)) { - throw new Error('CCIP Read sender did not match') - } + // Check the sender of the OffchainLookup matches the transaction + const sender = slice(data, 0, 32) + if (!BigNumber.from(sender).eq(txSender)) { + throw new Error('CCIP Read sender did not match') + } - // Read the URLs from the response - const urls: Array = [] - const urlsOffset = BigNumber.from(hexDataSlice(data, 32, 64)).toNumber() - const urlsLength = BigNumber.from( - hexDataSlice(data, urlsOffset, urlsOffset + 32), - ).toNumber() - const urlsData = hexDataSlice(data, urlsOffset + 32) - for (let u = 0; u < urlsLength; u += 1) { - const url = _parseString(urlsData, u * 32) - if (url == null) { - throw new Error('CCIP Read contained corrupt URL string') - } - urls.push(url) + // Read the URLs from the response + const urls: Array = [] + const urlsOffset = parseInt(slice(data, 32, 64), 16) + const urlsLength = parseInt(slice(data, urlsOffset, urlsOffset + 32)) + const urlsData = slice(data, urlsOffset + 32) + for (let u = 0; u < urlsLength; u += 1) { + const url = _parseString(urlsData, u * 32) + if (url == null) { + throw new Error('CCIP Read contained corrupt URL string') } + urls.push(url) + } - // Get the CCIP calldata to forward - const calldata = _parseBytes(data, 64) + // Get the CCIP calldata to forward + const calldata = _parseBytes(data, 64) as Hex - // Get the callbackSelector (bytes4) - if (!BigNumber.from(hexDataSlice(data, 100, 128)).isZero()) { - throw new Error('CCIP Read callback selected included junk') - } - const callbackSelector = hexDataSlice(data, 96, 100) - - // Get the extra data to send back to the contract as context - const extraData = _parseBytes(data, 128) - - const ccipResult = await provider.ccipReadFetch( - transaction, - calldata!, - urls, - ) - if (ccipResult == null) { - throw new Error('CCIP Read disabled or provided no URLs') - } + // Get the callbackSelector (bytes4) + if (BigInt(slice(data, 100, 128)) !== 0n) { + throw new Error('CCIP Read callback selected included junk') + } + const callbackSelector = slice(data, 96, 100) - const tx = { - to: txSender, - data: hexConcat([ - callbackSelector, - encodeBytes([ccipResult, extraData!]), - ]), - } + // Get the extra data to send back to the contract as context + const extraData = _parseBytes(data, 128) + + const ccipResult = await ccipReadFetch(transaction, calldata!, urls) + if (ccipResult == null) { + throw new Error('CCIP Read disabled or provided no URLs') + } - return await provider._call(tx, 'latest', 1) - } catch (error) { - console.error(error) + const tx = { + to: txSender, + data: concat([ + callbackSelector, + encodeAbiParameters( + [{ type: 'bytes' }, { type: 'bytes' }], + [ccipResult, extraData!], + ), + ]), } + + return client.call(tx).then((d) => d.data) } export default ccipLookup diff --git a/packages/ensjs/src/utils/generateFunction.ts b/packages/ensjs/src/utils/generateFunction.ts new file mode 100644 index 00000000..22828918 --- /dev/null +++ b/packages/ensjs/src/utils/generateFunction.ts @@ -0,0 +1,81 @@ +import { ClientWithEns } from '../contracts/addContracts' +import { TransactionRequestWithPassthrough } from '../types' + +type EncoderFunction = ( + ...args: any[] +) => Promise +type DecoderFunction = (...args: any[]) => Promise + +type OmitFirstArg = F extends (x: any, ...args: infer P) => infer R + ? (...args: P) => R + : never + +export type RawFunction = { + encode: EncoderFunction + decode: DecoderFunction +} + +export type BatchFunctionResult = { + args: Parameters + encode: TFunction['encode'] + decode: TFunction['decode'] +} + +type BatchFunction = ( + ...args: Parameters +) => BatchFunctionResult + +export interface GeneratedBatchFunction + extends Function, + RawFunction { + []>( + client: ClientWithEns, + ...args: I + ): Promise< + | { + [N in keyof I]: I[N] extends BatchFunctionResult + ? Awaited> + : never + } + | undefined + > + batch: BatchFunction +} + +export type FunctionSubtype = + | 'raw' + | 'decode' + | 'combine' + | 'batch' + | 'write' + | 'populateTransaction' + +export const generateFunction = < + TEncoderFn extends EncoderFunction, + TDecoderFn extends DecoderFunction, +>({ + encode, + decode, +}: { + encode: TEncoderFn + decode: TDecoderFn +}) => { + const single = async function ( + client: ClientWithEns, + ...args: Parameters> + ): Promise | null> { + const { passthrough, ...encodedData } = await encode(client, ...args) + const { data: result } = await client.call(encodedData) + if (!result) return null + if (passthrough) return decode(client, result, passthrough, ...args) + return decode(client, result, ...args) + } + single.batch = (...args: Parameters>) => ({ + args, + encode, + decode, + }) + single.encode = encode + single.decode = decode + return single +} diff --git a/packages/ensjs/src/utils/hexEncodedName.ts b/packages/ensjs/src/utils/hexEncodedName.ts index c82e9a2a..bb37eb00 100644 --- a/packages/ensjs/src/utils/hexEncodedName.ts +++ b/packages/ensjs/src/utils/hexEncodedName.ts @@ -1,7 +1,29 @@ -import packet from 'dns-packet' +// Adapted from https://github.com/mafintosh/dns-packet +import type { ByteArray } from 'viem' +import { stringToBytes } from 'viem' -export const hexEncodeName = (name: string) => - `0x${packet.name.encode(name).toString('hex')}` +/* + * @description Encodes a DNS packet into a ByteArray containing a UDP payload. + */ +export function packetToBytes(packet: string): ByteArray { + function length(value: string) { + if (value === '.' || value === '..') return 1 + return stringToBytes(value.replace(/^\.|\.$/gm, '')).length + 2 + } -export const hexDecodeName = (hex: string): string => - packet.name.decode(Buffer.from(hex.slice(2), 'hex')).toString() + const bytes = new Uint8Array(length(packet)) + // strip leading and trailing `.` + const value = packet.replace(/^\.|\.$/gm, '') + if (!value.length) return bytes + + let offset = 0 + const list = value.split('.') + for (let i = 0; i < list.length; i += 1) { + const encoded = stringToBytes(list[i]) + bytes[offset] = encoded.length + bytes.set(encoded, offset + 1) + offset += encoded.length + 1 + } + + return bytes +} diff --git a/packages/ensjs/src/utils/labels.ts b/packages/ensjs/src/utils/labels.ts index a7c864f7..4d3d6873 100644 --- a/packages/ensjs/src/utils/labels.ts +++ b/packages/ensjs/src/utils/labels.ts @@ -1,14 +1,11 @@ -import { keccak256 as solidityKeccak256 } from '@ethersproject/solidity' +import { Hex, labelhash } from 'viem' import { truncateFormat } from './format' const hasLocalStorage = typeof localStorage !== 'undefined' -export const labelhash = (input: string) => - solidityKeccak256(['string'], [input]) - export const keccakFromString = (input: string) => labelhash(input) -export function decodeLabelhash(hash: string) { +export function decodeLabelhash(hash: string): Hex { if (!(hash.startsWith('[') && hash.endsWith(']'))) { throw Error( 'Expected encoded labelhash to start and end with square brackets', diff --git a/packages/ensjs/src/utils/normalise.ts b/packages/ensjs/src/utils/normalise.ts index 688a11f7..4af30978 100644 --- a/packages/ensjs/src/utils/normalise.ts +++ b/packages/ensjs/src/utils/normalise.ts @@ -18,9 +18,7 @@ import { Token, ValidToken, } from '@adraffy/ens-normalize' -import { concat, hexlify } from '@ethersproject/bytes' -import { keccak256 } from '@ethersproject/keccak256' -import { toUtf8Bytes } from '@ethersproject/strings' +import { bytesToHex, concat, hexToBytes, keccak256, stringToBytes } from 'viem' import { decodeLabelhash, isEncodedLabelhash } from './labels' const zeros = new Uint8Array(32) @@ -28,28 +26,24 @@ zeros.fill(0) export const normalise = (name: string) => (name ? ens_normalize(name) : name) -export const namehash = (name: string): string => { - let result: string | Uint8Array = zeros +export function namehash(name: string) { + let result = new Uint8Array(32).fill(0) + if (!name) return bytesToHex(result) - if (name) { - const labels = name.split('.') - - for (let i = labels.length - 1; i >= 0; i -= 1) { - let labelSha: string - if (isEncodedLabelhash(labels[i])) { - labelSha = decodeLabelhash(labels[i]) - } else { - const normalised = normalise(labels[i]) - labelSha = keccak256(toUtf8Bytes(normalised)) - } - - result = keccak256(concat([result, labelSha])) + const labels = name.split('.') + // Iterate in reverse order building up hash + for (let i = labels.length - 1; i >= 0; i -= 1) { + let labelSha: Uint8Array + if (isEncodedLabelhash(labels[i])) { + labelSha = hexToBytes(decodeLabelhash(labels[i])) + } else { + const normalised = normalise(labels[i]) + labelSha = keccak256(stringToBytes(normalised), 'bytes') } - } else { - result = hexlify(zeros) + result = keccak256(concat([result, labelSha]), 'bytes') } - return result as string + return bytesToHex(result) } export const beautify = ens_beautify diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 87c1243a..b335e62b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,10 +18,10 @@ importers: eslint-plugin-prettier: ^4.0.0 prettier: ^2.6.0 ts-node: ^10.7.0 - typescript: ^4.7.4 + typescript: ^5.0.4 devDependencies: - '@typescript-eslint/eslint-plugin': 5.33.0_njno5y7ry2l2lcmiu4tywxkwnq - '@typescript-eslint/parser': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq + '@typescript-eslint/eslint-plugin': 5.33.0_ymmwmmrdflnhyagcksjv7xanui + '@typescript-eslint/parser': 5.33.0_udmbka6asohzlu5gmqb54xekz4 eslint: 8.21.0 eslint-config-airbnb: 19.0.4_jatgrcxl4x7ywe7ak6cnjca2ae eslint-config-airbnb-base: 15.0.0_jatgrcxl4x7ywe7ak6cnjca2ae @@ -30,8 +30,8 @@ importers: eslint-plugin-import: 2.26.0_qfqnhzzittf54udqwes54xx65q eslint-plugin-prettier: 4.2.1_h62lvancfh4b7r6zn2dgodrh5e prettier: 2.7.1 - ts-node: 10.9.1_typescript@4.7.4 - typescript: 4.7.4 + ts-node: 10.9.1_typescript@5.0.4 + typescript: 5.0.4 dependenciesMeta: ens-contracts: built: false @@ -105,6 +105,7 @@ importers: '@types/jest': ^27.4.1 '@types/pako': ^2.0.0 '@types/traverse': ^0.6.32 + abitype: ^0.8.0 cbor: ^8.1.0 dns-packet: ^5.3.1 dotenv: ^16.0.0 @@ -130,19 +131,22 @@ importers: ts-jest: ^27.1.4 ts-node: ^10.7.0 typechain: ^8.1.0 - typescript: ^4.7.4 + typescript: ^5.0.4 + viem: ^0.3.11 wait-on: ^6.0.1 dependencies: '@adraffy/ens-normalize': 1.9.0 '@ensdomains/address-encoder': 0.2.18 '@ensdomains/content-hash': 2.5.7 '@ensdomains/dnssecoraclejs': 0.2.8 + abitype: 0.8.0_typescript@5.0.4 cbor: 8.1.0 dns-packet: 5.4.0 graphql: 16.6.0 graphql-request: 5.1.0_graphql@16.6.0 pako: 2.1.0 traverse: 0.6.6 + viem: 0.3.11_typescript@5.0.4 devDependencies: '@ensdomains/buffer': 0.0.13_hardhat@2.10.2 '@ensdomains/ens-contracts': 0.0.17_hardhat@2.10.2 @@ -165,18 +169,18 @@ importers: '@openzeppelin/test-helpers': 0.5.16 '@swc/core': 1.2.241 '@swc/jest': 0.2.22_@swc+core@1.2.241 - '@typechain/ethers-v5': 10.1.0_2tsw7lzfas734g6njlsh3wi6vi + '@typechain/ethers-v5': 10.1.0_qxgsqlcab5zt56mrfb4ymlrzqy '@types/bn.js': 5.1.0 '@types/jest': 27.5.2 '@types/pako': 2.0.0 '@types/traverse': 0.6.32 dotenv: 16.0.1 esbuild: 0.15.6 - eslint-plugin-jest: 27.0.1_g4n3hsjlbmz4ag5o32ytojordu + eslint-plugin-jest: 27.0.1_ogirzayjtmpj5gdyims7ixoacm ethers: 5.7.2 ganache: 7.4.1 glob: 8.0.3 - hardhat: 2.10.2_6oasmw356qmm23djlsjgkwvrtm + hardhat: 2.10.2_pyqfhkbboucpxgvrs7ocxwodkm hardhat-abi-exporter: 2.10.0_hardhat@2.10.2 hardhat-deploy: 0.11.12 jest: 27.5.1_ts-node@10.9.1 @@ -186,10 +190,10 @@ importers: node-fetch: 3.2.10 node-fetch-commonjs: 3.1.1 solc: 0.8.16 - ts-jest: 27.1.5_2adzr5bv2qpyd5zjpgodfe3dyu - ts-node: 10.9.1_litewngskstwdadyxlilaik57a - typechain: 8.1.0_typescript@4.7.4 - typescript: 4.7.4 + ts-jest: 27.1.5_2fsxcsqm4i3k3eif4kwatgtfqe + ts-node: 10.9.1_k7iguhqbtoao35i6waebqvypgi + typechain: 8.1.0_typescript@5.0.4 + typescript: 5.0.4 wait-on: 6.0.1 packages: @@ -1542,10 +1546,26 @@ packages: resolution: {integrity: sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw==} dev: false + /@noble/curves/0.8.3: + resolution: {integrity: sha512-OqaOf4RWDaCRuBKJLDURrgVxjLmneGsiCXGuzYB5y95YithZMA6w4uk34DHSm0rKMrrYiaeZj48/81EvaAScLQ==} + dependencies: + '@noble/hashes': 1.3.0 + dev: false + + /@noble/curves/0.9.0: + resolution: {integrity: sha512-OAdtHMXBp7Chl2lcTn/i7vnFX/q+hhTwDnek5NfYfZsY4LyaUuHCcoq2JlLY3BTFTLT+ZhYZalhF6ejlV7KnJQ==} + dependencies: + '@noble/hashes': 1.3.0 + dev: false + /@noble/hashes/1.1.2: resolution: {integrity: sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==} dev: true + /@noble/hashes/1.3.0: + resolution: {integrity: sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==} + dev: false + /@noble/secp256k1/1.6.3: resolution: {integrity: sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ==} dev: true @@ -1583,7 +1603,7 @@ packages: chai: 4.3.6 ethereumjs-util: 7.1.5 fs-extra: 7.0.1 - hardhat: 2.10.2_6oasmw356qmm23djlsjgkwvrtm + hardhat: 2.10.2_pyqfhkbboucpxgvrs7ocxwodkm transitivePeerDependencies: - bufferutil - encoding @@ -1655,7 +1675,6 @@ packages: /@scure/base/1.1.1: resolution: {integrity: sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==} - dev: true /@scure/bip32/1.1.0: resolution: {integrity: sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q==} @@ -1665,6 +1684,14 @@ packages: '@scure/base': 1.1.1 dev: true + /@scure/bip32/1.2.0: + resolution: {integrity: sha512-O+vT/hBVk+ag2i6j2CDemwd1E1MtGt+7O1KzrPNsaNvSsiEK55MyPIxJIMI2PS8Ijj464B2VbQlpRoQXxw1uHg==} + dependencies: + '@noble/curves': 0.8.3 + '@noble/hashes': 1.3.0 + '@scure/base': 1.1.1 + dev: false + /@scure/bip39/1.1.0: resolution: {integrity: sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==} dependencies: @@ -1672,6 +1699,13 @@ packages: '@scure/base': 1.1.1 dev: true + /@scure/bip39/1.2.0: + resolution: {integrity: sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==} + dependencies: + '@noble/hashes': 1.3.0 + '@scure/base': 1.1.1 + dev: false + /@sentry/core/5.30.0: resolution: {integrity: sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==} engines: {node: '>=6'} @@ -2109,7 +2143,7 @@ packages: resolution: {integrity: sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==} dev: true - /@typechain/ethers-v5/10.1.0_2tsw7lzfas734g6njlsh3wi6vi: + /@typechain/ethers-v5/10.1.0_qxgsqlcab5zt56mrfb4ymlrzqy: resolution: {integrity: sha512-3LIb+eUpV3mNCrjUKT5oqp8PBsZYSnVrkfk6pY/ZM0boRs2mKxjFZ7bktx42vfDye8PPz3NxtW4DL5NsNsFqlg==} peerDependencies: '@ethersproject/abi': ^5.0.0 @@ -2124,9 +2158,9 @@ packages: '@ethersproject/providers': 5.6.8 ethers: 5.7.2 lodash: 4.17.21 - ts-essentials: 7.0.3_typescript@4.7.4 - typechain: 8.1.0_typescript@4.7.4 - typescript: 4.7.4 + ts-essentials: 7.0.3_typescript@5.0.4 + typechain: 8.1.0_typescript@5.0.4 + typescript: 5.0.4 dev: true /@types/abstract-leveldown/7.2.0: @@ -2303,7 +2337,7 @@ packages: '@types/yargs-parser': 21.0.0 dev: true - /@typescript-eslint/eslint-plugin/5.33.0_njno5y7ry2l2lcmiu4tywxkwnq: + /@typescript-eslint/eslint-plugin/5.33.0_ymmwmmrdflnhyagcksjv7xanui: resolution: {integrity: sha512-jHvZNSW2WZ31OPJ3enhLrEKvAZNyAFWZ6rx9tUwaessTc4sx9KmgMNhVcqVAl1ETnT5rU5fpXTLmY9YvC1DCNg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2314,23 +2348,23 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/parser': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq + '@typescript-eslint/parser': 5.33.0_udmbka6asohzlu5gmqb54xekz4 '@typescript-eslint/scope-manager': 5.33.0 - '@typescript-eslint/type-utils': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq - '@typescript-eslint/utils': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq + '@typescript-eslint/type-utils': 5.33.0_udmbka6asohzlu5gmqb54xekz4 + '@typescript-eslint/utils': 5.33.0_udmbka6asohzlu5gmqb54xekz4 debug: 4.3.4 eslint: 8.21.0 functional-red-black-tree: 1.0.1 ignore: 5.2.0 regexpp: 3.2.0 semver: 7.3.7 - tsutils: 3.21.0_typescript@4.7.4 - typescript: 4.7.4 + tsutils: 3.21.0_typescript@5.0.4 + typescript: 5.0.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/parser/5.33.0_qugx7qdu5zevzvxaiqyxfiwquq: + /@typescript-eslint/parser/5.33.0_udmbka6asohzlu5gmqb54xekz4: resolution: {integrity: sha512-cgM5cJrWmrDV2KpvlcSkelTBASAs1mgqq+IUGKJvFxWrapHpaRy5EXPQz9YaKF3nZ8KY18ILTiVpUtbIac86/w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2342,10 +2376,10 @@ packages: dependencies: '@typescript-eslint/scope-manager': 5.33.0 '@typescript-eslint/types': 5.33.0 - '@typescript-eslint/typescript-estree': 5.33.0_typescript@4.7.4 + '@typescript-eslint/typescript-estree': 5.33.0_typescript@5.0.4 debug: 4.3.4 eslint: 8.21.0 - typescript: 4.7.4 + typescript: 5.0.4 transitivePeerDependencies: - supports-color dev: true @@ -2358,7 +2392,7 @@ packages: '@typescript-eslint/visitor-keys': 5.33.0 dev: true - /@typescript-eslint/type-utils/5.33.0_qugx7qdu5zevzvxaiqyxfiwquq: + /@typescript-eslint/type-utils/5.33.0_udmbka6asohzlu5gmqb54xekz4: resolution: {integrity: sha512-2zB8uEn7hEH2pBeyk3NpzX1p3lF9dKrEbnXq1F7YkpZ6hlyqb2yZujqgRGqXgRBTHWIUG3NGx/WeZk224UKlIA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2368,11 +2402,11 @@ packages: typescript: optional: true dependencies: - '@typescript-eslint/utils': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq + '@typescript-eslint/utils': 5.33.0_udmbka6asohzlu5gmqb54xekz4 debug: 4.3.4 eslint: 8.21.0 - tsutils: 3.21.0_typescript@4.7.4 - typescript: 4.7.4 + tsutils: 3.21.0_typescript@5.0.4 + typescript: 5.0.4 transitivePeerDependencies: - supports-color dev: true @@ -2382,7 +2416,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true - /@typescript-eslint/typescript-estree/5.33.0_typescript@4.7.4: + /@typescript-eslint/typescript-estree/5.33.0_typescript@5.0.4: resolution: {integrity: sha512-tqq3MRLlggkJKJUrzM6wltk8NckKyyorCSGMq4eVkyL5sDYzJJcMgZATqmF8fLdsWrW7OjjIZ1m9v81vKcaqwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2397,13 +2431,13 @@ packages: globby: 11.1.0 is-glob: 4.0.3 semver: 7.3.7 - tsutils: 3.21.0_typescript@4.7.4 - typescript: 4.7.4 + tsutils: 3.21.0_typescript@5.0.4 + typescript: 5.0.4 transitivePeerDependencies: - supports-color dev: true - /@typescript-eslint/utils/5.33.0_qugx7qdu5zevzvxaiqyxfiwquq: + /@typescript-eslint/utils/5.33.0_typescript@5.0.4: resolution: {integrity: sha512-JxOAnXt9oZjXLIiXb5ZIcZXiwVHCkqZgof0O8KPgz7C7y0HS42gi75PdPlqh1Tf109M0fyUw45Ao6JLo7S5AHw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2412,16 +2446,15 @@ packages: '@types/json-schema': 7.0.11 '@typescript-eslint/scope-manager': 5.33.0 '@typescript-eslint/types': 5.33.0 - '@typescript-eslint/typescript-estree': 5.33.0_typescript@4.7.4 - eslint: 8.21.0 + '@typescript-eslint/typescript-estree': 5.33.0_typescript@5.0.4 eslint-scope: 5.1.1 - eslint-utils: 3.0.0_eslint@8.21.0 + eslint-utils: 3.0.0 transitivePeerDependencies: - supports-color - typescript dev: true - /@typescript-eslint/utils/5.33.0_typescript@4.7.4: + /@typescript-eslint/utils/5.33.0_udmbka6asohzlu5gmqb54xekz4: resolution: {integrity: sha512-JxOAnXt9oZjXLIiXb5ZIcZXiwVHCkqZgof0O8KPgz7C7y0HS42gi75PdPlqh1Tf109M0fyUw45Ao6JLo7S5AHw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: @@ -2430,9 +2463,10 @@ packages: '@types/json-schema': 7.0.11 '@typescript-eslint/scope-manager': 5.33.0 '@typescript-eslint/types': 5.33.0 - '@typescript-eslint/typescript-estree': 5.33.0_typescript@4.7.4 + '@typescript-eslint/typescript-estree': 5.33.0_typescript@5.0.4 + eslint: 8.21.0 eslint-scope: 5.1.1 - eslint-utils: 3.0.0 + eslint-utils: 3.0.0_eslint@8.21.0 transitivePeerDependencies: - supports-color - typescript @@ -2450,6 +2484,17 @@ packages: resolution: {integrity: sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==} dev: true + /@wagmi/chains/0.2.16_typescript@5.0.4: + resolution: {integrity: sha512-rkWaI2PxCnbD8G07ZZff5QXftnSkYL0h5f4DkHCG3fGYYr/ZDvmCL4bMae7j7A9sAif1csPPBmbCzHp3R5ogCQ==} + peerDependencies: + typescript: '>=4.9.4' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + typescript: 5.0.4 + dev: false + /@zxing/text-encoding/0.9.0: resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} requiresBuild: true @@ -2460,6 +2505,30 @@ packages: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} dev: true + /abitype/0.7.1_typescript@5.0.4: + resolution: {integrity: sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ==} + peerDependencies: + typescript: '>=4.9.4' + zod: ^3 >=3.19.1 + peerDependenciesMeta: + zod: + optional: true + dependencies: + typescript: 5.0.4 + dev: false + + /abitype/0.8.0_typescript@5.0.4: + resolution: {integrity: sha512-QqBuwc2trtaNd/kp4k0dSjVu3OQIYRAB4ww4LCPwfSECXcCGTD3imYovNYNjx0SyN3OHV0XUOzUVwnXP1kLsVQ==} + peerDependencies: + typescript: '>=4.9.4' + zod: ^3 >=3.19.1 + peerDependenciesMeta: + zod: + optional: true + dependencies: + typescript: 5.0.4 + dev: false + /abort-controller/3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -4432,8 +4501,8 @@ packages: eslint: ^7.32.0 || ^8.2.0 eslint-plugin-import: ^2.25.3 dependencies: - '@typescript-eslint/eslint-plugin': 5.33.0_njno5y7ry2l2lcmiu4tywxkwnq - '@typescript-eslint/parser': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq + '@typescript-eslint/eslint-plugin': 5.33.0_ymmwmmrdflnhyagcksjv7xanui + '@typescript-eslint/parser': 5.33.0_udmbka6asohzlu5gmqb54xekz4 eslint: 8.21.0 eslint-config-airbnb-base: 15.0.0_jatgrcxl4x7ywe7ak6cnjca2ae eslint-plugin-import: 2.26.0_qfqnhzzittf54udqwes54xx65q @@ -4495,7 +4564,7 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq + '@typescript-eslint/parser': 5.33.0_udmbka6asohzlu5gmqb54xekz4 debug: 3.2.7 eslint: 8.21.0 eslint-import-resolver-node: 0.3.6 @@ -4513,7 +4582,7 @@ packages: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 5.33.0_qugx7qdu5zevzvxaiqyxfiwquq + '@typescript-eslint/parser': 5.33.0_udmbka6asohzlu5gmqb54xekz4 array-includes: 3.1.5 array.prototype.flat: 1.3.0 debug: 2.6.9 @@ -4534,7 +4603,7 @@ packages: - supports-color dev: true - /eslint-plugin-jest/27.0.1_g4n3hsjlbmz4ag5o32ytojordu: + /eslint-plugin-jest/27.0.1_ogirzayjtmpj5gdyims7ixoacm: resolution: {integrity: sha512-LosUsrkwVSs/8Z/I8Hqn5vWgTEsHrfIquDEKOsV8/cl+gbFR4tiRCE1AimEotsHjSC0Rx1tYm6vPhw8C3ktmmg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} peerDependencies: @@ -4547,7 +4616,7 @@ packages: jest: optional: true dependencies: - '@typescript-eslint/utils': 5.33.0_typescript@4.7.4 + '@typescript-eslint/utils': 5.33.0_typescript@5.0.4 jest: 27.5.1_ts-node@10.9.1 transitivePeerDependencies: - supports-color @@ -5608,7 +5677,7 @@ packages: dependencies: '@ethersproject/abi': 5.6.4 delete-empty: 3.0.0 - hardhat: 2.10.2_6oasmw356qmm23djlsjgkwvrtm + hardhat: 2.10.2_pyqfhkbboucpxgvrs7ocxwodkm dev: true /hardhat-deploy-ethers/0.3.0-beta.13_xk7ts3jleh4tydykqykpb6jvzy: @@ -5618,7 +5687,7 @@ packages: hardhat: ^2.0.0 dependencies: ethers: 5.7.2 - hardhat: 2.10.2_6oasmw356qmm23djlsjgkwvrtm + hardhat: 2.10.2_pyqfhkbboucpxgvrs7ocxwodkm dev: true /hardhat-deploy/0.11.12: @@ -5643,7 +5712,7 @@ packages: - utf-8-validate dev: true - /hardhat/2.10.2_6oasmw356qmm23djlsjgkwvrtm: + /hardhat/2.10.2_pyqfhkbboucpxgvrs7ocxwodkm: resolution: {integrity: sha512-L/KvDDT/MA6332uAtYTqdcHoSABljw4pPjHQe5SHdIJ+xKfaSc6vDKw03CmrQ5Xup0gHs8XnVSBpZo1AbbIW7g==} engines: {node: ^14.0.0 || ^16.0.0 || ^18.0.0} hasBin: true @@ -5700,9 +5769,9 @@ packages: source-map-support: 0.5.21 stacktrace-parser: 0.1.10 true-case-path: 2.2.1 - ts-node: 10.9.1_litewngskstwdadyxlilaik57a + ts-node: 10.9.1_k7iguhqbtoao35i6waebqvypgi tsort: 0.0.1 - typescript: 4.7.4 + typescript: 5.0.4 undici: 5.9.1 uuid: 8.3.2 ws: 7.4.6 @@ -6208,6 +6277,14 @@ packages: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} dev: true + /isomorphic-ws/5.0.0_ws@8.12.0: + resolution: {integrity: sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==} + peerDependencies: + ws: '*' + dependencies: + ws: 8.12.0 + dev: false + /isstream/0.1.2: resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} dev: true @@ -6365,7 +6442,7 @@ packages: pretty-format: 27.5.1 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.1_litewngskstwdadyxlilaik57a + ts-node: 10.9.1_k7iguhqbtoao35i6waebqvypgi transitivePeerDependencies: - bufferutil - canvas @@ -9218,15 +9295,15 @@ packages: string-format: 2.0.0 dev: true - /ts-essentials/7.0.3_typescript@4.7.4: + /ts-essentials/7.0.3_typescript@5.0.4: resolution: {integrity: sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==} peerDependencies: typescript: '>=3.7.0' dependencies: - typescript: 4.7.4 + typescript: 5.0.4 dev: true - /ts-jest/27.1.5_2adzr5bv2qpyd5zjpgodfe3dyu: + /ts-jest/27.1.5_2fsxcsqm4i3k3eif4kwatgtfqe: resolution: {integrity: sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} hasBin: true @@ -9257,11 +9334,11 @@ packages: lodash.memoize: 4.1.2 make-error: 1.3.6 semver: 7.3.7 - typescript: 4.7.4 + typescript: 5.0.4 yargs-parser: 20.2.9 dev: true - /ts-node/10.9.1_litewngskstwdadyxlilaik57a: + /ts-node/10.9.1_k7iguhqbtoao35i6waebqvypgi: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -9287,12 +9364,12 @@ packages: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 4.7.4 + typescript: 5.0.4 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 dev: true - /ts-node/10.9.1_typescript@4.7.4: + /ts-node/10.9.1_typescript@5.0.4: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true peerDependencies: @@ -9317,7 +9394,7 @@ packages: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 4.7.4 + typescript: 5.0.4 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 dev: true @@ -9342,14 +9419,14 @@ packages: resolution: {integrity: sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==} dev: true - /tsutils/3.21.0_typescript@4.7.4: + /tsutils/3.21.0_typescript@5.0.4: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} peerDependencies: typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' dependencies: tslib: 1.14.1 - typescript: 4.7.4 + typescript: 5.0.4 dev: true /tunnel-agent/0.6.0: @@ -9420,7 +9497,7 @@ packages: resolution: {integrity: sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==} dev: true - /typechain/8.1.0_typescript@4.7.4: + /typechain/8.1.0_typescript@5.0.4: resolution: {integrity: sha512-5jToLgKTjHdI1VKqs/K8BLYy42Sr3o8bV5ojh4MnR9ExHO83cyyUdw+7+vMJCpKXUiVUvARM4qmHTFuyaCMAZQ==} hasBin: true peerDependencies: @@ -9435,8 +9512,8 @@ packages: mkdirp: 1.0.4 prettier: 2.7.1 ts-command-line-args: 2.3.1 - ts-essentials: 7.0.3_typescript@4.7.4 - typescript: 4.7.4 + ts-essentials: 7.0.3_typescript@5.0.4 + typescript: 5.0.4 transitivePeerDependencies: - supports-color dev: true @@ -9453,11 +9530,10 @@ packages: stacktrace-js: 1.3.1 dev: false - /typescript/4.7.4: - resolution: {integrity: sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==} - engines: {node: '>=4.2.0'} + /typescript/5.0.4: + resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} + engines: {node: '>=12.20'} hasBin: true - dev: true /typical/4.0.0: resolution: {integrity: sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==} @@ -9665,6 +9741,25 @@ packages: extsprintf: 1.3.0 dev: true + /viem/0.3.11_typescript@5.0.4: + resolution: {integrity: sha512-Z7AXS31B/hukJyrDD9GhdE9j2OtsoTdNNJgLNC3sNPMgBcF90y8u+t+uiHSxLivXia+JLCer5M+LC+edPXV6tw==} + dependencies: + '@adraffy/ens-normalize': 1.9.0 + '@noble/curves': 0.9.0 + '@noble/hashes': 1.3.0 + '@scure/bip32': 1.2.0 + '@scure/bip39': 1.2.0 + '@wagmi/chains': 0.2.16_typescript@5.0.4 + abitype: 0.7.1_typescript@5.0.4 + isomorphic-ws: 5.0.0_ws@8.12.0 + ws: 8.12.0 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + - zod + dev: false + /w3c-hr-time/1.0.2: resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} dependencies: @@ -10404,6 +10499,19 @@ packages: utf-8-validate: optional: true + /ws/8.12.0: + resolution: {integrity: sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: false + /xhr-request-promise/0.1.3: resolution: {integrity: sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==} dependencies: