fourmm 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.
Files changed (151) hide show
  1. package/README.md +147 -0
  2. package/dist/bin.d.ts +9 -0
  3. package/dist/bin.d.ts.map +1 -0
  4. package/dist/bin.js +14 -0
  5. package/dist/bin.js.map +1 -0
  6. package/dist/cli.d.ts +319 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +25 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/commands/config.d.ts +35 -0
  11. package/dist/commands/config.d.ts.map +1 -0
  12. package/dist/commands/config.js +145 -0
  13. package/dist/commands/config.js.map +1 -0
  14. package/dist/commands/query.d.ts +51 -0
  15. package/dist/commands/query.d.ts.map +1 -0
  16. package/dist/commands/query.js +364 -0
  17. package/dist/commands/query.js.map +1 -0
  18. package/dist/commands/token.d.ts +55 -0
  19. package/dist/commands/token.d.ts.map +1 -0
  20. package/dist/commands/token.js +650 -0
  21. package/dist/commands/token.js.map +1 -0
  22. package/dist/commands/tools.d.ts +54 -0
  23. package/dist/commands/tools.d.ts.map +1 -0
  24. package/dist/commands/tools.js +499 -0
  25. package/dist/commands/tools.js.map +1 -0
  26. package/dist/commands/trade.d.ts +63 -0
  27. package/dist/commands/trade.d.ts.map +1 -0
  28. package/dist/commands/trade.js +933 -0
  29. package/dist/commands/trade.js.map +1 -0
  30. package/dist/commands/transfer.d.ts +51 -0
  31. package/dist/commands/transfer.d.ts.map +1 -0
  32. package/dist/commands/transfer.js +728 -0
  33. package/dist/commands/transfer.js.map +1 -0
  34. package/dist/commands/wallet.d.ts +111 -0
  35. package/dist/commands/wallet.d.ts.map +1 -0
  36. package/dist/commands/wallet.js +716 -0
  37. package/dist/commands/wallet.js.map +1 -0
  38. package/dist/contracts/erc20.d.ts +72 -0
  39. package/dist/contracts/erc20.d.ts.map +1 -0
  40. package/dist/contracts/erc20.js +55 -0
  41. package/dist/contracts/erc20.js.map +1 -0
  42. package/dist/contracts/fourmemeMmRouter.d.ts +68 -0
  43. package/dist/contracts/fourmemeMmRouter.d.ts.map +1 -0
  44. package/dist/contracts/fourmemeMmRouter.js +48 -0
  45. package/dist/contracts/fourmemeMmRouter.js.map +1 -0
  46. package/dist/contracts/pancakeRouter.d.ts +73 -0
  47. package/dist/contracts/pancakeRouter.d.ts.map +1 -0
  48. package/dist/contracts/pancakeRouter.js +50 -0
  49. package/dist/contracts/pancakeRouter.js.map +1 -0
  50. package/dist/contracts/tokenManager2.d.ts +193 -0
  51. package/dist/contracts/tokenManager2.d.ts.map +1 -0
  52. package/dist/contracts/tokenManager2.js +108 -0
  53. package/dist/contracts/tokenManager2.js.map +1 -0
  54. package/dist/contracts/tokenManagerHelper3.d.ts +118 -0
  55. package/dist/contracts/tokenManagerHelper3.d.ts.map +1 -0
  56. package/dist/contracts/tokenManagerHelper3.js +66 -0
  57. package/dist/contracts/tokenManagerHelper3.js.map +1 -0
  58. package/dist/datastore/cache.d.ts +20 -0
  59. package/dist/datastore/cache.d.ts.map +1 -0
  60. package/dist/datastore/cache.js +45 -0
  61. package/dist/datastore/cache.js.map +1 -0
  62. package/dist/datastore/index.d.ts +85 -0
  63. package/dist/datastore/index.d.ts.map +1 -0
  64. package/dist/datastore/index.js +341 -0
  65. package/dist/datastore/index.js.map +1 -0
  66. package/dist/datastore/paths.d.ts +17 -0
  67. package/dist/datastore/paths.d.ts.map +1 -0
  68. package/dist/datastore/paths.js +39 -0
  69. package/dist/datastore/paths.js.map +1 -0
  70. package/dist/datastore/types.d.ts +105 -0
  71. package/dist/datastore/types.d.ts.map +1 -0
  72. package/dist/datastore/types.js +8 -0
  73. package/dist/datastore/types.js.map +1 -0
  74. package/dist/fourmeme/auth.d.ts +22 -0
  75. package/dist/fourmeme/auth.d.ts.map +1 -0
  76. package/dist/fourmeme/auth.js +78 -0
  77. package/dist/fourmeme/auth.js.map +1 -0
  78. package/dist/fourmeme/create.d.ts +31 -0
  79. package/dist/fourmeme/create.d.ts.map +1 -0
  80. package/dist/fourmeme/create.js +111 -0
  81. package/dist/fourmeme/create.js.map +1 -0
  82. package/dist/fourmeme/upload.d.ts +16 -0
  83. package/dist/fourmeme/upload.d.ts.map +1 -0
  84. package/dist/fourmeme/upload.js +52 -0
  85. package/dist/fourmeme/upload.js.map +1 -0
  86. package/dist/lib/bundle.d.ts +51 -0
  87. package/dist/lib/bundle.d.ts.map +1 -0
  88. package/dist/lib/bundle.js +95 -0
  89. package/dist/lib/bundle.js.map +1 -0
  90. package/dist/lib/config.d.ts +58 -0
  91. package/dist/lib/config.d.ts.map +1 -0
  92. package/dist/lib/config.js +183 -0
  93. package/dist/lib/config.js.map +1 -0
  94. package/dist/lib/const.d.ts +165 -0
  95. package/dist/lib/const.d.ts.map +1 -0
  96. package/dist/lib/const.js +98 -0
  97. package/dist/lib/const.js.map +1 -0
  98. package/dist/lib/env.d.ts +14 -0
  99. package/dist/lib/env.d.ts.map +1 -0
  100. package/dist/lib/env.js +18 -0
  101. package/dist/lib/env.js.map +1 -0
  102. package/dist/lib/guards.d.ts +44 -0
  103. package/dist/lib/guards.d.ts.map +1 -0
  104. package/dist/lib/guards.js +65 -0
  105. package/dist/lib/guards.js.map +1 -0
  106. package/dist/lib/identify.d.ts +85 -0
  107. package/dist/lib/identify.d.ts.map +1 -0
  108. package/dist/lib/identify.js +88 -0
  109. package/dist/lib/identify.js.map +1 -0
  110. package/dist/lib/pricing.d.ts +62 -0
  111. package/dist/lib/pricing.d.ts.map +1 -0
  112. package/dist/lib/pricing.js +302 -0
  113. package/dist/lib/pricing.js.map +1 -0
  114. package/dist/lib/routing.d.ts +57 -0
  115. package/dist/lib/routing.d.ts.map +1 -0
  116. package/dist/lib/routing.js +67 -0
  117. package/dist/lib/routing.js.map +1 -0
  118. package/dist/lib/slippage.d.ts +29 -0
  119. package/dist/lib/slippage.d.ts.map +1 -0
  120. package/dist/lib/slippage.js +110 -0
  121. package/dist/lib/slippage.js.map +1 -0
  122. package/dist/lib/tracker.d.ts +68 -0
  123. package/dist/lib/tracker.d.ts.map +1 -0
  124. package/dist/lib/tracker.js +155 -0
  125. package/dist/lib/tracker.js.map +1 -0
  126. package/dist/lib/viem.d.ts +12 -0
  127. package/dist/lib/viem.d.ts.map +1 -0
  128. package/dist/lib/viem.js +44 -0
  129. package/dist/lib/viem.js.map +1 -0
  130. package/dist/lib/wallet-rows.d.ts +30 -0
  131. package/dist/lib/wallet-rows.d.ts.map +1 -0
  132. package/dist/lib/wallet-rows.js +9 -0
  133. package/dist/lib/wallet-rows.js.map +1 -0
  134. package/dist/lib/walletClient.d.ts +16 -0
  135. package/dist/lib/walletClient.d.ts.map +1 -0
  136. package/dist/lib/walletClient.js +26 -0
  137. package/dist/lib/walletClient.js.map +1 -0
  138. package/dist/wallets/groups/encrypt.d.ts +26 -0
  139. package/dist/wallets/groups/encrypt.d.ts.map +1 -0
  140. package/dist/wallets/groups/encrypt.js +52 -0
  141. package/dist/wallets/groups/encrypt.js.map +1 -0
  142. package/dist/wallets/groups/generate.d.ts +19 -0
  143. package/dist/wallets/groups/generate.d.ts.map +1 -0
  144. package/dist/wallets/groups/generate.js +36 -0
  145. package/dist/wallets/groups/generate.js.map +1 -0
  146. package/dist/wallets/groups/store.d.ts +107 -0
  147. package/dist/wallets/groups/store.d.ts.map +1 -0
  148. package/dist/wallets/groups/store.js +254 -0
  149. package/dist/wallets/groups/store.js.map +1 -0
  150. package/package.json +50 -0
  151. package/skills/SKILL.md +187 -0
