@riaskov/iohtee-abi-wrapper 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,21 @@
1
+ # IohTee ABI Wrapper
2
+
3
+ [![Module type: ESM](https://img.shields.io/badge/module%20type-ESM-brightgreen)]()
4
+ [![Target: Node20](https://img.shields.io/badge/Node.js->=20-brightgreen)]()
5
+ [![Target: ES2022](https://img.shields.io/badge/target-ES2022-brightgreen)]()
6
+
7
+ IohTee ABI Wrapper is a TypeScript wrapper for EVM-compatible ABI of Solidity smartcontract.
8
+ It takes raw json artifact file and render ready-to-use TS-wrapper. Use viem v2 internally.
9
+
10
+ ## Run
11
+
12
+ ```bash
13
+ $ iohtee-abi-wrapper -o ./output ./abi
14
+ ```
15
+
16
+ ## TODO
17
+ - add postprocess with prettier
18
+ - TS-wrapper docs autogen
19
+ - add errors and library references handling
20
+ - generate d.ts and min.js wrapper files
21
+ - add solidity comments to wrapper
@@ -0,0 +1,8 @@
1
+ export declare class AbiWrapper {
2
+ templatesDir: string;
3
+ outputDir: string;
4
+ pattern: string;
5
+ minify: boolean;
6
+ constructor(pattern: string, outputDir: string, minify?: boolean);
7
+ run(): Promise<void>;
8
+ }
@@ -0,0 +1,33 @@
1
+ import fs from 'node:fs';
2
+ import ContractTemplate from './contractTemplate.js';
3
+ import { globSync } from 'glob';
4
+ import { mkdirp } from 'mkdirp';
5
+ import path from 'node:path';
6
+ export class AbiWrapper {
7
+ templatesDir;
8
+ outputDir;
9
+ pattern;
10
+ minify;
11
+ constructor(pattern, outputDir, minify) {
12
+ this.pattern = pattern;
13
+ this.templatesDir = path.join(import.meta.dirname, '../templates');
14
+ this.outputDir = outputDir;
15
+ this.minify = !!minify;
16
+ }
17
+ async run() {
18
+ if (!fs.existsSync(this.outputDir)) {
19
+ mkdirp.sync(this.outputDir);
20
+ }
21
+ const fileNames = globSync(this.pattern);
22
+ if (fileNames.length) {
23
+ fileNames.forEach((fileName) => {
24
+ let transformer = new ContractTemplate(this.templatesDir, this.outputDir);
25
+ transformer.render(fileName, this.minify);
26
+ });
27
+ }
28
+ else {
29
+ throw new Error(`No Truffle Contract artifact found at ${this.pattern}`);
30
+ }
31
+ }
32
+ }
33
+ //# sourceMappingURL=abiWrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abiWrapper.js","sourceRoot":"src/","sources":["abiWrapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,gBAAgB,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAQ,QAAQ,EAAE,MAAM,MAAM,CAAA;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,MAAM,OAAO,UAAU;IACrB,YAAY,CAAQ;IACpB,SAAS,CAAQ;IACjB,OAAO,CAAQ;IACf,MAAM,CAAS;IAEf,YAAY,OAAe,EAAE,SAAiB,EAAE,MAAgB;QAC9D,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,CAAA;QAClE,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAA;IACxB,CAAC;IAED,KAAK,CAAC,GAAG;QACP,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAC7B,CAAC;QACD,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QACxC,IAAI,SAAS,CAAC,MAAM,EAAE,CAAC;YACrB,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE;gBAC7B,IAAI,WAAW,GAAG,IAAI,gBAAgB,CACpC,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,SAAS,CACf,CAAA;gBACD,WAAW,CAAC,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,CAAA;YAC3C,CAAC,CAAC,CAAA;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,yCAAyC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAA;QAC1E,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ import yargs from 'yargs';
3
+ import { AbiWrapper } from '../index.js';
4
+ let args = yargs(process.argv.slice(2))
5
+ .option('output', {
6
+ describe: 'Folder for generated files',
7
+ alias: 'o',
8
+ })
9
+ .option('minify', {
10
+ describe: 'Also render minified JS-wrapper',
11
+ alias: 'm',
12
+ }).argv;
13
+ const pattern = args._[0];
14
+ const outputDir = args['output'];
15
+ const minify = args['minify'];
16
+ let abiWrapper = new AbiWrapper(pattern, outputDir, minify);
17
+ abiWrapper
18
+ .run()
19
+ .then(() => {
20
+ // Do Nothing
21
+ })
22
+ .catch((error) => {
23
+ console.error(error);
24
+ process.exit(1);
25
+ });
26
+ //# sourceMappingURL=iohtee-abi-wrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"iohtee-abi-wrapper.js","sourceRoot":"src/","sources":["bin/iohtee-abi-wrapper.ts"],"names":[],"mappings":";AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAExC,IAAI,IAAI,GAAQ,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;KACzC,MAAM,CAAC,QAAQ,EAAE;IAChB,QAAQ,EAAE,4BAA4B;IACtC,KAAK,EAAE,GAAG;CACX,CAAC;KACD,MAAM,CAAC,QAAQ,EAAE;IAChB,QAAQ,EAAE,iCAAiC;IAC3C,KAAK,EAAE,GAAG;CACX,CAAC,CAAC,IAAI,CAAA;AAET,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACzB,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAA;AAChC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAA;AAE7B,IAAI,UAAU,GAAG,IAAI,UAAU,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;AAC3D,UAAU;KACP,GAAG,EAAE;KACL,IAAI,CAAC,GAAG,EAAE;IACT,aAAa;AACf,CAAC,CAAC;KACD,KAAK,CAAC,CAAC,KAAY,EAAE,EAAE;IACtB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IACpB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
@@ -0,0 +1,13 @@
1
+ import { AbiEvent, AbiFunction } from 'viem';
2
+ export interface MethodAbi extends AbiFunction {
3
+ singleReturnValue: boolean;
4
+ }
5
+ export default interface Context {
6
+ artifact: string;
7
+ abi: string;
8
+ contractName: string;
9
+ relativeArtifactPath: string;
10
+ getters: Array<AbiFunction>;
11
+ functions: Array<AbiFunction>;
12
+ events: Array<AbiEvent>;
13
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"src/","sources":["context.ts"],"names":[],"mappings":""}
@@ -0,0 +1,14 @@
1
+ import Handlebars from 'handlebars';
2
+ import Context from './context.js';
3
+ export default class ContractTemplate {
4
+ handlebars: typeof Handlebars;
5
+ templatesDir: string;
6
+ outputDir: string;
7
+ private _template?;
8
+ constructor(templatesDir: string, outputDir: string);
9
+ get template(): Handlebars.TemplateDelegate<Context>;
10
+ registerPartials(): void;
11
+ registerHelpers(): void;
12
+ render(abiFilePath: string, minified?: boolean): void;
13
+ protected readTemplate(name: string): string;
14
+ }
@@ -0,0 +1,117 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs';
3
+ import { build } from 'esbuild';
4
+ import Handlebars from 'handlebars';
5
+ import * as helpers from './helpers.js';
6
+ const ABI_TYPE_FUNCTION = 'function';
7
+ const ABI_TYPE_EVENT = 'event';
8
+ function isAbiFunction(abi) {
9
+ return abi.type === ABI_TYPE_FUNCTION;
10
+ }
11
+ function isAbiEvent(abi) {
12
+ return abi.type === ABI_TYPE_EVENT;
13
+ }
14
+ export default class ContractTemplate {
15
+ handlebars;
16
+ templatesDir;
17
+ outputDir;
18
+ _template;
19
+ constructor(templatesDir, outputDir) {
20
+ this.handlebars = Handlebars.create();
21
+ this.templatesDir = templatesDir;
22
+ this.outputDir = outputDir;
23
+ this.registerPartials();
24
+ this.registerHelpers();
25
+ }
26
+ get template() {
27
+ if (this._template) {
28
+ return this._template;
29
+ }
30
+ else {
31
+ let contents = this.readTemplate('contract.mustache');
32
+ this._template = this.handlebars.compile(contents, {
33
+ noEscape: true,
34
+ });
35
+ return this._template;
36
+ }
37
+ }
38
+ registerPartials() {
39
+ fs.readdirSync(this.templatesDir).forEach((file) => {
40
+ let match = file.match(/^_(\w+)\.(handlebars|mustache)/);
41
+ if (match) {
42
+ this.handlebars.registerPartial(match[1], this.readTemplate(file));
43
+ }
44
+ });
45
+ }
46
+ registerHelpers() {
47
+ this.handlebars.registerHelper('inputType', helpers.inputType);
48
+ this.handlebars.registerHelper('outputType', helpers.outputType);
49
+ }
50
+ render(abiFilePath, minified) {
51
+ let artifact = JSON.parse(fs.readFileSync(abiFilePath).toString());
52
+ const sourceAbi = JSON.stringify(artifact.abi);
53
+ let abi = artifact.abi;
54
+ if (abi) {
55
+ let methods = abi.filter(isAbiFunction).map((abi) => {
56
+ if (abi.outputs.length === 1) {
57
+ abi.singleReturnValue = true;
58
+ }
59
+ abi.inputs = abi.inputs.map((input, index) => {
60
+ input.name = input.name ? input.name : `param${index}`;
61
+ return input;
62
+ });
63
+ return abi;
64
+ });
65
+ const nameCount = {};
66
+ methods = methods.map((abi) => {
67
+ const originalName = abi.name;
68
+ if (nameCount[originalName] >= 0) {
69
+ nameCount[originalName]++;
70
+ abi['namePostfix'] = nameCount[originalName];
71
+ }
72
+ else {
73
+ nameCount[originalName] = 0;
74
+ }
75
+ return abi;
76
+ });
77
+ let getters = methods.filter((abi) => abi.stateMutability === 'view' || abi.stateMutability === 'pure');
78
+ let functions = methods.filter((abi) => abi.stateMutability !== 'view' && abi.stateMutability !== 'pure');
79
+ let events = abi.filter(isAbiEvent);
80
+ let contractName = path.parse(abiFilePath).name;
81
+ const basename = path.basename(abiFilePath, path.extname(abiFilePath));
82
+ const filePath = `${this.outputDir}/${basename}Contract.ts`;
83
+ const relativeArtifactPath = path.relative(this.outputDir, abiFilePath);
84
+ let context = {
85
+ artifact: JSON.stringify(artifact, null, 2),
86
+ abi: JSON.stringify(sourceAbi),
87
+ contractName: contractName,
88
+ relativeArtifactPath: relativeArtifactPath,
89
+ getters: getters,
90
+ functions: functions,
91
+ events: events,
92
+ };
93
+ let code = this.template(context);
94
+ fs.writeFileSync(filePath, code);
95
+ if (minified) {
96
+ build({
97
+ entryPoints: [filePath],
98
+ outfile: `${filePath}.min.js`,
99
+ bundle: false,
100
+ minify: true,
101
+ format: 'esm',
102
+ platform: 'node',
103
+ sourcemap: false,
104
+ target: ['es2020'],
105
+ }).then();
106
+ }
107
+ }
108
+ else {
109
+ throw new Error(`No ABI found in ${abiFilePath}.`);
110
+ }
111
+ }
112
+ readTemplate(name) {
113
+ let file = path.resolve(this.templatesDir, name);
114
+ return fs.readFileSync(file).toString();
115
+ }
116
+ }
117
+ //# sourceMappingURL=contractTemplate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contractTemplate.js","sourceRoot":"src/","sources":["contractTemplate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function inputType(solidityType: string, components?: Array<any>): string;
2
+ export declare function outputType(solidityType: string): string;
@@ -0,0 +1,43 @@
1
+ const TYPE_MAPPING = [
2
+ { regex: '^string$', tsType: 'string' },
3
+ { regex: '^address$', tsType: '`0x${string}`' },
4
+ { regex: '^bool$', tsType: 'boolean' },
5
+ { regex: '^u?int\\d*$', tsType: 'bigint' },
6
+ { regex: '^bytes\\d*$', tsType: '`0x${string}`' },
7
+ ];
8
+ const INPUT_TYPE_MAPPING = [
9
+ { regex: '^u?int(8|16|32|64|128|256)?$', tsType: 'bigint' },
10
+ ].concat(TYPE_MAPPING);
11
+ const ARRAY_BRACES = /\[\d*]$/;
12
+ function isArray(solidityType) {
13
+ return !!solidityType.match(ARRAY_BRACES);
14
+ }
15
+ function isTuple(solidityType) {
16
+ return !!solidityType.match('tuple');
17
+ }
18
+ function typeConversion(types, solidityType, components) {
19
+ if (isArray(solidityType)) {
20
+ const solidityItemType = solidityType.replace(ARRAY_BRACES, '');
21
+ const type = typeConversion(types, solidityItemType);
22
+ return `${type}[]`;
23
+ }
24
+ else if (isTuple(solidityType) && components) {
25
+ return `[${components.map((e) => inputType(e.type)).join(' ,')}]`;
26
+ }
27
+ else {
28
+ let mapping = types.find((mapping) => !!solidityType.match(mapping.regex));
29
+ if (mapping) {
30
+ return mapping.tsType;
31
+ }
32
+ else {
33
+ throw new Error(`Unknown Solidity type found: ${solidityType}`);
34
+ }
35
+ }
36
+ }
37
+ export function inputType(solidityType, components) {
38
+ return typeConversion(INPUT_TYPE_MAPPING, solidityType, components);
39
+ }
40
+ export function outputType(solidityType) {
41
+ return typeConversion(TYPE_MAPPING, solidityType);
42
+ }
43
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"src/","sources":["helpers.ts"],"names":[],"mappings":"AAKA,MAAM,YAAY,GAAG;IACnB,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE;IACvC,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,EAAE;IAC/C,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE;IACtC,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE;IAC1C,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,eAAe,EAAE;CAClD,CAAA;AAED,MAAM,kBAAkB,GAAG;IACzB,EAAE,KAAK,EAAE,8BAA8B,EAAE,MAAM,EAAE,QAAQ,EAAE;CAC5D,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;AAEtB,MAAM,YAAY,GAAG,SAAS,CAAA;AAE9B,SAAS,OAAO,CAAC,YAAoB;IACnC,OAAO,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;AAC3C,CAAC;AAED,SAAS,OAAO,CAAC,YAAoB;IACnC,OAAO,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;AACtC,CAAC;AAED,SAAS,cAAc,CACrB,KAAqB,EACrB,YAAoB,EACpB,UAAuB;IAEvB,IAAI,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QAC1B,MAAM,gBAAgB,GAAG,YAAY,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAA;QAC/D,MAAM,IAAI,GAAG,cAAc,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAA;QACpD,OAAO,GAAG,IAAI,IAAI,CAAA;IACpB,CAAC;SAAM,IAAI,OAAO,CAAC,YAAY,CAAC,IAAI,UAAU,EAAE,CAAC;QAC/C,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAA;IACnE,CAAC;SAAM,CAAC;QACN,IAAI,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAA;QAC1E,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,OAAO,CAAC,MAAM,CAAA;QACvB,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,gCAAgC,YAAY,EAAE,CAAC,CAAA;QACjE,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,YAAoB,EACpB,UAAuB;IAEvB,OAAO,cAAc,CAAC,kBAAkB,EAAE,YAAY,EAAE,UAAU,CAAC,CAAA;AACrE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,YAAoB;IAC7C,OAAO,cAAc,CAAC,YAAY,EAAE,YAAY,CAAC,CAAA;AACnD,CAAC"}
@@ -0,0 +1 @@
1
+ export { AbiWrapper } from './abiWrapper.js';
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { AbiWrapper } from './abiWrapper.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"src/","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@riaskov/iohtee-abi-wrapper",
3
+ "version": "2.0.0",
4
+ "description": "Generate TypeScript wrapper class for Solidity smart contract ABI",
5
+ "license": "Apache-2.0",
6
+ "homepage": "https://github.com/ARyaskov/iohtee#readme",
7
+ "bugs": {
8
+ "url": "https://github.com/ARyaskov/iohtee/issues"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/ARyaskov/iohtee.git"
13
+ },
14
+ "engines": {
15
+ "node": ">=20"
16
+ },
17
+ "type": "module",
18
+ "main": "dist/index.js",
19
+ "types": "dist/index.d.ts",
20
+ "bin": "dist/bin/iohtee-abi-wrapper.js",
21
+ "files": [
22
+ "dist/",
23
+ "templates/"
24
+ ],
25
+ "scripts": {
26
+ "demo": "tsx src/bin/iohtee-abi-wrapper.ts ./abi/* -m -o ../contracts/src/abi-wrapper/",
27
+ "build": "yarn format && yarn clean && tsc --project tsconfig.json",
28
+ "clean": "rimraf dist",
29
+ "test": "exit 0",
30
+ "coverage": "exit 0",
31
+ "format": "npx prettier --write \"src/**/*.ts\""
32
+ },
33
+ "dependencies": {
34
+ "chalk": "^5.3.0",
35
+ "console.table": "^0.10.0",
36
+ "esbuild": "^0.21.5",
37
+ "glob": "^10.4.2",
38
+ "handlebars": "^4.7.8",
39
+ "mkdirp": "^3.0.1",
40
+ "viem": "*",
41
+ "yargs": "^17.7.2"
42
+ },
43
+ "devDependencies": {
44
+ "@types/yargs": "^17.0.32",
45
+ "rimraf": "*"
46
+ }
47
+ }
@@ -0,0 +1 @@
1
+ {{this.name}} = "{{this.name}}"{{#unless @last}}, {{/unless}}
@@ -0,0 +1,10 @@
1
+ export interface {{this.name}} {
2
+ {{#if inputs}}
3
+ args: {
4
+ {{#each inputs}}
5
+ {{name}}: {{#inputType type}}{{/inputType}}
6
+ {{/each}}
7
+ }
8
+ {{/if}}
9
+ }
10
+
@@ -0,0 +1,3 @@
1
+ is{{this.name}}Event (something: AbiEvent): boolean {
2
+ return something.name === "{{this.name}}"
3
+ }
@@ -0,0 +1,3 @@
1
+ {{this.name}}: {
2
+ ({{#if inputs}}filter: { {{#each inputs}}{{name}}?: {{#inputType type}}{{/inputType}}{{#unless @last}}, {{/unless}}{{/each}} }, {{/if}}options?: any): any
3
+ }
@@ -0,0 +1,21 @@
1
+ async {{this.name}}{{this.namePostfix}}({{> method_input inputs=inputs trailingComma=true}}options?: TxOptions): Promise<TransactionReceipt>{
2
+ const { request } = await this.publicClient().simulateContract({
3
+ chain: this.walletClient().chain,
4
+ address: this.address(),
5
+ abi: this.abi(),
6
+ functionName: "{{this.name}}",
7
+ args: [
8
+ {{> params inputs=inputs}}
9
+ ],
10
+ account: this.walletClient().account,
11
+ value: options?.value,
12
+ })
13
+ const txId = await this.walletClient().writeContract(request)
14
+
15
+ return await this.publicClient().waitForTransactionReceipt({
16
+ hash: txId,
17
+ confirmations: 3,
18
+ timeout: 45_000,
19
+ })
20
+ }
21
+
@@ -0,0 +1,10 @@
1
+ async {{this.name}}{{this.namePostfix}}({{> method_input inputs=inputs}}): Promise<{{> method_output outputs=outputs}}> {
2
+ return (await this.publicClient().readContract({
3
+ address: this.address(),
4
+ abi: this.abi(),
5
+ functionName: "{{this.name}}",
6
+ args: [
7
+ {{> params inputs=inputs}}
8
+ ],
9
+ })) as never as {{> method_output outputs=outputs}}
10
+ }
@@ -0,0 +1,4 @@
1
+ {{~#if inputs~}}
2
+ {{~#each inputs~}}{{name}}: {{#inputType type components}} {{/inputType}}{{#unless @last}}, {{/unless}}{{~/each~}}
3
+ {{~#if trailingComma~}}, {{/if}}
4
+ {{~/if~}}
@@ -0,0 +1,10 @@
1
+ {{~#if outputs~}}
2
+ {{~#singleReturnValue~}}
3
+ {{#outputType outputs.0.type}}{{/outputType}}
4
+ {{~/singleReturnValue~}}
5
+ {{~^singleReturnValue~}}
6
+ [{{#each outputs}}{{#outputType type}}{{/outputType}}{{#unless @last}}, {{/unless}}{{/each}}]
7
+ {{~/singleReturnValue~}}
8
+ {{~else~}}
9
+ void
10
+ {{~/if~}}
@@ -0,0 +1 @@
1
+ {{#each inputs}}{{name}}{{#unless @last}}, {{/unless}}{{/each}}
@@ -0,0 +1,209 @@
1
+ import {
2
+ Abi,
3
+ AbiEvent,
4
+ createPublicClient,
5
+ createWalletClient,
6
+ getContract,
7
+ http,
8
+ PublicClient,
9
+ WalletClient,
10
+ GetContractReturnType,
11
+ TransactionReceipt,
12
+ parseEventLogs,
13
+ ParseEventLogsReturnType,
14
+ extractChain
15
+ } from "viem"
16
+ import { mnemonicToAccount } from "viem/accounts"
17
+ import * as chains from "viem/chains"
18
+
19
+ export interface TxOptions {
20
+ value?: bigint
21
+ }
22
+
23
+ export type CtorBaseParams = {
24
+ cachePeriod?: number
25
+ }
26
+
27
+ export type CtorAccountParamPure = CtorBaseParams & {
28
+ httpRpcUrl: string
29
+ networkId: number
30
+ mnemonic: string
31
+ hdPath: `m/44'/60'/${string}`
32
+ }
33
+
34
+ export type CtorAccountParamViem = CtorBaseParams & {
35
+ publicClientViem: PublicClient
36
+ walletClientViem: WalletClient
37
+ }
38
+
39
+ export function isCtorAccountParamPure(
40
+ params: CtorBaseParams,
41
+ ): params is CtorAccountParamPure {
42
+ return (
43
+ (params as CtorAccountParamPure).httpRpcUrl !== undefined &&
44
+ (params as CtorAccountParamPure).mnemonic !== undefined
45
+ )
46
+ }
47
+
48
+ export type CtorParams = CtorAccountParamPure | CtorAccountParamViem
49
+
50
+ export interface {{contractName}}Event {
51
+ eventName: string
52
+ args: any
53
+ address: `0x${string}`
54
+ blockHash: `0x${string}`
55
+ blockNumber: bigint
56
+ data: `0x${string}`
57
+ logIndex: number
58
+ removed: boolean
59
+ topics: [] | [`0x${string}`, ...`0x${string}`[]]
60
+ transactionHash: `0x${string}`
61
+ transactionIndex: number
62
+ }
63
+
64
+ {{#each events}}
65
+ {{> event_types contractName=../contractName}}
66
+ {{/each}}
67
+
68
+ export enum {{contractName}}EventName {
69
+ {{#each events}}
70
+ {{> event_enum contractName=../contractName}}
71
+ {{/each}}
72
+ }
73
+
74
+ const abi = JSON.parse(`{{abi}}`)
75
+
76
+ export class {{contractName}}Contract {
77
+ private readonly _publicClient: PublicClient
78
+ private readonly _walletClient: WalletClient
79
+ private readonly _address: `0x${string}`
80
+ private readonly _contract: GetContractReturnType
81
+ private readonly _abi: any
82
+
83
+ /// GETTERS
84
+ {{#each getters}}
85
+ {{> getter contractName=../contractName}}
86
+
87
+ {{/each}}
88
+
89
+ /// SETTERS
90
+ {{#each functions}}
91
+ {{> function contractName=../contractName}}
92
+
93
+ {{/each}}
94
+
95
+ /// EVENTS
96
+ {{#each events}}
97
+ {{> event_utils contractName=../contractName}}
98
+
99
+ {{/each}}
100
+
101
+ static parseLogs(receipt: TransactionReceipt): ParseEventLogsReturnType {
102
+ const logs = parseEventLogs({
103
+ abi: abi as any,
104
+ logs: receipt.logs,
105
+ })
106
+ return logs as any
107
+ }
108
+
109
+ static hasEvent(receipt: TransactionReceipt, eventName: {{contractName}}EventName): boolean {
110
+ return parseEventLogs({ abi: abi, logs: receipt.logs }).some((log: any) => log.eventName === eventName)
111
+ }
112
+
113
+ static extractEventFromReceipt<T>(receipt: TransactionReceipt, eventName: {{contractName}}EventName): T {
114
+ return parseEventLogs({ abi: abi, logs: receipt.logs }).find((log: any) => log.eventName === eventName) as T
115
+ }
116
+
117
+ static parseEvents(receipt: TransactionReceipt): {{contractName}}Event[] {
118
+ return parseEventLogs({ abi: abi, logs: receipt.logs }).map((log: any) => {
119
+ return {
120
+ eventName: log.eventName,
121
+ args: log.args,
122
+ address: log.address,
123
+ blockHash: log.blockHash,
124
+ blockNumber: log.blockNumber,
125
+ data: log.data,
126
+ logIndex: log.logIndex,
127
+ removed: log.removed,
128
+ topics: log.topics,
129
+ transactionHash: log.transactionHash,
130
+ transactionIndex: log.transactionIndex,
131
+ }
132
+ })
133
+ }
134
+
135
+ constructor(deployedContractAddress: `0x${string}`, params: CtorParams) {
136
+ this._address = deployedContractAddress
137
+
138
+ if (isCtorAccountParamPure(params)) {
139
+ let network = extractChain({
140
+ chains: Object.values(chains) as any,
141
+ id: params.networkId,
142
+ })
143
+ // @ts-ignore
144
+ this._publicClient = createPublicClient({
145
+ batch: {
146
+ multicall: true,
147
+ },
148
+ // @ts-ignore
149
+ chain: network as any,
150
+ transport: http(
151
+ params.httpRpcUrl,
152
+ {
153
+ batch: true,
154
+ }
155
+ ),
156
+ })
157
+ this._walletClient = createWalletClient({
158
+ // @ts-ignore
159
+ chain: network as any,
160
+ transport: http(
161
+ params.httpRpcUrl,
162
+ {
163
+ batch: true,
164
+ }
165
+ ),
166
+ account: mnemonicToAccount(params.mnemonic, { path: params.hdPath }),
167
+ })
168
+ } else {
169
+ this._publicClient = params.publicClientViem
170
+ this._walletClient = params.walletClientViem
171
+ }
172
+
173
+ this._abi = abi
174
+
175
+ this._contract = getContract({
176
+ address: this._address,
177
+ abi: this._abi,
178
+ client: {
179
+ public: this._publicClient as never,
180
+ wallet: this._walletClient as never,
181
+ },
182
+ })
183
+
184
+ }
185
+
186
+ publicClient(): PublicClient {
187
+ return this._publicClient
188
+ }
189
+
190
+ walletClient(): WalletClient {
191
+ return this._walletClient
192
+ }
193
+
194
+ contract(): GetContractReturnType {
195
+ return this._contract
196
+ }
197
+
198
+ address(): `0x${string}` {
199
+ return this._address
200
+ }
201
+
202
+ abi(): Abi {
203
+ return this._abi
204
+ }
205
+
206
+ events(): AbiEvent[] {
207
+ return this.abi().filter((e) => e.type === 'event') as AbiEvent[]
208
+ }
209
+ }