@layerzerolabs/protocol-stellar-v2 0.2.18 → 0.2.20

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.
Files changed (213) hide show
  1. package/.turbo/turbo-build.log +303 -253
  2. package/.turbo/turbo-lint.log +66 -65
  3. package/.turbo/turbo-test.log +1312 -1282
  4. package/Cargo.lock +21 -8
  5. package/Cargo.toml +2 -0
  6. package/contracts/ERROR_SPEC.md +9 -2
  7. package/contracts/common-macros/src/contract_ttl.rs +18 -7
  8. package/contracts/common-macros/src/lib.rs +4 -4
  9. package/contracts/common-macros/src/tests/contract_ttl.rs +1 -1
  10. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__contract_ttl__snapshot_generated_contractimpl_code.snap +2 -1
  11. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +7 -12
  12. package/contracts/common-macros/src/upgradeable.rs +15 -21
  13. package/contracts/message-libs/uln-302/src/events.rs +4 -0
  14. package/contracts/message-libs/uln-302/src/send_uln.rs +23 -7
  15. package/contracts/message-libs/uln-302/src/tests/send_uln302/send.rs +38 -64
  16. package/contracts/oapps/counter/Cargo.toml +1 -0
  17. package/contracts/oapps/counter/integration_tests/setup_uln.rs +1 -1
  18. package/contracts/oapps/oapp/src/oapp_receiver.rs +1 -1
  19. package/contracts/oapps/oapp/src/tests/test_oapp_core.rs +113 -65
  20. package/contracts/oapps/oapp/src/tests/test_oapp_options_type3.rs +111 -82
  21. package/contracts/oapps/oapp/src/tests/test_oapp_receiver.rs +293 -65
  22. package/contracts/oapps/oapp/src/tests/test_oapp_sender.rs +331 -56
  23. package/contracts/oapps/oft/Cargo.toml +10 -7
  24. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_oft_fee.rs +3 -4
  25. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_pausable.rs +2 -3
  26. package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/test_rate_limiter.rs +1 -1
  27. package/contracts/oapps/oft/integration-tests/mod.rs +1 -1
  28. package/contracts/oapps/oft/integration-tests/setup.rs +28 -127
  29. package/contracts/oapps/oft/integration-tests/utils.rs +254 -21
  30. package/contracts/oapps/oft/src/extensions/oft_fee.rs +23 -8
  31. package/contracts/oapps/oft/src/extensions/pausable.rs +19 -4
  32. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +52 -28
  33. package/contracts/oapps/oft/src/lib.rs +10 -14
  34. package/contracts/oapps/oft/src/oft.rs +143 -193
  35. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +9 -11
  36. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +12 -14
  37. package/contracts/oapps/oft/src/oft_types/mod.rs +13 -0
  38. package/contracts/oapps/{oft-std → oft-core}/Cargo.toml +6 -4
  39. package/contracts/oapps/{oft-std → oft-core}/integration-tests/mod.rs +1 -1
  40. package/contracts/oapps/{oft-std → oft-core}/integration-tests/setup.rs +126 -29
  41. package/contracts/oapps/{oft → oft-core}/integration-tests/test_with_sml.rs +3 -3
  42. package/contracts/oapps/oft-core/integration-tests/utils.rs +201 -0
  43. package/contracts/oapps/oft-core/src/lib.rs +18 -0
  44. package/contracts/oapps/oft-core/src/oft_core.rs +479 -0
  45. package/contracts/oapps/{oft → oft-core}/src/tests/mod.rs +0 -2
  46. package/contracts/oapps/{oft → oft-core}/src/tests/test_lz_receive.rs +7 -7
  47. package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_msg_codec.rs +4 -4
  48. package/contracts/oapps/{oft → oft-core}/src/tests/test_resolve_address.rs +3 -3
  49. package/contracts/oapps/{oft → oft-core}/src/tests/test_utils.rs +46 -27
  50. package/contracts/oapps/{oft → oft-core}/src/utils.rs +1 -1
  51. package/contracts/upgrader/src/lib.rs +30 -57
  52. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract1.wasm +0 -0
  53. package/contracts/upgrader/src/tests/test_data/test_upgradeable_contract2.wasm +0 -0
  54. package/contracts/upgrader/src/tests/test_upgrader.rs +44 -35
  55. package/contracts/utils/src/buffer_reader.rs +1 -0
  56. package/contracts/utils/src/errors.rs +8 -2
  57. package/contracts/utils/src/ownable.rs +125 -3
  58. package/contracts/utils/src/tests/option_ext.rs +1 -1
  59. package/contracts/utils/src/tests/ownable.rs +445 -7
  60. package/contracts/utils/src/tests/ttl_configurable.rs +2 -2
  61. package/contracts/utils/src/tests/upgradeable.rs +372 -175
  62. package/contracts/utils/src/ttl_configurable.rs +3 -3
  63. package/contracts/utils/src/upgradeable.rs +48 -23
  64. package/contracts/workers/dvn/Cargo.toml +1 -0
  65. package/contracts/workers/dvn/src/auth.rs +12 -42
  66. package/contracts/workers/dvn/src/dvn.rs +16 -31
  67. package/contracts/workers/dvn/src/errors.rs +0 -1
  68. package/contracts/workers/dvn/src/interfaces/dvn.rs +35 -0
  69. package/contracts/workers/dvn/src/lib.rs +4 -3
  70. package/contracts/workers/dvn/src/tests/auth.rs +1 -1
  71. package/contracts/workers/dvn/src/tests/dvn.rs +19 -15
  72. package/contracts/workers/dvn/src/tests/multisig/set_threshold.rs +2 -4
  73. package/contracts/workers/dvn/src/tests/multisig/verify_signatures.rs +1 -3
  74. package/contracts/workers/dvn/src/tests/setup.rs +5 -9
  75. package/contracts/workers/dvn-fee-lib/Cargo.toml +1 -1
  76. package/contracts/workers/dvn-fee-lib/src/dvn_fee_lib.rs +3 -5
  77. package/contracts/workers/dvn-fee-lib/src/tests/dvn_fee_lib.rs +2 -3
  78. package/contracts/workers/executor/Cargo.toml +1 -0
  79. package/contracts/workers/executor/src/executor.rs +15 -26
  80. package/contracts/workers/executor-fee-lib/Cargo.toml +2 -1
  81. package/contracts/workers/executor-fee-lib/src/executor_fee_lib.rs +63 -5
  82. package/contracts/workers/executor-fee-lib/src/executor_option.rs +28 -1
  83. package/contracts/workers/executor-fee-lib/src/lib.rs +3 -0
  84. package/contracts/workers/executor-fee-lib/src/tests/executor_fee_lib.rs +701 -0
  85. package/contracts/workers/executor-fee-lib/src/tests/executor_option.rs +370 -0
  86. package/contracts/workers/executor-fee-lib/src/tests/mod.rs +4 -0
  87. package/contracts/workers/executor-fee-lib/src/tests/setup.rs +60 -0
  88. package/contracts/workers/executor-helper/src/lib.rs +3 -0
  89. package/contracts/workers/executor-helper/src/tests/executor_helper.rs +184 -0
  90. package/contracts/workers/executor-helper/src/tests/mod.rs +2 -0
  91. package/contracts/workers/executor-helper/src/tests/setup.rs +366 -0
  92. package/contracts/workers/fee-lib-interfaces/Cargo.toml +14 -0
  93. package/contracts/workers/{worker/src/interfaces/mod.rs → fee-lib-interfaces/src/lib.rs} +4 -3
  94. package/contracts/workers/price-feed/Cargo.toml +2 -1
  95. package/contracts/workers/price-feed/src/events.rs +1 -1
  96. package/contracts/workers/price-feed/src/lib.rs +3 -0
  97. package/contracts/workers/price-feed/src/price_feed.rs +6 -12
  98. package/contracts/workers/price-feed/src/storage.rs +1 -1
  99. package/contracts/workers/price-feed/src/tests/mod.rs +2 -0
  100. package/contracts/workers/price-feed/src/tests/price_feed.rs +869 -0
  101. package/contracts/workers/price-feed/src/tests/setup.rs +70 -0
  102. package/contracts/workers/price-feed/src/types.rs +1 -1
  103. package/contracts/workers/worker/src/errors.rs +0 -3
  104. package/contracts/workers/worker/src/lib.rs +0 -2
  105. package/contracts/workers/worker/src/storage.rs +32 -29
  106. package/contracts/workers/worker/src/tests/setup.rs +1 -7
  107. package/contracts/workers/worker/src/tests/worker.rs +50 -42
  108. package/contracts/workers/worker/src/worker.rs +49 -58
  109. package/package.json +4 -5
  110. package/sdk/.turbo/turbo-test.log +229 -217
  111. package/sdk/dist/generated/bml.d.ts +39 -1
  112. package/sdk/dist/generated/bml.js +33 -8
  113. package/sdk/dist/generated/counter.d.ts +131 -3
  114. package/sdk/dist/generated/counter.js +41 -10
  115. package/sdk/dist/generated/dvn.d.ts +431 -362
  116. package/sdk/dist/generated/dvn.js +80 -55
  117. package/sdk/dist/generated/dvn_fee_lib.d.ts +327 -251
  118. package/sdk/dist/generated/dvn_fee_lib.js +55 -57
  119. package/sdk/dist/generated/endpoint.d.ts +131 -3
  120. package/sdk/dist/generated/endpoint.js +41 -10
  121. package/sdk/dist/generated/executor.d.ts +503 -339
  122. package/sdk/dist/generated/executor.js +80 -48
  123. package/sdk/dist/generated/executor_fee_lib.d.ts +395 -319
  124. package/sdk/dist/generated/executor_fee_lib.js +54 -56
  125. package/sdk/dist/generated/executor_helper.d.ts +53 -187
  126. package/sdk/dist/generated/executor_helper.js +47 -29
  127. package/sdk/dist/generated/layerzero_view.d.ts +1271 -0
  128. package/sdk/dist/generated/layerzero_view.js +294 -0
  129. package/sdk/dist/generated/oft.d.ts +1851 -0
  130. package/sdk/dist/generated/oft.js +347 -0
  131. package/sdk/dist/generated/price_feed.d.ts +329 -253
  132. package/sdk/dist/generated/price_feed.js +55 -57
  133. package/sdk/dist/generated/sml.d.ts +131 -3
  134. package/sdk/dist/generated/sml.js +41 -10
  135. package/sdk/dist/generated/treasury.d.ts +131 -3
  136. package/sdk/dist/generated/treasury.js +41 -10
  137. package/sdk/dist/generated/uln302.d.ts +131 -3
  138. package/sdk/dist/generated/uln302.js +43 -12
  139. package/sdk/dist/generated/upgrader.d.ts +201 -15
  140. package/sdk/dist/generated/upgrader.js +99 -1
  141. package/sdk/dist/index.d.ts +2 -2
  142. package/sdk/dist/index.js +3 -3
  143. package/sdk/package.json +3 -2
  144. package/sdk/src/index.ts +3 -3
  145. package/sdk/test/oft-sml.test.ts +20 -20
  146. package/sdk/test/upgrader.test.ts +2 -3
  147. package/sdk/turbo.json +8 -0
  148. package/tools/ts-bindings-gen/Cargo.toml +2 -0
  149. package/tools/ts-bindings-gen/src/main.rs +53 -5
  150. package/turbo.json +0 -2
  151. package/contracts/oapps/oft/src/interfaces/mint_burn_token.rs +0 -23
  152. package/contracts/oapps/oft/src/interfaces/mod.rs +0 -3
  153. package/contracts/oapps/oft/src/oft_impl.rs +0 -201
  154. package/contracts/oapps/oft/src/tests/extensions/mod.rs +0 -11
  155. package/contracts/oapps/oft/src/tests/extensions/setup.rs +0 -917
  156. package/contracts/oapps/oft/src/tests/extensions/test_oft_fee.rs +0 -751
  157. package/contracts/oapps/oft/src/tests/extensions/test_pausable.rs +0 -434
  158. package/contracts/oapps/oft/src/tests/extensions/test_rate_limiter.rs +0 -1080
  159. package/contracts/oapps/oft-std/integration-tests/utils.rs +0 -427
  160. package/contracts/oapps/oft-std/src/lib.rs +0 -16
  161. package/contracts/oapps/oft-std/src/oft.rs +0 -174
  162. package/sdk/dist/generated/oft_std.d.ts +0 -1722
  163. package/sdk/dist/generated/oft_std.js +0 -316
  164. package/sdk/dist/wasm/blocked-message-lib.d.ts +0 -1
  165. package/sdk/dist/wasm/blocked-message-lib.js +0 -2
  166. package/sdk/dist/wasm/counter.d.ts +0 -1
  167. package/sdk/dist/wasm/counter.js +0 -2
  168. package/sdk/dist/wasm/dvn-fee-lib.d.ts +0 -1
  169. package/sdk/dist/wasm/dvn-fee-lib.js +0 -2
  170. package/sdk/dist/wasm/dvn.d.ts +0 -1
  171. package/sdk/dist/wasm/dvn.js +0 -2
  172. package/sdk/dist/wasm/endpoint-v2.d.ts +0 -1
  173. package/sdk/dist/wasm/endpoint-v2.js +0 -2
  174. package/sdk/dist/wasm/executor-fee-lib.d.ts +0 -1
  175. package/sdk/dist/wasm/executor-fee-lib.js +0 -2
  176. package/sdk/dist/wasm/executor-helper.d.ts +0 -1
  177. package/sdk/dist/wasm/executor-helper.js +0 -2
  178. package/sdk/dist/wasm/executor.d.ts +0 -1
  179. package/sdk/dist/wasm/executor.js +0 -2
  180. package/sdk/dist/wasm/layerzero-views.d.ts +0 -1
  181. package/sdk/dist/wasm/layerzero-views.js +0 -2
  182. package/sdk/dist/wasm/oft-std.d.ts +0 -1
  183. package/sdk/dist/wasm/oft-std.js +0 -2
  184. package/sdk/dist/wasm/price-feed.d.ts +0 -1
  185. package/sdk/dist/wasm/price-feed.js +0 -2
  186. package/sdk/dist/wasm/simple-message-lib.d.ts +0 -1
  187. package/sdk/dist/wasm/simple-message-lib.js +0 -2
  188. package/sdk/dist/wasm/treasury.d.ts +0 -1
  189. package/sdk/dist/wasm/treasury.js +0 -2
  190. package/sdk/dist/wasm/uln302.d.ts +0 -1
  191. package/sdk/dist/wasm/uln302.js +0 -2
  192. package/sdk/dist/wasm/upgrader.d.ts +0 -1
  193. package/sdk/dist/wasm/upgrader.js +0 -2
  194. package/sdk/dist/wasm.d.ts +0 -15
  195. package/sdk/dist/wasm.js +0 -15
  196. /package/contracts/oapps/{oft-std → oft}/integration-tests/extensions/mod.rs +0 -0
  197. /package/contracts/oapps/{oft → oft-core}/src/codec/mod.rs +0 -0
  198. /package/contracts/oapps/{oft → oft-core}/src/codec/oft_compose_msg_codec.rs +0 -0
  199. /package/contracts/oapps/{oft → oft-core}/src/codec/oft_msg_codec.rs +0 -0
  200. /package/contracts/oapps/{oft → oft-core}/src/errors.rs +0 -0
  201. /package/contracts/oapps/{oft → oft-core}/src/events.rs +0 -0
  202. /package/contracts/oapps/{oft → oft-core}/src/storage.rs +0 -0
  203. /package/contracts/oapps/{oft → oft-core}/src/tests/test_decimals.rs +0 -0
  204. /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_compose_msg_codec.rs +0 -0
  205. /package/contracts/oapps/{oft → oft-core}/src/tests/test_oft_version.rs +0 -0
  206. /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_oft.rs +0 -0
  207. /package/contracts/oapps/{oft → oft-core}/src/tests/test_quote_send.rs +0 -0
  208. /package/contracts/oapps/{oft → oft-core}/src/tests/test_send.rs +0 -0
  209. /package/contracts/oapps/{oft → oft-core}/src/tests/test_token.rs +0 -0
  210. /package/contracts/oapps/{oft → oft-core}/src/types.rs +0 -0
  211. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/dvn_fee_lib.rs +0 -0
  212. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/executor_fee_lib.rs +0 -0
  213. /package/contracts/workers/{worker/src/interfaces → fee-lib-interfaces/src}/price_feed.rs +0 -0
