Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

autoload: Add abiFillEmptyNames helper #150

Merged
merged 12 commits into from
Nov 8, 2024
259 changes: 259 additions & 0 deletions src/__tests__/helpers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
import { expect, describe, it } from "vitest";

import { abiFillEmptyNames } from "../helpers";
import { ABI } from "../abi";

describe("abiFillEmptyNames", () => {
type testCase = {
name: string;
abi: Array<ABI[number] | unknown>;
want: Array<ABI[number] | unknown>;
};

const testCases: testCase[] = [
{
name: "Basic test",
abi: [
{
type: "function",
selector: "0x95d376d7",
payable: false,
stateMutability: "payable",
inputs: [
{
type: "tuple",
name: "",
components: [
{ type: "uint32", name: "" },
{ type: "bytes", name: "" },
{ type: "bytes32", name: "" },
{ type: "uint64", name: "" },
{ type: "address", name: "" },
],
},
{ type: "bytes", name: "" },
],
outputs: [
{
type: "tuple",
name: "",
components: [
{ type: "uint32", name: "" },
{ type: "bytes", name: "" },
{ type: "bytes32", name: "" },
{ type: "uint64", name: "" },
{ type: "address", name: "" },
],
},
{ type: "bytes", name: "" },
],
sig: "assignJob((uint32,bytes,bytes32,uint64,address),bytes)",
name: "assignJob",
constant: false,
},
],
want: [
{
type: "function",
selector: "0x95d376d7",
payable: false,
stateMutability: "payable",
inputs: [
{
type: "tuple",
name: "",
components: [
{ type: "uint32", name: "field0" },
{ type: "bytes", name: "field1" },
{ type: "bytes32", name: "field2" },
{ type: "uint64", name: "field3" },
{ type: "address", name: "field4" },
],
},
{ type: "bytes", name: "" },
],
outputs: [
{
type: "tuple",
name: "",
components: [
{ type: "uint32", name: "field0" },
{ type: "bytes", name: "field1" },
{ type: "bytes32", name: "field2" },
{ type: "uint64", name: "field3" },
{ type: "address", name: "field4" },
],
},
{ type: "bytes", name: "" },
],
sig: "assignJob((uint32,bytes,bytes32,uint64,address),bytes)",
name: "assignJob",
constant: false,
},
],
},
{
name: "Nested tuple test",
abi: [
{
name: "test",
selector: "0x12345679",
inputs: [
{
name: "",
type: "tuple",
components: [
{
type: "tuple",
name: "",
components: [
{
type: "tuple",
name: "",
components: [
{ type: "uint256", name: "" },
{ type: "address", name: "x" },
],
},
{
type: "tuple",
name: "n1",
components: [
{ type: "uint256", name: "y" },
{ type: "address", name: "" },
],
},
],
},
],
},
],
stateMutability: "view",
type: "function",
},
],
want: [
{
name: "test",
selector: "0x12345679",
inputs: [
{
name: "",
type: "tuple",
components: [
{
type: "tuple",
name: "field0",
components: [
{
type: "tuple",
name: "field0",
components: [
{
type: "uint256",
name: "field0",
},
{ type: "address", name: "x" },
],
},
{
type: "tuple",
name: "n1",
components: [
{ type: "uint256", name: "y" },
{
type: "address",
name: "field1",
},
],
},
],
},
],
},
],
stateMutability: "view",
type: "function",
},
],
},
{
name: "Other Tuple types: Tuple[] and Tuple[k]",
abi: [
{
name: "test",
selector: "0x12345679",
inputs: [
{
name: "",
type: "tuple",
components: [
{
name: "",
type: "tuple[]",
components: [
{ name: "", type: "uint256" },
{ name: "", type: "uint256" },
],
},
{
name: "",
type: "tuple[2]",
components: [
{ name: "", type: "uint256" },
{ name: "", type: "uint256" },
],
},
],
},
],
stateMutability: "view",
type: "function",
},
],
want: [
{
name: "test",
selector: "0x12345679",
inputs: [
{
name: "",
type: "tuple",
components: [
{
name: "field0",
type: "tuple[]",
components: [
{ name: "field0", type: "uint256" },
{ name: "field1", type: "uint256" },
],
},
{
name: "field1",
type: "tuple[2]",
components: [
{ name: "field0", type: "uint256" },
{ name: "field1", type: "uint256" },
],
},
],
},
],
stateMutability: "view",
type: "function",
},
],
},
{
name: "Empty ABI",
abi: [],
want: [],
},
];

testCases.forEach((tc) => {
it(tc.name, () => {
expect(abiFillEmptyNames(tc.abi as ABI)).toStrictEqual(tc.want);
});
});
});
19 changes: 17 additions & 2 deletions src/abi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ export type ABIFunction = {
type: "function"; // TODO: constructor, receive, fallback
selector: string;
name?: string;
outputs?: {type: string, length?: number, name: string}[];
inputs?: {type: string, name: string}[];
outputs?: ABIOutput[];
inputs?: ABIInput[];
sig?: string;
sigAlts?: string[];
payable?: boolean;
Expand All @@ -21,4 +21,19 @@ export type ABIEvent = {
// TODO: ...
};

export type ABIInput = {
type: string;
name: string;
length?: number;
components?: ABIInOut[];
}

export type ABIOutput = {
type: string;
name: string;
components?: ABIInOut[];
}

export type ABIInOut = ABIInput|ABIOutput;

export type ABI = (ABIFunction|ABIEvent)[];
13 changes: 13 additions & 0 deletions src/auto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ export type AutoloadConfig = {
* @experimental
*/
enableExperimentalMetadata?: boolean;

/**
* Enable ABI helpers to apply additional transformations to the ABI before returning.
* @group Settings
* @experimental
*/
abiHelpers?: Array<(abi: ABI) => ABI>;
Copy link
Owner

@shazow shazow Nov 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still doesn't feel great to me, because the code difference is...

const result = whatsabi.autoload(address, { provider, abiHelpers: [whatsabi.abi.abiFillEmptyNames] })
result.abi // use it

vs

const result = whatsabi.autoload(address, { provider })
whatsabi.abi.abiFillEmptyNames(result.abi) // use it

Which doesn't feel like it warrants putting it into autoload?

Also now I'm thinking maybe it should be whatsabi.abi.fillEmptyNames, but I can update that later.

By the way, are you using enableExperimentalMetadata?

Copy link
Contributor Author

@yohamta yohamta Nov 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, let's remove it from autoload for now: 32be43b

I also renamed the function: 2ba3e9e

We're using enableExperimentalMetadata, though we’re not entirely sure if it's necessary at this point.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd feel more comfortable making it default under the enableExperimentalMetadata flag, since I don't think many people use it so it won't be very breaking (plus it is experimental).

Also I think WhatsABI would only generate types with empty names with that flag enabled? Or I might be mistaken.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appreciate the changes. No need to make further changes, just brainstorming at the moment. :)

}

/**
Expand Down Expand Up @@ -360,6 +367,12 @@ export async function autoload(address: string, config: AutoloadConfig): Promise
);
}

if (config.abiHelpers) {
for (const helper of config.abiHelpers) {
result.abi = helper(result.abi);
}
}

return result;
}

Expand Down
Loading
Loading