@agoric/deploy-script-support 0.10.4-other-dev-8f8782b.0 → 0.10.4-other-dev-fbe72e7.0.fbe72e7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -2
- package/package.json +34 -27
- package/src/assertOfferResult.js +5 -1
- package/src/cachedBundleSpec.js +4 -1
- package/src/coreProposalBehavior.js +122 -64
- package/src/depositInvitation.js +5 -0
- package/src/endo-pieces-contract.js +4 -2
- package/src/externalTypes.js +63 -17
- package/src/extract-proposal.js +229 -154
- package/src/getBundlerMaker.js +4 -0
- package/src/helpers.js +22 -3
- package/src/install.js +2 -11
- package/src/offer.js +10 -7
- package/src/parseCoreEvalArgs.js +36 -0
- package/src/permissioned-deployment.js +168 -0
- package/src/saveIssuer.js +5 -2
- package/src/startInstance.js +8 -5
- package/src/writeCoreEvalParts.js +228 -0
- package/CHANGELOG.md +0 -546
- package/exported.js +0 -3
- package/src/writeCoreProposal.js +0 -139
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file support for Contract Deployment Process
|
|
3
|
+
*
|
|
4
|
+
* 1. `agoric run XYZ.build.js` produces:
|
|
5
|
+
* - `b1-123.json` bundle x 2: contract, aux
|
|
6
|
+
* - `XYZ-permit.json`, `XYZ.js` script
|
|
7
|
+
* 2. Install bundles
|
|
8
|
+
* - permissionless with per-byte fee in IST
|
|
9
|
+
* 3. Submit CoreEval proposal to BLD stakers
|
|
10
|
+
* - `XYZ-permit.json`, `XYZ.js`
|
|
11
|
+
*
|
|
12
|
+
* @see {runBuilder}
|
|
13
|
+
* @see {installBundles}
|
|
14
|
+
* @see {submitCoreEval}
|
|
15
|
+
*/
|
|
16
|
+
import { toCLIOptions } from '@agoric/internal/src/cli-utils.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @import {CmdRunner} from '@agoric/pola-io';
|
|
20
|
+
* @import {FileRd} from '@agoric/pola-io';
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
*
|
|
25
|
+
* TODO: builder should be a FileRd
|
|
26
|
+
* TODO: parameterize dest dir
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* to use npx to find `agoric` in node_modules/.bin:
|
|
30
|
+
* const execP = promisify(childProcess.execFile)
|
|
31
|
+
* const agoric = makeCmdRunner('npx', { execFile: execP }).subCommand('agoric');
|
|
32
|
+
*
|
|
33
|
+
* XXX use a different name from execFile since the meaning is different
|
|
34
|
+
*
|
|
35
|
+
* @param {CmdRunner} agoric
|
|
36
|
+
* @param {FileRd} builder
|
|
37
|
+
* @param {string[]} [builderOpts]
|
|
38
|
+
* @param {{cwd?: FileRd}} [io]
|
|
39
|
+
*
|
|
40
|
+
* @returns {Promise<Plan>}
|
|
41
|
+
*
|
|
42
|
+
* @typedef {{
|
|
43
|
+
* name: string,
|
|
44
|
+
* script: string,
|
|
45
|
+
* permit: string,
|
|
46
|
+
* bundles: { entrypoint:string, bundleID:string, fileName:string}[];
|
|
47
|
+
* }} Plan
|
|
48
|
+
*/
|
|
49
|
+
export const runBuilder = async (
|
|
50
|
+
agoric,
|
|
51
|
+
builder,
|
|
52
|
+
builderOpts = [],
|
|
53
|
+
{ cwd = builder.join('../../') } = {},
|
|
54
|
+
) => {
|
|
55
|
+
const cmd = agoric.withFlags(...builderOpts);
|
|
56
|
+
const { stdout } = await cmd.exec(['run', String(builder)]);
|
|
57
|
+
const match = stdout?.match(/ (?<name>[-\w]+)-permit.json/);
|
|
58
|
+
if (!(match && match.groups)) {
|
|
59
|
+
throw Error('no permit found');
|
|
60
|
+
}
|
|
61
|
+
/** @type {Plan} */
|
|
62
|
+
const plan = await cwd.join(`${match.groups.name}-plan.json`).readJSON();
|
|
63
|
+
return plan;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const txFlags = ({
|
|
67
|
+
node,
|
|
68
|
+
from,
|
|
69
|
+
chainId,
|
|
70
|
+
keyringBackend = 'test',
|
|
71
|
+
broadcastMode = 'block',
|
|
72
|
+
}) => ({
|
|
73
|
+
node,
|
|
74
|
+
from,
|
|
75
|
+
'chain-id': chainId,
|
|
76
|
+
'keyring-backend': keyringBackend,
|
|
77
|
+
'broadcast-mode': broadcastMode,
|
|
78
|
+
// TODO: parameterize these?
|
|
79
|
+
gas: 'auto',
|
|
80
|
+
'gas-adjustment': '1.4',
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @param {CmdRunner} agd
|
|
85
|
+
* @param {number} n
|
|
86
|
+
*/
|
|
87
|
+
export const waitForBlock = async (agd, n = 1) => {
|
|
88
|
+
const getHeight = async () => {
|
|
89
|
+
const { stdout } = await agd.exec(['status']);
|
|
90
|
+
const status = JSON.parse(stdout);
|
|
91
|
+
const { latest_block_height: height } = status.sync_info || status.SyncInfo;
|
|
92
|
+
return height;
|
|
93
|
+
};
|
|
94
|
+
const initialHeight = await getHeight();
|
|
95
|
+
const SEC = 1000;
|
|
96
|
+
let currentHeight;
|
|
97
|
+
do {
|
|
98
|
+
await new Promise(resolve => setTimeout(resolve, 1 * SEC)); // XXX ambient
|
|
99
|
+
currentHeight = await getHeight();
|
|
100
|
+
} while (currentHeight - initialHeight < n);
|
|
101
|
+
console.log('block height:', initialHeight, currentHeight);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @param {CmdRunner} agd - agd with --from etc.
|
|
106
|
+
* @param {string[]} txArgs
|
|
107
|
+
*/
|
|
108
|
+
export const runTx = async (agd, txArgs) => {
|
|
109
|
+
const { stdout } = await agd.withFlags('-o', 'json').exec(['tx', ...txArgs]);
|
|
110
|
+
const result = JSON.parse(stdout);
|
|
111
|
+
if (result.code !== 0) {
|
|
112
|
+
throw Object.assign(Error(result.raw_log), result);
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @param {CmdRunner} agd
|
|
119
|
+
* @param {FileRd} bundle
|
|
120
|
+
*/
|
|
121
|
+
export const installBundle = async (agd, bundle) =>
|
|
122
|
+
runTx(agd, ['swingset', 'install-bundle', `@${bundle}`]);
|
|
123
|
+
|
|
124
|
+
export const txAbbr = tx => {
|
|
125
|
+
const { txhash, code, height, gas_used: g } = tx;
|
|
126
|
+
|
|
127
|
+
return { txhash, code, height, gas_used: g };
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @param {CmdRunner} agd
|
|
132
|
+
* @param {Plan['bundles']} bundles
|
|
133
|
+
* @param {FileRd} files
|
|
134
|
+
*/
|
|
135
|
+
export const installBundles = (agd, bundles, files) => {
|
|
136
|
+
const ps = bundles.map(b =>
|
|
137
|
+
installBundle(agd, files.join(files.relative(b.fileName))),
|
|
138
|
+
);
|
|
139
|
+
return Promise.all(ps);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* @param {CmdRunner} agd
|
|
144
|
+
* @param {Pick<Plan, 'permit' | 'script'>[]} evals
|
|
145
|
+
* @param {object} [opts]
|
|
146
|
+
* @param {string} [opts.title]
|
|
147
|
+
* @param {string} [opts.description]
|
|
148
|
+
* @param {object} [opts.depositOpts]
|
|
149
|
+
* @param {string} [opts.depositOpts.denom]
|
|
150
|
+
* @param {number} [opts.depositOpts.unit]
|
|
151
|
+
* @param {number} [opts.depositOpts.qty]
|
|
152
|
+
* @param {string} [opts.deposit]
|
|
153
|
+
*/
|
|
154
|
+
export const submitCoreEval = async (
|
|
155
|
+
agd,
|
|
156
|
+
evals,
|
|
157
|
+
{
|
|
158
|
+
title = evals[0].script,
|
|
159
|
+
description = title,
|
|
160
|
+
depositOpts: { denom = 'ubld', unit = 1_000_000, qty = 10 } = {},
|
|
161
|
+
deposit = `${qty * unit}${denom}`,
|
|
162
|
+
} = {},
|
|
163
|
+
) =>
|
|
164
|
+
runTx(agd, [
|
|
165
|
+
...'gov submit-proposal swingset-core-eval'.split(' '),
|
|
166
|
+
...evals.map(e => [e.permit, e.script]).flat(),
|
|
167
|
+
...toCLIOptions({ title, description, deposit }),
|
|
168
|
+
]);
|
package/src/saveIssuer.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
import { E } from '@endo/far';
|
|
3
3
|
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* @import {Petname} from '@agoric/deploy-script-support/src/externalTypes.js';
|
|
6
|
+
* @import {Amount, Brand, Issuer, Payment} from '@agoric/ertp';
|
|
7
|
+
*/
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* @param {ERef<any>} walletAdmin - an internal type of the
|
|
8
11
|
* wallet, not defined here
|
|
9
|
-
* @param {ERef<import('./startInstance').IssuerManager>} issuerManager
|
|
12
|
+
* @param {ERef<import('./startInstance.js').IssuerManager>} issuerManager
|
|
10
13
|
*/
|
|
11
14
|
export const makeSaveIssuer = (walletAdmin, issuerManager) => {
|
|
12
15
|
/**
|
package/src/startInstance.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
import { assert } from '@
|
|
2
|
+
import { assert } from '@endo/errors';
|
|
3
3
|
import { E, passStyleOf } from '@endo/far';
|
|
4
4
|
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* @import {Petname} from '@agoric/deploy-script-support/src/externalTypes.js';
|
|
7
|
+
* @import {Amount, Brand, Issuer, Payment, Purse} from '@agoric/ertp';
|
|
8
|
+
* @import {IssuerKeywordRecord, Keyword} from '@agoric/zoe';
|
|
9
|
+
*/
|
|
6
10
|
|
|
7
11
|
/**
|
|
8
12
|
* @template T
|
|
@@ -100,9 +104,8 @@ export const makeStartInstance = (
|
|
|
100
104
|
`creatorInvitation must be defined to be deposited`,
|
|
101
105
|
);
|
|
102
106
|
console.log(`-- Adding Invitation for: ${instancePetname}`);
|
|
103
|
-
const invitationAmount =
|
|
104
|
-
creatorInvitation
|
|
105
|
-
);
|
|
107
|
+
const invitationAmount =
|
|
108
|
+
await E(zoeInvitationPurse).deposit(creatorInvitation);
|
|
106
109
|
console.log(`- Created Contract Instance: ${instancePetname}`);
|
|
107
110
|
|
|
108
111
|
const creatorInvitationDetails = invitationAmount.value[0];
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { E } from '@endo/far';
|
|
4
|
+
import { deeplyFulfilled } from '@endo/marshal';
|
|
5
|
+
|
|
6
|
+
import { createBundles } from '@agoric/internal/src/node/createBundles.js';
|
|
7
|
+
import { defangAndTrim, mergePermits, stringify } from './code-gen.js';
|
|
8
|
+
import {
|
|
9
|
+
makeCoreProposalBehavior,
|
|
10
|
+
permits as defaultPermits,
|
|
11
|
+
} from './coreProposalBehavior.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @import {BundleSource, BundleSourceResult} from '@endo/bundle-source';
|
|
15
|
+
* @import {AgSoloHome, CanonicalHome, CommonHome, CoreEvalBuilder, CoreEvalDescriptor, ManifestBundleRef} from './externalTypes.js';
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef CoreEvalPlan
|
|
20
|
+
* @property {string} name
|
|
21
|
+
* @property {string} permit
|
|
22
|
+
* @property {string} script
|
|
23
|
+
* @property {{entrypoint: string, bundleID: string, fileName: string}[]} bundles
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @callback WriteCoreEval write to disk the files needed for a CoreEval (js code to`${filePrefix}.js`, permits to `${filePrefix}-permit.json`, an overall
|
|
28
|
+
* summary to `${filePrefix}-plan.json), plus whatever bundles bundles the code loads)
|
|
29
|
+
* see CoreEval in {@link '/golang/cosmos/x/swingset/types/swingset.pb.go'}
|
|
30
|
+
* @param {string} filePrefix name on disk
|
|
31
|
+
* @param {CoreEvalBuilder} builder
|
|
32
|
+
* @returns {Promise<CoreEvalPlan>}
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
*
|
|
37
|
+
* @param {Promise<CommonHome | AgSoloHome>} homeP
|
|
38
|
+
* @param {{
|
|
39
|
+
* bundleSource: BundleSource,
|
|
40
|
+
* pathResolve: (path: string) => string,
|
|
41
|
+
* }} endowments
|
|
42
|
+
* @param {{
|
|
43
|
+
* getBundlerMaker: () => Promise<import('./getBundlerMaker.js').BundleMaker>,
|
|
44
|
+
* getBundleSpec: (bundle: Promise<BundleSourceResult<'endoZipBase64'>>, getBundle: () => import('./getBundlerMaker.js').Bundler, opts?: any) => Promise<ManifestBundleRef>,
|
|
45
|
+
* log?: typeof console.log,
|
|
46
|
+
* writeFile?: typeof fs.promises.writeFile
|
|
47
|
+
* }} io
|
|
48
|
+
* @returns {WriteCoreEval}
|
|
49
|
+
*/
|
|
50
|
+
export const makeWriteCoreEval = (
|
|
51
|
+
homeP,
|
|
52
|
+
endowments,
|
|
53
|
+
{
|
|
54
|
+
getBundlerMaker,
|
|
55
|
+
getBundleSpec,
|
|
56
|
+
log = console.log,
|
|
57
|
+
writeFile = fs.promises.writeFile,
|
|
58
|
+
},
|
|
59
|
+
) => {
|
|
60
|
+
const { bundleSource, pathResolve } = endowments;
|
|
61
|
+
|
|
62
|
+
let bundlerCache;
|
|
63
|
+
/** @returns {import('./getBundlerMaker.js').Bundler} */
|
|
64
|
+
const getBundler = () => {
|
|
65
|
+
if (!bundlerCache) {
|
|
66
|
+
bundlerCache = E(getBundlerMaker()).makeBundler({
|
|
67
|
+
// @ts-expect-error lazily resolved for AgSoloHome
|
|
68
|
+
zoe: E.get(homeP).zoe,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
return bundlerCache;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
*
|
|
76
|
+
* @param {CoreEvalDescriptor} coreEval
|
|
77
|
+
* @param {*} additionalPermits
|
|
78
|
+
*/
|
|
79
|
+
const mergeEvalPermit = async (coreEval, additionalPermits) => {
|
|
80
|
+
const {
|
|
81
|
+
sourceSpec,
|
|
82
|
+
getManifestCall: [manifestGetterName, ...manifestGetterArgs],
|
|
83
|
+
} = coreEval;
|
|
84
|
+
|
|
85
|
+
const moduleNamespace = await import(pathResolve(sourceSpec));
|
|
86
|
+
|
|
87
|
+
// We only care about the manifest, not any restoreRef calls.
|
|
88
|
+
const { manifest } = await moduleNamespace[manifestGetterName](
|
|
89
|
+
harden({ restoreRef: x => `restoreRef:${x}` }),
|
|
90
|
+
...manifestGetterArgs,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const mergedPermits = mergePermits(manifest);
|
|
94
|
+
return {
|
|
95
|
+
manifest,
|
|
96
|
+
permits: mergePermits({ mergedPermits, additionalPermits }),
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
let mutex = /** @type {Promise<ManifestBundleRef | undefined>} */ (
|
|
101
|
+
Promise.resolve()
|
|
102
|
+
);
|
|
103
|
+
/** @type {WriteCoreEval} */
|
|
104
|
+
const writeCoreEval = async (filePrefix, builder) => {
|
|
105
|
+
/**
|
|
106
|
+
*
|
|
107
|
+
* @param {string} entrypoint
|
|
108
|
+
* @param {string} [bundlePath]
|
|
109
|
+
* @returns {Promise<BundleSourceResult<'endoZipBase64'>>}
|
|
110
|
+
*/
|
|
111
|
+
const getBundle = async (entrypoint, bundlePath) => {
|
|
112
|
+
if (!bundlePath) {
|
|
113
|
+
return bundleSource(pathResolve(entrypoint));
|
|
114
|
+
}
|
|
115
|
+
const bundleCache = pathResolve(bundlePath);
|
|
116
|
+
await createBundles([[pathResolve(entrypoint), bundleCache]]);
|
|
117
|
+
const ns = await import(bundleCache);
|
|
118
|
+
return ns.default;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
const bundles = [];
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Install an entrypoint.
|
|
125
|
+
*
|
|
126
|
+
* @param {string} entrypoint
|
|
127
|
+
* @param {string} [bundlePath]
|
|
128
|
+
* @param {unknown} [opts]
|
|
129
|
+
* @returns {Promise<ManifestBundleRef>}
|
|
130
|
+
*/
|
|
131
|
+
const install = async (entrypoint, bundlePath, opts) => {
|
|
132
|
+
const bundle = getBundle(entrypoint, bundlePath);
|
|
133
|
+
|
|
134
|
+
// Serialise the installations.
|
|
135
|
+
mutex = E.when(mutex, async () => {
|
|
136
|
+
// console.log('installing', { filePrefix, entrypoint, bundlePath });
|
|
137
|
+
const spec = await getBundleSpec(bundle, getBundler, opts);
|
|
138
|
+
bundles.push({
|
|
139
|
+
entrypoint,
|
|
140
|
+
...spec,
|
|
141
|
+
});
|
|
142
|
+
return spec;
|
|
143
|
+
});
|
|
144
|
+
// @ts-expect-error xxx mutex type narrowing
|
|
145
|
+
return mutex;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// Await a reference then publish to the board.
|
|
149
|
+
const cmds = [];
|
|
150
|
+
/** @param {Promise<ManifestBundleRef>} refP */
|
|
151
|
+
const publishRef = async refP => {
|
|
152
|
+
const { fileName, ...ref } = await refP;
|
|
153
|
+
if (fileName) {
|
|
154
|
+
cmds.push(`agd tx swingset install-bundle @${fileName}`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return harden(ref);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// Create the eval structure.
|
|
161
|
+
const evalDescriptor = await deeplyFulfilled(
|
|
162
|
+
harden(builder({ publishRef, install })),
|
|
163
|
+
);
|
|
164
|
+
const { sourceSpec, getManifestCall } = evalDescriptor;
|
|
165
|
+
// console.log('created', { filePrefix, sourceSpec, getManifestCall });
|
|
166
|
+
|
|
167
|
+
// Extract the top-level permit.
|
|
168
|
+
const { permits: evalPermits, manifest: customManifest } =
|
|
169
|
+
await mergeEvalPermit(evalDescriptor, defaultPermits);
|
|
170
|
+
|
|
171
|
+
// Get an install
|
|
172
|
+
const manifestBundleRef = await publishRef(install(sourceSpec));
|
|
173
|
+
|
|
174
|
+
// console.log('writing', { filePrefix, manifestBundleRef, sourceSpec });
|
|
175
|
+
const code = `\
|
|
176
|
+
// This is generated by writeCoreEval; please edit!
|
|
177
|
+
/* eslint-disable */
|
|
178
|
+
|
|
179
|
+
const manifestBundleRef = ${stringify(manifestBundleRef)};
|
|
180
|
+
const getManifestCall = harden(${stringify(getManifestCall, true)});
|
|
181
|
+
const customManifest = ${stringify(customManifest, true)};
|
|
182
|
+
|
|
183
|
+
// Make a behavior function and "export" it by way of script completion value.
|
|
184
|
+
// It is constructed by an anonymous invocation to ensure the absence of a global binding
|
|
185
|
+
// for makeCoreProposalBehavior, which may not be necessary but preserves behavior pre-dating
|
|
186
|
+
// https://github.com/Agoric/agoric-sdk/pull/8712 .
|
|
187
|
+
const behavior = (${makeCoreProposalBehavior})({ manifestBundleRef, getManifestCall, customManifest, E });
|
|
188
|
+
behavior;
|
|
189
|
+
`;
|
|
190
|
+
|
|
191
|
+
const trimmed = defangAndTrim(code);
|
|
192
|
+
|
|
193
|
+
const permitFile = `${filePrefix}-permit.json`;
|
|
194
|
+
log(`creating ${permitFile}`);
|
|
195
|
+
await writeFile(permitFile, JSON.stringify(evalPermits, null, 2));
|
|
196
|
+
|
|
197
|
+
const codeFile = `${filePrefix}.js`;
|
|
198
|
+
log(`creating ${codeFile}`);
|
|
199
|
+
await writeFile(codeFile, trimmed);
|
|
200
|
+
|
|
201
|
+
/** @type {CoreEvalPlan} */
|
|
202
|
+
const plan = {
|
|
203
|
+
name: filePrefix,
|
|
204
|
+
script: codeFile,
|
|
205
|
+
permit: permitFile,
|
|
206
|
+
bundles,
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
await writeFile(
|
|
210
|
+
`${filePrefix}-plan.json`,
|
|
211
|
+
`${JSON.stringify(plan, null, 2)}\n`,
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
log(`\
|
|
215
|
+
You can now run a governance submission command like:
|
|
216
|
+
agd tx gov submit-proposal swingset-core-eval ${permitFile} ${codeFile} \\
|
|
217
|
+
--title="Enable <something>" --description="Evaluate ${codeFile}" --deposit=1000000ubld \\
|
|
218
|
+
--gas=auto --gas-adjustment=1.2
|
|
219
|
+
Remember to install bundles before submitting the proposal:
|
|
220
|
+
${cmds.join('\n ')}
|
|
221
|
+
`);
|
|
222
|
+
return plan;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
return writeCoreEval;
|
|
226
|
+
};
|
|
227
|
+
/** @deprecated use makeWriteCoreEval */
|
|
228
|
+
export const makeWriteCoreProposal = makeWriteCoreEval;
|