@layerzerolabs/protocol-stellar-v2 0.2.83 → 0.2.85
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 +79 -7
- package/docs/oft-guide.md +75 -0
- package/package.json +4 -4
- package/sdk/package.json +1 -1
package/docs/oapp-guide.md
CHANGED
|
@@ -75,6 +75,77 @@ The `init_ownable_oapp` function:
|
|
|
75
75
|
2. Stores the LayerZero endpoint address
|
|
76
76
|
3. Sets a delegate on the endpoint
|
|
77
77
|
|
|
78
|
+
## Access control
|
|
79
|
+
|
|
80
|
+
OApps use a two-layer authorization model. The `Auth` trait returns the contract's authorizer — the owner under `#[lz_contract]`, or the contract itself under `#[lz_contract(multisig)]`. On top of that, `RoleBasedAccessControl` adds OpenZeppelin-style role membership so administrative actions can be delegated without surrendering ownership. A vanilla `#[lz_contract] #[oapp]` contract exposes both layers automatically.
|
|
81
|
+
|
|
82
|
+
### Custom roles
|
|
83
|
+
|
|
84
|
+
Declare a role as a `&str` constant and gate methods with `#[only_role]` (role check + `require_auth`) or `#[has_role]` (role check only — use when the address has already been authenticated to avoid a duplicate `require_auth` panic):
|
|
85
|
+
|
|
86
|
+
```rust
|
|
87
|
+
use common_macros::only_role;
|
|
88
|
+
|
|
89
|
+
pub const CONFIG_MANAGER_ROLE: &str = "CONFIG_MANAGER";
|
|
90
|
+
|
|
91
|
+
#[contract_impl]
|
|
92
|
+
impl MyOApp {
|
|
93
|
+
#[only_role(operator, CONFIG_MANAGER_ROLE)]
|
|
94
|
+
pub fn set_config(env: &Env, value: u64, operator: &Address) {
|
|
95
|
+
// operator must hold CONFIG_MANAGER_ROLE
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The role argument is expanded to `Symbol::new(env, ROLE)` internally.
|
|
101
|
+
|
|
102
|
+
### Managing roles
|
|
103
|
+
|
|
104
|
+
The standard RBAC entry points are auto-exposed by `#[oapp]`:
|
|
105
|
+
|
|
106
|
+
```rust
|
|
107
|
+
let role = Symbol::new(env, "CONFIG_MANAGER");
|
|
108
|
+
|
|
109
|
+
oapp_client.grant_role(&account, &role, &caller); // authorizer or role-admin
|
|
110
|
+
oapp_client.revoke_role(&account, &role, &caller); // authorizer or role-admin
|
|
111
|
+
oapp_client.renounce_role(&role, &account); // self only
|
|
112
|
+
oapp_client.set_role_admin(&role, &admin_role); // authorizer only
|
|
113
|
+
oapp_client.remove_role_admin(&role); // authorizer only
|
|
114
|
+
|
|
115
|
+
oapp_client.has_role(&account, &role); // Option<u32>
|
|
116
|
+
oapp_client.get_role_admin(&role); // Option<Symbol>
|
|
117
|
+
oapp_client.get_role_member_count(&role); // u32
|
|
118
|
+
oapp_client.get_role_member(&role, index); // Address — panics if out of bounds
|
|
119
|
+
oapp_client.get_existing_roles(); // Vec<Symbol>, capped at 256
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Operations emit `RoleGranted`, `RoleRevoked`, and `RoleAdminChanged` events; failures surface as `RbacError` variants (`Unauthorized`, `RoleNotHeld`, `AdminRoleNotFound`, `MaxRolesExceeded`, etc.).
|
|
123
|
+
|
|
124
|
+
### Constructor-time setup
|
|
125
|
+
|
|
126
|
+
Constructors run before an authorizer is in scope, so use the `_no_auth` helpers to seed roles:
|
|
127
|
+
|
|
128
|
+
```rust
|
|
129
|
+
use utils::rbac::{grant_role_no_auth, set_role_admin_no_auth};
|
|
130
|
+
|
|
131
|
+
#[contract_impl]
|
|
132
|
+
impl MyOApp {
|
|
133
|
+
pub fn __constructor(
|
|
134
|
+
env: &Env,
|
|
135
|
+
owner: &Address,
|
|
136
|
+
endpoint: &Address,
|
|
137
|
+
delegate: &Address,
|
|
138
|
+
config_manager: &Address,
|
|
139
|
+
) {
|
|
140
|
+
init_ownable_oapp::<Self>(env, owner, endpoint, delegate);
|
|
141
|
+
|
|
142
|
+
let role = Symbol::new(env, "CONFIG_MANAGER");
|
|
143
|
+
grant_role_no_auth(env, config_manager, &role, owner);
|
|
144
|
+
set_role_admin_no_auth(env, &role, &Symbol::new(env, "CONFIG_ADMIN"));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
78
149
|
## Peer management
|
|
79
150
|
|
|
80
151
|
Before sending or receiving messages, configure peers for each destination chain:
|
|
@@ -217,10 +288,11 @@ See `contracts/oapps/counter/` for a complete example demonstrating:
|
|
|
217
288
|
|
|
218
289
|
## Key traits summary
|
|
219
290
|
|
|
220
|
-
| Trait
|
|
221
|
-
|
|
|
222
|
-
| `OAppCore`
|
|
223
|
-
| `OAppSenderInternal`
|
|
224
|
-
| `OAppReceiver`
|
|
225
|
-
| `LzReceiveInternal`
|
|
226
|
-
| `OAppOptionsType3`
|
|
291
|
+
| Trait | Purpose | Default behavior |
|
|
292
|
+
| ------------------------ | -------------------------------- | --------------------------------------------------------------- |
|
|
293
|
+
| `OAppCore` | Peer management, endpoint access | Stores endpoint, manages peers |
|
|
294
|
+
| `OAppSenderInternal` | Send cross-chain messages | Handles fee payment and message dispatch |
|
|
295
|
+
| `OAppReceiver` | Receive cross-chain messages | Clears payload, delegates to `__lz_receive` |
|
|
296
|
+
| `LzReceiveInternal` | Application message handling | **Must implement** |
|
|
297
|
+
| `OAppOptionsType3` | Enforced execution options | No enforced options |
|
|
298
|
+
| `RoleBasedAccessControl` | Role-based access control | Provided automatically; manages role grants and admin hierarchy |
|
package/docs/oft-guide.md
CHANGED
|
@@ -69,6 +69,8 @@ impl OFTInternal for MyOFT {
|
|
|
69
69
|
}
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
When the underlying token is a Stellar Classic Asset wrapped as a SAC, you can route mints through the [SAC Manager](#sac-manager) instead of granting raw SAC admin authority to the OFT contract.
|
|
73
|
+
|
|
72
74
|
### LockUnlock
|
|
73
75
|
|
|
74
76
|
Locks tokens in contract on send, unlocks on receive. Use for wrapping existing tokens (OFT Adapter pattern).
|
|
@@ -318,6 +320,79 @@ oft.set_rate_limit(
|
|
|
318
320
|
let available = oft.rate_limit_capacity(&Direction::Outbound, 30101);
|
|
319
321
|
```
|
|
320
322
|
|
|
323
|
+
## SAC Manager
|
|
324
|
+
|
|
325
|
+
Wraps a Stellar Asset Contract (SAC) behind role-based admin so OFT MintBurn can mint without granting raw SAC admin authority to the OFT contract. Useful when the underlying token is a Stellar Classic Asset.
|
|
326
|
+
|
|
327
|
+
The SAC Manager:
|
|
328
|
+
|
|
329
|
+
- Becomes the admin of the underlying SAC
|
|
330
|
+
- Exposes a `mint` entry point that the OFT calls during credit
|
|
331
|
+
- Gates SAC admin operations (`mint`, `clawback`, `set_authorized`, `set_admin`) behind RBAC roles
|
|
332
|
+
- Inherits ownership and role management from `Ownable` and `RoleBasedAccessControl`
|
|
333
|
+
|
|
334
|
+
### Trust Model Requirement
|
|
335
|
+
|
|
336
|
+
**The issuer account must be locked (master weight set to 0).** In Stellar classic assets, transfers from/to the issuer are equivalent to minting/burning. The issuer can always mint more tokens and perform other classic operations directly, even when an explicit admin (this contract) is set. If the issuer account is not locked, the RBAC model enforced by this contract can be bypassed, breaking the trust model.
|
|
337
|
+
|
|
338
|
+
### Roles
|
|
339
|
+
|
|
340
|
+
Four roles guard the wrapped admin operations. Roles are passed as `Symbol` values to `grant_role`:
|
|
341
|
+
|
|
342
|
+
- `MINTER_ROLE`: authorizes `mint` (granted to the OFT contract for the credit path)
|
|
343
|
+
- `CLAWBACK_ROLE`: authorizes `clawback`
|
|
344
|
+
- `BLACKLISTER_ROLE`: authorizes `set_authorized`
|
|
345
|
+
- `ADMIN_MANAGER_ROLE`: authorizes `set_admin`
|
|
346
|
+
|
|
347
|
+
### Initialization
|
|
348
|
+
|
|
349
|
+
Deploy the SAC Manager and grant `MINTER_ROLE` to the OFT contract:
|
|
350
|
+
|
|
351
|
+
```rust
|
|
352
|
+
use soroban_sdk::{Env, Address, Symbol};
|
|
353
|
+
|
|
354
|
+
// Deploy the SAC Manager bound to the SAC and an owner
|
|
355
|
+
let manager = env.register(SACManager, (&sac_token, &owner));
|
|
356
|
+
let manager_client = SACManagerClient::new(&env, &manager);
|
|
357
|
+
|
|
358
|
+
// Grant MINTER_ROLE to the OFT contract so it can mint on credit
|
|
359
|
+
let minter_role = Symbol::new(&env, "MINTER_ROLE");
|
|
360
|
+
manager_client.grant_role(&oft_address, &minter_role, &owner);
|
|
361
|
+
|
|
362
|
+
// Make the manager the admin of the SAC
|
|
363
|
+
sac_client.set_admin(&manager);
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### SAC admin operations
|
|
367
|
+
|
|
368
|
+
Each operation is gated by an `#[only_role(operator, ROLE)]` check. The `operator` argument must hold the role and authorize the call.
|
|
369
|
+
|
|
370
|
+
```rust
|
|
371
|
+
fn mint(env: &Env, to: &Address, amount: i128, operator: &Address); // MINTER_ROLE
|
|
372
|
+
fn clawback(env: &Env, from: &Address, amount: i128, operator: &Address); // CLAWBACK_ROLE
|
|
373
|
+
fn set_authorized(env: &Env, id: &Address, authorize: bool, operator: &Address); // BLACKLISTER_ROLE
|
|
374
|
+
fn set_admin(env: &Env, new_admin: &Address, operator: &Address); // ADMIN_MANAGER_ROLE
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
`mint` and `clawback` proxy to the SAC's corresponding admin functions. `clawback` additionally requires the SAC issuer to have the `ClawbackEnabled` flag set; `set_authorized` requires the `Revocable` flag.
|
|
378
|
+
|
|
379
|
+
### Integrating with OFT MintBurn
|
|
380
|
+
|
|
381
|
+
1. Deploy the SAC and lock its issuer (master weight = 0).
|
|
382
|
+
2. Deploy the SAC Manager with the SAC address and owner.
|
|
383
|
+
3. Make the SAC Manager the admin of the SAC via `sac_client.set_admin(&manager)`.
|
|
384
|
+
4. Deploy the OFT in MintBurn mode, configured with the SAC as the token and the SAC Manager as the mint authority.
|
|
385
|
+
5. Grant `MINTER_ROLE` on the SAC Manager to the OFT contract address.
|
|
386
|
+
|
|
387
|
+
On send, the OFT burns directly on the SAC via SEP-41 `burn(sender, amount)` — this is signed by the sender and does not go through the SAC Manager. On receive, the OFT calls `manager.mint(to, amount, oft_address)`, which the SAC Manager forwards to the SAC.
|
|
388
|
+
|
|
389
|
+
### Ownership and role management
|
|
390
|
+
|
|
391
|
+
The SAC Manager inherits standard ownership and RBAC entry points:
|
|
392
|
+
|
|
393
|
+
- `owner`, `pending_owner`, `transfer_ownership`, `begin_ownership_transfer`, `accept_ownership`
|
|
394
|
+
- `grant_role`, `revoke_role`, `renounce_role`, `set_role_admin`, `remove_role_admin`
|
|
395
|
+
|
|
321
396
|
## Key traits summary
|
|
322
397
|
|
|
323
398
|
| Trait | Purpose | Exposed |
|
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.85",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "LZBL-1.3",
|
|
6
6
|
"devDependencies": {
|
|
7
7
|
"@types/node": "^22.18.6",
|
|
8
8
|
"tsx": "^4.19.3",
|
|
9
9
|
"typescript": "^5.8.2",
|
|
10
|
-
"@layerzerolabs/stellar-ts-bindings-gen": "0.2.
|
|
11
|
-
"@layerzerolabs/vm-tooling-stellar": "0.2.
|
|
12
|
-
"@layerzerolabs/common-node-utils": "0.2.
|
|
10
|
+
"@layerzerolabs/stellar-ts-bindings-gen": "0.2.85",
|
|
11
|
+
"@layerzerolabs/vm-tooling-stellar": "0.2.85",
|
|
12
|
+
"@layerzerolabs/common-node-utils": "0.2.85"
|
|
13
13
|
},
|
|
14
14
|
"publishConfig": {
|
|
15
15
|
"access": "public",
|