@axon-chain/axon-skills 0.1.0
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 +42 -0
- package/bin/axon-skills.js +12 -0
- package/lib/catalog.js +54 -0
- package/lib/cli.js +126 -0
- package/lib/install.js +175 -0
- package/package.json +31 -0
- package/skills/axon-mainnet-user/SKILL.md +32 -0
- package/skills/axon-mainnet-user/references/basic-rpc-usage.md +48 -0
- package/skills/axon-mainnet-user/references/mainnet-wallet.md +34 -0
- package/skills/axon-mainnet-user/references/user-troubleshooting.md +61 -0
- package/skills/axon-validator-node/SKILL.md +34 -0
- package/skills/axon-validator-node/references/mainnet-validator.md +149 -0
- package/skills/axon-validator-node/references/validator-security.md +50 -0
- package/skills/axon-validator-node/references/validator-troubleshooting.md +95 -0
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# @axon-chain/axon-skills
|
|
2
|
+
|
|
3
|
+
Install official Axon skills for OpenClaw and the global skills root with `npx`.
|
|
4
|
+
|
|
5
|
+
## Included skills
|
|
6
|
+
|
|
7
|
+
- `axon-validator-node`: Axon mainnet validator and sync-node operations
|
|
8
|
+
- `axon-mainnet-user`: Axon mainnet wallet and public RPC usage
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npx @axon-chain/axon-skills install validator
|
|
14
|
+
npx @axon-chain/axon-skills install user
|
|
15
|
+
npx @axon-chain/axon-skills install all
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Default installs update both roots:
|
|
19
|
+
|
|
20
|
+
- `${OPENCLAW_STATE_DIR:-~/.openclaw}/skills`
|
|
21
|
+
- `${AGENTS_SKILLS_DIR:-${AGENTS_HOME:-~/.agents}/skills}`
|
|
22
|
+
|
|
23
|
+
Repeat installs update existing skill folders in place.
|
|
24
|
+
|
|
25
|
+
## Commands
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx @axon-chain/axon-skills list
|
|
29
|
+
npx @axon-chain/axon-skills install validator
|
|
30
|
+
npx @axon-chain/axon-skills install user
|
|
31
|
+
npx @axon-chain/axon-skills install all
|
|
32
|
+
npx @axon-chain/axon-skills install all --dry-run
|
|
33
|
+
npx @axon-chain/axon-skills install validator --dest /path/to/skills
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Verify
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
openclaw skills list
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
If OpenClaw is not installed on the current machine, inspect the installed skill folders directly under the target roots.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { runCli } from "../lib/cli.js";
|
|
4
|
+
|
|
5
|
+
try {
|
|
6
|
+
const exitCode = await runCli(process.argv.slice(2));
|
|
7
|
+
process.exitCode = exitCode;
|
|
8
|
+
} catch (error) {
|
|
9
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
10
|
+
process.stderr.write(`${message}\n`);
|
|
11
|
+
process.exitCode = 1;
|
|
12
|
+
}
|
package/lib/catalog.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export const SKILL_CATALOG = Object.freeze({
|
|
2
|
+
validator: Object.freeze({
|
|
3
|
+
key: "validator",
|
|
4
|
+
packageDir: "axon-validator-node",
|
|
5
|
+
skillName: "axon-validator-node",
|
|
6
|
+
summary: "Official Axon mainnet validator and node-operator workflow."
|
|
7
|
+
}),
|
|
8
|
+
user: Object.freeze({
|
|
9
|
+
key: "user",
|
|
10
|
+
packageDir: "axon-mainnet-user",
|
|
11
|
+
skillName: "axon-mainnet-user",
|
|
12
|
+
summary: "Ordinary Axon mainnet wallet and chain-usage workflow."
|
|
13
|
+
})
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export const LIST_TARGETS = Object.freeze([
|
|
17
|
+
SKILL_CATALOG.validator,
|
|
18
|
+
SKILL_CATALOG.user,
|
|
19
|
+
Object.freeze({
|
|
20
|
+
key: "all",
|
|
21
|
+
packageDir: null,
|
|
22
|
+
skillName: null,
|
|
23
|
+
summary: "Install both skills."
|
|
24
|
+
})
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
export function resolveInstallTargets(requested) {
|
|
28
|
+
const normalized = String(requested || "").trim().toLowerCase();
|
|
29
|
+
|
|
30
|
+
if (!normalized) {
|
|
31
|
+
throw new Error("Missing install target. Use one of: validator, user, all.");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (normalized === "all") {
|
|
35
|
+
return [SKILL_CATALOG.validator, SKILL_CATALOG.user];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for (const entry of Object.values(SKILL_CATALOG)) {
|
|
39
|
+
if (normalized === entry.key || normalized === entry.skillName) {
|
|
40
|
+
return [entry];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
throw new Error(`Unknown install target "${requested}". Use one of: validator, user, all.`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function formatList() {
|
|
48
|
+
return [
|
|
49
|
+
"Available install targets:",
|
|
50
|
+
"validator\tOfficial Axon mainnet validator and node-operator workflow",
|
|
51
|
+
"user\tOrdinary Axon mainnet wallet and chain-usage workflow",
|
|
52
|
+
"all\tInstall both skills"
|
|
53
|
+
].join("\n");
|
|
54
|
+
}
|
package/lib/cli.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { formatList } from "./catalog.js";
|
|
2
|
+
import { installSkills } from "./install.js";
|
|
3
|
+
|
|
4
|
+
function buildHelpText() {
|
|
5
|
+
return [
|
|
6
|
+
"Usage:",
|
|
7
|
+
" axon-skills list",
|
|
8
|
+
" axon-skills install <validator|user|all> [--dest <dir>] [--force] [--dry-run]",
|
|
9
|
+
"",
|
|
10
|
+
"Examples:",
|
|
11
|
+
" npx @axon-chain/axon-skills list",
|
|
12
|
+
" npx @axon-chain/axon-skills install validator",
|
|
13
|
+
" npx @axon-chain/axon-skills install all --force",
|
|
14
|
+
"",
|
|
15
|
+
"Default install roots:",
|
|
16
|
+
" ${OPENCLAW_STATE_DIR:-~/.openclaw}/skills",
|
|
17
|
+
" ${AGENTS_SKILLS_DIR:-${AGENTS_HOME:-~/.agents}/skills}",
|
|
18
|
+
"",
|
|
19
|
+
"Repeat installs update existing skill folders in place."
|
|
20
|
+
].join("\n");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function parseInstallArgs(args) {
|
|
24
|
+
const parsed = {
|
|
25
|
+
requested: args[0],
|
|
26
|
+
destRoot: null,
|
|
27
|
+
force: false,
|
|
28
|
+
dryRun: false
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
for (let index = 1; index < args.length; index += 1) {
|
|
32
|
+
const current = args[index];
|
|
33
|
+
|
|
34
|
+
if (current === "--force") {
|
|
35
|
+
parsed.force = true;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (current === "--dry-run") {
|
|
40
|
+
parsed.dryRun = true;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (current === "--dest") {
|
|
45
|
+
index += 1;
|
|
46
|
+
if (index >= args.length) {
|
|
47
|
+
throw new Error("Missing value after --dest.");
|
|
48
|
+
}
|
|
49
|
+
parsed.destRoot = args[index];
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (current === "--help" || current === "-h") {
|
|
54
|
+
parsed.help = true;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
throw new Error(`Unknown option: ${current}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return parsed;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function write(stream, text) {
|
|
65
|
+
stream.write(`${text}\n`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function runCli(
|
|
69
|
+
argv,
|
|
70
|
+
options = {}
|
|
71
|
+
) {
|
|
72
|
+
const stdout = options.stdout ?? process.stdout;
|
|
73
|
+
const stderr = options.stderr ?? process.stderr;
|
|
74
|
+
const env = options.env ?? process.env;
|
|
75
|
+
const homedir = options.homedir;
|
|
76
|
+
const packageRoot = options.packageRoot;
|
|
77
|
+
|
|
78
|
+
const args = Array.isArray(argv) ? argv : [];
|
|
79
|
+
const command = args[0];
|
|
80
|
+
|
|
81
|
+
if (!command || command === "--help" || command === "-h" || command === "help") {
|
|
82
|
+
write(stdout, buildHelpText());
|
|
83
|
+
return 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (command === "list") {
|
|
87
|
+
write(stdout, formatList());
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (command === "install") {
|
|
92
|
+
const parsed = parseInstallArgs(args.slice(1));
|
|
93
|
+
if (parsed.help) {
|
|
94
|
+
write(stdout, buildHelpText());
|
|
95
|
+
return 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const result = await installSkills({
|
|
99
|
+
requested: parsed.requested,
|
|
100
|
+
destRoot: parsed.destRoot,
|
|
101
|
+
force: parsed.force,
|
|
102
|
+
dryRun: parsed.dryRun,
|
|
103
|
+
env,
|
|
104
|
+
homedir,
|
|
105
|
+
packageRoot
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const actionLabel = result.dryRun ? "Planned installs:" : "Installed skills:";
|
|
109
|
+
write(stdout, actionLabel);
|
|
110
|
+
for (const item of result.installed) {
|
|
111
|
+
write(stdout, `- ${item.skillName} -> ${item.finalDir}`);
|
|
112
|
+
}
|
|
113
|
+
write(stdout, "");
|
|
114
|
+
write(stdout, "Install roots:");
|
|
115
|
+
for (const root of result.destinationRoots) {
|
|
116
|
+
write(stdout, `- ${root}`);
|
|
117
|
+
}
|
|
118
|
+
write(stdout, "Verify with: openclaw skills list");
|
|
119
|
+
write(stdout, "Global skills live under the .agents skills root.");
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
write(stderr, `Unknown command: ${command}`);
|
|
124
|
+
write(stderr, buildHelpText());
|
|
125
|
+
return 1;
|
|
126
|
+
}
|
package/lib/install.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
import { resolveInstallTargets } from "./catalog.js";
|
|
7
|
+
|
|
8
|
+
export function resolvePackageRoot(metaUrl = import.meta.url) {
|
|
9
|
+
return path.resolve(path.dirname(fileURLToPath(metaUrl)), "..");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function resolveHomeDir(env = process.env, homedir = os.homedir) {
|
|
13
|
+
const explicit = env.HOME?.trim() || env.USERPROFILE?.trim();
|
|
14
|
+
if (explicit) {
|
|
15
|
+
return path.resolve(explicit);
|
|
16
|
+
}
|
|
17
|
+
return path.resolve(homedir());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function expandUserPath(input, env = process.env, homedir = os.homedir) {
|
|
21
|
+
const raw = String(input || "").trim();
|
|
22
|
+
if (!raw) {
|
|
23
|
+
throw new Error("Destination path must not be empty.");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (raw === "~") {
|
|
27
|
+
return resolveHomeDir(env, homedir);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (raw.startsWith("~/")) {
|
|
31
|
+
return path.join(resolveHomeDir(env, homedir), raw.slice(2));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return path.resolve(raw);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function resolveDefaultSkillsRoot(options = {}) {
|
|
38
|
+
const env = options.env ?? process.env;
|
|
39
|
+
const homedir = options.homedir ?? os.homedir;
|
|
40
|
+
const stateDir = env.OPENCLAW_STATE_DIR?.trim()
|
|
41
|
+
? expandUserPath(env.OPENCLAW_STATE_DIR, env, homedir)
|
|
42
|
+
: path.join(resolveHomeDir(env, homedir), ".openclaw");
|
|
43
|
+
return path.join(stateDir, "skills");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function resolveDefaultGlobalSkillsRoot(options = {}) {
|
|
47
|
+
const env = options.env ?? process.env;
|
|
48
|
+
const homedir = options.homedir ?? os.homedir;
|
|
49
|
+
|
|
50
|
+
if (env.AGENTS_SKILLS_DIR?.trim()) {
|
|
51
|
+
return expandUserPath(env.AGENTS_SKILLS_DIR, env, homedir);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (env.AGENTS_HOME?.trim()) {
|
|
55
|
+
return path.join(expandUserPath(env.AGENTS_HOME, env, homedir), "skills");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return path.join(resolveHomeDir(env, homedir), ".agents", "skills");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function resolveInstallRoots(options = {}) {
|
|
62
|
+
if (options.destRoot) {
|
|
63
|
+
return [expandUserPath(options.destRoot, options.env, options.homedir)];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const roots = [
|
|
67
|
+
resolveDefaultSkillsRoot(options),
|
|
68
|
+
resolveDefaultGlobalSkillsRoot(options)
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
return [...new Set(roots.map((root) => path.resolve(root)))];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function pathExists(targetPath) {
|
|
75
|
+
try {
|
|
76
|
+
await fs.access(targetPath);
|
|
77
|
+
return true;
|
|
78
|
+
} catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function randomSuffix() {
|
|
84
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function copyDirectoryAtomic({ sourceDir, finalDir, replaceExisting }) {
|
|
88
|
+
const destinationRoot = path.dirname(finalDir);
|
|
89
|
+
const destinationName = path.basename(finalDir);
|
|
90
|
+
|
|
91
|
+
await fs.mkdir(destinationRoot, { recursive: true });
|
|
92
|
+
|
|
93
|
+
const tmpRoot = await fs.mkdtemp(path.join(destinationRoot, `.tmp-${destinationName}-`));
|
|
94
|
+
const stagedDir = path.join(tmpRoot, destinationName);
|
|
95
|
+
let backupDir = null;
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
await fs.cp(sourceDir, stagedDir, {
|
|
99
|
+
recursive: true,
|
|
100
|
+
force: false,
|
|
101
|
+
errorOnExist: true
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const destinationExists = await pathExists(finalDir);
|
|
105
|
+
if (destinationExists) {
|
|
106
|
+
if (!replaceExisting) {
|
|
107
|
+
throw new Error(`Destination already exists: ${finalDir}. Re-run with --force to replace it.`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
backupDir = path.join(destinationRoot, `.${destinationName}.backup-${randomSuffix()}`);
|
|
111
|
+
await fs.rename(finalDir, backupDir);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
await fs.rename(stagedDir, finalDir);
|
|
115
|
+
|
|
116
|
+
if (backupDir) {
|
|
117
|
+
await fs.rm(backupDir, { recursive: true, force: true });
|
|
118
|
+
backupDir = null;
|
|
119
|
+
}
|
|
120
|
+
} catch (error) {
|
|
121
|
+
if (backupDir && !(await pathExists(finalDir)) && (await pathExists(backupDir))) {
|
|
122
|
+
await fs.rename(backupDir, finalDir).catch(() => undefined);
|
|
123
|
+
}
|
|
124
|
+
throw error;
|
|
125
|
+
} finally {
|
|
126
|
+
await fs.rm(tmpRoot, { recursive: true, force: true }).catch(() => undefined);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function installSkills(options) {
|
|
131
|
+
const env = options.env ?? process.env;
|
|
132
|
+
const homedir = options.homedir ?? os.homedir;
|
|
133
|
+
const packageRoot = options.packageRoot ?? resolvePackageRoot(import.meta.url);
|
|
134
|
+
const replaceExisting = options.replaceExisting ?? true;
|
|
135
|
+
const dryRun = Boolean(options.dryRun);
|
|
136
|
+
const destinationRoots = resolveInstallRoots({
|
|
137
|
+
destRoot: options.destRoot,
|
|
138
|
+
env,
|
|
139
|
+
homedir
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const targets = resolveInstallTargets(options.requested);
|
|
143
|
+
const installed = [];
|
|
144
|
+
|
|
145
|
+
for (const target of targets) {
|
|
146
|
+
const sourceDir = path.join(packageRoot, "skills", target.packageDir);
|
|
147
|
+
|
|
148
|
+
if (!(await pathExists(sourceDir))) {
|
|
149
|
+
throw new Error(`Missing packaged skill source: ${sourceDir}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
for (const destinationRoot of destinationRoots) {
|
|
153
|
+
const finalDir = path.join(destinationRoot, target.packageDir);
|
|
154
|
+
|
|
155
|
+
if (!dryRun) {
|
|
156
|
+
await copyDirectoryAtomic({ sourceDir, finalDir, replaceExisting });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
installed.push({
|
|
160
|
+
key: target.key,
|
|
161
|
+
skillName: target.skillName,
|
|
162
|
+
sourceDir,
|
|
163
|
+
finalDir,
|
|
164
|
+
destinationRoot
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
destinationRoots,
|
|
171
|
+
dryRun,
|
|
172
|
+
replaceExisting,
|
|
173
|
+
installed
|
|
174
|
+
};
|
|
175
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@axon-chain/axon-skills",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "OpenClaw-installable Axon mainnet skills for validator operators and ordinary users.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"axon-skills": "./bin/axon-skills.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin",
|
|
11
|
+
"lib",
|
|
12
|
+
"skills"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "node --test"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18"
|
|
19
|
+
},
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"axon",
|
|
25
|
+
"openclaw",
|
|
26
|
+
"skills",
|
|
27
|
+
"validator",
|
|
28
|
+
"wallet"
|
|
29
|
+
],
|
|
30
|
+
"license": "Apache-2.0"
|
|
31
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: axon-mainnet-user
|
|
3
|
+
description: Help ordinary users connect a wallet to Axon mainnet, check balances, inspect the chain through the public RPC, and send basic transactions. Use for MetaMask setup, AXON balance checks, public JSON-RPC reads, and wallet troubleshooting. Do not use for validator, SDK, or contract-deployment workflows.
|
|
4
|
+
metadata: {"openclaw":{"skillKey":"axon-mainnet-user","emoji":"🧭"}}
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Axon Mainnet User
|
|
8
|
+
|
|
9
|
+
Use this skill when the user is an ordinary Axon mainnet participant who needs wallet-first help.
|
|
10
|
+
|
|
11
|
+
Do not use this skill for validator nodes, sync nodes, SDK integration, or contract deployment. If the user wants to run a node or become a validator, switch to `axon-validator-node`.
|
|
12
|
+
|
|
13
|
+
## Non-negotiable rules
|
|
14
|
+
|
|
15
|
+
- Wallet-facing chain ID is `8210`.
|
|
16
|
+
- Public RPC is `https://mainnet-rpc.axonchain.ai/`.
|
|
17
|
+
- `AXON` is the gas token.
|
|
18
|
+
- `axon_8210-1` is the Cosmos node chain ID and should not be used as the MetaMask chain ID.
|
|
19
|
+
- Keep this skill wallet-first. Do not turn it into a validator workflow.
|
|
20
|
+
|
|
21
|
+
## Workflow routing
|
|
22
|
+
|
|
23
|
+
- For wallet onboarding and network fields, read `references/mainnet-wallet.md`.
|
|
24
|
+
- For direct public JSON-RPC reads, read `references/basic-rpc-usage.md`.
|
|
25
|
+
- For wrong-network, gas, or RPC errors, read `references/user-troubleshooting.md`.
|
|
26
|
+
|
|
27
|
+
## Response policy
|
|
28
|
+
|
|
29
|
+
- Give wallet configuration steps and public RPC examples.
|
|
30
|
+
- Prefer public RPC checks over node-operator commands.
|
|
31
|
+
- Keep advanced builder or validator topics out of scope in v1.
|
|
32
|
+
- If the user drifts into node operation, redirect to `axon-validator-node`.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Basic Public JSON-RPC Usage
|
|
2
|
+
|
|
3
|
+
Use the public endpoint:
|
|
4
|
+
|
|
5
|
+
- `https://mainnet-rpc.axonchain.ai/`
|
|
6
|
+
|
|
7
|
+
## Read the wallet-facing chain ID
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
curl -s https://mainnet-rpc.axonchain.ai/ \
|
|
11
|
+
-H 'content-type: application/json' \
|
|
12
|
+
--data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Expected result:
|
|
16
|
+
|
|
17
|
+
- `0x2012`, which is decimal `8210`
|
|
18
|
+
|
|
19
|
+
## Read the latest block number
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
curl -s https://mainnet-rpc.axonchain.ai/ \
|
|
23
|
+
-H 'content-type: application/json' \
|
|
24
|
+
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Read an address balance
|
|
28
|
+
|
|
29
|
+
Replace `<EVM_ADDRESS>` with a `0x...` address:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
curl -s https://mainnet-rpc.axonchain.ai/ \
|
|
33
|
+
-H 'content-type: application/json' \
|
|
34
|
+
--data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["<EVM_ADDRESS>","latest"],"id":1}'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The balance is returned in hex wei-style units for the EVM side.
|
|
38
|
+
|
|
39
|
+
## When to stop and redirect
|
|
40
|
+
|
|
41
|
+
If the user asks for:
|
|
42
|
+
|
|
43
|
+
- validator registration
|
|
44
|
+
- node sync
|
|
45
|
+
- P2P bootstrap
|
|
46
|
+
- the validator registration transaction
|
|
47
|
+
|
|
48
|
+
Switch to `axon-validator-node`.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Axon Mainnet Wallet Setup
|
|
2
|
+
|
|
3
|
+
## MetaMask network fields
|
|
4
|
+
|
|
5
|
+
- Network name: `Axon`
|
|
6
|
+
- RPC URL: `https://mainnet-rpc.axonchain.ai/`
|
|
7
|
+
- Chain ID: `8210`
|
|
8
|
+
- Currency symbol: `AXON`
|
|
9
|
+
|
|
10
|
+
## Important distinction
|
|
11
|
+
|
|
12
|
+
- Use `8210` in MetaMask and other EVM wallets.
|
|
13
|
+
- `axon_8210-1` is for Cosmos node operators, not wallet setup.
|
|
14
|
+
|
|
15
|
+
## What users can do with this setup
|
|
16
|
+
|
|
17
|
+
- view AXON balances
|
|
18
|
+
- send AXON transfers
|
|
19
|
+
- connect EVM dapps that support Axon mainnet
|
|
20
|
+
- inspect chain state through the public RPC
|
|
21
|
+
|
|
22
|
+
## Before sending transactions
|
|
23
|
+
|
|
24
|
+
- confirm the wallet is on chain ID `8210`
|
|
25
|
+
- confirm the wallet has enough AXON for gas
|
|
26
|
+
- confirm the recipient address format matches the tool being used
|
|
27
|
+
|
|
28
|
+
## Public RPC
|
|
29
|
+
|
|
30
|
+
Use only:
|
|
31
|
+
|
|
32
|
+
- `https://mainnet-rpc.axonchain.ai/`
|
|
33
|
+
|
|
34
|
+
Do not send ordinary wallet users to validator bootstrap peers or node-only chain IDs.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# User Troubleshooting
|
|
2
|
+
|
|
3
|
+
## Wrong network in wallet
|
|
4
|
+
|
|
5
|
+
Symptoms:
|
|
6
|
+
|
|
7
|
+
- transactions fail immediately
|
|
8
|
+
- wallet shows a different chain
|
|
9
|
+
- dapp requests a chain switch
|
|
10
|
+
|
|
11
|
+
Fix:
|
|
12
|
+
|
|
13
|
+
- set MetaMask chain ID to `8210`
|
|
14
|
+
- keep RPC URL at `https://mainnet-rpc.axonchain.ai/`
|
|
15
|
+
- do not use `axon_8210-1` in wallet configuration
|
|
16
|
+
|
|
17
|
+
## No gas for a transaction
|
|
18
|
+
|
|
19
|
+
Symptoms:
|
|
20
|
+
|
|
21
|
+
- wallet says insufficient funds
|
|
22
|
+
- transfer cannot be signed or broadcast
|
|
23
|
+
|
|
24
|
+
Fix:
|
|
25
|
+
|
|
26
|
+
- fund the wallet with `AXON`
|
|
27
|
+
- leave some AXON unspent for gas
|
|
28
|
+
|
|
29
|
+
## Public RPC seems unavailable
|
|
30
|
+
|
|
31
|
+
Checks:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
curl -s https://mainnet-rpc.axonchain.ai/ \
|
|
35
|
+
-H 'content-type: application/json' \
|
|
36
|
+
--data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
If this fails:
|
|
40
|
+
|
|
41
|
+
- retry later
|
|
42
|
+
- confirm the wallet or app is still pointed at the public Axon RPC
|
|
43
|
+
- avoid switching the user into validator-only node endpoints
|
|
44
|
+
|
|
45
|
+
## Chain ID confusion
|
|
46
|
+
|
|
47
|
+
- `8210` is the EVM wallet chain ID
|
|
48
|
+
- `axon_8210-1` is the Cosmos node chain ID
|
|
49
|
+
|
|
50
|
+
Correct the configuration before moving on.
|
|
51
|
+
|
|
52
|
+
## Out-of-scope requests
|
|
53
|
+
|
|
54
|
+
If the user asks to:
|
|
55
|
+
|
|
56
|
+
- run a validator
|
|
57
|
+
- bootstrap a node
|
|
58
|
+
- troubleshoot peers
|
|
59
|
+
- submit the validator registration transaction
|
|
60
|
+
|
|
61
|
+
Switch to `axon-validator-node`.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: axon-validator-node
|
|
3
|
+
description: Help users bootstrap, fund, sync, create, verify, and operate an Axon mainnet validator with the official script-based workflow. Use for validator onboarding, sync checks, create-validator troubleshooting, fee issues, peer issues, key handling, and ongoing node operations.
|
|
4
|
+
metadata: {"openclaw":{"skillKey":"axon-validator-node","emoji":"⛓️"}}
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Axon Validator Node
|
|
8
|
+
|
|
9
|
+
Use this skill when the user wants to run, recover, verify, or troubleshoot an Axon mainnet validator or sync node.
|
|
10
|
+
|
|
11
|
+
Do not use this skill for ordinary wallet usage. If the user only needs MetaMask, balances, transfers, or public RPC reads, switch to `axon-mainnet-user`.
|
|
12
|
+
|
|
13
|
+
## Non-negotiable rules
|
|
14
|
+
|
|
15
|
+
- Mainnet node identity uses Cosmos chain ID `axon_8210-1`.
|
|
16
|
+
- Wallet-facing EVM tooling uses chain ID `8210`.
|
|
17
|
+
- Prefer the official script workflow from the `axon-chain/axon` repository.
|
|
18
|
+
- Do not suggest a manual `axond init` plus manual genesis-fetch flow unless the user explicitly needs low-level debugging.
|
|
19
|
+
- Do not tell the user to submit `create-validator` until the validator account is funded, local RPC is up, and `catching_up` is `false`.
|
|
20
|
+
- Treat the mnemonic, `KEYRING_PASSWORD_FILE`, validator key files, and any signing key material as sensitive.
|
|
21
|
+
- Current practical transaction default: use `GAS_PRICES=1000000000aaxon` unless the local helper already sets a non-zero gas price.
|
|
22
|
+
|
|
23
|
+
## Workflow routing
|
|
24
|
+
|
|
25
|
+
- For first-time setup, rebuilds, or exact shell commands, read `references/mainnet-validator.md`.
|
|
26
|
+
- For backups, exposed ports, double-sign prevention, and key hygiene, read `references/validator-security.md`.
|
|
27
|
+
- For fee failures, peer failures, sync lag, or missing validator status, read `references/validator-troubleshooting.md`.
|
|
28
|
+
|
|
29
|
+
## Response policy
|
|
30
|
+
|
|
31
|
+
- Give exact commands when operating the node.
|
|
32
|
+
- Keep `axon_8210-1` and `8210` clearly separated in the response.
|
|
33
|
+
- Prefer `start_validator_node.sh` and `start_sync_node.sh` over lower-level alternatives.
|
|
34
|
+
- If the user asks to proceed with `create-validator`, explicitly check sync state, funding state, and local RPC readiness first.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Axon Mainnet Validator Workflow
|
|
2
|
+
|
|
3
|
+
## Network constants
|
|
4
|
+
|
|
5
|
+
- Cosmos chain ID: `axon_8210-1`
|
|
6
|
+
- EVM chain ID: `8210`
|
|
7
|
+
- Public wallet RPC: `https://mainnet-rpc.axonchain.ai/`
|
|
8
|
+
- Bootstrap peer: `e47ec82a1d08a371e3c235e6554496be2f114eae@mainnet-node.axonchain.ai:26656`
|
|
9
|
+
- Genesis file: `https://raw.githubusercontent.com/axon-chain/axon/main/docs/mainnet/genesis.json`
|
|
10
|
+
- Bootstrap peers file: `https://raw.githubusercontent.com/axon-chain/axon/main/docs/mainnet/bootstrap_peers.txt`
|
|
11
|
+
|
|
12
|
+
## Preferred working directory
|
|
13
|
+
|
|
14
|
+
Use `/opt/axon-node` for bare metal and containerized installs.
|
|
15
|
+
|
|
16
|
+
## Bootstrap the validator workspace
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
mkdir -p /opt/axon-node
|
|
20
|
+
cd /opt/axon-node
|
|
21
|
+
|
|
22
|
+
curl -fsSLo start_validator_node.sh https://raw.githubusercontent.com/axon-chain/axon/main/scripts/start_validator_node.sh
|
|
23
|
+
curl -fsSLo start_sync_node.sh https://raw.githubusercontent.com/axon-chain/axon/main/scripts/start_sync_node.sh
|
|
24
|
+
curl -fsSLo genesis.json https://raw.githubusercontent.com/axon-chain/axon/main/docs/mainnet/genesis.json
|
|
25
|
+
curl -fsSLo bootstrap_peers.txt https://raw.githubusercontent.com/axon-chain/axon/main/docs/mainnet/bootstrap_peers.txt
|
|
26
|
+
chmod 0755 start_validator_node.sh start_sync_node.sh
|
|
27
|
+
printf 'replace-with-a-strong-passphrase\n' > keyring.pass
|
|
28
|
+
chmod 0600 keyring.pass
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Optional: pre-download `axond`
|
|
32
|
+
|
|
33
|
+
For Linux AMD64:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
curl -fsSLo axond https://github.com/axon-chain/axon/releases/latest/download/axond_linux_amd64
|
|
37
|
+
curl -fsSLo axond.sha256 https://github.com/axon-chain/axon/releases/latest/download/axond_linux_amd64.sha256
|
|
38
|
+
echo "$(cat axond.sha256) axond" | sha256sum -c -
|
|
39
|
+
chmod 0755 axond
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Adjust the release asset URL for a different platform if needed.
|
|
43
|
+
|
|
44
|
+
## Initialize the validator account
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
cd /opt/axon-node
|
|
48
|
+
KEYRING_PASSWORD_FILE=/opt/axon-node/keyring.pass ./start_validator_node.sh init
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
What this creates:
|
|
52
|
+
|
|
53
|
+
- `data/validator.address`
|
|
54
|
+
- `data/validator.valoper`
|
|
55
|
+
- `data/validator.consensus_pubkey.json`
|
|
56
|
+
- `data/peer_info.txt`
|
|
57
|
+
|
|
58
|
+
The mnemonic is only printed once when a new account is generated.
|
|
59
|
+
|
|
60
|
+
## Start the node
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
cd /opt/axon-node
|
|
64
|
+
KEYRING_PASSWORD_FILE=/opt/axon-node/keyring.pass ./start_validator_node.sh start
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Default ports:
|
|
68
|
+
|
|
69
|
+
- P2P: `26656`
|
|
70
|
+
- CometBFT RPC: `26657`
|
|
71
|
+
- JSON-RPC: `8545`
|
|
72
|
+
- JSON-RPC WS: `8546`
|
|
73
|
+
- REST API: `1317`
|
|
74
|
+
- gRPC: `9090`
|
|
75
|
+
|
|
76
|
+
## Verify sync before any validator transaction
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
curl -s http://127.0.0.1:26657/status | jq '.result.sync_info'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Wait until:
|
|
83
|
+
|
|
84
|
+
- latest block height is moving
|
|
85
|
+
- peer count is non-zero
|
|
86
|
+
- `catching_up` becomes `false`
|
|
87
|
+
|
|
88
|
+
Do not submit `create-validator` before `catching_up=false`.
|
|
89
|
+
|
|
90
|
+
## Fund the validator account
|
|
91
|
+
|
|
92
|
+
Read the address from `data/validator.address` and transfer AXON to it.
|
|
93
|
+
|
|
94
|
+
Example:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
cat /opt/axon-node/data/validator.address
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Submit `create-validator`
|
|
101
|
+
|
|
102
|
+
Use a non-zero gas price. Current safe default:
|
|
103
|
+
|
|
104
|
+
- `GAS_PRICES=1000000000aaxon`
|
|
105
|
+
|
|
106
|
+
Command:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
cd /opt/axon-node
|
|
110
|
+
KEYRING_PASSWORD_FILE=/opt/axon-node/keyring.pass \
|
|
111
|
+
COMETBFT_RPC=http://127.0.0.1:26657 \
|
|
112
|
+
GAS_PRICES=1000000000aaxon \
|
|
113
|
+
./start_validator_node.sh create-validator
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Notes:
|
|
117
|
+
|
|
118
|
+
- The official script default self-bond is `100 AXON` unless `VALIDATOR_STAKE` is overridden.
|
|
119
|
+
- The node must already be running locally when you point `COMETBFT_RPC` at `http://127.0.0.1:26657`.
|
|
120
|
+
|
|
121
|
+
## Verify validator status
|
|
122
|
+
|
|
123
|
+
Query the transaction if needed:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
curl -s http://127.0.0.1:1317/cosmos/tx/v1beta1/txs/<TX_HASH>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Query the validator:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
axond query staking validator "$(cat /opt/axon-node/data/validator.valoper)" \
|
|
133
|
+
--node http://127.0.0.1:26657 \
|
|
134
|
+
--output json
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Useful success indicators:
|
|
138
|
+
|
|
139
|
+
- validator exists on chain
|
|
140
|
+
- validator has tokens bonded
|
|
141
|
+
- status is `BOND_STATUS_BONDED` or the expected waiting-state for the network
|
|
142
|
+
|
|
143
|
+
## Ongoing operations
|
|
144
|
+
|
|
145
|
+
- Logs: `tail -f /opt/axon-node/data/node.log`
|
|
146
|
+
- Sync info: `curl -s http://127.0.0.1:26657/status | jq '.result.sync_info'`
|
|
147
|
+
- Validator address: `cat /opt/axon-node/data/validator.valoper`
|
|
148
|
+
- Bootstrap peers should stay aligned with the official repository
|
|
149
|
+
- Set `P2P_EXTERNAL_ADDRESS=host:26656` only on a public inbound validator
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Validator Security
|
|
2
|
+
|
|
3
|
+
## Sensitive material
|
|
4
|
+
|
|
5
|
+
Treat these as secrets:
|
|
6
|
+
|
|
7
|
+
- mnemonic
|
|
8
|
+
- `keyring.pass`
|
|
9
|
+
- `data/node/config/priv_validator_key.json`
|
|
10
|
+
- any imported mnemonic source file
|
|
11
|
+
|
|
12
|
+
Do not paste them into chat, public issues, or shared docs.
|
|
13
|
+
|
|
14
|
+
## Backups
|
|
15
|
+
|
|
16
|
+
Back up at least:
|
|
17
|
+
|
|
18
|
+
- `/opt/axon-node/keyring.pass`
|
|
19
|
+
- `/opt/axon-node/data/validator.address`
|
|
20
|
+
- `/opt/axon-node/data/validator.valoper`
|
|
21
|
+
- `/opt/axon-node/data/node/config/priv_validator_key.json`
|
|
22
|
+
- the mnemonic if the validator account was generated locally
|
|
23
|
+
|
|
24
|
+
Keep backups offline when possible.
|
|
25
|
+
|
|
26
|
+
## Double-sign prevention
|
|
27
|
+
|
|
28
|
+
- Never run the same validator signing key on two live machines at the same time.
|
|
29
|
+
- If moving the validator, stop the old node completely before starting the new one.
|
|
30
|
+
- Treat failover automation as high risk unless the handoff is explicit and verified.
|
|
31
|
+
|
|
32
|
+
## RPC and network exposure
|
|
33
|
+
|
|
34
|
+
- Leave `P2P_EXTERNAL_ADDRESS` unset on ordinary outbound-only nodes.
|
|
35
|
+
- Only expose `26656` when the node should accept inbound peers.
|
|
36
|
+
- Keep admin access to the host locked down.
|
|
37
|
+
- Do not expose key material over network shares.
|
|
38
|
+
|
|
39
|
+
## Operational hygiene
|
|
40
|
+
|
|
41
|
+
- Use a dedicated machine or VM for validator duties.
|
|
42
|
+
- Keep system time correct and disks healthy.
|
|
43
|
+
- Monitor logs for downtime and peer churn.
|
|
44
|
+
- Review upgrade notes before restarting production validators.
|
|
45
|
+
|
|
46
|
+
## Chain ID hygiene
|
|
47
|
+
|
|
48
|
+
- Use `axon_8210-1` for node commands and validator operations.
|
|
49
|
+
- Use `8210` for wallet-facing EVM tools only.
|
|
50
|
+
- Do not mix them.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Validator Troubleshooting
|
|
2
|
+
|
|
3
|
+
## `fee not provided` or `insufficient fee`
|
|
4
|
+
|
|
5
|
+
Symptom:
|
|
6
|
+
|
|
7
|
+
- `create-validator` returns `fee not provided`
|
|
8
|
+
- raw log mentions insufficient fee
|
|
9
|
+
|
|
10
|
+
Fix:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
cd /opt/axon-node
|
|
14
|
+
KEYRING_PASSWORD_FILE=/opt/axon-node/keyring.pass \
|
|
15
|
+
COMETBFT_RPC=http://127.0.0.1:26657 \
|
|
16
|
+
GAS_PRICES=1000000000aaxon \
|
|
17
|
+
./start_validator_node.sh create-validator
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Use a non-zero gas price until the local helper script already defaults to one.
|
|
21
|
+
|
|
22
|
+
## `catching_up` stays `true`
|
|
23
|
+
|
|
24
|
+
Checks:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
curl -s http://127.0.0.1:26657/status | jq '.result.sync_info'
|
|
28
|
+
cat /opt/axon-node/data/peer_info.txt
|
|
29
|
+
tail -f /opt/axon-node/data/node.log
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Actions:
|
|
33
|
+
|
|
34
|
+
- confirm the bootstrap peers file matches the official repository
|
|
35
|
+
- confirm the node has reachable peers
|
|
36
|
+
- confirm disk and network are not constrained
|
|
37
|
+
- wait before any validator transaction
|
|
38
|
+
|
|
39
|
+
Do not submit `create-validator` until `catching_up=false`.
|
|
40
|
+
|
|
41
|
+
## Missing `KEYRING_PASSWORD_FILE`
|
|
42
|
+
|
|
43
|
+
Symptoms:
|
|
44
|
+
|
|
45
|
+
- the script refuses to run
|
|
46
|
+
- the keyring password file is missing or empty
|
|
47
|
+
|
|
48
|
+
Fix:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
ls -l /opt/axon-node/keyring.pass
|
|
52
|
+
cat /opt/axon-node/keyring.pass
|
|
53
|
+
chmod 0600 /opt/axon-node/keyring.pass
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Use a non-empty local passphrase file and point `KEYRING_PASSWORD_FILE` at it.
|
|
57
|
+
|
|
58
|
+
## Validator not found after submission
|
|
59
|
+
|
|
60
|
+
Checks:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
curl -s http://127.0.0.1:1317/cosmos/tx/v1beta1/txs/<TX_HASH>
|
|
64
|
+
axond query staking validator "$(cat /opt/axon-node/data/validator.valoper)" \
|
|
65
|
+
--node http://127.0.0.1:26657 \
|
|
66
|
+
--output json
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Possible causes:
|
|
70
|
+
|
|
71
|
+
- the transaction never landed
|
|
72
|
+
- the validator transaction was rejected
|
|
73
|
+
- the wrong RPC endpoint was queried
|
|
74
|
+
|
|
75
|
+
## No peers or unstable peers
|
|
76
|
+
|
|
77
|
+
Checks:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
tail -f /opt/axon-node/data/node.log
|
|
81
|
+
cat /opt/axon-node/bootstrap_peers.txt
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Actions:
|
|
85
|
+
|
|
86
|
+
- re-sync the official bootstrap peers file
|
|
87
|
+
- verify outbound connectivity to `mainnet-node.axonchain.ai:26656`
|
|
88
|
+
- only set `P2P_EXTERNAL_ADDRESS` when the host is publicly reachable
|
|
89
|
+
|
|
90
|
+
## Chain ID confusion
|
|
91
|
+
|
|
92
|
+
- `axon_8210-1` is the Cosmos chain ID for validator and node commands
|
|
93
|
+
- `8210` is the EVM chain ID for wallets
|
|
94
|
+
|
|
95
|
+
If the user mixes them, correct the command before continuing.
|