Skip to content

Commit 66db5ba

Browse files
committed
feat: add wallet import flow and release automation
1 parent 3ba1190 commit 66db5ba

38 files changed

Lines changed: 2270 additions & 876 deletions

.changeset/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Changesets
2+
3+
This directory stores release notes and semver intent for pending changes.
4+
5+
Run `pnpm changeset` after making a user-visible change, then commit the generated markdown file with the code change.

.changeset/config.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"$schema": "https://unpkg.com/@changesets/config@3.0.3/schema.json",
3+
"changelog": false,
4+
"commit": false,
5+
"fixed": [],
6+
"linked": [],
7+
"access": "public",
8+
"baseBranch": "main",
9+
"updateInternalDependencies": "patch",
10+
"ignore": []
11+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"solana-agent-wallet-ops": minor
3+
---
4+
5+
Add wallet import and storage cleanup work, tighten the CLI structure, and introduce automated version PRs plus GitHub releases.

.github/workflows/ci.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- main
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout
15+
uses: actions/checkout@v4
16+
17+
- name: Setup pnpm
18+
uses: pnpm/action-setup@v4
19+
with:
20+
version: 8.15.1
21+
22+
- name: Setup Node.js
23+
uses: actions/setup-node@v4
24+
with:
25+
node-version: 20
26+
cache: pnpm
27+
28+
- name: Install dependencies
29+
run: pnpm install --frozen-lockfile
30+
31+
- name: Build
32+
run: pnpm build
33+
34+
- name: Test
35+
run: pnpm test
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
name: GitHub Release
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
paths:
8+
- package.json
9+
10+
permissions:
11+
contents: write
12+
13+
jobs:
14+
release:
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 0
22+
23+
- name: Read package versions
24+
id: package
25+
run: |
26+
CURRENT_VERSION=$(node -e "console.log(JSON.parse(require('node:fs').readFileSync('package.json', 'utf8')).version)")
27+
PREVIOUS_VERSION=$(git show "${{ github.event.before }}:package.json" 2>/dev/null | node -e "const fs = require('node:fs'); const input = fs.readFileSync(0, 'utf8').trim(); console.log(input ? JSON.parse(input).version : '');" || true)
28+
echo "current=$CURRENT_VERSION" >> "$GITHUB_OUTPUT"
29+
echo "previous=$PREVIOUS_VERSION" >> "$GITHUB_OUTPUT"
30+
31+
- name: Fetch tags
32+
run: git fetch --tags --force
33+
34+
- name: Check for version bump
35+
id: version
36+
run: |
37+
if [ "${{ steps.package.outputs.current }}" = "${{ steps.package.outputs.previous }}" ]; then
38+
echo "changed=false" >> "$GITHUB_OUTPUT"
39+
else
40+
echo "changed=true" >> "$GITHUB_OUTPUT"
41+
fi
42+
43+
- name: Check for existing tag
44+
id: tag
45+
if: steps.version.outputs.changed == 'true'
46+
run: |
47+
if git rev-parse "v${{ steps.package.outputs.current }}" >/dev/null 2>&1; then
48+
echo "exists=true" >> "$GITHUB_OUTPUT"
49+
else
50+
echo "exists=false" >> "$GITHUB_OUTPUT"
51+
fi
52+
53+
- name: Create tag
54+
if: steps.version.outputs.changed == 'true' && steps.tag.outputs.exists == 'false'
55+
run: |
56+
git tag "v${{ steps.package.outputs.current }}"
57+
git push origin "v${{ steps.package.outputs.current }}"
58+
59+
- name: Publish GitHub release
60+
if: steps.version.outputs.changed == 'true' && steps.tag.outputs.exists == 'false'
61+
uses: softprops/action-gh-release@v2
62+
with:
63+
tag_name: v${{ steps.package.outputs.current }}
64+
generate_release_notes: true
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: Version Packages
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
permissions:
9+
contents: write
10+
pull-requests: write
11+
12+
jobs:
13+
version:
14+
if: github.actor != 'github-actions[bot]'
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 0
22+
23+
- name: Setup pnpm
24+
uses: pnpm/action-setup@v4
25+
with:
26+
version: 8.15.1
27+
28+
- name: Setup Node.js
29+
uses: actions/setup-node@v4
30+
with:
31+
node-version: 20
32+
cache: pnpm
33+
34+
- name: Install dependencies
35+
run: pnpm install --frozen-lockfile
36+
37+
- name: Create or update version PR
38+
uses: changesets/action@v1
39+
with:
40+
version: pnpm release:version
41+
commit: "chore: version packages"
42+
title: "chore: version packages"
43+
env:
44+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
node_modules
22
dist
33
.DS_Store
4+
.agents
5+
.pnpm-store
46
*.log
57
exports
68
data/wallet-sets/

README.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This is not a consumer wallet app, not a GUI, and not tied to a single host such
1414
## Features
1515