@@ -0,0 +1,107 @@
1
+ /**
2
+ * In-house wallet group store.
3
+ *
4
+ * Storage: ~/.fourmm/wallets/wallet-store.json (mode 0600), JSON-encoded.
5
+ * Each wallet's privateKey is AES-encrypted with the master password.
6
+ *
7
+ * File shape:
8
+ * {
9
+ * encrypted: true,
10
+ * passwordCheck: "...", // AES('ALMM_PASSWORD_OK', password)
11
+ * groups: {
12
+ * "1": { groupId, name, wallets: [...], note, createdAt, updatedAt },
13
+ * ...
14
+ * }
15
+ * }
16
+ *
17
+ * All public functions take the master password as an explicit first argument.
18
+ * There is NO module-level password state: that makes testing easier, hides
19
+ * fewer dependencies, and lets Week-2 code pass different passwords in the
20
+ * same process (e.g. import + decrypt two different vaults).
21
+ *
22
+ * Write path is atomic (write .tmp, rename) to avoid half-written files.
23
+ */
24
+ import type { Address, Hex } from 'viem';
25
+ /** A single wallet inside a group */
26
+ export type StoredWallet = {
27
+ /** EVM checksummed address */
28
+ address: Address;
29
+ /** AES-encrypted hex private key (with 0x prefix before encryption) */
30
+ encryptedPrivateKey: string;
31
+ /** Free-form note */
32
+ note: string;
33
+ };
34
+ /** A wallet group (collection of wallets under a shared name) */
35
+ export type WalletGroup = {
36
+ /** Numeric group ID (auto-assigned) */
37
+ groupId: number;
38
+ /** Group name */
39
+ name: string;
40
+ /** Free-form description */
41
+ note: string;
42
+ /** Wallets in this group */
43
+ wallets: StoredWallet[];
44
+ /** ISO 8601 timestamps */
45
+ createdAt: string;
46
+ updatedAt: string;
47
+ };
48
+ /** Root file format */
49
+ export type WalletStoreFile = {
50
+ encrypted: true;
51
+ passwordCheck: string;
52
+ groups: Record<number, WalletGroup>;
53
+ };
54
+ /** Group summary (what `list-groups` returns — no wallet contents) */
55
+ export type WalletGroupSummary = Omit<WalletGroup, 'wallets'> & {
56
+ walletCount: number;
57
+ };
58
+ /** Does a wallet store file already exist? */
59
+ export declare function storeExists(): boolean;
60
+ /**
61
+ * Load the wallet store.
62
+ *
63
+ * - Returns `null` if the store doesn't exist yet. Read-only callers
64
+ * (e.g. `list-groups`) should treat this as "no groups".
65
+ * - Throws if the store exists but the password doesn't match.
66
+ * - **Never writes to disk.** Callers that want to create a new store
67
+ * must call `initStore(password)` explicitly.
68
+ */
69
+ export declare function loadStore(password: string): WalletStoreFile | null;
70
+ /**
71
+ * Create a new empty wallet store, encrypted with the given password.
72
+ *
73
+ * Throws if a store file already exists. Callers should check `storeExists()`
74
+ * or use `loadOrInitStore()` if they want idempotent creation.
75
+ */
76
+ export declare function initStore(password: string): WalletStoreFile;
77
+ /**
78
+ * Convenience: load the store, or init an empty one if it doesn't exist.
79
+ * Used by write operations (`createGroup`, etc.).
80
+ */
81
+ export declare function loadOrInitStore(password: string): WalletStoreFile;
82
+ /** Persist the wallet store atomically */
83
+ export declare function saveStore(store: WalletStoreFile): void;
84
+ /** List all groups (summary form). Returns empty array if no store. */
85
+ export declare function listGroups(password: string): WalletGroupSummary[];
86
+ /** Get a single group by ID. Returns undefined if store or group missing. */
87
+ export declare function getGroup(password: string, groupId: number): WalletGroup | undefined;
88
+ /**
89
+ * Create a new wallet group with N freshly-generated wallets.
90
+ * Private keys are encrypted at rest immediately.
91
+ */
92
+ export declare function createGroup(password: string, options: {
93
+ name: string;
94
+ count: number;
95
+ note?: string | undefined;
96
+ }): WalletGroup;
97
+ /** Add newly-generated wallets to an existing group */
98
+ export declare function addGeneratedWallets(password: string, groupId: number, count: number): StoredWallet[];
99
+ /** Add a wallet from an existing private key */
100
+ export declare function addWalletFromPrivateKey(password: string, groupId: number, privateKey: Hex, note?: string): StoredWallet;
101
+ /** Delete an entire wallet group by ID. */
102
+ export declare function deleteGroup(password: string, groupId: number): void;
103
+ /** Remove a single wallet from a group by address. */
104
+ export declare function removeWalletFromGroup(password: string, groupId: number, address: Address): void;
105
+ /** Decrypt a wallet's private key (only for in-process signing — never log) */
106
+ export declare function decryptPrivateKey(stored: StoredWallet, password: string): Hex;
107
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/wallets/groups/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,MAAM,CAAA;AAaxC,qCAAqC;AACrC,MAAM,MAAM,YAAY,GAAG;IACzB,8BAA8B;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,uEAAuE;IACvE,mBAAmB,EAAE,MAAM,CAAA;IAC3B,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,iEAAiE;AACjE,MAAM,MAAM,WAAW,GAAG;IACxB,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAA;IACf,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,4BAA4B;IAC5B,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,uBAAuB;AACvB,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,IAAI,CAAA;IACf,aAAa,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAA;CACpC,CAAA;AAED,sEAAsE;AACtE,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,GAAG;IAC9D,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAiCD,8CAA8C;AAC9C,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CA0BlE;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAkB3D;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAEjE;AAED,0CAA0C;AAC1C,wBAAgB,SAAS,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAEtD;AAMD,uEAAuE;AACvE,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAajE;AAED,6EAA6E;AAC7E,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,WAAW,GAAG,SAAS,CAIzB;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACzB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;IACP,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC1B,GACA,WAAW,CA0Bb;AAED,uDAAuD;AACvD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,YAAY,EAAE,CAqBhB;AAED,gDAAgD;AAChD,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,GAAG,EACf,IAAI,SAAK,GACR,YAAY,CA2Bd;AAED,2CAA2C;AAC3C,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAMnE;AAED,sDAAsD;AACtD,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,GACf,IAAI,CAcN;AAED,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,GAAG,GAAG,CAQ7E"}
@@ -0,0 +1,254 @@
1
+ /**
2
+ * In-house wallet group store.
3
+ *
4
+ * Storage: ~/.fourmm/wallets/wallet-store.json (mode 0600), JSON-encoded.
5
+ * Each wallet's privateKey is AES-encrypted with the master password.
6
+ *
7
+ * File shape:
8
+ * {
9
+ * encrypted: true,
10
+ * passwordCheck: "...", // AES('ALMM_PASSWORD_OK', password)
11
+ * groups: {
12
+ * "1": { groupId, name, wallets: [...], note, createdAt, updatedAt },
13
+ * ...
14
+ * }
15
+ * }
16
+ *
17
+ * All public functions take the master password as an explicit first argument.
18
+ * There is NO module-level password state: that makes testing easier, hides
19
+ * fewer dependencies, and lets Week-2 code pass different passwords in the
20
+ * same process (e.g. import + decrypt two different vaults).
21
+ *
22
+ * Write path is atomic (write .tmp, rename) to avoid half-written files.
23
+ */
24
+ import fs from 'node:fs';
25
+ import path from 'node:path';
26
+ import { walletsDir, ensureFourmmDirs } from '../../lib/config.js';
27
+ import { decrypt, encrypt, generatePasswordCheck, verifyPassword, } from './encrypt.js';
28
+ import { generateWallets, walletFromPrivateKey } from './generate.js';
29
+ // ============================================================
30
+ // Paths
31
+ // ============================================================
32
+ const STORE_FILE = 'wallet-store.json';
33
+ function storePath() {
34
+ return path.join(walletsDir(), STORE_FILE);
35
+ }
36
+ // ============================================================
37
+ // Atomic file ops
38
+ // ============================================================
39
+ function atomicWriteJson(filePath, data) {
40
+ const dir = path.dirname(filePath);
41
+ if (!fs.existsSync(dir)) {
42
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
43
+ }
44
+ const tmpPath = `${filePath}.tmp`;
45
+ fs.writeFileSync(tmpPath, JSON.stringify(data, null, 2), {
46
+ encoding: 'utf-8',
47
+ mode: 0o600,
48
+ });
49
+ fs.renameSync(tmpPath, filePath);
50
+ }
51
+ // ============================================================
52
+ // Load / init
53
+ // ============================================================
54
+ /** Does a wallet store file already exist? */
55
+ export function storeExists() {
56
+ return fs.existsSync(storePath());
57
+ }
58
+ /**
59
+ * Load the wallet store.
60
+ *
61
+ * - Returns `null` if the store doesn't exist yet. Read-only callers
62
+ * (e.g. `list-groups`) should treat this as "no groups".
63
+ * - Throws if the store exists but the password doesn't match.
64
+ * - **Never writes to disk.** Callers that want to create a new store
65
+ * must call `initStore(password)` explicitly.
66
+ */
67
+ export function loadStore(password) {
68
+ if (!password)
69
+ throw new Error('loadStore: password is empty');
70
+ const file = storePath();
71
+ if (!fs.existsSync(file))
72
+ return null;
73
+ let parsed;
74
+ try {
75
+ const raw = fs.readFileSync(file, 'utf-8');
76
+ parsed = JSON.parse(raw);
77
+ }
78
+ catch {
79
+ throw new Error(`Wallet store at ${file} is corrupt (not valid JSON)`);
80
+ }
81
+ if (!parsed.encrypted || !parsed.passwordCheck) {
82
+ throw new Error(`Wallet store at ${file} is missing encrypted/passwordCheck fields`);
83
+ }
84
+ if (!verifyPassword(password, parsed.passwordCheck)) {
85
+ throw new Error('Wrong master password: unable to decrypt wallet store');
86
+ }
87
+ if (!parsed.groups)
88
+ parsed.groups = {};
89
+ return parsed;
90
+ }
91
+ /**
92
+ * Create a new empty wallet store, encrypted with the given password.
93
+ *
94
+ * Throws if a store file already exists. Callers should check `storeExists()`
95
+ * or use `loadOrInitStore()` if they want idempotent creation.
96
+ */
97
+ export function initStore(password) {
98
+ if (!password)
99
+ throw new Error('initStore: password is empty');
100
+ ensureFourmmDirs();
101
+ const file = storePath();
102
+ if (fs.existsSync(file)) {
103
+ throw new Error(`Wallet store at ${file} already exists. Refusing to overwrite.`);
104
+ }
105
+ const store = {
106
+ encrypted: true,
107
+ passwordCheck: generatePasswordCheck(password),
108
+ groups: {},
109
+ };
110
+ atomicWriteJson(file, store);
111
+ return store;
112
+ }
113
+ /**
114
+ * Convenience: load the store, or init an empty one if it doesn't exist.
115
+ * Used by write operations (`createGroup`, etc.).
116
+ */
117
+ export function loadOrInitStore(password) {
118
+ return loadStore(password) ?? initStore(password);
119
+ }
120
+ /** Persist the wallet store atomically */
121
+ export function saveStore(store) {
122
+ atomicWriteJson(storePath(), store);
123
+ }
124
+ // ============================================================
125
+ // Group operations
126
+ // ============================================================
127
+ /** List all groups (summary form). Returns empty array if no store. */
128
+ export function listGroups(password) {
129
+ const store = loadStore(password);
130
+ if (!store)
131
+ return [];
132
+ return Object.values(store.groups)
133
+ .sort((a, b) => a.groupId - b.groupId)
134
+ .map((g) => ({
135
+ groupId: g.groupId,
136
+ name: g.name,
137
+ note: g.note,
138
+ walletCount: g.wallets.length,
139
+ createdAt: g.createdAt,
140
+ updatedAt: g.updatedAt,
141
+ }));
142
+ }
143
+ /** Get a single group by ID. Returns undefined if store or group missing. */
144
+ export function getGroup(password, groupId) {
145
+ const store = loadStore(password);
146
+ if (!store)
147
+ return undefined;
148
+ return store.groups[groupId];
149
+ }
150
+ /**
151
+ * Create a new wallet group with N freshly-generated wallets.
152
+ * Private keys are encrypted at rest immediately.
153
+ */
154
+ export function createGroup(password, options) {
155
+ const store = loadOrInitStore(password);
156
+ const nextId = Object.values(store.groups).reduce((max, g) => Math.max(max, g.groupId), 0) +
157
+ 1;
158
+ const fresh = generateWallets(options.count);
159
+ const now = new Date().toISOString();
160
+ const group = {
161
+ groupId: nextId,
162
+ name: options.name,
163
+ note: options.note ?? '',
164
+ wallets: fresh.map((w) => ({
165
+ address: w.address,
166
+ encryptedPrivateKey: encrypt(w.privateKey, password),
167
+ note: w.note,
168
+ })),
169
+ createdAt: now,
170
+ updatedAt: now,
171
+ };
172
+ store.groups[nextId] = group;
173
+ saveStore(store);
174
+ return group;
175
+ }
176
+ /** Add newly-generated wallets to an existing group */
177
+ export function addGeneratedWallets(password, groupId, count) {
178
+ const store = loadStore(password);
179
+ if (!store) {
180
+ throw new Error(`No wallet store found. Create a group first with \`fourmm wallet create-group\`.`);
181
+ }
182
+ const group = store.groups[groupId];
183
+ if (!group)
184
+ throw new Error(`Group ${groupId} does not exist`);
185
+ const fresh = generateWallets(count);
186
+ const stored = fresh.map((w) => ({
187
+ address: w.address,
188
+ encryptedPrivateKey: encrypt(w.privateKey, password),
189
+ note: w.note,
190
+ }));
191
+ group.wallets.push(...stored);
192
+ group.updatedAt = new Date().toISOString();
193
+ saveStore(store);
194
+ return stored;
195
+ }
196
+ /** Add a wallet from an existing private key */
197
+ export function addWalletFromPrivateKey(password, groupId, privateKey, note = '') {
198
+ const store = loadStore(password);
199
+ if (!store) {
200
+ throw new Error(`No wallet store found. Create a group first with \`fourmm wallet create-group\`.`);
201
+ }
202
+ const group = store.groups[groupId];
203
+ if (!group)
204
+ throw new Error(`Group ${groupId} does not exist`);
205
+ const w = walletFromPrivateKey(privateKey, note);
206
+ // Dedupe by address
207
+ if (group.wallets.some((existing) => existing.address === w.address)) {
208
+ throw new Error(`Wallet ${w.address} already exists in group ${groupId}`);
209
+ }
210
+ const stored = {
211
+ address: w.address,
212
+ encryptedPrivateKey: encrypt(privateKey, password),
213
+ note: w.note,
214
+ };
215
+ group.wallets.push(stored);
216
+ group.updatedAt = new Date().toISOString();
217
+ saveStore(store);
218
+ return stored;
219
+ }
220
+ /** Delete an entire wallet group by ID. */
221
+ export function deleteGroup(password, groupId) {
222
+ const store = loadStore(password);
223
+ if (!store)
224
+ throw new Error('No wallet store found.');
225
+ if (!store.groups[groupId])
226
+ throw new Error(`Group ${groupId} does not exist`);
227
+ delete store.groups[groupId];
228
+ saveStore(store);
229
+ }
230
+ /** Remove a single wallet from a group by address. */
231
+ export function removeWalletFromGroup(password, groupId, address) {
232
+ const store = loadStore(password);
233
+ if (!store)
234
+ throw new Error('No wallet store found.');
235
+ const group = store.groups[groupId];
236
+ if (!group)
237
+ throw new Error(`Group ${groupId} does not exist`);
238
+ const before = group.wallets.length;
239
+ group.wallets = group.wallets.filter((w) => w.address.toLowerCase() !== address.toLowerCase());
240
+ if (group.wallets.length === before) {
241
+ throw new Error(`Address ${address} not found in group ${groupId}`);
242
+ }
243
+ group.updatedAt = new Date().toISOString();
244
+ saveStore(store);
245
+ }
246
+ /** Decrypt a wallet's private key (only for in-process signing — never log) */
247
+ export function decryptPrivateKey(stored, password) {
248
+ const pk = decrypt(stored.encryptedPrivateKey, password);
249
+ if (!pk.startsWith('0x')) {
250
+ throw new Error(`Decrypted private key for ${stored.address} is malformed (missing 0x prefix)`);
251
+ }
252
+ return pk;
253
+ }
254
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/wallets/groups/store.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "fourmm",
3
+ "version": "0.1.0",
4
+ "description": "fourMM — agent-first CLI for Four.meme market making on BSC",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "linwe",
8
+ "keywords": [
9
+ "bsc",
10
+ "four.meme",
11
+ "market-making",
12
+ "agent",
13
+ "cli",
14
+ "defi",
15
+ "meme"
16
+ ],
17
+ "bin": {
18
+ "fourmm": "./dist/bin.js"
19
+ },
20
+ "main": "./dist/cli.js",
21
+ "files": [
22
+ "dist",
23
+ "skills",
24
+ "README.md"
25
+ ],
26
+ "engines": {
27
+ "node": ">=22"
28
+ },
29
+ "dependencies": {
30
+ "crypto-js": "^4.2.0",
31
+ "incur": "^0.3.25",
32
+ "viem": "^2.21.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/crypto-js": "^4.2.2",
36
+ "@types/node": "^22.0.0",
37
+ "tsx": "^4.19.0",
38
+ "typescript": "^5.6.0",
39
+ "vitest": "^2.1.0"
40
+ },
41
+ "scripts": {
42
+ "build": "tsc",
43
+ "clean": "rm -rf dist",
44
+ "dev": "tsx src/bin.ts",
45
+ "fourmm": "tsx src/bin.ts",
46
+ "test": "vitest run",
47
+ "test:watch": "vitest",
48
+ "typecheck": "tsc --noEmit"
49
+ }
50
+ }
@@ -0,0 +1,187 @@
1
+ ---
2
+ name: fourmm
3
+ description: fourMM — Agent-first market-making CLI for Four.meme on BSC. Create tokens, trade (buy/sell/sniper), pump/dump via Flashbot bundles, generate volume, manage wallets, transfer funds.
4
+ requires_bin: fourmm
5
+ install: pnpm install && pnpm build && npx fourmm skills add
6
+ ---
7
+
8
+ # fourMM
9
+
10
+ Market-making CLI for [Four.meme](https://four.meme) on BNB Smart Chain.
11
+
12
+ ## Setup
13
+
14
+ ```sh
15
+ # Clone & build
16
+ git clone <repo-url> && cd fourMM
17
+ pnpm install && pnpm build
18
+
19
+ # Install skills for your agent
20
+ npx fourmm skills add
21
+
22
+ # Or start as MCP server
23
+ npx fourmm --mcp
24
+
25
+ # Initialize config (BlockRazor MEV-protected RPC)
26
+ fourmm config init
27
+
28
+ # Create wallets
29
+ fourmm wallet create-group --name main --count 5
30
+
31
+ # Fund wallets
32
+ fourmm transfer out --from <your-funded-wallet> --to-group 1 --value 0.01
33
+ ```
34
+
35
+ **Environment:** Set `FOURMM_PASSWORD` to avoid `--password` on every command.
36
+
37
+ ---
38
+
39
+ ## Intent Routing
40
+
41
+ ### I want to create a token
42
+ ```sh
43
+ fourmm token create --name "Name" --symbol "SYM" --image logo.png --dev-wallet <groupId> [--preset-buy 0.01]
44
+ ```
45
+
46
+ ### I want to buy tokens
47
+ ```sh
48
+ # All wallets in group buy the same amount
49
+ fourmm trade buy --group <id> --token <CA> --amount <BNB> [--slippage 500]
50
+
51
+ # Each wallet buys a different amount
52
+ fourmm trade sniper --group <id> --token <CA> --amounts "0.01,0.02,0.05"
53
+ ```
54
+
55
+ ### I want to sell tokens
56
+ ```sh
57
+ # Sell everything
58
+ fourmm trade sell --group <id> --token <CA> --amount all
59
+
60
+ # Sell 50%
61
+ fourmm trade sell --group <id> --token <CA> --amount 50%
62
+
63
+ # Sell fixed amount
64
+ fourmm trade sell --group <id> --token <CA> --amount 1000000
65
+ ```
66
+
67
+ ### I want to pump the price
68
+ ```sh
69
+ fourmm tools robot-price --group <id> --token <CA> --direction up --target-price <BNB_PER_TOKEN> --amount <BNB_PER_ROUND>
70
+ ```
71
+ All wallets' buys are bundled via Flashbot (`eth_sendBundle`) — atomic, same block, no MEV.
72
+
73
+ ### I want to dump the price
74
+ ```sh
75
+ fourmm tools robot-price --group <id> --token <CA> --direction down --target-price <BNB_PER_TOKEN> --amount <BNB_PER_ROUND>
76
+ ```
77
+
78
+ ### I want to generate volume
79
+ ```sh
80
+ # Atomic buy+sell per wallet (zero net position, generates on-chain volume)
81
+ fourmm tools volume --group <id> --token <CA> --amount <BNB> --rounds 10
82
+
83
+ # Single atomic round-trip per wallet
84
+ fourmm trade batch --group <id> --token <CA> --amount <BNB>
85
+ ```
86
+
87
+ ### I want to distribute holder count
88
+ ```sh
89
+ # Group A pays BNB, Group B receives tokens (via Router.turnover)
90
+ fourmm tools turnover --from-group <A> --to-group <B> --token <CA> --amount <BNB>
91
+ ```
92
+
93
+ ### I want to check my positions
94
+ ```sh
95
+ fourmm query monitor --group <id> --token <CA> # Holdings + PnL
96
+ fourmm query balance --address <ADDR> # BNB balance
97
+ fourmm query price --token <CA> # Live price
98
+ fourmm query transactions --group <id> # Trade history
99
+ ```
100
+
101
+ ### I want to manage wallets
102
+ ```sh
103
+ fourmm wallet create-group --name <NAME> --count <N> # Create group
104
+ fourmm wallet list-groups # List all groups
105
+ fourmm wallet group-info --id <ID> # View wallets
106
+ fourmm wallet generate --group <ID> --count <N> # Add wallets
107
+ fourmm wallet add --group <ID> --private-key <KEY> # Import key
108
+ fourmm wallet remove --group <ID> --address <ADDR> # Remove wallet
109
+ fourmm wallet import --group <ID> --file wallets.csv # Bulk import
110
+ fourmm wallet export --group <ID> --file out.csv # Export keys
111
+ fourmm wallet delete-group --id <ID> --force # Delete group
112
+ fourmm wallet overview # Aggregate PnL
113
+ ```
114
+
115
+ ### I want to move funds
116
+ ```sh
117
+ fourmm transfer out --from <ADDR> --to-group <ID> --value <BNB> # Distribute
118
+ fourmm transfer in --to <ADDR> --from-group <ID> --amount all # Collect all
119
+ fourmm transfer in --to <ADDR> --from-group <ID> --amount reserve --value 0.002 # Keep reserve
120
+ fourmm transfer many-to-many --from-group <A> --to-group <B> # Pair transfer
121
+ ```
122
+
123
+ ### I want to check a token
124
+ ```sh
125
+ fourmm token info --ca <CA> # Full on-chain metadata
126
+ fourmm token identify --ca <CA> # Variant: standard / anti-sniper / tax / x-mode
127
+ fourmm token graduate-status --ca <CA> # Graduation progress
128
+ fourmm token pool --ca <CA> # Pool / liquidity info
129
+ fourmm query kline --token <CA> # K-line candles (graduated only)
130
+ ```
131
+
132
+ ### I want to change config
133
+ ```sh
134
+ fourmm config get # View all
135
+ fourmm config get --key rpcUrl # View one
136
+ fourmm config set rpcUrl <URL> # Change RPC
137
+ fourmm config set defaultSlippageBps 500 # Change slippage
138
+ fourmm config init # Reset to defaults
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Key Concepts
144
+
145
+ **Wallet Groups:** Wallets are organized in numbered groups (1, 2, 3...). Each group has N wallets. Trade/volume/transfer commands operate on entire groups.
146
+
147
+ **Bonding Curve vs PancakeSwap:** Tokens start on Four.meme's bonding curve. After raising 18 BNB, they "graduate" to PancakeSwap. fourMM auto-detects and routes accordingly.
148
+
149
+ **Token Variants:**
150
+ - `standard` — normal token, fully supported
151
+ - `anti-sniper-fee` — dynamic fee decreasing block-by-block, supported (use higher slippage)
152
+ - `tax-token` — fourMM refuses (round-trip loss too high)
153
+ - `x-mode` — fourMM refuses (special buy interface)
154
+
155
+ **Slippage:** In basis points. 300 = 3%, 500 = 5%, 1500 = 15%. Higher = more tolerance for price movement.
156
+
157
+ **Flashbot Bundle:** `robot-price` signs all wallets' txs offline and submits via `eth_sendBundle`. All execute in the same block or none do. No public mempool exposure.
158
+
159
+ **Router Contract:** `0xd62c2fd94176f98424af83e4b9a333d454b2216c` — atomic `volume()` (buy+sell), `turnover()` (buy for another wallet), `volumePancake()` (graduated tokens).
160
+
161
+ ---
162
+
163
+ ## Common Workflows
164
+
165
+ ### Launch + Market Make
166
+ ```sh
167
+ fourmm wallet create-group --name launch --count 10
168
+ fourmm transfer out --from <funded-wallet> --to-group 1 --value 0.05
169
+ fourmm token create --name "My Token" --symbol "MTK" --image logo.png --dev-wallet 1 --preset-buy 0.01
170
+ # Wait for CA in output, then:
171
+ fourmm tools volume --group 1 --token <CA> --amount 0.01 --rounds 20
172
+ fourmm tools robot-price --group 1 --token <CA> --direction up --target-price 0.00001 --amount 0.01
173
+ ```
174
+
175
+ ### Snipe + Dump
176
+ ```sh
177
+ fourmm trade sniper --group 1 --token <CA> --amounts "0.05,0.03,0.02,0.01,0.01"
178
+ fourmm query monitor --group 1 --token <CA>
179
+ # When ready:
180
+ fourmm trade sell --group 1 --token <CA> --amount all
181
+ fourmm transfer in --to <your-wallet> --from-group 1 --amount all
182
+ ```
183
+
184
+ ### Volume Bot
185
+ ```sh
186
+ fourmm tools volume --group 1 --token <CA> --amount 0.01 --rounds 50 --interval 3000
187
+ ```