@rozoai/checkout 0.1.2 → 0.1.3
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 +30 -9
- package/docs/QUICKSTART.es.md +4 -2
- package/docs/QUICKSTART.ja.md +3 -2
- package/docs/QUICKSTART.md +7 -3
- package/docs/QUICKSTART.zh.md +3 -3
- package/docs/README.es.md +10 -2
- package/docs/README.ja.md +9 -2
- package/docs/README.zh.md +8 -2
- package/llms.txt +7 -1
- package/package.json +1 -1
- package/scripts/dist/cli.js +81 -7
- package/scripts/dist/create-order.js +53 -2
- package/scripts/dist/quote.js +4 -0
- package/scripts/dist/send-evm.js +4 -0
- package/scripts/dist/send-sol.js +4 -0
- package/scripts/dist/status.js +39 -1
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ npx @rozoai/checkout pay <coinbase-link> --with usdt-solana
|
|
|
32
32
|
| Polygon | `usdt-polygon` `usdc-polygon` | `137` | 6 decimals |
|
|
33
33
|
| Base | `usdc-base` | `8453` | 6 decimals |
|
|
34
34
|
| Solana | `usdt-solana` `usdc-solana` | `900` | SPL; native SOL not supported |
|
|
35
|
-
| Stellar | `usdc-stellar` | `1500` | memo required — shown in the deposit block |
|
|
35
|
+
| Stellar | `usdc-stellar` | `1500` | `MEMO_TEXT` memo required — shown in the deposit block |
|
|
36
36
|
| Bitcoin Lightning | `btc-lightning` | `lightning` | BOLT11; amounts in satoshis |
|
|
37
37
|
|
|
38
38
|
Native gas coins (SOL, BNB, ETH, MATIC) and on-chain BTC are not accepted.
|
|
@@ -169,7 +169,8 @@ npx @rozoai/checkout pay <coinbase-link> --with usdt-solana
|
|
|
169
169
|
```
|
|
170
170
|
|
|
171
171
|
Wallet: any wallet, no key. `--send` signs locally using your Solana CLI
|
|
172
|
-
keypair or an encrypted keystore
|
|
172
|
+
keypair or an encrypted keystore, and supports EVM chains and Solana only —
|
|
173
|
+
Stellar and Lightning are Mode A only.
|
|
173
174
|
</details>
|
|
174
175
|
|
|
175
176
|
<details>
|
|
@@ -183,7 +184,8 @@ npx @rozoai/checkout pay <coinbase-link> --with usdt-solana
|
|
|
183
184
|
```
|
|
184
185
|
|
|
185
186
|
Wallet: any wallet, no key. `--send` signs locally using your Solana CLI
|
|
186
|
-
keypair or an encrypted keystore
|
|
187
|
+
keypair or an encrypted keystore, and supports EVM chains and Solana only —
|
|
188
|
+
Stellar and Lightning are Mode A only.
|
|
187
189
|
</details>
|
|
188
190
|
|
|
189
191
|
<details>
|
|
@@ -197,7 +199,8 @@ npx @rozoai/checkout pay <coinbase-link> --with usdt-solana
|
|
|
197
199
|
```
|
|
198
200
|
|
|
199
201
|
Wallet: any wallet, no key. `--send` signs locally using your Solana CLI
|
|
200
|
-
keypair or an encrypted keystore
|
|
202
|
+
keypair or an encrypted keystore, and supports EVM chains and Solana only —
|
|
203
|
+
Stellar and Lightning are Mode A only.
|
|
201
204
|
</details>
|
|
202
205
|
|
|
203
206
|
<details>
|
|
@@ -211,7 +214,8 @@ npx @rozoai/checkout pay <coinbase-link> --with usdt-solana
|
|
|
211
214
|
```
|
|
212
215
|
|
|
213
216
|
Wallet: any wallet, no key. `--send` signs locally using your Solana CLI
|
|
214
|
-
keypair or an encrypted keystore
|
|
217
|
+
keypair or an encrypted keystore, and supports EVM chains and Solana only —
|
|
218
|
+
Stellar and Lightning are Mode A only.
|
|
215
219
|
</details>
|
|
216
220
|
|
|
217
221
|
<details>
|
|
@@ -226,7 +230,8 @@ npx @rozoai/checkout pay <coinbase-link> --with usdt-solana
|
|
|
226
230
|
```
|
|
227
231
|
|
|
228
232
|
Wallet: any wallet, no key. `--send` signs locally using your Solana CLI
|
|
229
|
-
keypair or an encrypted keystore
|
|
233
|
+
keypair or an encrypted keystore, and supports EVM chains and Solana only —
|
|
234
|
+
Stellar and Lightning are Mode A only.
|
|
230
235
|
</details>
|
|
231
236
|
|
|
232
237
|
<details>
|
|
@@ -240,7 +245,8 @@ openclaw agent exec "Pay this OpenRouter link with USDT on Solana by running: np
|
|
|
240
245
|
```
|
|
241
246
|
|
|
242
247
|
Wallet: any wallet, no key. `--send` signs locally using your Solana CLI
|
|
243
|
-
keypair or an encrypted keystore
|
|
248
|
+
keypair or an encrypted keystore, and supports EVM chains and Solana only —
|
|
249
|
+
Stellar and Lightning are Mode A only.
|
|
244
250
|
</details>
|
|
245
251
|
|
|
246
252
|
<details>
|
|
@@ -255,7 +261,8 @@ npx @rozoai/checkout pay <coinbase-link> --with usdt-solana
|
|
|
255
261
|
```
|
|
256
262
|
|
|
257
263
|
Wallet: any wallet, no key. `--send` signs locally using your Solana CLI
|
|
258
|
-
keypair or an encrypted keystore
|
|
264
|
+
keypair or an encrypted keystore, and supports EVM chains and Solana only —
|
|
265
|
+
Stellar and Lightning are Mode A only.
|
|
259
266
|
</details>
|
|
260
267
|
|
|
261
268
|
<details>
|
|
@@ -304,7 +311,8 @@ If the agent has no shell but can make HTTP requests, it can drive the four
|
|
|
304
311
|
public endpoints directly — see [how it works](docs/how-it-works.md).
|
|
305
312
|
|
|
306
313
|
Wallet: any wallet, no key. `--send` signs locally using your Solana CLI
|
|
307
|
-
keypair or an encrypted keystore
|
|
314
|
+
keypair or an encrypted keystore, and supports EVM chains and Solana only —
|
|
315
|
+
Stellar and Lightning are Mode A only.
|
|
308
316
|
</details>
|
|
309
317
|
|
|
310
318
|
## Three rules worth knowing
|
|
@@ -313,6 +321,10 @@ keypair or an encrypted keystore.
|
|
|
313
321
|
cached response or a screenshot.
|
|
314
322
|
- **Send the exact amount shown.** It is normally larger than the invoice — it
|
|
315
323
|
includes the bridge and network fees.
|
|
324
|
+
- **Stellar deposits carry a memo, and it is `MEMO_TEXT`** — even when it looks
|
|
325
|
+
like a number (`65371582` is text, not an id). Sending it as `MEMO_ID`
|
|
326
|
+
produces a different memo and the payment will not be matched. The deposit
|
|
327
|
+
block states the type as `receiverMemoType`.
|
|
316
328
|
- **Never pay a funded order twice.** If a payment has already been detected,
|
|
317
329
|
stop and get a human to reconcile it; a second payment to a one-time address
|
|
318
330
|
is not guaranteed to be credited.
|
|
@@ -331,6 +343,15 @@ The full list of what this refuses to do, and why, is in
|
|
|
331
343
|
|
|
332
344
|
## Changelog
|
|
333
345
|
|
|
346
|
+
- **0.1.3** — fixes found by the first real payment. The built bundles no
|
|
347
|
+
longer crash with `__filename is not defined` on Node 22+ (the esbuild banner
|
|
348
|
+
now shims `__filename`/`__dirname` as well as `require`, and every bundle is
|
|
349
|
+
now executed by the test suite). Stellar deposits state their memo type
|
|
350
|
+
(`MEMO_TEXT`, even when the memo looks numeric). Orders show remaining
|
|
351
|
+
validity as a duration ("expires in 47m") in the deposit block and in
|
|
352
|
+
`status`, with the exact command to make a fresh one. Reusing an existing
|
|
353
|
+
unpaid order, and asking for a coin that differs from it, are both explained
|
|
354
|
+
instead of reading as errors.
|
|
334
355
|
- **0.1.2** — Mode B no longer needs a raw private key in an environment
|
|
335
356
|
variable. On Solana it uses the `~/.config/solana/id.json` that
|
|
336
357
|
`solana-keygen` already wrote; on EVM an encrypted V3 keystore whose
|
package/docs/QUICKSTART.es.md
CHANGED
|
@@ -159,6 +159,7 @@ exigen los scripts de envío.
|
|
|
159
159
|
```
|
|
160
160
|
|
|
161
161
|
Enviar exactamente los campos que entrega este bloque. Para Lightning,
|
|
162
|
+
En Stellar el memo es **`MEMO_TEXT`** aunque parezca numérico: enviarlo como `MEMO_ID` no coincidirá. `deposit.expiresIn` indica cuánto sigue siendo válida la orden.
|
|
162
163
|
`deposit.lnInvoice` contiene la cadena BOLT11 que hay que escanear, y
|
|
163
164
|
`deposit.amount` está en satoshis.
|
|
164
165
|
|
|
@@ -182,8 +183,9 @@ Eso es todo el Modo A. Continuar en el paso 5.
|
|
|
182
183
|
|
|
183
184
|
### La forma opcional: dejar que el script pague (Modo B)
|
|
184
185
|
|
|
185
|
-
Solo si se quiere que esta máquina firme, y únicamente en cadenas EVM y
|
|
186
|
-
|
|
186
|
+
Solo si se quiere que esta máquina firme, y únicamente en **cadenas EVM y
|
|
187
|
+
Solana**: no hay `--send` para Stellar ni Lightning, que se pagan desde la
|
|
188
|
+
propia billetera. Esta es la única parte que necesita una clave.
|
|
187
189
|
|
|
188
190
|
**Solana: usar el par de claves que ya se tiene.** Si alguna vez se ejecutó
|
|
189
191
|
`solana-keygen`, `~/.config/solana/id.json` ya existe y se usa automáticamente:
|
package/docs/QUICKSTART.ja.md
CHANGED
|
@@ -141,6 +141,7 @@ node scripts/dist/create-order.js --url "$LINK" --chain 900 --token USDT --confi
|
|
|
141
141
|
```
|
|
142
142
|
|
|
143
143
|
このブロックが示すフィールドを、そのとおりに送金してください。Lightning の場合、
|
|
144
|
+
Stellar の memo は数字に見えても**常に `MEMO_TEXT`** です — `MEMO_ID` として送ると一致しません。`deposit.expiresIn` に残りの有効時間が出ます。
|
|
144
145
|
`deposit.lnInvoice` にスキャン用の BOLT11 文字列が入り、`deposit.amount` は
|
|
145
146
|
satoshi 単位になります。
|
|
146
147
|
|
|
@@ -163,8 +164,8 @@ satoshi 単位になります。
|
|
|
163
164
|
|
|
164
165
|
### 任意の方法: スクリプトに支払わせる(モード B)
|
|
165
166
|
|
|
166
|
-
このマシンに署名させたい場合のみで、対象は EVM チェーンと Solana
|
|
167
|
-
|
|
167
|
+
このマシンに署名させたい場合のみで、対象は **EVM チェーンと Solana だけ**です。Stellar と
|
|
168
|
+
Lightning に `--send` はなく、自分のウォレットから支払います。鍵が必要なのはこの部分だけです。
|
|
168
169
|
|
|
169
170
|
**Solana — すでにある鍵ペアをそのまま使います。** `solana-keygen` を一度でも実行して
|
|
170
171
|
いれば `~/.config/solana/id.json` が既に存在し、自動的に使われます:
|
package/docs/QUICKSTART.md
CHANGED
|
@@ -151,7 +151,10 @@ scripts require.
|
|
|
151
151
|
}
|
|
152
152
|
```
|
|
153
153
|
|
|
154
|
-
Send exactly the fields this block gives you.
|
|
154
|
+
Send exactly the fields this block gives you. On Stellar the memo is
|
|
155
|
+
**`MEMO_TEXT`** even when it looks numeric — sending it as `MEMO_ID` will not
|
|
156
|
+
match. `deposit.expiresIn` tells you how long the order stays valid. For
|
|
157
|
+
Lightning,
|
|
155
158
|
`deposit.lnInvoice` holds the BOLT11 string to scan, and `deposit.amount` is
|
|
156
159
|
in satoshis.
|
|
157
160
|
|
|
@@ -174,8 +177,9 @@ That is the whole of Mode A. Skip to step 5.
|
|
|
174
177
|
|
|
175
178
|
### The optional way: let the script pay (Mode B)
|
|
176
179
|
|
|
177
|
-
Only if you want this machine to sign for you, and only on EVM chains and
|
|
178
|
-
Solana
|
|
180
|
+
Only if you want this machine to sign for you, and only on **EVM chains and
|
|
181
|
+
Solana** — there is no `--send` for Stellar or Lightning, which are paid from
|
|
182
|
+
your own wallet. This is the only part that needs a key.
|
|
179
183
|
|
|
180
184
|
**Solana — use the keypair you already have.** If you have ever run
|
|
181
185
|
`solana-keygen`, `~/.config/solana/id.json` already exists and is used
|
package/docs/QUICKSTART.zh.md
CHANGED
|
@@ -129,7 +129,7 @@ node scripts/dist/create-order.js --url "$LINK" --chain 900 --token USDT --confi
|
|
|
129
129
|
}
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
严格按这个信息块给出的字段来发送。Stellar 上的 memo 即使看起来是纯数字,也**始终是 `MEMO_TEXT`** —— 按 `MEMO_ID` 发送不会被匹配上。`deposit.expiresIn` 会告诉你订单还有多久过期。对于 Lightning,`deposit.lnInvoice` 里是供扫码的
|
|
133
133
|
BOLT11 字符串,`deposit.amount` 的单位是聪 (satoshis)。
|
|
134
134
|
|
|
135
135
|
## 4. 付款
|
|
@@ -150,8 +150,8 @@ BOLT11 字符串,`deposit.amount` 的单位是聪 (satoshis)。
|
|
|
150
150
|
|
|
151
151
|
### 可选的方式:让脚本替你付(模式 B)
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
只有当你想让这台机器替你签名时才需要,而且**只支持 EVM 链和 Solana** —— Stellar 和
|
|
154
|
+
闪电网络没有 `--send`,只能用你自己的钱包付。这是整个流程里唯一需要私钥的部分。
|
|
155
155
|
|
|
156
156
|
**Solana —— 直接用你已有的密钥对。** 只要你跑过 `solana-keygen`,
|
|
157
157
|
`~/.config/solana/id.json` 就已经存在,脚本会自动使用它:
|
package/docs/README.es.md
CHANGED
|
@@ -34,7 +34,7 @@ npx @rozoai/checkout pay <coinbase-link> --with usdt-solana
|
|
|
34
34
|
| Polygon | `usdt-polygon` `usdc-polygon` | `137` | 6 decimales |
|
|
35
35
|
| Base | `usdc-base` | `8453` | 6 decimales |
|
|
36
36
|
| Solana | `usdt-solana` `usdc-solana` | `900` | SPL; SOL nativo no admitido |
|
|
37
|
-
| Stellar | `usdc-stellar` | `1500` | memo obligatorio — se muestra en el bloque de depósito |
|
|
37
|
+
| Stellar | `usdc-stellar` | `1500` | memo `MEMO_TEXT` obligatorio — se muestra en el bloque de depósito |
|
|
38
38
|
| Bitcoin Lightning | `btc-lightning` | `lightning` | BOLT11; importes en satoshis |
|
|
39
39
|
|
|
40
40
|
Las monedas de gas nativas (SOL, BNB, ETH, MATIC) y el BTC on-chain no se aceptan.
|
|
@@ -103,7 +103,7 @@ Para pagar un enlace de pago de OpenRouter / Coinbase, ejecuta:
|
|
|
103
103
|
npx @rozoai/checkout pay <coinbase-link> --with usdt-solana
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
Billetera: cualquier billetera, **sin clave**. Solo `--send` la necesita: firma localmente con el par de claves de la CLI de Solana o un keystore cifrado.
|
|
106
|
+
Billetera: cualquier billetera, **sin clave**. Solo `--send` la necesita: firma localmente con el par de claves de la CLI de Solana o un keystore cifrado, y solo admite cadenas EVM y Solana (Stellar y Lightning son solo Modo A).
|
|
107
107
|
</details>
|
|
108
108
|
|
|
109
109
|
<details>
|
|
@@ -329,6 +329,14 @@ La lista completa de lo que esta herramienta se niega a hacer, y por qué, está
|
|
|
329
329
|
|
|
330
330
|
## Registro de cambios
|
|
331
331
|
|
|
332
|
+
- **0.1.3** — Correcciones surgidas del primer pago real. Los bundles ya no fallan
|
|
333
|
+
con `__filename is not defined` en Node 22+ (el banner de esbuild ahora define
|
|
334
|
+
`__filename`/`__dirname` además de `require`, y las pruebas ejecutan cada bundle
|
|
335
|
+
de verdad). Los depósitos de Stellar indican el tipo de memo (`MEMO_TEXT`, aunque
|
|
336
|
+
el memo parezca numérico). Las órdenes muestran la validez restante como duración
|
|
337
|
+
("expires in 47m") en el bloque de depósito y en `status`, con el comando exacto
|
|
338
|
+
para crear una nueva. Reutilizar una orden impaga existente, y pedir una moneda
|
|
339
|
+
distinta a la suya, ahora se explican en lugar de parecer errores.
|
|
332
340
|
- **0.1.2** — El Modo B ya no necesita una clave privada en bruto en una variable
|
|
333
341
|
de entorno. En Solana usa el `~/.config/solana/id.json` que `solana-keygen` ya
|
|
334
342
|
creó; en EVM un keystore V3 cifrado cuya frase de contraseña se solicita.
|
package/docs/README.ja.md
CHANGED
|
@@ -33,7 +33,7 @@ npx @rozoai/checkout pay <coinbase-link> --with usdt-solana
|
|
|
33
33
|
| Polygon | `usdt-polygon` `usdc-polygon` | `137` | 小数点以下 6 桁 |
|
|
34
34
|
| Base | `usdc-base` | `8453` | 小数点以下 6 桁 |
|
|
35
35
|
| Solana | `usdt-solana` `usdc-solana` | `900` | SPL。ネイティブの SOL は非対応 |
|
|
36
|
-
| Stellar | `usdc-stellar` | `1500` |
|
|
36
|
+
| Stellar | `usdc-stellar` | `1500` | `MEMO_TEXT` のメモが必須 — 入金ブロックに表示されます |
|
|
37
37
|
| Bitcoin Lightning | `btc-lightning` | `lightning` | BOLT11。金額は satoshi 単位 |
|
|
38
38
|
|
|
39
39
|
ネイティブのガス通貨(SOL、BNB、ETH、MATIC)およびオンチェーンの BTC は受け付けません。
|
|
@@ -89,7 +89,7 @@ OpenRouter / Coinbase の決済リンクを支払うには、次を実行して
|
|
|
89
89
|
npx @rozoai/checkout pay <coinbase-link> --with usdt-solana
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
-
ウォレット: どのウォレットでも構いません。**鍵は不要です。** Solana CLI の鍵ペアまたは暗号化された keystore を使う `--send
|
|
92
|
+
ウォレット: どのウォレットでも構いません。**鍵は不要です。** Solana CLI の鍵ペアまたは暗号化された keystore を使う `--send`(EVM チェーンと Solana のみ、Stellar と Lightning はモード A 専用)の場合だけ
|
|
93
93
|
環境変数の鍵が必要です。
|
|
94
94
|
</details>
|
|
95
95
|
|
|
@@ -320,6 +320,13 @@ WalletConnect 専用のモバイルウォレット、取引所アカウント。
|
|
|
320
320
|
|
|
321
321
|
## 変更履歴
|
|
322
322
|
|
|
323
|
+
- **0.1.3** — 最初の実際の支払いで見つかった不具合の修正。ビルド済みバンドルが Node 22 以降で
|
|
324
|
+
`__filename is not defined` により停止しなくなりました(esbuild の banner が `require` に加えて
|
|
325
|
+
`__filename`/`__dirname` も定義し、テストが各バンドルを実際に実行するようになりました)。Stellar の
|
|
326
|
+
入金では memo の種類(`MEMO_TEXT`。memo が数字に見える場合も同様)を明示します。注文の残り有効時間を
|
|
327
|
+
入金ブロックと `status` の両方で継続時間として表示し("expires in 47m")、期限切れ時には新しい注文を
|
|
328
|
+
作るコマンドをそのまま示します。既存の未払い注文の再利用と、それと異なる通貨を指定した場合の双方を、
|
|
329
|
+
エラーではなく説明として提示します。
|
|
323
330
|
- **0.1.2** — モード B で環境変数に生の秘密鍵を置く必要がなくなりました。Solana では
|
|
324
331
|
`solana-keygen` が既に作成した `~/.config/solana/id.json` を、EVM では暗号化された
|
|
325
332
|
V3 keystore(パスフレーズは対話的に入力)を使います。`--keyfile` でどちらも明示でき、
|
package/docs/README.zh.md
CHANGED
|
@@ -30,7 +30,7 @@ npx @rozoai/checkout pay <coinbase-link> --with usdt-solana
|
|
|
30
30
|
| Polygon | `usdt-polygon` `usdc-polygon` | `137` | 6 位小数 |
|
|
31
31
|
| Base | `usdc-base` | `8453` | 6 位小数 |
|
|
32
32
|
| Solana | `usdt-solana` `usdc-solana` | `900` | SPL;不支持原生 SOL |
|
|
33
|
-
| Stellar | `usdc-stellar` | `1500` | 必须带 memo —— 会在充值信息块里给出 |
|
|
33
|
+
| Stellar | `usdc-stellar` | `1500` | 必须带 `MEMO_TEXT` memo —— 会在充值信息块里给出 |
|
|
34
34
|
| Bitcoin Lightning | `btc-lightning` | `lightning` | BOLT11;金额单位为聪 |
|
|
35
35
|
|
|
36
36
|
原生 gas 币(SOL、BNB、ETH、MATIC)以及链上 BTC 均不接受。
|
|
@@ -82,7 +82,7 @@ Codex 会读取项目根目录的 `AGENTS.md`。加一条常驻指令,它就
|
|
|
82
82
|
npx @rozoai/checkout pay <coinbase-link> --with usdt-solana
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
-
钱包:任意钱包,**无需私钥**。只有 `--send` 需要,它用你的 Solana CLI 密钥对或加密 keystore
|
|
85
|
+
钱包:任意钱包,**无需私钥**。只有 `--send` 需要,它用你的 Solana CLI 密钥对或加密 keystore 在本地签名;仅支持 EVM 链和 Solana —— Stellar 与闪电网络只能用模式 A。
|
|
86
86
|
</details>
|
|
87
87
|
|
|
88
88
|
<details>
|
|
@@ -292,6 +292,12 @@ echo '.env' >> .gitignore
|
|
|
292
292
|
|
|
293
293
|
## 更新日志
|
|
294
294
|
|
|
295
|
+
- **0.1.3** —— 第一笔真实付款暴露出来的问题修复。构建产物在 Node 22+ 上不再因
|
|
296
|
+
`__filename is not defined` 崩溃(esbuild banner 现在同时注入 `__filename`/`__dirname`
|
|
297
|
+
和 `require`,并且测试会真正执行每一个构建产物)。Stellar 充值现在会明确标出 memo
|
|
298
|
+
类型(`MEMO_TEXT`,即使 memo 看起来是纯数字)。订单在充值信息块和 `status` 里都会以
|
|
299
|
+
时长显示剩余有效期("expires in 47m"),过期时给出重新下单的完整命令。复用已有的未付
|
|
300
|
+
订单、以及所选币种与已有订单不一致这两种情况,现在都会被解释清楚,而不是看起来像报错。
|
|
295
301
|
- **0.1.2** —— 模式 B 不再需要把原始私钥放进环境变量。Solana 直接用 `solana-keygen`
|
|
296
302
|
已经写好的 `~/.config/solana/id.json`;EVM 用加密的 V3 keystore,口令交互式提示输入。
|
|
297
303
|
`--keyfile` 可以显式指定其中任一种,相关设置也可以放进已 gitignore 的 `.env`。
|
package/llms.txt
CHANGED
|
@@ -54,6 +54,8 @@ Where the material comes from:
|
|
|
54
54
|
`cast wallet import <name> --interactive` writes a V3 keystore to
|
|
55
55
|
~/.foundry/keystores/<name>; `geth account import` also writes one. Browser
|
|
56
56
|
wallets do not export keystores, only raw keys.
|
|
57
|
+
- --send supports EVM chains and Solana ONLY. Stellar and Lightning are paid
|
|
58
|
+
from the user's own wallet (Mode A); there is no local signing for them.
|
|
57
59
|
- Hardware wallets, WalletConnect-only wallets and exchange accounts CANNOT be
|
|
58
60
|
used with --send. Those users take the default keyless path, which works with
|
|
59
61
|
any wallet.
|
|
@@ -108,7 +110,11 @@ private key (EVM and Solana only); every other path is keyless.
|
|
|
108
110
|
Pay EXACTLY source.amount of the requested token on the requested chain to
|
|
109
111
|
source.receiverAddress, including every other field the block carries (Stellar
|
|
110
112
|
routes on a shared hub address plus source.receiverMemo, so omitting the memo
|
|
111
|
-
loses the payment).
|
|
113
|
+
loses the payment). The Stellar memo is ALWAYS MEMO_TEXT, even when it is all
|
|
114
|
+
digits (e.g. 65371582) — sending it as MEMO_ID produces a different memo and
|
|
115
|
+
the payment will not match. The deposit block reports this as
|
|
116
|
+
receiverMemoType. Orders expire: deposit.expiresIn / status expiry.expiresIn
|
|
117
|
+
give the remaining validity as a duration.
|
|
112
118
|
For Lightning, pay the BOLT11 in source.lnInvoice (receiverAddress is empty).
|
|
113
119
|
|
|
114
120
|
4) Poll until settled:
|
package/package.json
CHANGED
package/scripts/dist/cli.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __rozoCreateRequire } from 'node:module';
|
|
3
|
+
import { fileURLToPath as __rozoFileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname as __rozoDirname } from 'node:path';
|
|
3
5
|
const require = __rozoCreateRequire(import.meta.url);
|
|
6
|
+
const __filename = __rozoFileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = __rozoDirname(__filename);
|
|
4
8
|
var __create = Object.create;
|
|
5
9
|
var __defProp = Object.defineProperty;
|
|
6
10
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -41655,6 +41659,7 @@ function formatAmount(source) {
|
|
|
41655
41659
|
function chainName(chainId) {
|
|
41656
41660
|
return CHAIN_NAMES[String(chainId)] || CHAIN_NAMES[chainId] || `chain ${chainId}`;
|
|
41657
41661
|
}
|
|
41662
|
+
var STELLAR_MEMO_TYPE = "MEMO_TEXT";
|
|
41658
41663
|
function chainFamily(chainId) {
|
|
41659
41664
|
return CHAIN_FAMILY[String(chainId)] || CHAIN_FAMILY[chainId] || null;
|
|
41660
41665
|
}
|
|
@@ -42701,6 +42706,16 @@ var MARGINS_MS = {
|
|
|
42701
42706
|
};
|
|
42702
42707
|
var BOLT11_MIN_VALIDITY_MS = 10 * MINUTE;
|
|
42703
42708
|
var DEFAULT_MARGIN_MS = 10 * MINUTE;
|
|
42709
|
+
function formatRemaining(ms) {
|
|
42710
|
+
if (!Number.isFinite(ms)) return "unknown";
|
|
42711
|
+
if (ms <= 0) return "expired";
|
|
42712
|
+
const totalMinutes = Math.floor(ms / 6e4);
|
|
42713
|
+
if (totalMinutes < 1) return `${Math.floor(ms / 1e3)}s`;
|
|
42714
|
+
if (totalMinutes < 60) return `${totalMinutes}m`;
|
|
42715
|
+
const h = Math.floor(totalMinutes / 60);
|
|
42716
|
+
const m = totalMinutes % 60;
|
|
42717
|
+
return m ? `${h}h ${m}m` : `${h}h`;
|
|
42718
|
+
}
|
|
42704
42719
|
function marginFor(chainId) {
|
|
42705
42720
|
const key = String(chainId);
|
|
42706
42721
|
return MARGINS_MS[key] ?? MARGINS_MS[chainId] ?? DEFAULT_MARGIN_MS;
|
|
@@ -43342,6 +43357,33 @@ async function main2(argv) {
|
|
|
43342
43357
|
const rozoPaymentId = assertRozoPaymentId(created.rozoPaymentId);
|
|
43343
43358
|
const verify = verifyCreateAgainstQuote({ snapshot: snapshot2, created, requested });
|
|
43344
43359
|
if (!verify.ok) {
|
|
43360
|
+
const onlySourceDrift = verify.drift.length > 0 && verify.drift.every((d) => d.field.startsWith("source."));
|
|
43361
|
+
if (created.reused && onlySourceDrift) {
|
|
43362
|
+
const existing = created.source || {};
|
|
43363
|
+
const remainingMs = created.expiresAt ? Date.parse(created.expiresAt) - Date.now() : NaN;
|
|
43364
|
+
emit(
|
|
43365
|
+
{
|
|
43366
|
+
success: false,
|
|
43367
|
+
step: "reuse-source-mismatch",
|
|
43368
|
+
error: {
|
|
43369
|
+
code: "REUSED_SOURCE_MISMATCH",
|
|
43370
|
+
message: `This link already has an unpaid order, created for ${existing.tokenSymbol ?? "?"} on chain ${existing.chainId ?? "?"}. You asked for ${tokenSymbol} on chain ${chainId}. Only one order exists per link at a time, so nothing new was created.`
|
|
43371
|
+
},
|
|
43372
|
+
linkId: created.linkId ?? parsedLinkId,
|
|
43373
|
+
rozoPaymentId,
|
|
43374
|
+
paymentLink: created.paymentLink ?? null,
|
|
43375
|
+
existingOrder: {
|
|
43376
|
+
rozoPaymentId,
|
|
43377
|
+
chainId: existing.chainId ?? null,
|
|
43378
|
+
tokenSymbol: existing.tokenSymbol ?? null,
|
|
43379
|
+
expiresAt: created.expiresAt ?? null,
|
|
43380
|
+
expiresIn: Number.isFinite(remainingMs) ? formatRemaining(remainingMs) : null
|
|
43381
|
+
},
|
|
43382
|
+
guidance: `Either pay the existing order with ${existing.tokenSymbol ?? "its own coin"} (re-run with --chain ${existing.chainId} --token ${existing.tokenSymbol}), or wait ${Number.isFinite(remainingMs) ? formatRemaining(remainingMs) : "for it to expire"} for it to expire and then create a new one. Unpaid orders cost nothing.`
|
|
43383
|
+
},
|
|
43384
|
+
EXIT_ERROR
|
|
43385
|
+
);
|
|
43386
|
+
}
|
|
43345
43387
|
emit(
|
|
43346
43388
|
{
|
|
43347
43389
|
success: false,
|
|
@@ -43463,7 +43505,8 @@ async function main2(argv) {
|
|
|
43463
43505
|
step: "create-order",
|
|
43464
43506
|
confirmed,
|
|
43465
43507
|
reused: Boolean(created.reused),
|
|
43466
|
-
reusedNote: created.reused ?
|
|
43508
|
+
reusedNote: created.reused ? `An existing unpaid order for this link was reused (${rozoPaymentId}), valid for another ${formatRemaining(expiry.msRemaining)}. Nothing new was created.` : null,
|
|
43509
|
+
orderCost: "Creating an order moves no money. An order you never fund simply expires and costs nothing.",
|
|
43467
43510
|
linkId: created.linkId ?? parsedLinkId,
|
|
43468
43511
|
rozoPaymentId,
|
|
43469
43512
|
paymentLink: created.paymentLink ?? null,
|
|
@@ -43481,12 +43524,16 @@ async function main2(argv) {
|
|
|
43481
43524
|
// Lightning has no deposit address: the BOLT11 IS the instruction.
|
|
43482
43525
|
receiverAddress: lightning ? null : source.receiverAddress,
|
|
43483
43526
|
receiverMemo: source.receiverMemo ?? null,
|
|
43527
|
+
// Stellar memos are TEXT even when they look numeric. Sending one as
|
|
43528
|
+
// MEMO_ID produces a different memo and the payment will not match.
|
|
43529
|
+
receiverMemoType: source.receiverMemo ? STELLAR_MEMO_TYPE : null,
|
|
43484
43530
|
amount: source.amount,
|
|
43485
43531
|
amountUnit: source.amountUnit ?? null,
|
|
43486
43532
|
isSats: isSatsUnit(source.amountUnit),
|
|
43487
43533
|
lnInvoice: bolt11 || null,
|
|
43488
43534
|
payTo: depositInfo.payTo,
|
|
43489
|
-
expiresAt: payment?.expiresAt ?? null
|
|
43535
|
+
expiresAt: payment?.expiresAt ?? null,
|
|
43536
|
+
expiresIn: formatRemaining(expiry.msRemaining)
|
|
43490
43537
|
} : null,
|
|
43491
43538
|
depositWithheld: !confirmed,
|
|
43492
43539
|
// Safe for prose / chat.
|
|
@@ -43498,12 +43545,16 @@ async function main2(argv) {
|
|
|
43498
43545
|
payToMasked: maskAddress2(depositInfo.payTo),
|
|
43499
43546
|
receiverMemoMasked: maskMemo(source.receiverMemo),
|
|
43500
43547
|
hasMemo: Boolean(source.receiverMemo),
|
|
43548
|
+
memoType: source.receiverMemo ? STELLAR_MEMO_TYPE : null,
|
|
43501
43549
|
memoRequirement
|
|
43502
43550
|
},
|
|
43503
43551
|
expiry: {
|
|
43504
43552
|
intentExpiresAt: payment?.expiresAt ?? null,
|
|
43505
43553
|
coinbaseExpiry: snapshot2?.coinbase?.preApprovalExpiry ?? null,
|
|
43506
43554
|
effectiveDeadlineIso: new Date(expiry.effectiveDeadlineMs).toISOString(),
|
|
43555
|
+
// A duration, not just a timestamp: this is what a payer actually needs.
|
|
43556
|
+
expiresIn: formatRemaining(expiry.msRemaining),
|
|
43557
|
+
msRemaining: expiry.msRemaining,
|
|
43507
43558
|
marginMinutes: Math.round(expiry.marginMs / 6e4),
|
|
43508
43559
|
minutesOfSlack: Math.floor(expiry.msOfSlack / 6e4)
|
|
43509
43560
|
},
|
|
@@ -43599,6 +43650,16 @@ async function snapshot({ rozoPaymentId, linkId }) {
|
|
|
43599
43650
|
senderAddressMasked: source.senderAddress ? maskAddress2(source.senderAddress) : null,
|
|
43600
43651
|
chain: source.chainId ? chainName(source.chainId) : null
|
|
43601
43652
|
},
|
|
43653
|
+
expiry: (() => {
|
|
43654
|
+
const iso = payment?.expiresAt ?? status?.rozoPayment?.expiresAt ?? null;
|
|
43655
|
+
if (!iso) return { expiresAt: null, expiresIn: null, msRemaining: null };
|
|
43656
|
+
const ms = Date.parse(iso) - Date.now();
|
|
43657
|
+
return {
|
|
43658
|
+
expiresAt: iso,
|
|
43659
|
+
expiresIn: formatRemaining(ms),
|
|
43660
|
+
msRemaining: Number.isFinite(ms) ? ms : null
|
|
43661
|
+
};
|
|
43662
|
+
})(),
|
|
43602
43663
|
payout: {
|
|
43603
43664
|
txHash: payment?.destination?.txHash ?? status?.rozoPayment?.destination?.txHash ?? null,
|
|
43604
43665
|
confirmedAt: payment?.destination?.confirmedAt ?? status?.rozoPayment?.destination?.confirmedAt ?? null
|
|
@@ -43624,7 +43685,7 @@ async function main3(argv) {
|
|
|
43624
43685
|
if (next.state !== result.state) history.push({ at: (/* @__PURE__ */ new Date()).toISOString(), state: next.state });
|
|
43625
43686
|
result = next;
|
|
43626
43687
|
}
|
|
43627
|
-
const guidance = result.escalate ? "MONEY DETECTED and the order is not on a healthy path. Do NOT pay again and do NOT create a new order for this link. Preserve linkId, rozoPaymentId and every tx hash, then escalate to the operator for manual reconciliation." : result.unknown ? "The order state could not be established. This is NOT evidence that nothing was paid \u2014 do not create a new order and do not send again on the strength of it. Retry, or pass --rozo-payment-id so the authoritative pay-in view can be read." : !result.authoritativeView ? "Only the fulfilment view was readable; the pay-in view is unavailable, so the money-detected rule cannot be enforced. Pass --rozo-payment-id for a complete answer." : result.state === "expired_unfunded" ? "Nothing was funded.
|
|
43688
|
+
const guidance = result.escalate ? "MONEY DETECTED and the order is not on a healthy path. Do NOT pay again and do NOT create a new order for this link. Preserve linkId, rozoPaymentId and every tx hash, then escalate to the operator for manual reconciliation." : result.unknown ? "The order state could not be established. This is NOT evidence that nothing was paid \u2014 do not create a new order and do not send again on the strength of it. Retry, or pass --rozo-payment-id so the authoritative pay-in view can be read." : !result.authoritativeView ? "Only the fulfilment view was readable; the pay-in view is unavailable, so the money-detected rule cannot be enforced. Pass --rozo-payment-id for a complete answer." : result.state === "expired_unfunded" ? "Nothing was funded, so nothing was lost. Start a fresh order with: rozo-checkout pay <coinbase-link> --with <coin> (or create-order.js --url <link> --chain <id> --token <SYMBOL>)" : result.terminal ? "Done." : "Still in flight. Poll again in ~10s.";
|
|
43628
43689
|
const unresolved = watch && !result.terminal && !result.escalate && !result.unknown;
|
|
43629
43690
|
const failed = result.escalate || result.unknown || !result.authoritativeView;
|
|
43630
43691
|
emit(
|
|
@@ -57910,6 +57971,9 @@ async function cmdStatus(opts) {
|
|
|
57910
57971
|
out();
|
|
57911
57972
|
out(` State ${stateLabel(payload.state)}`);
|
|
57912
57973
|
if (payload.detail) out(` ${dim(payload.detail)}`);
|
|
57974
|
+
if (payload.expiry?.expiresIn && !payload.terminal) {
|
|
57975
|
+
out(` Expires in ${payload.expiry.expiresIn}`);
|
|
57976
|
+
}
|
|
57913
57977
|
if (payload.payin?.txHash) out(` Pay-in ${payload.payin.txHash}`);
|
|
57914
57978
|
if (payload.guidance) out(` ${payload.escalate ? red(payload.guidance) : dim(payload.guidance)}`);
|
|
57915
57979
|
printMoneyWarning(payload);
|
|
@@ -57996,9 +58060,14 @@ async function cmdPay(opts) {
|
|
|
57996
58060
|
out(` Invoice ${bold(`${p.invoice?.amount} ${p.invoice?.currency ?? "USD"}`)}`);
|
|
57997
58061
|
out(` You send ${bold(p.display?.amount)} on ${bold(p.display?.chain)}`);
|
|
57998
58062
|
out(` To ${p.display?.payToMasked} ${dim("(full address shown after you confirm)")}`);
|
|
57999
|
-
if (p.display?.hasMemo)
|
|
58000
|
-
|
|
58001
|
-
|
|
58063
|
+
if (p.display?.hasMemo) {
|
|
58064
|
+
out(` Memo ${p.display.receiverMemoMasked} ${dim(`(${p.display.memoType})`)}`);
|
|
58065
|
+
}
|
|
58066
|
+
out(` Expires in ${bold(p.expiry?.expiresIn ?? "?")} ${dim(`(${p.expiry?.effectiveDeadlineIso})`)}`);
|
|
58067
|
+
if (p.reused) {
|
|
58068
|
+
out(` ${dim(`Reusing the existing unpaid order ${p.rozoPaymentId} \u2014 nothing new was created.`)}`);
|
|
58069
|
+
}
|
|
58070
|
+
out(` ${dim("An order you never fund simply expires and costs nothing.")}`);
|
|
58002
58071
|
out();
|
|
58003
58072
|
out(dim(` The amount you send includes bridge and network fees, so it is`));
|
|
58004
58073
|
out(dim(` normally larger than the invoice.`));
|
|
@@ -58127,7 +58196,12 @@ function printDeposit(deposit, opts) {
|
|
|
58127
58196
|
out(` Amount ${bold(`${deposit.amount} ${deposit.tokenSymbol}`)}`);
|
|
58128
58197
|
out(` Chain ${bold(deposit.chain)}`);
|
|
58129
58198
|
out(` Address ${bold(deposit.receiverAddress)}`);
|
|
58130
|
-
if (deposit.receiverMemo)
|
|
58199
|
+
if (deposit.receiverMemo) {
|
|
58200
|
+
out(
|
|
58201
|
+
` Memo ${bold(deposit.receiverMemo)} ${red(`(required, ${deposit.receiverMemoType})`)}`
|
|
58202
|
+
);
|
|
58203
|
+
}
|
|
58204
|
+
if (deposit.expiresIn) out(` Expires in ${bold(deposit.expiresIn)}`);
|
|
58131
58205
|
}
|
|
58132
58206
|
out();
|
|
58133
58207
|
const unit = isSatsUnit(deposit.amountUnit) ? "sats" : deposit.tokenSymbol;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __rozoCreateRequire } from 'node:module';
|
|
3
|
+
import { fileURLToPath as __rozoFileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname as __rozoDirname } from 'node:path';
|
|
3
5
|
const require = __rozoCreateRequire(import.meta.url);
|
|
6
|
+
const __filename = __rozoFileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = __rozoDirname(__filename);
|
|
4
8
|
|
|
5
9
|
// scripts/src/lib/output.mjs
|
|
6
10
|
var EXIT_OK = 0;
|
|
@@ -396,6 +400,7 @@ function formatAmount(source) {
|
|
|
396
400
|
function chainName(chainId) {
|
|
397
401
|
return CHAIN_NAMES[String(chainId)] || CHAIN_NAMES[chainId] || `chain ${chainId}`;
|
|
398
402
|
}
|
|
403
|
+
var STELLAR_MEMO_TYPE = "MEMO_TEXT";
|
|
399
404
|
function chainFamily(chainId) {
|
|
400
405
|
return CHAIN_FAMILY[String(chainId)] || CHAIN_FAMILY[chainId] || null;
|
|
401
406
|
}
|
|
@@ -418,6 +423,16 @@ var MARGINS_MS = {
|
|
|
418
423
|
};
|
|
419
424
|
var BOLT11_MIN_VALIDITY_MS = 10 * MINUTE;
|
|
420
425
|
var DEFAULT_MARGIN_MS = 10 * MINUTE;
|
|
426
|
+
function formatRemaining(ms) {
|
|
427
|
+
if (!Number.isFinite(ms)) return "unknown";
|
|
428
|
+
if (ms <= 0) return "expired";
|
|
429
|
+
const totalMinutes = Math.floor(ms / 6e4);
|
|
430
|
+
if (totalMinutes < 1) return `${Math.floor(ms / 1e3)}s`;
|
|
431
|
+
if (totalMinutes < 60) return `${totalMinutes}m`;
|
|
432
|
+
const h = Math.floor(totalMinutes / 60);
|
|
433
|
+
const m = totalMinutes % 60;
|
|
434
|
+
return m ? `${h}h ${m}m` : `${h}h`;
|
|
435
|
+
}
|
|
421
436
|
function marginFor(chainId) {
|
|
422
437
|
const key = String(chainId);
|
|
423
438
|
return MARGINS_MS[key] ?? MARGINS_MS[chainId] ?? DEFAULT_MARGIN_MS;
|
|
@@ -1135,6 +1150,33 @@ async function main(argv) {
|
|
|
1135
1150
|
const rozoPaymentId = assertRozoPaymentId(created.rozoPaymentId);
|
|
1136
1151
|
const verify = verifyCreateAgainstQuote({ snapshot, created, requested });
|
|
1137
1152
|
if (!verify.ok) {
|
|
1153
|
+
const onlySourceDrift = verify.drift.length > 0 && verify.drift.every((d) => d.field.startsWith("source."));
|
|
1154
|
+
if (created.reused && onlySourceDrift) {
|
|
1155
|
+
const existing = created.source || {};
|
|
1156
|
+
const remainingMs = created.expiresAt ? Date.parse(created.expiresAt) - Date.now() : NaN;
|
|
1157
|
+
emit(
|
|
1158
|
+
{
|
|
1159
|
+
success: false,
|
|
1160
|
+
step: "reuse-source-mismatch",
|
|
1161
|
+
error: {
|
|
1162
|
+
code: "REUSED_SOURCE_MISMATCH",
|
|
1163
|
+
message: `This link already has an unpaid order, created for ${existing.tokenSymbol ?? "?"} on chain ${existing.chainId ?? "?"}. You asked for ${tokenSymbol} on chain ${chainId}. Only one order exists per link at a time, so nothing new was created.`
|
|
1164
|
+
},
|
|
1165
|
+
linkId: created.linkId ?? parsedLinkId,
|
|
1166
|
+
rozoPaymentId,
|
|
1167
|
+
paymentLink: created.paymentLink ?? null,
|
|
1168
|
+
existingOrder: {
|
|
1169
|
+
rozoPaymentId,
|
|
1170
|
+
chainId: existing.chainId ?? null,
|
|
1171
|
+
tokenSymbol: existing.tokenSymbol ?? null,
|
|
1172
|
+
expiresAt: created.expiresAt ?? null,
|
|
1173
|
+
expiresIn: Number.isFinite(remainingMs) ? formatRemaining(remainingMs) : null
|
|
1174
|
+
},
|
|
1175
|
+
guidance: `Either pay the existing order with ${existing.tokenSymbol ?? "its own coin"} (re-run with --chain ${existing.chainId} --token ${existing.tokenSymbol}), or wait ${Number.isFinite(remainingMs) ? formatRemaining(remainingMs) : "for it to expire"} for it to expire and then create a new one. Unpaid orders cost nothing.`
|
|
1176
|
+
},
|
|
1177
|
+
EXIT_ERROR
|
|
1178
|
+
);
|
|
1179
|
+
}
|
|
1138
1180
|
emit(
|
|
1139
1181
|
{
|
|
1140
1182
|
success: false,
|
|
@@ -1256,7 +1298,8 @@ async function main(argv) {
|
|
|
1256
1298
|
step: "create-order",
|
|
1257
1299
|
confirmed,
|
|
1258
1300
|
reused: Boolean(created.reused),
|
|
1259
|
-
reusedNote: created.reused ?
|
|
1301
|
+
reusedNote: created.reused ? `An existing unpaid order for this link was reused (${rozoPaymentId}), valid for another ${formatRemaining(expiry.msRemaining)}. Nothing new was created.` : null,
|
|
1302
|
+
orderCost: "Creating an order moves no money. An order you never fund simply expires and costs nothing.",
|
|
1260
1303
|
linkId: created.linkId ?? parsedLinkId,
|
|
1261
1304
|
rozoPaymentId,
|
|
1262
1305
|
paymentLink: created.paymentLink ?? null,
|
|
@@ -1274,12 +1317,16 @@ async function main(argv) {
|
|
|
1274
1317
|
// Lightning has no deposit address: the BOLT11 IS the instruction.
|
|
1275
1318
|
receiverAddress: lightning ? null : source.receiverAddress,
|
|
1276
1319
|
receiverMemo: source.receiverMemo ?? null,
|
|
1320
|
+
// Stellar memos are TEXT even when they look numeric. Sending one as
|
|
1321
|
+
// MEMO_ID produces a different memo and the payment will not match.
|
|
1322
|
+
receiverMemoType: source.receiverMemo ? STELLAR_MEMO_TYPE : null,
|
|
1277
1323
|
amount: source.amount,
|
|
1278
1324
|
amountUnit: source.amountUnit ?? null,
|
|
1279
1325
|
isSats: isSatsUnit(source.amountUnit),
|
|
1280
1326
|
lnInvoice: bolt11 || null,
|
|
1281
1327
|
payTo: depositInfo.payTo,
|
|
1282
|
-
expiresAt: payment?.expiresAt ?? null
|
|
1328
|
+
expiresAt: payment?.expiresAt ?? null,
|
|
1329
|
+
expiresIn: formatRemaining(expiry.msRemaining)
|
|
1283
1330
|
} : null,
|
|
1284
1331
|
depositWithheld: !confirmed,
|
|
1285
1332
|
// Safe for prose / chat.
|
|
@@ -1291,12 +1338,16 @@ async function main(argv) {
|
|
|
1291
1338
|
payToMasked: maskAddress(depositInfo.payTo),
|
|
1292
1339
|
receiverMemoMasked: maskMemo(source.receiverMemo),
|
|
1293
1340
|
hasMemo: Boolean(source.receiverMemo),
|
|
1341
|
+
memoType: source.receiverMemo ? STELLAR_MEMO_TYPE : null,
|
|
1294
1342
|
memoRequirement
|
|
1295
1343
|
},
|
|
1296
1344
|
expiry: {
|
|
1297
1345
|
intentExpiresAt: payment?.expiresAt ?? null,
|
|
1298
1346
|
coinbaseExpiry: snapshot?.coinbase?.preApprovalExpiry ?? null,
|
|
1299
1347
|
effectiveDeadlineIso: new Date(expiry.effectiveDeadlineMs).toISOString(),
|
|
1348
|
+
// A duration, not just a timestamp: this is what a payer actually needs.
|
|
1349
|
+
expiresIn: formatRemaining(expiry.msRemaining),
|
|
1350
|
+
msRemaining: expiry.msRemaining,
|
|
1300
1351
|
marginMinutes: Math.round(expiry.marginMs / 6e4),
|
|
1301
1352
|
minutesOfSlack: Math.floor(expiry.msOfSlack / 6e4)
|
|
1302
1353
|
},
|
package/scripts/dist/quote.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __rozoCreateRequire } from 'node:module';
|
|
3
|
+
import { fileURLToPath as __rozoFileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname as __rozoDirname } from 'node:path';
|
|
3
5
|
const require = __rozoCreateRequire(import.meta.url);
|
|
6
|
+
const __filename = __rozoFileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = __rozoDirname(__filename);
|
|
4
8
|
|
|
5
9
|
// scripts/src/lib/output.mjs
|
|
6
10
|
var EXIT_OK = 0;
|
package/scripts/dist/send-evm.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __rozoCreateRequire } from 'node:module';
|
|
3
|
+
import { fileURLToPath as __rozoFileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname as __rozoDirname } from 'node:path';
|
|
3
5
|
const require = __rozoCreateRequire(import.meta.url);
|
|
6
|
+
const __filename = __rozoFileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = __rozoDirname(__filename);
|
|
4
8
|
var __defProp = Object.defineProperty;
|
|
5
9
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
10
|
var __esm = (fn, res) => function __init() {
|
package/scripts/dist/send-sol.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __rozoCreateRequire } from 'node:module';
|
|
3
|
+
import { fileURLToPath as __rozoFileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname as __rozoDirname } from 'node:path';
|
|
3
5
|
const require = __rozoCreateRequire(import.meta.url);
|
|
6
|
+
const __filename = __rozoFileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = __rozoDirname(__filename);
|
|
4
8
|
var __create = Object.create;
|
|
5
9
|
var __defProp = Object.defineProperty;
|
|
6
10
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
package/scripts/dist/status.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire as __rozoCreateRequire } from 'node:module';
|
|
3
|
+
import { fileURLToPath as __rozoFileURLToPath } from 'node:url';
|
|
4
|
+
import { dirname as __rozoDirname } from 'node:path';
|
|
3
5
|
const require = __rozoCreateRequire(import.meta.url);
|
|
6
|
+
const __filename = __rozoFileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = __rozoDirname(__filename);
|
|
4
8
|
|
|
5
9
|
// scripts/src/lib/output.mjs
|
|
6
10
|
var EXIT_OK = 0;
|
|
@@ -428,6 +432,30 @@ function classifyStatus({ payment, routerState, coinbase, now = Date.now(), view
|
|
|
428
432
|
);
|
|
429
433
|
}
|
|
430
434
|
|
|
435
|
+
// scripts/src/lib/expiry.mjs
|
|
436
|
+
var MINUTE = 6e4;
|
|
437
|
+
var MARGINS_MS = {
|
|
438
|
+
1: 10 * MINUTE,
|
|
439
|
+
56: 10 * MINUTE,
|
|
440
|
+
137: 10 * MINUTE,
|
|
441
|
+
8453: 10 * MINUTE,
|
|
442
|
+
900: 5 * MINUTE,
|
|
443
|
+
1500: 10 * MINUTE,
|
|
444
|
+
lightning: 10 * MINUTE
|
|
445
|
+
};
|
|
446
|
+
var BOLT11_MIN_VALIDITY_MS = 10 * MINUTE;
|
|
447
|
+
var DEFAULT_MARGIN_MS = 10 * MINUTE;
|
|
448
|
+
function formatRemaining(ms) {
|
|
449
|
+
if (!Number.isFinite(ms)) return "unknown";
|
|
450
|
+
if (ms <= 0) return "expired";
|
|
451
|
+
const totalMinutes = Math.floor(ms / 6e4);
|
|
452
|
+
if (totalMinutes < 1) return `${Math.floor(ms / 1e3)}s`;
|
|
453
|
+
if (totalMinutes < 60) return `${totalMinutes}m`;
|
|
454
|
+
const h = Math.floor(totalMinutes / 60);
|
|
455
|
+
const m = totalMinutes % 60;
|
|
456
|
+
return m ? `${h}h ${m}m` : `${h}h`;
|
|
457
|
+
}
|
|
458
|
+
|
|
431
459
|
// scripts/src/lib/state.mjs
|
|
432
460
|
import fs from "node:fs";
|
|
433
461
|
import path from "node:path";
|
|
@@ -519,6 +547,16 @@ async function snapshot({ rozoPaymentId, linkId }) {
|
|
|
519
547
|
senderAddressMasked: source.senderAddress ? maskAddress(source.senderAddress) : null,
|
|
520
548
|
chain: source.chainId ? chainName(source.chainId) : null
|
|
521
549
|
},
|
|
550
|
+
expiry: (() => {
|
|
551
|
+
const iso = payment?.expiresAt ?? status?.rozoPayment?.expiresAt ?? null;
|
|
552
|
+
if (!iso) return { expiresAt: null, expiresIn: null, msRemaining: null };
|
|
553
|
+
const ms = Date.parse(iso) - Date.now();
|
|
554
|
+
return {
|
|
555
|
+
expiresAt: iso,
|
|
556
|
+
expiresIn: formatRemaining(ms),
|
|
557
|
+
msRemaining: Number.isFinite(ms) ? ms : null
|
|
558
|
+
};
|
|
559
|
+
})(),
|
|
522
560
|
payout: {
|
|
523
561
|
txHash: payment?.destination?.txHash ?? status?.rozoPayment?.destination?.txHash ?? null,
|
|
524
562
|
confirmedAt: payment?.destination?.confirmedAt ?? status?.rozoPayment?.destination?.confirmedAt ?? null
|
|
@@ -544,7 +582,7 @@ async function main(argv) {
|
|
|
544
582
|
if (next.state !== result.state) history.push({ at: (/* @__PURE__ */ new Date()).toISOString(), state: next.state });
|
|
545
583
|
result = next;
|
|
546
584
|
}
|
|
547
|
-
const guidance = result.escalate ? "MONEY DETECTED and the order is not on a healthy path. Do NOT pay again and do NOT create a new order for this link. Preserve linkId, rozoPaymentId and every tx hash, then escalate to the operator for manual reconciliation." : result.unknown ? "The order state could not be established. This is NOT evidence that nothing was paid \u2014 do not create a new order and do not send again on the strength of it. Retry, or pass --rozo-payment-id so the authoritative pay-in view can be read." : !result.authoritativeView ? "Only the fulfilment view was readable; the pay-in view is unavailable, so the money-detected rule cannot be enforced. Pass --rozo-payment-id for a complete answer." : result.state === "expired_unfunded" ? "Nothing was funded.
|
|
585
|
+
const guidance = result.escalate ? "MONEY DETECTED and the order is not on a healthy path. Do NOT pay again and do NOT create a new order for this link. Preserve linkId, rozoPaymentId and every tx hash, then escalate to the operator for manual reconciliation." : result.unknown ? "The order state could not be established. This is NOT evidence that nothing was paid \u2014 do not create a new order and do not send again on the strength of it. Retry, or pass --rozo-payment-id so the authoritative pay-in view can be read." : !result.authoritativeView ? "Only the fulfilment view was readable; the pay-in view is unavailable, so the money-detected rule cannot be enforced. Pass --rozo-payment-id for a complete answer." : result.state === "expired_unfunded" ? "Nothing was funded, so nothing was lost. Start a fresh order with: rozo-checkout pay <coinbase-link> --with <coin> (or create-order.js --url <link> --chain <id> --token <SYMBOL>)" : result.terminal ? "Done." : "Still in flight. Poll again in ~10s.";
|
|
548
586
|
const unresolved = watch && !result.terminal && !result.escalate && !result.unknown;
|
|
549
587
|
const failed = result.escalate || result.unknown || !result.authoritativeView;
|
|
550
588
|
emit(
|