@cosmwasm/ts-codegen 0.30.1 → 0.31.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +5 -2
- package/main/builder/builder.js +41 -4
- package/main/generators/create-helpers.js +38 -0
- package/main/helpers/contractContextBase.js +8 -0
- package/main/helpers/contractsContextTSX.js +8 -0
- package/main/helpers/index.js +31 -0
- package/main/plugins/client.js +11 -6
- package/main/plugins/message-composer.js +10 -6
- package/main/plugins/msg-builder.js +1 -1
- package/main/plugins/plugin-base.js +9 -2
- package/main/plugins/provider-bundle.js +146 -0
- package/main/plugins/provider.js +170 -0
- package/main/plugins/react-query.js +1 -1
- package/main/plugins/recoil.js +1 -1
- package/main/plugins/types.js +1 -1
- package/main/utils/files.js +77 -0
- package/main/utils/unused.js +68 -0
- package/module/builder/builder.js +28 -2
- package/module/generators/create-helpers.js +25 -0
- package/module/helpers/contractContextBase.js +92 -0
- package/module/helpers/contractsContextTSX.js +73 -0
- package/module/helpers/index.js +2 -0
- package/module/plugins/client.js +13 -10
- package/module/plugins/message-composer.js +12 -10
- package/module/plugins/msg-builder.js +1 -1
- package/module/plugins/plugin-base.js +15 -8
- package/module/plugins/provider-bundle.js +65 -0
- package/module/plugins/provider.js +81 -0
- package/module/plugins/react-query.js +1 -1
- package/module/plugins/recoil.js +1 -1
- package/module/plugins/types.js +1 -1
- package/module/utils/files.js +44 -0
- package/module/utils/unused.js +45 -0
- package/package.json +3 -3
- package/src/builder/builder.ts +36 -1
- package/src/generators/create-helpers.ts +28 -0
- package/src/helpers/contractContextBase.ts +92 -0
- package/src/helpers/contractsContextTSX.ts +73 -0
- package/src/helpers/index.ts +2 -0
- package/src/plugins/client.ts +30 -14
- package/src/plugins/message-composer.ts +23 -14
- package/src/plugins/msg-builder.ts +1 -1
- package/src/plugins/plugin-base.ts +30 -20
- package/src/plugins/provider-bundle.ts +97 -0
- package/src/plugins/provider.ts +114 -0
- package/src/plugins/react-query.ts +1 -1
- package/src/plugins/recoil.ts +1 -1
- package/src/plugins/types.ts +1 -1
- package/src/utils/files.ts +73 -0
- package/src/utils/unused.ts +52 -0
- package/types/src/builder/builder.d.ts +7 -1
- package/types/src/generators/create-helpers.d.ts +3 -0
- package/types/src/helpers/contractContextBase.d.ts +1 -0
- package/types/src/helpers/contractsContextTSX.d.ts +1 -0
- package/types/src/helpers/index.d.ts +2 -0
- package/types/src/plugins/client.d.ts +4 -3
- package/types/src/plugins/message-composer.d.ts +4 -3
- package/types/src/plugins/plugin-base.d.ts +7 -3
- package/types/src/plugins/provider-bundle.d.ts +13 -0
- package/types/src/plugins/provider.d.ts +15 -0
- package/types/src/plugins/use-contracts.d.ts +12 -0
- package/types/src/utils/files.d.ts +3 -0
- package/types/src/utils/unused.d.ts +5 -0
@@ -1,11 +1,12 @@
|
|
1
|
-
import { pascal } from
|
2
|
-
import * as w from
|
3
|
-
import { findAndParseTypes, findExecuteMsg } from
|
4
|
-
import { getMessageProperties, RenderContext } from
|
5
|
-
import { BuilderPluginBase } from
|
1
|
+
import { pascal } from "case";
|
2
|
+
import * as w from "wasm-ast-types";
|
3
|
+
import { findAndParseTypes, findExecuteMsg } from "../utils";
|
4
|
+
import { getMessageProperties, RenderContext } from "wasm-ast-types";
|
5
|
+
import { BuilderPluginBase } from "./plugin-base";
|
6
|
+
export const TYPE = "message-composer";
|
6
7
|
export class MessageComposerPlugin extends BuilderPluginBase {
|
7
8
|
initContext(contract, options) {
|
8
|
-
return new RenderContext(contract, options);
|
9
|
+
return new RenderContext(contract, options, this.builder.builderContext);
|
9
10
|
}
|
10
11
|
|
11
12
|
async doRender(name, context) {
|
@@ -20,8 +21,8 @@ export class MessageComposerPlugin extends BuilderPluginBase {
|
|
20
21
|
const {
|
21
22
|
schemas
|
22
23
|
} = context.contract;
|
23
|
-
const localname = pascal(name) +
|
24
|
-
const TypesFile = pascal(name) +
|
24
|
+
const localname = pascal(name) + ".message-composer.ts";
|
25
|
+
const TypesFile = pascal(name) + ".types";
|
25
26
|
const ExecuteMsg = findExecuteMsg(schemas);
|
26
27
|
const typeHash = await findAndParseTypes(schemas);
|
27
28
|
const body = [];
|
@@ -35,16 +36,17 @@ export class MessageComposerPlugin extends BuilderPluginBase {
|
|
35
36
|
const Interface = pascal(`${name}Message`);
|
36
37
|
body.push(w.createMessageComposerInterface(context, Interface, ExecuteMsg));
|
37
38
|
body.push(w.createMessageComposerClass(context, TheClass, Interface, ExecuteMsg));
|
39
|
+
context.addProviderInfo(name, w.PROVIDER_TYPES.MESSAGE_COMPOSER_TYPE, TheClass, localname);
|
38
40
|
}
|
39
41
|
}
|
40
42
|
|
41
|
-
if (typeHash.hasOwnProperty(
|
43
|
+
if (typeHash.hasOwnProperty("Coin")) {
|
42
44
|
// @ts-ignore
|
43
45
|
delete context.utils.Coin;
|
44
46
|
}
|
45
47
|
|
46
48
|
return [{
|
47
|
-
type:
|
49
|
+
type: TYPE,
|
48
50
|
localname,
|
49
51
|
body
|
50
52
|
}];
|
@@ -5,7 +5,7 @@ import { getMessageProperties, RenderContext } from 'wasm-ast-types';
|
|
5
5
|
import { BuilderPluginBase } from './plugin-base';
|
6
6
|
export class MsgBuilderPlugin extends BuilderPluginBase {
|
7
7
|
initContext(contract, options) {
|
8
|
-
return new RenderContext(contract, options);
|
8
|
+
return new RenderContext(contract, options, this.builder.builderContext);
|
9
9
|
}
|
10
10
|
|
11
11
|
async doRender(name, context) {
|
@@ -1,21 +1,28 @@
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
|
2
|
-
import { sync as mkdirp } from
|
3
|
-
import { join } from
|
4
|
-
import { writeFileSync } from
|
5
|
-
import { header } from
|
6
|
-
import generate from
|
7
|
-
import * as t from
|
2
|
+
import { sync as mkdirp } from "mkdirp";
|
3
|
+
import { join } from "path";
|
4
|
+
import { writeFileSync } from "fs";
|
5
|
+
import { header } from "../utils/header";
|
6
|
+
import generate from "@babel/generator";
|
7
|
+
import * as t from "@babel/types";
|
8
8
|
|
9
9
|
/**
|
10
10
|
* BuilderPluginBase enable ts-codegen users implement their own plugins by only implement a few functions.
|
11
11
|
*/
|
12
12
|
export class BuilderPluginBase {
|
13
|
-
constructor(opt) {
|
13
|
+
constructor(opt, builder) {
|
14
|
+
_defineProperty(this, "builder", void 0);
|
15
|
+
|
14
16
|
_defineProperty(this, "option", void 0);
|
15
17
|
|
16
18
|
_defineProperty(this, "utils", void 0);
|
17
19
|
|
18
20
|
this.option = opt;
|
21
|
+
this.builder = builder;
|
22
|
+
}
|
23
|
+
|
24
|
+
setBuilder(builder) {
|
25
|
+
this.builder = builder;
|
19
26
|
}
|
20
27
|
|
21
28
|
async render(name, contractInfo, outPath) {
|
@@ -35,7 +42,7 @@ export class BuilderPluginBase {
|
|
35
42
|
}
|
36
43
|
|
37
44
|
return results.map(result => {
|
38
|
-
const imports = context.getImports(this.utils);
|
45
|
+
const imports = context.getImports(this.utils, result.localname);
|
39
46
|
const code = header + generate(t.program([...imports, ...result.body])).code;
|
40
47
|
mkdirp(outPath);
|
41
48
|
const filename = join(outPath, result.localname);
|
@@ -0,0 +1,65 @@
|
|
1
|
+
import { pascal } from "case";
|
2
|
+
import * as w from "wasm-ast-types";
|
3
|
+
import { RenderContext } from "wasm-ast-types";
|
4
|
+
import { BuilderPluginBase } from "./plugin-base";
|
5
|
+
import { GetLocalBaseNameByContractName } from "./provider";
|
6
|
+
export class ContractsProviderBundlePlugin extends BuilderPluginBase {
|
7
|
+
constructor(opt) {
|
8
|
+
super(opt);
|
9
|
+
this.utils = {
|
10
|
+
CosmWasmClient: "@cosmjs/cosmwasm-stargate",
|
11
|
+
SigningCosmWasmClient: "@cosmjs/cosmwasm-stargate",
|
12
|
+
IQueryClientProvider: "__contractContextBase__",
|
13
|
+
ISigningClientProvider: "__contractContextBase__",
|
14
|
+
IMessageComposerProvider: "__contractContextBase__"
|
15
|
+
};
|
16
|
+
}
|
17
|
+
|
18
|
+
initContext(contract, options) {
|
19
|
+
return new RenderContext(contract, options, this.builder.builderContext);
|
20
|
+
}
|
21
|
+
|
22
|
+
async doRender(name, context) {
|
23
|
+
if (!this.option?.useContracts?.enabled) {
|
24
|
+
return;
|
25
|
+
}
|
26
|
+
|
27
|
+
const providerInfos = context.getProviderInfos();
|
28
|
+
|
29
|
+
if (!Object.keys(providerInfos)?.length) {
|
30
|
+
return;
|
31
|
+
}
|
32
|
+
|
33
|
+
const localname = "contractContextProviders.ts";
|
34
|
+
const body = [];
|
35
|
+
context.addUtil("CosmWasmClient");
|
36
|
+
context.addUtil("SigningCosmWasmClient");
|
37
|
+
context.addUtil("IQueryClientProvider");
|
38
|
+
context.addUtil("ISigningClientProvider");
|
39
|
+
context.addUtil("IMessageComposerProvider");
|
40
|
+
|
41
|
+
for (const name in providerInfos) {
|
42
|
+
if (Object.prototype.hasOwnProperty.call(providerInfos, name)) {
|
43
|
+
const providerInfo = providerInfos[name];
|
44
|
+
|
45
|
+
for (const key in providerInfo) {
|
46
|
+
if (Object.prototype.hasOwnProperty.call(providerInfo, key)) {
|
47
|
+
const info = providerInfo[key];
|
48
|
+
body.push(w.importStmt([info.classname], `./${info.basename}`));
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
body.push(w.importStmt([pascal(name)], `./${GetLocalBaseNameByContractName(name)}`));
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
body.push(w.createIContractsContext(providerInfos));
|
57
|
+
body.push(w.createGettingProviders(providerInfos));
|
58
|
+
return [{
|
59
|
+
type: "plugin",
|
60
|
+
localname,
|
61
|
+
body
|
62
|
+
}];
|
63
|
+
}
|
64
|
+
|
65
|
+
}
|
@@ -0,0 +1,81 @@
|
|
1
|
+
import { pascal } from "case";
|
2
|
+
import * as w from "wasm-ast-types";
|
3
|
+
import { RenderContext } from "wasm-ast-types";
|
4
|
+
import { BuilderPluginBase } from "./plugin-base";
|
5
|
+
export const GetLocalNameByContractName = name => `${pascal(name)}.provider.ts`;
|
6
|
+
export const GetLocalBaseNameByContractName = name => `${pascal(name)}.provider`;
|
7
|
+
export class ContractsContextProviderPlugin extends BuilderPluginBase {
|
8
|
+
constructor(opt) {
|
9
|
+
super(opt);
|
10
|
+
this.utils = {
|
11
|
+
ContractBase: "__contractContextBase__",
|
12
|
+
IContractConstructor: "__contractContextBase__",
|
13
|
+
IEmptyClient: "__contractContextBase__"
|
14
|
+
};
|
15
|
+
}
|
16
|
+
|
17
|
+
initContext(contract, options) {
|
18
|
+
return new RenderContext(contract, options, this.builder.builderContext);
|
19
|
+
}
|
20
|
+
|
21
|
+
async doRender(name, context) {
|
22
|
+
if (!this.option?.useContracts?.enabled) {
|
23
|
+
return;
|
24
|
+
}
|
25
|
+
|
26
|
+
const providerInfo = context.getProviderInfos()[name];
|
27
|
+
|
28
|
+
if (!Object.keys(providerInfo)?.length) {
|
29
|
+
return;
|
30
|
+
}
|
31
|
+
|
32
|
+
context.addUtil("ContractBase");
|
33
|
+
context.addUtil("IContractConstructor");
|
34
|
+
const localname = GetLocalNameByContractName(name);
|
35
|
+
let needEmptyClientType = false;
|
36
|
+
let clientFile = null;
|
37
|
+
let clientClasses = [];
|
38
|
+
const body = [];
|
39
|
+
const signClientProviderInfo = providerInfo[w.PROVIDER_TYPES.SIGNING_CLIENT_TYPE];
|
40
|
+
|
41
|
+
if (signClientProviderInfo) {
|
42
|
+
clientFile = `./${signClientProviderInfo.basename}`;
|
43
|
+
clientClasses.push(signClientProviderInfo.classname);
|
44
|
+
} else {
|
45
|
+
needEmptyClientType = true;
|
46
|
+
}
|
47
|
+
|
48
|
+
const queryClientProviderInfo = providerInfo[w.PROVIDER_TYPES.QUERY_CLIENT_TYPE];
|
49
|
+
|
50
|
+
if (queryClientProviderInfo) {
|
51
|
+
clientFile = `./${queryClientProviderInfo.basename}`;
|
52
|
+
clientClasses.push(queryClientProviderInfo.classname);
|
53
|
+
} else {
|
54
|
+
needEmptyClientType = true;
|
55
|
+
}
|
56
|
+
|
57
|
+
if (clientFile) {
|
58
|
+
body.push(w.importStmt(clientClasses, clientFile));
|
59
|
+
}
|
60
|
+
|
61
|
+
const messageComposerProviderInfo = providerInfo[w.PROVIDER_TYPES.MESSAGE_COMPOSER_TYPE];
|
62
|
+
|
63
|
+
if (messageComposerProviderInfo) {
|
64
|
+
body.push(w.importStmt([messageComposerProviderInfo.classname], `./${messageComposerProviderInfo.basename}`));
|
65
|
+
} else {
|
66
|
+
needEmptyClientType = true;
|
67
|
+
}
|
68
|
+
|
69
|
+
if (needEmptyClientType) {
|
70
|
+
context.addUtil("IEmptyClient");
|
71
|
+
}
|
72
|
+
|
73
|
+
body.push(w.createProvider(name, providerInfo));
|
74
|
+
return [{
|
75
|
+
type: "plugin",
|
76
|
+
localname,
|
77
|
+
body
|
78
|
+
}];
|
79
|
+
}
|
80
|
+
|
81
|
+
}
|
@@ -5,7 +5,7 @@ import { getMessageProperties, RenderContext } from 'wasm-ast-types';
|
|
5
5
|
import { BuilderPluginBase } from './plugin-base';
|
6
6
|
export class ReactQueryPlugin extends BuilderPluginBase {
|
7
7
|
initContext(contract, options) {
|
8
|
-
return new RenderContext(contract, options);
|
8
|
+
return new RenderContext(contract, options, this.builder.builderContext);
|
9
9
|
}
|
10
10
|
|
11
11
|
async doRender(name, context) {
|
package/module/plugins/recoil.js
CHANGED
@@ -14,7 +14,7 @@ export class RecoilPlugin extends BuilderPluginBase {
|
|
14
14
|
}
|
15
15
|
|
16
16
|
initContext(contract, options) {
|
17
|
-
return new RenderContext(contract, options);
|
17
|
+
return new RenderContext(contract, options, this.builder.builderContext);
|
18
18
|
}
|
19
19
|
|
20
20
|
async doRender(name, context) {
|
package/module/plugins/types.js
CHANGED
@@ -6,7 +6,7 @@ import { RenderContext } from 'wasm-ast-types';
|
|
6
6
|
import { BuilderPluginBase } from './plugin-base';
|
7
7
|
export class TypesPlugin extends BuilderPluginBase {
|
8
8
|
initContext(contract, options) {
|
9
|
-
return new RenderContext(contract, options);
|
9
|
+
return new RenderContext(contract, options, this.builder.builderContext);
|
10
10
|
}
|
11
11
|
|
12
12
|
async doRender(name, context) {
|
@@ -0,0 +1,44 @@
|
|
1
|
+
import * as t from "@babel/types";
|
2
|
+
import { parse } from "@babel/parser";
|
3
|
+
import { sync as mkdirp } from "mkdirp";
|
4
|
+
import { writeFileSync } from "fs";
|
5
|
+
import { dirname } from "path";
|
6
|
+
import generate from "@babel/generator";
|
7
|
+
import { unused } from "./unused";
|
8
|
+
import traverse from "@babel/traverse";
|
9
|
+
export const writeAstToFile = (outPath, program, filename, removeUnusedImports = false, isTsDisable = false, isEslintDisable = false) => {
|
10
|
+
const ast = t.program(program);
|
11
|
+
const content = generate(ast).code;
|
12
|
+
|
13
|
+
if (removeUnusedImports) {
|
14
|
+
const plugins = ["typescript"];
|
15
|
+
const newAst = parse(content, {
|
16
|
+
sourceType: "module",
|
17
|
+
plugins
|
18
|
+
});
|
19
|
+
traverse(newAst, unused);
|
20
|
+
const content2 = generate(newAst).code;
|
21
|
+
writeContentToFile(outPath, content2, filename, isTsDisable, isEslintDisable);
|
22
|
+
} else {
|
23
|
+
writeContentToFile(outPath, content, filename, isTsDisable, isEslintDisable);
|
24
|
+
}
|
25
|
+
};
|
26
|
+
export const writeContentToFile = (outPath, content, filename, isTsDisable = false, isEslintDisable = false) => {
|
27
|
+
let esLintPrefix = "";
|
28
|
+
let tsLintPrefix = "";
|
29
|
+
let nameWithoutPath = filename.replace(outPath, ""); // strip off leading slash
|
30
|
+
|
31
|
+
if (nameWithoutPath.startsWith("/")) nameWithoutPath = nameWithoutPath.replace(/^\//, "");
|
32
|
+
|
33
|
+
if (isTsDisable) {
|
34
|
+
tsLintPrefix = `//@ts-nocheck\n`;
|
35
|
+
}
|
36
|
+
|
37
|
+
if (isEslintDisable) {
|
38
|
+
esLintPrefix = `/* eslint-disable */\n`;
|
39
|
+
}
|
40
|
+
|
41
|
+
const text = tsLintPrefix + esLintPrefix + content;
|
42
|
+
mkdirp(dirname(filename));
|
43
|
+
writeFileSync(filename, text);
|
44
|
+
};
|
@@ -0,0 +1,45 @@
|
|
1
|
+
//@ts-nocheck
|
2
|
+
import * as t from '@babel/types'; // https://github.com/chuyik/babel-plugin-danger-remove-unused-import
|
3
|
+
// https://github.com/chuyik/babel-plugin-danger-remove-unused-import/blob/c5454c21e94698a2464a12baa5590761932a71a8/License#L1
|
4
|
+
|
5
|
+
export const unused = {
|
6
|
+
Program: {
|
7
|
+
exit: path => {
|
8
|
+
const UnRefBindings = new Map();
|
9
|
+
|
10
|
+
for (const [name, binding] of Object.entries(path.scope.bindings)) {
|
11
|
+
if (!binding.path.parentPath || binding.kind !== 'module') continue;
|
12
|
+
const source = binding.path.parentPath.get('source');
|
13
|
+
const importName = source.node.value;
|
14
|
+
if (!t.isStringLiteral(source)) continue;
|
15
|
+
const key = `${importName}(${source.node.loc && source.node.loc.start.line})`;
|
16
|
+
|
17
|
+
if (!UnRefBindings.has(key)) {
|
18
|
+
UnRefBindings.set(key, binding);
|
19
|
+
}
|
20
|
+
|
21
|
+
if (binding.referenced) {
|
22
|
+
UnRefBindings.set(key, null);
|
23
|
+
} else {
|
24
|
+
const nodeType = binding.path.node.type;
|
25
|
+
|
26
|
+
if (nodeType === 'ImportSpecifier') {
|
27
|
+
binding.path.remove();
|
28
|
+
} else if (nodeType === 'ImportDefaultSpecifier') {
|
29
|
+
binding.path.remove();
|
30
|
+
} else if (nodeType === 'ImportNamespaceSpecifier') {
|
31
|
+
binding.path.remove();
|
32
|
+
} else if (binding.path.parentPath) {
|
33
|
+
binding.path.parentPath.remove();
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
UnRefBindings.forEach((binding, key) => {
|
39
|
+
if (binding && binding.path.parentPath) {
|
40
|
+
binding.path.parentPath.remove();
|
41
|
+
}
|
42
|
+
});
|
43
|
+
}
|
44
|
+
}
|
45
|
+
};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@cosmwasm/ts-codegen",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.31.0",
|
4
4
|
"description": "@cosmwasm/ts-codegen converts your CosmWasm smart contracts into dev-friendly TypeScript classes so you can focus on shipping code.",
|
5
5
|
"author": "Dan Lynch <pyramation@gmail.com>",
|
6
6
|
"homepage": "https://github.com/cosmwasm/ts-codegen",
|
@@ -96,7 +96,7 @@
|
|
96
96
|
"parse-package-name": "1.0.0",
|
97
97
|
"rimraf": "3.0.2",
|
98
98
|
"shelljs": "0.8.5",
|
99
|
-
"wasm-ast-types": "^0.
|
99
|
+
"wasm-ast-types": "^0.24.0"
|
100
100
|
},
|
101
|
-
"gitHead": "
|
101
|
+
"gitHead": "4553eb80cd89969583df6164d1c764ab5b289c63"
|
102
102
|
}
|
package/src/builder/builder.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { RenderOptions, defaultOptions, RenderContext, ContractInfo, MessageComposerOptions} from "wasm-ast-types";
|
1
|
+
import { RenderOptions, defaultOptions, RenderContext, ContractInfo, MessageComposerOptions, BuilderContext} from "wasm-ast-types";
|
2
2
|
|
3
3
|
import { header } from '../utils/header';
|
4
4
|
import { join } from "path";
|
@@ -21,6 +21,9 @@ import { MsgBuilderPlugin } from "../plugins/msg-builder";
|
|
21
21
|
import { MessageComposerPlugin } from "../plugins/message-composer";
|
22
22
|
import { ClientPlugin } from "../plugins/client";
|
23
23
|
import { TypesPlugin } from "../plugins/types";
|
24
|
+
import { ContractsContextProviderPlugin } from "../plugins/provider";
|
25
|
+
import { createHelpers } from "../generators/create-helpers";
|
26
|
+
import { ContractsProviderBundlePlugin } from "../plugins/provider-bundle";
|
24
27
|
|
25
28
|
const defaultOpts: TSBuilderOptions = {
|
26
29
|
bundle: {
|
@@ -44,8 +47,14 @@ export interface BundleOptions {
|
|
44
47
|
bundlePath?: string;
|
45
48
|
};
|
46
49
|
|
50
|
+
export interface UseContractsOptions {
|
51
|
+
enabled?: boolean;
|
52
|
+
filename?: string;
|
53
|
+
};
|
54
|
+
|
47
55
|
export type TSBuilderOptions = {
|
48
56
|
bundle?: BundleOptions;
|
57
|
+
useContracts?: UseContractsOptions;
|
49
58
|
} & RenderOptions;
|
50
59
|
|
51
60
|
export type BuilderFileType = 'type' | 'client' | 'recoil' | 'react-query' | 'message-composer' | 'msg-builder' | 'plugin';
|
@@ -83,6 +92,7 @@ export class TSBuilder {
|
|
83
92
|
outPath: string;
|
84
93
|
options?: TSBuilderOptions;
|
85
94
|
plugins: IBuilderPlugin[] = [];
|
95
|
+
builderContext: BuilderContext = new BuilderContext();
|
86
96
|
|
87
97
|
protected files: BuilderFile[] = [];
|
88
98
|
|
@@ -94,6 +104,7 @@ export class TSBuilder {
|
|
94
104
|
new ReactQueryPlugin(this.options),
|
95
105
|
new RecoilPlugin(this.options),
|
96
106
|
new MsgBuilderPlugin(this.options),
|
107
|
+
new ContractsContextProviderPlugin(this.options),
|
97
108
|
]);
|
98
109
|
}
|
99
110
|
|
@@ -113,6 +124,8 @@ export class TSBuilder {
|
|
113
124
|
if (plugins && plugins.length) {
|
114
125
|
[].push.apply(this.plugins, plugins);
|
115
126
|
}
|
127
|
+
|
128
|
+
this.plugins.forEach(plugin=> plugin.setBuilder(this))
|
116
129
|
}
|
117
130
|
|
118
131
|
async build() {
|
@@ -147,6 +160,28 @@ export class TSBuilder {
|
|
147
160
|
if (this.options.bundle.enabled) {
|
148
161
|
this.bundle();
|
149
162
|
}
|
163
|
+
|
164
|
+
//create useContracts bundle file
|
165
|
+
const contractsProviderBundlePlugin = new ContractsProviderBundlePlugin(this.options);
|
166
|
+
contractsProviderBundlePlugin.setBuilder(this);
|
167
|
+
|
168
|
+
let files = await contractsProviderBundlePlugin.render(
|
169
|
+
"",
|
170
|
+
{
|
171
|
+
schemas: [],
|
172
|
+
},
|
173
|
+
this.outPath
|
174
|
+
);
|
175
|
+
if(files && files.length){
|
176
|
+
[].push.apply(this.files, files);
|
177
|
+
}
|
178
|
+
|
179
|
+
createHelpers({
|
180
|
+
outPath: this.outPath,
|
181
|
+
contracts: this.contracts,
|
182
|
+
options: this.options,
|
183
|
+
plugins: this.plugins,
|
184
|
+
}, this.builderContext);
|
150
185
|
}
|
151
186
|
|
152
187
|
async bundle() {
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import { join, dirname } from "path";
|
2
|
+
import { sync as mkdirp } from "mkdirp";
|
3
|
+
import pkg from "../../package.json";
|
4
|
+
import { writeContentToFile } from "../utils/files";
|
5
|
+
import { TSBuilderInput } from "../builder";
|
6
|
+
import { contractContextBase, contractsContextTSX } from "../helpers";
|
7
|
+
import { BuilderContext } from "wasm-ast-types";
|
8
|
+
|
9
|
+
const version = process.env.NODE_ENV === "test" ? "latest" : pkg.version;
|
10
|
+
const header = `/**
|
11
|
+
* This file and any referenced files were automatically generated by ${pkg.name}@${version}
|
12
|
+
* DO NOT MODIFY BY HAND. Instead, download the latest proto files for your chain
|
13
|
+
* and run the transpile command or yarn proto command to regenerate this bundle.
|
14
|
+
*/
|
15
|
+
\n`;
|
16
|
+
|
17
|
+
const write = (outPath: string, file: string, content: string) => {
|
18
|
+
const outFile = join(outPath, file);
|
19
|
+
mkdirp(dirname(outFile));
|
20
|
+
writeContentToFile(outPath, header + content, outFile);
|
21
|
+
};
|
22
|
+
|
23
|
+
export const createHelpers = (input: TSBuilderInput, builderContext: BuilderContext) => {
|
24
|
+
if (input.options?.useContracts?.enabled && Object.keys(builderContext.providers)?.length) {
|
25
|
+
write(input.outPath, "contractContextBase.ts", contractContextBase);
|
26
|
+
write(input.outPath, "contracts-context.tsx", contractsContextTSX);
|
27
|
+
}
|
28
|
+
};
|
@@ -0,0 +1,92 @@
|
|
1
|
+
export const contractContextBase = `
|
2
|
+
import {
|
3
|
+
CosmWasmClient,
|
4
|
+
SigningCosmWasmClient,
|
5
|
+
} from '@cosmjs/cosmwasm-stargate';
|
6
|
+
|
7
|
+
export interface IContractConstructor {
|
8
|
+
address: string | undefined;
|
9
|
+
cosmWasmClient: CosmWasmClient | undefined;
|
10
|
+
signingCosmWasmClient: SigningCosmWasmClient | undefined;
|
11
|
+
}
|
12
|
+
|
13
|
+
export const NO_SINGING_ERROR_MESSAGE = 'signingCosmWasmClient not connected';
|
14
|
+
|
15
|
+
export const NO_COSMWASW_CLIENT_ERROR_MESSAGE = 'cosmWasmClient not connected';
|
16
|
+
|
17
|
+
export const NO_ADDRESS_ERROR_MESSAGE = "address doesn't exist";
|
18
|
+
|
19
|
+
export const NO_SIGNING_CLIENT_ERROR_MESSAGE =
|
20
|
+
'Signing client is not generated. Please check ts-codegen config';
|
21
|
+
|
22
|
+
export const NO_QUERY_CLIENT_ERROR_MESSAGE =
|
23
|
+
'Query client is not generated. Please check ts-codegen config';
|
24
|
+
|
25
|
+
export const NO_MESSAGE_COMPOSER_ERROR_MESSAGE =
|
26
|
+
'Message composer client is not generated. Please check ts-codegen config';
|
27
|
+
|
28
|
+
/**
|
29
|
+
* a placeholder for non-generated classes
|
30
|
+
*/
|
31
|
+
export interface IEmptyClient {}
|
32
|
+
|
33
|
+
export interface ISigningClientProvider<T> {
|
34
|
+
getSigningClient(contractAddr: string): T;
|
35
|
+
}
|
36
|
+
|
37
|
+
export interface IQueryClientProvider<T> {
|
38
|
+
getQueryClient(contractAddr: string): T;
|
39
|
+
}
|
40
|
+
|
41
|
+
export interface IMessageComposerProvider<T> {
|
42
|
+
getMessageComposer(contractAddr: string): T;
|
43
|
+
}
|
44
|
+
|
45
|
+
export class ContractBase<
|
46
|
+
TSign = IEmptyClient,
|
47
|
+
TQuery = IEmptyClient,
|
48
|
+
TMsgComposer = IEmptyClient
|
49
|
+
> {
|
50
|
+
constructor(
|
51
|
+
protected address: string | undefined,
|
52
|
+
protected cosmWasmClient: CosmWasmClient | undefined,
|
53
|
+
protected signingCosmWasmClient: SigningCosmWasmClient | undefined,
|
54
|
+
private TSign?: new (
|
55
|
+
client: SigningCosmWasmClient,
|
56
|
+
sender: string,
|
57
|
+
contractAddress: string
|
58
|
+
) => TSign,
|
59
|
+
private TQuery?: new (
|
60
|
+
client: CosmWasmClient,
|
61
|
+
contractAddress: string
|
62
|
+
) => TQuery,
|
63
|
+
private TMsgComposer?: new (
|
64
|
+
sender: string,
|
65
|
+
contractAddress: string
|
66
|
+
) => TMsgComposer
|
67
|
+
) {}
|
68
|
+
|
69
|
+
public getSigningClient(contractAddr: string): TSign {
|
70
|
+
if (!this.signingCosmWasmClient) throw new Error(NO_SINGING_ERROR_MESSAGE);
|
71
|
+
if (!this.address) throw new Error(NO_ADDRESS_ERROR_MESSAGE);
|
72
|
+
if (!this.TSign) throw new Error(NO_SIGNING_CLIENT_ERROR_MESSAGE);
|
73
|
+
return new this.TSign(
|
74
|
+
this.signingCosmWasmClient,
|
75
|
+
this.address,
|
76
|
+
contractAddr
|
77
|
+
);
|
78
|
+
}
|
79
|
+
|
80
|
+
public getQueryClient(contractAddr: string): TQuery {
|
81
|
+
if (!this.cosmWasmClient) throw new Error(NO_COSMWASW_CLIENT_ERROR_MESSAGE);
|
82
|
+
if (!this.TQuery) throw new Error(NO_QUERY_CLIENT_ERROR_MESSAGE);
|
83
|
+
return new this.TQuery(this.cosmWasmClient, contractAddr);
|
84
|
+
}
|
85
|
+
|
86
|
+
public getMessageComposer(contractAddr: string): TMsgComposer {
|
87
|
+
if (!this.address) throw new Error(NO_ADDRESS_ERROR_MESSAGE);
|
88
|
+
if (!this.TMsgComposer) throw new Error(NO_MESSAGE_COMPOSER_ERROR_MESSAGE);
|
89
|
+
return new this.TMsgComposer(this.address, contractAddr);
|
90
|
+
}
|
91
|
+
}
|
92
|
+
`;
|
@@ -0,0 +1,73 @@
|
|
1
|
+
export const contractsContextTSX = `
|
2
|
+
import React, { useEffect, useMemo, useRef, useState, useContext } from 'react';
|
3
|
+
import {
|
4
|
+
CosmWasmClient,
|
5
|
+
SigningCosmWasmClient,
|
6
|
+
} from '@cosmjs/cosmwasm-stargate';
|
7
|
+
|
8
|
+
import { IContractsContext, getProviders } from './contractContextProviders';
|
9
|
+
|
10
|
+
interface ContractsConfig {
|
11
|
+
address: string | undefined;
|
12
|
+
getCosmWasmClient: () => Promise<CosmWasmClient>;
|
13
|
+
getSigningCosmWasmClient: () => Promise<SigningCosmWasmClient>;
|
14
|
+
}
|
15
|
+
|
16
|
+
const ContractsContext = React.createContext<IContractsContext | null>(null);
|
17
|
+
|
18
|
+
export const ContractsProvider = ({
|
19
|
+
children,
|
20
|
+
contractsConfig,
|
21
|
+
}: {
|
22
|
+
children: React.ReactNode;
|
23
|
+
contractsConfig: ContractsConfig;
|
24
|
+
}) => {
|
25
|
+
const [cosmWasmClient, setCosmWasmClient] = useState<CosmWasmClient>();
|
26
|
+
const [signingCosmWasmClient, setSigningCosmWasmClient] =
|
27
|
+
useState<SigningCosmWasmClient>();
|
28
|
+
|
29
|
+
const { address, getCosmWasmClient, getSigningCosmWasmClient } =
|
30
|
+
contractsConfig;
|
31
|
+
|
32
|
+
const prevAddressRef = useRef<string | undefined>(address);
|
33
|
+
|
34
|
+
const contracts: IContractsContext = useMemo(() => {
|
35
|
+
return getProviders(address, cosmWasmClient, signingCosmWasmClient);
|
36
|
+
}, [address, cosmWasmClient, signingCosmWasmClient]);
|
37
|
+
|
38
|
+
useEffect(() => {
|
39
|
+
const connectSigningCwClient = async () => {
|
40
|
+
if (address && prevAddressRef.current !== address) {
|
41
|
+
const signingCosmWasmClient = await getSigningCosmWasmClient();
|
42
|
+
setSigningCosmWasmClient(signingCosmWasmClient);
|
43
|
+
} else if (!address) {
|
44
|
+
setSigningCosmWasmClient(undefined);
|
45
|
+
}
|
46
|
+
prevAddressRef.current = address;
|
47
|
+
};
|
48
|
+
connectSigningCwClient();
|
49
|
+
}, [address, getSigningCosmWasmClient]);
|
50
|
+
|
51
|
+
useEffect(() => {
|
52
|
+
const connectCosmWasmClient = async () => {
|
53
|
+
const cosmWasmClient = await getCosmWasmClient();
|
54
|
+
setCosmWasmClient(cosmWasmClient);
|
55
|
+
};
|
56
|
+
connectCosmWasmClient();
|
57
|
+
}, [getCosmWasmClient]);
|
58
|
+
|
59
|
+
return (
|
60
|
+
<ContractsContext.Provider value={contracts}>
|
61
|
+
{children}
|
62
|
+
</ContractsContext.Provider>
|
63
|
+
);
|
64
|
+
};
|
65
|
+
|
66
|
+
export const useContracts = () => {
|
67
|
+
const contracts = useContext(ContractsContext);
|
68
|
+
if (contracts === null) {
|
69
|
+
throw new Error('useContracts must be used within a ContractsProvider');
|
70
|
+
}
|
71
|
+
return contracts;
|
72
|
+
};
|
73
|
+
`;
|