@blocklet/meta 1.8.33 → 1.8.35

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 (58) hide show
  1. package/lib/channel.d.ts +27 -0
  2. package/lib/channel.js +32 -32
  3. package/lib/constants.d.ts +2 -0
  4. package/lib/constants.js +6 -3
  5. package/lib/did.d.ts +10 -0
  6. package/lib/did.js +18 -24
  7. package/lib/engine.d.ts +7 -0
  8. package/lib/engine.js +21 -25
  9. package/lib/entry.d.ts +3 -0
  10. package/lib/entry.js +51 -64
  11. package/lib/extension.d.ts +10 -0
  12. package/lib/extension.js +78 -77
  13. package/lib/file.d.ts +21 -0
  14. package/lib/file.js +44 -39
  15. package/lib/fix.d.ts +33 -0
  16. package/lib/fix.js +218 -208
  17. package/lib/get-component-process-id.d.ts +5 -0
  18. package/lib/get-component-process-id.js +13 -14
  19. package/lib/has-reserved-key.d.ts +3 -0
  20. package/lib/has-reserved-key.js +10 -9
  21. package/lib/index.d.ts +82 -0
  22. package/lib/index.js +51 -34
  23. package/lib/info.d.ts +13 -0
  24. package/lib/info.js +58 -60
  25. package/lib/name.d.ts +5 -0
  26. package/lib/name.js +14 -7
  27. package/lib/nft-templates.d.ts +86 -0
  28. package/lib/nft-templates.js +47 -42
  29. package/lib/parse-navigation.d.ts +3 -0
  30. package/lib/parse-navigation.js +168 -209
  31. package/lib/parse.d.ts +22 -0
  32. package/lib/parse.js +71 -82
  33. package/lib/payment/index.d.ts +254 -0
  34. package/lib/payment/index.js +13 -6
  35. package/lib/payment/v1.d.ts +185 -0
  36. package/lib/payment/v1.js +80 -81
  37. package/lib/payment/v2.d.ts +242 -0
  38. package/lib/payment/v2.js +453 -531
  39. package/lib/schema.d.ts +50 -0
  40. package/lib/schema.js +405 -397
  41. package/lib/service-configs/auth.json +61 -61
  42. package/lib/service.d.ts +26 -0
  43. package/lib/service.js +69 -85
  44. package/lib/types/index.d.ts +1 -0
  45. package/lib/types/index.js +18 -0
  46. package/lib/types/schema.d.ts +241 -0
  47. package/lib/types/schema.js +3 -0
  48. package/lib/util-meta.d.ts +42 -0
  49. package/lib/util-meta.js +138 -158
  50. package/lib/util.d.ts +185 -0
  51. package/lib/util.js +359 -414
  52. package/lib/validate.d.ts +10 -0
  53. package/lib/validate.js +28 -34
  54. package/lib/verify-multi-sig.d.ts +3 -0
  55. package/lib/verify-multi-sig.js +94 -101
  56. package/lib/wallet.d.ts +9 -0
  57. package/lib/wallet.js +17 -27
  58. package/package.json +41 -18
package/lib/payment/v2.js CHANGED
@@ -1,48 +1,78 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ var __importDefault = (this && this.__importDefault) || function (mod) {
35
+ return (mod && mod.__esModule) ? mod : { "default": mod };
36
+ };
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.version = exports.checkFreeBlocklet = exports.verifyNftFactory = exports.verifyPaymentIntegrity = exports.createNftFactoryItx = exports._test = void 0;
1
39
  /* eslint-disable no-await-in-loop */
2
- const crypto = require('crypto');
3
- const debug = require('debug')('@blocklet/meta:payment');
4
- const joinURL = require('url-join');
5
- const stableStringify = require('json-stable-stringify');
6
- const get = require('lodash/get');
7
- const pick = require('lodash/pick');
8
- const cloneDeep = require('lodash/cloneDeep');
9
- const axios = require('@abtnode/util/lib/axios');
10
- const { BN, fromTokenToUnit, fromUnitToToken } = require('@ocap/util');
11
- const { isValidFactory } = require('@ocap/asset');
12
- const { fromPublicKey } = require('@ocap/wallet');
13
- const { toFactoryAddress } = require('@arcblock/did-util');
14
- const { toTypeInfo } = require('@arcblock/did');
15
- const { BLOCKLET_STORE_META_PATH } = require('@abtnode/constant');
16
- const { getBlockletPurchaseTemplate } = require('../nft-templates');
17
- const { validateMeta } = require('../validate');
18
- const { isComponentBlocklet, isFreeComponent, isFreeBlocklet } = require('../util');
19
- const { getBlockletMetaFromUrls, getSourceUrlsFromConfig } = require('../util-meta');
20
-
40
+ const crypto_1 = __importDefault(require("crypto"));
41
+ const debug_1 = __importDefault(require("debug"));
42
+ const url_join_1 = __importDefault(require("url-join"));
43
+ const json_stable_stringify_1 = __importDefault(require("json-stable-stringify"));
44
+ const get_1 = __importDefault(require("lodash/get"));
45
+ const pick_1 = __importDefault(require("lodash/pick"));
46
+ const cloneDeep_1 = __importDefault(require("lodash/cloneDeep"));
47
+ const axios_1 = __importDefault(require("@abtnode/util/lib/axios"));
48
+ const util_1 = require("@ocap/util");
49
+ const asset_1 = require("@ocap/asset");
50
+ const wallet_1 = require("@ocap/wallet");
51
+ const did_util_1 = require("@arcblock/did-util");
52
+ const did = __importStar(require("@arcblock/did"));
53
+ const constant_1 = __importDefault(require("@abtnode/constant"));
54
+ const nft_templates_1 = require("../nft-templates");
55
+ const validate_1 = require("../validate");
56
+ const util_2 = require("../util");
57
+ const util_meta_1 = require("../util-meta");
58
+ const debug = (0, debug_1.default)('@blocklet/meta:payment');
59
+ const { toTypeInfo } = did;
60
+ const { BLOCKLET_STORE_META_PATH } = constant_1.default;
21
61
  const VERSION = '2.0.0';
