Skip to content

Commit

Permalink
Merge pull request #234 from tonlabs/1.18.1-rc
Browse files Browse the repository at this point in the history
1.18.1 rc
  • Loading branch information
d3p authored Jul 2, 2021
2 parents 0bcc271 + e367ad5 commit d22d853
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 12 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

All notable changes to this project will be documented in this file.

## [1.18.1] – 2021-07-01

### Improved

- Improved error messages regarding Union-typed parameters (e.g. `abi` and `signer` in `encode_message`): helper functions (e.g. `signerNone`, `signerKeys`, etc.) are suggested if applicable.

## [1.18.0] – 2021-06-26

### New
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"packages": [
"packages/*"
],
"version": "1.18.0",
"version": "1.18.1",
"command": {
"version": {
"message": "Release"
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tonclient/core",
"version": "1.18.0",
"version": "1.18.1",
"description": "TON Client for Java Script",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
77 changes: 75 additions & 2 deletions packages/core/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
ResponseHandler,
useLibrary,
} from "./bin";
import { TonClientError } from "./errors";

export class TonClient {
private static _defaultConfig: ClientConfig = {};
Expand Down Expand Up @@ -112,6 +113,74 @@ export class TonClient {
}
}

async resolveError(functionName: string, params: any, err: TonClientError): Promise<TonClientError> {
if (err.code !== 23 || !(err.data?.suggest_use_helper_for)) {
return err;
}
try {
const [modName, funcName] = functionName.split(".");
const api = (await this.client.get_api_reference()).api;

const allTypesArray = api.modules.reduce((accumulator: any, element: any) => accumulator.concat(element.types), []);
const allTypesDict: { [name: string]: any } = {};
allTypesArray.forEach((element: any) => allTypesDict[element.name] = element);

const module = api.modules.find((x: any) => x.name === modName);
const func = module.functions.find((x: any) => x.name === funcName);
const param = func.params[1];

// If there is only context param (or AppObject second param), there is nothing to analyze
if (!param || param.generic_name == "AppObject") {
return err;
}

const paramTypeInfo = allTypesDict[param.ref_name];
walkParameters(paramTypeInfo, params, "");

function walkParameters(valueTypeInfo: any, value: any, path: string) {
switch (valueTypeInfo.type) {
case "Array":
if (Array.isArray(value)) {
value.forEach(v => walkParameters(valueTypeInfo.array_item, v, `${path}[i]`));
}
break;
case "Struct":
valueTypeInfo.struct_fields.forEach((sf: any) => walkParameters(sf, value[sf.name], path ? `${path}.${sf.name}` : sf.name));
break;
case "Optional":
if (value) {
walkParameters(valueTypeInfo.optional_inner, value, path);
}
break;
case "Ref":
if (valueTypeInfo.ref_name != "Value" &&
valueTypeInfo.ref_name != "API" &&
valueTypeInfo.ref_name != "AbiParam") {

walkParameters(allTypesDict[valueTypeInfo.ref_name], value, path);
}
break;
case "EnumOfTypes":
if (valueTypeInfo.enum_types.some((et: any) => et.name == value.type)) {
return;
}

let parameterName = valueTypeInfo.name.toLowerCase();
let helperFunctions: string[] = [];
valueTypeInfo.enum_types.forEach((et: any) => helperFunctions.push(parameterName + et.name));

err.message = `Consider using one of the helper methods (${helperFunctions.join(", ")}) for the \"${path}\" parameter\n` + err.message;
break;
default:
break;
}
}
} catch (e) {
err.message = e;
}
return err;
}

async request(
functionName: string,
functionParams: any,
Expand All @@ -124,8 +193,12 @@ export class TonClient {
context = await getBridge().createContext(this.config);
this.context = context;
}
return getBridge().request(context, functionName, functionParams, responseHandler ?? (() => {
}));

return getBridge()
.request(context, functionName, functionParams, responseHandler ?? (() => {}))
.catch(async (reason) => {
throw await this.resolveError(functionName, functionParams, reason);
});
}

async resolve_app_request(app_request_id: number | null, result: any): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion packages/lib-node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tonclient/lib-node",
"version": "1.18.0",
"version": "1.18.1",
"description": "TON Client NodeJs AddOn",
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion packages/lib-react-native/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tonclient/lib-react-native",
"version": "1.18.0",
"version": "1.18.1",
"description": "TON Client React Native Module",
"main": "index.js",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/lib-web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tonclient/lib-web",
"version": "1.18.0",
"version": "1.18.1",
"description": "TON Client WASM module for browsers",
"main": "index.js",
"repository": {
Expand Down
2 changes: 1 addition & 1 deletion packages/tests-node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tonclient/tests-node",
"version": "1.18.0",
"version": "1.18.1",
"private": true,
"description": "TON Client Tests runner on NodeJs",
"main": "index.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/tests-react-native/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tonclient/tests-react-native",
"version": "1.18.0",
"version": "1.18.1",
"private": true,
"main": "index.js",
"browser": true,
Expand Down
2 changes: 1 addition & 1 deletion packages/tests-web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tonclient/tests-web",
"version": "1.18.0",
"version": "1.18.1",
"private": true,
"description": "TON Client WASM module tests runner",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/tests/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@tonclient/tests",
"version": "1.18.0",
"version": "1.18.1",
"private": true,
"description": "TON Client Tests",
"main": "dist/index.js",
Expand Down
101 changes: 100 additions & 1 deletion packages/tests/src/tests/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

import { runner } from '../runner';
import { test, expect } from '../jest';
import { contracts } from '../contracts';
import { signerKeys } from '@tonclient/core';

test("Test versions compatibility", async () => {
const client = runner.getClient().client;
Expand All @@ -37,4 +39,101 @@ test("client: build_info", async () => {
const build_info = await client.build_info();

expect(build_info).not.toBeNull();
})
});

test("client: walk api reference", async () => {
const client = runner.getClient().client;

const apiResult = await client.get_api_reference();
const api = apiResult.api;

const allTypesArray = api.modules.reduce((accumulator: any, element: any) => accumulator.concat(element.types), []);
const allTypesDict: { [name: string]: any } = {};
allTypesArray.forEach((element: any) => allTypesDict[element.name] = element);

allTypesArray.forEach((typeInfo: any) => {
walkSubtypes(typeInfo);
});

const allFunctionsArray = api.modules.reduce((accumulator: any, element: any) => accumulator.concat(element.functions), []);
allFunctionsArray.forEach((functionInfo: any) => {
if (functionInfo.params[0].generic_name != "Arc" ||
functionInfo.params[2] && (functionInfo.params[2].generic_name != "Arc" && functionInfo.params[2].generic_name != "AppObject") ||
functionInfo.params[3] ||
functionInfo.params[1] && functionInfo.params[1].generic_name != "AppObject" && !allTypesDict[functionInfo.params[1].ref_name]) {
throw Error("The API has changed, need to check helpers suggestions");
}
});

function walkSubtypes(typeInfo: any) {
switch (typeInfo.type) {
case "Array":
walkSubtypes(typeInfo.array_item);
break;
case "Struct":
typeInfo.struct_fields.forEach((sf: any) => walkSubtypes(sf));
break;
case "Optional":
walkSubtypes(typeInfo.optional_inner);
break;
case "Ref":
if (typeInfo.ref_name != "Value" &&
typeInfo.ref_name != "API" &&
typeInfo.ref_name != "AbiParam") {

walkSubtypes(allTypesDict[typeInfo.ref_name]);
}
break;
case "EnumOfTypes":
case "EnumOfConsts":
case "BigInt":
case "Boolean":
case "Number":
case "String":
break;
default:
console.log("Unknown type: ", typeInfo.type);
}
}
});

test("client: Should suggest helper functions if applicable", async () => {
const {
abi,
crypto
} = runner.getClient();

const keys = await crypto.generate_random_sign_keys();
const c = await runner.getAccount(contracts.Hello, 2, signerKeys(keys));

try {
await abi.encode_message({
abi: c.abi,
address: await c.getAddress(),
signer: keys as any,
call_set: {
function_name: "touch"
},
});
expect("This line should not be reached").toHaveLength(0);
} catch (err) {
expect(err.message.startsWith("Consider using one of the helper methods (signerNone, signerExternal, signerKeys, signerSigningBox) for the \"signer\" parameter"))
.toBeTruthy();
}

try {
await abi.encode_message({
abi: contracts.Hello[2].abi as any,
address: await c.getAddress(),
signer: signerKeys(keys),
call_set: {
function_name: "touch"
},
});
expect("This line should not be reached").toHaveLength(0);
} catch (err) {
expect(err.message.startsWith("Consider using one of the helper methods (abiContract, abiJson, abiHandle, abiSerialized) for the \"abi\" parameter"))
.toBeTruthy();
}
});

0 comments on commit d22d853

Please sign in to comment.