@layerzerolabs/protocol-stellar-v2 0.2.115 → 0.2.117
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/docs/oapp-guide.md +107 -0
- package/package.json +4 -4
- package/sdk/package.json +1 -1
package/docs/oapp-guide.md
CHANGED
|
@@ -277,6 +277,113 @@ pub struct MyCustomOApp;
|
|
|
277
277
|
// Implement each trait manually...
|
|
278
278
|
```
|
|
279
279
|
|
|
280
|
+
## Debugging and recovering messages
|
|
281
|
+
|
|
282
|
+
An inbound path is tracked by `(receiver, src_eid, sender)` and guarded by an Endpoint nonce.
|
|
283
|
+
A message can only be delivered — via `lz_receive` (on the OApp) or `clear` (on the Endpoint) — once its nonce is at or below
|
|
284
|
+
`inbound_nonce`, and `inbound_nonce` only advances across the gapless prefix of verified, skipped,
|
|
285
|
+
or nilified nonces. Two very different situations are worth separating:
|
|
286
|
+
|
|
287
|
+
- **A verification gap truly stalls the channel.** If a lower nonce is never verified, `inbound_nonce`
|
|
288
|
+
stops before it, so every nonce at or above the gap becomes undeliverable until the gap is resolved
|
|
289
|
+
(for example, verified or skipped).
|
|
290
|
+
- **A reverting `lz_receive` does not, by itself, block later nonces.** The payload hash stays stored,
|
|
291
|
+
so that nonce can be retried or cleared. Whether a stuck nonce holds up higher ones depends on
|
|
292
|
+
whether the OApp enforces ordered delivery via `next_nonce` (the default returns `0`, i.e. unordered).
|
|
293
|
+
|
|
294
|
+
The Endpoint exposes four channel-management operations for an OApp to recover from these situations.
|
|
295
|
+
They are the on-chain equivalents of the skip / clear / nilify / burn flow described for other VMs.
|
|
296
|
+
|
|
297
|
+
### Authorization
|
|
298
|
+
|
|
299
|
+
All four operations are gated by `require_oapp_auth`: the `caller` must be **the OApp contract
|
|
300
|
+
itself or its registered delegate**, and `caller.require_auth()` is enforced. The `receiver`
|
|
301
|
+
argument is always the OApp address. Two calling patterns are available:
|
|
302
|
+
|
|
303
|
+
- **Delegate calls the endpoint directly** — the delegate set via `set_delegate` (typically the
|
|
304
|
+
owner or an admin) invokes the endpoint, passing itself as `caller` and the OApp as `receiver`.
|
|
305
|
+
- **OApp wraps the call** — add an admin-gated method on the OApp that reaches the endpoint through
|
|
306
|
+
the shared client and passes the OApp as both `caller` and `receiver`:
|
|
307
|
+
|
|
308
|
+
```rust
|
|
309
|
+
use oapp::oapp_core::endpoint_client;
|
|
310
|
+
|
|
311
|
+
#[contract_impl]
|
|
312
|
+
impl MyOApp {
|
|
313
|
+
#[only_role(operator, MESSAGE_ADMIN_ROLE)]
|
|
314
|
+
pub fn skip_message(env: &Env, src_eid: u32, sender: &BytesN<32>, nonce: u64, operator: &Address) {
|
|
315
|
+
let oapp = env.current_contract_address();
|
|
316
|
+
endpoint_client::<Self>(env).skip(&oapp, &oapp, &src_eid, sender, &nonce);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
`endpoint_client::<Self>(env)` returns the endpoint client, which exposes `skip`, `clear`, `nilify`,
|
|
322
|
+
and `burn` alongside a read-only `inbound_payload_hash` for inspecting stored payloads.
|
|
323
|
+
|
|
324
|
+
### Skip
|
|
325
|
+
|
|
326
|
+
Skips the next expected inbound nonce **before it is verified**. Use this to bypass a message you
|
|
327
|
+
never want delivered (e.g. flagged by a precrime alert) so the channel keeps advancing.
|
|
328
|
+
|
|
329
|
+
```rust
|
|
330
|
+
endpoint.skip(&caller, &receiver, src_eid, &sender, nonce);
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
- `nonce` **must** be the next expected nonce (`inbound_nonce + 1`), otherwise the call fails with
|
|
334
|
+
`EndpointError::InvalidNonce`.
|
|
335
|
+
- The skipped nonce counts as "verified" for ordering purposes, so subsequent nonces can proceed.
|
|
336
|
+
- Emits `InboundNonceSkipped`.
|
|
337
|
+
|
|
338
|
+
### Clear
|
|
339
|
+
|
|
340
|
+
Clears a **verified** message from the channel without running your `__lz_receive` logic. This is the
|
|
341
|
+
PULL-mode counterpart to `lz_receive`: use it to accept-and-drop a payload that can never execute
|
|
342
|
+
successfully but that you want to move past.
|
|
343
|
+
|
|
344
|
+
```rust
|
|
345
|
+
endpoint.clear(&caller, &origin, &receiver, &guid, &message);
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
- Requires the reconstructed payload (`guid` + `message`) to match the stored payload hash for the
|
|
349
|
+
nonce carried in `origin`; a missing or mismatched hash fails with
|
|
350
|
+
`EndpointError::PayloadHashNotFound`.
|
|
351
|
+
- The nonce must be at or below `inbound_nonce`, otherwise the call fails with
|
|
352
|
+
`EndpointError::InvalidNonce`.
|
|
353
|
+
- Removes the stored payload hash and emits `PacketDelivered`, but does **not** advance
|
|
354
|
+
`inbound_nonce`; nonce advancement happens during `verify`, `skip`, or `nilify`.
|
|
355
|
+
|
|
356
|
+
### Nilify
|
|
357
|
+
|
|
358
|
+
Marks a message as nil so it **cannot execute until it is re-verified**. Unlike `burn`, the message
|
|
359
|
+
can be verified again later, making this the recoverable option for temporarily blocking a nonce.
|
|
360
|
+
|
|
361
|
+
```rust
|
|
362
|
+
endpoint.nilify(&caller, &receiver, src_eid, &sender, nonce, &payload_hash);
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
- `payload_hash` must match the currently stored hash; a mismatch fails with
|
|
366
|
+
`EndpointError::PayloadHashNotFound`.
|
|
367
|
+
- Pass `None` only when no hash is stored yet, and only for a future nonce inside the pending window
|
|
368
|
+
(`inbound_nonce < nonce <= inbound_nonce + 256`).
|
|
369
|
+
- Sets the stored hash to the NIL sentinel; a fresh `verify` can restore it.
|
|
370
|
+
- Emits `PacketNilified`.
|
|
371
|
+
|
|
372
|
+
### Burn
|
|
373
|
+
|
|
374
|
+
Permanently marks a nonce as **unexecutable and un-verifiable** — it can never be re-verified or
|
|
375
|
+
executed. Use this as the terminal action for a message you have decided to discard for good.
|
|
376
|
+
|
|
377
|
+
```rust
|
|
378
|
+
endpoint.burn(&caller, &receiver, src_eid, &sender, nonce, &payload_hash);
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
- There must be a matching stored payload hash — possibly the NIL sentinel left by `nilify` — at the
|
|
382
|
+
target nonce. A missing or mismatched hash fails with `EndpointError::PayloadHashNotFound`.
|
|
383
|
+
- `nonce` must be at or below `inbound_nonce`; otherwise the call fails with
|
|
384
|
+
`EndpointError::InvalidNonce`.
|
|
385
|
+
- Removes the payload hash from storage and emits `PacketBurnt`.
|
|
386
|
+
|
|
280
387
|
## Example: Counter OApp
|
|
281
388
|
|
|
282
389
|
See `contracts/oapps/counter/` for a complete example demonstrating:
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@layerzerolabs/protocol-stellar-v2",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.117",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "LZBL-1.3",
|
|
6
6
|
"devDependencies": {
|
|
7
7
|
"@types/node": "^22.18.6",
|
|
8
8
|
"tsx": "^4.21.0",
|
|
9
9
|
"typescript": "^5.8.2",
|
|
10
|
-
"@layerzerolabs/
|
|
11
|
-
"@layerzerolabs/
|
|
12
|
-
"@layerzerolabs/
|
|
10
|
+
"@layerzerolabs/common-node-utils": "0.2.117",
|
|
11
|
+
"@layerzerolabs/stellar-ts-bindings-gen": "0.2.117",
|
|
12
|
+
"@layerzerolabs/vm-tooling-stellar": "0.2.117"
|
|
13
13
|
},
|
|
14
14
|
"publishConfig": {
|
|
15
15
|
"access": "public",
|