@akta/dao-cli 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 +137 -0
- package/images/dao_view_styled.png +0 -0
- package/images/fees_view_styled.png +0 -0
- package/images/proposals_view_styled.png +0 -0
- package/images/wallet_view_styled.png +0 -0
- package/install.sh +19 -0
- package/package.json +33 -0
- package/src/commands/info.ts +33 -0
- package/src/commands/proposals.ts +133 -0
- package/src/commands/state.ts +167 -0
- package/src/commands/wallet.ts +356 -0
- package/src/formatting.ts +659 -0
- package/src/index.ts +188 -0
- package/src/output.ts +232 -0
- package/src/sdk.ts +37 -0
- package/src/theme.ts +73 -0
- package/src/tui/app.ts +366 -0
- package/src/tui/input.ts +85 -0
- package/src/tui/panels.ts +133 -0
- package/src/tui/renderer.ts +126 -0
- package/src/tui/terminal.ts +66 -0
- package/src/tui/types.ts +74 -0
- package/src/tui/views/dao.ts +338 -0
- package/src/tui/views/fees.ts +164 -0
- package/src/tui/views/proposal-detail.ts +331 -0
- package/src/tui/views/proposals-list.ts +213 -0
- package/src/tui/views/wallet.ts +560 -0
package/README.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Akita DAO CLI
|
|
2
|
+
|
|
3
|
+
A read-only CLI and interactive TUI for exploring [Akita DAO](https://akita.community) on-chain state on Algorand. Query DAO configuration, fees, proposals, wallet plugins, and more — all from your terminal.
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img src="images/dao_view_styled.png" alt="DAO overview" width="800" />
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
**One-liner** (installs [Bun](https://bun.sh) if needed):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
curl -fsSL https://raw.githubusercontent.com/akita-protocol/dao-cli/main/install.sh | bash
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Or with npm / bun:**
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx @akta/dao-cli
|
|
21
|
+
bun add -g @akta/dao-cli
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Interactive TUI
|
|
25
|
+
|
|
26
|
+
Running `akita-dao` with no arguments launches a full-screen interactive dashboard. Navigate with keyboard shortcuts to explore every aspect of the DAO.
|
|
27
|
+
|
|
28
|
+
| Key | Action |
|
|
29
|
+
|-----|--------|
|
|
30
|
+
| `Tab` / `Shift+Tab` | Switch tabs |
|
|
31
|
+
| `[` `]` | Cycle items (proposals, accounts) |
|
|
32
|
+
| `↑` `↓` `k` `j` | Scroll |
|
|
33
|
+
| `PgUp` `PgDn` | Scroll fast |
|
|
34
|
+
| `g` / `G` | Jump to top / bottom |
|
|
35
|
+
| `r` | Refresh data |
|
|
36
|
+
| `q` | Quit |
|
|
37
|
+
|
|
38
|
+
### DAO Tab
|
|
39
|
+
|
|
40
|
+
Full DAO overview — core settings, asset supply bars, registered app IDs, proposal settings with participation/approval thresholds, and a revenue split chart.
|
|
41
|
+
|
|
42
|
+
### Fees Tab
|
|
43
|
+
|
|
44
|
+
All fee parameters across Wallet, Social, Staking, Subscription, NFT, and Swap categories displayed side-by-side.
|
|
45
|
+
|
|
46
|
+
<p align="center">
|
|
47
|
+
<img src="images/fees_view_styled.png" alt="Fees view" width="800" />
|
|
48
|
+
</p>
|
|
49
|
+
|
|
50
|
+
### Wallet Tab
|
|
51
|
+
|
|
52
|
+
DAO wallet state, escrow accounts with balances, installed plugins with method restrictions, named plugin aliases, spending allowances, and execution keys.
|
|
53
|
+
|
|
54
|
+
<p align="center">
|
|
55
|
+
<img src="images/wallet_view_styled.png" alt="Wallet view" width="800" />
|
|
56
|
+
</p>
|
|
57
|
+
|
|
58
|
+
### Proposals Tab
|
|
59
|
+
|
|
60
|
+
Browse and inspect governance proposals. Each proposal shows its status, vote counts, and fully decoded actions — including field updates, plugin changes, and app registrations.
|
|
61
|
+
|
|
62
|
+
<p align="center">
|
|
63
|
+
<img src="images/proposals_view_styled.png" alt="Proposals view" width="800" />
|
|
64
|
+
</p>
|
|
65
|
+
|
|
66
|
+
## CLI Commands
|
|
67
|
+
|
|
68
|
+
Every view in the TUI is also available as a standalone command for scripting and CI use.
|
|
69
|
+
|
|
70
|
+
### Global Options
|
|
71
|
+
|
|
72
|
+
| Flag | Description | Default |
|
|
73
|
+
|------|-------------|---------|
|
|
74
|
+
| `-n, --network <network>` | `mainnet`, `testnet`, or `localnet` | `mainnet` |
|
|
75
|
+
| `-j, --json` | Output as JSON | off |
|
|
76
|
+
|
|
77
|
+
### DAO
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
akita-dao info # quick dashboard
|
|
81
|
+
akita-dao state # full decoded global state
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Proposals
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
akita-dao proposals list # all proposals, newest first
|
|
88
|
+
akita-dao proposals list -s active # draft + voting only
|
|
89
|
+
akita-dao proposals list -s past # rejected, approved, executed
|
|
90
|
+
akita-dao proposals list -l 5 # limit results
|
|
91
|
+
akita-dao proposals get 117 # detailed view with decoded actions
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Wallet
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
akita-dao wallet info # wallet global state
|
|
98
|
+
akita-dao wallet plugins # installed plugins
|
|
99
|
+
akita-dao wallet named-plugins # plugin aliases
|
|
100
|
+
akita-dao wallet escrows # escrow accounts
|
|
101
|
+
akita-dao wallet allowances # spending allowances
|
|
102
|
+
akita-dao wallet executions # execution keys
|
|
103
|
+
akita-dao wallet balance # ALGO, AKTA, BONES balances
|
|
104
|
+
akita-dao wallet balance 31566704 # specific asset
|
|
105
|
+
akita-dao wallet balance -e rec_gov # escrow balance
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### JSON Output
|
|
109
|
+
|
|
110
|
+
Add `--json` to any command for machine-readable output. BigInts are serialized as strings, byte arrays as hex.
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
akita-dao --json state
|
|
114
|
+
akita-dao --json proposals list -l 10
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Network Switching
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
akita-dao -n testnet info
|
|
121
|
+
akita-dao -n testnet proposals list
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Development
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
git clone https://github.com/akita-protocol/dao-cli.git
|
|
128
|
+
cd dao-cli
|
|
129
|
+
bun install
|
|
130
|
+
bun run src/index.ts
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Requires [akita-sdk](https://github.com/akita-protocol/akita-sc) built locally at `../../akita-sc/projects/akita-sdk`.
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
MIT
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/install.sh
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
echo "Installing Akita DAO CLI..."
|
|
5
|
+
|
|
6
|
+
# Install bun if not present
|
|
7
|
+
if ! command -v bun &> /dev/null; then
|
|
8
|
+
echo "Bun not found. Installing..."
|
|
9
|
+
curl -fsSL https://bun.sh/install | bash
|
|
10
|
+
export BUN_INSTALL="$HOME/.bun"
|
|
11
|
+
export PATH="$BUN_INSTALL/bin:$PATH"
|
|
12
|
+
echo ""
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# Install the CLI globally
|
|
16
|
+
bun add -g @akta/dao-cli
|
|
17
|
+
|
|
18
|
+
echo ""
|
|
19
|
+
echo "Done! Run 'akita-dao info' to get started."
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@akta/dao-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Read-only CLI and TUI for querying Akita DAO state on Algorand",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"akita-dao": "./src/index.ts"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "bun run src/index.ts",
|
|
11
|
+
"typecheck": "tsc --noEmit"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@akta/sdk": "0.0.1",
|
|
15
|
+
"@algorandfoundation/algokit-utils": "9.1.2",
|
|
16
|
+
"algosdk": "3.5.2",
|
|
17
|
+
"commander": "^13.1.0",
|
|
18
|
+
"chalk": "^5.4.1"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/bun": "latest",
|
|
22
|
+
"sharp": "^0.34.5",
|
|
23
|
+
"typescript": "^5.7.0"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"src/",
|
|
27
|
+
"images/*_styled.png",
|
|
28
|
+
"install.sh"
|
|
29
|
+
],
|
|
30
|
+
"keywords": ["algorand", "dao", "akita", "akta", "cli", "tui"],
|
|
31
|
+
"author": "Kyle Breeding",
|
|
32
|
+
"license": "MIT"
|
|
33
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { AkitaDaoSDK } from "@akta/sdk";
|
|
2
|
+
import { getNetworkAppIds, type AkitaNetwork } from "@akta/sdk";
|
|
3
|
+
import { printJson, printKV, header } from "../output";
|
|
4
|
+
|
|
5
|
+
export async function infoCommand(dao: AkitaDaoSDK, network: AkitaNetwork, json: boolean): Promise<void> {
|
|
6
|
+
const state = await dao.getGlobalState();
|
|
7
|
+
const ids = getNetworkAppIds(network);
|
|
8
|
+
|
|
9
|
+
const data = {
|
|
10
|
+
network,
|
|
11
|
+
daoAppId: dao.appId,
|
|
12
|
+
version: state.version,
|
|
13
|
+
walletAppId: state.wallet,
|
|
14
|
+
akta: state.akitaAssets?.akta,
|
|
15
|
+
bones: state.akitaAssets?.bones,
|
|
16
|
+
proposalCount: state.proposalId,
|
|
17
|
+
proposalActionLimit: state.proposalActionLimit,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
if (json) return printJson(data);
|
|
21
|
+
|
|
22
|
+
header("Akita DAO");
|
|
23
|
+
printKV([
|
|
24
|
+
["Network", network],
|
|
25
|
+
["DAO App ID", dao.appId.toString()],
|
|
26
|
+
["Version", state.version ?? "-"],
|
|
27
|
+
["Wallet App ID", state.wallet?.toString() ?? "-"],
|
|
28
|
+
["AKTA Asset ID", state.akitaAssets?.akta?.toString() ?? ids.akta.toString()],
|
|
29
|
+
["BONES Asset ID", state.akitaAssets?.bones?.toString() ?? ids.bones.toString()],
|
|
30
|
+
["Next Proposal ID", state.proposalId?.toString() ?? "-"],
|
|
31
|
+
["Proposal Action Limit", state.proposalActionLimit?.toString() ?? "-"],
|
|
32
|
+
]);
|
|
33
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { AkitaDaoSDK } from "@akta/sdk";
|
|
2
|
+
import { printJson, printColumns, header, printKV } from "../output";
|
|
3
|
+
import {
|
|
4
|
+
truncateAddress,
|
|
5
|
+
formatTimestamp,
|
|
6
|
+
formatCID,
|
|
7
|
+
proposalStatusLabel,
|
|
8
|
+
proposalActionLabel,
|
|
9
|
+
formatMicroAlgo,
|
|
10
|
+
formatBigInt,
|
|
11
|
+
colorStatus,
|
|
12
|
+
} from "../formatting";
|
|
13
|
+
|
|
14
|
+
type StatusFilter = "all" | "active" | "past";
|
|
15
|
+
|
|
16
|
+
function isActive(status: number): boolean {
|
|
17
|
+
return status === 0 || status === 20;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isPast(status: number): boolean {
|
|
21
|
+
return status >= 30;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export async function listProposals(
|
|
25
|
+
dao: AkitaDaoSDK,
|
|
26
|
+
json: boolean,
|
|
27
|
+
statusFilter: StatusFilter = "all",
|
|
28
|
+
limit: number = 20
|
|
29
|
+
): Promise<void> {
|
|
30
|
+
const proposals = await dao.client.state.box.proposals.getMap();
|
|
31
|
+
|
|
32
|
+
let entries = Array.from(proposals.entries());
|
|
33
|
+
|
|
34
|
+
if (statusFilter === "active") {
|
|
35
|
+
entries = entries.filter(([, p]) => isActive(p.status));
|
|
36
|
+
} else if (statusFilter === "past") {
|
|
37
|
+
entries = entries.filter(([, p]) => isPast(p.status));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Sort by ID descending (newest first)
|
|
41
|
+
entries.sort((a, b) => (b[0] > a[0] ? 1 : b[0] < a[0] ? -1 : 0));
|
|
42
|
+
entries = entries.slice(0, limit);
|
|
43
|
+
|
|
44
|
+
if (json) {
|
|
45
|
+
return printJson(
|
|
46
|
+
entries.map(([id, p]) => ({
|
|
47
|
+
id,
|
|
48
|
+
status: p.status,
|
|
49
|
+
statusLabel: proposalStatusLabel(p.status),
|
|
50
|
+
creator: p.creator,
|
|
51
|
+
cid: p.cid,
|
|
52
|
+
votes: p.votes,
|
|
53
|
+
created: p.created,
|
|
54
|
+
votingTs: p.votingTs,
|
|
55
|
+
feesPaid: p.feesPaid,
|
|
56
|
+
actionCount: p.actions.length,
|
|
57
|
+
}))
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
header(`Proposals (${statusFilter}, showing ${entries.length})`);
|
|
62
|
+
|
|
63
|
+
if (entries.length === 0) {
|
|
64
|
+
console.log(" No proposals found.");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const rows = entries.map(([id, p]) => [
|
|
69
|
+
id.toString(),
|
|
70
|
+
colorStatus(proposalStatusLabel(p.status)),
|
|
71
|
+
truncateAddress(p.creator),
|
|
72
|
+
`${p.votes.approvals}/${p.votes.rejections}/${p.votes.abstains}`,
|
|
73
|
+
p.actions.length.toString(),
|
|
74
|
+
formatTimestamp(p.created),
|
|
75
|
+
]);
|
|
76
|
+
|
|
77
|
+
printColumns(["ID", "Status", "Creator", "Votes", "Actions", "Created"], rows);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function getProposal(dao: AkitaDaoSDK, id: bigint, json: boolean): Promise<void> {
|
|
81
|
+
const proposal = await dao.getProposal(id);
|
|
82
|
+
|
|
83
|
+
if (json) return printJson({ id, ...proposal });
|
|
84
|
+
|
|
85
|
+
header(`Proposal #${id}`);
|
|
86
|
+
printKV([
|
|
87
|
+
["Status", colorStatus(proposalStatusLabel(proposal.status))],
|
|
88
|
+
["Creator", proposal.creator],
|
|
89
|
+
["CID", formatCID(proposal.cid)],
|
|
90
|
+
["Created", formatTimestamp(proposal.created)],
|
|
91
|
+
["Voting Timestamp", formatTimestamp(proposal.votingTs)],
|
|
92
|
+
["Fees Paid", formatMicroAlgo(proposal.feesPaid)],
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
header("Votes");
|
|
96
|
+
printKV([
|
|
97
|
+
["Approvals", formatBigInt(proposal.votes.approvals)],
|
|
98
|
+
["Rejections", formatBigInt(proposal.votes.rejections)],
|
|
99
|
+
["Abstains", formatBigInt(proposal.votes.abstains)],
|
|
100
|
+
]);
|
|
101
|
+
|
|
102
|
+
if (proposal.actions.length > 0) {
|
|
103
|
+
header("Actions");
|
|
104
|
+
const rows = proposal.actions.map((action, i) => [
|
|
105
|
+
(i + 1).toString(),
|
|
106
|
+
proposalActionLabel(action.type),
|
|
107
|
+
formatActionDetails(action),
|
|
108
|
+
]);
|
|
109
|
+
printColumns(["#", "Type", "Details"], rows);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function formatActionDetails(action: { type: number; [key: string]: unknown }): string {
|
|
114
|
+
const { type, ...rest } = action;
|
|
115
|
+
const parts: string[] = [];
|
|
116
|
+
|
|
117
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
118
|
+
if (value instanceof Uint8Array) {
|
|
119
|
+
const hex = Buffer.from(value).toString("hex");
|
|
120
|
+
parts.push(`${key}: ${hex.length > 16 ? hex.slice(0, 16) + "..." : hex}`);
|
|
121
|
+
} else if (typeof value === "bigint") {
|
|
122
|
+
parts.push(`${key}: ${value.toString()}`);
|
|
123
|
+
} else if (typeof value === "string") {
|
|
124
|
+
parts.push(`${key}: ${value.length > 40 ? value.slice(0, 40) + "..." : value}`);
|
|
125
|
+
} else if (Array.isArray(value)) {
|
|
126
|
+
parts.push(`${key}: [${value.length} items]`);
|
|
127
|
+
} else if (value !== undefined && value !== null) {
|
|
128
|
+
parts.push(`${key}: ${String(value)}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return parts.join(", ");
|
|
133
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { AkitaDaoSDK } from "@akta/sdk";
|
|
2
|
+
import { type AkitaNetwork } from "@akta/sdk";
|
|
3
|
+
import type { AkitaDaoGlobalState } from "@akta/sdk/dao";
|
|
4
|
+
import { printJson, printKV, header, printColumns, printMultiColumnKV, type KVGroup } from "../output";
|
|
5
|
+
import {
|
|
6
|
+
formatMicroAlgo,
|
|
7
|
+
formatBasisPoints,
|
|
8
|
+
formatDuration,
|
|
9
|
+
formatBigInt,
|
|
10
|
+
daoStateLabel,
|
|
11
|
+
resolveAppName,
|
|
12
|
+
colorState,
|
|
13
|
+
} from "../formatting";
|
|
14
|
+
|
|
15
|
+
export async function stateCommand(dao: AkitaDaoSDK, network: AkitaNetwork, json: boolean): Promise<void> {
|
|
16
|
+
const state = await dao.getGlobalState();
|
|
17
|
+
|
|
18
|
+
if (json) return printJson(state);
|
|
19
|
+
|
|
20
|
+
header("Core Settings");
|
|
21
|
+
printKV([
|
|
22
|
+
["State", state.state !== undefined ? colorState(daoStateLabel(state.state)) : "-"],
|
|
23
|
+
["Version", state.version ?? "-"],
|
|
24
|
+
["Wallet", state.wallet ? resolveAppName(state.wallet, network) : "-"],
|
|
25
|
+
["Proposal Action Limit", state.proposalActionLimit?.toString() ?? "-"],
|
|
26
|
+
["Min Rewards Impact", state.minRewardsImpact?.toString() ?? "-"],
|
|
27
|
+
["Next Proposal ID", state.proposalId?.toString() ?? "-"],
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
if (state.akitaAssets) {
|
|
31
|
+
header("Assets");
|
|
32
|
+
printKV([
|
|
33
|
+
["AKTA", state.akitaAssets.akta.toString()],
|
|
34
|
+
["BONES", state.akitaAssets.bones.toString()],
|
|
35
|
+
]);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
printAppLists(state, network);
|
|
39
|
+
printFees(state);
|
|
40
|
+
printProposalSettings(state);
|
|
41
|
+
printRevenueSplits(state, network);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function printAppLists(state: Partial<AkitaDaoGlobalState>, network: AkitaNetwork): void {
|
|
45
|
+
header("App IDs");
|
|
46
|
+
|
|
47
|
+
const sections: [string, Record<string, bigint> | undefined][] = [
|
|
48
|
+
["Core", state.akitaAppList as Record<string, bigint> | undefined],
|
|
49
|
+
["Social", state.akitaSocialAppList as Record<string, bigint> | undefined],
|
|
50
|
+
["Plugins", state.pluginAppList as Record<string, bigint> | undefined],
|
|
51
|
+
["Other", state.otherAppList as Record<string, bigint> | undefined],
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const rows: string[][] = [];
|
|
55
|
+
for (const [category, apps] of sections) {
|
|
56
|
+
if (!apps) continue;
|
|
57
|
+
for (const [, id] of Object.entries(apps)) {
|
|
58
|
+
rows.push([category, resolveAppName(id, network), id.toString()]);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
printColumns(["Category", "Name", "App ID"], rows);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Fields that represent basis point percentages
|
|
66
|
+
const PERCENTAGE_FIELDS = new Set([
|
|
67
|
+
"referrerPercentage",
|
|
68
|
+
"impactTaxMin", "impactTaxMax",
|
|
69
|
+
"paymentPercentage", "triggerPercentage",
|
|
70
|
+
"marketplaceSalePercentageMin", "marketplaceSalePercentageMax",
|
|
71
|
+
"marketplaceComposablePercentage", "marketplaceRoyaltyDefaultPercentage",
|
|
72
|
+
"shuffleSalePercentage",
|
|
73
|
+
"auctionSaleImpactTaxMin", "auctionSaleImpactTaxMax",
|
|
74
|
+
"auctionComposablePercentage", "auctionRafflePercentage",
|
|
75
|
+
"raffleSaleImpactTaxMin", "raffleSaleImpactTaxMax",
|
|
76
|
+
"raffleComposablePercentage",
|
|
77
|
+
]);
|
|
78
|
+
|
|
79
|
+
// Fields that represent microAlgo fees
|
|
80
|
+
const FEE_FIELDS = new Set([
|
|
81
|
+
"createFee", "creationFee", "serviceCreationFee",
|
|
82
|
+
"postFee", "reactFee",
|
|
83
|
+
"omnigemSaleFee", "auctionCreationFee", "raffleCreationFee",
|
|
84
|
+
]);
|
|
85
|
+
|
|
86
|
+
function formatFeeValue(key: string, value: bigint): string {
|
|
87
|
+
if (PERCENTAGE_FIELDS.has(key)) return formatBasisPoints(value);
|
|
88
|
+
if (FEE_FIELDS.has(key)) return formatMicroAlgo(value);
|
|
89
|
+
return formatBigInt(value);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function feeLabel(key: string): string {
|
|
93
|
+
return key
|
|
94
|
+
.replace(/([A-Z])/g, " $1")
|
|
95
|
+
.replace(/^./, (c) => c.toUpperCase())
|
|
96
|
+
.trim();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function printFees(state: Partial<AkitaDaoGlobalState>): void {
|
|
100
|
+
const feeGroups: [string, Record<string, bigint> | undefined][] = [
|
|
101
|
+
["Wallet Fees", state.walletFees as Record<string, bigint> | undefined],
|
|
102
|
+
["Social Fees", state.socialFees as Record<string, bigint> | undefined],
|
|
103
|
+
["Staking Fees", state.stakingFees as Record<string, bigint> | undefined],
|
|
104
|
+
["Subscription Fees", state.subscriptionFees as Record<string, bigint> | undefined],
|
|
105
|
+
["NFT Fees", state.nftFees as Record<string, bigint> | undefined],
|
|
106
|
+
["Swap Fees", state.swapFees as Record<string, bigint> | undefined],
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
const groups: KVGroup[] = [];
|
|
110
|
+
for (const [name, fees] of feeGroups) {
|
|
111
|
+
if (!fees) continue;
|
|
112
|
+
groups.push({
|
|
113
|
+
title: name,
|
|
114
|
+
pairs: Object.entries(fees).map(([k, v]) => [feeLabel(k), formatFeeValue(k, v)]),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (groups.length === 0) return;
|
|
119
|
+
|
|
120
|
+
header("Fees");
|
|
121
|
+
printMultiColumnKV(groups);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function printProposalSettings(state: Partial<AkitaDaoGlobalState>): void {
|
|
125
|
+
const settings: [string, { fee: bigint; power: bigint; duration: bigint; participation: bigint; approval: bigint } | undefined][] = [
|
|
126
|
+
["Upgrade App", state.upgradeAppProposalSettings],
|
|
127
|
+
["Add Plugin", state.addPluginProposalSettings],
|
|
128
|
+
["Remove Plugin", state.removePluginProposalSettings],
|
|
129
|
+
["Remove Execute Plugin", state.removeExecutePluginProposalSettings],
|
|
130
|
+
["Add Allowances", state.addAllowancesProposalSettings],
|
|
131
|
+
["Remove Allowances", state.removeAllowancesProposalSettings],
|
|
132
|
+
["New Escrow", state.newEscrowProposalSettings],
|
|
133
|
+
["Toggle Escrow Lock", state.toggleEscrowLockProposalSettings],
|
|
134
|
+
["Update Fields", state.updateFieldsProposalSettings],
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
const hasAny = settings.some(([, v]) => v !== undefined);
|
|
138
|
+
if (!hasAny) return;
|
|
139
|
+
|
|
140
|
+
header("Proposal Settings");
|
|
141
|
+
const rows = settings.map(([label, ps]) => {
|
|
142
|
+
if (!ps) return [label, "-", "-", "-", "-", "-"];
|
|
143
|
+
return [
|
|
144
|
+
label,
|
|
145
|
+
formatMicroAlgo(ps.fee),
|
|
146
|
+
formatBigInt(ps.power),
|
|
147
|
+
formatDuration(ps.duration),
|
|
148
|
+
formatBasisPoints(ps.participation),
|
|
149
|
+
formatBasisPoints(ps.approval),
|
|
150
|
+
];
|
|
151
|
+
});
|
|
152
|
+
printColumns(["Category", "Fee", "Power", "Duration", "Participation", "Approval"], rows);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function printRevenueSplits(state: Partial<AkitaDaoGlobalState>, network: AkitaNetwork): void {
|
|
156
|
+
if (!state.revenueSplits || state.revenueSplits.length === 0) return;
|
|
157
|
+
|
|
158
|
+
header("Revenue Splits");
|
|
159
|
+
const typeLabels: Record<number, string> = { 10: "Flat", 20: "Percentage", 30: "Remainder" };
|
|
160
|
+
const rows = state.revenueSplits.map(([[wallet, escrow], type, value]) => [
|
|
161
|
+
resolveAppName(wallet, network),
|
|
162
|
+
escrow || "(default)",
|
|
163
|
+
typeLabels[type] ?? `Unknown (${type})`,
|
|
164
|
+
type === 20 ? formatBasisPoints(value) : formatBigInt(value),
|
|
165
|
+
]);
|
|
166
|
+
printColumns(["Wallet", "Escrow", "Type", "Value"], rows);
|
|
167
|
+
}
|