@layerzerolabs/protocol-stellar-v2 0.2.18 → 0.2.19
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 +275 -248
- package/.turbo/turbo-lint.log +52 -58
- package/.turbo/turbo-test.log +1224 -1358
- package/Cargo.lock +8 -5
- package/Cargo.toml +1 -1
- package/contracts/ERROR_SPEC.md +1 -1
- package/contracts/message-libs/uln-302/src/send_uln.rs +1 -1
- package/contracts/oapps/oapp/src/oapp_receiver.rs +1 -1
- package/contracts/oapps/oft/Cargo.toml +10 -7
- package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_oft_fee.rs +3 -4
- package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_pausable.rs +2 -3
- package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_rate_limiter.rs +1 -1
- package/contracts/oapps/oft/integration-tests/mod.rs +1 -1
- package/contracts/oapps/oft/integration-tests/setup.rs +28 -127
- package/contracts/oapps/oft/integration-tests/utils.rs +254 -21
- package/contracts/oapps/oft/src/extensions/oft_fee.rs +5 -6
- package/contracts/oapps/oft/src/lib.rs +10 -14
- package/contracts/oapps/oft/src/oft.rs +151 -189
- package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +9 -11
- package/contracts/oapps/oft/src/oft_types/mint_burn.rs +32 -12
- package/contracts/oapps/oft/src/oft_types/mod.rs +13 -0
- package/contracts/oapps/{oft-std → oft-core}/Cargo.toml +6 -4
- package/contracts/oapps/{oft-std → oft-core}/integration-tests/mod.rs +1 -1
- package/contracts/oapps/{oft-std → oft-core}/integration-tests/setup.rs +126 -29
- package/contracts/oapps/{oft → oft-core}/integration-tests/test_with_sml.rs +3 -3
- package/contracts/oapps/oft-core/integration-tests/utils.rs +201 -0
- package/contracts/oapps/oft-core/src/lib.rs +18 -0
- package/contracts/oapps/oft-core/src/oft_core.rs +439 -0
- package/contracts/oapps/{oft → oft-core}/src/tests/mod.rs +0 -2
- package/contracts/oapps/{oft → oft-core}/src/tests/test_lz_receive.rs +7 -7
- package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_msg_codec.rs +4 -4
- package/contracts/oapps/{oft → oft-core}/src/tests/test_resolve_address.rs +3 -3
- package/contracts/oapps/{oft → oft-core}/src/tests/test_utils.rs +44 -25
- package/contracts/oapps/{oft → oft-core}/src/utils.rs +1 -1
- package/contracts/utils/src/errors.rs +5 -1
- package/contracts/utils/src/ownable.rs +125 -3
- package/contracts/utils/src/tests/option_ext.rs +1 -1
- package/contracts/utils/src/tests/ownable.rs +445 -7
- package/contracts/utils/src/tests/ttl_configurable.rs +2 -2
- package/package.json +4 -5
- package/sdk/.turbo/turbo-test.log +216 -206
- package/sdk/dist/generated/bml.d.ts +30 -0
- package/sdk/dist/generated/bml.js +28 -5
- package/sdk/dist/generated/counter.d.ts +122 -2
- package/sdk/dist/generated/counter.js +36 -7
- package/sdk/dist/generated/dvn.d.ts +30 -0
- package/sdk/dist/generated/dvn.js +28 -5
- package/sdk/dist/generated/dvn_fee_lib.d.ts +122 -2
- package/sdk/dist/generated/dvn_fee_lib.js +36 -7
- package/sdk/dist/generated/endpoint.d.ts +122 -2
- package/sdk/dist/generated/endpoint.js +36 -7
- package/sdk/dist/generated/executor.d.ts +122 -2
- package/sdk/dist/generated/executor.js +36 -7
- package/sdk/dist/generated/executor_fee_lib.d.ts +122 -2
- package/sdk/dist/generated/executor_fee_lib.js +36 -7
- package/sdk/dist/generated/executor_helper.d.ts +30 -0
- package/sdk/dist/generated/executor_helper.js +28 -5
- package/sdk/dist/generated/oft.d.ts +1842 -0
- package/sdk/dist/generated/oft.js +345 -0
- package/sdk/dist/generated/price_feed.d.ts +122 -2
- package/sdk/dist/generated/price_feed.js +36 -7
- package/sdk/dist/generated/sml.d.ts +122 -2
- package/sdk/dist/generated/sml.js +36 -7
- package/sdk/dist/generated/treasury.d.ts +122 -2
- package/sdk/dist/generated/treasury.js +36 -7
- package/sdk/dist/generated/uln302.d.ts +122 -2
- package/sdk/dist/generated/uln302.js +36 -7
- package/sdk/dist/generated/upgrader.d.ts +15 -0
- package/sdk/dist/generated/upgrader.js +18 -0
- package/sdk/dist/index.d.ts +1 -2
- package/sdk/dist/index.js +1 -3
- package/sdk/package.json +3 -2
- package/sdk/src/index.ts +1 -4
- package/sdk/test/oft-sml.test.ts +16 -16
- package/sdk/turbo.json +8 -0
- package/tools/ts-bindings-gen/Cargo.toml +2 -0
- package/tools/ts-bindings-gen/src/main.rs +51 -4
- package/turbo.json +0 -2
- package/contracts/oapps/oft/src/interfaces/mint_burn_token.rs +0 -23
- package/contracts/oapps/oft/src/interfaces/mod.rs +0 -3
- package/contracts/oapps/oft/src/oft_impl.rs +0 -201
- package/contracts/oapps/oft/src/tests/extensions/mod.rs +0 -11
- package/contracts/oapps/oft/src/tests/extensions/setup.rs +0 -917
- package/contracts/oapps/oft/src/tests/extensions/test_oft_fee.rs +0 -751
- package/contracts/oapps/oft/src/tests/extensions/test_pausable.rs +0 -434
- package/contracts/oapps/oft/src/tests/extensions/test_rate_limiter.rs +0 -1080
- package/contracts/oapps/oft-std/integration-tests/utils.rs +0 -427
- package/contracts/oapps/oft-std/src/lib.rs +0 -16
- package/contracts/oapps/oft-std/src/oft.rs +0 -174
- package/sdk/dist/generated/oft_std.d.ts +0 -1722
- package/sdk/dist/generated/oft_std.js +0 -316
- package/sdk/dist/wasm/blocked-message-lib.d.ts +0 -1
- package/sdk/dist/wasm/blocked-message-lib.js +0 -2
- package/sdk/dist/wasm/counter.d.ts +0 -1
- package/sdk/dist/wasm/counter.js +0 -2
- package/sdk/dist/wasm/dvn-fee-lib.d.ts +0 -1
- package/sdk/dist/wasm/dvn-fee-lib.js +0 -2
- package/sdk/dist/wasm/dvn.d.ts +0 -1
- package/sdk/dist/wasm/dvn.js +0 -2
- package/sdk/dist/wasm/endpoint-v2.d.ts +0 -1
- package/sdk/dist/wasm/endpoint-v2.js +0 -2
- package/sdk/dist/wasm/executor-fee-lib.d.ts +0 -1
- package/sdk/dist/wasm/executor-fee-lib.js +0 -2
- package/sdk/dist/wasm/executor-helper.d.ts +0 -1
- package/sdk/dist/wasm/executor-helper.js +0 -2
- package/sdk/dist/wasm/executor.d.ts +0 -1
- package/sdk/dist/wasm/executor.js +0 -2
- package/sdk/dist/wasm/layerzero-views.d.ts +0 -1
- package/sdk/dist/wasm/layerzero-views.js +0 -2
- package/sdk/dist/wasm/oft-std.d.ts +0 -1
- package/sdk/dist/wasm/oft-std.js +0 -2
- package/sdk/dist/wasm/price-feed.d.ts +0 -1
- package/sdk/dist/wasm/price-feed.js +0 -2
- package/sdk/dist/wasm/simple-message-lib.d.ts +0 -1
- package/sdk/dist/wasm/simple-message-lib.js +0 -2
- package/sdk/dist/wasm/treasury.d.ts +0 -1
- package/sdk/dist/wasm/treasury.js +0 -2
- package/sdk/dist/wasm/uln302.d.ts +0 -1
- package/sdk/dist/wasm/uln302.js +0 -2
- package/sdk/dist/wasm/upgrader.d.ts +0 -1
- package/sdk/dist/wasm/upgrader.js +0 -2
- package/sdk/dist/wasm.d.ts +0 -15
- package/sdk/dist/wasm.js +0 -15
- /package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/mod.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/codec/mod.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/codec/oft_compose_msg_codec.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/codec/oft_msg_codec.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/errors.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/events.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/storage.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_decimals.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_compose_msg_codec.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_version.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_oft.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_send.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_send.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/tests/test_token.rs +0 -0
- /package/contracts/oapps/{oft → oft-core}/src/types.rs +0 -0
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
use crate::{
|
|
6
6
|
codec::oft_msg_codec::OFTMessage,
|
|
7
|
-
|
|
7
|
+
oft_core::OFTClient,
|
|
8
8
|
types::{OFTReceipt, SendParam},
|
|
9
9
|
};
|
|
10
10
|
use endpoint_v2::{LayerZeroReceiverClient, MessagingFee, MessagingParams, MessagingReceipt, Origin};
|
|
@@ -104,15 +104,21 @@ pub fn create_origin(src_eid: u32, sender: &BytesN<32>, nonce: u64) -> Origin {
|
|
|
104
104
|
// ==================== Test OFT Contracts ====================
|
|
105
105
|
|
|
106
106
|
mod test_mint_burn_oft {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
use crate::types::OFTReceipt;
|
|
107
|
+
use crate::{
|
|
108
|
+
self as oft_core,
|
|
109
|
+
oft_core::{initialize_oft, lz_receive, OFTCore, OFTInternal},
|
|
110
|
+
types::OFTReceipt,
|
|
111
|
+
};
|
|
113
112
|
use endpoint_v2::Origin;
|
|
114
113
|
use oapp::oapp_receiver::LzReceiveInternal;
|
|
115
|
-
use soroban_sdk::{contractimpl, Address, Bytes, BytesN, Env};
|
|
114
|
+
use soroban_sdk::{contractclient, contractimpl, Address, Bytes, BytesN, Env};
|
|
115
|
+
|
|
116
|
+
#[contractclient(name = "MintBurnTokenClient")]
|
|
117
|
+
#[allow(dead_code)]
|
|
118
|
+
trait MintBurnToken {
|
|
119
|
+
fn mint(env: Env, to: Address, amount: i128);
|
|
120
|
+
fn burn(env: Env, from: Address, amount: i128);
|
|
121
|
+
}
|
|
116
122
|
|
|
117
123
|
#[oapp_macros::oapp]
|
|
118
124
|
pub struct TestMintBurnOFT;
|
|
@@ -132,7 +138,7 @@ mod test_mint_burn_oft {
|
|
|
132
138
|
}
|
|
133
139
|
|
|
134
140
|
#[contractimpl(contracttrait)]
|
|
135
|
-
impl
|
|
141
|
+
impl OFTCore for TestMintBurnOFT {}
|
|
136
142
|
|
|
137
143
|
impl LzReceiveInternal for TestMintBurnOFT {
|
|
138
144
|
fn __lz_receive(
|
|
@@ -144,32 +150,36 @@ mod test_mint_burn_oft {
|
|
|
144
150
|
executor: &Address,
|
|
145
151
|
value: i128,
|
|
146
152
|
) {
|
|
147
|
-
|
|
153
|
+
lz_receive::<Self>(env, executor, origin, guid, message, extra_data, value)
|
|
148
154
|
}
|
|
149
155
|
}
|
|
150
156
|
|
|
151
157
|
impl OFTInternal for TestMintBurnOFT {
|
|
152
158
|
fn __debit(env: &Env, sender: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
|
|
153
|
-
|
|
159
|
+
// Inline mint_burn::debit implementation
|
|
160
|
+
let receipt = Self::__debit_view(env, amount_ld, min_amount_ld, dst_eid);
|
|
161
|
+
MintBurnTokenClient::new(env, &Self::token(env)).burn(sender, &receipt.amount_received_ld);
|
|
162
|
+
receipt
|
|
154
163
|
}
|
|
155
164
|
|
|
156
|
-
fn __credit(env: &Env, to: &Address, amount_ld: i128,
|
|
157
|
-
|
|
165
|
+
fn __credit(env: &Env, to: &Address, amount_ld: i128, _src_eid: u32) -> i128 {
|
|
166
|
+
// Inline mint_burn::credit implementation
|
|
167
|
+
MintBurnTokenClient::new(env, &Self::token(env)).mint(to, &amount_ld);
|
|
168
|
+
amount_ld
|
|
158
169
|
}
|
|
159
170
|
}
|
|
160
171
|
}
|
|
161
172
|
pub use test_mint_burn_oft::TestMintBurnOFT;
|
|
162
173
|
|
|
163
174
|
mod test_lock_unlock_oft {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
use crate::types::OFTReceipt;
|
|
175
|
+
use crate::{
|
|
176
|
+
self as oft_core,
|
|
177
|
+
oft_core::{initialize_oft, lz_receive, OFTCore, OFTInternal},
|
|
178
|
+
types::OFTReceipt,
|
|
179
|
+
};
|
|
170
180
|
use endpoint_v2::Origin;
|
|
171
181
|
use oapp::oapp_receiver::{LzReceiveInternal, OAppReceiver};
|
|
172
|
-
use soroban_sdk::{contractimpl, Address, Bytes, BytesN, Env};
|
|
182
|
+
use soroban_sdk::{contractimpl, token::TokenClient, Address, Bytes, BytesN, Env};
|
|
173
183
|
|
|
174
184
|
#[oapp_macros::oapp(custom = [receiver])]
|
|
175
185
|
pub struct TestLockUnlockOFT;
|
|
@@ -189,7 +199,7 @@ mod test_lock_unlock_oft {
|
|
|
189
199
|
}
|
|
190
200
|
|
|
191
201
|
#[contractimpl(contracttrait)]
|
|
192
|
-
impl
|
|
202
|
+
impl OFTCore for TestLockUnlockOFT {}
|
|
193
203
|
|
|
194
204
|
impl LzReceiveInternal for TestLockUnlockOFT {
|
|
195
205
|
fn __lz_receive(
|
|
@@ -201,7 +211,7 @@ mod test_lock_unlock_oft {
|
|
|
201
211
|
executor: &Address,
|
|
202
212
|
value: i128,
|
|
203
213
|
) {
|
|
204
|
-
|
|
214
|
+
lz_receive::<Self>(env, executor, origin, guid, message, extra_data, value)
|
|
205
215
|
}
|
|
206
216
|
}
|
|
207
217
|
|
|
@@ -211,11 +221,20 @@ mod test_lock_unlock_oft {
|
|
|
211
221
|
|
|
212
222
|
impl OFTInternal for TestLockUnlockOFT {
|
|
213
223
|
fn __debit(env: &Env, sender: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
|
|
214
|
-
|
|
224
|
+
// Inline lock_unlock::debit implementation
|
|
225
|
+
let receipt: OFTReceipt = Self::__debit_view(env, amount_ld, min_amount_ld, dst_eid);
|
|
226
|
+
TokenClient::new(env, &Self::token(env)).transfer(
|
|
227
|
+
sender,
|
|
228
|
+
env.current_contract_address(),
|
|
229
|
+
&receipt.amount_received_ld,
|
|
230
|
+
);
|
|
231
|
+
receipt
|
|
215
232
|
}
|
|
216
233
|
|
|
217
|
-
fn __credit(env: &Env, to: &Address, amount_ld: i128,
|
|
218
|
-
|
|
234
|
+
fn __credit(env: &Env, to: &Address, amount_ld: i128, _src_eid: u32) -> i128 {
|
|
235
|
+
// Inline lock_unlock::credit implementation
|
|
236
|
+
TokenClient::new(env, &Self::token(env)).transfer(&env.current_contract_address(), to, &amount_ld);
|
|
237
|
+
amount_ld
|
|
219
238
|
}
|
|
220
239
|
}
|
|
221
240
|
}
|
|
@@ -38,7 +38,7 @@ pub fn remove_dust(amount_ld: i128, conversion_rate: i128) -> i128 {
|
|
|
38
38
|
///
|
|
39
39
|
/// # Returns
|
|
40
40
|
/// A 32-byte payload (contract ID hash or Ed25519 public key)
|
|
41
|
-
pub fn
|
|
41
|
+
pub fn address_payload(address: &Address) -> BytesN<32> {
|
|
42
42
|
match address.to_payload().unwrap() {
|
|
43
43
|
AddressPayload::ContractIdHash(payload) => payload,
|
|
44
44
|
AddressPayload::AccountIdPublicKeyEd25519(payload) => payload,
|
|
@@ -27,8 +27,12 @@ pub enum TtlConfigurableError {
|
|
|
27
27
|
/// OwnableError: 1030-1039
|
|
28
28
|
#[contract_error]
|
|
29
29
|
pub enum OwnableError {
|
|
30
|
-
|
|
30
|
+
InvalidPendingOwner = 1030,
|
|
31
|
+
InvalidTtl,
|
|
32
|
+
NoPendingTransfer,
|
|
33
|
+
OwnerAlreadySet,
|
|
31
34
|
OwnerNotSet,
|
|
35
|
+
TransferInProgress,
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
/// BytesExtError: 1040-1049
|
|
@@ -6,7 +6,7 @@ use soroban_sdk::{assert_with_error, contractevent, Address, Env};
|
|
|
6
6
|
// Ownable events
|
|
7
7
|
// ===========================================================================
|
|
8
8
|
|
|
9
|
-
/// Event emitted when ownership is transferred.
|
|
9
|
+
/// Event emitted when ownership is transferred (both single-step and two-step completion).
|
|
10
10
|
#[contractevent]
|
|
11
11
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
12
12
|
pub struct OwnershipTransferred {
|
|
@@ -14,6 +14,15 @@ pub struct OwnershipTransferred {
|
|
|
14
14
|
pub new_owner: Address,
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/// Event emitted when a 2-step ownership transfer is proposed.
|
|
18
|
+
#[contractevent]
|
|
19
|
+
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
20
|
+
pub struct OwnershipTransferring {
|
|
21
|
+
pub old_owner: Address,
|
|
22
|
+
pub new_owner: Address,
|
|
23
|
+
pub ttl: u32,
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
/// Event emitted when ownership is renounced.
|
|
18
27
|
#[contractevent]
|
|
19
28
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
@@ -30,6 +39,10 @@ pub struct OwnershipRenounced {
|
|
|
30
39
|
pub enum OwnableStorage {
|
|
31
40
|
#[instance(Address)]
|
|
32
41
|
Owner,
|
|
42
|
+
/// Pending owner for 2-step transfer. Stored in temporary storage with TTL -
|
|
43
|
+
/// automatically expires if not accepted in time.
|
|
44
|
+
#[temporary(Address)]
|
|
45
|
+
PendingOwner,
|
|
33
46
|
}
|
|
34
47
|
|
|
35
48
|
// ===========================================================================
|
|
@@ -40,23 +53,126 @@ pub enum OwnableStorage {
|
|
|
40
53
|
///
|
|
41
54
|
/// Extends `Auth` to provide owner-based authorization. The `Auth::authorizer()`
|
|
42
55
|
/// implementation should return the owner address for Ownable contracts.
|
|
56
|
+
///
|
|
57
|
+
/// Supports both single-step and two-step ownership transfer:
|
|
58
|
+
/// - Single-step: `transfer_ownership` - Immediate transfer (use with caution)
|
|
59
|
+
/// - Two-step: `propose_ownership_transfer` + `accept_ownership` - Safer, requires new owner to accept
|
|
43
60
|
#[contract_trait]
|
|
44
61
|
pub trait Ownable: Sized + Auth {
|
|
62
|
+
// ===========================================================================
|
|
63
|
+
// View functions
|
|
64
|
+
// ===========================================================================
|
|
65
|
+
|
|
45
66
|
/// Returns the current owner address, or None if no owner is set.
|
|
46
67
|
fn owner(env: &Env) -> Option<Address> {
|
|
47
68
|
OwnableStorage::owner(env)
|
|
48
69
|
}
|
|
49
70
|
|
|
50
|
-
///
|
|
71
|
+
/// Returns the pending owner address for 2-step transfer, or None if no transfer is pending.
|
|
72
|
+
fn pending_owner(env: &Env) -> Option<Address> {
|
|
73
|
+
OwnableStorage::pending_owner(env)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ===========================================================================
|
|
77
|
+
// Single-step transfer (immediate)
|
|
78
|
+
// ===========================================================================
|
|
79
|
+
|
|
80
|
+
/// Transfers ownership immediately to a new address.
|
|
81
|
+
///
|
|
82
|
+
/// Use with caution - if you transfer to a wrong address, ownership is lost forever.
|
|
83
|
+
/// Consider using `propose_ownership_transfer` instead.
|
|
84
|
+
///
|
|
85
|
+
/// # Panics
|
|
86
|
+
/// - `OwnerNotSet` if no owner is currently set
|
|
87
|
+
/// - `TransferInProgress` if a 2-step transfer is in progress
|
|
51
88
|
fn transfer_ownership(env: &Env, new_owner: &Address) {
|
|
52
89
|
let old_owner = enforce_owner_auth::<Self>(env);
|
|
90
|
+
assert_no_pending_transfer::<Self>(env);
|
|
91
|
+
|
|
53
92
|
OwnableStorage::set_owner(env, new_owner);
|
|
54
93
|
OwnershipTransferred { old_owner, new_owner: new_owner.clone() }.publish(env);
|
|
55
94
|
}
|
|
56
95
|
|
|
57
|
-
|
|
96
|
+
// ===========================================================================
|
|
97
|
+
// Two-step transfer (safer)
|
|
98
|
+
// ===========================================================================
|
|
99
|
+
|
|
100
|
+
/// Proposes an ownership transfer to a new address.
|
|
101
|
+
///
|
|
102
|
+
/// The new owner must call `accept_ownership()` within `ttl` ledgers
|
|
103
|
+
/// to complete the transfer. The pending transfer will automatically expire after.
|
|
104
|
+
///
|
|
105
|
+
/// # Arguments
|
|
106
|
+
/// - `new_owner` - The proposed new owner
|
|
107
|
+
/// - `ttl` - Number of ledgers the new owner has to accept.
|
|
108
|
+
/// Use `0` to cancel a pending transfer (new_owner must match pending).
|
|
109
|
+
///
|
|
110
|
+
/// # Panics
|
|
111
|
+
/// - `OwnerNotSet` if no owner is currently set
|
|
112
|
+
/// - `NoPendingTransfer` when cancelling and no pending transfer exists
|
|
113
|
+
/// - `InvalidTtl` if ttl exceeds max TTL
|
|
114
|
+
/// - `InvalidPendingOwner` when cancelling with wrong new_owner address
|
|
115
|
+
fn propose_ownership_transfer(env: &Env, new_owner: &Address, ttl: u32) {
|
|
116
|
+
let old_owner = enforce_owner_auth::<Self>(env);
|
|
117
|
+
|
|
118
|
+
// Cancel case: ttl == 0
|
|
119
|
+
if ttl == 0 {
|
|
120
|
+
let pending = Self::pending_owner(env).unwrap_or_panic(env, OwnableError::NoPendingTransfer);
|
|
121
|
+
|
|
122
|
+
// Verify new_owner matches pending (prevents accidental cancellation)
|
|
123
|
+
assert_with_error!(env, pending == *new_owner, OwnableError::InvalidPendingOwner);
|
|
124
|
+
|
|
125
|
+
OwnableStorage::remove_pending_owner(env);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Initiate case: validate ttl
|
|
130
|
+
assert_with_error!(env, ttl <= env.storage().max_ttl(), OwnableError::InvalidTtl);
|
|
131
|
+
|
|
132
|
+
// Store pending owner with TTL
|
|
133
|
+
OwnableStorage::set_pending_owner(env, new_owner);
|
|
134
|
+
OwnableStorage::extend_pending_owner_ttl(env, ttl, ttl);
|
|
135
|
+
|
|
136
|
+
OwnershipTransferring { old_owner, new_owner: new_owner.clone(), ttl }.publish(env);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/// Accepts a pending 2-step ownership transfer.
|
|
140
|
+
///
|
|
141
|
+
/// Must be called by the pending owner before the TTL expires.
|
|
142
|
+
///
|
|
143
|
+
/// # Panics
|
|
144
|
+
/// - `NoPendingTransfer` if there is no pending transfer (or it expired)
|
|
145
|
+
fn accept_ownership(env: &Env) {
|
|
146
|
+
let new_owner = Self::pending_owner(env).unwrap_or_panic(env, OwnableError::NoPendingTransfer);
|
|
147
|
+
|
|
148
|
+
// Require authorization from the pending owner
|
|
149
|
+
new_owner.require_auth();
|
|
150
|
+
|
|
151
|
+
// Safe to unwrap: owner must exist if pending_owner exists because:
|
|
152
|
+
// 1. pending_owner can only be set via propose_ownership_transfer, which requires owner auth
|
|
153
|
+
// 2. renounce_ownership is blocked while a 2-step transfer is in progress
|
|
154
|
+
let old_owner = OwnableStorage::owner(env).unwrap();
|
|
155
|
+
|
|
156
|
+
// Transfer ownership
|
|
157
|
+
OwnableStorage::remove_pending_owner(env);
|
|
158
|
+
OwnableStorage::set_owner(env, &new_owner);
|
|
159
|
+
|
|
160
|
+
OwnershipTransferred { old_owner, new_owner }.publish(env);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ===========================================================================
|
|
164
|
+
// Renounce
|
|
165
|
+
// ===========================================================================
|
|
166
|
+
|
|
167
|
+
/// Permanently renounces ownership.
|
|
168
|
+
///
|
|
169
|
+
/// # Panics
|
|
170
|
+
/// - `OwnerNotSet` if no owner is currently set
|
|
171
|
+
/// - `TransferInProgress` if a 2-step transfer is in progress (cancel it first)
|
|
58
172
|
fn renounce_ownership(env: &Env) {
|
|
59
173
|
let old_owner = enforce_owner_auth::<Self>(env);
|
|
174
|
+
assert_no_pending_transfer::<Self>(env);
|
|
175
|
+
|
|
60
176
|
OwnableStorage::remove_owner(env);
|
|
61
177
|
OwnershipRenounced { old_owner }.publish(env);
|
|
62
178
|
}
|
|
@@ -86,3 +202,9 @@ pub fn enforce_owner_auth<T: Ownable>(env: &Env) -> Address {
|
|
|
86
202
|
pub fn require_owner_auth<T: Ownable>(env: &Env) {
|
|
87
203
|
let _ = enforce_owner_auth::<T>(env);
|
|
88
204
|
}
|
|
205
|
+
|
|
206
|
+
/// Asserts that no 2-step ownership transfer is in progress.
|
|
207
|
+
/// Panics with `TransferInProgress` if a pending transfer exists.
|
|
208
|
+
fn assert_no_pending_transfer<T: Ownable>(env: &Env) {
|
|
209
|
+
assert_with_error!(env, T::pending_owner(env).is_none(), OwnableError::TransferInProgress);
|
|
210
|
+
}
|
|
@@ -10,7 +10,7 @@ fn unwrap_or_panic_some_returns_value() {
|
|
|
10
10
|
|
|
11
11
|
#[test]
|
|
12
12
|
fn unwrap_or_panic_none_panics_with_error() {
|
|
13
|
-
const EXPECTED: &str = "Error(Contract, #
|
|
13
|
+
const EXPECTED: &str = "Error(Contract, #1034)"; // OwnerNotSet
|
|
14
14
|
assert_panics_contains("none unwrap_or_panic", EXPECTED, || {
|
|
15
15
|
let env = Env::default();
|
|
16
16
|
let _got: u32 = None::<u32>.unwrap_or_panic(&env, OwnableError::OwnerNotSet);
|