mirror of
https://github.com/logos-storage/logos-storage-installer.git
synced 2026-01-03 14:03:08 +00:00
setting up
This commit is contained in:
parent
a63a8944a2
commit
d017b5a733
115
package-lock.json
generated
115
package-lock.json
generated
@ -12,6 +12,8 @@
|
||||
"axios": "^1.6.2",
|
||||
"boxen": "^7.1.1",
|
||||
"chalk": "^5.3.0",
|
||||
"crypto": "^1.0.1",
|
||||
"ethers": "^6.13.5",
|
||||
"fs-extra": "^11.3.0",
|
||||
"fs-filesystem": "^2.1.2",
|
||||
"inquirer": "^9.2.12",
|
||||
@ -31,6 +33,12 @@
|
||||
"node": ">=20"
|
||||
}
|
||||
},
|
||||
"node_modules/@adraffy/ens-normalize": {
|
||||
"version": "1.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz",
|
||||
"integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.0",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
|
||||
@ -446,6 +454,30 @@
|
||||
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@noble/curves": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
|
||||
"integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.3.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
|
||||
"integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.34.8",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz",
|
||||
@ -699,6 +731,15 @@
|
||||
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz",
|
||||
"integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.7.tgz",
|
||||
@ -805,6 +846,12 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/aes-js": {
|
||||
"version": "4.0.0-beta.5",
|
||||
"resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz",
|
||||
"integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ansi-align": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
|
||||
@ -1212,6 +1259,13 @@
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/crypto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz",
|
||||
"integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==",
|
||||
"deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in.",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
@ -1364,6 +1418,40 @@
|
||||
"@types/estree": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ethers": {
|
||||
"version": "6.13.5",
|
||||
"resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.5.tgz",
|
||||
"integrity": "sha512-+knKNieu5EKRThQJWwqaJ10a6HE9sSehGeqWN65//wE7j47ZpFhKAnHB/JJFibwwg61I/koxaPsXbXpD/skNOQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/ethers-io/"
|
||||
},
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://www.buymeacoffee.com/ricmoo"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@adraffy/ens-normalize": "1.10.1",
|
||||
"@noble/curves": "1.2.0",
|
||||
"@noble/hashes": "1.3.2",
|
||||
"@types/node": "22.7.5",
|
||||
"aes-js": "4.0.0-beta.5",
|
||||
"tslib": "2.7.0",
|
||||
"ws": "8.17.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ethers/node_modules/tslib": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
|
||||
"integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/expect-type": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz",
|
||||
@ -2237,6 +2325,12 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.19.8",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||
@ -2571,6 +2665,27 @@
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/yoctocolors-cjs": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz",
|
||||
|
||||
@ -27,6 +27,8 @@
|
||||
"axios": "^1.6.2",
|
||||
"boxen": "^7.1.1",
|
||||
"chalk": "^5.3.0",
|
||||
"crypto": "^1.0.1",
|
||||
"ethers": "^6.13.5",
|
||||
"fs-extra": "^11.3.0",
|
||||
"fs-filesystem": "^2.1.2",
|
||||
"inquirer": "^9.2.12",
|
||||
@ -34,7 +36,7 @@
|
||||
"nanospinner": "^1.1.0",
|
||||
"open": "^10.1.0",
|
||||
"ps-list": "^8.1.1"
|
||||
},
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^3.4.2",
|
||||
"vitest": "^3.0.5"
|
||||
|
||||
@ -30,6 +30,7 @@ export const mockFsService = {
|
||||
moveDir: vi.fn(),
|
||||
deleteDir: vi.fn(),
|
||||
readJsonFile: vi.fn(),
|
||||
readFile: vi.fn(),
|
||||
writeJsonFile: vi.fn(),
|
||||
writeFile: vi.fn(),
|
||||
ensureDirExists: vi.fn(),
|
||||
|
||||
@ -1,10 +1,17 @@
|
||||
export class Installer {
|
||||
constructor(configService, shellService, osService, fsService) {
|
||||
constructor(
|
||||
configService,
|
||||
shellService,
|
||||
osService,
|
||||
fsService,
|
||||
marketplaceSetup,
|
||||
) {
|
||||
this.config = configService.get();
|
||||
this.configService = configService;
|
||||
this.shell = shellService;
|
||||
this.os = osService;
|
||||
this.fs = fsService;
|
||||
this.market = marketplaceSetup;
|
||||
}
|
||||
|
||||
isCodexInstalled = async () => {
|
||||
@ -28,6 +35,8 @@ export class Installer {
|
||||
this.fs.ensureDirExists(this.config.codexRoot);
|
||||
if (!(await this.arePrerequisitesCorrect(processCallbacks))) return;
|
||||
|
||||
if (!(await this.market.runClientWizard())) return;
|
||||
|
||||
processCallbacks.installStarts();
|
||||
if (this.os.isWindows()) {
|
||||
await this.installCodexWindows(processCallbacks);
|
||||
|
||||
@ -72,6 +72,13 @@ export class ProcessControl {
|
||||
const workingDir = this.configService.get().codexRoot;
|
||||
const args = [
|
||||
`--config-file=${this.configService.getCodexConfigFilePath()}`,
|
||||
|
||||
// Marketplace client parameters cannot be set via config file.
|
||||
// Open issue: https://github.com/codex-storage/nim-codex/issues/1206
|
||||
// So we're setting them here.
|
||||
"persistence",
|
||||
`--eth-provider=https://rpc.testnet.codex.storage`,
|
||||
`--eth-private-key=eth.key`
|
||||
];
|
||||
await this.shell.spawnDetachedProcess(executable, workingDir, args);
|
||||
};
|
||||
|
||||
10
src/main.js
10
src/main.js
@ -20,7 +20,6 @@ import {
|
||||
import { runCodex, checkNodeStatus } from "./handlers/nodeHandlers.js";
|
||||
import { showInfoMessage } from "./utils/messages.js";
|
||||
import { ConfigService } from "./services/configService.js";
|
||||
|
||||
import { UiService } from "./services/uiService.js";
|
||||
import { FsService } from "./services/fsService.js";
|
||||
import { MainMenu } from "./ui/mainMenu.js";
|
||||
@ -35,6 +34,8 @@ import { OsService } from "./services/osService.js";
|
||||
import { ProcessControl } from "./handlers/processControl.js";
|
||||
import { CodexGlobals } from "./services/codexGlobals.js";
|
||||
import { CodexApp } from "./services/codexApp.js";
|
||||
import { EthersService } from "./services/ethersService.js";
|
||||
import { MarketplaceSetup } from "./ui/marketplaceSetup.js";
|
||||
|
||||
async function showNavigationMenu() {
|
||||
console.log("\n");
|
||||
@ -108,11 +109,18 @@ export async function main() {
|
||||
const configService = new ConfigService(fsService, osService);
|
||||
const codexApp = new CodexApp(configService);
|
||||
const pathSelector = new PathSelector(uiService, new MenuLoop(), fsService);
|
||||
const ethersService = new EthersService(fsService, configService);
|
||||
const marketplaceSetup = new MarketplaceSetup(
|
||||
uiService,
|
||||
configService,
|
||||
ethersService,
|
||||
);
|
||||
const installer = new Installer(
|
||||
configService,
|
||||
shellService,
|
||||
osService,
|
||||
fsService,
|
||||
marketplaceSetup,
|
||||
);
|
||||
const installMenu = new InstallMenu(
|
||||
uiService,
|
||||
|
||||
@ -10,4 +10,8 @@ export class CodexGlobals {
|
||||
const result = (await axios.get(`https://spr.codex.storage/testnet`)).data;
|
||||
return result.split("\n").filter((line) => line.length > 0);
|
||||
};
|
||||
|
||||
getEthProvider = () => {
|
||||
return "https://rpc.testnet.codex.storage";
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,8 @@ const defaultConfig = {
|
||||
const datadir = "datadir";
|
||||
const codexLogFile = "codex.log";
|
||||
const codexConfigFile = "config.toml";
|
||||
const ethKeyFile = "eth.key";
|
||||
const ethAddressFile = "eth.address";
|
||||
|
||||
export class ConfigService {
|
||||
constructor(fsService, osService) {
|
||||
@ -38,6 +40,13 @@ export class ConfigService {
|
||||
return this.fs.pathJoin([this.config.codexRoot, codexConfigFile]);
|
||||
};
|
||||
|
||||
getEthFilePaths = () => {
|
||||
return {
|
||||
key: this.fs.pathJoin([this.config.codexRoot, ethKeyFile]),
|
||||
address: this.fs.pathJoin([this.config.codexRoot, ethAddressFile]),
|
||||
};
|
||||
};
|
||||
|
||||
loadConfig = () => {
|
||||
const filePath = this.getConfigFilename();
|
||||
try {
|
||||
@ -76,7 +85,7 @@ export class ConfigService {
|
||||
throw new Error("Storage quota must be at least 100MB");
|
||||
};
|
||||
|
||||
writeCodexConfigFile = (publicIp, bootstrapNodes) => {
|
||||
writeCodexConfigFile = (publicIp, bootstrapNodes, ethProvider) => {
|
||||
this.validateConfiguration();
|
||||
|
||||
const nl = "\n";
|
||||
@ -85,15 +94,22 @@ export class ConfigService {
|
||||
this.fs.writeFile(
|
||||
this.getCodexConfigFilePath(),
|
||||
`data-dir="${datadir}"${nl}` +
|
||||
`log-level="DEBUG"${nl}` +
|
||||
`log-file="${codexLogFile}"${nl}` +
|
||||
`storage-quota=${this.config.storageQuota}${nl}` +
|
||||
`disc-port=${this.config.ports.discPort}${nl}` +
|
||||
`listen-addrs=["/ip4/0.0.0.0/tcp/${this.config.ports.listenPort}"]${nl}` +
|
||||
`api-port=${this.config.ports.apiPort}${nl}` +
|
||||
`nat="extip:${publicIp}"${nl}` +
|
||||
`api-cors-origin="*"${nl}` +
|
||||
`bootstrap-node=[${bootNodes}]${nl}`,
|
||||
`log-level="TRACE"${nl}` +
|
||||
`log-file="${codexLogFile}"${nl}` +
|
||||
`storage-quota=${this.config.storageQuota}${nl}` +
|
||||
`disc-port=${this.config.ports.discPort}${nl}` +
|
||||
`listen-addrs=["/ip4/0.0.0.0/tcp/${this.config.ports.listenPort}"]${nl}` +
|
||||
`api-port=${this.config.ports.apiPort}${nl}` +
|
||||
`nat="extip:${publicIp}"${nl}` +
|
||||
`api-cors-origin="*"${nl}` +
|
||||
`bootstrap-node=[${bootNodes}]${nl}` +
|
||||
// Marketplace client parameters:
|
||||
// `[persistence]${nl}` +
|
||||
//`eth-provider="${ethProvider}"${nl}` +
|
||||
// `eth-provider="https://rpc.testnet.codex.storage"${nl}` +
|
||||
// //`eth-private-key="${ethKeyFile}"${nl}` +
|
||||
// `eth-private-key="notafile.no"${nl}` +
|
||||
`${nl}`
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
41
src/services/ethersService.js
Normal file
41
src/services/ethersService.js
Normal file
@ -0,0 +1,41 @@
|
||||
import { ethers } from 'ethers';
|
||||
import crypto from "crypto";
|
||||
|
||||
export class EthersService {
|
||||
constructor(fsService, configService) {
|
||||
this.fs = fsService;
|
||||
this.configService = configService;
|
||||
}
|
||||
|
||||
getOrCreateEthKey = () => {
|
||||
const paths = this.configService.getEthFilePaths();
|
||||
|
||||
if (!this.fs.isFile(paths.key)) {
|
||||
this.generateAndSaveKey(paths);
|
||||
}
|
||||
|
||||
const address = this.fs.readFile(paths.address);
|
||||
|
||||
return {
|
||||
privateKeyFilePath: paths.key,
|
||||
addressFilePath: paths.address,
|
||||
address: address,
|
||||
};
|
||||
};
|
||||
|
||||
generateAndSaveKey = async (paths) => {
|
||||
const keys = this.generateKey();
|
||||
this.fs.writeFile(paths.key, keys.key);
|
||||
this.fs.writeFile(paths.address, keys.address);
|
||||
};
|
||||
|
||||
generateKey = () => {
|
||||
var id = crypto.randomBytes(32).toString("hex");
|
||||
var privateKey = "0x" + id;
|
||||
var wallet = new ethers.Wallet(privateKey);
|
||||
return {
|
||||
key: privateKey,
|
||||
address: wallet.address,
|
||||
};
|
||||
};
|
||||
}
|
||||
@ -68,6 +68,10 @@ export class FsService {
|
||||
return JSON.parse(fs.readFileSync(filePath));
|
||||
};
|
||||
|
||||
readFile = (filePath) => {
|
||||
return fs.readFileSync(filePath);
|
||||
};
|
||||
|
||||
writeJsonFile = (filePath, jsonObject) => {
|
||||
fs.writeFileSync(filePath, JSON.stringify(jsonObject));
|
||||
};
|
||||
|
||||
@ -20,20 +20,20 @@ export class ShellService {
|
||||
var child = spawn(cmd, args, {
|
||||
cwd: workingDir,
|
||||
detached: true,
|
||||
stdio: ["ignore", "ignore", "ignore"],
|
||||
//stdio: ["ignore", "ignore", "ignore"],
|
||||
});
|
||||
|
||||
// child.stdout.on("data", (data) => {
|
||||
// console.log(`stdout: ${data}`);
|
||||
// });
|
||||
child.stdout.on("data", (data) => {
|
||||
console.log(`stdout: ${data}`);
|
||||
});
|
||||
|
||||
// child.stderr.on("data", (data) => {
|
||||
// console.error(`stderr: ${data}`);
|
||||
// });
|
||||
child.stderr.on("data", (data) => {
|
||||
console.error(`stderr: ${data}`);
|
||||
});
|
||||
|
||||
// child.on("close", (code) => {
|
||||
// console.log(`child process exited with code ${code}`);
|
||||
// });
|
||||
child.on("close", (code) => {
|
||||
console.log(`child process exited with code ${code}`);
|
||||
});
|
||||
|
||||
child.unref();
|
||||
|
||||
|
||||
@ -91,12 +91,12 @@ export class MainMenu {
|
||||
};
|
||||
|
||||
startCodex = async () => {
|
||||
const spinner = this.ui.createAndStartSpinner("Starting...");
|
||||
// const spinner = this.ui.createAndStartSpinner("Starting...");
|
||||
try {
|
||||
await this.processControl.startCodexProcess();
|
||||
this.ui.stopSpinnerSuccess(spinner);
|
||||
// this.ui.stopSpinnerSuccess(spinner);
|
||||
} catch (exception) {
|
||||
this.ui.stopSpinnerError(spinner);
|
||||
// this.ui.stopSpinnerError(spinner);
|
||||
this.ui.showErrorMessage(`Failed to start Codex. "${exception}"`);
|
||||
}
|
||||
};
|
||||
|
||||
63
src/ui/marketplaceSetup.js
Normal file
63
src/ui/marketplaceSetup.js
Normal file
@ -0,0 +1,63 @@
|
||||
const ethFaucetAddress = "https://faucet-eth.testnet.codex.storage/";
|
||||
const tstFaucetAddress = "https://faucet-tst.testnet.codex.storage/";
|
||||
const discordServerAddress = "https://discord.gg/codex-storage";
|
||||
const botChannelLink =
|
||||
"https://discord.com/channels/895609329053474826/1230785221553819669";
|
||||
|
||||
export class MarketplaceSetup {
|
||||
constructor(uiService, configService, ethersService) {
|
||||
this.ui = uiService;
|
||||
this.ethers = ethersService;
|
||||
this.config = configService.get();
|
||||
}
|
||||
|
||||
runClientWizard = async () => {
|
||||
await this.generateKeyPair();
|
||||
await this.showMintInstructions();
|
||||
return this.isSuccessful;
|
||||
};
|
||||
|
||||
generateKeyPair = async () => {
|
||||
const ehtKey = await this.ethers.getOrCreateEthKey();
|
||||
|
||||
this.ui.showSuccessMessage(
|
||||
"Your Codex node Ethereum account:\n" +
|
||||
`Private key saved to '${ehtKey.privateKeyFilePath}'\n` +
|
||||
`Address saved to '${ehtKey.addressFilePath}'\n` +
|
||||
`Ethereum Account: '${ehtKey.address}'`,
|
||||
);
|
||||
};
|
||||
|
||||
showMintInstructions = async () => {
|
||||
this.ui.showInfoMessage(
|
||||
"Use one of these two methods to receive your testnet tokens:\n\n" +
|
||||
"Faucets:\n" +
|
||||
`Use the Eth faucet: '${ethFaucetAddress}'\n` +
|
||||
`Then use the TST faucet: '${tstFaucetAddress}'\n\n` +
|
||||
"or\n\n" +
|
||||
"Discord bot:\n" +
|
||||
`Join the server: ${discordServerAddress}\n` +
|
||||
`Go to the #BOT channel: ${botChannelLink}\n` +
|
||||
"Use '/set' and '/mint' commands to receive tokens.\n",
|
||||
);
|
||||
|
||||
await this.ui.askMultipleChoice("Take your time.", [
|
||||
{
|
||||
label: "Proceed",
|
||||
action: this.proceed,
|
||||
},
|
||||
{
|
||||
label: "Abort",
|
||||
action: this.abort,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
proceed = async () => {
|
||||
this.isSuccessful = true;
|
||||
};
|
||||
|
||||
abort = async () => {
|
||||
this.isSuccessful = false;
|
||||
};
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user