Skip to content

Instantly share code, notes, and snippets.

@atengberg
Last active January 25, 2023 19:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save atengberg/7a698218112615517969247f762d92fd to your computer and use it in GitHub Desktop.
Save atengberg/7a698218112615517969247f762d92fd to your computer and use it in GitHub Desktop.
an example of using zx to deploy an icrc1 ledger with a esm utility function and run dfx nns install after checking if networks config is correct
uses zx as ilocal project dependency; zx can also
be used by including its shebang at the beginning
of the mjs using it--in that case can be run by
> chmod +x file.mjs
> zx file.mjs
⸎⸖------------------------------------------------------------------------⸖⸎
./zx-scripts/generate_icrc1_deployment_literal.mjs:
<start of file>
import { oneLine } from 'common-tags';
const getICRC1DeploymentLiteral = (
{ mintingPrincipal, initiallyFunded, name, symbol, decimals, fee },
asOneLine = true,
dfxJsonCanisterName = 'icrc1_example_ledger',
) => {
if (!mintingPrincipal) throw new Error('No minting principal specified, aborting!');
const mintingPrincipal_ = mintingPrincipal;
const initMints_ = initiallyFunded ?? [];
const name_ = name ?? 'Reloaded';
const symbol_ = symbol ?? 'ICRC1R';
const decimals_ = decimals ?? 8;
const fee_ = fee ?? 10000;
if (initMints_.length > 0) {
for (let who of initMints_) {
if (!who.principal || !who.amount) {
throw new Error('Any to be initially funded must included as { principal, amount }');
}
}
}
const getInitialMints = () => {
return initMints_.reduce((s, c) => {
return (s += `
record {
account = record {
owner = principal"${c.principal}";
};
amount = ${c.amount};
};`);
}, ``);
};
const getArg = () => `
dfx deploy ${dfxJsonCanisterName} --argument '(
record {
initial_mints = vec { ${getInitialMints()}
};
minting_account = record {
owner = principal"${mintingPrincipal_}";
};
token_name = "${name_}";
token_symbol = "${symbol_}";
decimals = ${decimals_};
transfer_fee = ${fee_};
}
)'`;
return asOneLine ? oneLine`${getArg()}` : getArg();
};
class Builder {
constructor(mintPrincipal, autoFundMintPrincipal = true) {
this.mintingPrincipal = mintPrincipal;
this.initMints = [];
if (autoFundMintPrincipal) {
this.initMints.push({ principal: mintPrincipal, amount: 1000000000000n });
}
this.transferFee = 10000;
this.setDecimals = 8;
}
addInitialMint(principal, amount) {
if (principal && amount) {
this.initMints.push({ principal, amount });
} else {
throw new Error("Can't add faulty principal and or amount to init mint accounts list");
}
return this;
}
setTokenNameAndSymbol(name, symbol) {
this.name = name;
this.symbol = symbol;
return this;
}
setTransferFee(fee) {
this.transferFee = fee;
return this;
}
// "setDecimals" always weirdly throws an undefined function error
setDecimalz(decimals) {
this.decimals = decimals;
return this;
}
build(asOneLine = true) {
return getICRC1DeploymentLiteral(
{
mintingPrincipal: this.mintingPrincipal,
initiallyFunded: this.initMints,
name: this.name,
symbol: this.symbol,
decimalz: this.decimals,
fee: this.fee,
},
asOneLine,
);
}
}
export { getICRC1DeploymentLiteral, Builder as ICRC1DeployLitBuilder };
<eof>
⸎⸖------------------------------------------------------------------------⸖⸎
./initReplicaScript.mjs
<start of file>
import { $, argv, chalk, fs } from 'zx';
import { spawnSync } from 'child_process';
import { ICRC1DeployLitBuilder } from './zx-scripts/generate_icrc1_deployment_literal.mjs';
// hides verbose logging or not, fyi --quiet => $.verbose = true;
//$.verbose = false;
// if JSON.stringify/parse traps
// BigInt.prototype.toJSON = function () { return this.toString(); };
const dfxRaw = async cmd => {
// Zx escapes input, this interferes with dfx commands;
// (see https://github.com/google/zx/issues/164 fmi)
// until a better quote function for dfx for zx can be made
// available run all dfx commands through zx without
// its escaping function, then reset it to normal.
const escaping = $.quote;
$.quote = (...all) => all;
const res = await $`${cmd}`;
$.quote = escaping;
return res;
};
const restartDfxClean = async withNNSinstall => {
console.info(chalk.bgBlue('restarting dfx clean'));
await $`dfx stop`;
// there are other ways of doing this, but this was this easiest
// IF you don't use this with --background dfx stop will lose
// ability to stop its own spawned processes
spawnSync('dfx', ['start', '--clean', '--background'], {
detached: true,
stdio: 'ignore',
});
if (withNNSinstall) {
let networks = `${await $`dfx info networks-json-path`}`.trim();
let { local } = JSON.parse(fs.readFileSync(networks, 'utf8'));
// confirm local replica network is configured correctly for dfx nns install
if (local.bind === '127.0.0.1:8080' && local?.replica?.subnet_type === 'system') {
console.info(chalk.bgBlue('with fresh dfx nns deployment'));
await $`dfx nns install`;
} else {
console.info(chalk.bgMagenta('networks.json not configured for dfx nns, skipping'));
}
}
};
const run = async (dfxnnsRedeploy = true) => {
await restartDfxClean(dfxnnsRedeploy);
const currentIdentityPrincipal = `${await $`dfx identity get-principal`}`;
// dfx nns install is deployed without an available minting account but has
// two identities that are loaded up, this one is the secpk256KeyIdentity
// that has a ident-1.pem file available for dfx cli. the other is an
// edscakeyidentity for node / e2e testing etc
const nnsFundedSecpkIdP = `hpikg-6exdt-jn33w-ndty3-fc7jc-tl2lr-buih3-cs3y7-tftkp-sfp62-gqe`;
let icrc1DeployLit = new ICRC1DeployLitBuilder(currentIdentityPrincipal.trim())
.addInitialMint(nnsFundedSecpkIdP, 1000000000n)
.setTokenNameAndSymbol('Reloaded', 'ICPR')
.setDecimalz(8)
.setTransferFee(10000)
.build(true);
console.info(chalk.bgBlue('deploying ICRC1 ledger'));
await dfxRaw(icrc1DeployLit);
console.info(chalk.bgBlue(`deployment finished, canisters rollin'`));
};
// argv zx cli args --sayHello=Hiro
if (argv.sayHello) {
console.log(`hello ${argv.sayHello}`);
}
// you can also try catch etc etc
run(argv.dfxnnsRedeploy);
<eof>
⸎⸖------------------------------------------------------------------------⸖⸎
Usage:
node initReplicaScript.mjs --sayHello=Hiro --dfxnnsRedeploy
!!!note this was updated to formalize wrapper around
running dfx commands through zx raw only when needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment