Skip to content
This repository was archived by the owner on Aug 30, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/sdk.abievent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [@thirdweb-dev/sdk](./sdk.md) &gt; [AbiEvent](./sdk.abievent.md)

## AbiEvent type

<b>Signature:</b>

```typescript
export declare type AbiEvent = {
name: string;
inputs: z.infer<typeof AbiTypeSchema>[];
outputs: z.infer<typeof AbiTypeSchema>[];
comment: string;
};
```
1 change: 1 addition & 0 deletions docs/sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@

| Type Alias | Description |
| --- | --- |
| [AbiEvent](./sdk.abievent.md) | |
| [AbiFunction](./sdk.abifunction.md) | |
| [AirdropInput](./sdk.airdropinput.md) | Input model to pass a list of addresses + amount to transfer to each one |
| [Amount](./sdk.amount.md) | Represents a currency amount already formatted. ie. "1" for 1 ether. |
Expand Down
17 changes: 17 additions & 0 deletions etc/sdk.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ import { ZodTypeAny } from 'zod';
import { ZodTypeDef } from 'zod';
import { ZodUnion } from 'zod';

// @public (undocumented)
export type AbiEvent = {
name: string;
inputs: z.infer<typeof AbiTypeSchema>[];
outputs: z.infer<typeof AbiTypeSchema>[];
comment: string;
};

