Skip to content

Commit

Permalink
refactor: narrowify
Browse files Browse the repository at this point in the history
  • Loading branch information
jxom committed Feb 9, 2023
1 parent 40c76e3 commit ea9d5f7
Show file tree
Hide file tree
Showing 27 changed files with 193 additions and 184 deletions.
8 changes: 4 additions & 4 deletions src/actions/public/deployContract.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Abi } from 'abitype'
import { Abi, Narrow } from 'abitype'
import { WalletClient } from '../../clients'

import { Chain, ExtractConstructorArgsFromAbi, Hex } from '../../types'
Expand All @@ -11,18 +11,18 @@ import {

export type DeployContractArgs<
TChain extends Chain = Chain,
TAbi extends Abi = Abi,
TAbi extends Abi | readonly unknown[] = Abi,
> = Omit<
SendTransactionArgs<TChain>,
'accessList' | 'to' | 'data' | 'value'
> & {
abi: TAbi
abi: Narrow<TAbi>
bytecode: Hex
} & ExtractConstructorArgsFromAbi<TAbi>

export type DeployContractResponse = SendTransactionResponse

export function deployContract<TChain extends Chain, TAbi extends Abi = Abi>(
export function deployContract<TChain extends Chain, TAbi extends Abi>(
walletClient: WalletClient,
{ abi, args, bytecode, ...request }: DeployContractArgs<TChain, TAbi>,
): Promise<DeployContractResponse> {
Expand Down
23 changes: 5 additions & 18 deletions src/actions/public/readContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,8 @@ export type FormattedReadContract<

export type ReadContractArgs<
TAbi extends Abi | readonly unknown[] = Abi,
TFunctionName extends string = any,
> = Omit<
CallArgs,
| 'accessList'
| 'chain'
| 'from'
| 'gas'
| 'gasPrice'
| 'maxFeePerGas'
| 'maxPriorityFeePerGas'
| 'nonce'
| 'to'
| 'data'
| 'value'
> &
TFunctionName extends string = string,
> = Pick<CallArgs, 'blockNumber' | 'blockTag'> &
ContractConfig<TAbi, TFunctionName, 'view' | 'pure'>

export type ReadContractResponse<
Expand All @@ -45,8 +32,8 @@ export type ReadContractResponse<
> = ExtractResultFromAbi<TAbi, TFunctionName>

export async function readContract<
TAbi extends Abi = Abi,
TFunctionName extends string = any,
TAbi extends Abi | readonly unknown[],
TFunctionName extends string,
>(
client: PublicClient,
{
Expand Down Expand Up @@ -76,7 +63,7 @@ export async function readContract<
} as DecodeFunctionResultArgs<TAbi, TFunctionName>)
} catch (err) {
throw getContractError(err as BaseError, {
abi,
abi: abi as Abi,
address,
args,
docsPath: '/docs/contract/readContract',
Expand Down
1 change: 0 additions & 1 deletion src/actions/public/simulateContract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,6 @@ test('fake contract address', async () => {
address: '0x0000000000000000000000000000000000000069',
functionName: 'mint',
from: accounts[0].address,
args: [],
}),
).rejects.toThrowErrorMatchingInlineSnapshot(`
"The contract function \\"mint\\" returned no data (\\"0x\\").
Expand Down
15 changes: 6 additions & 9 deletions src/actions/public/simulateContract.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Abi } from 'abitype'
import { Abi, Narrow } from 'abitype'

import type { PublicClient } from '../../clients'
import { BaseError } from '../../errors'
Expand Down Expand Up @@ -36,17 +36,14 @@ export type SimulateContractResponse<
TFunctionName extends string = string,
> = {
result: ExtractResultFromAbi<TAbi, TFunctionName>
request: WriteContractArgs<TChain, TAbi, TFunctionName> & {
address: Address
abi: TAbi
functionName: ExtractFunctionNameFromAbi<TAbi, TFunctionName>
} & ExtractArgsFromAbi<TAbi, TFunctionName>
request: WriteContractArgs<TChain, TAbi, TFunctionName> &
ContractConfig<TAbi, TFunctionName, 'payable' | 'nonpayable'>
}

export async function simulateContract<
TChain extends Chain,
TAbi extends Abi = Abi,
TFunctionName extends string = any,
TAbi extends Abi | readonly unknown[],
TFunctionName extends string,
>(
client: PublicClient,
{
Expand Down Expand Up @@ -86,7 +83,7 @@ export async function simulateContract<
} as unknown as SimulateContractResponse<TChain, TAbi, TFunctionName>
} catch (err) {
throw getContractError(err as BaseError, {
abi,
abi: abi as Abi,
address,
args,
docsPath: '/docs/contract/simulateContract',
Expand Down
6 changes: 3 additions & 3 deletions src/actions/wallet/writeContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
export type WriteContractArgs<
TChain extends Chain = Chain,
TAbi extends Abi | readonly unknown[] = Abi,
TFunctionName extends string = any,
TFunctionName extends string = string,
> = Omit<SendTransactionArgs<TChain>, 'to' | 'data' | 'value'> & {
value?: GetValue<TAbi, TFunctionName, SendTransactionArgs<TChain>['value']>
} & ContractConfig<TAbi, TFunctionName, 'payable' | 'nonpayable'>
Expand All @@ -21,8 +21,8 @@ export type WriteContractResponse = SendTransactionResponse

export async function writeContract<
TChain extends Chain,
TAbi extends Abi = Abi,
TFunctionName extends string = any,
TAbi extends Abi | readonly unknown[],
TFunctionName extends string,
>(
client: WalletClient,
{
Expand Down
17 changes: 15 additions & 2 deletions src/types/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import type {
ExtractAbiFunction,
ExtractAbiEvent,
ExtractAbiEventNames,
ExtractAbiFunctionNames,
ExtractAbiError,
ExtractAbiErrorNames,
ExtractAbiFunctionNames,
Narrow,
} from 'abitype'
import type { Address } from './misc'
import type { TransactionRequest } from './transaction'
Expand Down Expand Up @@ -164,6 +165,18 @@ export type ExtractEventArgsFromAbi<
args?: TArgs
}

export type ExtractErrorNameFromAbi<
TAbi extends Abi | readonly unknown[] = Abi,
TErrorName extends string = string,
> = TAbi extends Abi
? ExtractAbiErrorNames<TAbi> extends infer AbiErrorNames
?
| AbiErrorNames
| (TErrorName extends AbiErrorNames ? TErrorName : never)
| (Abi extends TAbi ? string : never)
: never
: TErrorName

export type ExtractEventNameFromAbi<
TAbi extends Abi | readonly unknown[] = Abi,
TEventName extends string = string,
Expand Down Expand Up @@ -355,7 +368,7 @@ export type ContractConfig<
TAbiStateMutability extends AbiStateMutability = AbiStateMutability,
> = {
/** Contract ABI */
abi: TAbi
abi: Narrow<TAbi>
/** Contract address */
address: Address
/** Function to invoke on the contract */
Expand Down
1 change: 1 addition & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type {
ExtractArgsFromFunctionDefinition,
ExtractConstructorArgsFromAbi,
ExtractErrorArgsFromAbi,
ExtractErrorNameFromAbi,
ExtractEventArgsFromAbi,
ExtractEventNameFromAbi,
ExtractFunctionNameFromAbi,
Expand Down
2 changes: 1 addition & 1 deletion src/utils/abi/decodeAbi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1603,7 +1603,7 @@ test('error: zero data', () => {
stateMutability: 'pure',
type: 'function',
},
] as const,
],
data: '0x',
}),
).toThrowErrorMatchingInlineSnapshot(`
Expand Down
18 changes: 11 additions & 7 deletions src/utils/abi/decodeAbi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
AbiParameter,
AbiParametersToPrimitiveTypes,
AbiParameterToPrimitiveType,
Narrow,
} from 'abitype'

import {
Expand All @@ -15,21 +16,24 @@ import { size, slice, trim } from '../data'
import { hexToBigInt, hexToBool, hexToNumber, hexToString } from '../encoding'
import { getArrayComponents } from './encodeAbi'

export type DecodeAbiArgs<TParams extends readonly AbiParameter[]> = {
export type DecodeAbiArgs<
TParams extends
| readonly AbiParameter[]
| readonly unknown[] = readonly AbiParameter[],
> = {
data: Hex
params: TParams
params: Narrow<TParams>
}

export function decodeAbi<TParams extends readonly AbiParameter[]>({
data,
params,
}: DecodeAbiArgs<TParams>) {
export function decodeAbi<
TParams extends readonly AbiParameter[] | readonly unknown[],
>({ data, params }: DecodeAbiArgs<TParams>) {
if (data === '0x' && params.length > 0) throw new AbiDecodingZeroDataError()
if (size(data) % 32 !== 0)
throw new AbiDecodingDataSizeInvalidError(size(data))
const values = decodeParams({
data,
params,
params: params as readonly AbiParameter[],
})
if (values.length === 0) return undefined
return values
Expand Down
18 changes: 7 additions & 11 deletions src/utils/abi/decodeDeployData.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ test('constructor()', () => {
stateMutability: 'nonpayable',
type: 'constructor',
},
] as const,
],
bytecode:
'0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033',
data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033',
Expand All @@ -22,13 +22,12 @@ test('constructor()', () => {
})
expect(
decodeDeployData({
// @ts-expect-error
abi: [
{
stateMutability: 'nonpayable',
type: 'constructor',
},
] as const,
],
bytecode:
'0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033',
data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033',
Expand All @@ -54,7 +53,7 @@ test('constructor(uint256)', () => {
stateMutability: 'nonpayable',
type: 'constructor',
},
] as const,
],
bytecode:
'0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033',
data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c634300080700330000000000000000000000000000000000000000000000000000000000010f2c',
Expand All @@ -69,8 +68,7 @@ test('constructor(uint256)', () => {
test('error: constructor not found', () => {
expect(() =>
decodeDeployData({
// @ts-expect-error
abi: [{}] as const,
abi: [{}],
bytecode:
'0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033',
data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c634300080700330000000000000000000000000000000000000000000000000000000000010f2c',
Expand All @@ -87,13 +85,12 @@ test('error: constructor not found', () => {
test('error: no inputs', () => {
expect(() =>
decodeDeployData({
// @ts-expect-error
abi: [
{
stateMutability: 'nonpayable',
type: 'constructor',
},
] as const,
],
bytecode:
'0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033',
data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c634300080700330000000000000000000000000000000000000000000000000000000000010f2c',
Expand All @@ -115,7 +112,7 @@ test('error: no inputs', () => {
stateMutability: 'nonpayable',
type: 'constructor',
},
] as const,
],
bytecode:
'0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033',
data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c634300080700330000000000000000000000000000000000000000000000000000000000010f2c',
Expand All @@ -131,14 +128,13 @@ test('error: no inputs', () => {
)
expect(() =>
decodeDeployData({
// @ts-expect-error
abi: [
{
inputs: undefined,
stateMutability: 'nonpayable',
type: 'constructor',
},
] as const,
],
bytecode:
'0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c63430008070033',
data: '0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220116554d4ba29ee08da9e97dc54ff9a2a65d67a648140d616fc225a25ff08c86364736f6c634300080700330000000000000000000000000000000000000000000000000000000000010f2c',
Expand Down
17 changes: 10 additions & 7 deletions src/utils/abi/decodeDeployData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,27 @@ import { decodeAbi } from './decodeAbi'

const docsPath = '/docs/contract/decodeDeployData'

export type DecodeDeployDataArgs<TAbi extends Abi = Abi> = {
abi: TAbi
bytecode: Hex
data: Hex
}
export type DecodeDeployDataArgs<TAbi extends Abi | readonly unknown[] = Abi> =
{
abi: TAbi
bytecode: Hex
data: Hex
}
export type DecodeDeployDataResponse = {
args?: readonly unknown[] | undefined
bytecode: Hex
}

export function decodeDeployData<TAbi extends Abi = Abi>({
export function decodeDeployData<TAbi extends Abi | readonly unknown[]>({
abi,
bytecode,
data,
}: DecodeDeployDataArgs<TAbi>): DecodeDeployDataResponse {
if (data === bytecode) return { bytecode }

const description = abi.find((x) => 'type' in x && x.type === 'constructor')
const description = (abi as Abi).find(
(x) => 'type' in x && x.type === 'constructor',
)
if (!description) throw new AbiConstructorNotFoundError({ docsPath })
if (!('inputs' in description))
throw new AbiConstructorParamsNotFoundError({ docsPath })
Expand Down
Loading

0 comments on commit ea9d5f7

Please sign in to comment.