@@ -4,7 +4,7 @@
4
4
 
5
5
  use crate::{
6
6
  codec::oft_msg_codec::OFTMessage,
7
- oft::OFTClient,
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
- extern crate self as oft;
108
-
109
- use crate::initialize_oft;
110
- use crate::oft::{OFTInternal, OFT};
111
- use crate::oft_impl;
112
- use crate::types::OFTReceipt;
107
+ use crate::{
108
+ self as oft_core,
109
+ oft_core::{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;
@@ -127,12 +133,12 @@ mod test_mint_burn_oft {
127
133
  delegate: &Option<Address>,
128
134
  shared_decimals: u32,
129
135
  ) {
130
- initialize_oft::<Self>(env, owner, token, endpoint, delegate, shared_decimals)
136
+ Self::__initialize_oft(env, owner, token, endpoint, delegate, shared_decimals)
131
137
  }
132
138
  }
133
139
 
134
140
  #[contractimpl(contracttrait)]
135
- impl OFT for TestMintBurnOFT {}
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
- oft_impl::lz_receive::<Self>(env, executor, origin, guid, message, extra_data, value)
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
- crate::oft_types::mint_burn::debit::<Self>(env, sender, amount_ld, min_amount_ld, dst_eid)
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, src_eid: u32) -> i128 {
157
- crate::oft_types::mint_burn::credit::<Self>(env, to, amount_ld, src_eid)
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
- extern crate self as oft;
165
-
166
- use crate::initialize_oft;
167
- use crate::oft::{OFTInternal, OFT};
168
- use crate::oft_impl;
169
- use crate::types::OFTReceipt;
175
+ use crate::{
176
+ self as oft_core,
177
+ oft_core::{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;
@@ -184,12 +194,12 @@ mod test_lock_unlock_oft {
184
194
  delegate: &Option<Address>,
185
195
  shared_decimals: u32,
186
196
  ) {
187
- initialize_oft::<Self>(env, owner, token, endpoint, delegate, shared_decimals)
197
+ Self::__initialize_oft(env, owner, token, endpoint, delegate, shared_decimals)
188
198
  }
189
199
  }
190
200
 
191
201
  #[contractimpl(contracttrait)]
192
- impl OFT for TestLockUnlockOFT {}
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
- oft_impl::lz_receive::<Self>(env, executor, origin, guid, message, extra_data, value)
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
- crate::oft_types::lock_unlock::debit::<Self>(env, sender, amount_ld, min_amount_ld, dst_eid)
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, src_eid: u32) -> i128 {
218
- crate::oft_types::lock_unlock::credit::<Self>(env, to, amount_ld, src_eid)
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 address_to_bytes32(address: &Address) -> BytesN<32> {
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,
@@ -2,45 +2,25 @@
2
2
 
3
3
  //! # Upgrader Contract
4
4
  //!
5
- //! A stateless utility contract for performing upgrade and migrate operations on upgradeable contracts.
6
- //!
7
- //! This contract provides a convenient way to upgrade other contracts, especially useful for
8
- //! performing upgrade+migrate atomically in a single transaction.
5
+ //! A stateless utility contract for performing atomic upgrade and migrate operations
6
+ //! on contracts implementing the [`Upgradeable`](utils::upgradeable::Upgradeable) trait.
9
7
  //!
10
8
  //! ## Security Model
11
9
  //!
12
- //! The Upgrader is a permissionless utility - anyone can call it, but security is enforced
13
- //! by the target contract's authorization checks. When you call `upgrader.upgrade()`, the target
14
- //! contract's `#[only_auth]` guard ensures only the target's authorizer can successfully upgrade it.
10
+ //! The Upgrader is permissionless - anyone can call it, but security is enforced by the
11
+ //! target contract's authorization checks. The target contract's `#[only_auth]` guard
12
+ //! ensures only its authorizer can successfully upgrade it.
15
13
  //!
16
14
  //! ## Usage
17
15
  //!
18
- //! Deploy this contract once, then anyone can use it to upgrade contracts they own.
19
- //!
20
16
  //! ```ignore
21
- //! // Deploy upgrader (no initialization needed)
22
- //! let upgrader_id = env.register(Upgrader, ());
23
17
  //! let upgrader = UpgraderClient::new(&env, &upgrader_id);
24
- //!
25
- //! // Upgrade a contract (you must be the target contract's owner)
26
- //! upgrader.upgrade(&target_contract, &new_wasm_hash);
27
- //!
28
- //! // Or upgrade and migrate in one transaction
29
- //! let migration_data = vec![&env, val1, val2];
30
- //! upgrader.upgrade_and_migrate(&target_contract, &new_wasm_hash, migration_data);
18
+ //! let migration_data = my_data.to_xdr(&env);
19
+ //! upgrader.upgrade_and_migrate(&target_contract, &new_wasm_hash, &migration_data);
31
20
  //! ```
32
21
 
33
- use soroban_sdk::{contract, contractclient, contractimpl, symbol_short, Address, BytesN, Env, Symbol, Vec};
34
-
35
- /// Symbol for the migrate function call
36
- pub const MIGRATE: Symbol = symbol_short!("migrate");
37
-
38
- /// Trait representing an upgradeable contract's interface
39
- #[contractclient(name = "UpgradeableContractClient")]
40
- pub trait UpgradeableContract {
41
- /// Upgrades the contract to new WASM bytecode
42
- fn upgrade(env: &Env, new_wasm_hash: BytesN<32>);
43
- }
22
+ use soroban_sdk::{contract, contractimpl, xdr::ToXdr, Address, Bytes, BytesN, Env};
23
+ use utils::upgradeable::UpgradeableClient;
44
24
 
45
25
  /// Upgrader contract for managing upgrades of other contracts.
46
26
  ///
@@ -51,46 +31,39 @@ pub struct Upgrader;
51
31
 
52
32
  #[contractimpl]
53
33
  impl Upgrader {
54
- /// Upgrade a target contract and run its migration in a single transaction
34
+ /// Upgrades a target contract without custom migration data.
35
+ ///
36
+ /// This is a convenience wrapper that calls `upgrade_and_migrate` with empty migration data.
37
+ ///
38
+ /// # Arguments
39
+ /// * `contract_address` - The address of the contract to upgrade
40
+ /// * `wasm_hash` - The hash of the new WASM bytecode
41
+ pub fn upgrade(env: &Env, contract_address: &Address, wasm_hash: &BytesN<32>) {
42
+ Self::upgrade_and_migrate(env, contract_address, wasm_hash, &().to_xdr(env));
43
+ }
44
+
45
+ /// Upgrades a target contract and runs its migration in a single transaction.
55
46
  ///
56
47
  /// The caller must be authorized as the authorizer of the target contract.
57
48
  /// This is enforced by the target contract's `#[only_auth]` check.
58
49
  ///
59
- /// This is useful for atomic upgrades where you want to ensure the migration
60
- /// happens immediately after the upgrade, or the entire operation fails.
61
- ///
62
50
  /// # Arguments
63
51
  /// * `contract_address` - The address of the contract to upgrade
64
52
  /// * `wasm_hash` - The hash of the new WASM bytecode
65
- /// * `migration_data` - A vector of values to pass to the migrate function.
66
- /// The types of these values depend on the target contract's MigrationData type.
53
+ /// * `migration_data` - XDR-encoded bytes to pass to the migrate function.
54
+ /// Use `value.to_xdr(&env)` to encode the target contract's MigrationData type.
67
55
  ///
68
56
  /// # Example
69
57
  /// ```ignore
70
- /// // For a contract with MigrationData = (u32, bool)
71
- /// let migration_data = vec![
72
- /// &env,
73
- /// 42u32.into_val(&env),
74
- /// true.into_val(&env),
75
- /// ];
76
- /// upgrader.upgrade_and_migrate(&contract_addr, &wasm_hash, migration_data);
58
+ /// let migration_data = my_data.to_xdr(&env);
59
+ /// upgrader.upgrade_and_migrate(&contract_addr, &wasm_hash, &migration_data);
77
60
  /// ```
78
- pub fn upgrade_and_migrate(
79
- env: &Env,
80
- contract_address: &Address,
81
- wasm_hash: BytesN<32>,
82
- migration_data: Vec<soroban_sdk::Val>,
83
- ) {
84
- let contract_client = UpgradeableContractClient::new(env, contract_address);
85
-
86
- // First upgrade the contract
87
- contract_client.upgrade(&wasm_hash);
88
-
89
- // Then call migrate with the provided data
90
- // We use invoke_contract because the migration data type is unknown to this contract
91
- env.invoke_contract::<()>(contract_address, &MIGRATE, migration_data);
61
+ pub fn upgrade_and_migrate(env: &Env, contract_address: &Address, wasm_hash: &BytesN<32>, migration_data: &Bytes) {
62
+ let client = UpgradeableClient::new(env, contract_address);
63
+ client.upgrade(wasm_hash);
64
+ client.migrate(migration_data);
92
65
  }
93
66
  }
94
67
 
95
68
  #[cfg(test)]
96
- mod tests;
69
+ mod tests;
@@ -1,6 +1,6 @@
1
1
  extern crate std;
2
2
 
3
- use soroban_sdk::{contractclient, testutils::Address as _, Address, BytesN, Env, TryIntoVal};
3
+ use soroban_sdk::{contractclient, testutils::Address as _, xdr::ToXdr, Address, BytesN, Env};
4
4
 
5
5
  use crate::{Upgrader, UpgraderClient};
6
6
 
@@ -17,16 +17,20 @@ trait TestUpgradeableContract2 {
17
17
  }
18
18
 
19
19
  mod contract_v1 {
20
- // #[contract]
21
- // #[upgradeable]
22
- // #[ownable]
23
- // pub struct TestUpgradeableContract;
20
+ //#![no_std]
24
21
 
25
- // impl UpgradeableInternal for TestUpgradeableContract {
26
- // type MigrationData = ();
22
+ // use soroban_sdk::{contractimpl, Env, Symbol, Address, contractclient};
23
+ // use utils::{ upgradeable::UpgradeableInternal};
24
+ // use common_macros::lz_contract;
27
25
 
28
- // fn _migrate(env: &Env, _migration_data: &Self::MigrationData) {
29
- // env.storage().instance().set(&Symbol::new(env, "counter"), &2);
26
+ // #[lz_contract(upgradeable)]
27
+ // pub struct DummyContract;
28
+
29
+ // impl UpgradeableInternal for DummyContract {
30
+ // type MigrationData = u32;
31
+
32
+ // fn __migrate(env: &Env, migration_data: &Self::MigrationData) {
33
+ // env.storage().instance().set(&Symbol::new(env, "counter2"), migration_data);
30
34
  // }
31
35
  // }
32
36
 
@@ -35,34 +39,37 @@ mod contract_v1 {
35
39
  // fn counter(env: &Env) -> u32;
36
40
  // }
37
41
 
38
- // #[contract_impl]
39
- // impl TestUpgradeable for TestUpgradeableContract {
42
+ // #[contractimpl]
43
+ // impl TestUpgradeable for DummyContract {
40
44
  // fn counter(env: &Env) -> u32 {
41
45
  // env.storage().instance().get(&Symbol::new(env, "counter")).unwrap_or(0)
42
46
  // }
43
47
  // }
44
48
 
45
- // #[contract_impl]
46
- // impl TestUpgradeableContract {
49
+ // #[contractimpl]
50
+ // impl DummyContract {
47
51
  // pub fn __constructor(env: &Env, owner: &Address) {
48
52
  // Self::init_owner(env, owner);
49
53
  // env.storage().instance().set(&Symbol::new(env, "counter"), &1_u32);
50
54
  // }
51
55
  // }
52
- use super::MigrationData;
53
56
  soroban_sdk::contractimport!(file = "./src/tests/test_data/test_upgradeable_contract1.wasm");
54
57
  }
55
58
  mod contract_v2 {
56
- // #[contract]
57
- // #[upgradeable]
58
- // #[ownable]
59
- // pub struct TestUpgradeableContract;
59
+ //#![no_std]
60
+
61
+ // use soroban_sdk::{contractimpl, Env, Symbol, Address, contractclient};
62
+ // use utils::{ upgradeable::UpgradeableInternal};
63
+ // use common_macros::lz_contract;
64
+
65
+ // #[lz_contract(upgradeable)]
66
+ // pub struct DummyContract;
60
67
 
61
- // impl UpgradeableInternal for TestUpgradeableContract {
62
- // type MigrationData = ();
68
+ // impl UpgradeableInternal for DummyContract {
69
+ // type MigrationData = u32;
63
70
 
64
- // fn _migrate(env: &Env, _migration_data: &Self::MigrationData) {
65
- // env.storage().instance().set(&Symbol::new(env, "counter2"), &2_u32);
71
+ // fn __migrate(env: &Env, migration_data: &Self::MigrationData) {
72
+ // env.storage().instance().set(&Symbol::new(env, "counter2"), migration_data);
66
73
  // }
67
74
  // }
68
75
 
@@ -72,8 +79,8 @@ mod contract_v2 {
72
79
  // fn counter2(env: &Env) -> u32;
73
80
  // }
74
81
 
75
- // #[contract_impl]
76
- // impl TestUpgradeable for TestUpgradeableContract {
82
+ // #[contractimpl]
83
+ // impl TestUpgradeable for DummyContract {
77
84
  // fn counter(env: &Env) -> u32 {
78
85
  // env.storage().instance().get(&Symbol::new(env, "counter")).unwrap_or(0)
79
86
  // }
@@ -82,7 +89,14 @@ mod contract_v2 {
82
89
  // env.storage().instance().get(&Symbol::new(env, "counter2")).unwrap_or(0)
83
90
  // }
84
91
  // }
85
- use super::MigrationData;
92
+
93
+ // #[contractimpl]
94
+ // impl DummyContract {
95
+ // pub fn __constructor(env: &Env, owner: &Address) {
96
+ // Self::init_owner(env, owner);
97
+ // env.storage().instance().set(&Symbol::new(env, "counter"), &1_u32);
98
+ // }
99
+ // }
86
100
  soroban_sdk::contractimport!(file = "./src/tests/test_data/test_upgradeable_contract2.wasm");
87
101
  }
88
102
 
@@ -90,9 +104,6 @@ fn install_new_wasm(e: &Env) -> BytesN<32> {
90
104
  e.deployer().upload_contract_wasm(contract_v2::WASM)
91
105
  }
92
106
 
93
- #[allow(dead_code)]
94
- type MigrationData = ();
95
-
96
107
  #[test]
97
108
  fn test_upgrade_with_upgrader() {
98
109
  let e = Env::default();
@@ -107,14 +118,12 @@ fn test_upgrade_with_upgrader() {
107
118
  let upgrader_client = UpgraderClient::new(&e, &upgrader);
108
119
 
109
120
  let new_wasm_hash = install_new_wasm(&e);
110
-
111
- upgrader_client.upgrade_and_migrate(
112
- &contract_id,
113
- &new_wasm_hash,
114
- &soroban_sdk::vec![&e, ().try_into_val(&e).unwrap()],
115
- );
121
+ let counter_value = 2_u32;
122
+ // Encode migration data as XDR bytes
123
+ let migration_data = counter_value.to_xdr(&e);
124
+ upgrader_client.upgrade_and_migrate(&contract_id, &new_wasm_hash, &migration_data);
116
125
 
117
126
  let client_v2 = TestUpgradeableContractClient2::new(&e, &contract_id);
118
127
 
119
- assert_eq!(client_v2.counter2(), 2);
128
+ assert_eq!(client_v2.counter2(), counter_value);
120
129
  }
@@ -92,6 +92,7 @@ impl<'a> BufferReader<'a> {
92
92
  value
93
93
  }
94
94
 
95
+ /// Reads all bytes from current position to the end of the buffer.
95
96
  pub fn read_bytes_until_end(&mut self) -> Bytes {
96
97
  self.read_bytes(self.remaining_len())
97
98
  }
@@ -27,8 +27,12 @@ pub enum TtlConfigurableError {
27
27
  /// OwnableError: 1030-1039
28
28
  #[contract_error]
29
29
  pub enum OwnableError {
30
- OwnerAlreadySet = 1030,
30
+ InvalidPendingOwner = 1030,
31
+ InvalidTtl,
32
+ NoPendingTransfer,
33
+ OwnerAlreadySet,
31
34
  OwnerNotSet,
35
+ TransferInProgress,
32
36
  }
33
37
 
34
38
  /// BytesExtError: 1040-1049
@@ -40,7 +44,9 @@ pub enum BytesExtError {
40
44
  /// UpgradeableError: 1050-1059
41
45
  #[contract_error]
42
46
  pub enum UpgradeableError {
43
- MigrationNotAllowed = 1050,
47
+ InvalidMigrationData = 1050,
48
+ MigrationNotAllowed,
49
+ UpgradesFrozen,
44
50
  }
45
51
 
46
52
  /// MultisigError: 1060-1069
@@ -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
- /// Transfers ownership to a new address. Requires current owner authorization.
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
- /// Permanently renounces ownership. Requires current owner authorization.
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, #1031)"; // OwnerNotSet
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);