diff --git a/.changeset/beige-pillows-report.md b/.changeset/beige-pillows-report.md new file mode 100644 index 0000000000..36a6f9b784 --- /dev/null +++ b/.changeset/beige-pillows-report.md @@ -0,0 +1,5 @@ +--- +"viem": patch +--- + +Added `decodeDeployData`. diff --git a/site/.vitepress/sidebar.ts b/site/.vitepress/sidebar.ts index ad5477584a..1d8fc1b7d5 100644 --- a/site/.vitepress/sidebar.ts +++ b/site/.vitepress/sidebar.ts @@ -434,7 +434,7 @@ export const sidebar: DefaultTheme.Sidebar = { link: '/docs/contract/decodeAbi', }, { - text: 'decodeDeployData 🚧', + text: 'decodeDeployData', link: '/docs/contract/decodeDeployData', }, { diff --git a/site/docs/contract/decodeDeployData.md b/site/docs/contract/decodeDeployData.md index bd08012129..29b6121faa 100644 --- a/site/docs/contract/decodeDeployData.md +++ b/site/docs/contract/decodeDeployData.md @@ -1,3 +1,112 @@ -# decodeConstructorData +# decodeDeployData -TODO \ No newline at end of file +Decodes ABI encoded deploy data (bytecode & arguments). + +The opposite of [`encodeDeployData`](/docs/contract/encodeDeployData). + +## Install + +```ts +import { decodeDeployData } from 'viem' +``` + +## Usage + +::: code-group + +```ts [example.ts] +import { decodeFunctionData } from 'viem' + +const { args } = decodeFunctionData({ + abi: wagmiAbi, + bytecode: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033', + data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c634300080700330000000000000000000000000000000000000000000000000000000000010f2c' +}) +// { args: [69420n], bytecode: '0x6080604...' } +``` + +```ts +export const wagmiAbi = [ + ... + { + inputs: [ + { + internalType: 'uint256', + name: 'a', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + ... +] as const; +``` + +```ts [client.ts] +import { createPublicClient, http } from 'viem' +import { mainnet } from 'viem/chains' + +export const publicClient = createPublicClient({ + chain: mainnet, + transport: http() +}) +``` + +::: + + +## Return Value + +```ts +{ + args: unknown[] | undefined; + bytecode: Hex; +} +``` + +Decoded deploy data. + +## Parameters + +### abi + +- **Type:** [`Abi`](/docs/glossary/types#TODO) + +The contract's ABI. + +```ts +const { args } = decodeFunctionData({ + abi: wagmiAbi, // [!code focus] + bytecode: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033', + data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c634300080700330000000000000000000000000000000000000000000000000000000000010f2c' +}) +``` + +### bytecode + +- **Type:** `Hex` + +Contract bytecode. + +```ts +const { args } = decodeFunctionData({ + abi: wagmiAbi, + bytecode: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033', // [!code focus] + data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c634300080700330000000000000000000000000000000000000000000000000000000000010f2c' +}) +``` + +### data + +- **Type:** `Hex` + +The encoded calldata. + +```ts +const { args } = decodeFunctionData({ + abi: wagmiAbi, + bytecode: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033', + data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c634300080700330000000000000000000000000000000000000000000000000000000000010f2c' // [!code focus] +}) +``` \ No newline at end of file diff --git a/src/actions/public/getTransactionCount.ts b/src/actions/public/getTransactionCount.ts index 5059e54692..2896984227 100644 --- a/src/actions/public/getTransactionCount.ts +++ b/src/actions/public/getTransactionCount.ts @@ -30,5 +30,5 @@ export async function getTransactionCount( method: 'eth_getTransactionCount', params: [address, blockNumber ? numberToHex(blockNumber) : blockTag], }) - return hexToNumber(count ?? '0x0') + return hexToNumber(count) } diff --git a/src/errors/abi.test.ts b/src/errors/abi.test.ts index 93e4aef22d..92e8ae7280 100644 --- a/src/errors/abi.test.ts +++ b/src/errors/abi.test.ts @@ -18,11 +18,13 @@ test('AbiDecodingDataSizeInvalidError', () => { }) test('InvalidAbiDecodingTypeError', () => { - expect(new InvalidAbiDecodingTypeError('lol')).toMatchInlineSnapshot(` + expect( + new InvalidAbiDecodingTypeError('lol', { docsPath: '/lol' }), + ).toMatchInlineSnapshot(` [InvalidAbiDecodingType: Type "lol" is not a valid decoding type. Please provide a valid ABI type. - Docs: https://viem.sh/docs/contract/decodeAbi#params + Docs: https://viem.sh/lol Version: viem@1.0.2] `) @@ -60,11 +62,13 @@ test('AbiEncodingLengthMismatchError', () => { }) test('InvalidAbiEncodingTypeError', () => { - expect(new InvalidAbiEncodingTypeError('lol')).toMatchInlineSnapshot(` + expect( + new InvalidAbiEncodingTypeError('lol', { docsPath: '/lol' }), + ).toMatchInlineSnapshot(` [InvalidAbiEncodingType: Type "lol" is not a valid encoding type. Please provide a valid ABI type. - Docs: https://viem.sh/docs/contract/encodeAbi#params + Docs: https://viem.sh/lol Version: viem@1.0.2] `) diff --git a/src/errors/abi.ts b/src/errors/abi.ts index dcb3fb21fa..2b6c22690e 100644 --- a/src/errors/abi.ts +++ b/src/errors/abi.ts @@ -3,14 +3,14 @@ import { BaseError } from './base' export class AbiConstructorNotFoundError extends BaseError { name = 'AbiConstructorNotFoundError' - constructor() { + constructor({ docsPath }: { docsPath: string }) { super( [ - 'Constructor arguments were provided (`args`), but a constructor was not found on the ABI.', + 'A constructor was not found on the ABI.', 'Make sure you are using the correct ABI and that the constructor exists on it.', ].join('\n'), { - docsPath: '/docs/contract/encodeDeployData', + docsPath, }, ) } @@ -18,14 +18,14 @@ export class AbiConstructorNotFoundError extends BaseError { export class AbiConstructorParamsNotFoundError extends BaseError { name = 'AbiConstructorParamsNotFoundError' - constructor() { + constructor({ docsPath }: { docsPath: string }) { super( [ 'Constructor arguments were provided (`args`), but a constructor parameters (`inputs`) were not found on the ABI.', 'Make sure you are using the correct ABI, and that the `inputs` attribute on the constructor exists.', ].join('\n'), { - docsPath: '/docs/contract/encodeDeployData', + docsPath, }, ) } @@ -78,7 +78,7 @@ export class AbiEncodingLengthMismatchError extends BaseError { export class AbiErrorInputsNotFoundError extends BaseError { name = 'AbiErrorInputsNotFoundError' - constructor(errorName: string) { + constructor(errorName: string, { docsPath }: { docsPath: string }) { super( [ `Arguments (\`args\`) were provided to "${errorName}", but "${errorName}" on the ABI does not contain any parameters (\`inputs\`).`, @@ -86,7 +86,7 @@ export class AbiErrorInputsNotFoundError extends BaseError { 'Make sure you are using the correct ABI and that the inputs exist on it.', ].join('\n'), { - docsPath: '/docs/contract/encodeErrorResult', + docsPath, }, ) } @@ -94,14 +94,14 @@ export class AbiErrorInputsNotFoundError extends BaseError { export class AbiErrorNotFoundError extends BaseError { name = 'AbiErrorNotFoundError' - constructor(errorName: string) { + constructor(errorName: string, { docsPath }: { docsPath: string }) { super( [ `Error "${errorName}" not found on ABI.`, 'Make sure you are using the correct ABI and that the error exists on it.', ].join('\n'), { - docsPath: '/docs/contract/encodeErrorResult', + docsPath, }, ) } @@ -109,7 +109,7 @@ export class AbiErrorNotFoundError extends BaseError { export class AbiErrorSignatureNotFoundError extends BaseError { name = 'AbiErrorSignatureNotFoundError' - constructor(signature: Hex) { + constructor(signature: Hex, { docsPath }: { docsPath: string }) { super( [ `Encoded error signature "${signature}" not found on ABI.`, @@ -117,7 +117,7 @@ export class AbiErrorSignatureNotFoundError extends BaseError { `You can look up the signature "${signature}" here: https://sig.eth.samczsun.com/.`, ].join('\n'), { - docsPath: '/docs/contract/decodeErrorResult', + docsPath, }, ) } @@ -125,14 +125,14 @@ export class AbiErrorSignatureNotFoundError extends BaseError { export class AbiEventNotFoundError extends BaseError { name = 'AbiEventNotFoundError' - constructor(eventName: string) { + constructor(eventName: string, { docsPath }: { docsPath: string }) { super( [ `Event "${eventName}" not found on ABI.`, 'Make sure you are using the correct ABI and that the event exists on it.', ].join('\n'), { - docsPath: '/docs/contract/encodeEventTopics', + docsPath, }, ) } @@ -140,14 +140,14 @@ export class AbiEventNotFoundError extends BaseError { export class AbiFunctionNotFoundError extends BaseError { name = 'AbiFunctionNotFoundError' - constructor(functionName: string) { + constructor(functionName: string, { docsPath }: { docsPath: string }) { super( [ `Function "${functionName}" not found on ABI.`, 'Make sure you are using the correct ABI and that the function exists on it.', ].join('\n'), { - docsPath: '/docs/contract/encodeFunctionData', + docsPath, }, ) } @@ -155,7 +155,7 @@ export class AbiFunctionNotFoundError extends BaseError { export class AbiFunctionOutputsNotFoundError extends BaseError { name = 'AbiFunctionOutputsNotFoundError' - constructor(functionName: string) { + constructor(functionName: string, { docsPath }: { docsPath: string }) { super( [ `Function "${functionName}" does not contain any \`outputs\` on ABI.`, @@ -163,7 +163,7 @@ export class AbiFunctionOutputsNotFoundError extends BaseError { 'Make sure you are using the correct ABI and that the function exists on it.', ].join('\n'), { - docsPath: '/docs/contract/decodeFunctionResult', + docsPath, }, ) } @@ -171,7 +171,7 @@ export class AbiFunctionOutputsNotFoundError extends BaseError { export class AbiFunctionSignatureNotFoundError extends BaseError { name = 'AbiFunctionSignatureNotFoundError' - constructor(signature: Hex) { + constructor(signature: Hex, { docsPath }: { docsPath: string }) { super( [ `Encoded function signature "${signature}" not found on ABI.`, @@ -179,7 +179,7 @@ export class AbiFunctionSignatureNotFoundError extends BaseError { `You can look up the signature "${signature}" here: https://sig.eth.samczsun.com/.`, ].join('\n'), { - docsPath: '/docs/contract/decodeFunctionData', + docsPath, }, ) } @@ -187,26 +187,26 @@ export class AbiFunctionSignatureNotFoundError extends BaseError { export class InvalidAbiEncodingTypeError extends BaseError { name = 'InvalidAbiEncodingType' - constructor(type: string) { + constructor(type: string, { docsPath }: { docsPath: string }) { super( [ `Type "${type}" is not a valid encoding type.`, 'Please provide a valid ABI type.', ].join('\n'), - { docsPath: '/docs/contract/encodeAbi#params' }, + { docsPath }, ) } } export class InvalidAbiDecodingTypeError extends BaseError { name = 'InvalidAbiDecodingType' - constructor(type: string) { + constructor(type: string, { docsPath }: { docsPath: string }) { super( [ `Type "${type}" is not a valid decoding type.`, 'Please provide a valid ABI type.', ].join('\n'), - { docsPath: '/docs/contract/decodeAbi#params' }, + { docsPath }, ) } } @@ -220,14 +220,14 @@ export class InvalidArrayError extends BaseError { export class InvalidDefinitionTypeError extends BaseError { name = 'InvalidDefinitionTypeError' - constructor(type: string) { + constructor(type: string, { docsPath }: { docsPath: string }) { super( [ `"${type}" is not a valid definition type.`, 'Valid types: "function", "event", "error"', ].join('\n'), { - docsPath: '/docs/contract/getDefinition', + docsPath, }, ) } diff --git a/src/types/eip1193.ts b/src/types/eip1193.ts index 76302651b1..9bc185267f 100644 --- a/src/types/eip1193.ts +++ b/src/types/eip1193.ts @@ -458,7 +458,7 @@ export type PublicRequests = { * */ method: 'eth_getTransactionCount' params: [address: Address, block: BlockNumber | BlockTag | BlockIdentifier] - }): Promise + }): Promise request(args: { /** * @description Returns the receipt of a transaction specified by hash diff --git a/src/utils/abi/decodeAbi.test.ts b/src/utils/abi/decodeAbi.test.ts index de6a88c09f..5f11faef28 100644 --- a/src/utils/abi/decodeAbi.test.ts +++ b/src/utils/abi/decodeAbi.test.ts @@ -3,6 +3,15 @@ import { describe, expect, test } from 'vitest' import { decodeAbi } from './decodeAbi' describe('static', () => { + test('blank', () => { + expect( + decodeAbi({ + data: '0x', + params: [], + }), + ).toBe(undefined) + }) + test('uint', () => { expect( decodeAbi({ @@ -1573,7 +1582,7 @@ test('invalid type', () => { "Type \\"lol\\" is not a valid decoding type. Please provide a valid ABI type. - Docs: https://viem.sh/docs/contract/decodeAbi#params + Docs: https://viem.sh/docs/contract/decodeAbi Version: viem@1.0.2" `) diff --git a/src/utils/abi/decodeAbi.ts b/src/utils/abi/decodeAbi.ts index a1c8a5b2f1..f5c1789eb7 100644 --- a/src/utils/abi/decodeAbi.ts +++ b/src/utils/abi/decodeAbi.ts @@ -20,10 +20,12 @@ export function decodeAbi({ }: { data: Hex; params: TParams }) { if (size(data) % 32 !== 0) throw new AbiDecodingDataSizeInvalidError(size(data)) - return decodeParams({ + const values = decodeParams({ data, params, }) + if (values.length === 0) return undefined + return values } //////////////////////////////////////////////////////////////////// @@ -88,7 +90,9 @@ function decodeParam({ if (param.type === 'bool') { return decodeBool(value) } - throw new InvalidAbiDecodingTypeError(param.type) + throw new InvalidAbiDecodingTypeError(param.type, { + docsPath: '/docs/contract/decodeAbi', + }) } //////////////////////////////////////////////////////////////////// diff --git a/src/utils/abi/decodeDeployData.test.ts b/src/utils/abi/decodeDeployData.test.ts new file mode 100644 index 0000000000..d7bab94fd0 --- /dev/null +++ b/src/utils/abi/decodeDeployData.test.ts @@ -0,0 +1,159 @@ +import { expect, test } from 'vitest' + +import { decodeDeployData } from './decodeDeployData' + +test('constructor()', () => { + expect( + decodeDeployData({ + abi: [ + { + inputs: [], + stateMutability: 'nonpayable', + type: 'constructor', + }, + ] as const, + bytecode: + '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033', + data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033', + }), + ).toEqual({ + bytecode: + '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033', + }) + expect( + decodeDeployData({ + // @ts-expect-error + abi: [ + { + stateMutability: 'nonpayable', + type: 'constructor', + }, + ] as const, + bytecode: + '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033', + data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033', + }), + ).toEqual({ + bytecode: + '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033', + }) +}) + +test('constructor(uint256)', () => { + expect( + decodeDeployData({ + abi: [ + { + inputs: [ + { + internalType: 'uint256', + name: 'a', + type: 'uint256', + }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + ] as const, + bytecode: + '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033', + data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c634300080700330000000000000000000000000000000000000000000000000000000000010f2c', + }), + ).toEqual({ + args: [69420n], + bytecode: + '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033', + }) +}) + +test('error: constructor not found', () => { + expect(() => + decodeDeployData({ + // @ts-expect-error + abi: [{}] as const, + bytecode: + '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033', + data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c634300080700330000000000000000000000000000000000000000000000000000000000010f2c', + }), + ).toThrowErrorMatchingInlineSnapshot(` + "A constructor was not found on the ABI. + Make sure you are using the correct ABI and that the constructor exists on it. + + Docs: https://viem.sh/docs/contract/decodeDeployData + + Version: viem@1.0.2" + `) +}) + +test('error: no inputs', () => { + expect(() => + decodeDeployData({ + // @ts-expect-error + abi: [ + { + stateMutability: 'nonpayable', + type: 'constructor', + }, + ] as const, + bytecode: + '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033', + data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c634300080700330000000000000000000000000000000000000000000000000000000000010f2c', + }), + ).toThrowErrorMatchingInlineSnapshot( + ` + "Constructor arguments were provided (\`args\`), but a constructor parameters (\`inputs\`) were not found on the ABI. + Make sure you are using the correct ABI, and that the \`inputs\` attribute on the constructor exists. + + Docs: https://viem.sh/docs/contract/decodeDeployData + + Version: viem@1.0.2" + `, + ) + expect(() => + decodeDeployData({ + abi: [ + { + inputs: [], + stateMutability: 'nonpayable', + type: 'constructor', + }, + ] as const, + bytecode: + '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033', + data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c634300080700330000000000000000000000000000000000000000000000000000000000010f2c', + }), + ).toThrowErrorMatchingInlineSnapshot( + ` + "Constructor arguments were provided (\`args\`), but a constructor parameters (\`inputs\`) were not found on the ABI. + Make sure you are using the correct ABI, and that the \`inputs\` attribute on the constructor exists. + + Docs: https://viem.sh/docs/contract/decodeDeployData + + Version: viem@1.0.2" + `, + ) + expect(() => + decodeDeployData({ + // @ts-expect-error + abi: [ + { + inputs: undefined, + stateMutability: 'nonpayable', + type: 'constructor', + }, + ] as const, + bytecode: + '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033', + data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c634300080700330000000000000000000000000000000000000000000000000000000000010f2c', + }), + ).toThrowErrorMatchingInlineSnapshot( + ` + "Constructor arguments were provided (\`args\`), but a constructor parameters (\`inputs\`) were not found on the ABI. + Make sure you are using the correct ABI, and that the \`inputs\` attribute on the constructor exists. + + Docs: https://viem.sh/docs/contract/decodeDeployData + + Version: viem@1.0.2" + `, + ) +}) diff --git a/src/utils/abi/decodeDeployData.ts b/src/utils/abi/decodeDeployData.ts new file mode 100644 index 0000000000..c22bed8f7f --- /dev/null +++ b/src/utils/abi/decodeDeployData.ts @@ -0,0 +1,34 @@ +import { Abi } from 'abitype' + +import { + AbiConstructorNotFoundError, + AbiConstructorParamsNotFoundError, +} from '../../errors' +import { Hex } from '../../types' +import { decodeAbi } from './decodeAbi' + +const docsPath = '/docs/contract/decodeDeployData' + +export function decodeDeployData({ + abi, + bytecode, + data, +}: { abi: TAbi; bytecode: Hex; data: Hex }): { + args?: readonly unknown[] | undefined + bytecode: Hex +} { + if (data === bytecode) return { bytecode } + + const description = abi.find((x) => 'type' in x && x.type === 'constructor') + if (!description) throw new AbiConstructorNotFoundError({ docsPath }) + if (!('inputs' in description)) + throw new AbiConstructorParamsNotFoundError({ docsPath }) + if (!description.inputs || description.inputs.length === 0) + throw new AbiConstructorParamsNotFoundError({ docsPath }) + + const args = decodeAbi({ + data: `0x${data.replace(bytecode, '')}`, + params: description.inputs, + }) + return { args, bytecode } +} diff --git a/src/utils/abi/decodeErrorResult.ts b/src/utils/abi/decodeErrorResult.ts index 5933882ecc..c73125487d 100644 --- a/src/utils/abi/decodeErrorResult.ts +++ b/src/utils/abi/decodeErrorResult.ts @@ -11,14 +11,16 @@ export function decodeErrorResult({ abi, data }: { abi: Abi; data: Hex }) { const description = abi.find( (x) => signature === getFunctionSignature(getDefinition(x)), ) - if (!description) throw new AbiErrorSignatureNotFoundError(signature) + if (!description) + throw new AbiErrorSignatureNotFoundError(signature, { + docsPath: '/docs/contract/decodeErrorResult', + }) return { errorName: (description as { name: string }).name, - args: - 'inputs' in description && - description.inputs && - description.inputs.length > 0 - ? decodeAbi({ data: slice(data, 4), params: description.inputs }) - : undefined, + args: ('inputs' in description && + description.inputs && + description.inputs.length > 0 + ? decodeAbi({ data: slice(data, 4), params: description.inputs }) + : undefined) as readonly unknown[] | undefined, } } diff --git a/src/utils/abi/decodeFunctionData.ts b/src/utils/abi/decodeFunctionData.ts index 2356d1b048..2b012a7152 100644 --- a/src/utils/abi/decodeFunctionData.ts +++ b/src/utils/abi/decodeFunctionData.ts @@ -12,14 +12,16 @@ export function decodeFunctionData({ abi, data }: { abi: Abi; data: Hex }) { const description = abi.find( (x) => signature === getFunctionSignature(getDefinition(x)), ) - if (!description) throw new AbiFunctionSignatureNotFoundError(signature) + if (!description) + throw new AbiFunctionSignatureNotFoundError(signature, { + docsPath: '/docs/contract/decodeFunctionData', + }) return { functionName: (description as { name: string }).name, - args: - 'inputs' in description && - description.inputs && - description.inputs.length > 0 - ? decodeAbi({ data: slice(data, 4), params: description.inputs }) - : undefined, + args: ('inputs' in description && + description.inputs && + description.inputs.length > 0 + ? decodeAbi({ data: slice(data, 4), params: description.inputs }) + : undefined) as readonly unknown[] | undefined, } } diff --git a/src/utils/abi/decodeFunctionResult.test.ts b/src/utils/abi/decodeFunctionResult.test.ts index 75329e8dc3..fb2f84cf28 100644 --- a/src/utils/abi/decodeFunctionResult.test.ts +++ b/src/utils/abi/decodeFunctionResult.test.ts @@ -226,7 +226,7 @@ test("error: function doesn't exist", () => { "Function \\"baz\\" not found on ABI. Make sure you are using the correct ABI and that the function exists on it. - Docs: https://viem.sh/docs/contract/encodeFunctionData + Docs: https://viem.sh/docs/contract/decodeFunctionResult Version: viem@1.0.2" `, diff --git a/src/utils/abi/decodeFunctionResult.ts b/src/utils/abi/decodeFunctionResult.ts index 10ecb98603..dfcc142872 100644 --- a/src/utils/abi/decodeFunctionResult.ts +++ b/src/utils/abi/decodeFunctionResult.ts @@ -7,6 +7,8 @@ import { import { Hex } from '../../types' import { decodeAbi } from './decodeAbi' +const docsPath = '/docs/contract/decodeFunctionResult' + export function decodeFunctionResult< TAbi extends Abi = Abi, TFunctionName extends ExtractAbiFunctionNames = any, @@ -16,13 +18,12 @@ export function decodeFunctionResult< data, }: { abi: TAbi; functionName: TFunctionName; data: Hex }) { const description = abi.find((x) => 'name' in x && x.name === functionName) - if (!description) throw new AbiFunctionNotFoundError(functionName) + if (!description) + throw new AbiFunctionNotFoundError(functionName, { docsPath }) if (!('outputs' in description)) - throw new AbiFunctionOutputsNotFoundError(functionName) + throw new AbiFunctionOutputsNotFoundError(functionName, { docsPath }) const values = decodeAbi({ data, params: description.outputs }) - return values.length > 1 - ? values - : values.length === 1 - ? values[0] - : undefined + if (values && values.length > 1) return values + if (values && values.length === 1) return values[0] + return undefined } diff --git a/src/utils/abi/encodeAbi.test.ts b/src/utils/abi/encodeAbi.test.ts index 11fdc3f582..8a03fea449 100644 --- a/src/utils/abi/encodeAbi.test.ts +++ b/src/utils/abi/encodeAbi.test.ts @@ -1356,7 +1356,7 @@ test('invalid type', () => { "Type \\"lol\\" is not a valid encoding type. Please provide a valid ABI type. - Docs: https://viem.sh/docs/contract/encodeAbi#params + Docs: https://viem.sh/docs/contract/encodeAbi Version: viem@1.0.2" `) diff --git a/src/utils/abi/encodeAbi.ts b/src/utils/abi/encodeAbi.ts index 2769b9a8a5..cca05deca7 100644 --- a/src/utils/abi/encodeAbi.ts +++ b/src/utils/abi/encodeAbi.ts @@ -90,7 +90,9 @@ function prepareParam({ if (param.type === 'string') { return encodeString(value as unknown as string) } - throw new InvalidAbiEncodingTypeError(param.type) + throw new InvalidAbiEncodingTypeError(param.type, { + docsPath: '/docs/contract/encodeAbi', + }) } ///////////////////////////////////////////////////////////////// diff --git a/src/utils/abi/encodeDeployData.test.ts b/src/utils/abi/encodeDeployData.test.ts index e1650dbd9f..14eddd020a 100644 --- a/src/utils/abi/encodeDeployData.test.ts +++ b/src/utils/abi/encodeDeployData.test.ts @@ -71,7 +71,7 @@ test('error: constructor not found', () => { args: [69420n], }), ).toThrowErrorMatchingInlineSnapshot(` - "Constructor arguments were provided (\`args\`), but a constructor was not found on the ABI. + "A constructor was not found on the ABI. Make sure you are using the correct ABI and that the constructor exists on it. Docs: https://viem.sh/docs/contract/encodeDeployData diff --git a/src/utils/abi/encodeDeployData.ts b/src/utils/abi/encodeDeployData.ts index 2f8d326c9c..27f8191d85 100644 --- a/src/utils/abi/encodeDeployData.ts +++ b/src/utils/abi/encodeDeployData.ts @@ -8,6 +8,8 @@ import { ExtractConstructorArgsFromAbi, Hex } from '../../types' import { concatHex } from '../data' import { encodeAbi } from './encodeAbi' +const docsPath = '/docs/contract/encodeDeployData' + export function encodeDeployData({ abi, args, @@ -16,10 +18,11 @@ export function encodeDeployData({ if (!args || args.length === 0) return bytecode const description = abi.find((x) => 'type' in x && x.type === 'constructor') - if (!description) throw new AbiConstructorNotFoundError() - if (!('inputs' in description)) throw new AbiConstructorParamsNotFoundError() + if (!description) throw new AbiConstructorNotFoundError({ docsPath }) + if (!('inputs' in description)) + throw new AbiConstructorParamsNotFoundError({ docsPath }) if (!description.inputs || description.inputs.length === 0) - throw new AbiConstructorParamsNotFoundError() + throw new AbiConstructorParamsNotFoundError({ docsPath }) const data = encodeAbi({ params: description.inputs, diff --git a/src/utils/abi/encodeErrorResult.ts b/src/utils/abi/encodeErrorResult.ts index 731fa32273..501e4ec0cd 100644 --- a/src/utils/abi/encodeErrorResult.ts +++ b/src/utils/abi/encodeErrorResult.ts @@ -10,6 +10,8 @@ import { getFunctionSignature } from '../hash' import { encodeAbi } from './encodeAbi' import { getDefinition } from './getDefinition' +const docsPath = '/docs/contract/encodeErrorResult' + export function encodeErrorResult< TAbi extends Abi = Abi, TErrorName extends ExtractAbiErrorNames = any, @@ -22,14 +24,14 @@ export function encodeErrorResult< TErrorName >) { const description = abi.find((x) => 'name' in x && x.name === errorName) - if (!description) throw new AbiErrorNotFoundError(errorName) + if (!description) throw new AbiErrorNotFoundError(errorName, { docsPath }) const definition = getDefinition(description) const signature = getFunctionSignature(definition) let data: Hex = '0x' if (args && args.length > 0) { if (!('inputs' in description && description.inputs)) - throw new AbiErrorInputsNotFoundError(errorName) + throw new AbiErrorInputsNotFoundError(errorName, { docsPath }) data = encodeAbi({ params: description.inputs, values: args as any }) } return concatHex([signature, data]) diff --git a/src/utils/abi/encodeEventTopics.ts b/src/utils/abi/encodeEventTopics.ts index 73e720b75c..9b47a31c01 100644 --- a/src/utils/abi/encodeEventTopics.ts +++ b/src/utils/abi/encodeEventTopics.ts @@ -27,7 +27,10 @@ export function encodeEventTopics< eventName: TEventName } & ExtractEventArgsFromAbi) { const description = abi.find((x) => 'name' in x && x.name === eventName) - if (!description) throw new AbiEventNotFoundError(eventName) + if (!description) + throw new AbiEventNotFoundError(eventName, { + docsPath: '/docs/contract/encodeEventTopics', + }) const definition = getDefinition(description) const signature = getEventSignature(definition as `${string}(${string})`) diff --git a/src/utils/abi/encodeFunctionData.ts b/src/utils/abi/encodeFunctionData.ts index f30f4d662d..8f1168139c 100644 --- a/src/utils/abi/encodeFunctionData.ts +++ b/src/utils/abi/encodeFunctionData.ts @@ -19,7 +19,10 @@ export function encodeFunctionData< TFunctionName >) { const description = abi.find((x) => 'name' in x && x.name === functionName) - if (!description) throw new AbiFunctionNotFoundError(functionName) + if (!description) + throw new AbiFunctionNotFoundError(functionName, { + docsPath: '/docs/contract/encodeFunctionData', + }) const definition = getDefinition(description) const signature = getFunctionSignature(definition) const data = diff --git a/src/utils/abi/encodeFunctionResult.test.ts b/src/utils/abi/encodeFunctionResult.test.ts index 1ef075a318..6597686287 100644 --- a/src/utils/abi/encodeFunctionResult.test.ts +++ b/src/utils/abi/encodeFunctionResult.test.ts @@ -248,7 +248,7 @@ test("error: function doesn't exist", () => { "Function \\"baz\\" not found on ABI. Make sure you are using the correct ABI and that the function exists on it. - Docs: https://viem.sh/docs/contract/encodeFunctionData + Docs: https://viem.sh/docs/contract/encodeFunctionResult Version: viem@1.0.2" `, @@ -276,7 +276,7 @@ test("error: function doesn't exist", () => { Cannot decode function result without knowing what the parameter types are. Make sure you are using the correct ABI and that the function exists on it. - Docs: https://viem.sh/docs/contract/decodeFunctionResult + Docs: https://viem.sh/docs/contract/encodeFunctionResult Version: viem@1.0.2" `, diff --git a/src/utils/abi/encodeFunctionResult.ts b/src/utils/abi/encodeFunctionResult.ts index 9d983683cb..5f7f2d37f5 100644 --- a/src/utils/abi/encodeFunctionResult.ts +++ b/src/utils/abi/encodeFunctionResult.ts @@ -7,6 +7,8 @@ import { import { ExtractResultFromAbi } from '../../types' import { encodeAbi } from './encodeAbi' +const docsPath = '/docs/contract/encodeFunctionResult' + export function encodeFunctionResult< TAbi extends Abi = Abi, TFunctionName extends ExtractAbiFunctionNames = any, @@ -19,9 +21,10 @@ export function encodeFunctionResult< TFunctionName >) { const description = abi.find((x) => 'name' in x && x.name === functionName) - if (!description) throw new AbiFunctionNotFoundError(functionName) + if (!description) + throw new AbiFunctionNotFoundError(functionName, { docsPath }) if (!('outputs' in description)) - throw new AbiFunctionOutputsNotFoundError(functionName) + throw new AbiFunctionOutputsNotFoundError(functionName, { docsPath }) let values = Array.isArray(result) ? result : [result] if (description.outputs.length === 0 && !values[0]) values = [] diff --git a/src/utils/abi/getDefinition.ts b/src/utils/abi/getDefinition.ts index 6c13a1043c..2ec70ab100 100644 --- a/src/utils/abi/getDefinition.ts +++ b/src/utils/abi/getDefinition.ts @@ -7,7 +7,9 @@ export function getDefinition(description: AbiFunction | AbiEvent | AbiError) { description.type !== 'event' && description.type !== 'error' ) - throw new InvalidDefinitionTypeError(description.type) + throw new InvalidDefinitionTypeError(description.type, { + docsPath: '/docs/contract/getDefinition', + }) return `${description.name}(${getParams(description.inputs)})` }