Skip to content

Commit

Permalink
feat: add strict param
Browse files Browse the repository at this point in the history
  • Loading branch information
TateB committed Jan 4, 2024
1 parent da156ca commit 66caae7
Show file tree
Hide file tree
Showing 24 changed files with 1,014 additions and 223 deletions.
21 changes: 21 additions & 0 deletions packages/ensjs/src/functions/public/_getAbi.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { ClientWithEns } from '../../contracts/consts.js'
import _getAbi from './_getAbi.js'

it('does not propagate error when strict is false', async () => {
const result = await _getAbi.decode({} as ClientWithEns, '0x1234', {
strict: false,
})
expect(result).toBeNull()
})

it('propagates error when strict is true', async () => {
await expect(_getAbi.decode({} as ClientWithEns, '0x1234', { strict: true }))
.rejects.toThrowErrorMatchingInlineSnapshot(`
"Data size of 2 bytes is too small for given parameters.
Params: (uint256, bytes)
Data: 0x1234 (2 bytes)
Version: [email protected]"
`)
})
114 changes: 61 additions & 53 deletions packages/ensjs/src/functions/public/_getAbi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { namehash } from '../../utils/normalise.js'
export type InternalGetAbiParameters = {
/** Name to get ABI record for */
name: string
/** Whether or not to throw decoding errors */
strict?: boolean
}

export type InternalGetAbiReturnType = Prettify<DecodedAbi | null>
Expand All @@ -28,11 +30,11 @@ export type InternalGetAbiReturnType = Prettify<DecodedAbi | null>
// ID 2: zlib compressed JSON
// ID 4: CBOR
// ID 8: URI
const supportedContentTypes = BigInt('0xf')
const supportedContentTypes = 0xfn

const encode = (
_client: ClientWithEns,
{ name }: InternalGetAbiParameters,
{ name }: Omit<InternalGetAbiParameters, 'strict'>,
): SimpleTransactionRequest => {
return {
to: EMPTY_ADDRESS,
Expand All @@ -47,66 +49,72 @@ const encode = (
const decode = async (
_client: ClientWithEns,
data: Hex,
{ strict }: Pick<InternalGetAbiParameters, 'strict'>,
): Promise<InternalGetAbiReturnType> => {
if (data === '0x') return null

const [bigintContentType, encodedAbiData] = decodeFunctionResult({
abi: publicResolverAbiSnippet,
functionName: 'ABI',
data,
})

if (!bigintContentType || !encodedAbiData) {
return null
}

const contentType = Number(bigintContentType)
if (!contentType) {
return null
}
try {
const [bigintContentType, encodedAbiData] = decodeFunctionResult({
abi: publicResolverAbiSnippet,
functionName: 'ABI',
data,
})

let abiData: string | object
let decoded = false
switch (contentType) {
// JSON
case 1:
abiData = JSON.parse(hexToString(encodedAbiData))
decoded = true
break
// zlib compressed JSON
case 2: {
const { inflate } = await import('pako/dist/pako_inflate.min.js')
abiData = JSON.parse(
inflate(hexToBytes(encodedAbiData), { to: 'string' }),
)
decoded = true
break
if (!bigintContentType || !encodedAbiData) {
return null
}
// CBOR
case 4: {
const { cborDecode } = await import('@ensdomains/address-encoder/utils')
abiData = await cborDecode(hexToBytes(encodedAbiData).buffer)
decoded = true
break

const contentType = Number(bigintContentType)
if (!contentType) {
return null
}
// URI
case 8:
abiData = hexToString(encodedAbiData)
decoded = true
break
default:
try {
abiData = hexToString(encodedAbiData)

let abiData: string | object
let decoded = false
switch (contentType) {
// JSON
case 1:
abiData = JSON.parse(hexToString(encodedAbiData))
decoded = true
} catch {
abiData = encodedAbiData
break
// zlib compressed JSON
case 2: {
const { inflate } = await import('pako/dist/pako_inflate.min.js')
abiData = JSON.parse(
inflate(hexToBytes(encodedAbiData), { to: 'string' }),
)
decoded = true
break
}
}
// CBOR
case 4: {
const { cborDecode } = await import('@ensdomains/address-encoder/utils')
abiData = await cborDecode(hexToBytes(encodedAbiData).buffer)
decoded = true
break
}
// URI
case 8:
abiData = hexToString(encodedAbiData)
decoded = true
break
default:
try {
abiData = hexToString(encodedAbiData)
decoded = true
} catch {
abiData = encodedAbiData
}
}

return {
contentType,
decoded,
abi: abiData,
return {
contentType,
decoded,
abi: abiData,
}
} catch (error) {
if (strict) throw error
return null
}
}

Expand Down
21 changes: 21 additions & 0 deletions packages/ensjs/src/functions/public/_getAddr.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { ClientWithEns } from '../../contracts/consts.js'
import _getAddr from './_getAddr.js'

it('does not propagate error when strict is false', async () => {
const result = await _getAddr.decode({} as ClientWithEns, '0x1234', {
strict: false,
})
expect(result).toBeNull()
})

it('propagates error when strict is true', async () => {
await expect(_getAddr.decode({} as ClientWithEns, '0x1234', { strict: true }))
.rejects.toThrowErrorMatchingInlineSnapshot(`
"Data size of 2 bytes is too small for given parameters.
Params: (address)
Data: 0x1234 (2 bytes)
Version: [email protected]"
`)
})
55 changes: 31 additions & 24 deletions packages/ensjs/src/functions/public/_getAddr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ export type InternalGetAddrParameters = {
coin?: string | number
/** Optionally return raw bytes value of address record (default: false) */
bypassFormat?: boolean
/** Whether or not to throw decoding errors */
strict?: boolean
}

export type InternalGetAddrReturnType = Prettify<DecodedAddr | null>

const encode = (
_client: ClientWithEns,
{ name, coin = 60, bypassFormat }: InternalGetAddrParameters,
{ name, coin = 60, bypassFormat }: Omit<InternalGetAddrParameters, 'strict'>,
): SimpleTransactionRequest => {
const coder = getCoderFromCoin(coin)
if (coder.coinType === 60) {
Expand Down Expand Up @@ -72,41 +74,46 @@ const encode = (
const decode = async (
_client: ClientWithEns,
data: Hex,
{ coin = 60 }: InternalGetAddrParameters,
{ coin = 60, strict }: Pick<InternalGetAddrParameters, 'coin' | 'strict'>,
): Promise<InternalGetAddrReturnType> => {
if (data === '0x') return null

const coder = getCoderFromCoin(coin)
let response: Hex

if (coder.coinType === 60) {
response = decodeFunctionResult({
abi: publicResolverSingleAddrSnippet,
functionName: 'addr',
data,
})
} else {
response = decodeFunctionResult({
abi: publicResolverMultiAddrSnippet,
functionName: 'addr',
data,
})
}
try {
if (coder.coinType === 60) {
response = decodeFunctionResult({
abi: publicResolverSingleAddrSnippet,
functionName: 'addr',
data,
})
} else {
response = decodeFunctionResult({
abi: publicResolverMultiAddrSnippet,
functionName: 'addr',
data,
})
}

if (!response) return null
if (!response) return null

const trimmed = trim(response)
if (trimmed === '0x' || trimmed === '0x0' || trimmed === '0x00') {
return null
}
const trimmed = trim(response)
if (trimmed === '0x' || trimmed === '0x0' || trimmed === '0x00') {
return null
}

const decodedAddr = coder.encode(hexToBytes(response))

const decodedAddr = coder.encode(hexToBytes(response))
if (!decodedAddr) {
return null
}

if (!decodedAddr) {
return { id: coder.coinType, name: coder.name, value: decodedAddr }
} catch (error) {
if (strict) throw error
return null
}

return { id: coder.coinType, name: coder.name, value: decodedAddr }
}

const _getAddr = generateFunction({ encode, decode })
Expand Down
22 changes: 22 additions & 0 deletions packages/ensjs/src/functions/public/_getContentHash.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { ClientWithEns } from '../../contracts/consts.js'
import _getContentHash from './_getContentHash.js'

it('does not propagate error when strict is false', async () => {
const result = await _getContentHash.decode({} as ClientWithEns, '0x1234', {
strict: false,
})
expect(result).toBeNull()
})

it('propagates error when strict is true', async () => {
await expect(
_getContentHash.decode({} as ClientWithEns, '0x1234', { strict: true }),
).rejects.toThrowErrorMatchingInlineSnapshot(`
"Data size of 2 bytes is too small for given parameters.
Params: (bytes)
Data: 0x1234 (2 bytes)
Version: [email protected]"
`)
})
22 changes: 15 additions & 7 deletions packages/ensjs/src/functions/public/_getContentHash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ import { namehash } from '../../utils/normalise.js'
export type InternalGetContentHashParameters = {
/** Name to get content hash record for */
name: string
/** Whether or not to throw decoding errors */
strict?: boolean
}

export type InternalGetContentHashReturnType =
Prettify<DecodedContentHash | null>

const encode = (
_client: ClientWithEns,
{ name }: InternalGetContentHashParameters,
{ name }: Omit<InternalGetContentHashParameters, 'strict'>,
): SimpleTransactionRequest => {
return {
to: EMPTY_ADDRESS,
Expand All @@ -35,16 +37,22 @@ const encode = (
const decode = async (
_client: ClientWithEns,
data: Hex,
{ strict }: Pick<InternalGetContentHashParameters, 'strict'>,
): Promise<InternalGetContentHashReturnType> => {
if (data === '0x') return null

const response = decodeFunctionResult({
abi: publicResolverContenthashSnippet,
functionName: 'contenthash',
data,
})
try {
const response = decodeFunctionResult({
abi: publicResolverContenthashSnippet,
functionName: 'contenthash',
data,
})

return decodeContentHash(response)
return decodeContentHash(response)
} catch (error) {
if (strict) throw error
return null
}
}

const _getContentHash = generateFunction({ encode, decode })
Expand Down
21 changes: 21 additions & 0 deletions packages/ensjs/src/functions/public/_getText.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import type { ClientWithEns } from '../../contracts/consts.js'
import _getText from './_getText.js'

it('does not propagate error when strict is false', async () => {
const result = await _getText.decode({} as ClientWithEns, '0x1234', {
strict: false,
})
expect(result).toBeNull()
})

it('propagates error when strict is true', async () => {
await expect(_getText.decode({} as ClientWithEns, '0x1234', { strict: true }))
.rejects.toThrowErrorMatchingInlineSnapshot(`
"Data size of 2 bytes is too small for given parameters.
Params: (string)
Data: 0x1234 (2 bytes)
Version: [email protected]"
`)
})
Loading

0 comments on commit 66caae7

Please sign in to comment.