@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.
Files changed (70) hide show
  1. package/README.md +147 -118
  2. package/builder/builder.js +26 -24
  3. package/bundler/bundler.js +27 -17
  4. package/cli.js +2 -2
  5. package/commands/create-boilerplate.js +29 -18
  6. package/commands/generate.js +26 -35
  7. package/commands/install.js +22 -18
  8. package/esm/builder/builder.js +10 -18
  9. package/esm/bundler/bundler.js +10 -10
  10. package/esm/cli.js +2 -2
  11. package/esm/commands/create-boilerplate.js +11 -10
  12. package/esm/commands/generate.js +26 -35
  13. package/esm/commands/install.js +23 -19
  14. package/esm/file.js +4 -3
  15. package/esm/helpers/baseClient.js +184 -0
  16. package/esm/helpers/contractContextBase.js +15 -13
  17. package/esm/helpers/contractContextBaseShortHandCtor.js +11 -9
  18. package/esm/helpers/contractsContextTSX.js +7 -7
  19. package/esm/helpers/create-helpers.js +3 -1
  20. package/esm/helpers/index.js +1 -0
  21. package/esm/plugins/client.js +1 -1
  22. package/esm/plugins/message-builder.js +4 -4
  23. package/esm/plugins/message-composer.js +1 -1
  24. package/esm/plugins/plugin-base.js +3 -2
  25. package/esm/plugins/provider-bundle.js +9 -5
  26. package/esm/plugins/provider.js +1 -1
  27. package/esm/plugins/react-query.js +8 -7
  28. package/esm/plugins/recoil.js +4 -4
  29. package/esm/plugins/types.js +3 -3
  30. package/esm/ts-codegen.js +2 -1
  31. package/esm/utils/clean.js +1 -1
  32. package/esm/utils/cleanse.js +9 -5
  33. package/esm/utils/package.js +1 -1
  34. package/esm/utils/parse.js +5 -7
  35. package/esm/utils/prompt.js +2 -2
  36. package/esm/utils/schemas.js +32 -19
  37. package/esm/utils/unused.js +3 -4
  38. package/file.js +7 -3
  39. package/helpers/baseClient.d.ts +1 -0
  40. package/helpers/baseClient.js +187 -0
  41. package/helpers/contractContextBase.d.ts +1 -1
  42. package/helpers/contractContextBase.js +15 -13
  43. package/helpers/contractContextBaseShortHandCtor.d.ts +1 -1
  44. package/helpers/contractContextBaseShortHandCtor.js +11 -9
  45. package/helpers/contractsContextTSX.d.ts +1 -1
  46. package/helpers/contractsContextTSX.js +7 -7
  47. package/helpers/create-helpers.js +2 -0
  48. package/helpers/index.d.ts +1 -0
  49. package/helpers/index.js +1 -0
  50. package/package.json +20 -15
  51. package/plugins/client.js +18 -8
  52. package/plugins/message-builder.js +20 -10
  53. package/plugins/message-composer.js +18 -8
  54. package/plugins/plugin-base.js +19 -8
  55. package/plugins/provider-bundle.js +25 -11
  56. package/plugins/provider.js +17 -7
  57. package/plugins/react-query.js +24 -13
  58. package/plugins/recoil.js +20 -10
  59. package/plugins/types.js +19 -9
  60. package/ts-codegen.js +5 -1
  61. package/utils/clean.js +1 -1
  62. package/utils/cleanse.d.ts +1 -0
  63. package/utils/cleanse.js +12 -7
  64. package/utils/files.js +17 -7
  65. package/utils/package.js +2 -3
  66. package/utils/parse.js +5 -7
  67. package/utils/prompt.js +2 -2
  68. package/utils/schemas.d.ts +5 -1
  69. package/utils/schemas.js +34 -20
  70. 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([...imports, ...result.body])).code;
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
- CosmWasmClient: '@cosmjs/cosmwasm-stargate',
13
- SigningCosmWasmClient: '@cosmjs/cosmwasm-stargate',
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('CosmWasmClient');
33
- context.addUtil('SigningCosmWasmClient');
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');
@@ -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
- QueryMsg && clientImports.push(QueryClient);
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 (typeHash.hasOwnProperty('Coin')) {
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
  }
@@ -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 (typeHash.hasOwnProperty('Coin')) {
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
  }
@@ -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
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
+ import minimist from 'minimist';
2
3
  import { cli } from './cli';
3
- let argv = require('minimist')(process.argv.slice(2));
4
+ let argv = minimist(process.argv.slice(2));
4
5
  (async () => {
5
6
  await cli(argv);
6
7
  })();
@@ -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 (obj.hasOwnProperty(attr)) {
24
+ if (Object.prototype.hasOwnProperty.call(obj, attr)) {
25
25
  switch (attr) {
26
26
  case 'leadingComments':
27
27
  case 'trailingComments':
@@ -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 (/_[a-z]+_[A-Z]/.test(str)) {
8
- const m = str.match(/(_[a-z]+_)[A-Z]/);
9
- str = str.replace(m[1], pascal(m[1]));
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 (obj.hasOwnProperty(attr)) {
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]);
@@ -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
@@ -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
  });
@@ -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 {
@@ -1,37 +1,50 @@
1
1
  import { compile } from '@pyramation/json-schema-to-typescript';
2
2
  import { readFileSync } from 'fs';
3
- import { sync as glob } from 'glob';
3
+ import { globSync as glob } from 'glob';
4
4
  import { cleanse } from './cleanse';
5
5
  import { parser } from './parse';
6
- ;
7
- export const readSchemas = async ({ schemaDir, clean = true }) => {
8
- const fn = clean ? cleanse : (schema) => schema;
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.match(/\/raw\//));
11
- const schemas = files
12
- .map(file => JSON.parse(readFileSync(file, 'utf-8')));
13
- if (schemas.length > 1) {
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 (schemas.length === 0) {
30
+ if (files.length === 0) {
21
31
  throw new Error('Error [too few files]: requires one schema file per contract');
22
32
  }
23
- if (schemas.length !== 1) {
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 = schemas[0];
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(schemas)
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) => {
@@ -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 = require('minimist')(process.argv.slice(2));
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 CosmWasmClient,\n SigningCosmWasmClient,\n} from '@cosmjs/cosmwasm-stargate';\n\nexport interface IContractConstructor {\n address: string | undefined;\n cosmWasmClient: CosmWasmClient | undefined;\n signingCosmWasmClient: SigningCosmWasmClient | 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: CosmWasmClient | undefined;\n signingCosmWasmClient: SigningCosmWasmClient | undefined;\n TSign?: new (\n client: SigningCosmWasmClient,\n sender: string,\n contractAddress: string\n ) => TSign;\n TQuery?: new (\n client: CosmWasmClient,\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: CosmWasmClient | undefined,\n signingCosmWasmClient: SigningCosmWasmClient | undefined,\n TSign?: new (\n client: SigningCosmWasmClient,\n sender: string,\n contractAddress: string\n ) => TSign,\n TQuery?: new (\n client: CosmWasmClient,\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";
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";