Last active
January 25, 2023 19:14
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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