22
-
23
- const ZeroBN = new BN(0);
62
+ exports.version = VERSION;
63
+ const ZeroBN = new util_1.BN(0);
24
64
  const defaultDecimals = 1e6; // we only support 6 decimals on share ratio
25
- const defaultDecimalsBN = new BN(defaultDecimals);
26
-
65
+ const defaultDecimalsBN = new util_1.BN(defaultDecimals);
27
66
  const getComponentConfig = (meta) => meta.components || meta.children;
28
-
29
- const safeMul = (a, b) =>
30
- Number(
31
- fromUnitToToken(
32
- fromTokenToUnit(a)
33
- .mul(new BN(b * defaultDecimals))
34
- .div(defaultDecimalsBN)
35
- )
36
- );
37
-
38
- const md5 = (str) => crypto.createHash('md5').update(str).digest('hex');
39
-
40
- const getStoreInfo = async (url) => {
41
- const storeMetaUrl = joinURL(new URL(url).origin, BLOCKLET_STORE_META_PATH);
42
- const { data: info } = await axios.get(storeMetaUrl, { timeout: 8000 });
43
- return info;
44
- };
45
-
67
+ const safeMul = (a, b) => Number((0, util_1.fromUnitToToken)((0, util_1.fromTokenToUnit)(a)
68
+ .mul(new util_1.BN(b * defaultDecimals))
69
+ .div(defaultDecimalsBN)));
70
+ const md5 = (str) => crypto_1.default.createHash('md5').update(str).digest('hex');
71
+ const getStoreInfo = (url) => __awaiter(void 0, void 0, void 0, function* () {
72
+ const storeMetaUrl = (0, url_join_1.default)(new URL(url).origin, BLOCKLET_STORE_META_PATH);
73
+ const { data: info } = yield axios_1.default.get(storeMetaUrl, { timeout: 8000 });
74
+ return info;
75
+ });
46
76
  /**
47
77
  * @typedef {{
48
78
  * meta: Object
@@ -51,9 +81,9 @@ const getStoreInfo = async (url) => {
51
81
  * children: Array<Component>
52
82
  * }} Component
53
83
  *
54
- * @param {BlockletMeta} inputMeta
84
+ * @param {TBlockletMeta} inputMeta
55
85
  * @param {{
56
- * ancestors: Array<{BlockletMeta}>
86
+ * ancestors: Array<{TBlockletMeta}>
57
87
  * bundles: {
58
88
  * <bundleName>: <storeId>
59
89
  * }
@@ -61,164 +91,135 @@ const getStoreInfo = async (url) => {
61
91
  *
62
92
  * @returns {Array<Component>}
63
93
  */
