@layerzerolabs/protocol-stellar-v2 0.2.34 → 0.2.36
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/.turbo/turbo-build.log +281 -276
- package/.turbo/turbo-lint.log +209 -211
- package/.turbo/turbo-test.log +1705 -1701
- package/Cargo.lock +10 -10
- package/Cargo.toml +1 -1
- package/contracts/common-macros/src/auth.rs +5 -5
- package/contracts/common-macros/src/lib.rs +69 -0
- package/contracts/common-macros/src/rbac.rs +90 -0
- package/contracts/common-macros/src/storage.rs +7 -5
- package/contracts/common-macros/src/tests/lz_contract.rs +5 -7
- package/contracts/common-macros/src/tests/mod.rs +1 -0
- package/contracts/common-macros/src/tests/rbac.rs +420 -0
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_multisig_code.snap +4 -4
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__auth__snapshot_generated_ownable_code.snap +5 -12
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__rbac__snapshot_preserve_function_signature.snap +17 -0
- package/contracts/common-macros/src/tests/storage/parse_name.rs +0 -1
- package/contracts/common-macros/src/tests/storage/snapshots/common_macros__tests__storage__generate_storage__snapshot_generated_storage_code.snap +3 -3
- package/contracts/endpoint-v2/src/endpoint_v2.rs +5 -4
- package/contracts/endpoint-v2/src/interfaces/messaging_channel.rs +7 -8
- package/contracts/endpoint-v2/src/messaging_channel.rs +78 -45
- package/contracts/endpoint-v2/src/storage.rs +8 -3
- package/contracts/endpoint-v2/src/tests/endpoint_setup.rs +2 -2
- package/contracts/endpoint-v2/src/tests/endpoint_v2/clear.rs +12 -15
- package/contracts/endpoint-v2/src/tests/endpoint_v2/verifiable.rs +46 -9
- package/contracts/endpoint-v2/src/tests/messaging_channel/burn.rs +7 -23
- package/contracts/endpoint-v2/src/tests/messaging_channel/clear_payload.rs +23 -20
- package/contracts/endpoint-v2/src/tests/messaging_channel/inbound.rs +94 -1
- package/contracts/endpoint-v2/src/tests/messaging_channel/inbound_nonce.rs +17 -15
- package/contracts/endpoint-v2/src/tests/messaging_channel/mod.rs +1 -1
- package/contracts/endpoint-v2/src/tests/messaging_channel/nilify.rs +48 -13
- package/contracts/endpoint-v2/src/tests/messaging_channel/pending_inbound_nonces.rs +111 -0
- package/contracts/endpoint-v2/src/tests/messaging_channel/skip.rs +15 -25
- package/contracts/layerzero-views/src/layerzero_view.rs +2 -2
- package/contracts/layerzero-views/src/tests/layerzero_view_tests.rs +3 -4
- package/contracts/layerzero-views/src/tests/setup.rs +0 -21
- package/contracts/macro-integration-tests/tests/runtime/lz_contract/wrapper_default.rs +1 -1
- package/contracts/macro-integration-tests/tests/runtime/lz_contract/wrapper_multisig.rs +1 -1
- package/contracts/macro-integration-tests/tests/runtime/lz_contract/wrapper_multisig_upgradeable.rs +1 -1
- package/contracts/macro-integration-tests/tests/runtime/multisig/self_auth.rs +1 -1
- package/contracts/macro-integration-tests/tests/runtime/ownable/initialization.rs +8 -5
- package/contracts/macro-integration-tests/tests/runtime/ownable/ownership_transfer.rs +2 -2
- package/contracts/macro-integration-tests/tests/runtime/rbac/guard_behavior.rs +91 -0
- package/contracts/macro-integration-tests/tests/runtime/rbac/mod.rs +30 -0
- package/contracts/macro-integration-tests/tests/runtime/ttl_configurable/configuration.rs +2 -2
- package/contracts/macro-integration-tests/tests/runtime/upgradeable/migrate_guard_and_state.rs +4 -4
- package/contracts/macro-integration-tests/tests/ui/lz_contract/pass/basic.rs +1 -1
- package/contracts/macro-integration-tests/tests/ui/ownable/pass/basic.rs +1 -1
- package/contracts/macro-integration-tests/tests/ui/rbac/fail/missing_env.rs +18 -0
- package/contracts/macro-integration-tests/tests/ui/rbac/fail/missing_env.stderr +16 -0
- package/contracts/macro-integration-tests/tests/ui/rbac/fail/param_not_address.rs +18 -0
- package/contracts/macro-integration-tests/tests/ui/rbac/fail/param_not_address.stderr +24 -0
- package/contracts/macro-integration-tests/tests/ui/rbac/fail/param_not_found.rs +18 -0
- package/contracts/macro-integration-tests/tests/ui/rbac/fail/param_not_found.stderr +24 -0
- package/contracts/macro-integration-tests/tests/ui/rbac/pass/basic.rs +71 -0
- package/contracts/macro-integration-tests/tests/ui_rbac.rs +12 -0
- package/contracts/message-libs/blocked-message-lib/src/lib.rs +4 -4
- package/contracts/message-libs/uln-302/src/send_uln.rs +5 -5
- package/contracts/oapps/counter/src/counter.rs +6 -0
- package/contracts/oapps/oapp/src/oapp_sender.rs +3 -2
- package/contracts/oapps/oft/src/extensions/oft_fee.rs +5 -0
- package/contracts/oapps/oft/src/interfaces/mintable.rs +2 -2
- package/contracts/oapps/oft/src/oft.rs +5 -4
- package/contracts/oapps/oft/src/tests/extensions/oft_fee.rs +2 -2
- package/contracts/oapps/oft/src/tests/extensions/pausable.rs +2 -2
- package/contracts/oapps/oft/src/tests/extensions/rate_limiter.rs +2 -2
- package/contracts/oapps/sac-manager/Cargo.toml +0 -1
- package/contracts/oapps/sac-manager/src/interfaces/mod.rs +3 -0
- package/contracts/oapps/sac-manager/src/interfaces/sac_admin_wrapper.rs +49 -0
- package/contracts/oapps/sac-manager/src/lib.rs +3 -3
- package/contracts/oapps/sac-manager/src/sac_manager.rs +45 -73
- package/contracts/oapps/sac-manager/src/storage.rs +2 -9
- package/contracts/oapps/sac-manager/src/tests/sac_manager/clawback.rs +8 -10
- package/contracts/oapps/sac-manager/src/tests/sac_manager/mint.rs +13 -18
- package/contracts/oapps/sac-manager/src/tests/sac_manager/mod.rs +0 -1
- package/contracts/oapps/sac-manager/src/tests/sac_manager/set_admin.rs +22 -12
- package/contracts/oapps/sac-manager/src/tests/sac_manager/set_authorized.rs +19 -9
- package/contracts/oapps/sac-manager/src/tests/sac_manager/test_helper.rs +27 -10
- package/contracts/oapps/sac-manager/src/tests/sac_manager/view_functions.rs +0 -15
- package/contracts/oapps/sac-manager/src/tests/test_helper.rs +19 -28
- package/contracts/upgrader/src/lib.rs +5 -2
- package/contracts/utils/src/auth.rs +6 -2
- package/contracts/utils/src/errors.rs +18 -0
- package/contracts/utils/src/lib.rs +1 -0
- package/contracts/utils/src/multisig.rs +5 -1
- package/contracts/utils/src/ownable.rs +1 -1
- package/contracts/utils/src/rbac.rs +428 -0
- package/contracts/utils/src/tests/auth.rs +2 -2
- package/contracts/utils/src/tests/mod.rs +1 -0
- package/contracts/utils/src/tests/multisig.rs +2 -2
- package/contracts/utils/src/tests/ownable.rs +4 -5
- package/contracts/utils/src/tests/rbac.rs +559 -0
- package/contracts/utils/src/tests/ttl_configurable.rs +5 -6
- package/contracts/utils/src/tests/upgradeable.rs +4 -5
- package/contracts/workers/worker/src/worker.rs +1 -1
- package/docs/layerzero-v2-on-stellar.md +46 -2
- package/package.json +3 -3
- package/sdk/.turbo/turbo-test.log +370 -372
- package/sdk/dist/generated/bml.d.ts +53 -3
- package/sdk/dist/generated/bml.js +27 -3
- package/sdk/dist/generated/counter.d.ts +84 -5
- package/sdk/dist/generated/counter.js +31 -4
- package/sdk/dist/generated/dvn.d.ts +55 -5
- package/sdk/dist/generated/dvn.js +28 -4
- package/sdk/dist/generated/dvn_fee_lib.d.ts +55 -5
- package/sdk/dist/generated/dvn_fee_lib.js +28 -4
- package/sdk/dist/generated/endpoint.d.ts +64 -15
- package/sdk/dist/generated/endpoint.js +32 -8
- package/sdk/dist/generated/executor.d.ts +55 -5
- package/sdk/dist/generated/executor.js +28 -4
- package/sdk/dist/generated/executor_fee_lib.d.ts +55 -5
- package/sdk/dist/generated/executor_fee_lib.js +28 -4
- package/sdk/dist/generated/executor_helper.d.ts +53 -3
- package/sdk/dist/generated/executor_helper.js +27 -3
- package/sdk/dist/generated/layerzero_view.d.ts +55 -5
- package/sdk/dist/generated/layerzero_view.js +28 -4
- package/sdk/dist/generated/oft.d.ts +84 -5
- package/sdk/dist/generated/oft.js +31 -4
- package/sdk/dist/generated/price_feed.d.ts +55 -5
- package/sdk/dist/generated/price_feed.js +28 -4
- package/sdk/dist/generated/sac_manager.d.ts +213 -666
- package/sdk/dist/generated/sac_manager.js +57 -238
- package/sdk/dist/generated/sml.d.ts +55 -5
- package/sdk/dist/generated/sml.js +28 -4
- package/sdk/dist/generated/treasury.d.ts +55 -5
- package/sdk/dist/generated/treasury.js +28 -4
- package/sdk/dist/generated/uln302.d.ts +55 -5
- package/sdk/dist/generated/uln302.js +28 -4
- package/sdk/dist/generated/upgrader.d.ts +53 -3
- package/sdk/dist/generated/upgrader.js +27 -3
- package/sdk/package.json +1 -1
- package/sdk/test/oft-sml.test.ts +10 -9
- package/sdk/test/{sac-manager-redistribution.test.ts → sac-manager.test.ts} +49 -25
- package/contracts/endpoint-v2/src/tests/messaging_channel/lazy_inbound_nonce.rs +0 -39
- package/contracts/oapps/sac-manager/src/errors.rs +0 -14
- package/contracts/oapps/sac-manager/src/tests/sac_manager/set_minter.rs +0 -69
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
use super::{TestContract, TestContractClient};
|
|
9
9
|
use soroban_sdk::{testutils::Address as _, Address, Env};
|
|
10
|
-
use utils::errors::OwnableError;
|
|
10
|
+
use utils::errors::{AuthError, OwnableError};
|
|
11
11
|
|
|
12
12
|
#[test]
|
|
13
13
|
fn init_and_query() {
|
|
@@ -52,9 +52,12 @@ fn uninitialized_returns_none() {
|
|
|
52
52
|
// No init() call - owner should be None
|
|
53
53
|
assert_eq!(client.owner(), None);
|
|
54
54
|
|
|
55
|
-
//
|
|
56
|
-
assert_eq!(client.
|
|
57
|
-
|
|
55
|
+
// authorizer() returns None when owner is not set (no panic)
|
|
56
|
+
assert_eq!(client.authorizer(), None);
|
|
57
|
+
|
|
58
|
+
// guarded() uses require_auth -> AuthorizerNotFound when authorizer is None
|
|
59
|
+
assert_eq!(client.try_guarded().unwrap_err().unwrap(), AuthError::AuthorizerNotFound.into());
|
|
60
|
+
// transfer/renounce use enforce_owner_auth -> OwnerNotSet when owner is None
|
|
58
61
|
assert_eq!(
|
|
59
62
|
client.try_transfer_ownership(&Address::generate(&env)).unwrap_err().unwrap(),
|
|
60
63
|
OwnableError::OwnerNotSet.into()
|
|
@@ -71,5 +74,5 @@ fn authorizer_returns_owner() {
|
|
|
71
74
|
let owner = Address::generate(&env);
|
|
72
75
|
client.init(&owner);
|
|
73
76
|
|
|
74
|
-
assert_eq!(client.authorizer(), owner);
|
|
77
|
+
assert_eq!(client.authorizer(), Some(owner));
|
|
75
78
|
}
|
|
@@ -10,7 +10,7 @@ use soroban_sdk::{
|
|
|
10
10
|
xdr::{ScErrorCode, ScErrorType},
|
|
11
11
|
Address, Env, Error, IntoVal,
|
|
12
12
|
};
|
|
13
|
-
use utils::errors::OwnableError;
|
|
13
|
+
use utils::errors::{AuthError, OwnableError};
|
|
14
14
|
use utils::ownable::{OwnershipRenounced, OwnershipTransferred};
|
|
15
15
|
use utils::testing_utils::assert_eq_event;
|
|
16
16
|
|
|
@@ -161,7 +161,7 @@ fn renounce_clears_owner() {
|
|
|
161
161
|
},
|
|
162
162
|
}])
|
|
163
163
|
.try_guarded();
|
|
164
|
-
assert_eq!(guarded_result.unwrap_err().unwrap(),
|
|
164
|
+
assert_eq!(guarded_result.unwrap_err().unwrap(), AuthError::AuthorizerNotFound.into());
|
|
165
165
|
|
|
166
166
|
assert_eq!(
|
|
167
167
|
client.try_transfer_ownership(&Address::generate(&env)).unwrap_err().unwrap(),
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
use super::{TestContract, TestContractClient};
|
|
2
|
+
use soroban_sdk::{
|
|
3
|
+
testutils::{Address as _, MockAuth, MockAuthInvoke},
|
|
4
|
+
xdr::{ScErrorCode, ScErrorType},
|
|
5
|
+
Address, Env, Error, IntoVal,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
#[test]
|
|
9
|
+
fn has_role_allows_member_and_rejects_non_member() {
|
|
10
|
+
let env = Env::default();
|
|
11
|
+
let contract_id = env.register(TestContract, ());
|
|
12
|
+
let client = TestContractClient::new(&env, &contract_id);
|
|
13
|
+
|
|
14
|
+
let admin = Address::generate(&env);
|
|
15
|
+
let minter = Address::generate(&env);
|
|
16
|
+
let other = Address::generate(&env);
|
|
17
|
+
|
|
18
|
+
client.init(&admin, &minter);
|
|
19
|
+
|
|
20
|
+
// Member call should succeed.
|
|
21
|
+
client.has_role_guarded(&minter);
|
|
22
|
+
|
|
23
|
+
// Non-member call should fail with RBAC Unauthorized.
|
|
24
|
+
let res = client.try_has_role_guarded(&other);
|
|
25
|
+
assert_eq!(res.err().unwrap().ok().unwrap(), utils::errors::RbacError::Unauthorized.into());
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#[test]
|
|
29
|
+
fn only_role_enforces_require_auth_and_role_membership() {
|
|
30
|
+
let env = Env::default();
|
|
31
|
+
let contract_id = env.register(TestContract, ());
|
|
32
|
+
let client = TestContractClient::new(&env, &contract_id);
|
|
33
|
+
|
|
34
|
+
let admin = Address::generate(&env);
|
|
35
|
+
let minter = Address::generate(&env);
|
|
36
|
+
let other = Address::generate(&env);
|
|
37
|
+
|
|
38
|
+
client.init(&admin, &minter);
|
|
39
|
+
|
|
40
|
+
// 1) Role holder without auth should fail at `require_auth()`.
|
|
41
|
+
let no_auth = client.try_only_role_guarded(&minter);
|
|
42
|
+
assert_eq!(
|
|
43
|
+
no_auth.unwrap_err().unwrap(),
|
|
44
|
+
Error::from_type_and_code(ScErrorType::Context, ScErrorCode::InvalidAction)
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// 2) With auth but role missing, should fail with RBAC Unauthorized.
|
|
48
|
+
let missing_role_with_auth = client
|
|
49
|
+
.mock_auths(&[MockAuth {
|
|
50
|
+
address: &other,
|
|
51
|
+
invoke: &MockAuthInvoke {
|
|
52
|
+
contract: &contract_id,
|
|
53
|
+
fn_name: "only_role_guarded",
|
|
54
|
+
args: (&other,).into_val(&env),
|
|
55
|
+
sub_invokes: &[],
|
|
56
|
+
},
|
|
57
|
+
}])
|
|
58
|
+
.try_only_role_guarded(&other);
|
|
59
|
+
assert_eq!(missing_role_with_auth.err().unwrap().ok().unwrap(), utils::errors::RbacError::Unauthorized.into());
|
|
60
|
+
|
|
61
|
+
// 3) With auth + role, should succeed.
|
|
62
|
+
client
|
|
63
|
+
.mock_auths(&[MockAuth {
|
|
64
|
+
address: &minter,
|
|
65
|
+
invoke: &MockAuthInvoke {
|
|
66
|
+
contract: &contract_id,
|
|
67
|
+
fn_name: "only_role_guarded",
|
|
68
|
+
args: (&minter,).into_val(&env),
|
|
69
|
+
sub_invokes: &[],
|
|
70
|
+
},
|
|
71
|
+
}])
|
|
72
|
+
.only_role_guarded(&minter);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#[test]
|
|
76
|
+
fn only_role_checks_role_before_require_auth() {
|
|
77
|
+
let env = Env::default();
|
|
78
|
+
let contract_id = env.register(TestContract, ());
|
|
79
|
+
let client = TestContractClient::new(&env, &contract_id);
|
|
80
|
+
|
|
81
|
+
let admin = Address::generate(&env);
|
|
82
|
+
let minter = Address::generate(&env);
|
|
83
|
+
let other = Address::generate(&env);
|
|
84
|
+
|
|
85
|
+
client.init(&admin, &minter);
|
|
86
|
+
|
|
87
|
+
// No auth, no role: current macro expansion checks role first, so this should
|
|
88
|
+
// return RBAC Unauthorized (not an auth error).
|
|
89
|
+
let res = client.try_only_role_guarded(&other);
|
|
90
|
+
assert_eq!(res.err().unwrap().ok().unwrap(), utils::errors::RbacError::Unauthorized.into());
|
|
91
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
use soroban_sdk::{contract, contractimpl, Address, Env, Symbol};
|
|
2
|
+
|
|
3
|
+
mod guard_behavior;
|
|
4
|
+
|
|
5
|
+
const MINTER_ROLE: &str = "minter";
|
|
6
|
+
|
|
7
|
+
#[contract]
|
|
8
|
+
pub struct TestContract;
|
|
9
|
+
|
|
10
|
+
#[contractimpl]
|
|
11
|
+
impl TestContract {
|
|
12
|
+
/// Test helper to deterministically grant the minter role to `minter`.
|
|
13
|
+
///
|
|
14
|
+
/// This is intentionally unguarded; it is only used in integration tests.
|
|
15
|
+
pub fn init(env: Env, admin: Address, minter: Address) {
|
|
16
|
+
let role = Symbol::new(&env, MINTER_ROLE);
|
|
17
|
+
utils::rbac::grant_role_no_auth(&env, &minter, &role, &admin);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
#[common_macros::has_role(caller, MINTER_ROLE)]
|
|
21
|
+
pub fn has_role_guarded(env: &Env, caller: Address) {
|
|
22
|
+
let _ = (env, caller);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#[common_macros::only_role(caller, MINTER_ROLE)]
|
|
26
|
+
pub fn only_role_guarded(env: &Env, caller: Address) {
|
|
27
|
+
let _ = (env, caller);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
@@ -14,7 +14,7 @@ use soroban_sdk::{
|
|
|
14
14
|
};
|
|
15
15
|
use utils::testing_utils::assert_eq_event;
|
|
16
16
|
use utils::ttl_configurable::TtlConfigsSet;
|
|
17
|
-
use utils::{errors::{
|
|
17
|
+
use utils::{errors::{AuthError, TtlConfigurableError}, ttl_configurable::TtlConfig};
|
|
18
18
|
|
|
19
19
|
#[contract]
|
|
20
20
|
#[ttl_configurable]
|
|
@@ -98,7 +98,7 @@ fn set_before_init_fails_with_owner_not_set() {
|
|
|
98
98
|
let instance = Some(TtlConfig::new(1, 2));
|
|
99
99
|
let persistent = None::<TtlConfig>;
|
|
100
100
|
let err = client.try_set_ttl_configs(&instance, &persistent).unwrap_err().unwrap();
|
|
101
|
-
assert_eq!(err,
|
|
101
|
+
assert_eq!(err, AuthError::AuthorizerNotFound.into());
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
#[test]
|
package/contracts/macro-integration-tests/tests/runtime/upgradeable/migrate_guard_and_state.rs
CHANGED
|
@@ -14,7 +14,7 @@ use soroban_sdk::{
|
|
|
14
14
|
Address, Bytes, BytesN, Env, Error, IntoVal, Val,
|
|
15
15
|
};
|
|
16
16
|
use utils::upgradeable::{UpgradeableInternal, UpgradeableStorage};
|
|
17
|
-
use utils::errors::
|
|
17
|
+
use utils::errors::AuthError;
|
|
18
18
|
|
|
19
19
|
// A small, known-good contract WASM used for upgrade() testing.
|
|
20
20
|
// Sourced from the `upgrader` crate test fixtures.
|
|
@@ -192,10 +192,10 @@ fn upgrade_and_migrate_fail_before_owner_init() {
|
|
|
192
192
|
let contract_id = env.register(TestContract, ());
|
|
193
193
|
let client = TestContractClient::new(&env, &contract_id);
|
|
194
194
|
|
|
195
|
-
// Without owner initialization, authorizer lookup fails (
|
|
195
|
+
// Without owner initialization, authorizer lookup fails (AuthError::AuthorizerNotFound),
|
|
196
196
|
// which should surface as a contract error (not Context/InvalidAction).
|
|
197
197
|
let hash = BytesN::<32>::from_array(&env, &[7u8; 32]);
|
|
198
|
-
assert_eq!(client.try_upgrade(&hash).unwrap_err().unwrap(),
|
|
198
|
+
assert_eq!(client.try_upgrade(&hash).unwrap_err().unwrap(), AuthError::AuthorizerNotFound.into());
|
|
199
199
|
|
|
200
200
|
// Even if migrating is set, migrate() still fails before init because auth can't resolve authorizer.
|
|
201
201
|
env.as_contract(&contract_id, || {
|
|
@@ -204,7 +204,7 @@ fn upgrade_and_migrate_fail_before_owner_init() {
|
|
|
204
204
|
let migration_data = 1u32.to_xdr(&env);
|
|
205
205
|
assert_eq!(
|
|
206
206
|
client.try_migrate(&migration_data).unwrap_err().unwrap(),
|
|
207
|
-
|
|
207
|
+
AuthError::AuthorizerNotFound.into()
|
|
208
208
|
);
|
|
209
209
|
}
|
|
210
210
|
|
|
@@ -20,7 +20,7 @@ impl MyContract {
|
|
|
20
20
|
|
|
21
21
|
pub fn smoke(env: Env) {
|
|
22
22
|
// Auth impl exists (provided by ownable/multisig, here: ownable).
|
|
23
|
-
let _authorizer: Address = <Self as utils::auth::Auth>::authorizer(&env);
|
|
23
|
+
let _authorizer: Option<Address> = <Self as utils::auth::Auth>::authorizer(&env);
|
|
24
24
|
let _owner: Option<Address> = <Self as utils::ownable::Ownable>::owner(&env);
|
|
25
25
|
|
|
26
26
|
let _cfg: (Option<TtlConfig>, Option<TtlConfig>) = Self::ttl_configs(&env);
|
|
@@ -22,7 +22,7 @@ impl MyContract {
|
|
|
22
22
|
<Self as utils::ownable::OwnableInitializer>::init_owner(&env, &owner);
|
|
23
23
|
|
|
24
24
|
// Auth impl exists (type-check only).
|
|
25
|
-
let _authorizer: Address = <Self as utils::auth::Auth>::authorizer(&env);
|
|
25
|
+
let _authorizer: Option<Address> = <Self as utils::auth::Auth>::authorizer(&env);
|
|
26
26
|
|
|
27
27
|
// Ownable trait impl exists (type-check only).
|
|
28
28
|
let _owner: Option<Address> = <Self as utils::ownable::Ownable>::owner(&env);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// UI (trybuild) negative test: `#[has_role]` requires an `Env` parameter.
|
|
2
|
+
|
|
3
|
+
use soroban_sdk::{contract, contractimpl, Address};
|
|
4
|
+
|
|
5
|
+
#[contract]
|
|
6
|
+
pub struct MyContract;
|
|
7
|
+
|
|
8
|
+
#[contractimpl]
|
|
9
|
+
impl MyContract {
|
|
10
|
+
// Intentionally missing `Env`: should fail during macro expansion.
|
|
11
|
+
#[common_macros::has_role(caller, "minter")]
|
|
12
|
+
pub fn bad(caller: Address) {
|
|
13
|
+
let _ = caller;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fn main() {}
|
|
18
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
error: custom attribute panicked
|
|
2
|
+
--> tests/ui/rbac/fail/missing_env.rs:11:5
|
|
3
|
+
|
|
|
4
|
+
11 | #[common_macros::has_role(caller, "minter")]
|
|
5
|
+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
6
|
+
|
|
|
7
|
+
= help: message: function must have an Env argument
|
|
8
|
+
|
|
9
|
+
error[E0599]: no function or associated item named `bad` found for struct `MyContract` in the current scope
|
|
10
|
+
--> tests/ui/rbac/fail/missing_env.rs:12:12
|
|
11
|
+
|
|
|
12
|
+
6 | pub struct MyContract;
|
|
13
|
+
| --------------------- function or associated item `bad` not found for this struct
|
|
14
|
+
...
|
|
15
|
+
12 | pub fn bad(caller: Address) {
|
|
16
|
+
| ^^^ function or associated item not found in `MyContract`
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// UI (trybuild) negative test: `#[has_role]` requires the named parameter to be `Address` or `&Address`.
|
|
2
|
+
|
|
3
|
+
use soroban_sdk::{contract, contractimpl, Env};
|
|
4
|
+
|
|
5
|
+
#[contract]
|
|
6
|
+
pub struct MyContract;
|
|
7
|
+
|
|
8
|
+
#[contractimpl]
|
|
9
|
+
impl MyContract {
|
|
10
|
+
// `caller` exists but is not an Address type.
|
|
11
|
+
#[common_macros::has_role(caller, "minter")]
|
|
12
|
+
pub fn bad(env: Env, caller: u32) {
|
|
13
|
+
let _ = (env, caller);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fn main() {}
|
|
18
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
error: custom attribute panicked
|
|
2
|
+
--> tests/ui/rbac/fail/param_not_address.rs:11:5
|
|
3
|
+
|
|
|
4
|
+
11 | #[common_macros::has_role(caller, "minter")]
|
|
5
|
+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
6
|
+
|
|
|
7
|
+
= help: message: Parameter `caller` must be of type `Address` or `&Address`
|
|
8
|
+
|
|
9
|
+
warning: unused import: `Env`
|
|
10
|
+
--> tests/ui/rbac/fail/param_not_address.rs:3:43
|
|
11
|
+
|
|
|
12
|
+
3 | use soroban_sdk::{contract, contractimpl, Env};
|
|
13
|
+
| ^^^
|
|
14
|
+
|
|
|
15
|
+
= note: `#[warn(unused_imports)]` on by default
|
|
16
|
+
|
|
17
|
+
error[E0599]: no function or associated item named `bad` found for struct `MyContract` in the current scope
|
|
18
|
+
--> tests/ui/rbac/fail/param_not_address.rs:12:12
|
|
19
|
+
|
|
|
20
|
+
6 | pub struct MyContract;
|
|
21
|
+
| --------------------- function or associated item `bad` not found for this struct
|
|
22
|
+
...
|
|
23
|
+
12 | pub fn bad(env: Env, caller: u32) {
|
|
24
|
+
| ^^^ function or associated item not found in `MyContract`
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// UI (trybuild) negative test: `#[has_role]` requires the first arg to name a parameter in the signature.
|
|
2
|
+
|
|
3
|
+
use soroban_sdk::{contract, contractimpl, Address, Env};
|
|
4
|
+
|
|
5
|
+
#[contract]
|
|
6
|
+
pub struct MyContract;
|
|
7
|
+
|
|
8
|
+
#[contractimpl]
|
|
9
|
+
impl MyContract {
|
|
10
|
+
// Macro names `caller` but the signature uses `account`.
|
|
11
|
+
#[common_macros::has_role(caller, "minter")]
|
|
12
|
+
pub fn bad(env: Env, account: Address) {
|
|
13
|
+
let _ = (env, account);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fn main() {}
|
|
18
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
error: custom attribute panicked
|
|
2
|
+
--> tests/ui/rbac/fail/param_not_found.rs:11:5
|
|
3
|
+
|
|
|
4
|
+
11 | #[common_macros::has_role(caller, "minter")]
|
|
5
|
+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
6
|
+
|
|
|
7
|
+
= help: message: Parameter `caller` not found in function signature
|
|
8
|
+
|
|
9
|
+
warning: unused import: `Env`
|
|
10
|
+
--> tests/ui/rbac/fail/param_not_found.rs:3:52
|
|
11
|
+
|
|
|
12
|
+
3 | use soroban_sdk::{contract, contractimpl, Address, Env};
|
|
13
|
+
| ^^^
|
|
14
|
+
|
|
|
15
|
+
= note: `#[warn(unused_imports)]` on by default
|
|
16
|
+
|
|
17
|
+
error[E0599]: no function or associated item named `bad` found for struct `MyContract` in the current scope
|
|
18
|
+
--> tests/ui/rbac/fail/param_not_found.rs:12:12
|
|
19
|
+
|
|
|
20
|
+
6 | pub struct MyContract;
|
|
21
|
+
| --------------------- function or associated item `bad` not found for this struct
|
|
22
|
+
...
|
|
23
|
+
12 | pub fn bad(env: Env, account: Address) {
|
|
24
|
+
| ^^^ function or associated item not found in `MyContract`
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// UI (trybuild) test: `#[has_role]` and `#[only_role]` compile for common signatures.
|
|
2
|
+
//
|
|
3
|
+
// Purpose:
|
|
4
|
+
// - Verifies the RBAC attribute macros can locate an `Env` argument anywhere in the signature.
|
|
5
|
+
// - Verifies both `Address` and `&Address` parameters are accepted.
|
|
6
|
+
// - Verifies role argument can be a literal or const expression.
|
|
7
|
+
|
|
8
|
+
use soroban_sdk::{contract, Address, Env};
|
|
9
|
+
|
|
10
|
+
const MINTER_ROLE: &str = "minter";
|
|
11
|
+
|
|
12
|
+
#[contract]
|
|
13
|
+
pub struct MyContract;
|
|
14
|
+
|
|
15
|
+
impl MyContract {
|
|
16
|
+
// Env by reference, Address by value, role literal.
|
|
17
|
+
#[common_macros::has_role(caller, "minter")]
|
|
18
|
+
pub fn f1(env: &Env, caller: Address) {
|
|
19
|
+
let _ = (env, caller);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Env by value, Address by value, role const expr.
|
|
23
|
+
#[common_macros::has_role(caller, MINTER_ROLE)]
|
|
24
|
+
pub fn f2(env: Env, caller: Address) {
|
|
25
|
+
let _ = (env, caller);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Env not first param, Address by value.
|
|
29
|
+
#[common_macros::has_role(caller, "minter")]
|
|
30
|
+
pub fn f3(x: u32, env: &Env, caller: Address) {
|
|
31
|
+
let _ = (x, env, caller);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// &Address param is accepted (forwarded without extra borrow).
|
|
35
|
+
#[common_macros::has_role(caller, "minter")]
|
|
36
|
+
pub fn f4(env: &Env, caller: &Address) {
|
|
37
|
+
let _ = (env, caller);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// only_role also injects `require_auth()`.
|
|
41
|
+
#[common_macros::only_role(caller, "minter")]
|
|
42
|
+
pub fn f5(env: &Env, caller: Address) {
|
|
43
|
+
let _ = (env, caller);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// only_role also injects `require_auth()`.
|
|
47
|
+
#[common_macros::only_role(caller, MINTER_ROLE)]
|
|
48
|
+
pub fn f6(env: &Env, caller: Address) {
|
|
49
|
+
let _ = (env, caller);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Env not first param (only_role).
|
|
53
|
+
#[common_macros::only_role(caller, "minter")]
|
|
54
|
+
pub fn f7(x: u32, env: &Env, caller: Address) {
|
|
55
|
+
let _ = (x, env, caller);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Qualified Env + Address types are accepted.
|
|
59
|
+
#[common_macros::has_role(caller, "minter")]
|
|
60
|
+
pub fn f8(env: &soroban_sdk::Env, caller: soroban_sdk::Address) {
|
|
61
|
+
let _ = (env, caller);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Fully-qualified Env path + qualified &Address.
|
|
65
|
+
#[common_macros::only_role(caller, MINTER_ROLE)]
|
|
66
|
+
pub fn f9(x: u32, env: ::soroban_sdk::Env, caller: &soroban_sdk::Address) {
|
|
67
|
+
let _ = (x, env, caller);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
fn main() {}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#[test]
|
|
2
|
+
fn ui_rbac() {
|
|
3
|
+
// Important: set this before trybuild::TestCases::new() so each shard has
|
|
4
|
+
// its own trybuild project directory + lock + artifacts.
|
|
5
|
+
let target_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("target/trybuild-shards/ui_rbac");
|
|
6
|
+
std::env::set_var("CARGO_TARGET_DIR", target_dir.as_os_str());
|
|
7
|
+
|
|
8
|
+
let t = trybuild::TestCases::new();
|
|
9
|
+
t.pass("tests/ui/rbac/**/pass/*.rs");
|
|
10
|
+
t.compile_fail("tests/ui/rbac/**/fail/*.rs");
|
|
11
|
+
}
|
|
12
|
+
|
|
@@ -15,12 +15,12 @@
|
|
|
15
15
|
#[cfg(test)]
|
|
16
16
|
mod tests;
|
|
17
17
|
|
|
18
|
-
use common_macros::
|
|
18
|
+
use common_macros::contract_error;
|
|
19
19
|
use endpoint_v2::{
|
|
20
20
|
FeesAndPacket, IMessageLib, ISendLib, MessageLibType, MessageLibVersion, MessagingFee, OutboundPacket,
|
|
21
21
|
SetConfigParam,
|
|
22
22
|
};
|
|
23
|
-
use soroban_sdk::{contract, panic_with_error, Address, Bytes, Env, Vec};
|
|
23
|
+
use soroban_sdk::{contract, contractimpl, panic_with_error, Address, Bytes, Env, Vec};
|
|
24
24
|
|
|
25
25
|
#[contract_error]
|
|
26
26
|
pub enum BlockedMessageLibError {
|
|
@@ -31,7 +31,7 @@ pub enum BlockedMessageLibError {
|
|
|
31
31
|
#[contract]
|
|
32
32
|
pub struct BlockedMessageLib;
|
|
33
33
|
|
|
34
|
-
#[
|
|
34
|
+
#[contractimpl]
|
|
35
35
|
impl IMessageLib for BlockedMessageLib {
|
|
36
36
|
/// Always panics - config modification is not supported.
|
|
37
37
|
fn set_config(env: &Env, _oapp: &Address, _param: &Vec<SetConfigParam>) {
|
|
@@ -59,7 +59,7 @@ impl IMessageLib for BlockedMessageLib {
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
#[
|
|
62
|
+
#[contractimpl]
|
|
63
63
|
impl ISendLib for BlockedMessageLib {
|
|
64
64
|
/// Always panics - quoting is blocked.
|
|
65
65
|
fn quote(env: &Env, _packet: &OutboundPacket, _options: &Bytes, _pay_in_zro: bool) -> MessagingFee {
|
|
@@ -45,7 +45,7 @@ impl ISendLib for Uln302 {
|
|
|
45
45
|
|
|
46
46
|
// Treasury fee
|
|
47
47
|
let workers_fee = executor_fee + dvns_fee;
|
|
48
|
-
let (_, treasury_fee) = Self::quote_treasury(env, &packet.sender, packet.dst_eid,
|
|
48
|
+
let (_, treasury_fee) = Self::quote_treasury(env, &packet.sender, packet.dst_eid, workers_fee, pay_in_zro);
|
|
49
49
|
|
|
50
50
|
if pay_in_zro {
|
|
51
51
|
MessagingFee { native_fee: workers_fee, zro_fee: treasury_fee }
|
|
@@ -91,7 +91,7 @@ impl ISendLib for Uln302 {
|
|
|
91
91
|
// Treasury fee
|
|
92
92
|
let total_worker_fee = native_fee_recipients.iter().map(|fee| fee.amount).sum();
|
|
93
93
|
let (treasury_addr, treasury_fee) =
|
|
94
|
-
Self::quote_treasury(env, &packet.sender, packet.dst_eid,
|
|
94
|
+
Self::quote_treasury(env, &packet.sender, packet.dst_eid, total_worker_fee, pay_in_zro);
|
|
95
95
|
|
|
96
96
|
// Handle ZRO fee recipients
|
|
97
97
|
let mut zro_fee_recipients = vec![env];
|
|
@@ -280,12 +280,12 @@ impl Uln302 {
|
|
|
280
280
|
env: &Env,
|
|
281
281
|
sender: &Address,
|
|
282
282
|
dst_eid: u32,
|
|
283
|
-
workers_fee:
|
|
284
|
-
pay_in_zro:
|
|
283
|
+
workers_fee: i128,
|
|
284
|
+
pay_in_zro: bool,
|
|
285
285
|
) -> (Address, i128) {
|
|
286
286
|
let treasury_addr = Self::treasury(env);
|
|
287
287
|
let treasury_fee =
|
|
288
|
-
LayerZeroTreasuryClient::new(env, &treasury_addr).get_fee(sender, &dst_eid, workers_fee, pay_in_zro);
|
|
288
|
+
LayerZeroTreasuryClient::new(env, &treasury_addr).get_fee(sender, &dst_eid, &workers_fee, &pay_in_zro);
|
|
289
289
|
assert_with_error!(env, treasury_fee >= 0, Uln302Error::InvalidFee);
|
|
290
290
|
(treasury_addr, treasury_fee)
|
|
291
291
|
}
|
|
@@ -67,6 +67,12 @@ impl Counter {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
#[only_auth]
|
|
71
|
+
pub fn withdraw(env: &Env, to: &Address, amount: i128) {
|
|
72
|
+
let native_token = LayerZeroEndpointV2Client::new(env, &Self::endpoint(env)).native_token();
|
|
73
|
+
TokenClient::new(env, &native_token).transfer(&env.current_contract_address(), to, &amount);
|
|
74
|
+
}
|
|
75
|
+
|
|
70
76
|
// ============================================================================================
|
|
71
77
|
// View functions
|
|
72
78
|
// ============================================================================================
|
|
@@ -3,7 +3,7 @@ use crate::{
|
|
|
3
3
|
oapp_core::{endpoint_client, get_peer_or_panic, OAppCore},
|
|
4
4
|
};
|
|
5
5
|
use endpoint_v2::{MessagingFee, MessagingParams, MessagingReceipt};
|
|
6
|
-
use soroban_sdk::{token::TokenClient, Address, Bytes, Env};
|
|
6
|
+
use soroban_sdk::{contracttype, token::TokenClient, Address, Bytes, Env};
|
|
7
7
|
use utils::option_ext::OptionExt;
|
|
8
8
|
|
|
9
9
|
/// The version of the OAppSender implementation.
|
|
@@ -22,7 +22,8 @@ pub const SENDER_VERSION: u64 = 1;
|
|
|
22
22
|
/// - `Verified` — Caller asserts that `require_auth()` has already been called.
|
|
23
23
|
/// Use this to avoid a duplicate `require_auth()` node in the Soroban auth tree
|
|
24
24
|
/// (e.g., when the same address was already authorized as the message sender).
|
|
25
|
-
#[
|
|
25
|
+
#[contracttype]
|
|
26
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
26
27
|
pub enum FeePayer {
|
|
27
28
|
/// The fee payer has **not** been authorized yet.
|
|
28
29
|
/// `__lz_send` will call `fee_payer.require_auth()` before transferring fees.
|
|
@@ -122,6 +122,11 @@ pub trait OFTFee: OFTFeeInternal + Auth {
|
|
|
122
122
|
Self::__effective_fee_bps(env, dst_eid)
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
/// Returns true if the OFT has a fee rate greater than 0 for the specified destination
|
|
126
|
+
fn has_oft_fee(env: &soroban_sdk::Env, dst_eid: u32) -> bool {
|
|
127
|
+
Self::__effective_fee_bps(env, dst_eid) > 0
|
|
128
|
+
}
|
|
129
|
+
|
|
125
130
|
/// Returns the fee deposit address.
|
|
126
131
|
fn fee_deposit_address(env: &soroban_sdk::Env) -> Option<soroban_sdk::Address> {
|
|
127
132
|
Self::__fee_deposit_address(env)
|
|
@@ -8,7 +8,7 @@ use soroban_sdk::{contractclient, Address, Env};
|
|
|
8
8
|
/// for crediting; the OFT calls the token (SAC) directly for burn on debit.
|
|
9
9
|
#[contractclient(name = "MintableClient")]
|
|
10
10
|
pub trait Mintable {
|
|
11
|
-
/// Mints `amount` tokens to `to`. The `
|
|
11
|
+
/// Mints `amount` tokens to `to`. The `operator` address is the caller (e.g. OFT)
|
|
12
12
|
/// requesting the mint, for use by SAC wrappers that enforce authorization.
|
|
13
|
-
fn mint(env: &Env, to: &Address, amount: i128,
|
|
13
|
+
fn mint(env: &Env, to: &Address, amount: i128, operator: &Address);
|
|
14
14
|
}
|
|
@@ -80,21 +80,22 @@ impl OFTInternal for OFT {
|
|
|
80
80
|
/// Overrides default to add pausable check and fee calculation.
|
|
81
81
|
///
|
|
82
82
|
/// Dust handling (consistent with EVM):
|
|
83
|
-
/// - fee
|
|
84
|
-
/// - fee
|
|
83
|
+
/// - no fee: dust stays with sender (amount_sent_ld has dust removed)
|
|
84
|
+
/// - has fee: dust is absorbed into the charged fee (amount_sent_ld is the full amount)
|
|
85
85
|
fn __debit_view(env: &Env, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> (i128, i128) {
|
|
86
86
|
Self::__assert_not_paused(env);
|
|
87
87
|
|
|
88
88
|
let conversion_rate = Self::__decimal_conversion_rate(env);
|
|
89
|
-
let
|
|
89
|
+
let has_fee = Self::has_oft_fee(env, dst_eid);
|
|
90
90
|
|
|
91
|
-
let (amount_sent_ld, amount_received_ld) = if
|
|
91
|
+
let (amount_sent_ld, amount_received_ld) = if !has_fee {
|
|
92
92
|
// No fee: dust stays with sender (default OFT behavior)
|
|
93
93
|
let amount_sent_ld = oft_utils::remove_dust(amount_ld, conversion_rate);
|
|
94
94
|
(amount_sent_ld, amount_sent_ld)
|
|
95
95
|
} else {
|
|
96
96
|
// With fee: match EVM OFTFee behavior
|
|
97
97
|
// - sender pays full amount_ld (no dust removed), dust is absorbed into the charged fee
|
|
98
|
+
let fee = Self::__fee_view(env, dst_eid, amount_ld);
|
|
98
99
|
let amount_received_ld = oft_utils::remove_dust(amount_ld - fee, conversion_rate);
|
|
99
100
|
(amount_ld, amount_received_ld)
|
|
100
101
|
};
|
|
@@ -17,8 +17,8 @@ use utils::auth::Auth;
|
|
|
17
17
|
struct FeeTestContract;
|
|
18
18
|
|
|
19
19
|
impl Auth for FeeTestContract {
|
|
20
|
-
fn authorizer(env: &Env) -> Address {
|
|
21
|
-
env.current_contract_address()
|
|
20
|
+
fn authorizer(env: &Env) -> Option<Address> {
|
|
21
|
+
Some(env.current_contract_address())
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -12,8 +12,8 @@ use utils::auth::Auth;
|
|
|
12
12
|
struct PausableTestContract;
|
|
13
13
|
|
|
14
14
|
impl Auth for PausableTestContract {
|
|
15
|
-
fn authorizer(env: &Env) -> Address {
|
|
16
|
-
env.current_contract_address()
|
|
15
|
+
fn authorizer(env: &Env) -> Option<Address> {
|
|
16
|
+
Some(env.current_contract_address())
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -15,8 +15,8 @@ use utils::auth::Auth;
|
|
|
15
15
|
struct RateLimiterTestContract;
|
|
16
16
|
|
|
17
17
|
impl Auth for RateLimiterTestContract {
|
|
18
|
-
fn authorizer(env: &Env) -> Address {
|
|
19
|
-
env.current_contract_address()
|
|
18
|
+
fn authorizer(env: &Env) -> Option<Address> {
|
|
19
|
+
Some(env.current_contract_address())
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|