1616
- Bulk create Solana wallets and save them as named wallet sets
17+
- Import wallet JSON into local wallet-set storage
1718
- List wallet sets or inspect wallets inside a set
1819
- Store wallet sets in local SQLite with indexed lookups
1920
- Check balances in bulk
@@ -47,6 +48,56 @@ Optional local build:
4748
pnpm build
4849
```
4950

51+
## Releases
52+
53+
This repo now has GitHub-backed release automation with Changesets.
54+
55+
Local release workflow:
56+
57+
1. Run `pnpm changeset` for any user-visible change.
58+
2. Commit the generated file in `.changeset/` with the code change.
59+
3. Merge to `main`.
60+
4. GitHub Actions opens or updates a `chore: version packages` PR.
61+
5. After that PR is merged, GitHub Actions tags `v<version>` and creates a GitHub Release.
62+
63+
Useful commands:
64+
65+
```bash
66+
pnpm changeset
67+
pnpm release:status
68+
pnpm release:version
69+
```
70+
71+
## Agent Skill
72+
73+
This repo now exposes the repository root as the skill package for GitHub distribution:
74+
75+
```text
76+
SKILL.md
77+
agents/openai.yml
78+
```
79+
80+
That root-level skill is meant for Codex-style agents that need a safe, repeatable workflow for:
81+
82+
- creating wallet sets
83+
- inspecting balances
84+
- exporting public addresses
85+
- previewing and executing SOL or SPL batch transfers
86+
- running SplitNOW quote, order, and status flows
87+
88+
The skill points agents at the existing repo docs instead of copying them. It also bakes in the important operational rules:
89+
90+
- keep wallet DBs and API keys outside the repo
91+
- use `devnet` by default unless the user explicitly wants `mainnet-beta`
92+
- preview before any live transfer
93+
- preserve the sender rent reserve for SOL transfers
94+
95+
If an execution environment blocks `pnpm tsx` because of `tsx` IPC restrictions, agents can use the equivalent fallback form:
96+
97+
```bash
98+
node --import tsx src/cli/<command>.ts ...
99+
```
100+
50101
## Command Examples
51102

52103
Create a wallet set:
@@ -61,6 +112,18 @@ Create a wallet set with an explicit external DB path:
61112
pnpm tsx src/cli/create-wallets.ts --set test-set --count 25 --network devnet --db-path ~/.solana-agent-wallet-ops/wallets.sqlite
62113
```
63114

115+
Import a wallet-set JSON file into SQLite storage:
116+
117+
```bash
118+
pnpm tsx src/cli/import-wallets.ts --from ./wallet-set.json
119+
```
120+
121+
Import a standalone wallet JSON file as a one-wallet set:
122+
123+
```bash
124+
pnpm tsx src/cli/import-wallets.ts --from ./wallet.json --set imported-treasury --network devnet
125+
```
126+
64127
List all wallet sets:
65128

66129
```bash
@@ -128,6 +191,18 @@ Preview a SplitNOW order from an existing quote into a wallet set:
128191
pnpm tsx src/cli/splitnow-order.ts --quote-id QUOTE123 --to-set campaign
129192
```
130193

194+
Preview a SplitNOW order from a CSV recipient list:
195+
196+
```bash
197+
pnpm tsx src/cli/splitnow-order.ts --quote-id QUOTE123 --to-csv ./splitnow-recipients.csv
198+
```
199+
200+
Preview a SplitNOW order with a specific exchanger from the quote:
201+
202+
```bash
203+
pnpm tsx src/cli/splitnow-order.ts --quote-id QUOTE123 --to-set campaign --exchanger changenow
204+
```
205+
131206
Create the real SplitNOW order after review:
132207

133208
```bash
@@ -140,6 +215,27 @@ Track SplitNOW order status:
140215
pnpm tsx src/cli/splitnow-status.ts --order-id ABC123
141216
```
142217

218+
## SplitNOW Notes
219+
220+
- `splitnow-quote` creates a floating-rate quote and prints the exchangers returned by SplitNOW, sorted by estimated output.
221+
- `splitnow-order` consumes an existing quote and builds a multi-wallet order from either `--to-set` or `--to-csv`.
222+
- The current implementation splits funds evenly across recipients. It does not support per-recipient custom percentages yet.
223+
- The current implementation uses one exchanger per order. By default it selects `best`, or you can pass `--exchanger <id>` to force one of the exchanger ids returned by the quote.
224+
- SplitNOW recipient sources are address-only:
225+
- wallet set via `--to-set`
226+
- CSV via `--to-csv`
227+
- SplitNOW recipient CSV columns:
228+
- required: `public_key`
229+
- optional: `label`
230+
- SplitNOW orders reject empty recipient lists, duplicate recipient public keys, and recipient counts above 100.
231+
- Before creating an order, the client checks SplitNOW deposit limits and rejects orders that are below the minimum required deposit for the selected asset and recipient count.
232+
- Creating an order does not move funds by itself. It returns:
233+
- `orderId`
234+
- `depositAddress`
235+
- `depositAmount`
236+
- After order creation, you still need to send the deposit to the returned address, then track progress with `splitnow-status`.
237+
- Use `--api-url` only when intentionally pointing at a non-default SplitNOW API endpoint.
238+
143239
Notes for CSV recipient input:
144240

145241
- If the CSV includes an `amount` column, per-row amounts are used.
@@ -157,6 +253,12 @@ Storage path overrides:
157253
- repo-local DBs are rejected unless you pass `--allow-repo-db` or set `SAWO_ALLOW_REPO_DB=1`
158254
- SplitNOW API key: `SPLITNOW_API_KEY=...`
159255

256+
Wallet import formats:
257+
258+
- wallet-set JSON file with `set_name`, `created_at`, `network`, and `wallets`
259+
- standalone wallet JSON object with `label`, `public_key`, and `secret_key_base58`
260+
- JSON array of wallet entries when you also pass `--set` and `--network`
261+
160262
## Storage Format
161263

162264
Primary wallet storage lives in a local SQLite database:

0 commit comments

Comments
 (0)