// @public (undocumented)
export type AbiFunction = {
name: string;
Expand Down Expand Up @@ -1302,6 +1310,8 @@ export class ContractPrimarySale<TContract extends IPrimarySale> implements Dete
export class ContractPublishedMetadata<TContract extends BaseContract> {
constructor(contractWrapper: ContractWrapper<TContract>, storage: IStorage);
// @public (undocumented)
extractEvents(): Promise<AbiEvent[]>;
// @public (undocumented)
extractFunctions(): Promise<AbiFunction[]>;
// @public
get(): Promise<PublishedMetadata>;
Expand Down Expand Up @@ -2714,6 +2724,11 @@ export function extractConstructorParamsFromAbi(abi: z.input<typeof AbiSchema>):
name: string;
}[];

// Warning: (ae-internal-missing-underscore) The name "extractEventsFromAbi" should be prefixed with an underscore because the declaration is marked as @internal
//
// @internal (undocumented)
export function extractEventsFromAbi(abi: z.input<typeof AbiSchema>, metadata?: Record<string, any>): AbiEvent[];

// Warning: (ae-internal-missing-underscore) The name "extractFunctions" should be prefixed with an underscore because the declaration is marked as @internal
//
// @internal (undocumented)
Expand Down Expand Up @@ -7061,6 +7076,8 @@ export class WrongListingTypeError extends Error {
//
// dist/src/schema/contracts/custom.d.ts:1270:5 - (ae-incompatible-release-tags) The symbol "inputs" is marked as @public, but its signature references "AbiTypeSchema" which is marked as @internal
// dist/src/schema/contracts/custom.d.ts:1271:5 - (ae-incompatible-release-tags) The symbol "outputs" is marked as @public, but its signature references "AbiTypeSchema" which is marked as @internal
// dist/src/schema/contracts/custom.d.ts:1278:5 - (ae-incompatible-release-tags) The symbol "inputs" is marked as @public, but its signature references "AbiTypeSchema" which is marked as @internal
// dist/src/schema/contracts/custom.d.ts:1279:5 - (ae-incompatible-release-tags) The symbol "outputs" is marked as @public, but its signature references "AbiTypeSchema" which is marked as @internal

// (No @packageDocumentation comment for this package)

Expand Down
63 changes: 51 additions & 12 deletions src/common/feature-detection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { BaseContract, ethers } from "ethers";
import { ContractWrapper } from "../core/classes/contract-wrapper";
import { IStorage } from "../core";
import {
AbiEvent,
AbiFunction,
AbiSchema,
AbiTypeSchema,
Expand Down Expand Up @@ -73,6 +74,32 @@ export async function extractFunctions(
return extractFunctionsFromAbi(metadata.abi, metadata.metadata);
}

/**
* @internal
* @param name
* @param metadata
* @param type
*/
function extractCommentFromMetadata(
name: string | undefined,
metadata: Record<string, any> | undefined,
type: "methods" | "events",
) {
// console.log(metadata?.output?.userdoc);
return (
metadata?.output?.userdoc?.[type]?.[
Object.keys(metadata?.output?.userdoc[type] || {}).find((fn) =>
fn.includes(name || "unknown"),
) || ""
]?.notice ||
metadata?.output?.devdoc?.[type]?.[
Object.keys(metadata?.output?.devdoc[type] || {}).find((fn) =>
fn.includes(name || "unknown"),
) || ""
]?.details
);
}

/**
*
* @param abi
Expand Down Expand Up @@ -103,18 +130,7 @@ export function extractFunctionsFromAbi(

const parsed: AbiFunction[] = [];
for (const f of functions) {
const doc =
metadata?.output?.userdoc.methods[
Object.keys(metadata?.output?.userdoc.methods || {}).find((fn) =>
fn.includes(f.name || "unknown"),
) || ""
]?.notice ||
metadata?.output?.devdoc.methods[
Object.keys(metadata?.output?.devdoc.methods || {}).find((fn) =>
fn.includes(f.name || "unknown"),
) || ""
]?.details;

const doc = extractCommentFromMetadata(f.name, metadata, "methods");
const args =
f.inputs?.map((i) => `${i.name || "key"}: ${toJSType(i)}`)?.join(", ") ||
"";
Expand All @@ -134,6 +150,29 @@ export function extractFunctionsFromAbi(
return parsed;
}

/**
* @internal
* @param abi
* @param metadata
*/
export function extractEventsFromAbi(
abi: z.input<typeof AbiSchema>,
metadata?: Record<string, any>,
): AbiEvent[] {
const events = abi.filter((el) => el.type === "event");
const parsed: AbiEvent[] = [];
for (const e of events) {
const doc = extractCommentFromMetadata(e.name, metadata, "events");
parsed.push({
inputs: e.inputs ?? [],
outputs: e.outputs ?? [],
name: e.name ?? "unknown",
comment: doc,
});
}
return parsed;
}

function toJSType(
contractType: z.input<typeof AbiTypeSchema>,
isReturnType = false,
Expand Down
19 changes: 19 additions & 0 deletions src/core/classes/contract-published-metadata.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { ContractWrapper } from "./contract-wrapper";
import {
extractEventsFromAbi,
extractFunctionsFromAbi,
fetchContractMetadataFromAddress,
} from "../../common";
import { IStorage } from "../interfaces";
import {
AbiEvent,
AbiFunction,
AbiSchema,
PublishedMetadata,
Expand Down Expand Up @@ -58,4 +60,21 @@ export class ContractPublishedMetadata<TContract extends BaseContract> {
publishedMetadata?.metadata,
);
}

/**
* @public
*/
public async extractEvents(): Promise<AbiEvent[]> {
let publishedMetadata;
try {
publishedMetadata = await this.get();
} catch (e) {
// ignore for built-in contracts
}
// to construct a contract we already **have** to have the abi on the contract wrapper, so there is no reason to look fetch it again (means this function can become synchronous as well!)
return extractEventsFromAbi(
AbiSchema.parse(this.contractWrapper.abi),
publishedMetadata?.metadata,
);
}
}
6 changes: 6 additions & 0 deletions src/schema/contracts/custom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,12 @@ export type AbiFunction = {
stateMutability: string;
comment: string;
};
export type AbiEvent = {
name: string;
inputs: z.infer<typeof AbiTypeSchema>[];
outputs: z.infer<typeof AbiTypeSchema>[];
comment: string;
};
export type ContractSource = {
filename: string;
source: string;
Expand Down
7 changes: 7 additions & 0 deletions test/custom.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ describe("Custom Contracts", async () => {
expect(functions.length).gt(0);
});

it("should extract events", async () => {
const c = await sdk.getContract(customContractAddress);
invariant(c, "Contract undefined");
const events = await c.publishedMetadata.extractEvents();
expect(events.length).gt(0);
});

it("should detect feature: metadata", async () => {
const c = await sdk.getContract(customContractAddress);
invariant(c, "Contract undefined");
Expand Down