diff --git a/src/components/DevTools/ContractManageForm.vue b/src/components/DevTools/ContractManageForm.vue index 3b43e5d..eff67d7 100644 --- a/src/components/DevTools/ContractManageForm.vue +++ b/src/components/DevTools/ContractManageForm.vue @@ -5,10 +5,9 @@ {{ $t("message.DevTools.DefaultText") }} + - - - +
    @@ -21,7 +20,9 @@ \ No newline at end of file diff --git a/src/components/Fields/AddressField.vue b/src/components/Fields/AddressField.vue index 2ec94c7..b044840 100644 --- a/src/components/Fields/AddressField.vue +++ b/src/components/Fields/AddressField.vue @@ -1,25 +1,40 @@ \ No newline at end of file diff --git a/src/components/Fields/SliceField.vue b/src/components/Fields/SliceField.vue index 18d8a67..7fbc80c 100644 --- a/src/components/Fields/SliceField.vue +++ b/src/components/Fields/SliceField.vue @@ -1,39 +1,40 @@ diff --git a/src/components/Inputs/BooleanInput.vue b/src/components/Inputs/BooleanInput.vue deleted file mode 100644 index 3a6f218..0000000 --- a/src/components/Inputs/BooleanInput.vue +++ /dev/null @@ -1,31 +0,0 @@ - - - diff --git a/src/components/Inputs/NumberInput.vue b/src/components/Inputs/NumberInput.vue deleted file mode 100644 index 2543489..0000000 --- a/src/components/Inputs/NumberInput.vue +++ /dev/null @@ -1,31 +0,0 @@ - - - diff --git a/src/components/Inputs/TextAreaInput.vue b/src/components/Inputs/TextAreaInput.vue deleted file mode 100644 index b89949b..0000000 --- a/src/components/Inputs/TextAreaInput.vue +++ /dev/null @@ -1,15 +0,0 @@ - - - \ No newline at end of file diff --git a/src/components/Inputs/TextInput.vue b/src/components/Inputs/TextInput.vue deleted file mode 100644 index 9afe051..0000000 --- a/src/components/Inputs/TextInput.vue +++ /dev/null @@ -1,7 +0,0 @@ - \ No newline at end of file diff --git a/src/devTools/base.ts b/src/devTools/base.ts new file mode 100644 index 0000000..e8e6699 --- /dev/null +++ b/src/devTools/base.ts @@ -0,0 +1,34 @@ +import type { BaseFieldElement } from "@/components/Fields/FieldList.vue"; +import { FieldsManager, type InputItem } from "./fields"; + +/** + * Base extractor/parser class for contract ABI + */ +export class BaseDevTools { + protected fields: FieldsManager; + protected title?: string; + + constructor() { + this.fields = new FieldsManager(); + } + + /** + * List of fields + * @returns + */ + public getFields(): InputItem[] { + return this.fields.getFields(); + } + + public getTitle(): string | null{ + return this.title ?? null; + } + + public async execute(elements: BaseFieldElement[]): Promise { + let success = true; + for (const el of elements) { + success = success && el.validate(); + } + return success; + } +} \ No newline at end of file diff --git a/src/utils/devTools.ts b/src/devTools/fields.ts similarity index 60% rename from src/utils/devTools.ts rename to src/devTools/fields.ts index da467c3..937e81e 100644 --- a/src/utils/devTools.ts +++ b/src/devTools/fields.ts @@ -1,8 +1,4 @@ -import AddressField from "@/components/Fields/AddressField.vue"; -import SliceField from "@/components/Fields/SliceField.vue"; -import { TonConnectUI } from "@tonconnect/ui"; -import { beginCell, type ABIField, type ABIGetter, type ABIReceiver, type ABIType, type Builder, type ContractProvider, type TupleItem } from "ton-core"; -import type { CreateComponentPublicInstanceWithMixins } from "vue"; +import type { ABIField, ABIGetter, ABIReceiver, ABIType } from "ton-core"; export interface BaseFieldInput { optional: boolean, @@ -40,10 +36,6 @@ export interface SimpleFieldParameters { format?: string | number | boolean; }; -export type BaseFieldElement = CreateComponentPublicInstanceWithMixins<{ - validate(): boolean, - store(builder: Builder): void, -}>; export class FieldsManager { private fields: InputItem[]; @@ -90,7 +82,7 @@ export class FieldsManager { * @param receiver * @param types */ - public storeGetterFields(getter: ABIGetter, types: ABIType[]) { + public storeGetterFields(getter: ABIGetter) { if (!getter.arguments) { return; } @@ -193,6 +185,18 @@ export class FieldsManager { } } + /** + * Store `bool` field + * @param type + */ + public storeBoolean(type: SimpleFieldParameters) { + this.fields.push({ + type: "bool", + label: type.name, + optional: type.optional, + }); + } + /** * Store `slice` field * @param type @@ -235,7 +239,7 @@ export class FieldsManager { case "int": return this.storeUnknown(type.type); // TODO case "bool": - return this.storeUnknown(type.type); // TODO + return this.storeBoolean(type); case "cell": return this.storeUnknown(type.type); // TODO case "slice": @@ -275,146 +279,3 @@ export class FieldsManager { } } } - -/** - * Base extractor/parser class for contract ABI - */ -export class BaseDevTools { - protected fields: FieldsManager; - protected title?: string; - protected elements: BaseFieldElement[]; - - constructor() { - this.fields = new FieldsManager(); - this.elements = []; - } - - /** - * List of fields - * @returns - */ - public getFields(): InputItem[] { - return this.fields.getFields(); - } - - public registerInput(el: BaseFieldElement) { - console.log("NEW INPUT", this.elements.length, el); - this.elements.push(el); - } - - public getTitle(): string | null{ - return this.title ?? null; - } - - public async execute(): Promise { - let success = true; - for (const el of this.elements) { - success &&= el.validate(); - } - return success; - } -} - -interface ReceiverDevToolsOptions { - receiver: ABIReceiver; - types: ABIType[]; - tonConnectUI: TonConnectUI; - address: string; - tonAmount: bigint; -} - -export class ReceiverDevTools extends BaseDevTools { - private header?: number; - private address: string; - private tonConnectUI: TonConnectUI; - private tonAmount: bigint; - - constructor(options: ReceiverDevToolsOptions) { - super(); - - this.tonConnectUI = options.tonConnectUI; - this.address = options.address; - this.tonAmount = options.tonAmount; - - if (options.receiver.message.kind == "typed") { - const type = this.fields.findTypeByName(options.types, options.receiver.message.type); - if (type) { - this.header = type.header ?? undefined; - this.title = type.name; - } - } - - this.fields.storeReceiverFields(options.receiver, options.types); - } - - public override async execute(): Promise { - if (!await super.execute()) { - return false; - } - - const tx = beginCell(); - - if (this.header) { - tx.storeUint(this.header, 32); - } - - for (const el of this.elements) { - tx.store(el.store); - } - - await this.tonConnectUI.sendTransaction( - { - validUntil: Math.floor(Date.now() / 1000) + 360, - messages: [ - { - address: this.address, - amount: this.tonAmount.toString(), - payload: tx.endCell().toBoc().toString("base64"), - } - ] - } - ); - - return true; - } -} - -interface GetterDevToolsOptions { - getter: ABIGetter; - types: ABIType[]; - provider: ContractProvider; -} - -export class GetterDevTools extends BaseDevTools { - private name: string; - private provider: ContractProvider; - - constructor(options: GetterDevToolsOptions) { - super(); - - this.provider = options.provider; - - this.name = options.getter.name; - this.title = options.getter.name; - - this.fields.storeGetterFields(options.getter, options.types); - } - - public override async execute(): Promise { - if (!await super.execute()) { - return false; - } - - const args = [] as TupleItem[]; - for (const el of this.elements) { - args.push({ - type: "slice", - cell: beginCell().store(el.store).endCell() - }) - } - - const result = await this.provider.get(this.name, args); - - return true; - } -} diff --git a/src/devTools/getter.ts b/src/devTools/getter.ts new file mode 100644 index 0000000..2556194 --- /dev/null +++ b/src/devTools/getter.ts @@ -0,0 +1,42 @@ +import type { BaseFieldElement } from "@/components/Fields/FieldList.vue"; +import { beginCell, type ABIGetter, type ContractProvider, type TupleItem } from "ton-core"; +import { BaseDevTools } from "./base"; + +interface GetterDevToolsOptions { + getter: ABIGetter; + provider: ContractProvider; +} + +export class GetterDevTools extends BaseDevTools { + private name: string; + private provider: ContractProvider; + + constructor(options: GetterDevToolsOptions) { + super(); + + this.provider = options.provider; + + this.name = options.getter.name; + this.title = options.getter.name; + + this.fields.storeGetterFields(options.getter); + } + + public override async execute(elements: BaseFieldElement[]): Promise { + if (!await super.execute(elements)) { + return false; + } + + const args = [] as TupleItem[]; + for (const el of elements) { + args.push({ + type: "slice", + cell: beginCell().store(el.store).endCell() + }) + } + + await this.provider.get(this.name, args); // TODO + + return true; + } +} diff --git a/src/devTools/receiver.ts b/src/devTools/receiver.ts new file mode 100644 index 0000000..b0c12e9 --- /dev/null +++ b/src/devTools/receiver.ts @@ -0,0 +1,68 @@ +import type { TonConnectUI } from "@tonconnect/ui"; +import { beginCell, type ABIReceiver, type ABIType } from "ton-core"; +import { BaseDevTools } from "./base"; +import type { BaseFieldElement } from "@/components/Fields/FieldList.vue"; + +interface ReceiverDevToolsOptions { + receiver: ABIReceiver; + types: ABIType[]; + tonConnectUI: TonConnectUI; + address: string; + tonAmount: bigint; +} + +export class ReceiverDevTools extends BaseDevTools { + private header?: number; + private address: string; + private tonConnectUI: TonConnectUI; + private tonAmount: bigint; + + constructor(options: ReceiverDevToolsOptions) { + super(); + + this.tonConnectUI = options.tonConnectUI; + this.address = options.address; + this.tonAmount = options.tonAmount; + + if (options.receiver.message.kind == "typed") { + const type = this.fields.findTypeByName(options.types, options.receiver.message.type); + if (type) { + this.header = type.header ?? undefined; + this.title = type.name; + } + } + + this.fields.storeReceiverFields(options.receiver, options.types); + } + + public override async execute(elements: BaseFieldElement[]): Promise { + if (!await super.execute(elements)) { + return false; + } + + const tx = beginCell(); + + if (this.header) { + tx.storeUint(this.header, 32); + } + + for (const el of elements) { + tx.store(el.store); + } + + await this.tonConnectUI.sendTransaction( + { + validUntil: Math.floor(Date.now() / 1000) + 360, + messages: [ + { + address: this.address, + amount: this.tonAmount.toString(), + payload: tx.endCell().toBoc().toString("base64"), + } + ] + } + ); + + return true; + } +} diff --git a/src/i18n/en.json b/src/i18n/en.json index 91b8d81..b8522a1 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -103,21 +103,41 @@ }, "Common": { "Warning": "Warning!", - "requiredField": "required field", - "RequiredField": "This field is required", - "NaN": "Not a number" + "requiredField": "required field" }, "Fields": { - "Address_Placeholder": "e.g. 0QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkT", - "Address_HelpText": "TON address in any format", - "Coins_Placeholder": "e.g. 1337", - "Coins_HelpText": "Coins amount", - "Uint_Placeholder": "Numbers from {min} to {max}", - "Uint_HelpText": "Unsigned {format}-bit integer field", - "Slice_Placeholder": "", - "Slice_HelpText": "Slice value in base64 format", - "String_Placeholder": "", - "String_HelpText": "String field" + "Errors": { + "RequiredField": "This field is required", + "WrongAddress": "Cannot parse address", + "InvalidNumber": "Value must be number", + "MustBeMoreThan": "Value must be more than {min}", + "MustBeLessThan": "Value must be less than {max}", + "InvalidBase64": "Invalid base64" + }, + "Address": { + "Placeholder": "e.g. 0QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkT", + "HelpText": "TON address in any format" + }, + "Coins": { + "Placeholder": "e.g. 1337", + "HelpText": "Coins amount" + }, + "Uint": { + "Placeholder": "Numbers from {min} to {max}", + "HelpText": "Unsigned {format}-bit integer field" + }, + "Boolean": { + "Placeholder": "", + "HelpText": "" + }, + "Slice": { + "Placeholder": "base64", + "HelpText": "Slice value in base64 format" + }, + "String": { + "Placeholder": "e.g. Hello World", + "HelpText": "String field" + } } } } diff --git a/src/i18n/ru.json b/src/i18n/ru.json index a6dea84..36113ee 100644 --- a/src/i18n/ru.json +++ b/src/i18n/ru.json @@ -103,22 +103,41 @@ }, "Common": { "Warning": "Внимание!", - "requiredField": "обязательное поле", - "RequiredField": "Обязательное поле", - "NaN": "Ожидалось число", - "InvalidSlice": "Неправильный формат slice" + "requiredField": "обязательное поле" }, "Fields": { - "Address_Placeholder": "напр. 0QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkT", - "Address_HelpText": "TON адрес в любом формате", - "Coins_Placeholder": "напр. 1337", - "Coins_HelpText": "Количество монет", - "Uint_Placeholder": "Число от {min} до {max}", - "Uint_HelpText": "Целое {format}-битное число без знака", - "Slice_Placeholder": "", - "Slice_HelpText": "Slice в формате base64", - "String_Placeholder": "", - "String_HelpText": "Строка" + "Errors": { + "RequiredField": "Обязательное поле", + "WrongAddress": "Некорректный формат адреса", + "InvalidNumber": "Должно быть числом", + "MustBeMoreThan": "Значение должно быть больше чем {min}", + "MustBeLessThan": "Значение должно быть меньше чем {max}", + "InvalidBase64": "Неправильное значение base64" + }, + "Address": { + "Placeholder": "напр. 0QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACkT", + "HelpText": "Адрес контракта в любом формате" + }, + "Coins": { + "Placeholder": "напр. 1337", + "HelpText": "Количество токенов" + }, + "Uint": { + "Placeholder": "Число от {min} до {max}", + "HelpText": "Целое {format}-битное число без знака" + }, + "Boolean": { + "Placeholder": "", + "HelpText": "" + }, + "Slice": { + "Placeholder": "base64", + "HelpText": "Slice в формате base64" + }, + "String": { + "Placeholder": "напр. Привет Мир", + "HelpText": "Строковое поле" + } } } } diff --git a/src/views/DevTools.vue b/src/views/DevTools.vue index 1b07529..d3ea92d 100644 --- a/src/views/DevTools.vue +++ b/src/views/DevTools.vue @@ -64,8 +64,7 @@