From 2bf38c7dff70f8928126df81e0c3c1ec65136dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A8=D0=B2=D0=B5=D1=86=20=D0=90=D0=BD=D0=B4=D1=80=D0=B5?= =?UTF-8?q?=D0=B9=20=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D1=8C=D0=B5=D0=B2=D0=B8?= =?UTF-8?q?=D1=87?= <91282981+Shvandre@users.noreply.github.com> Date: Wed, 27 Nov 2024 20:07:02 +0300 Subject: [PATCH 1/3] Refactored generated class so it is convenient to extend it by user. Added opcodes class generation. Added ExitCodes map generation. --- src/bindings/writeTypescript.ts | 65 ++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/src/bindings/writeTypescript.ts b/src/bindings/writeTypescript.ts index 4f06b19f6..5dcd25d06 100644 --- a/src/bindings/writeTypescript.ts +++ b/src/bindings/writeTypescript.ts @@ -192,6 +192,20 @@ export function writeTypescript( } // Errors + // Map, so user can do something like ExitCodes["Stack Overflow"] + w.append(`const ${abi.name}_errorMessages: { [key: string]: number } = {`); + w.inIndent(() => { + if (abi.errors) { + Object.entries(abi.errors).forEach(([k, abiError]) => { + const escapedMessage = abiError.message.replaceAll('"', '\\"'); + w.append(` "${escapedMessage}": ${k},`); + }); + } + }); + w.append(`};`); + w.append(); + + //map to generate ABI later. w.append( `const ${abi.name}_errors: { [key: number]: { message: string } } = {`, ); @@ -199,12 +213,12 @@ export function writeTypescript( if (abi.errors) { Object.entries(abi.errors).forEach(([k, abiError]) => { w.append( - `${k}: { message: \`${abiError.message.replaceAll("`", "\\`")}\` },`, + ` ${k}: { message: \`${abiError.message.replaceAll("`", "\\`")}\` },`, ); }); } }); - w.append(`}`); + w.append(`};`); w.append(); // Types @@ -217,6 +231,23 @@ export function writeTypescript( } }); w.append(`]`); + + //Opcodes + //So user can use them in sandbox tests + w.append("export abstract class Opcodes {"); + w.inIndent(() => { + if (abi.types) { + for (const t of abi.types) { + if (t.header) { + const hexString = + "0x" + t.header.toString(16).padStart(8, "0"); + w.append("static " + t.name + " = " + hexString); + } + } + } + }); + w.append(`}`); + w.append(); const getterNames: Map = new Map(); @@ -272,42 +303,51 @@ export function writeTypescript( if (init) { w.append( - `static async init(${writeArguments(init.args).join(", ")}) {`, + `static async init(` + + `this: new (address: Address, init?: { code: Cell, data: Cell }) => T,` + + `${writeArguments(init.args).join(", ")}) {`, ); w.inIndent(() => { w.append( - `return await ${abi.name}_init(${init!.args.map((v) => v.name).join(", ")});`, + `const init = await ${abi.name}_init(${init.args.map((v) => v.name).join(", ")});`, ); + w.append(`return new this(contractAddress(0, init), init);`); }); w.append(`}`); w.append(); w.append( - `static async fromInit(${writeArguments(init.args).join(", ")}) {`, + `static async fromInit(` + + `this: new (address: Address, init?: { code: Cell, data: Cell }) => T,` + + `${writeArguments(init.args).join(", ")}) {`, ); w.inIndent(() => { w.append( - `const init = await ${abi.name}_init(${init!.args.map((v) => v.name).join(", ")});`, + `const init = await ${abi.name}_init(${init.args.map((v) => v.name).join(", ")});`, ); w.append(`const address = contractAddress(0, init);`); - w.append(`return new ${abi.name}(address, init);`); + w.append(`return new this(address, init);`); }); w.append(`}`); w.append(); } - w.append(`static fromAddress(address: Address) {`); + w.append( + `static fromAddress(` + + `this: new (address: Address, init?: { code: Cell, data: Cell }) => T, ` + + `address: Address) {`, + ); w.inIndent(() => { - w.append(`return new ${abi.name}(address);`); + w.append(`return new this(address);`); }); w.append(`}`); w.append(); - w.append(`readonly address: Address; `); + w.append(`readonly address: Address;`); w.append(`readonly init?: { code: Cell, data: Cell };`); w.append(`readonly abi: ContractABI = {`); w.inIndent(() => { - w.append(`types: ${abi.name}_types,`); + w.append(`types: ${abi.name}_types,`); w.append(`getters: ${abi.name}_getters,`); w.append(`receivers: ${abi.name}_receivers,`); w.append(`errors: ${abi.name}_errors,`); @@ -315,7 +355,7 @@ export function writeTypescript( w.append(`};`); w.append(); w.append( - `private constructor(address: Address, init?: { code: Cell, data: Cell }) {`, + `constructor(address: Address, init?: { code: Cell, data: Cell }) {`, ); w.inIndent(() => { w.append("this.address = address;"); @@ -323,7 +363,6 @@ export function writeTypescript( }); w.append("}"); w.append(); - // Internal receivers if ( abi.receivers && From bb94460afa5dd1919ffd395b4c4491a99ac77c6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A8=D0=B2=D0=B5=D1=86=20=D0=90=D0=BD=D0=B4=D1=80=D0=B5?= =?UTF-8?q?=D0=B9=20=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D1=8C=D0=B5=D0=B2=D0=B8?= =?UTF-8?q?=D1=87?= <91282981+Shvandre@users.noreply.github.com> Date: Wed, 27 Nov 2024 20:13:21 +0300 Subject: [PATCH 2/3] Fixed formatting --- src/bindings/writeTypescript.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bindings/writeTypescript.ts b/src/bindings/writeTypescript.ts index 5dcd25d06..17ffb834c 100644 --- a/src/bindings/writeTypescript.ts +++ b/src/bindings/writeTypescript.ts @@ -202,7 +202,7 @@ export function writeTypescript( }); } }); - w.append(`};`); + w.append(`}`); w.append(); //map to generate ABI later. @@ -213,12 +213,12 @@ export function writeTypescript( if (abi.errors) { Object.entries(abi.errors).forEach(([k, abiError]) => { w.append( - ` ${k}: { message: \`${abiError.message.replaceAll("`", "\\`")}\` },`, + `${k}: { message: \`${abiError.message.replaceAll("`", "\\`")}\` },`, ); }); } }); - w.append(`};`); + w.append(`}`); w.append(); // Types From 095d8c63dd08e9a510a9c6499e5fb7d18a5bebf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A8=D0=B2=D0=B5=D1=86=20=D0=90=D0=BD=D0=B4=D1=80=D0=B5?= =?UTF-8?q?=D0=B9=20=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D1=8C=D0=B5=D0=B2=D0=B8?= =?UTF-8?q?=D1=87?= <91282981+Shvandre@users.noreply.github.com> Date: Thu, 28 Nov 2024 12:08:48 +0300 Subject: [PATCH 3/3] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99a91449f..22516c92e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - +- Abstract classes in the generated Typescript code contain a list of opcodes and errors for easy use in tests. PR [#1100](https://github.com/tact-lang/tact/pull/1100) - `&&=`, `||=`, `>>=` and `<<=` augmented assignment operators: PR [#853](https://github.com/tact-lang/tact/pull/853) - New CSpell dictionaries: TVM instructions and adjusted list of Fift words: PR [#881](https://github.com/tact-lang/tact/pull/881) - Docs: the `description` property to the frontmatter of the each page for better SEO: PR [#916](https://github.com/tact-lang/tact/pull/916) @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Generated Typescript class with wrappers for smart contract makes it more convenient to inherit from it. PR [#1100](https://github.com/tact-lang/tact/pull/1100) - The `parseImports` function now returns AST import nodes instead of raw strings: PR [#966](https://github.com/tact-lang/tact/pull/966) - Optional types for `self` argument in `extends mutates` functions are now allowed: PR [#854](https://github.com/tact-lang/tact/pull/854) - Docs: complete overhaul of the exit codes page: PR [#978](https://github.com/tact-lang/tact/pull/978)