@layerzerolabs/protocol-stellar-v2 0.2.40 → 0.2.43
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 +312 -397
- package/.turbo/turbo-lint.log +185 -245
- package/.turbo/turbo-test.log +1846 -1942
- package/Cargo.lock +22 -127
- package/Cargo.toml +4 -6
- package/contracts/common-macros/src/lib.rs +38 -15
- package/contracts/common-macros/src/lz_contract.rs +12 -21
- package/contracts/common-macros/src/tests/lz_contract.rs +17 -8
- package/contracts/common-macros/src/tests/snapshots/common_macros__tests__lz_contract__snapshot_generated_lz_contract_code.snap +20 -0
- package/contracts/common-macros/src/upgradeable.rs +37 -30
- package/contracts/endpoint-v2/src/endpoint_v2.rs +4 -3
- package/contracts/endpoint-v2/src/errors.rs +2 -2
- package/contracts/endpoint-v2/src/messaging_channel.rs +11 -0
- package/contracts/endpoint-v2/src/messaging_composer.rs +1 -0
- package/contracts/endpoint-v2/src/tests/endpoint_v2/clear.rs +12 -25
- package/contracts/endpoint-v2/src/tests/endpoint_v2/initializable.rs +4 -4
- package/contracts/endpoint-v2/src/tests/endpoint_v2/verifiable.rs +50 -10
- package/contracts/endpoint-v2/src/tests/endpoint_v2/verify.rs +6 -35
- package/contracts/endpoint-v2/src/tests/messaging_channel/burn.rs +2 -2
- package/contracts/endpoint-v2/src/tests/messaging_channel/clear_payload.rs +50 -1
- package/contracts/endpoint-v2/src/tests/messaging_channel/inbound.rs +78 -0
- package/contracts/endpoint-v2/src/tests/messaging_channel/insert_and_drain_pending_nonces.rs +272 -0
- package/contracts/endpoint-v2/src/tests/messaging_channel/mod.rs +1 -0
- package/contracts/endpoint-v2/src/tests/messaging_channel/nilify.rs +10 -5
- package/contracts/endpoint-v2/src/tests/messaging_channel/skip.rs +30 -0
- package/contracts/macro-integration-tests/tests/runtime/oapp/mod.rs +25 -6
- package/contracts/macro-integration-tests/tests/runtime/oapp/oapp_core.rs +13 -11
- package/contracts/macro-integration-tests/tests/runtime/oapp/options_type3.rs +13 -10
- package/contracts/macro-integration-tests/tests/runtime/oapp/receiver.rs +15 -11
- package/contracts/macro-integration-tests/tests/runtime/oapp/sender.rs +5 -3
- package/contracts/macro-integration-tests/tests/runtime/ownable/mod.rs +1 -1
- package/contracts/macro-integration-tests/tests/runtime/ownable/two_step_transfer.rs +14 -12
- package/contracts/macro-integration-tests/tests/runtime/upgradeable/migrate_guard_and_state.rs +3 -9
- package/contracts/macro-integration-tests/tests/ui/lz_contract/fail/upgradeable_invalid_inner_option.stderr +24 -1
- package/contracts/macro-integration-tests/tests/ui/lz_contract/fail/upgradeable_missing_internal.stderr +3 -3
- package/contracts/macro-integration-tests/tests/ui/lz_contract/pass/upgradeable_rbac.rs +44 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/missing_auth_trait.rs +28 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/missing_auth_trait.stderr +397 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/missing_lz_receive_internal.rs +1 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/fail/missing_lz_receive_internal.stderr +10 -10
- package/contracts/macro-integration-tests/tests/ui/oapp/pass/custom_all.rs +4 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/pass/custom_single_trait.rs +7 -0
- package/contracts/macro-integration-tests/tests/ui/oapp/pass/minimal_contract.rs +5 -4
- package/contracts/macro-integration-tests/tests/ui/oapp/pass/struct_with_fields.rs +2 -0
- package/contracts/macro-integration-tests/tests/ui/ownable/pass/basic.rs +1 -1
- package/contracts/macro-integration-tests/tests/ui/upgradeable/fail/attr_args.stderr +1 -1
- package/contracts/macro-integration-tests/tests/ui/upgradeable/fail/missing_auth_trait.stderr +2 -2
- package/contracts/macro-integration-tests/tests/ui/upgradeable/fail/missing_upgradeable_internal.stderr +2 -2
- package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/rbac.rs +44 -0
- package/contracts/oapps/counter/integration_tests/utils.rs +5 -3
- package/contracts/oapps/counter/src/counter.rs +4 -3
- package/contracts/oapps/counter/src/tests/mod.rs +16 -1
- package/contracts/oapps/counter/src/tests/test_counter.rs +5 -2
- package/contracts/oapps/oapp/src/oapp_core.rs +22 -8
- package/contracts/oapps/oapp/src/oapp_options_type3.rs +7 -5
- package/contracts/oapps/oapp/src/tests/mod.rs +21 -0
- package/contracts/oapps/oapp/src/tests/oapp_core.rs +14 -11
- package/contracts/oapps/oapp/src/tests/oapp_options_type3.rs +17 -10
- package/contracts/oapps/oapp/src/tests/oapp_receiver.rs +6 -3
- package/contracts/oapps/oapp/src/tests/oapp_sender.rs +5 -3
- package/contracts/oapps/oapp/src/tests/test_macros.rs +25 -0
- package/contracts/oapps/oapp-macros/src/generators.rs +12 -9
- package/contracts/oapps/oapp-macros/src/lib.rs +1 -1
- package/contracts/oapps/oapp-macros/src/tests/snapshots/oapp_macros__tests__oapp__snapshot_generate_oapp.snap +15 -7
- package/contracts/oapps/oft/integration-tests/setup.rs +22 -4
- package/contracts/oapps/oft/integration-tests/utils.rs +94 -13
- package/contracts/oapps/oft/src/extensions/oft_fee.rs +23 -10
- package/contracts/oapps/oft/src/extensions/pausable.rs +31 -10
- package/contracts/oapps/oft/src/extensions/rate_limiter.rs +9 -4
- package/contracts/oapps/oft/src/oft.rs +3 -3
- package/contracts/oapps/oft/src/tests/extensions/oft_fee.rs +39 -27
- package/contracts/oapps/oft/src/tests/extensions/pausable.rs +38 -24
- package/contracts/oapps/oft/src/tests/extensions/rate_limiter.rs +87 -69
- package/contracts/oapps/oft/src/tests/oft_types/lock_unlock.rs +1 -0
- package/contracts/oapps/oft-core/integration-tests/setup.rs +28 -3
- package/contracts/oapps/oft-core/src/oft_core.rs +11 -6
- package/contracts/oapps/oft-core/src/tests/test_msg_inspector.rs +20 -20
- package/contracts/oapps/oft-core/src/tests/test_utils.rs +33 -3
- package/contracts/upgrader/src/lib.rs +67 -30
- package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract3.wasm +0 -0
- package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract4.wasm +0 -0
- package/contracts/upgrader/src/tests/test_upgrader.rs +50 -4
- package/contracts/utils/src/ownable.rs +16 -5
- package/contracts/utils/src/tests/ownable.rs +39 -39
- package/contracts/utils/src/upgradeable.rs +60 -17
- package/docs/oapp-guide.md +18 -13
- package/package.json +5 -5
- package/sdk/.turbo/turbo-test.log +359 -348
- package/sdk/dist/generated/bml.d.ts +4 -4
- package/sdk/dist/generated/bml.js +6 -6
- package/sdk/dist/generated/counter.d.ts +269 -123
- package/sdk/dist/generated/counter.js +45 -25
- package/sdk/dist/generated/dvn.d.ts +4 -6
- package/sdk/dist/generated/dvn.js +8 -8
- package/sdk/dist/generated/dvn_fee_lib.d.ts +8 -10
- package/sdk/dist/generated/dvn_fee_lib.js +8 -8
- package/sdk/dist/generated/endpoint.d.ts +9 -9
- package/sdk/dist/generated/endpoint.js +9 -9
- package/sdk/dist/generated/executor.d.ts +9 -11
- package/sdk/dist/generated/executor.js +11 -11
- package/sdk/dist/generated/executor_fee_lib.d.ts +9 -11
- package/sdk/dist/generated/executor_fee_lib.js +11 -11
- package/sdk/dist/generated/executor_helper.d.ts +4 -4
- package/sdk/dist/generated/executor_helper.js +6 -6
- package/sdk/dist/generated/layerzero_view.d.ts +9 -11
- package/sdk/dist/generated/layerzero_view.js +11 -11
- package/sdk/dist/generated/oft.d.ts +323 -156
- package/sdk/dist/generated/oft.js +65 -43
- package/sdk/dist/generated/price_feed.d.ts +8 -10
- package/sdk/dist/generated/price_feed.js +8 -8
- package/sdk/dist/generated/sac_manager.d.ts +8 -8
- package/sdk/dist/generated/sac_manager.js +6 -6
- package/sdk/dist/generated/sml.d.ts +9 -9
- package/sdk/dist/generated/sml.js +9 -9
- package/sdk/dist/generated/treasury.d.ts +9 -9
- package/sdk/dist/generated/treasury.js +9 -9
- package/sdk/dist/generated/uln302.d.ts +9 -9
- package/sdk/dist/generated/uln302.js +9 -9
- package/sdk/dist/generated/upgrader.d.ts +25 -16
- package/sdk/dist/generated/upgrader.js +5 -5
- package/sdk/package.json +1 -1
- package/sdk/test/counter-sml.test.ts +20 -0
- package/sdk/test/counter-uln.test.ts +20 -0
- package/sdk/test/oft-sml.test.ts +22 -0
- package/sdk/test/upgrader.test.ts +1 -0
- package/ts-bindings-gen.toml +67 -0
- package/turbo.json +1 -8
- package/tools/ts-bindings-gen/Cargo.toml +0 -16
- package/tools/ts-bindings-gen/src/main.rs +0 -214
|
@@ -60,17 +60,17 @@ fn test_set_msg_inspector() {
|
|
|
60
60
|
// Deploy a passing inspector
|
|
61
61
|
let inspector_address = env.register(PassingInspector, ());
|
|
62
62
|
|
|
63
|
-
// Owner sets the inspector
|
|
63
|
+
// Owner (with OAPP_ADMIN_ROLE) sets the inspector
|
|
64
64
|
env.mock_auths(&[MockAuth {
|
|
65
65
|
address: &setup.owner,
|
|
66
66
|
invoke: &MockAuthInvoke {
|
|
67
67
|
contract: &setup.oft.address,
|
|
68
68
|
fn_name: "set_msg_inspector",
|
|
69
|
-
args: (&Some(inspector_address.clone()),).into_val(&env),
|
|
69
|
+
args: (&Some(inspector_address.clone()), &setup.owner).into_val(&env),
|
|
70
70
|
sub_invokes: &[],
|
|
71
71
|
},
|
|
72
72
|
}]);
|
|
73
|
-
setup.oft.set_msg_inspector(&Some(inspector_address.clone()));
|
|
73
|
+
setup.oft.set_msg_inspector(&Some(inspector_address.clone()), &setup.owner);
|
|
74
74
|
|
|
75
75
|
// Verify inspector is set
|
|
76
76
|
let stored_inspector = setup.oft.msg_inspector();
|
|
@@ -90,11 +90,11 @@ fn test_remove_msg_inspector() {
|
|
|
90
90
|
invoke: &MockAuthInvoke {
|
|
91
91
|
contract: &setup.oft.address,
|
|
92
92
|
fn_name: "set_msg_inspector",
|
|
93
|
-
args: (&Some(inspector_address.clone()),).into_val(&env),
|
|
93
|
+
args: (&Some(inspector_address.clone()), &setup.owner).into_val(&env),
|
|
94
94
|
sub_invokes: &[],
|
|
95
95
|
},
|
|
96
96
|
}]);
|
|
97
|
-
setup.oft.set_msg_inspector(&Some(inspector_address));
|
|
97
|
+
setup.oft.set_msg_inspector(&Some(inspector_address), &setup.owner);
|
|
98
98
|
|
|
99
99
|
// Verify it's set
|
|
100
100
|
assert!(setup.oft.msg_inspector().is_some());
|
|
@@ -105,11 +105,11 @@ fn test_remove_msg_inspector() {
|
|
|
105
105
|
invoke: &MockAuthInvoke {
|
|
106
106
|
contract: &setup.oft.address,
|
|
107
107
|
fn_name: "set_msg_inspector",
|
|
108
|
-
args: (&None::<Address>,).into_val(&env),
|
|
108
|
+
args: (&None::<Address>, &setup.owner).into_val(&env),
|
|
109
109
|
sub_invokes: &[],
|
|
110
110
|
},
|
|
111
111
|
}]);
|
|
112
|
-
setup.oft.set_msg_inspector(&None);
|
|
112
|
+
setup.oft.set_msg_inspector(&None, &setup.owner);
|
|
113
113
|
|
|
114
114
|
// Verify inspector is removed
|
|
115
115
|
let stored_inspector = setup.oft.msg_inspector();
|
|
@@ -119,7 +119,7 @@ fn test_remove_msg_inspector() {
|
|
|
119
119
|
// ==================== Access Control Tests ====================
|
|
120
120
|
|
|
121
121
|
#[test]
|
|
122
|
-
#[should_panic(expected = "
|
|
122
|
+
#[should_panic(expected = "Error(Contract, #1086)")] // RbacError::Unauthorized
|
|
123
123
|
fn test_set_msg_inspector_requires_owner() {
|
|
124
124
|
let env = Env::default();
|
|
125
125
|
let setup = OFTTestSetup::new(&env);
|
|
@@ -127,20 +127,20 @@ fn test_set_msg_inspector_requires_owner() {
|
|
|
127
127
|
// Deploy a passing inspector
|
|
128
128
|
let inspector_address = env.register(PassingInspector, ());
|
|
129
129
|
|
|
130
|
-
// Non-owner tries to set the inspector
|
|
130
|
+
// Non-owner (without OAPP_ADMIN_ROLE) tries to set the inspector
|
|
131
131
|
let non_owner = Address::generate(&env);
|
|
132
132
|
env.mock_auths(&[MockAuth {
|
|
133
133
|
address: &non_owner,
|
|
134
134
|
invoke: &MockAuthInvoke {
|
|
135
135
|
contract: &setup.oft.address,
|
|
136
136
|
fn_name: "set_msg_inspector",
|
|
137
|
-
args: (&Some(inspector_address.clone()),).into_val(&env),
|
|
137
|
+
args: (&Some(inspector_address.clone()), &non_owner).into_val(&env),
|
|
138
138
|
sub_invokes: &[],
|
|
139
139
|
},
|
|
140
140
|
}]);
|
|
141
141
|
|
|
142
|
-
// This should panic because non_owner
|
|
143
|
-
setup.oft.set_msg_inspector(&Some(inspector_address));
|
|
142
|
+
// This should panic because non_owner does not have OAPP_ADMIN_ROLE
|
|
143
|
+
setup.oft.set_msg_inspector(&Some(inspector_address), &non_owner);
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
// ==================== Integration Tests with Send ====================
|
|
@@ -184,11 +184,11 @@ fn test_send_with_passing_inspector() {
|
|
|
184
184
|
invoke: &MockAuthInvoke {
|
|
185
185
|
contract: &setup.oft.address,
|
|
186
186
|
fn_name: "set_msg_inspector",
|
|
187
|
-
args: (&Some(inspector_address.clone()),).into_val(&env),
|
|
187
|
+
args: (&Some(inspector_address.clone()), &setup.owner).into_val(&env),
|
|
188
188
|
sub_invokes: &[],
|
|
189
189
|
},
|
|
190
190
|
}]);
|
|
191
|
-
setup.oft.set_msg_inspector(&Some(inspector_address));
|
|
191
|
+
setup.oft.set_msg_inspector(&Some(inspector_address), &setup.owner);
|
|
192
192
|
|
|
193
193
|
let sender = Address::generate(&env);
|
|
194
194
|
|
|
@@ -224,11 +224,11 @@ fn test_send_with_failing_inspector() {
|
|
|
224
224
|
invoke: &MockAuthInvoke {
|
|
225
225
|
contract: &setup.oft.address,
|
|
226
226
|
fn_name: "set_msg_inspector",
|
|
227
|
-
args: (&Some(inspector_address.clone()),).into_val(&env),
|
|
227
|
+
args: (&Some(inspector_address.clone()), &setup.owner).into_val(&env),
|
|
228
228
|
sub_invokes: &[],
|
|
229
229
|
},
|
|
230
230
|
}]);
|
|
231
|
-
setup.oft.set_msg_inspector(&Some(inspector_address));
|
|
231
|
+
setup.oft.set_msg_inspector(&Some(inspector_address), &setup.owner);
|
|
232
232
|
|
|
233
233
|
let sender = Address::generate(&env);
|
|
234
234
|
|
|
@@ -266,11 +266,11 @@ fn test_quote_send_with_passing_inspector() {
|
|
|
266
266
|
invoke: &MockAuthInvoke {
|
|
267
267
|
contract: &setup.oft.address,
|
|
268
268
|
fn_name: "set_msg_inspector",
|
|
269
|
-
args: (&Some(inspector_address.clone()),).into_val(&env),
|
|
269
|
+
args: (&Some(inspector_address.clone()), &setup.owner).into_val(&env),
|
|
270
270
|
sub_invokes: &[],
|
|
271
271
|
},
|
|
272
272
|
}]);
|
|
273
|
-
setup.oft.set_msg_inspector(&Some(inspector_address));
|
|
273
|
+
setup.oft.set_msg_inspector(&Some(inspector_address), &setup.owner);
|
|
274
274
|
|
|
275
275
|
let sender = Address::generate(&env);
|
|
276
276
|
|
|
@@ -300,11 +300,11 @@ fn test_quote_send_with_failing_inspector() {
|
|
|
300
300
|
invoke: &MockAuthInvoke {
|
|
301
301
|
contract: &setup.oft.address,
|
|
302
302
|
fn_name: "set_msg_inspector",
|
|
303
|
-
args: (&Some(inspector_address.clone()),).into_val(&env),
|
|
303
|
+
args: (&Some(inspector_address.clone()), &setup.owner).into_val(&env),
|
|
304
304
|
sub_invokes: &[],
|
|
305
305
|
},
|
|
306
306
|
}]);
|
|
307
|
-
setup.oft.set_msg_inspector(&Some(inspector_address));
|
|
307
|
+
setup.oft.set_msg_inspector(&Some(inspector_address), &setup.owner);
|
|
308
308
|
|
|
309
309
|
let sender = Address::generate(&env);
|
|
310
310
|
|
|
@@ -8,7 +8,7 @@ use crate::{
|
|
|
8
8
|
types::{OFTReceipt, SendParam},
|
|
9
9
|
};
|
|
10
10
|
use endpoint_v2::{LayerZeroReceiverClient, MessagingFee, MessagingParams, MessagingReceipt, Origin};
|
|
11
|
-
use oapp::oapp_core::OAppCoreClient;
|
|
11
|
+
use oapp::oapp_core::{OAppCoreClient, OAPP_ADMIN_ROLE};
|
|
12
12
|
use soroban_sdk::{
|
|
13
13
|
address_payload::AddressPayload,
|
|
14
14
|
bytes, contract, contractimpl, log, symbol_short,
|
|
@@ -16,6 +16,7 @@ use soroban_sdk::{
|
|
|
16
16
|
token::{StellarAssetClient, TokenClient},
|
|
17
17
|
Address, Bytes, BytesN, Env, IntoVal, String, Symbol,
|
|
18
18
|
};
|
|
19
|
+
use utils::rbac::grant_role_no_auth;
|
|
19
20
|
|
|
20
21
|
// ==================== Constants ====================
|
|
21
22
|
|
|
@@ -99,6 +100,20 @@ pub fn create_origin(src_eid: u32, sender: &BytesN<32>, nonce: u64) -> Origin {
|
|
|
99
100
|
Origin { src_eid, sender: sender.clone(), nonce }
|
|
100
101
|
}
|
|
101
102
|
|
|
103
|
+
fn grant_oapp_admin(env: &Env, contract: &Address, owner: &Address) {
|
|
104
|
+
let role = Symbol::new(env, oapp::oapp_core::OAPP_ADMIN_ROLE);
|
|
105
|
+
env.mock_auths(&[MockAuth {
|
|
106
|
+
address: owner,
|
|
107
|
+
invoke: &MockAuthInvoke {
|
|
108
|
+
contract,
|
|
109
|
+
fn_name: "grant_role",
|
|
110
|
+
args: (owner, &role, owner).into_val(env),
|
|
111
|
+
sub_invokes: &[],
|
|
112
|
+
},
|
|
113
|
+
}]);
|
|
114
|
+
utils::rbac::RoleBasedAccessControlClient::new(env, contract).grant_role(owner, &role, owner);
|
|
115
|
+
}
|
|
116
|
+
|
|
102
117
|
// ==================== Test OFT Contracts ====================
|
|
103
118
|
|
|
104
119
|
mod test_mint_burn_oft {
|
|
@@ -118,6 +133,7 @@ mod test_mint_burn_oft {
|
|
|
118
133
|
}
|
|
119
134
|
|
|
120
135
|
#[oapp_macros::oapp]
|
|
136
|
+
#[common_macros::lz_contract]
|
|
121
137
|
pub struct TestMintBurnOFT;
|
|
122
138
|
|
|
123
139
|
#[contractimpl]
|
|
@@ -178,6 +194,7 @@ mod test_lock_unlock_oft {
|
|
|
178
194
|
use soroban_sdk::{contractimpl, token::TokenClient, Address, Bytes, BytesN, Env};
|
|
179
195
|
|
|
180
196
|
#[oapp_macros::oapp(custom = [receiver])]
|
|
197
|
+
#[common_macros::lz_contract]
|
|
181
198
|
pub struct TestLockUnlockOFT;
|
|
182
199
|
|
|
183
200
|
#[contractimpl]
|
|
@@ -596,6 +613,16 @@ impl<'a> OFTTestSetupBuilder<'a> {
|
|
|
596
613
|
OFTTestSetup::mint_to(env, &owner, &native_token, &owner, INITIAL_MINT_AMOUNT);
|
|
597
614
|
OFTTestSetup::mint_to(env, &owner, &zro_token, &owner, INITIAL_MINT_AMOUNT);
|
|
598
615
|
|
|
616
|
+
// Grant OAPP_ADMIN_ROLE to owner so they can call set_peer, set_delegate, set_msg_inspector, etc.
|
|
617
|
+
env.as_contract(&oft_address, || {
|
|
618
|
+
grant_role_no_auth(
|
|
619
|
+
env,
|
|
620
|
+
&owner,
|
|
621
|
+
&Symbol::new(env, OAPP_ADMIN_ROLE),
|
|
622
|
+
&owner,
|
|
623
|
+
);
|
|
624
|
+
});
|
|
625
|
+
|
|
599
626
|
// Setup based on OFT type
|
|
600
627
|
match oft_type {
|
|
601
628
|
OFTType::MintBurn => {
|
|
@@ -649,16 +676,19 @@ impl<'a> OFTTestSetup<'a> {
|
|
|
649
676
|
}
|
|
650
677
|
|
|
651
678
|
pub fn set_peer(&self, eid: u32, peer: &BytesN<32>) {
|
|
679
|
+
grant_oapp_admin(self.env, &self.oft.address, &self.owner);
|
|
680
|
+
|
|
681
|
+
let peer_option = Some(peer.clone());
|
|
652
682
|
self.env.mock_auths(&[MockAuth {
|
|
653
683
|
address: &self.owner,
|
|
654
684
|
invoke: &MockAuthInvoke {
|
|
655
685
|
contract: &self.oft.address,
|
|
656
686
|
fn_name: "set_peer",
|
|
657
|
-
args: (&eid,
|
|
687
|
+
args: (&eid, &peer_option, &self.owner).into_val(self.env),
|
|
658
688
|
sub_invokes: &[],
|
|
659
689
|
},
|
|
660
690
|
}]);
|
|
661
|
-
OAppCoreClient::new(self.env, &self.oft.address).set_peer(&eid, &
|
|
691
|
+
OAppCoreClient::new(self.env, &self.oft.address).set_peer(&eid, &peer_option, &self.owner);
|
|
662
692
|
}
|
|
663
693
|
|
|
664
694
|
pub fn mint_to(env: &Env, owner: &Address, token: &Address, to: &Address, amount: i128) {
|
|
@@ -2,30 +2,47 @@
|
|
|
2
2
|
|
|
3
3
|
//! # Upgrader Contract
|
|
4
4
|
//!
|
|
5
|
-
//! A stateless utility contract for performing atomic upgrade
|
|
6
|
-
//! on contracts
|
|
5
|
+
//! A stateless utility contract for performing atomic upgrade-and-migrate operations
|
|
6
|
+
//! on contracts that implement [`Upgradeable`](utils::upgradeable::Upgradeable) (Auth-based)
|
|
7
|
+
//! or [`UpgradeableRbac`](utils::upgradeable::UpgradeableRbac) (RBAC-based).
|
|
7
8
|
//!
|
|
8
|
-
//! ## Security
|
|
9
|
+
//! ## Security model
|
|
9
10
|
//!
|
|
10
|
-
//! The Upgrader is permissionless
|
|
11
|
-
//!
|
|
12
|
-
//! ensures only its authorizer can
|
|
11
|
+
//! The Upgrader is permissionless: anyone may call it. Security is enforced by the target
|
|
12
|
+
//! contract’s authorization:
|
|
13
|
+
//! - **Auth-based**: the target’s `#[only_auth]` ensures only its authorizer can upgrade/migrate.
|
|
14
|
+
//! - **RBAC-based**: the target’s `#[only_role(operator, UPGRADER_ROLE)]` ensures only an
|
|
15
|
+
//! address with `UPGRADER_ROLE` can upgrade/migrate; that address must be passed as `operator`
|
|
16
|
+
//! and must have signed the transaction.
|
|
13
17
|
//!
|
|
14
18
|
//! ## Usage
|
|
15
19
|
//!
|
|
20
|
+
//! - For **Auth-based** targets, pass `operator: &None`. The transaction must be authorized
|
|
21
|
+
//! by the target contract’s authorizer.
|
|
22
|
+
//! - For **RBAC-based** targets, pass `operator: &Some(upgrader_address)`. The transaction
|
|
23
|
+
//! must be signed by that address, which must hold `UPGRADER_ROLE` on the target.
|
|
24
|
+
//!
|
|
16
25
|
//! ```ignore
|
|
17
26
|
//! let upgrader = UpgraderClient::new(&env, &upgrader_id);
|
|
18
27
|
//! let migration_data = my_data.to_xdr(&env);
|
|
19
|
-
//!
|
|
28
|
+
//! // Auth-based target:
|
|
29
|
+
//! upgrader.upgrade_and_migrate(&target_contract, &new_wasm_hash, &migration_data, &None);
|
|
30
|
+
//! // RBAC-based target:
|
|
31
|
+
//! upgrader.upgrade_and_migrate(&target_contract, &new_wasm_hash, &migration_data, &Some(operator));
|
|
20
32
|
//! ```
|
|
21
33
|
|
|
22
34
|
use soroban_sdk::{contract, contractimpl, xdr::ToXdr, Address, Bytes, BytesN, Env};
|
|
23
|
-
use utils::{
|
|
35
|
+
use utils::{
|
|
36
|
+
auth::AuthClient,
|
|
37
|
+
errors::AuthError,
|
|
38
|
+
option_ext::OptionExt,
|
|
39
|
+
upgradeable::{UpgradeableClient, UpgradeableRbacClient},
|
|
40
|
+
};
|
|
24
41
|
|
|
25
42
|
/// Upgrader contract for managing upgrades of other contracts.
|
|
26
43
|
///
|
|
27
|
-
///
|
|
28
|
-
///
|
|
44
|
+
/// Stateless utility: anyone may call it. Authorization is enforced by the target
|
|
45
|
+
/// contract (Auth or RBAC).
|
|
29
46
|
#[contract]
|
|
30
47
|
pub struct Upgrader;
|
|
31
48
|
|
|
@@ -33,39 +50,59 @@ pub struct Upgrader;
|
|
|
33
50
|
impl Upgrader {
|
|
34
51
|
/// Upgrades a target contract without custom migration data.
|
|
35
52
|
///
|
|
36
|
-
///
|
|
53
|
+
/// Convenience wrapper around [`upgrade_and_migrate`](Self::upgrade_and_migrate) that
|
|
54
|
+
/// passes empty migration data (XDR encoding of `()`). Use only when the target’s
|
|
55
|
+
/// `MigrationData` is `()` or it supports empty migration.
|
|
37
56
|
///
|
|
38
57
|
/// # Arguments
|
|
39
|
-
/// * `contract_address` -
|
|
40
|
-
/// * `wasm_hash` -
|
|
41
|
-
|
|
42
|
-
|
|
58
|
+
/// * `contract_address` - Address of the contract to upgrade.
|
|
59
|
+
/// * `wasm_hash` - Hash of the new WASM bytecode.
|
|
60
|
+
/// * `operator` - `None` for Auth-based targets; `Some(addr)` for RBAC-based targets
|
|
61
|
+
pub fn upgrade(env: &Env, contract_address: &Address, wasm_hash: &BytesN<32>, operator: &Option<Address>) {
|
|
62
|
+
Self::upgrade_and_migrate(env, contract_address, wasm_hash, &().to_xdr(env), operator);
|
|
43
63
|
}
|
|
44
64
|
|
|
45
65
|
/// Upgrades a target contract and runs its migration in a single transaction.
|
|
46
66
|
///
|
|
47
|
-
///
|
|
48
|
-
///
|
|
67
|
+
/// Chooses Auth-based or RBAC-based flow from `operator`:
|
|
68
|
+
/// - **`Some(operator)`**: RBAC flow. `operator` must sign the transaction and must have
|
|
69
|
+
/// `UPGRADER_ROLE` on the target. The target must implement [`UpgradeableRbac`](utils::upgradeable::UpgradeableRbac).
|
|
70
|
+
/// - **`None`**: Auth flow. The target’s authorizer must sign the transaction. The target
|
|
71
|
+
/// must implement [`Upgradeable`](utils::upgradeable::Upgradeable).
|
|
49
72
|
///
|
|
50
73
|
/// # Arguments
|
|
51
|
-
/// * `contract_address` -
|
|
52
|
-
/// * `wasm_hash` -
|
|
53
|
-
/// * `migration_data` - XDR-encoded
|
|
54
|
-
///
|
|
74
|
+
/// * `contract_address` - Address of the contract to upgrade.
|
|
75
|
+
/// * `wasm_hash` - Hash of the new WASM bytecode.
|
|
76
|
+
/// * `migration_data` - XDR-encoded migration payload. Use `value.to_xdr(&env)` for the
|
|
77
|
+
/// target contract’s `MigrationData` type; use `().to_xdr(&env)` for no custom data.
|
|
78
|
+
/// * `operator` - `None` for Auth-based target; `Some(operator)` for RBAC-based target.
|
|
55
79
|
///
|
|
56
80
|
/// # Example
|
|
57
81
|
/// ```ignore
|
|
58
82
|
/// let migration_data = my_data.to_xdr(&env);
|
|
59
|
-
/// upgrader.upgrade_and_migrate(&contract_addr, &wasm_hash, &migration_data);
|
|
83
|
+
/// upgrader.upgrade_and_migrate(&contract_addr, &wasm_hash, &migration_data, &None);
|
|
60
84
|
/// ```
|
|
61
|
-
pub fn upgrade_and_migrate(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
85
|
+
pub fn upgrade_and_migrate(
|
|
86
|
+
env: &Env,
|
|
87
|
+
contract_address: &Address,
|
|
88
|
+
wasm_hash: &BytesN<32>,
|
|
89
|
+
migration_data: &Bytes,
|
|
90
|
+
operator: &Option<Address>,
|
|
91
|
+
) {
|
|
92
|
+
if let Some(operator) = operator {
|
|
93
|
+
operator.require_auth();
|
|
94
|
+
let client = UpgradeableRbacClient::new(env, contract_address);
|
|
95
|
+
client.upgrade(wasm_hash, operator);
|
|
96
|
+
client.migrate(migration_data, operator);
|
|
97
|
+
} else {
|
|
98
|
+
AuthClient::new(env, contract_address)
|
|
99
|
+
.authorizer()
|
|
100
|
+
.unwrap_or_panic(env, AuthError::AuthorizerNotFound)
|
|
101
|
+
.require_auth();
|
|
102
|
+
let client = UpgradeableClient::new(env, contract_address);
|
|
103
|
+
client.upgrade(wasm_hash);
|
|
104
|
+
client.migrate(migration_data);
|
|
105
|
+
}
|
|
69
106
|
}
|
|
70
107
|
}
|
|
71
108
|
|
|
@@ -16,6 +16,19 @@ trait TestUpgradeableContract2 {
|
|
|
16
16
|
fn counter2(env: &Env) -> u32;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
#[allow(dead_code)]
|
|
20
|
+
#[contractclient(name = "TestRbacUpgradeableContractClient")]
|
|
21
|
+
trait TestRbacUpgradeableContract {
|
|
22
|
+
fn counter(env: &Env) -> u32;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#[allow(dead_code)]
|
|
26
|
+
#[contractclient(name = "TestRbacUpgradeableContractClient2")]
|
|
27
|
+
trait TestRbacUpgradeableContract2 {
|
|
28
|
+
fn counter(env: &Env) -> u32;
|
|
29
|
+
fn counter2(env: &Env) -> u32;
|
|
30
|
+
}
|
|
31
|
+
|
|
19
32
|
mod contract_v1 {
|
|
20
33
|
//#![no_std]
|
|
21
34
|
|
|
@@ -100,6 +113,13 @@ mod contract_v2 {
|
|
|
100
113
|
soroban_sdk::contractimport!(file = "./src/tests/test_data/test_upgradeable_contract2.wasm");
|
|
101
114
|
}
|
|
102
115
|
|
|
116
|
+
mod contract_v3 {
|
|
117
|
+
soroban_sdk::contractimport!(file = "./src/tests/test_data/test_upgradeable_contract3.wasm");
|
|
118
|
+
}
|
|
119
|
+
mod contract_v4 {
|
|
120
|
+
soroban_sdk::contractimport!(file = "./src/tests/test_data/test_upgradeable_contract4.wasm");
|
|
121
|
+
}
|
|
122
|
+
|
|
103
123
|
fn install_new_wasm(e: &Env) -> BytesN<32> {
|
|
104
124
|
e.deployer().upload_contract_wasm(contract_v2::WASM)
|
|
105
125
|
}
|
|
@@ -107,7 +127,7 @@ fn install_new_wasm(e: &Env) -> BytesN<32> {
|
|
|
107
127
|
#[test]
|
|
108
128
|
fn test_upgrade_with_upgrader() {
|
|
109
129
|
let e = Env::default();
|
|
110
|
-
e.
|
|
130
|
+
e.mock_all_auths();
|
|
111
131
|
|
|
112
132
|
let owner = Address::generate(&e);
|
|
113
133
|
let contract_id = e.register(contract_v1::WASM, (&owner,));
|
|
@@ -121,7 +141,7 @@ fn test_upgrade_with_upgrader() {
|
|
|
121
141
|
let counter_value = 2_u32;
|
|
122
142
|
// Encode migration data as XDR bytes
|
|
123
143
|
let migration_data = counter_value.to_xdr(&e);
|
|
124
|
-
upgrader_client.upgrade_and_migrate(&contract_id, &new_wasm_hash, &migration_data);
|
|
144
|
+
upgrader_client.upgrade_and_migrate(&contract_id, &new_wasm_hash, &migration_data, &None);
|
|
125
145
|
|
|
126
146
|
let client_v2 = TestUpgradeableContractClient2::new(&e, &contract_id);
|
|
127
147
|
|
|
@@ -131,7 +151,7 @@ fn test_upgrade_with_upgrader() {
|
|
|
131
151
|
#[test]
|
|
132
152
|
fn test_upgrade_without_migration_data_returns_error_for_non_unit_migration() {
|
|
133
153
|
let e = Env::default();
|
|
134
|
-
e.
|
|
154
|
+
e.mock_all_auths();
|
|
135
155
|
|
|
136
156
|
let owner = Address::generate(&e);
|
|
137
157
|
let contract_id = e.register(contract_v1::WASM, (&owner,));
|
|
@@ -142,6 +162,32 @@ fn test_upgrade_without_migration_data_returns_error_for_non_unit_migration() {
|
|
|
142
162
|
let new_wasm_hash = install_new_wasm(&e);
|
|
143
163
|
// The upgradeable WASM fixture requires non-unit migration data (u32).
|
|
144
164
|
// `Upgrader::upgrade` always passes empty `()` migration bytes, so this must fail.
|
|
145
|
-
let res = upgrader_client.try_upgrade(&contract_id, &new_wasm_hash);
|
|
165
|
+
let res = upgrader_client.try_upgrade(&contract_id, &new_wasm_hash, &None);
|
|
146
166
|
assert_eq!(res.err().unwrap().unwrap(), utils::errors::UpgradeableError::InvalidMigrationData.into());
|
|
147
167
|
}
|
|
168
|
+
|
|
169
|
+
#[test]
|
|
170
|
+
fn test_upgrade_with_upgrader_rbac() {
|
|
171
|
+
let e = Env::default();
|
|
172
|
+
e.mock_all_auths();
|
|
173
|
+
|
|
174
|
+
let owner = Address::generate(&e);
|
|
175
|
+
let operator = Address::generate(&e);
|
|
176
|
+
// RBAC fixture constructor: (owner, upgrader_operator)
|
|
177
|
+
let contract_id = e.register(contract_v3::WASM, (&owner, &operator));
|
|
178
|
+
let client_v3 = TestRbacUpgradeableContractClient::new(&e, &contract_id);
|
|
179
|
+
assert_eq!(client_v3.counter(), 1);
|
|
180
|
+
|
|
181
|
+
let upgrader = e.register(Upgrader, ());
|
|
182
|
+
let upgrader_client = UpgraderClient::new(&e, &upgrader);
|
|
183
|
+
|
|
184
|
+
let new_wasm_hash = e.deployer().upload_contract_wasm(contract_v4::WASM);
|
|
185
|
+
let counter_value = 42_u32;
|
|
186
|
+
let migration_data = counter_value.to_xdr(&e);
|
|
187
|
+
// Use RBAC path: pass Some(operator) so the upgrader uses UpgradeableRbac and operator must have signed
|
|
188
|
+
upgrader_client.upgrade_and_migrate(&contract_id, &new_wasm_hash, &migration_data, &Some(operator));
|
|
189
|
+
|
|
190
|
+
let client_v4 = TestRbacUpgradeableContractClient2::new(&e, &contract_id);
|
|
191
|
+
assert_eq!(client_v4.counter(), 1);
|
|
192
|
+
assert_eq!(client_v4.counter2(), counter_value);
|
|
193
|
+
}
|
|
@@ -64,7 +64,7 @@ pub enum OwnableStorage {
|
|
|
64
64
|
///
|
|
65
65
|
/// Supports both single-step and two-step ownership transfer:
|
|
66
66
|
/// - Single-step: `transfer_ownership` - Immediate transfer (use with caution)
|
|
67
|
-
/// - Two-step: `
|
|
67
|
+
/// - Two-step: `begin_ownership_transfer` + `accept_ownership` - Safer, requires new owner to accept
|
|
68
68
|
#[contract_trait]
|
|
69
69
|
pub trait Ownable: Auth {
|
|
70
70
|
// ===========================================================================
|
|
@@ -88,7 +88,7 @@ pub trait Ownable: Auth {
|
|
|
88
88
|
/// Transfers ownership immediately to a new address.
|
|
89
89
|
///
|
|
90
90
|
/// Use with caution - if you transfer to a wrong address, ownership is lost forever.
|
|
91
|
-
/// Consider using `
|
|
91
|
+
/// Consider using `begin_ownership_transfer` instead.
|
|
92
92
|
///
|
|
93
93
|
/// # Panics
|
|
94
94
|
/// - `OwnerNotSet` if no owner is currently set
|
|
@@ -105,7 +105,7 @@ pub trait Ownable: Auth {
|
|
|
105
105
|
// Two-step transfer (safer)
|
|
106
106
|
// ===========================================================================
|
|
107
107
|
|
|
108
|
-
///
|
|
108
|
+
/// Begins an ownership transfer to a new address.
|
|
109
109
|
///
|
|
110
110
|
/// The new owner must call `accept_ownership()` within `ttl` ledgers
|
|
111
111
|
/// to complete the transfer. The pending transfer will automatically expire after.
|
|
@@ -120,7 +120,7 @@ pub trait Ownable: Auth {
|
|
|
120
120
|
/// - `NoPendingTransfer` when cancelling and no pending transfer exists
|
|
121
121
|
/// - `InvalidTtl` if ttl exceeds max TTL
|
|
122
122
|
/// - `InvalidPendingOwner` when cancelling with wrong new_owner address
|
|
123
|
-
fn
|
|
123
|
+
fn begin_ownership_transfer(env: &soroban_sdk::Env, new_owner: &soroban_sdk::Address, ttl: u32) {
|
|
124
124
|
let old_owner = enforce_owner_auth::<Self>(env);
|
|
125
125
|
|
|
126
126
|
// Cancel case: ttl == 0
|
|
@@ -158,7 +158,7 @@ pub trait Ownable: Auth {
|
|
|
158
158
|
new_owner.require_auth();
|
|
159
159
|
|
|
160
160
|
// Safe to unwrap: owner must exist if pending_owner exists because:
|
|
161
|
-
// 1. pending_owner can only be set via
|
|
161
|
+
// 1. pending_owner can only be set via begin_ownership_transfer, which requires owner auth
|
|
162
162
|
// 2. renounce_ownership is blocked while a 2-step transfer is in progress
|
|
163
163
|
let old_owner = OwnableStorage::owner(env).unwrap();
|
|
164
164
|
|
|
@@ -189,6 +189,17 @@ pub trait Ownable: Auth {
|
|
|
189
189
|
|
|
190
190
|
/// Trait for initializing the owner of the contract.
|
|
191
191
|
pub trait OwnableInitializer {
|
|
192
|
+
/// Initializes the owner of the contract.
|
|
193
|
+
///
|
|
194
|
+
/// # Critical: constructor-only, never expose as a public entrypoint
|
|
195
|
+
///
|
|
196
|
+
/// `init_owner` must **ONLY** be called from the contract constructor. Do not expose it
|
|
197
|
+
/// as a public function under the assumption that it will "simply fail" after initialization.
|
|
198
|
+
///
|
|
199
|
+
/// After `renounce_ownership`, the owner is removed and `has_owner` returns false. If
|
|
200
|
+
/// `init_owner` were exposed publicly, anyone could call it post-renounce and become the
|
|
201
|
+
/// new owner, effectively undoing the renunciation. Always keep this logic internal to
|
|
202
|
+
/// the constructor.
|
|
192
203
|
fn init_owner(env: &Env, owner: &Address) {
|
|
193
204
|
assert_with_error!(env, !OwnableStorage::has_owner(env), OwnableError::OwnerAlreadySet);
|
|
194
205
|
OwnableStorage::set_owner(env, owner);
|