64
- const _getComponents = async (inputMeta, context = {}) => {
65
- // FIXME 是否需要验证: 在同一个链上; 重复的 component
66
- const { ancestors = [], bundles = {} } = context;
67
-
68
- // check ancestor length
69
- if (ancestors.length > 40) {
70
- throw new Error('The depth of component should not exceed 40');
71
- }
72
-
73
- const configs = getComponentConfig(inputMeta);
74
-
75
- if (!configs || !configs.length) {
76
- return [];
77
- }
78
-
79
- const children = [];
80
-
81
- for (const config of configs) {
82
- // get component meta
83
- const urls = getSourceUrlsFromConfig(config);
84
- let meta;
85
- let url;
86
- try {
87
- const res = await getBlockletMetaFromUrls(urls, {
88
- returnUrl: true,
89
- validateFn: (m) => validateMeta(m),
90
- ensureTarball: false,
91
- });
92
- meta = res.meta;
93
- url = res.url;
94
- } catch (err) {
95
- throw new Error(`Failed get component meta: ${config.title || config.name}: ${err.message}`);
96
- }
97
-
98
- // check is component
99
- if (!isComponentBlocklet(meta)) {
100
- throw new Error(`The blocklet cannot be a component: ${meta.title}`);
94
+ const innerGetComponents = (inputMeta, context = {}) => __awaiter(void 0, void 0, void 0, function* () {
95
+ // FIXME 是否需要验证: 在同一个链上; 重复的 component
96
+ const { ancestors = [], bundles = {} } = context;
97
+ // check ancestor length
98
+ if (ancestors.length > 40) {
99
+ throw new Error('The depth of component should not exceed 40');
101
100
  }
102
-
103
- // check circular dependencies
104
- if (ancestors.map((x) => x.meta?.did).indexOf(meta.did) > -1) {
105
- throw new Error('Blocklet components have circular dependencies');
101
+ const configs = getComponentConfig(inputMeta);
102
+ if (!configs || !configs.length) {
103
+ return [];
106
104
  }
107
-
108
- // generate child
109
- const child = {
110
- meta,
111
- };
112
-
113
- // child store info
114
- if (config.source.store) {
115
- const storeInfo = await getStoreInfo(url);
116
-
117
- // check uniq bundle did in different stores
118
- if (!bundles[child.meta.did]) {
119
- bundles[child.meta.did] = storeInfo.id;
120
- } else if (bundles[child.meta.did] !== storeInfo.id) {
121
- throw new Error('Bundles with the same did cannot in different stores');
122
- }
123
-
124
- child.storeInfo = storeInfo;
125
- child.storeUrl = new URL(url).origin;
105
+ const children = [];
106
+ for (const config of configs) {
107
+ // get component meta
108
+ const urls = (0, util_meta_1.getSourceUrlsFromConfig)(config);
109
+ let meta;
110
+ let url;
111
+ try {
112
+ const res = yield (0, util_meta_1.getBlockletMetaFromUrls)(urls, {
113
+ returnUrl: true,
114
+ validateFn: (m) => (0, validate_1.validateMeta)(m),
115
+ ensureTarball: false,
116
+ });
117
+ meta = res.meta;
118
+ url = res.url;
119
+ }
120
+ catch (err) {
121
+ throw new Error(`Failed get component meta: ${config.title || config.name}: ${err.message}`);
122
+ }
123
+ // check is component
124
+ if (!(0, util_2.isComponentBlocklet)(meta)) {
125
+ throw new Error(`The blocklet cannot be a component: ${meta.title}`);
126
+ }
127
+ // check circular dependencies
128
+ if (ancestors.map((x) => { var _a; return (_a = x.meta) === null || _a === void 0 ? void 0 : _a.did; }).indexOf(meta.did) > -1) {
129
+ throw new Error('Blocklet components have circular dependencies');
130
+ }
131
+ // generate child
132
+ const child = {
133
+ meta,
134
+ };
135
+ // child store info
136
+ if (config.source.store) {
137
+ const storeInfo = yield getStoreInfo(url);
138
+ // check uniq bundle did in different stores
139
+ if (!bundles[child.meta.did]) {
140
+ bundles[child.meta.did] = storeInfo.id;
141
+ }
142
+ else if (bundles[child.meta.did] !== storeInfo.id) {
143
+ throw new Error('Bundles with the same did cannot in different stores');
144
+ }
145
+ child.storeInfo = storeInfo;
146
+ child.storeUrl = new URL(url).origin;
147
+ }
148
+ // child children
149
+ child.children = yield innerGetComponents(meta, {
150
+ ancestors: [...ancestors, { meta }],
151
+ bundles,
152
+ });
153
+ children.push(child);
126
154
  }
127
-
128
- // child children
129
- child.children = await _getComponents(meta, {
130
- ancestors: [...ancestors, { meta }],
131
- bundles,
132
- });
133
-
134
- children.push(child);
135
- }
136
-
137
- return children;
138
- };
139
-
155
+ return children;
156
+ });
140
157
  /**
141
- * @typedef {{
142
- * id: string
143
- * pk: string
144
- * url: string
145
- * components: Array<{did: string, version: string}>
146
- * }} Store
147
158
  * @param {Array<Component>} components
148
159
  * @param {Array<Store>} _stores
149
160
  * @returns {Array<Store>}
150
161
  */
151
- const _getStores = (components, _stores = []) => {
152
- for (const { meta, storeInfo, storeUrl, children } of components) {
153
- if (storeInfo && (!isFreeBlocklet(meta) || !isFreeComponent(meta))) {
154
- const store = _stores.find((x) => x.id === storeInfo.id);
155
- if (!store) {
156
- _stores.push({
157
- id: storeInfo.id,
158
- pk: storeInfo.pk,
159
- url: storeUrl,
160
- components: [{ did: meta.did, version: meta.version }],
161
- });
162
- } else if (!store.components.some((x) => x.did === meta.did && x.version === meta.version)) {
163
- store.components.push({ did: meta.did, version: meta.version });
164
- }
162
+ const getStores = (components, _stores = []) => {
163
+ for (const { meta, storeInfo, storeUrl, children } of components) {
164
+ if (storeInfo && (!(0, util_2.isFreeBlocklet)(meta) || !(0, util_2.isFreeComponent)(meta))) {
165
+ const store = _stores.find((x) => x.id === storeInfo.id);
166
+ if (!store) {
167
+ _stores.push({
168
+ id: storeInfo.id,
169
+ pk: storeInfo.pk,
170
+ url: storeUrl,
171
+ components: [{ did: meta.did, version: meta.version }],
172
+ });
173
+ }
174
+ else if (!store.components.some((x) => x.did === meta.did && x.version === meta.version)) {
175
+ store.components.push({ did: meta.did, version: meta.version });
176
+ }
177
+ }
178
+ if (children && children.length > 0) {
179
+ getStores(children, _stores);
180
+ }
165
181
  }
166
-
167
- if (children && children.length > 0) {
168
- _getStores(children, _stores);
169
- }
170
- }
171
-
172
- return _stores;
173
- };
174
-
175
- const getComponents = async (inputMeta) => {
176
- const components = await _getComponents(inputMeta);
177
- const stores = await _getStores(components);
178
- return { components, stores };
182
+ return _stores;
179
183
  };
180
-
181
- const getPriceTokens = async (meta, ocapClient) => {
182
- const priceTokens = cloneDeep(get(meta, 'payment.price', []));
183
- for (const token of priceTokens) {
184
- // eslint-disable-next-line no-await-in-loop
185
- const { state } = await ocapClient.getTokenState({ address: token.address });
186
- if (!state) {
187
- throw new Error(`Token specified in blocklet meta was not found on chain: ${token.address}`);
184
+ const getComponents = (inputMeta) => __awaiter(void 0, void 0, void 0, function* () {
185
+ const components = yield innerGetComponents(inputMeta);
186
+ const stores = yield getStores(components);
187
+ return { components, stores };
188
+ });
189
+ const getPriceTokens = (meta, ocapClient) => __awaiter(void 0, void 0, void 0, function* () {
190
+ const priceTokens = (0, cloneDeep_1.default)((0, get_1.default)(meta, 'payment.price', []));
191
+ for (const token of priceTokens) {
192
+ // eslint-disable-next-line no-await-in-loop
193
+ const { state } = yield ocapClient.getTokenState({ address: token.address });
194
+ if (!state) {
195
+ throw new Error(`Token specified in blocklet meta was not found on chain: ${token.address}`);
196
+ }
197
+ token.decimal = state.decimal;
188
198
  }
189
-
190
- token.decimal = state.decimal;
191
- }
192
- return priceTokens;
193
- };
194
-
199
+ return priceTokens;
200
+ });
195
201
  const getChildShare = (childMeta, parentPrice) => {
196
- if (!childMeta?.payment?.componentPrice) {
197
- return 0;
198
- }
199
-
200
- const priceList = childMeta.payment.componentPrice;
201
-
202
- let price = 0;
203
-
204
- for (const { type, value, parentPriceRange } of priceList) {
205
- const isDefault = !parentPriceRange || !parentPriceRange.length;
206
- const skip = isDefault && price !== 0;
207
- const inRange =
208
- isDefault || (parentPriceRange && parentPrice >= parentPriceRange[0] && parentPrice <= parentPriceRange[1]);
209
-
210
- if (!skip && inRange) {
211
- if (type === 'fixed') {
212
- price = value;
213
- } else if (type === 'percentage') {
214
- price = safeMul(parentPrice, value);
215
- }
202
+ var _a;
203
+ if (!((_a = childMeta === null || childMeta === void 0 ? void 0 : childMeta.payment) === null || _a === void 0 ? void 0 : _a.componentPrice)) {
204
+ return 0;
205
+ }
206
+ const priceList = childMeta.payment.componentPrice;
207
+ let price = 0;
208
+ for (const { type, value, parentPriceRange } of priceList) {
209
+ const isDefault = !parentPriceRange || !parentPriceRange.length;
210
+ const skip = isDefault && price !== 0;
211
+ const inRange = isDefault || (parentPriceRange && parentPrice >= parentPriceRange[0] && parentPrice <= parentPriceRange[1]);
212
+ if (!skip && inRange) {
213
+ if (type === 'fixed') {
214
+ price = value;
215
+ }
216
+ else if (type === 'percentage') {
217
+ price = safeMul(parentPrice, value);
218
+ }
219
+ }
216
220
  }
217
- }
218
-
219
- return price;
221
+ return price;
220
222
  };
221
-
222
223
  /**
223
224
  * @returns {Array<{
224
225
  * tokenAddress: string
@@ -226,99 +227,69 @@ const getChildShare = (childMeta, parentPrice) => {
226
227
  * amount: BN
227
228
  * }>}
228
229
  */
229
- const getTokenTransfers = ({ priceToken, shares = [], components = [] }) => {
230
- // check share
231
- const shareSum = shares.reduce((sum, x) => sum + x.value, 0);
232
- if (shareSum > 1) {
233
- throw new Error('payment.share invalid: share sum should not be greater than 1');
234
- }
235
-
236
- const { value: price } = priceToken;
237
-
238
- let parentShareBN = fromTokenToUnit(price, priceToken.decimal);
239
-
240
- const contracts = [];
241
-
242
- for (const child of components) {
243
- if (!isFreeComponent(child.meta)) {
244
- // // check same token
245
- const [token] = child.meta.payment.price || [];
246
- if (token && token.address !== priceToken.address) {
247
- throw new Error(
248
- `The token address of the component "${
249
- child.meta.title || child.meta.name
250
- }" is inconsistent with the blocklet. Component: ${priceToken.address}, Composite Blocklet: ${token.address}`
251
- );
252
- }
253
-
254
- const childShare = getChildShare(child.meta, price);
255
-
256
- parentShareBN = parentShareBN.sub(fromTokenToUnit(childShare, priceToken.decimal));
257
-
258
- const componentContracts = getTokenTransfers({
259
- priceToken: { ...priceToken, value: childShare },
260
- shares: child.meta.payment.share,
261
- components: child.children || [],
262
- });
263
-
264
- contracts.push(...componentContracts);
230
+ const getTokenTransfers = ({ priceToken, shares = [], components = [], }) => {
231
+ // check share
232
+ const shareSum = shares.reduce((sum, x) => sum + x.value, 0);
233
+ if (shareSum > 1) {
234
+ throw new Error('payment.share invalid: share sum should not be greater than 1');
265
235
  }
266
- }
267
-
268
- if (parentShareBN.lt(ZeroBN)) {
269
- const needPrice = fromUnitToToken(fromTokenToUnit(price, priceToken.decimal).sub(parentShareBN));
270
- throw new Error(
271
- `Price for composite blocklet must be greater than ${needPrice} because paid components are included.`
272
- );
273
- }
274
-
275
- shares.forEach(({ name, address: accountAddress, value: ratio }) => {
276
- contracts.push({
277
- tokenAddress: priceToken.address,
278
- accountName: name,
279
- accountAddress,
280
- amount: parentShareBN.mul(new BN(ratio * defaultDecimals)).div(defaultDecimalsBN),
281
- });
282
- });
283
-
284
- const mergedContracts = [];
285
-
286
- contracts.forEach((x) => {
287
- const index = mergedContracts.findIndex(
288
- (y) => y.tokenAddress === x.tokenAddress && y.accountAddress === x.accountAddress
289
- );
290
- if (index > -1) {
291
- mergedContracts[index].amount = mergedContracts[index].amount.add(x.amount);
292
- } else {
293
- mergedContracts.push(x);
236
+ const { value: price } = priceToken;
237
+ let parentShareBN = (0, util_1.fromTokenToUnit)(price, priceToken.decimal);
238
+ const contracts = [];
239
+ for (const child of components) {
240
+ if (!(0, util_2.isFreeComponent)(child.meta)) {
241
+ // // check same token
242
+ const [token] = child.meta.payment.price || [];
243
+ if (token && token.address !== priceToken.address) {
244
+ throw new Error(`The token address of the component "${child.meta.title || child.meta.name}" is inconsistent with the blocklet. Component: ${priceToken.address}, Composite Blocklet: ${token.address}`);
245
+ }
246
+ const childShare = getChildShare(child.meta, price);
247
+ parentShareBN = parentShareBN.sub((0, util_1.fromTokenToUnit)(childShare, priceToken.decimal));
248
+ const componentContracts = getTokenTransfers({
249
+ priceToken: Object.assign(Object.assign({}, priceToken), { value: childShare }),
250
+ shares: child.meta.payment.share,
251
+ components: child.children || [],
252
+ });
253
+ contracts.push(...componentContracts);
254
+ }
294
255
  }
295
- });
296
-
297
- return mergedContracts;
298
- };
299
-
300
- const getContract = async ({ meta, priceTokens, components }) => {
301
- const shares = meta.payment.share || [];
302
-
303
- const [priceToken] = priceTokens;
304
-
305
- const contracts = getTokenTransfers({ priceToken, shares, components });
306
-
307
- const code = contracts
308
- .map((x) => `transferToken('${x.tokenAddress}','${x.accountAddress}','${x.amount.toString()}')`)
309
- .join(';\n');
310
-
311
- const shareList = contracts.map((x) => ({
312
- ...x,
313
- amount: fromUnitToToken(x.amount, priceToken.decimal),
314
- }));
315
-
316
- return {
317
- code,
318
- shares: shareList,
319
- };
256
+ if (parentShareBN.lt(ZeroBN)) {
257
+ const needPrice = (0, util_1.fromUnitToToken)((0, util_1.fromTokenToUnit)(price, priceToken.decimal).sub(parentShareBN));
258
+ throw new Error(`Price for composite blocklet must be greater than ${needPrice} because paid components are included.`);
259
+ }
260
+ shares.forEach(({ name, address: accountAddress, value: ratio }) => {
261
+ contracts.push({
262
+ tokenAddress: priceToken.address,
263
+ accountName: name,
264
+ accountAddress,
265
+ amount: parentShareBN.mul(new util_1.BN(ratio * defaultDecimals)).div(defaultDecimalsBN),
266
+ });
267
+ });
268
+ const mergedContracts = [];
269
+ contracts.forEach((x) => {
270
+ const index = mergedContracts.findIndex((y) => y.tokenAddress === x.tokenAddress && y.accountAddress === x.accountAddress);
271
+ if (index > -1) {
272
+ mergedContracts[index].amount = mergedContracts[index].amount.add(x.amount);
273
+ }
274
+ else {
275
+ mergedContracts.push(x);
276
+ }
277
+ });
278
+ return mergedContracts;
320
279
  };
321
-
280
+ const getContract = ({ meta, priceTokens, components, }) => __awaiter(void 0, void 0, void 0, function* () {
281
+ const shares = meta.payment.share || [];
282
+ const [priceToken] = priceTokens;
283
+ const contracts = getTokenTransfers({ priceToken, shares, components });
284
+ const code = contracts
285
+ .map((x) => `transferToken('${x.tokenAddress}','${x.accountAddress}','${x.amount.toString()}')`)
286
+ .join(';\n');
287
+ const shareList = contracts.map((x) => (Object.assign(Object.assign({}, x), { amount: (0, util_1.fromUnitToToken)(x.amount, priceToken.decimal) })));
288
+ return {
289
+ code,
290
+ shares: shareList,
291
+ };
292
+ });
322
293
  /**
323
294
  * we need to ensure that blocklet purchase factory does not change across changes
324
295
  *
@@ -345,151 +316,130 @@ const getContract = async ({ meta, priceTokens, components }) => {
345
316
  * }} Itx
346
317
  * @returns {Itx}
347
318
  */
348
- const _createNftFactoryItx = ({ meta, issuers, serviceUrl, storeSignatures, factoryInput, contract }) => {
349
- const factoryOutput = getBlockletPurchaseTemplate(serviceUrl);
350
- const itx = {
351
- name: meta.title || meta.name,
352
- description: `Purchase NFT factory for blocklet ${meta.name}`,
353
- settlement: 'instant',
354
- limit: 0,
355
- trustedIssuers: issuers,
356
- input: factoryInput,
357
- output: {
358
- issuer: '{{ctx.issuer.id}}',
359
- parent: '{{ctx.factory}}',
360
- moniker: 'BlockletPurchaseNFT',
361
- readonly: true,
362
- transferrable: false,
363
- data: factoryOutput,
364
- },
365
- data: {
366
- type: 'json',
367
- value: {
368
- did: meta.did,
369
- url: joinURL(serviceUrl, `/blocklet/${meta.did}`),
370
- name: meta.name,
371
- version: meta.version,
372
- payment: {
373
- version: VERSION,
319
+ const innerCreateNftFactoryItx = ({ meta, issuers, serviceUrl, storeSignatures, factoryInput, contract, }) => {
320
+ const factoryOutput = (0, nft_templates_1.getBlockletPurchaseTemplate)(serviceUrl);
321
+ const itx = {
322
+ name: meta.title || meta.name,
323
+ description: `Purchase NFT factory for blocklet ${meta.name}`,
324
+ settlement: 'instant',
325
+ limit: 0,
326
+ trustedIssuers: issuers,
327
+ input: factoryInput,
328
+ output: {
329
+ issuer: '{{ctx.issuer.id}}',
330
+ parent: '{{ctx.factory}}',
331
+ moniker: 'BlockletPurchaseNFT',
332
+ readonly: true,
333
+ transferrable: false,
334
+ data: factoryOutput,
335
+ },
336
+ data: {
337
+ type: 'json',
338
+ value: {
339
+ did: meta.did,
340
+ url: (0, url_join_1.default)(serviceUrl, `/blocklet/${meta.did}`),
341
+ name: meta.name,
342
+ version: meta.version,
343
+ payment: {
344
+ version: VERSION,
345
+ },
346
+ stores: storeSignatures.map((x) => (0, pick_1.default)(x, ['signer', 'pk', 'signature', 'components', 'paymentIntegrity'])),
347
+ },
374
348
  },
375
- stores: storeSignatures.map((x) => pick(x, ['signer', 'pk', 'signature', 'components', 'paymentIntegrity'])),
376
- },
377
- },
378
- hooks: [
379
- {
380
- name: 'mint',
381
- type: 'contract',
382
- hook: contract,
383
- },
384
- ],
385
- };
386
-
387
- itx.address = toFactoryAddress(itx);
388
-
389
- isValidFactory(itx, true);
390
-
391
- return itx;
349
+ hooks: [
350
+ {
351
+ name: 'mint',
352
+ type: 'contract',
353
+ hook: contract,
354
+ },
355
+ ],
356
+ };
357
+ itx.address = (0, did_util_1.toFactoryAddress)(itx);
358
+ // @ts-expect-error FIXME: help wanted
359
+ (0, asset_1.isValidFactory)(itx, true);
360
+ return itx;
392
361
  };
393
-
394
362
  const getFactoryInput = (inputTokens, { formatToken = true } = {}) => {
395
- const tokens = cloneDeep(inputTokens);
396
- tokens.forEach((token) => {
397
- if (formatToken) {
398
- token.value = fromTokenToUnit(token.value, token.decimal).toString();
399
- }
400
- delete token.decimal;
401
- });
402
- return {
403
- tokens,
404
- assets: [],
405
- variables: [],
406
- };
407
- };
408
-
409
- const getPaymentIntegrity = async ({ contract, factoryInput, storeComponents, meta, client, storeId }) => {
410
- if (!contract && !factoryInput && !storeComponents) {
411
- const priceTokens = await getPriceTokens(meta, client);
412
- const { components, stores } = await getComponents(meta);
413
- const store = stores.find((x) => x.id === storeId);
414
-
415
- // eslint-disable-next-line no-param-reassign
416
- contract = (await getContract({ meta, components, priceTokens })).code;
417
- // eslint-disable-next-line no-param-reassign
418
- factoryInput = await getFactoryInput(priceTokens);
419
- // eslint-disable-next-line no-param-reassign
420
- storeComponents = store?.components || [];
421
- }
422
-
423
- const paymentData = {
424
- factoryInput,
425
- contract,
426
- components: storeComponents || [],
427
- };
428
-
429
- const integrity = md5(stableStringify(paymentData));
430
-
431
- return integrity;
363
+ const tokens = (0, cloneDeep_1.default)(inputTokens);
364
+ tokens.forEach((token) => {
365
+ if (formatToken) {
366
+ token.value = (0, util_1.fromTokenToUnit)(token.value, token.decimal).toString();
367
+ }
368
+ delete token.decimal;
369
+ });
370
+ return {
371
+ tokens,
372
+ assets: [],
373
+ variables: [],
374
+ };
432
375
  };
433
-
434
- const getStoreSignatures = async ({ meta, stores, factoryInput, contract }) => {
435
- const storeSignatures = [];
436
- for (const store of stores) {
437
- const { id, url, pk, components: storeComponents } = store;
438
-
439
- const paymentIntegrity = await getPaymentIntegrity({ factoryInput, contract, storeComponents });
440
-
441
- /**
442
- * protocol: /api/payment/signature
443
- * method: POST
444
- * body: { blockletMeta, paymentIntegrity, paymentVersion }
445
- * return: { signer, pk, signature}
446
- */
447
- const { data: res } = await axios.post(
448
- `${url}/api/payment/signature`,
449
- {
450
- blockletMeta: meta,
451
- paymentIntegrity,
452
- paymentVersion: VERSION,
453
- },
454
- { timeout: 20000 }
455
- );
456
-
457
- if (res.signer !== id) {
458
- throw new Error('store signature: store id does not match');
376
+ const getPaymentIntegrity = ({ contract, factoryInput, storeComponents, meta, client, storeId, }) => __awaiter(void 0, void 0, void 0, function* () {
377
+ if (!contract && !factoryInput && !storeComponents) {
378
+ const priceTokens = yield getPriceTokens(meta, client);
379
+ const { components, stores } = yield getComponents(meta);
380
+ const store = stores.find((x) => x.id === storeId);
381
+ // eslint-disable-next-line no-param-reassign
382
+ contract = (yield getContract({ meta, components, priceTokens })).code;
383
+ // eslint-disable-next-line no-param-reassign
384
+ factoryInput = yield getFactoryInput(priceTokens);
385
+ // eslint-disable-next-line no-param-reassign
386
+ storeComponents = (store === null || store === void 0 ? void 0 : store.components) || [];
459
387
  }
460
-
461
- if (res.pk !== pk) {
462
- throw new Error('store signature: store pk does not match');
463
- }
464
-
465
- // verify sig
466
- const type = toTypeInfo(id);
467
- const wallet = fromPublicKey(pk, type);
468
- const verifyRes = wallet.verify(paymentIntegrity, res.signature);
469
- if (verifyRes !== true) {
470
- throw new Error('verify store signature failed');
388
+ const paymentData = {
389
+ factoryInput,
390
+ contract,
391
+ components: storeComponents || [],
392
+ };
393
+ const integrity = md5((0, json_stable_stringify_1.default)(paymentData));
394
+ return integrity;
395
+ });
396
+ const getStoreSignatures = ({ meta, stores, factoryInput, contract, }) => __awaiter(void 0, void 0, void 0, function* () {
397
+ const storeSignatures = [];
398
+ for (const store of stores) {
399
+ const { id, url, pk, components: storeComponents } = store;
400
+ const paymentIntegrity = yield getPaymentIntegrity({ factoryInput, contract, storeComponents });
401
+ /**
402
+ * protocol: /api/payment/signature
403
+ * method: POST
404
+ * body: { blockletMeta, paymentIntegrity, paymentVersion }
405
+ * return: { signer, pk, signature}
406
+ */
407
+ const { data: res } = yield axios_1.default.post(`${url}/api/payment/signature`, {
408
+ blockletMeta: meta,
409
+ paymentIntegrity,
410
+ paymentVersion: VERSION,
411
+ }, { timeout: 20000 });
412
+ if (res.signer !== id) {
413
+ throw new Error('store signature: store id does not match');
414
+ }
415
+ if (res.pk !== pk) {
416
+ throw new Error('store signature: store pk does not match');
417
+ }
418
+ // verify sig
419
+ const type = toTypeInfo(id);
420
+ const wallet = (0, wallet_1.fromPublicKey)(pk, type);
421
+ const verifyRes = wallet.verify(paymentIntegrity, res.signature);
422
+ if (verifyRes !== true) {
423
+ throw new Error('verify store signature failed');
424
+ }
425
+ storeSignatures.push({
426
+ signer: res.signer,
427
+ pk: res.pk,
428
+ signature: res.signature,
429
+ components: storeComponents,
430
+ paymentIntegrity,
431
+ storeUrl: url,
432
+ });
471
433
  }
472
-
473
- storeSignatures.push({
474
- signer: res.signer,
475
- pk: res.pk,
476
- signature: res.signature,
477
- components: storeComponents,
478
- paymentIntegrity,
479
- storeUrl: url,
480
- });
481
- }
482
-
483
- return {
484
- storeSignatures,
485
- };
486
- };
487
-
434
+ return {
435
+ storeSignatures,
436
+ };
437
+ });
488
438
  /**
489
439
  * Used by CLI and Store to independent compute factory itx
490
440
  *
491
441
  * @param {{
492
- * blockletMeta: BlockletMeta,
442
+ * blockletMeta: TBlockletMeta,
493
443
  * ocapClient: OcapClient,
494
444
  * issuers: Array<string>,
495
445
  * storeUrl: string,
@@ -505,66 +455,54 @@ const getStoreSignatures = async ({ meta, stores, factoryInput, contract }) => {
505
455
  * }>
506
456
  * }}
507
457
  */
508
- const createNftFactoryItx = async ({ blockletMeta, ocapClient, issuers, storeUrl }) => {
509
- const priceTokens = await getPriceTokens(blockletMeta, ocapClient);
510
- const { components, stores } = await getComponents(blockletMeta);
511
-
512
- const factoryInput = getFactoryInput(priceTokens);
513
- const { code: contract, shares } = await getContract({
514
- meta: blockletMeta,
515
- client: ocapClient,
516
- priceTokens,
517
- components,
518
- });
519
-
520
- const { storeSignatures } = await getStoreSignatures({
521
- meta: blockletMeta,
522
- client: ocapClient,
523
- priceTokens,
524
- components,
525
- stores,
526
- factoryInput,
527
- contract,
528
- });
529
-
530
- return {
531
- itx: _createNftFactoryItx({
532
- meta: blockletMeta,
533
- issuers,
534
- serviceUrl: storeUrl,
535
- priceTokens,
536
- components,
537
- stores,
538
- storeSignatures,
539
- factoryInput,
540
- contract,
541
- }),
542
- stores: storeSignatures.map((x) => ({ id: x.signer, url: x.storeUrl })),
543
- shares,
544
- };
545
- };
546
-
458
+ const createNftFactoryItx = ({ blockletMeta, ocapClient, issuers, storeUrl, }) => __awaiter(void 0, void 0, void 0, function* () {
459
+ const priceTokens = yield getPriceTokens(blockletMeta, ocapClient);
460
+ const { components, stores } = yield getComponents(blockletMeta);
461
+ const factoryInput = getFactoryInput(priceTokens);
462
+ const { code: contract, shares } = yield getContract({
463
+ meta: blockletMeta,
464
+ priceTokens,
465
+ components,
466
+ });
467
+ const { storeSignatures } = yield getStoreSignatures({
468
+ meta: blockletMeta,
469
+ stores,
470
+ factoryInput,
471
+ contract,
472
+ });
473
+ return {
474
+ itx: innerCreateNftFactoryItx({
475
+ meta: blockletMeta,
476
+ issuers,
477
+ serviceUrl: storeUrl,
478
+ storeSignatures,
479
+ factoryInput,
480
+ contract,
481
+ }),
482
+ stores: storeSignatures.map((x) => ({ id: x.signer, url: x.storeUrl })),
483
+ shares,
484
+ };
485
+ });
486
+ exports.createNftFactoryItx = createNftFactoryItx;
547
487
  /**
548
488
  * Used by Store before generating payment signature
549
489
  *
550
490
  * @param {{
551
491
  * integrity: string,
552
- * blockletMeta: BlockletMeta,
492
+ * blockletMeta: TBlockletMeta,
553
493
  * ocapClient: OcapClient,
554
494
  * storeId: string
555
495
  * }}
556
496
  * @returns {string} integrity
557
497
  */
558
- const verifyPaymentIntegrity = async ({ integrity: expected, blockletMeta, ocapClient, storeId }) => {
559
- const actual = await getPaymentIntegrity({ meta: blockletMeta, client: ocapClient, storeId });
560
-
561
- if (actual !== expected) {
562
- throw new Error('verify payment integrity failed');
563
- }
564
-
565
- return expected;
566
- };
567
-
498
+ const verifyPaymentIntegrity = ({ integrity: expected, blockletMeta, ocapClient, storeId, }) => __awaiter(void 0, void 0, void 0, function* () {
499
+ const actual = yield getPaymentIntegrity({ meta: blockletMeta, client: ocapClient, storeId });
500
+ if (actual !== expected) {
501
+ throw new Error('verify payment integrity failed');
502
+ }
503
+ return expected;
504
+ });
505
+ exports.verifyPaymentIntegrity = verifyPaymentIntegrity;
568
506
  /**
569
507
  * Used by Store before generating downloadToken
570
508
  *
@@ -577,83 +515,67 @@ const verifyPaymentIntegrity = async ({ integrity: expected, blockletMeta, ocapC
577
515
  * components: Array<{did: string, version: string}>
578
516
  * }}
579
517
  */
580
- const verifyNftFactory = async ({ factoryState, signerWallet }) => {
581
- const data = JSON.parse(factoryState?.data?.value);
582
- const stores = data?.stores || [];
583
- const store = stores.find((x) => x.signer === signerWallet.address);
584
-
585
- if (!store) {
586
- throw new Error(
587
- `Signer does not found in factory. factory: ${factoryState.address}, signer: ${signerWallet.address}`
588
- );
589
- }
590
-
591
- const c = factoryState.hooks.find((x) => x.type === 'contract');
592
- const { components } = store;
593
-
594
- // Token 的字段和 factory 中的字段不一致
595
- const factoryInput = getFactoryInput(
596
- factoryState.input.tokens.map((x) => pick(x, ['address', 'value'])),
597
- { formatToken: false }
598
- );
599
-
600
- const integrity = await getPaymentIntegrity({
601
- contract: c.hook,
602
- factoryInput,
603
- storeComponents: components,
604
- });
605
-
606
- if (signerWallet.sign(integrity) !== store.signature) {
607
- debug(store, factoryInput, integrity, components, c.hook);
608
- throw new Error(`verify nft factory failed: ${factoryState.address}`);
609
- }
610
-
611
- return { components };
612
- };
613
-
518
+ const verifyNftFactory = ({ factoryState, signerWallet, }) => __awaiter(void 0, void 0, void 0, function* () {
519
+ var _a;
520
+ const data = JSON.parse((_a = factoryState === null || factoryState === void 0 ? void 0 : factoryState.data) === null || _a === void 0 ? void 0 : _a.value);
521
+ const stores = (data === null || data === void 0 ? void 0 : data.stores) || [];
522
+ const store = stores.find((x) => x.signer === signerWallet.address);
523
+ if (!store) {
524
+ throw new Error(`Signer does not found in factory. factory: ${factoryState.address}, signer: ${signerWallet.address}`);
525
+ }
526
+ const c = factoryState.hooks.find((x) => x.type === 'contract');
527
+ const { components } = store;
528
+ // Token 的字段和 factory 中的字段不一致
529
+ const factoryInput = getFactoryInput(factoryState.input.tokens.map((x) => (0, pick_1.default)(x, ['address', 'value'])), { formatToken: false });
530
+ const integrity = yield getPaymentIntegrity({
531
+ contract: c.hook,
532
+ factoryInput,
533
+ storeComponents: components,
534
+ });
535
+ if (signerWallet.sign(integrity) !== store.signature) {
536
+ debug(store, factoryInput, integrity, components, c.hook);
537
+ throw new Error(`verify nft factory failed: ${factoryState.address}`);
538
+ }
539
+ return { components };
540
+ });
541
+ exports.verifyNftFactory = verifyNftFactory;
614
542
  /**
615
543
  * Check blocklet and all of components are free
616
544
  * Throw Error if not free
617
545
  *
618
- * @param {BlockletMeta} meta
546
+ * @param {TBlockletMeta} meta
619
547
  */
620
- const checkFreeBlocklet = async (blockletMeta) => {
621
- if (!isFreeBlocklet(blockletMeta)) {
622
- return Promise.reject(new Error('blocklet is not free'));
623
- }
624
-
625
- const { components } = await getComponents(blockletMeta);
626
-
627
- const shouldAllComponentFree = (arr) => {
628
- arr.forEach(({ meta, children }) => {
629
- if (!isFreeBlocklet(meta) || !isFreeComponent(meta)) {
630
- // throw new Error(`Found paid component "${meta.title || meta.name}" in free blocklet`);
631
- throw new Error(
632
- `Paid component "${meta.title || meta.name}" found in free blocklet "${
633
- blockletMeta.title || blockletMeta.name
634
- }", which is forbidden`
635
- );
636
- }
637
- shouldAllComponentFree(children || []);
638
- });
639
- };
640
-
641
- shouldAllComponentFree(components);
642
-
643
- return true;
644
- };
645
-
646
- module.exports = {
647
- createNftFactoryItx,
648
- verifyPaymentIntegrity,
649
- verifyNftFactory,
650
- checkFreeBlocklet,
651
- version: VERSION,
652
- _test: {
548
+ const checkFreeBlocklet = (blockletMeta) => __awaiter(void 0, void 0, void 0, function* () {
549
+ if (!(0, util_2.isFreeBlocklet)(blockletMeta)) {
550
+ return Promise.reject(new Error('blocklet is not free'));
551
+ }
552
+ const { components } = yield getComponents(blockletMeta);
553
+ const shouldAllComponentFree = (arr) => {
554
+ arr.forEach(({ meta, children }) => {
555
+ if (!(0, util_2.isFreeBlocklet)(meta) || !(0, util_2.isFreeComponent)(meta)) {
556
+ // throw new Error(`Found paid component "${meta.title || meta.name}" in free blocklet`);
557
+ throw new Error(`Paid component "${meta.title || meta.name}" found in free blocklet "${blockletMeta.title || blockletMeta.name}", which is forbidden`);
558
+ }
559
+ shouldAllComponentFree(children || []);
560
+ });
561
+ };
562
+ shouldAllComponentFree(components);
563
+ return true;
564
+ });
565
+ exports.checkFreeBlocklet = checkFreeBlocklet;
566
+ // eslint-disable-next-line @typescript-eslint/naming-convention
567
+ exports._test = {
653
568
  getPriceTokens,
654
569
  getFactoryInput,
655
570
  getPaymentIntegrity,
656
571
  getComponents,
657
572
  getContract,
658
- },
573
+ };
574
+ exports.default = {
575
+ createNftFactoryItx,
576
+ verifyPaymentIntegrity,
577
+ verifyNftFactory,
578
+ checkFreeBlocklet,
579
+ version: VERSION,
580
+ _test: exports._test,
659
581
  };