@evergreen-stellar/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/LICENSE +21 -0
- package/README.md +185 -0
- package/dist/evergreen.mjs +2051 -0
- package/dist/evergreen.mjs.map +7 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Apex (Fatih Maulana, Rakha)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# `evergreen` (CLI)
|
|
2
|
+
|
|
3
|
+
**Find out when your Soroban contract's data expires, and what keeping it alive costs.**
|
|
4
|
+
|
|
5
|
+
Soroban ledger entries have a TTL measured in ledgers, not seconds. Every closed ledger decrements it. When it runs out the entry is **archived** — or for temporary entries **deleted outright** — and your contract stops working until someone pays to restore it. `evergreen scan` tells you how long you have, what happens when time runs out, and what an extension would cost.
|
|
6
|
+
|
|
7
|
+
`scan` is read-only: it never signs or submits. [Manual extension](#manual-extension-testnet) simulates by default and requires explicit flags before signing or submitting.
|
|
8
|
+
|
|
9
|
+
## Quickstart
|
|
10
|
+
|
|
11
|
+
Scan any Testnet contract directly from npm — you do not need to own it, and no
|
|
12
|
+
wallet or signup is involved:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npx @evergreen-stellar/cli scan CANZNTAW7DYMCZ6EAY5BP672H4AL2O2HVRBP4O4HRUEZRATHQRRLXL6L
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
That contract is our public test subject, so the command works before you have one of your own.
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Coverage: known keys only — contract storage has NOT been fully enumerated.
|
|
22
|
+
CANZNTAW7DYMCZ6EAY5BP672H4AL2O2HVRBP4O4HRUEZRATHQRRLXL6L: 0 explicit data key(s)
|
|
23
|
+
No data keys were supplied, so any further entries are unread.
|
|
24
|
+
|
|
25
|
+
HEALTHY instance AAAABgAAAA…
|
|
26
|
+
contracts: CANZNTAW7DYMCZ6EAY5BP672H4AL2O2HVRBP4O4HRUEZRATHQRRLXL6L
|
|
27
|
+
remaining: 1,424,255 ledgers — live
|
|
28
|
+
ends at: ledger 6,025,589
|
|
29
|
+
approx: 2026-12-01T18:58:54Z (estimate — ledgers are the truth)
|
|
30
|
+
observed: ledger 4,601,334
|
|
31
|
+
health: HEALTHY — Above threshold.
|
|
32
|
+
|
|
33
|
+
HEALTHY code AAAAB8flXw…
|
|
34
|
+
…
|
|
35
|
+
|
|
36
|
+
Worst entry health: HEALTHY (warn below 120,960 · act below 17,280 ledgers)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**That coverage block is the first thing printed, deliberately.** A scan reads the keys it is given and cannot enumerate a contract's storage, so `HEALTHY` means *"everything I was asked to check is healthy"* and never *"this contract is healthy"*. Ledger numbers drift between runs; yours will differ.
|
|
40
|
+
|
|
41
|
+
### What will it cost to keep alive?
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx @evergreen-stellar/cli scan CANZNTAW7DYMCZ6EAY5BP672H4AL2O2HVRBP4O4HRUEZRATHQRRLXL6L --cost --ledgers 518400
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
Cost to extend 2 entries by 518,400 more ledgers
|
|
49
|
+
total about 0.82 XLM (8,212,414 stroops) — what leaves the account
|
|
50
|
+
rent about 0.82 XLM (8,188,798 stroops)
|
|
51
|
+
fees about 0.0024 XLM (23,616 stroops) — non-refundable resource + base fee
|
|
52
|
+
|
|
53
|
+
99% of that rent is one entry (AAAAB8flXw…). Code entries hold the
|
|
54
|
+
Wasm and are usually the expensive one — and the one shared between contracts.
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Prices come from simulating the real operation against the network, not from a formula. **They are estimates**: rent pricing moves with network state and has differed ~18% between days, which is why the figures are rounded and labelled "about".
|
|
58
|
+
|
|
59
|
+
### For CI
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npx @evergreen-stellar/cli scan <contract-id> --threshold 120960 --require-declared-scope --json
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`--threshold N` sets the act-now boundary in ledgers for this run. A repository
|
|
66
|
+
that wants about a week of warning can use `120960`; the CLI default remains
|
|
67
|
+
`17280` (about one day). Both health tiers move with the selected threshold.
|
|
68
|
+
Exit code `0` is healthy, `1` is at or below the threshold, `2` is an error and
|
|
69
|
+
`3` is an incomplete scan. See [Coverage and exit codes](#coverage-and-exit-codes)
|
|
70
|
+
— the distinction between `1` and `3` matters more than it looks.
|
|
71
|
+
|
|
72
|
+
## The two things people get wrong
|
|
73
|
+
|
|
74
|
+
**Archived is not deleted.** Instance, code and persistent entries are *archived* and can be restored. **Temporary entries are deleted and cannot be.** The output always says which, because telling someone their recoverable data is gone — or that their unrecoverable data can be restored — is worse than saying nothing.
|
|
75
|
+
|
|
76
|
+
**Contracts built from the same Wasm share ONE code entry.** If you deploy 40 vaults from one Wasm, that is 40 contracts and one `ContractCode` entry. When it expires, all 40 stop working at the same moment — and a per-contract scan shows 40 healthy contracts right up until they die together. `evergreen` deduplicates by ledger key, tells you when an entry is shared, and grades severity by how many contracts an entry takes down.
|
|
77
|
+
|
|
78
|
+
A scan of a single contract says so explicitly, because it *cannot* know who else built from that Wasm:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
⚠ sharing: code entries are shared by every contract built from the same Wasm.
|
|
82
|
+
This scan saw 1. Others may depend on this entry and are invisible here —
|
|
83
|
+
pass them together to see the real blast radius.
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Options
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
--json machine-readable output; the complete record
|
|
90
|
+
--cost [--ledgers N] estimate the cost of N more ledgers (default 518,400 ≈ 30 days)
|
|
91
|
+
--keys-file <path> supply explicit persistent/temporary data keys
|
|
92
|
+
--no-data-keys assert this contract has none beyond its instance
|
|
93
|
+
--require-declared-scope also exit 3 when scope was not declared (for CI on a contract you own)
|
|
94
|
+
--threshold N act-now threshold in ledgers (default 17,280)
|
|
95
|
+
--help usage, without connecting
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`SOROBAN_RPC_URL` overrides the default public Testnet RPC. The live network passphrase is checked before anything is read: pointed at mainnet, the command refuses and says so.
|
|
99
|
+
|
|
100
|
+
`--ledgers N` means **"give me N more ledgers."** The protocol wants an absolute target rather than an increment, so the CLI computes that for you and caps it at the protocol maximum, saying so when it does.
|
|
101
|
+
|
|
102
|
+
## Data keys
|
|
103
|
+
|
|
104
|
+
The optional keys file contains only this property:
|
|
105
|
+
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"dataKeys": ["<canonical base64 XDR LedgerKey>"]
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Supply `ContractData` keys for the requested contract, with persistent or temporary durability. Instance keys, code keys, foreign-contract keys and malformed XDR are rejected. Instance/code keys are discovered automatically; duplicate supplied keys count once. See the [recorded public A keys](https://github.com/Fatihmaull/evergreen/blob/main/docs/evidence/2026-09-08-scan-entry-types/data-keys.json) for a concrete example. These keys identify that test contract only; a missing entry is possible on later reads.
|
|
113
|
+
|
|
114
|
+
## Coverage and exit codes
|
|
115
|
+
|
|
116
|
+
`getLedgerEntries` reads known keys; it does not enumerate arbitrary contract storage. Human output states this limitation, and JSON includes `coverage.mode: "known-keys"` plus each contract's count of unique, validated supplied data keys. A live instance is not evidence of healthy persistent storage. With no keys file, the command reads instance/code only.
|
|
117
|
+
|
|
118
|
+
| Exit | Meaning, in precedence order |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `2` | Invalid arguments/input/response, network refusal or RPC failure |
|
|
121
|
+
| `3` | The scan came back **degraded** — an entry not returned, a TTL unavailable, an executable that cannot be followed, or nothing observed |
|
|
122
|
+
| `1` | All observations are available and at least one TTL is at or below the selected threshold (default 17,280 ledgers) |
|
|
123
|
+
| `0` | Everything the command was asked to check has known TTL above the selected threshold |
|
|
124
|
+
|
|
125
|
+
**`0` means "everything I was asked to check is healthy", never "this contract is healthy".** The command reads the keys it is given and cannot enumerate storage, so coverage is printed on every scan and belongs in how you read the result.
|
|
126
|
+
|
|
127
|
+
`--no-data-keys` asserts that this contract has no data keys beyond its instance, and is mutually exclusive with `--keys-file`. **Only the contract's author can know that** — it is a caller declaration recorded as `coverage.noDataKeysDeclaredByContract[id]: true`, never an on-chain completeness check. An empty keys file says "here are my keys: none"; only this flag says "there are none".
|
|
128
|
+
|
|
129
|
+
`--require-declared-scope` additionally exits `3` when a contract's scope was not declared. **Use it in CI on a contract you own** — `evergreen-check` sets it by default. It is off otherwise, because scanning a contract you did not write makes declaring scope impossible, and an exit code every default invocation triggers is not a signal. Programmatic callers get the same choice through `exitCodeFor(result, threshold, { requireDeclaredScope })`.
|
|
130
|
+
|
|
131
|
+
Precedence is **2 > 3 > 1 > 0**. If a low TTL is observed alongside a missing entry, exit is `3` and JSON still contains both findings. Exit `1` reports low TTL; it never requests or authorizes an extension — a future engine must evaluate observations, issues, policy, payer and budgets itself. See [ADR-006](https://github.com/Fatihmaull/evergreen/blob/main/docs/adr/ADR-006-scan-health-exit-codes.md), accepted 2026-09-10 as amended.
|
|
132
|
+
|
|
133
|
+
Zero applies only to the supplied/discovered keys; it never guarantees complete storage coverage. Missing entries are reported as issues, not asserted to be archived or deleted. Temporary entries expire by deletion; other supported entry types archive. Zero remaining ledgers is still the final live ledger.
|
|
134
|
+
|
|
135
|
+
`--json` emits `ScanResult` alone on stdout once scanning starts, including partial results. Argument/file/connection failures emit a diagnostic on stderr and exit `2`. Successful entries survive another batch's failure. Dates are approximate display projections; each entry's own observation ledger drives its TTL math.
|
|
136
|
+
|
|
137
|
+
## Publication
|
|
138
|
+
|
|
139
|
+
The public npm package is `@evergreen-stellar/cli`, with command name `evergreen`.
|
|
140
|
+
`publishConfig.access: public` is part of the package manifest. The `scan` command
|
|
141
|
+
is read-only and submits no transactions.
|
|
142
|
+
|
|
143
|
+
## Manual extension (Testnet)
|
|
144
|
+
|
|
145
|
+
Simulate using your funded public Testnet payer; the package is already built:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
npx @evergreen-stellar/cli extend <contract-id> --ledgers 1000 --source-account <G-public-account> --json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`--ledgers N` adds N ledgers to each selected entry. The target is computed separately for every entry and capped at `max_entry_ttl - 1` from live network configuration. Capping is reported. Instance only is the default; `--keys-file keys.json` adds explicit persistent/temporary keys. Shared Wasm requires `--include-code`: extending it also benefits contracts outside this scan. Selected scope never establishes whole-contract protection. Expired or unreadable selected keys are refused; restore is not automated.
|
|
152
|
+
|
|
153
|
+
The public payer can also come from `EVERGREEN_SOURCE_ACCOUNT`. There is no fallback payer and no automatic funding. Simulation does not resolve a secret, sign, or submit. A zero exit means simulation completed, not that TTL changed.
|
|
154
|
+
|
|
155
|
+
After reviewing the selected keys, target and fee, an explicit live request is:
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
npx @evergreen-stellar/cli extend <contract-id> --ledgers 1000 --source-account <G-public-account> --submit --secret-env EVERGREEN_SECRET_KEY --max-fee-stroops <reviewed-total-budget> --json
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Export the secret through your private shell environment or secret manager first; never paste it into command arguments or commit it. `--secret-env` names a variable, not a secret. This command does not automatically load `.env`. A wrong key/payer, operation, footprint, network, fee or validity interval is rejected before signing. This plain local adapter is not the W3 on-chain policy signer.
|
|
162
|
+
|
|
163
|
+
The fee cap bounds the sum of prepared envelope fees for the command, in integer stroops. Each entry gets one sequential transaction with a fresh account sequence. Fees shown are prepared upper bounds, not receipts. The cap is never silently increased. The command stops on any failure and reports previous outcomes and unattempted keys.
|
|
164
|
+
|
|
165
|
+
Previews go to stderr **before signing**, including the prepared hash (not yet sent); JSON stdout contains one final report. Preserve both outputs. Exit 0 requires all entries to be simulated, justified no-ops, or confirmed and post-verified live successes. Exit 2 covers errors and partial/unconfirmed outcomes. A `submitted` record is not success: reconcile that exact hash through RPC `getTransaction` and re-read TTL before retrying. A timeout or NOT_FOUND does not authorize a replacement. No automatic retry is performed, including for TRY_AGAIN_LATER. If a process was interrupted, the prepared hash printed on stderr identifies the possible transaction.
|
|
166
|
+
|
|
167
|
+
Live success checks the absolute expiry ledger against the before observation and inclusion ledger plus target. Later remaining-TTL readings naturally decrease as ledgers advance. A concurrent third-party extension may also improve post-state; the CLI does not prove exclusive causation.
|
|
168
|
+
|
|
169
|
+
For the sprint proof use **A's instance only** after review. Do not submit for B/C or shared A/B/C Wasm while decay proofs are pending. [D11 simulation evidence](https://github.com/Fatihmaull/evergreen/blob/main/docs/evidence/2026-09-10-manual-extend-simulation/README.md) is unsigned and is not a live extension proof.
|
|
170
|
+
## Storage advice
|
|
171
|
+
|
|
172
|
+
Add `--optimize` to a scan for conditional design recommendations with evidence:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
npx @evergreen-stellar/cli scan <contract-id> --keys-file keys.json --optimize
|
|
176
|
+
npx @evergreen-stellar/cli scan <contract-id> --keys-file keys.json --optimize --cost --ledgers 1000 --json
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The report covers temporary retention, persistent-data durability choices, and shared-code dependencies. It reads network minimum lifetimes but requires no payer or simulation unless `--cost` is also requested. Plain scans retain their existing output and network work.
|
|
180
|
+
|
|
181
|
+
Recommendations are scoped to observed keys. They do not infer payload size, duplicate contents, application requirements or unseen consumers. Instance storage is never suggested for conversion to temporary. A persistent-data suggestion applies only when the data is disposable or recomputable; durable state should remain persistent. No storage change, extension or automatic migration happens.
|
|
182
|
+
|
|
183
|
+
Current per-key rent quotes retain their pricing context when `--cost` is supplied. Missing, invalid or stale quotes remain unavailable, never zero. The historical A rent comparison is separately labelled; its approximately 1.95x ratio is not a promise of savings. Current network minimum lifetime is not the current entry's expiry: the report retains each entry's actual observed TTL. If settings cannot be read, a dated historical reference and limitation replace them.
|
|
184
|
+
|
|
185
|
+
JSON adds `optimization` alongside unchanged scan/health/cost fields. Advice does not change scan exit precedence; a healthy exit is not a complete-storage audit. Even when optional pricing fails, the scan and qualified advice remain visible. [Read-only A evidence](https://github.com/Fatihmaull/evergreen/blob/main/docs/evidence/2026-09-10-storage-advice/README.md) includes full RPC and both output modes.
|