@cosmwasm/ts-codegen 1.12.1 → 1.13.1
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 +147 -118
- package/builder/builder.js +26 -24
- package/bundler/bundler.js +27 -17
- package/cli.js +2 -2
- package/commands/create-boilerplate.js +29 -18
- package/commands/generate.js +26 -35
- package/commands/install.js +22 -18
- package/esm/builder/builder.js +10 -18
- package/esm/bundler/bundler.js +10 -10
- package/esm/cli.js +2 -2
- package/esm/commands/create-boilerplate.js +11 -10
- package/esm/commands/generate.js +26 -35
- package/esm/commands/install.js +23 -19
- package/esm/file.js +4 -3
- package/esm/helpers/baseClient.js +184 -0
- package/esm/helpers/contractContextBase.js +15 -13
- package/esm/helpers/contractContextBaseShortHandCtor.js +11 -9
- package/esm/helpers/contractsContextTSX.js +7 -7
- package/esm/helpers/create-helpers.js +3 -1
- package/esm/helpers/index.js +1 -0
- package/esm/plugins/client.js +1 -1
- package/esm/plugins/message-builder.js +4 -4
- package/esm/plugins/message-composer.js +1 -1
- package/esm/plugins/plugin-base.js +3 -2
- package/esm/plugins/provider-bundle.js +9 -5
- package/esm/plugins/provider.js +1 -1
- package/esm/plugins/react-query.js +8 -7
- package/esm/plugins/recoil.js +4 -4
- package/esm/plugins/types.js +3 -3
- package/esm/ts-codegen.js +2 -1
- package/esm/utils/clean.js +1 -1
- package/esm/utils/cleanse.js +9 -5
- package/esm/utils/package.js +1 -1
- package/esm/utils/parse.js +5 -7
- package/esm/utils/prompt.js +2 -2
- package/esm/utils/schemas.js +32 -19
- package/esm/utils/unused.js +3 -4
- package/file.js +7 -3
- package/helpers/baseClient.d.ts +1 -0
- package/helpers/baseClient.js +187 -0
- package/helpers/contractContextBase.d.ts +1 -1
- package/helpers/contractContextBase.js +15 -13
- package/helpers/contractContextBaseShortHandCtor.d.ts +1 -1
- package/helpers/contractContextBaseShortHandCtor.js +11 -9
- package/helpers/contractsContextTSX.d.ts +1 -1
- package/helpers/contractsContextTSX.js +7 -7
- package/helpers/create-helpers.js +2 -0
- package/helpers/index.d.ts +1 -0
- package/helpers/index.js +1 -0
- package/package.json +20 -15
- package/plugins/client.js +18 -8
- package/plugins/message-builder.js +20 -10
- package/plugins/message-composer.js +18 -8
- package/plugins/plugin-base.js +19 -8
- package/plugins/provider-bundle.js +25 -11
- package/plugins/provider.js +17 -7
- package/plugins/react-query.js +24 -13
- package/plugins/recoil.js +20 -10
- package/plugins/types.js +19 -9
- package/ts-codegen.js +5 -1
- package/utils/clean.js +1 -1
- package/utils/cleanse.d.ts +1 -0
- package/utils/cleanse.js +12 -7
- package/utils/files.js +17 -7
- package/utils/package.js +2 -3
- package/utils/parse.js +5 -7
- package/utils/prompt.js +2 -2
- package/utils/schemas.d.ts +5 -1
- package/utils/schemas.js +34 -20
- package/utils/unused.js +20 -11
@@ -1,6 +1,6 @@
|
|
1
1
|
import generate from '@babel/generator';
|
2
2
|
import * as t from '@babel/types';
|
3
|
-
import { defaultOptions } from '@cosmwasm/ts-codegen-ast';
|
3
|
+
import { defaultOptions, } from '@cosmwasm/ts-codegen-ast';
|
4
4
|
import deepmerge from 'deepmerge';
|
5
5
|
import { writeFileSync } from 'fs';
|
6
6
|
import { sync as mkdirp } from 'mkdirp';
|
@@ -41,8 +41,9 @@ export class BuilderPluginBase {
|
|
41
41
|
}
|
42
42
|
return results.map((result) => {
|
43
43
|
const imports = context.getImports(this.utils, result.localname);
|
44
|
+
const nodes = [...imports, ...result.body];
|
44
45
|
// @ts-ignore
|
45
|
-
const code = header + generate(t.program(
|
46
|
+
const code = header + generate(t.program(nodes)).code;
|
46
47
|
mkdirp(outPath);
|
47
48
|
const filename = join(outPath, result.localname);
|
48
49
|
writeFileSync(filename, code);
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import * as w from '@cosmwasm/ts-codegen-ast';
|
2
|
-
import { RenderContext } from '@cosmwasm/ts-codegen-ast';
|
2
|
+
import { RenderContext, } from '@cosmwasm/ts-codegen-ast';
|
3
3
|
import { pascal } from 'case';
|
4
4
|
import { BuilderPluginBase } from './plugin-base';
|
5
5
|
import { GetLocalBaseNameByContractName } from './provider';
|
@@ -9,8 +9,10 @@ export class ContractsProviderBundlePlugin extends BuilderPluginBase {
|
|
9
9
|
this.lifecycle = 'after';
|
10
10
|
this.defaultContractName = 'contractContextProviders';
|
11
11
|
this.utils = {
|
12
|
-
|
13
|
-
|
12
|
+
ICosmWasmClient: '__baseClient__',
|
13
|
+
ISigningCosmWasmClient: '__baseClient__',
|
14
|
+
getCosmWasmClient: '__baseClient__',
|
15
|
+
getSigningCosmWasmClient: '__baseClient__',
|
14
16
|
IQueryClientProvider: '__contractContextBase__',
|
15
17
|
ISigningClientProvider: '__contractContextBase__',
|
16
18
|
IMessageComposerProvider: '__contractContextBase__',
|
@@ -29,8 +31,10 @@ export class ContractsProviderBundlePlugin extends BuilderPluginBase {
|
|
29
31
|
}
|
30
32
|
const localname = 'contractContextProviders.ts';
|
31
33
|
const body = [];
|
32
|
-
context.addUtil('
|
33
|
-
context.addUtil('
|
34
|
+
context.addUtil('ICosmWasmClient');
|
35
|
+
context.addUtil('ISigningCosmWasmClient');
|
36
|
+
context.addUtil('getCosmWasmClient');
|
37
|
+
context.addUtil('getSigningCosmWasmClient');
|
34
38
|
context.addUtil('IQueryClientProvider');
|
35
39
|
context.addUtil('ISigningClientProvider');
|
36
40
|
context.addUtil('IMessageComposerProvider');
|
package/esm/plugins/provider.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import * as w from '@cosmwasm/ts-codegen-ast';
|
2
|
-
import { RenderContext } from '@cosmwasm/ts-codegen-ast';
|
2
|
+
import { RenderContext, } from '@cosmwasm/ts-codegen-ast';
|
3
3
|
import { pascal } from 'case';
|
4
4
|
import { BuilderPluginBase } from './plugin-base';
|
5
5
|
export const GetLocalNameByContractName = (name) => `${pascal(name)}.provider.ts`;
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import * as w from '@cosmwasm/ts-codegen-ast';
|
2
|
-
import { getMessageProperties, RenderContext } from '@cosmwasm/ts-codegen-ast';
|
2
|
+
import { getMessageProperties, RenderContext, } from '@cosmwasm/ts-codegen-ast';
|
3
3
|
import { pascal } from 'case';
|
4
4
|
import { findAndParseTypes, findExecuteMsg, findQueryMsg } from '../utils';
|
5
5
|
import { BuilderPluginBase } from './plugin-base';
|
@@ -24,7 +24,8 @@ export class ReactQueryPlugin extends BuilderPluginBase {
|
|
24
24
|
const QueryClient = pascal(`${name}QueryClient`);
|
25
25
|
const body = [];
|
26
26
|
const clientImports = [];
|
27
|
-
|
27
|
+
if (QueryMsg)
|
28
|
+
clientImports.push(QueryClient);
|
28
29
|
// check that there are commands within the exec msg
|
29
30
|
const shouldGenerateMutationHooks = ExecuteMsg &&
|
30
31
|
options?.version === 'v4' &&
|
@@ -43,7 +44,7 @@ export class ReactQueryPlugin extends BuilderPluginBase {
|
|
43
44
|
context,
|
44
45
|
queryMsg: QueryMsg,
|
45
46
|
contractName: name,
|
46
|
-
QueryClient
|
47
|
+
QueryClient,
|
47
48
|
}));
|
48
49
|
}
|
49
50
|
if (shouldGenerateMutationHooks) {
|
@@ -51,10 +52,10 @@ export class ReactQueryPlugin extends BuilderPluginBase {
|
|
51
52
|
context,
|
52
53
|
execMsg: ExecuteMsg,
|
53
54
|
contractName: name,
|
54
|
-
ExecuteClient
|
55
|
+
ExecuteClient,
|
55
56
|
}));
|
56
57
|
}
|
57
|
-
if (
|
58
|
+
if (Object.prototype.hasOwnProperty.call(typeHash, 'Coin')) {
|
58
59
|
// @ts-ignore
|
59
60
|
delete context.utils.Coin;
|
60
61
|
}
|
@@ -62,8 +63,8 @@ export class ReactQueryPlugin extends BuilderPluginBase {
|
|
62
63
|
{
|
63
64
|
type: 'react-query',
|
64
65
|
localname,
|
65
|
-
body
|
66
|
-
}
|
66
|
+
body,
|
67
|
+
},
|
67
68
|
];
|
68
69
|
}
|
69
70
|
}
|
package/esm/plugins/recoil.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import * as w from '@cosmwasm/ts-codegen-ast';
|
2
|
-
import { RenderContext } from '@cosmwasm/ts-codegen-ast';
|
2
|
+
import { RenderContext, } from '@cosmwasm/ts-codegen-ast';
|
3
3
|
import { pascal } from 'case';
|
4
4
|
import { findAndParseTypes, findQueryMsg } from '../utils';
|
5
5
|
import { BuilderPluginBase } from './plugin-base';
|
@@ -36,7 +36,7 @@ export class RecoilPlugin extends BuilderPluginBase {
|
|
36
36
|
const selectors = w.createRecoilSelectors(context, name, QueryClient, QueryMsg);
|
37
37
|
body.push(...selectors);
|
38
38
|
}
|
39
|
-
if (
|
39
|
+
if (Object.prototype.hasOwnProperty.call(typeHash, 'Coin')) {
|
40
40
|
// @ts-ignore
|
41
41
|
delete context.utils.Coin;
|
42
42
|
}
|
@@ -44,8 +44,8 @@ export class RecoilPlugin extends BuilderPluginBase {
|
|
44
44
|
{
|
45
45
|
type: 'recoil',
|
46
46
|
localname,
|
47
|
-
body
|
48
|
-
}
|
47
|
+
body,
|
48
|
+
},
|
49
49
|
];
|
50
50
|
}
|
51
51
|
}
|
package/esm/plugins/types.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import * as t from '@babel/types';
|
2
|
-
import { RenderContext } from '@cosmwasm/ts-codegen-ast';
|
2
|
+
import { RenderContext, } from '@cosmwasm/ts-codegen-ast';
|
3
3
|
import { pascal } from 'case';
|
4
4
|
import { findAndParseTypes, findExecuteMsg } from '../utils';
|
5
5
|
import { clean } from '../utils/clean';
|
@@ -31,8 +31,8 @@ export class TypesPlugin extends BuilderPluginBase {
|
|
31
31
|
{
|
32
32
|
type: 'type',
|
33
33
|
localname,
|
34
|
-
body
|
35
|
-
}
|
34
|
+
body,
|
35
|
+
},
|
36
36
|
];
|
37
37
|
}
|
38
38
|
}
|
package/esm/ts-codegen.js
CHANGED
package/esm/utils/clean.js
CHANGED
@@ -21,7 +21,7 @@ export const clean = (obj) => {
|
|
21
21
|
if (obj instanceof Object || typeof obj === 'object') {
|
22
22
|
copy = {};
|
23
23
|
for (let attr in obj) {
|
24
|
-
if (
|
24
|
+
if (Object.prototype.hasOwnProperty.call(obj, attr)) {
|
25
25
|
switch (attr) {
|
26
26
|
case 'leadingComments':
|
27
27
|
case 'trailingComments':
|
package/esm/utils/cleanse.js
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
import { pascal } from 'case';
|
2
|
-
const cleanFor = (str) => {
|
2
|
+
export const cleanFor = (str) => {
|
3
3
|
/*
|
4
4
|
1. look at first char after _for_
|
5
5
|
2. ONLY if you find capitals after, modify it
|
6
6
|
*/
|
7
|
-
while (
|
8
|
-
const
|
9
|
-
|
7
|
+
while (true) {
|
8
|
+
const match = str.match(/(_[a-z]+_)[A-Z]/);
|
9
|
+
if (!match)
|
10
|
+
break;
|
11
|
+
// this replace is unsafe as it replaces the same text but maybe
|
12
|
+
// in a different location than the match
|
13
|
+
str = str.replace(match[1], pascal(match[1]));
|
10
14
|
}
|
11
15
|
return str;
|
12
16
|
};
|
@@ -46,7 +50,7 @@ export const cleanse = (obj) => {
|
|
46
50
|
}
|
47
51
|
}
|
48
52
|
for (let attr in obj) {
|
49
|
-
if (
|
53
|
+
if (Object.prototype.hasOwnProperty.call(obj, attr)) {
|
50
54
|
if (/_for_/.test(attr)) {
|
51
55
|
// @ts-ignore
|
52
56
|
copy[cleanFor(attr)] = cleanse(obj[attr]);
|
package/esm/utils/package.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { existsSync, readFileSync } from 'fs';
|
2
2
|
import { dirname, join } from 'path';
|
3
|
-
// need to search due to the dist/ folder and src/, etc.
|
3
|
+
// need to search due to the dist/ folder and src/, etc.
|
4
4
|
function findPackageJson(currentDir) {
|
5
5
|
const filePath = join(currentDir, 'package.json');
|
6
6
|
// Check if package.json exists in the current directory
|
package/esm/utils/parse.js
CHANGED
@@ -2,18 +2,16 @@ import { parse } from '@babel/parser';
|
|
2
2
|
import babelTraverse from '@babel/traverse';
|
3
3
|
export const parser = (codes) => {
|
4
4
|
const hash = {};
|
5
|
-
codes.forEach(code => {
|
6
|
-
const plugins = [
|
7
|
-
'typescript',
|
8
|
-
];
|
5
|
+
codes.forEach((code) => {
|
6
|
+
const plugins = ['typescript'];
|
9
7
|
const ast = parse(code, {
|
10
8
|
sourceType: 'module',
|
11
|
-
plugins
|
9
|
+
plugins,
|
12
10
|
});
|
13
11
|
const visitor = visitorFn({
|
14
12
|
addType(key, node) {
|
15
13
|
hash[key] = node;
|
16
|
-
}
|
14
|
+
},
|
17
15
|
});
|
18
16
|
babelTraverse(ast, visitor);
|
19
17
|
});
|
@@ -32,5 +30,5 @@ const visitorFn = (parser) => ({
|
|
32
30
|
},
|
33
31
|
TSInterfaceDeclaration(path) {
|
34
32
|
parser.addType(path.node.id.name, path.parentPath.node);
|
35
|
-
}
|
33
|
+
},
|
36
34
|
});
|
package/esm/utils/prompt.js
CHANGED
@@ -36,7 +36,7 @@ const transform = (questions) => {
|
|
36
36
|
return {
|
37
37
|
...q,
|
38
38
|
type: 'autocomplete',
|
39
|
-
source: getFuzzySearch(choices)
|
39
|
+
source: getFuzzySearch(choices),
|
40
40
|
};
|
41
41
|
}
|
42
42
|
else if (q.type === 'fuzzy:objects') {
|
@@ -45,7 +45,7 @@ const transform = (questions) => {
|
|
45
45
|
return {
|
46
46
|
...q,
|
47
47
|
type: 'autocomplete',
|
48
|
-
source: getFuzzySearchNames(choices)
|
48
|
+
source: getFuzzySearchNames(choices),
|
49
49
|
};
|
50
50
|
}
|
51
51
|
else {
|
package/esm/utils/schemas.js
CHANGED
@@ -1,37 +1,50 @@
|
|
1
1
|
import { compile } from '@pyramation/json-schema-to-typescript';
|
2
2
|
import { readFileSync } from 'fs';
|
3
|
-
import {
|
3
|
+
import { globSync as glob } from 'glob';
|
4
4
|
import { cleanse } from './cleanse';
|
5
5
|
import { parser } from './parse';
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
/**
|
7
|
+
* Takes a schema directory and returns a list of relevant file paths
|
8
|
+
*/
|
9
|
+
export const findSchemaFiles = async (schemaDir) => {
|
9
10
|
const files = glob(schemaDir + '/**/*.json')
|
10
|
-
.filter(file => !file.
|
11
|
-
|
12
|
-
.
|
13
|
-
|
11
|
+
.filter((file) => !file.includes('/raw/')) // raw JSON Schema files that are also included in the main <contract_name>.json
|
12
|
+
.filter((file) => !file.includes('/cw_schema/')) // sub-folder for the new schema format for CosmWasm 3+
|
13
|
+
.sort();
|
14
|
+
return files;
|
15
|
+
};
|
16
|
+
export const readSchemas = async ({ schemaDir, clean = true, }) => {
|
17
|
+
const fn = clean
|
18
|
+
? cleanse
|
19
|
+
: (schema) => schema;
|
20
|
+
const files = (await findSchemaFiles(schemaDir)).map((path) => readFileSync(path, 'utf-8'));
|
21
|
+
if (files.length > 1) {
|
14
22
|
// legacy
|
15
23
|
// TODO add console.warn here
|
24
|
+
console.warn('Found a multiple schema files. This mode will be removed in the next major version. Please migrate the schemas that contain a single <contract_name>.json IDL file (CosmWasm 1.1+).');
|
25
|
+
const schemas = files.map((file) => JSON.parse(file));
|
16
26
|
return {
|
17
|
-
schemas: fn(schemas)
|
27
|
+
schemas: fn(schemas),
|
18
28
|
};
|
19
29
|
}
|
20
|
-
if (
|
30
|
+
if (files.length === 0) {
|
21
31
|
throw new Error('Error [too few files]: requires one schema file per contract');
|
22
32
|
}
|
23
|
-
if (
|
33
|
+
if (files.length !== 1) {
|
24
34
|
throw new Error('Error [too many files]: CosmWasm v1.1 schemas supports one file');
|
25
35
|
}
|
26
|
-
const idlObject =
|
36
|
+
const idlObject = JSON.parse(files[0]);
|
27
37
|
const {
|
28
38
|
// contract_name,
|
29
39
|
// contract_version,
|
30
|
-
idl_version, responses, instantiate, execute, query, migrate, sudo } = idlObject;
|
40
|
+
idl_version, responses, instantiate, execute, query, migrate, sudo, } = idlObject;
|
31
41
|
if (typeof idl_version !== 'string') {
|
32
42
|
// legacy
|
43
|
+
// fall back to a single JSON Schema file
|
44
|
+
console.warn('Found a single schema file with missing idl_version. This mode will be removed in the next major version. Please migrate the schemas that contain a single <contract_name>.json IDL file (CosmWasm 1.1+).');
|
45
|
+
const schema = JSON.parse(files[0]);
|
33
46
|
return {
|
34
|
-
schemas: fn(
|
47
|
+
schemas: fn([schema]),
|
35
48
|
};
|
36
49
|
}
|
37
50
|
// TODO use contract_name, etc.
|
@@ -40,23 +53,23 @@ export const readSchemas = async ({ schemaDir, clean = true }) => {
|
|
40
53
|
execute,
|
41
54
|
query,
|
42
55
|
migrate,
|
43
|
-
sudo
|
56
|
+
sudo,
|
44
57
|
};
|
45
58
|
return {
|
46
59
|
schemas: [
|
47
60
|
...Object.values(fn(idl)).filter(Boolean),
|
48
|
-
...Object.values(fn({ ...responses })).filter(Boolean)
|
61
|
+
...Object.values(fn({ ...responses })).filter(Boolean),
|
49
62
|
],
|
50
63
|
responses,
|
51
|
-
idlObject
|
64
|
+
idlObject,
|
52
65
|
};
|
53
66
|
};
|
54
67
|
export const findQueryMsg = (schemas) => {
|
55
|
-
const queryMsg = schemas.find(schema => schema.title === 'QueryMsg');
|
68
|
+
const queryMsg = schemas.find((schema) => schema.title === 'QueryMsg');
|
56
69
|
return queryMsg;
|
57
70
|
};
|
58
71
|
export const findExecuteMsg = (schemas) => {
|
59
|
-
const executeMsg = schemas.find(schema => schema.title.startsWith('ExecuteMsg'));
|
72
|
+
const executeMsg = schemas.find((schema) => schema.title.startsWith('ExecuteMsg'));
|
60
73
|
return executeMsg;
|
61
74
|
};
|
62
75
|
export const findAndParseTypes = async (schemas) => {
|
package/esm/utils/unused.js
CHANGED
@@ -13,8 +13,7 @@ export const unused = {
|
|
13
13
|
const importName = source.node.value;
|
14
14
|
if (!t.isStringLiteral(source))
|
15
15
|
continue;
|
16
|
-
const key = `${importName}(${source.node.loc &&
|
17
|
-
source.node.loc.start.line})`;
|
16
|
+
const key = `${importName}(${source.node.loc && source.node.loc.start.line})`;
|
18
17
|
if (!UnRefBindings.has(key)) {
|
19
18
|
UnRefBindings.set(key, binding);
|
20
19
|
}
|
@@ -42,6 +41,6 @@ export const unused = {
|
|
42
41
|
binding.path.parentPath.remove();
|
43
42
|
}
|
44
43
|
});
|
45
|
-
}
|
46
|
-
}
|
44
|
+
},
|
45
|
+
},
|
47
46
|
};
|
package/file.js
CHANGED
@@ -1,17 +1,21 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
2
|
"use strict";
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
5
|
+
};
|
3
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
4
7
|
const fs_1 = require("fs");
|
8
|
+
const minimist_1 = __importDefault(require("minimist"));
|
5
9
|
const cli_1 = require("./cli");
|
6
10
|
const prompt_1 = require("./utils/prompt");
|
7
|
-
const argv =
|
11
|
+
const argv = (0, minimist_1.default)(process.argv.slice(2));
|
8
12
|
const question = [
|
9
13
|
{
|
10
14
|
_: true,
|
11
15
|
type: 'string',
|
12
16
|
name: 'file',
|
13
|
-
message: 'file'
|
14
|
-
}
|
17
|
+
message: 'file',
|
18
|
+
},
|
15
19
|
];
|
16
20
|
(async () => {
|
17
21
|
const { file } = await (0, prompt_1.prompt)(question, argv);
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare const baseClient = "\nimport { StdFee, Coin } from '@interchainjs/types';\nimport { DirectSigner } from '@interchainjs/cosmos';\nimport { getSmartContractState } from 'interchainjs/cosmwasm/wasm/v1/query.rpc.func';\nimport { executeContract } from 'interchainjs/cosmwasm/wasm/v1/tx.rpc.func';\nimport { QuerySmartContractStateRequest, QuerySmartContractStateResponse } from 'interchainjs/cosmwasm/wasm/v1/query';\nimport { MsgExecuteContract } from 'interchainjs/cosmwasm/wasm/v1/tx';\nimport { Chain } from '@chain-registry/v2-types';\n\n// Encoding utility functions\nconst fromUint8Array = <T>(uint8Array: Uint8Array): T => {\n const text = new TextDecoder().decode(uint8Array);\n return JSON.parse(text);\n};\n\nconst toUint8Array = (obj: any): Uint8Array => {\n const text = JSON.stringify(obj);\n return new TextEncoder().encode(text);\n};\n\n// Chain registry configuration\n// The amount under gasPrice represents gas price per unit\nexport interface ChainConfig {\n chain?: Chain;\n gasPrice?: {\n denom: string;\n amount: string;\n };\n}\n\n// Gas fee calculation utilities\nexport const calculateGasFromChain = (chain: Chain, gasAmount: string): StdFee => {\n try {\n const feeTokens = chain.fees?.feeTokens;\n \n if (feeTokens && feeTokens.length > 0) {\n const primaryToken = feeTokens[0];\n // v2 chain-registry uses camelCase: averageGasPrice, lowGasPrice, fixedMinGasPrice\n const gasPrice = primaryToken.averageGasPrice || primaryToken.lowGasPrice || primaryToken.fixedMinGasPrice || 0.025;\n const gasAmountNum = parseInt(gasAmount);\n const feeAmount = Math.ceil(gasAmountNum * gasPrice).toString();\n \n return {\n amount: [{\n denom: primaryToken.denom,\n amount: feeAmount\n }],\n gas: gasAmount\n };\n }\n } catch (error) {\n console.warn('Failed to calculate gas from chain registry:', error);\n }\n \n // Fallback to default\n return { amount: [], gas: gasAmount };\n};\n\n// Default gas amount - users can easily change this\nexport let DEFAULT_GAS_AMOUNT = '200000';\n\n// Allow users to set their preferred default gas amount\nexport const setDefaultGasAmount = (gasAmount: string): void => {\n DEFAULT_GAS_AMOUNT = gasAmount;\n};\n\n// Get current default gas amount\nexport const getDefaultGasAmount = (): string => DEFAULT_GAS_AMOUNT;\n\nexport const getAutoGasFee = (chainConfig?: ChainConfig): StdFee => {\n const gasAmount = DEFAULT_GAS_AMOUNT;\n \n if (chainConfig?.chain) {\n return calculateGasFromChain(chainConfig.chain, gasAmount);\n }\n \n if (chainConfig?.gasPrice) {\n const gasAmountNum = parseInt(gasAmount);\n const gasPriceNum = parseFloat(chainConfig.gasPrice.amount);\n const feeAmount = Math.ceil(gasAmountNum * gasPriceNum).toString();\n \n return {\n amount: [{\n denom: chainConfig.gasPrice.denom,\n amount: feeAmount\n }],\n gas: gasAmount\n };\n }\n \n // Fallback: no fee tokens, just gas amount\n return { amount: [], gas: gasAmount };\n};\n\n// InterchainJS interfaces for CosmWasm clients\nexport interface ICosmWasmClient {\n queryContractSmart(contractAddr: string, query: any): Promise<any>;\n}\n\nexport interface ISigningCosmWasmClient {\n execute(\n sender: string, \n contractAddress: string, \n msg: any, \n fee?: number | StdFee | \"auto\", \n memo?: string, \n funds?: Coin[], \n chainConfig?: ChainConfig\n ): Promise<any>;\n}\n\nexport interface ISigningClient {\n signAndBroadcast(\n signerAddress: string,\n messages: any[],\n fee: number | StdFee | \"auto\",\n memo?: string\n ): Promise<any>;\n}\n\n// Helper functions to create InterchainJS clients\nexport function getCosmWasmClient(rpcEndpoint: string): ICosmWasmClient {\n return {\n queryContractSmart: async (contractAddr: string, query: any) => {\n // Create the request object\n const request: QuerySmartContractStateRequest = {\n address: contractAddr,\n queryData: toUint8Array(query)\n };\n \n // Execute the query using InterchainJS\n const response: QuerySmartContractStateResponse = await getSmartContractState(rpcEndpoint, request);\n \n // Parse and return the result\n return fromUint8Array(response.data);\n },\n };\n}\n\nexport function getSigningCosmWasmClient(signingClient: DirectSigner): ISigningCosmWasmClient {\n return {\n execute: async (\n sender: string, \n contractAddress: string, \n msg: any, \n fee?: number | StdFee | \"auto\", \n memo?: string, \n funds?: Coin[], \n chainConfig?: ChainConfig\n ) => {\n // Handle fee conversion\n let finalFee: StdFee;\n if (typeof fee === 'number') {\n finalFee = { amount: [], gas: fee.toString() };\n } else if (fee === 'auto') {\n finalFee = getAutoGasFee(chainConfig);\n } else if (fee) {\n finalFee = fee;\n } else {\n finalFee = getAutoGasFee(chainConfig);\n }\n\n // Create the message object\n const message: MsgExecuteContract = {\n sender,\n contract: contractAddress,\n msg: toUint8Array(msg),\n funds: funds || []\n };\n \n // Execute the transaction using InterchainJS\n const result = await executeContract(\n signingClient as any,\n sender,\n message,\n finalFee,\n memo || ''\n );\n \n return result;\n },\n };\n}\n";
|
@@ -0,0 +1,187 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.baseClient = void 0;
|
4
|
+
exports.baseClient = `
|
5
|
+
import { StdFee, Coin } from '@interchainjs/types';
|
6
|
+
import { DirectSigner } from '@interchainjs/cosmos';
|
7
|
+
import { getSmartContractState } from 'interchainjs/cosmwasm/wasm/v1/query.rpc.func';
|
8
|
+
import { executeContract } from 'interchainjs/cosmwasm/wasm/v1/tx.rpc.func';
|
9
|
+
import { QuerySmartContractStateRequest, QuerySmartContractStateResponse } from 'interchainjs/cosmwasm/wasm/v1/query';
|
10
|
+
import { MsgExecuteContract } from 'interchainjs/cosmwasm/wasm/v1/tx';
|
11
|
+
import { Chain } from '@chain-registry/v2-types';
|
12
|
+
|
13
|
+
// Encoding utility functions
|
14
|
+
const fromUint8Array = <T>(uint8Array: Uint8Array): T => {
|
15
|
+
const text = new TextDecoder().decode(uint8Array);
|
16
|
+
return JSON.parse(text);
|
17
|
+
};
|
18
|
+
|
19
|
+
const toUint8Array = (obj: any): Uint8Array => {
|
20
|
+
const text = JSON.stringify(obj);
|
21
|
+
return new TextEncoder().encode(text);
|
22
|
+
};
|
23
|
+
|
24
|
+
// Chain registry configuration
|
25
|
+
// The amount under gasPrice represents gas price per unit
|
26
|
+
export interface ChainConfig {
|
27
|
+
chain?: Chain;
|
28
|
+
gasPrice?: {
|
29
|
+
denom: string;
|
30
|
+
amount: string;
|
31
|
+
};
|
32
|
+
}
|
33
|
+
|
34
|
+
// Gas fee calculation utilities
|
35
|
+
export const calculateGasFromChain = (chain: Chain, gasAmount: string): StdFee => {
|
36
|
+
try {
|
37
|
+
const feeTokens = chain.fees?.feeTokens;
|
38
|
+
|
39
|
+
if (feeTokens && feeTokens.length > 0) {
|
40
|
+
const primaryToken = feeTokens[0];
|
41
|
+
// v2 chain-registry uses camelCase: averageGasPrice, lowGasPrice, fixedMinGasPrice
|
42
|
+
const gasPrice = primaryToken.averageGasPrice || primaryToken.lowGasPrice || primaryToken.fixedMinGasPrice || 0.025;
|
43
|
+
const gasAmountNum = parseInt(gasAmount);
|
44
|
+
const feeAmount = Math.ceil(gasAmountNum * gasPrice).toString();
|
45
|
+
|
46
|
+
return {
|
47
|
+
amount: [{
|
48
|
+
denom: primaryToken.denom,
|
49
|
+
amount: feeAmount
|
50
|
+
}],
|
51
|
+
gas: gasAmount
|
52
|
+
};
|
53
|
+
}
|
54
|
+
} catch (error) {
|
55
|
+
console.warn('Failed to calculate gas from chain registry:', error);
|
56
|
+
}
|
57
|
+
|
58
|
+
// Fallback to default
|
59
|
+
return { amount: [], gas: gasAmount };
|
60
|
+
};
|
61
|
+
|
62
|
+
// Default gas amount - users can easily change this
|
63
|
+
export let DEFAULT_GAS_AMOUNT = '200000';
|
64
|
+
|
65
|
+
// Allow users to set their preferred default gas amount
|
66
|
+
export const setDefaultGasAmount = (gasAmount: string): void => {
|
67
|
+
DEFAULT_GAS_AMOUNT = gasAmount;
|
68
|
+
};
|
69
|
+
|
70
|
+
// Get current default gas amount
|
71
|
+
export const getDefaultGasAmount = (): string => DEFAULT_GAS_AMOUNT;
|
72
|
+
|
73
|
+
export const getAutoGasFee = (chainConfig?: ChainConfig): StdFee => {
|
74
|
+
const gasAmount = DEFAULT_GAS_AMOUNT;
|
75
|
+
|
76
|
+
if (chainConfig?.chain) {
|
77
|
+
return calculateGasFromChain(chainConfig.chain, gasAmount);
|
78
|
+
}
|
79
|
+
|
80
|
+
if (chainConfig?.gasPrice) {
|
81
|
+
const gasAmountNum = parseInt(gasAmount);
|
82
|
+
const gasPriceNum = parseFloat(chainConfig.gasPrice.amount);
|
83
|
+
const feeAmount = Math.ceil(gasAmountNum * gasPriceNum).toString();
|
84
|
+
|
85
|
+
return {
|
86
|
+
amount: [{
|
87
|
+
denom: chainConfig.gasPrice.denom,
|
88
|
+
amount: feeAmount
|
89
|
+
}],
|
90
|
+
gas: gasAmount
|
91
|
+
};
|
92
|
+
}
|
93
|
+
|
94
|
+
// Fallback: no fee tokens, just gas amount
|
95
|
+
return { amount: [], gas: gasAmount };
|
96
|
+
};
|
97
|
+
|
98
|
+
// InterchainJS interfaces for CosmWasm clients
|
99
|
+
export interface ICosmWasmClient {
|
100
|
+
queryContractSmart(contractAddr: string, query: any): Promise<any>;
|
101
|
+
}
|
102
|
+
|
103
|
+
export interface ISigningCosmWasmClient {
|
104
|
+
execute(
|
105
|
+
sender: string,
|
106
|
+
contractAddress: string,
|
107
|
+
msg: any,
|
108
|
+
fee?: number | StdFee | "auto",
|
109
|
+
memo?: string,
|
110
|
+
funds?: Coin[],
|
111
|
+
chainConfig?: ChainConfig
|
112
|
+
): Promise<any>;
|
113
|
+
}
|
114
|
+
|
115
|
+
export interface ISigningClient {
|
116
|
+
signAndBroadcast(
|
117
|
+
signerAddress: string,
|
118
|
+
messages: any[],
|
119
|
+
fee: number | StdFee | "auto",
|
120
|
+
memo?: string
|
121
|
+
): Promise<any>;
|
122
|
+
}
|
123
|
+
|
124
|
+
// Helper functions to create InterchainJS clients
|
125
|
+
export function getCosmWasmClient(rpcEndpoint: string): ICosmWasmClient {
|
126
|
+
return {
|
127
|
+
queryContractSmart: async (contractAddr: string, query: any) => {
|
128
|
+
// Create the request object
|
129
|
+
const request: QuerySmartContractStateRequest = {
|
130
|
+
address: contractAddr,
|
131
|
+
queryData: toUint8Array(query)
|
132
|
+
};
|
133
|
+
|
134
|
+
// Execute the query using InterchainJS
|
135
|
+
const response: QuerySmartContractStateResponse = await getSmartContractState(rpcEndpoint, request);
|
136
|
+
|
137
|
+
// Parse and return the result
|
138
|
+
return fromUint8Array(response.data);
|
139
|
+
},
|
140
|
+
};
|
141
|
+
}
|
142
|
+
|
143
|
+
export function getSigningCosmWasmClient(signingClient: DirectSigner): ISigningCosmWasmClient {
|
144
|
+
return {
|
145
|
+
execute: async (
|
146
|
+
sender: string,
|
147
|
+
contractAddress: string,
|
148
|
+
msg: any,
|
149
|
+
fee?: number | StdFee | "auto",
|
150
|
+
memo?: string,
|
151
|
+
funds?: Coin[],
|
152
|
+
chainConfig?: ChainConfig
|
153
|
+
) => {
|
154
|
+
// Handle fee conversion
|
155
|
+
let finalFee: StdFee;
|
156
|
+
if (typeof fee === 'number') {
|
157
|
+
finalFee = { amount: [], gas: fee.toString() };
|
158
|
+
} else if (fee === 'auto') {
|
159
|
+
finalFee = getAutoGasFee(chainConfig);
|
160
|
+
} else if (fee) {
|
161
|
+
finalFee = fee;
|
162
|
+
} else {
|
163
|
+
finalFee = getAutoGasFee(chainConfig);
|
164
|
+
}
|
165
|
+
|
166
|
+
// Create the message object
|
167
|
+
const message: MsgExecuteContract = {
|
168
|
+
sender,
|
169
|
+
contract: contractAddress,
|
170
|
+
msg: toUint8Array(msg),
|
171
|
+
funds: funds || []
|
172
|
+
};
|
173
|
+
|
174
|
+
// Execute the transaction using InterchainJS
|
175
|
+
const result = await executeContract(
|
176
|
+
signingClient as any,
|
177
|
+
sender,
|
178
|
+
message,
|
179
|
+
finalFee,
|
180
|
+
memo || ''
|
181
|
+
);
|
182
|
+
|
183
|
+
return result;
|
184
|
+
},
|
185
|
+
};
|
186
|
+
}
|
187
|
+
`;
|
@@ -1 +1 @@
|
|
1
|
-
export declare const contractContextBase = "\nimport {\n
|
1
|
+
export declare const contractContextBase = "\nimport {\n ICosmWasmClient,\n ISigningCosmWasmClient,\n getCosmWasmClient,\n getSigningCosmWasmClient,\n} from './baseClient';\n\nexport interface IContractConstructor {\n address: string | undefined;\n cosmWasmClient: ICosmWasmClient | undefined;\n signingCosmWasmClient: ISigningCosmWasmClient | undefined;\n}\n\nexport const NO_SINGING_ERROR_MESSAGE = 'signingCosmWasmClient not connected';\n\nexport const NO_COSMWASW_CLIENT_ERROR_MESSAGE = 'cosmWasmClient not connected';\n\nexport const NO_ADDRESS_ERROR_MESSAGE = \"address doesn't exist\";\n\nexport const NO_SIGNING_CLIENT_ERROR_MESSAGE =\n 'Signing client is not generated. Please check ts-codegen config';\n\nexport const NO_QUERY_CLIENT_ERROR_MESSAGE =\n 'Query client is not generated. Please check ts-codegen config';\n\nexport const NO_MESSAGE_COMPOSER_ERROR_MESSAGE =\n 'Message composer client is not generated. Please check ts-codegen config';\n\n/**\n * a placeholder for non-generated classes\n */\nexport interface IEmptyClient {}\n\nexport interface ISigningClientProvider<T> {\n getSigningClient(contractAddr: string): T;\n}\n\nexport interface IQueryClientProvider<T> {\n getQueryClient(contractAddr: string): T;\n}\n\nexport interface IMessageComposerProvider<T> {\n getMessageComposer(contractAddr: string): T;\n}\n\nexport class ContractBase<\n TSign = IEmptyClient,\n TQuery = IEmptyClient,\n TMsgComposer = IEmptyClient\n> {\n\n address: string | undefined;\n cosmWasmClient: ICosmWasmClient | undefined;\n signingCosmWasmClient: ISigningCosmWasmClient | undefined;\n TSign?: new (\n client: ISigningCosmWasmClient,\n sender: string,\n contractAddress: string\n ) => TSign;\n TQuery?: new (\n client: ICosmWasmClient,\n contractAddress: string\n ) => TQuery;\n TMsgComposer?: new (\n sender: string,\n contractAddress: string\n ) => TMsgComposer;\n\n constructor(\n address: string | undefined,\n cosmWasmClient: ICosmWasmClient | undefined,\n signingCosmWasmClient: ISigningCosmWasmClient | undefined,\n TSign?: new (\n client: ISigningCosmWasmClient,\n sender: string,\n contractAddress: string\n ) => TSign,\n TQuery?: new (\n client: ICosmWasmClient,\n contractAddress: string\n ) => TQuery,\n TMsgComposer?: new (\n sender: string,\n contractAddress: string\n ) => TMsgComposer\n ) {\n this.address = address;\n this.cosmWasmClient = cosmWasmClient;\n this.signingCosmWasmClient = signingCosmWasmClient;\n this.TSign = TSign;\n this.TQuery = TQuery;\n this.TMsgComposer = TMsgComposer;\n }\n\n public getSigningClient(contractAddr: string): TSign {\n if (!this.signingCosmWasmClient) throw new Error(NO_SINGING_ERROR_MESSAGE);\n if (!this.address) throw new Error(NO_ADDRESS_ERROR_MESSAGE);\n if (!this.TSign) throw new Error(NO_SIGNING_CLIENT_ERROR_MESSAGE);\n return new this.TSign(\n this.signingCosmWasmClient,\n this.address,\n contractAddr\n );\n }\n\n public getQueryClient(contractAddr: string): TQuery {\n if (!this.cosmWasmClient) throw new Error(NO_COSMWASW_CLIENT_ERROR_MESSAGE);\n if (!this.TQuery) throw new Error(NO_QUERY_CLIENT_ERROR_MESSAGE);\n return new this.TQuery(this.cosmWasmClient, contractAddr);\n }\n\n public getMessageComposer(contractAddr: string): TMsgComposer {\n if (!this.address) throw new Error(NO_ADDRESS_ERROR_MESSAGE);\n if (!this.TMsgComposer) throw new Error(NO_MESSAGE_COMPOSER_ERROR_MESSAGE);\n return new this.TMsgComposer(this.address, contractAddr);\n }\n}\n";
|