@layerzerolabs/protocol-stellar-v2 0.2.29 → 0.2.30

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 (205) hide show
  1. package/.turbo/turbo-build.log +371 -321
  2. package/.turbo/turbo-lint.log +211 -202
  3. package/.turbo/turbo-test.log +1766 -1673
  4. package/Cargo.lock +11 -1
  5. package/contracts/common-macros/src/lib.rs +0 -2
  6. package/contracts/common-macros/src/tests/snapshots/common_macros__tests__upgradeable__snapshot_generated_upgradeable_code.snap +1 -0
  7. package/contracts/endpoint-v2/src/messaging_channel.rs +32 -3
  8. package/contracts/endpoint-v2/src/tests/endpoint_setup.rs +1 -1
  9. package/contracts/endpoint-v2/src/tests/messaging_channel/clear_payload.rs +1 -1
  10. package/contracts/endpoint-v2/src/tests/messaging_channel/inbound.rs +6 -6
  11. package/contracts/endpoint-v2/src/tests/messaging_channel/inbound_payload_hash.rs +1 -1
  12. package/contracts/endpoint-v2/src/tests/messaging_channel/outbound.rs +16 -10
  13. package/contracts/macro-integration-tests/tests/runtime/oapp/options_type3.rs +10 -10
  14. package/contracts/macro-integration-tests/tests/runtime/oapp/receiver.rs +3 -3
  15. package/contracts/macro-integration-tests/tests/runtime/oapp/sender.rs +4 -3
  16. package/contracts/macro-integration-tests/tests/runtime/upgradeable/migrate_guard_and_state.rs +1 -57
  17. package/contracts/macro-integration-tests/tests/ui/lz_contract/fail/upgradeable_missing_internal.stderr +0 -30
  18. package/contracts/macro-integration-tests/tests/ui/oapp/fail/custom_wrong_value.stderr +5 -3
  19. package/contracts/macro-integration-tests/tests/ui/oapp/fail/non_struct_input.stderr +6 -4
  20. package/contracts/macro-integration-tests/tests/ui/oapp/fail/unknown_custom_option.stderr +5 -3
  21. package/contracts/macro-integration-tests/tests/ui/oapp/fail/wrong_key.stderr +5 -3
  22. package/contracts/macro-integration-tests/tests/ui/upgradeable/fail/missing_auth_trait.stderr +0 -30
  23. package/contracts/macro-integration-tests/tests/ui/upgradeable/fail/missing_upgradeable_internal.stderr +0 -30
  24. package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/basic.rs +0 -2
  25. package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/multisig_contract.rs +0 -2
  26. package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/no_migration.rs +0 -2
  27. package/contracts/macro-integration-tests/tests/ui/upgradeable/pass/no_user_contractimpl.rs +1 -3
  28. package/contracts/message-libs/message-lib-common/src/packet_codec_v1.rs +3 -6
  29. package/contracts/message-libs/message-lib-common/src/tests/worker_options/extract_type_3_options.rs +10 -0
  30. package/contracts/message-libs/message-lib-common/src/worker_options.rs +6 -2
  31. package/contracts/message-libs/treasury/src/interfaces/zro_fee_lib.rs +3 -3
  32. package/contracts/message-libs/treasury/src/lib.rs +2 -1
  33. package/contracts/message-libs/treasury/src/tests/setup.rs +1 -1
  34. package/contracts/message-libs/treasury/src/treasury.rs +5 -2
  35. package/contracts/message-libs/uln-302/src/errors.rs +2 -0
  36. package/contracts/message-libs/uln-302/src/events.rs +3 -3
  37. package/contracts/message-libs/uln-302/src/interfaces/receive_uln.rs +8 -0
  38. package/contracts/message-libs/uln-302/src/lib.rs +2 -1
  39. package/contracts/message-libs/uln-302/src/receive_uln.rs +16 -13
  40. package/contracts/message-libs/uln-302/src/send_uln.rs +51 -24
  41. package/contracts/message-libs/uln-302/src/storage.rs +2 -2
  42. package/contracts/message-libs/uln-302/src/tests/receive_uln302/effective_receive_uln_config.rs +45 -1
  43. package/contracts/message-libs/uln-302/src/tests/receive_uln302/verifiable.rs +63 -0
  44. package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_executor_config.rs +47 -2
  45. package/contracts/message-libs/uln-302/src/tests/send_uln302/effective_send_uln_config.rs +50 -1
  46. package/contracts/message-libs/uln-302/src/uln302.rs +0 -8
  47. package/contracts/oapps/counter/Cargo.toml +4 -4
  48. package/contracts/oapps/counter/integration_tests/setup_uln.rs +22 -2
  49. package/contracts/oapps/counter/src/counter.rs +8 -8
  50. package/contracts/oapps/oapp/src/interfaces/oapp_msg_inspector.rs +33 -10
  51. package/contracts/oapps/oapp/src/lib.rs +6 -2
  52. package/contracts/oapps/oapp/src/oapp_core.rs +49 -24
  53. package/contracts/oapps/oapp/src/oapp_options_type3.rs +21 -14
  54. package/contracts/oapps/oapp/src/oapp_receiver.rs +17 -16
  55. package/contracts/oapps/oapp/src/oapp_sender.rs +66 -15
  56. package/contracts/oapps/oapp/src/tests/oapp_core.rs +5 -5
  57. package/contracts/oapps/oapp/src/tests/oapp_options_type3.rs +18 -18
  58. package/contracts/oapps/oapp/src/tests/oapp_receiver.rs +4 -4
  59. package/contracts/oapps/oapp/src/tests/oapp_sender.rs +3 -3
  60. package/contracts/oapps/oapp-macros/Cargo.toml +0 -1
  61. package/contracts/oapps/oapp-macros/src/generators.rs +87 -46
  62. package/contracts/oapps/oapp-macros/src/lib.rs +3 -61
  63. package/contracts/oapps/oapp-macros/src/tests/oapp.rs +9 -23
  64. package/contracts/oapps/oapp-macros/src/tests/parse_custom_impls.rs +15 -11
  65. package/contracts/oapps/oft/Cargo.toml +1 -1
  66. package/contracts/oapps/oft/integration-tests/extensions/test_oft_fee.rs +3 -3
  67. package/contracts/oapps/oft/integration-tests/extensions/test_pausable.rs +4 -4
  68. package/contracts/oapps/oft/integration-tests/extensions/test_rate_limiter.rs +144 -8
  69. package/contracts/oapps/oft/integration-tests/setup.rs +4 -2
  70. package/contracts/oapps/oft/integration-tests/utils.rs +25 -11
  71. package/contracts/oapps/oft/src/extensions/oft_fee.rs +65 -63
  72. package/contracts/oapps/oft/src/extensions/pausable.rs +2 -3
  73. package/contracts/oapps/oft/src/extensions/rate_limiter.rs +22 -5
  74. package/contracts/oapps/oft/src/interfaces/mint_burnable.rs +18 -0
  75. package/contracts/oapps/oft/src/interfaces/mod.rs +3 -0
  76. package/contracts/oapps/oft/src/lib.rs +4 -2
  77. package/contracts/oapps/oft/src/oft.rs +35 -36
  78. package/contracts/oapps/oft/src/oft_types/lock_unlock.rs +13 -9
  79. package/contracts/oapps/oft/src/oft_types/mint_burn.rs +14 -9
  80. package/contracts/oapps/oft/src/oft_types/mod.rs +14 -12
  81. package/contracts/oapps/oft/src/tests/extensions/oft_fee.rs +28 -20
  82. package/contracts/oapps/oft/src/tests/extensions/rate_limiter.rs +136 -2
  83. package/contracts/oapps/oft/src/tests/oft_types/lock_unlock.rs +12 -8
  84. package/contracts/oapps/oft-core/integration-tests/setup.rs +8 -9
  85. package/contracts/oapps/oft-core/integration-tests/test_with_sml.rs +7 -6
  86. package/contracts/oapps/oft-core/integration-tests/utils.rs +5 -4
  87. package/contracts/oapps/oft-core/src/codec/oft_compose_msg_codec.rs +2 -2
  88. package/contracts/oapps/oft-core/src/codec/oft_msg_codec.rs +33 -37
  89. package/contracts/oapps/oft-core/src/errors.rs +2 -1
  90. package/contracts/oapps/oft-core/src/events.rs +6 -0
  91. package/contracts/oapps/oft-core/src/lib.rs +8 -4
  92. package/contracts/oapps/oft-core/src/oft_core.rs +205 -148
  93. package/contracts/oapps/oft-core/src/storage.rs +4 -2
  94. package/contracts/oapps/oft-core/src/tests/test_decimals.rs +2 -2
  95. package/contracts/oapps/oft-core/src/tests/test_lz_receive.rs +6 -6
  96. package/contracts/oapps/oft-core/src/tests/test_msg_inspector.rs +7 -6
  97. package/contracts/oapps/oft-core/src/tests/test_oft_msg_codec.rs +11 -82
  98. package/contracts/oapps/oft-core/src/tests/test_quote_oft.rs +13 -13
  99. package/contracts/oapps/oft-core/src/tests/test_quote_send.rs +1 -1
  100. package/contracts/oapps/oft-core/src/tests/test_resolve_address.rs +2 -2
  101. package/contracts/oapps/oft-core/src/tests/test_send.rs +22 -22
  102. package/contracts/oapps/oft-core/src/tests/test_utils.rs +20 -22
  103. package/contracts/oapps/oft-core/src/utils.rs +12 -8
  104. package/contracts/sac-manager/Cargo.toml +25 -0
  105. package/contracts/sac-manager/src/errors.rs +18 -0
  106. package/contracts/sac-manager/src/extensions/mod.rs +6 -0
  107. package/contracts/sac-manager/src/extensions/redistribution.rs +109 -0
  108. package/contracts/sac-manager/src/extensions/supply_control/mod.rs +488 -0
  109. package/contracts/sac-manager/src/extensions/supply_control/rate_limit.rs +126 -0
  110. package/contracts/sac-manager/src/interfaces/mod.rs +3 -0
  111. package/contracts/sac-manager/src/interfaces/sac_manager.rs +52 -0
  112. package/contracts/sac-manager/src/lib.rs +23 -0
  113. package/contracts/sac-manager/src/sac_manager.rs +193 -0
  114. package/contracts/sac-manager/src/storage.rs +20 -0
  115. package/contracts/sac-manager/src/tests/mod.rs +14 -0
  116. package/contracts/sac-manager/src/tests/redistribution/mod.rs +1 -0
  117. package/contracts/sac-manager/src/tests/redistribution/redistribute_funds.rs +82 -0
  118. package/contracts/sac-manager/src/tests/sac_manager/admin_mint.rs +206 -0
  119. package/contracts/sac-manager/src/tests/sac_manager/burn.rs +215 -0
  120. package/contracts/sac-manager/src/tests/sac_manager/clawback.rs +209 -0
  121. package/contracts/sac-manager/src/tests/sac_manager/mint.rs +252 -0
  122. package/contracts/sac-manager/src/tests/sac_manager/mod.rs +9 -0
  123. package/contracts/sac-manager/src/tests/sac_manager/set_admin.rs +36 -0
  124. package/contracts/sac-manager/src/tests/sac_manager/set_authorized.rs +43 -0
  125. package/contracts/sac-manager/src/tests/sac_manager/set_oft_address.rs +47 -0
  126. package/contracts/sac-manager/src/tests/sac_manager/test_helper.rs +75 -0
  127. package/contracts/sac-manager/src/tests/sac_manager/view_functions.rs +60 -0
  128. package/contracts/sac-manager/src/tests/supply_control/enumerable_set.rs +256 -0
  129. package/contracts/sac-manager/src/tests/supply_control/mod.rs +8 -0
  130. package/contracts/sac-manager/src/tests/supply_control/refill.rs +90 -0
  131. package/contracts/sac-manager/src/tests/supply_control/set_mint_whitelist.rs +245 -0
  132. package/contracts/sac-manager/src/tests/supply_control/set_supply_controller.rs +267 -0
  133. package/contracts/sac-manager/src/tests/supply_control/set_supply_controller_manager.rs +122 -0
  134. package/contracts/sac-manager/src/tests/supply_control/test_helper.rs +38 -0
  135. package/contracts/sac-manager/src/tests/supply_control/update_allow_any_mint_burn.rs +114 -0
  136. package/contracts/sac-manager/src/tests/supply_control/update_limit_config.rs +257 -0
  137. package/contracts/sac-manager/src/tests/test_helper.rs +190 -0
  138. package/contracts/upgrader/src/lib.rs +2 -1
  139. package/contracts/utils/src/errors.rs +0 -1
  140. package/contracts/utils/src/tests/upgradeable.rs +0 -66
  141. package/contracts/utils/src/upgradeable.rs +0 -18
  142. package/contracts/workers/dvn/src/dvn.rs +2 -2
  143. package/contracts/workers/dvn/src/interfaces/dvn.rs +2 -2
  144. package/contracts/workers/dvn/src/lib.rs +2 -1
  145. package/contracts/workers/dvn-fee-lib/src/lib.rs +3 -1
  146. package/contracts/workers/executor/src/auth.rs +42 -26
  147. package/contracts/workers/executor/src/executor.rs +28 -3
  148. package/contracts/workers/executor/src/lib.rs +4 -2
  149. package/contracts/workers/executor/src/storage.rs +21 -1
  150. package/contracts/workers/executor/src/tests/auth.rs +64 -20
  151. package/contracts/workers/executor/src/tests/executor.rs +1 -1
  152. package/contracts/workers/executor/src/tests/setup.rs +18 -0
  153. package/contracts/workers/executor-fee-lib/src/lib.rs +4 -1
  154. package/contracts/workers/executor-helper/src/executor_helper.rs +24 -10
  155. package/contracts/workers/executor-helper/src/tests/setup.rs +147 -34
  156. package/contracts/workers/price-feed/src/lib.rs +3 -1
  157. package/contracts/workers/worker/src/lib.rs +2 -1
  158. package/contracts/workers/worker/src/worker.rs +31 -17
  159. package/docs/oapp-guide.md +17 -8
  160. package/docs/oft-guide.md +3 -3
  161. package/package.json +3 -3
  162. package/sdk/.turbo/turbo-test.log +512 -351
  163. package/sdk/dist/generated/bml.d.ts +3 -9
  164. package/sdk/dist/generated/bml.js +6 -7
  165. package/sdk/dist/generated/counter.d.ts +22 -28
  166. package/sdk/dist/generated/counter.js +11 -12
  167. package/sdk/dist/generated/dvn.d.ts +36 -54
  168. package/sdk/dist/generated/dvn.js +10 -15
  169. package/sdk/dist/generated/dvn_fee_lib.d.ts +3 -21
  170. package/sdk/dist/generated/dvn_fee_lib.js +6 -11
  171. package/sdk/dist/generated/endpoint.d.ts +3 -9
  172. package/sdk/dist/generated/endpoint.js +6 -7
  173. package/sdk/dist/generated/executor.d.ts +80 -54
  174. package/sdk/dist/generated/executor.js +16 -16
  175. package/sdk/dist/generated/executor_fee_lib.d.ts +3 -21
  176. package/sdk/dist/generated/executor_fee_lib.js +6 -11
  177. package/sdk/dist/generated/executor_helper.d.ts +36 -42
  178. package/sdk/dist/generated/executor_helper.js +9 -10
  179. package/sdk/dist/generated/layerzero_view.d.ts +20 -32
  180. package/sdk/dist/generated/layerzero_view.js +25 -26
  181. package/sdk/dist/generated/oft.d.ts +147 -79
  182. package/sdk/dist/generated/oft.js +47 -54
  183. package/sdk/dist/generated/price_feed.d.ts +20 -38
  184. package/sdk/dist/generated/price_feed.js +15 -20
  185. package/sdk/dist/generated/sac_manager.d.ts +1309 -0
  186. package/sdk/dist/generated/sac_manager.js +484 -0
  187. package/sdk/dist/generated/sml.d.ts +3 -9
  188. package/sdk/dist/generated/sml.js +6 -7
  189. package/sdk/dist/generated/treasury.d.ts +3 -9
  190. package/sdk/dist/generated/treasury.js +8 -9
  191. package/sdk/dist/generated/uln302.d.ts +20 -20
  192. package/sdk/dist/generated/uln302.js +25 -22
  193. package/sdk/dist/generated/upgrader.d.ts +3 -9
  194. package/sdk/dist/generated/upgrader.js +6 -7
  195. package/sdk/dist/index.d.ts +1 -0
  196. package/sdk/dist/index.js +1 -0
  197. package/sdk/package.json +1 -1
  198. package/sdk/src/index.ts +1 -0
  199. package/sdk/test/oft-sml.test.ts +7 -5
  200. package/sdk/test/sac-manager-redistribution.test.ts +578 -0
  201. package/sdk/test/suites/globalSetup.ts +11 -6
  202. package/sdk/test/test_data/test_upgradeable_dvn.wasm +0 -0
  203. package/sdk/test/upgrader.test.ts +75 -202
  204. package/sdk/test/utils.ts +40 -0
  205. package/tools/ts-bindings-gen/src/main.rs +1 -0
@@ -4,7 +4,7 @@ use utils::{auth::Auth, option_ext::OptionExt};
4
4
 
5
5
  /// Base fee in basis points (10,000 BPS = 100%)
6
6
  /// Used as denominator in fee calculations
7
- const BASE_FEE_BPS: u64 = 10_000;
7
+ const BASE_FEE_BPS: u32 = 10_000;
8
8
 
9
9
  // =========================================================================
10
10
  // Storage
@@ -12,15 +12,14 @@ const BASE_FEE_BPS: u64 = 10_000;
12
12
 
13
13
  #[storage]
14
14
  pub enum OFTFeeStorage {
15
- /// Default fee rate in basis points (0-10,000, where 10,000 = 100%)
15
+ /// Default fee rate in basis points (1-10,000, where 10,000 = 100%)
16
16
  /// Applied to destinations without specific fee configuration
17
- /// No fee is charged(default to 0) by default
18
- #[instance(u64)]
19
- #[default(0)]
17
+ /// Not set by default (effective rate is 0 when unset)
18
+ #[instance(u32)]
20
19
  DefaultFeeBps,
21
20
 
22
21
  /// Destination-specific fee rates mapped by endpoint ID (eid)
23
- #[persistent(u64)]
22
+ #[persistent(u32)]
24
23
  FeeBps { eid: u32 },
25
24
 
26
25
  /// Address where collected fees will be deposited
@@ -46,7 +45,8 @@ pub enum OFTFeeError {
46
45
  #[contractevent]
47
46
  #[derive(Clone, Debug, Eq, PartialEq)]
48
47
  pub struct DefaultFeeBpsSet {
49
- pub fee_bps: u64,
48
+ /// The default fee rate in basis points, or None if the default fee is removed
49
+ pub fee_bps: Option<u32>,
50
50
  }
51
51
 
52
52
  #[contractevent]
@@ -54,13 +54,14 @@ pub struct DefaultFeeBpsSet {
54
54
  pub struct FeeBpsSet {
55
55
  pub dst_eid: u32,
56
56
  /// The fee rate in basis points, or None if the fee is removed
57
- pub fee_bps: Option<u64>,
57
+ pub fee_bps: Option<u32>,
58
58
  }
59
59
 
60
60
  #[contractevent]
61
61
  #[derive(Clone, Debug, Eq, PartialEq)]
62
62
  pub struct FeeDepositAddressSet {
63
- pub fee_deposit_address: Address,
63
+ /// The address to deposit fees to, or None to remove the fee deposit address
64
+ pub fee_deposit_address: Option<Address>,
64
65
  }
65
66
 
66
67
  // =========================================================================
@@ -73,49 +74,56 @@ pub trait OFTFee: OFTFeeInternal + Auth {
73
74
  // Management Functions
74
75
  // =========================================================================
75
76
 
76
- /// Sets the default fee rate in basis points.
77
+ /// Sets or removes the default fee rate in basis points.
78
+ ///
79
+ /// - `Some(n)`: sets the default fee to `n` basis points (must be >0 and <=10,000).
80
+ /// - `Some(0)`: rejected — use `None` to remove the default fee instead.
81
+ /// - `None`: removes the default fee (effective rate becomes 0).
77
82
  #[only_auth]
78
- fn set_default_fee_bps(env: &soroban_sdk::Env, default_fee_bps: u64) {
83
+ fn set_default_fee_bps(env: &soroban_sdk::Env, default_fee_bps: &Option<u32>) {
79
84
  Self::__set_default_fee_bps(env, default_fee_bps);
80
85
  }
81
86
 
82
87
  /// Sets or removes the fee rate for a specific destination endpoint.
83
88
  ///
89
+ /// - `Some(0)`: explicitly sets zero fee for this destination, overriding the default fee.
90
+ /// - `None`: removes the per-destination override; falls back to the default fee.
91
+ ///
84
92
  /// # Arguments
85
93
  /// * `dst_eid` - The destination endpoint ID
86
94
  /// * `fee_bps` - The fee rate (0-10,000), or None to remove the fee configuration
87
95
  #[only_auth]
88
- fn set_fee_bps(env: &soroban_sdk::Env, dst_eid: u32, fee_bps: Option<u64>) {
96
+ fn set_fee_bps(env: &soroban_sdk::Env, dst_eid: u32, fee_bps: &Option<u32>) {
89
97
  Self::__set_fee_bps(env, dst_eid, fee_bps);
90
98
  }
91
99
 
92
- /// Sets the address where collected fees will be deposited.
100
+ /// Sets or removes the address where collected fees will be deposited.
93
101
  #[only_auth]
94
- fn set_fee_deposit_address(env: &soroban_sdk::Env, fee_deposit_address: soroban_sdk::Address) {
95
- Self::__set_fee_deposit_address(env, &fee_deposit_address);
102
+ fn set_fee_deposit_address(env: &soroban_sdk::Env, fee_deposit_address: &Option<soroban_sdk::Address>) {
103
+ Self::__set_fee_deposit_address(env, fee_deposit_address);
96
104
  }
97
105
 
98
106
  // =========================================================================
99
107
  // View Functions
100
108
  // =========================================================================
101
109
 
102
- /// Returns the default fee rate in basis points.
103
- fn default_fee_bps(env: &soroban_sdk::Env) -> u64 {
110
+ /// Returns the default fee rate in basis points, if set.
111
+ fn default_fee_bps(env: &soroban_sdk::Env) -> Option<u32> {
104
112
  Self::__default_fee_bps(env)
105
113
  }
106
114
 
107
115
  /// Returns the fee rate for a specific destination, if set.
108
- fn fee_bps(env: &soroban_sdk::Env, dst_eid: u32) -> Option<u64> {
116
+ fn fee_bps(env: &soroban_sdk::Env, dst_eid: u32) -> Option<u32> {
109
117
  Self::__fee_bps(env, dst_eid)
110
118
  }
111
119
 
112
120
  /// Returns the effective fee rate for a destination (destination-specific or default).
113
- fn effective_fee_bps(env: &soroban_sdk::Env, dst_eid: u32) -> u64 {
121
+ fn effective_fee_bps(env: &soroban_sdk::Env, dst_eid: u32) -> u32 {
114
122
  Self::__effective_fee_bps(env, dst_eid)
115
123
  }
116
124
 
117
125
  /// Returns the fee deposit address.
118
- fn fee_deposit_address(env: &soroban_sdk::Env) -> soroban_sdk::Address {
126
+ fn fee_deposit_address(env: &soroban_sdk::Env) -> Option<soroban_sdk::Address> {
119
127
  Self::__fee_deposit_address(env)
120
128
  }
121
129
  }
@@ -157,7 +165,8 @@ pub trait OFTFeeInternal {
157
165
  /// * `fee_amount` - The fee amount to transfer
158
166
  fn __charge_fee(env: &Env, token: &Address, from: &Address, fee_amount: i128) {
159
167
  if fee_amount != 0 {
160
- let fee_deposit = Self::__fee_deposit_address(env);
168
+ let fee_deposit =
169
+ Self::__fee_deposit_address(env).unwrap_or_panic(env, OFTFeeError::InvalidFeeDepositAddress);
161
170
  TokenClient::new(env, token).transfer(from, &fee_deposit, &fee_amount);
162
171
  }
163
172
  }
@@ -166,52 +175,49 @@ pub trait OFTFeeInternal {
166
175
  // Management Functions
167
176
  // =========================================================================
168
177
 
169
- /// Sets the default fee rate in basis points.
178
+ /// Sets or removes the default fee rate in basis points.
170
179
  ///
171
180
  /// # Arguments
172
- /// * `default_fee_bps` - The default fee rate (0-10,000, where 10,000 = 100%)
173
- fn __set_default_fee_bps(env: &Env, default_fee_bps: u64) {
174
- assert_with_error!(env, default_fee_bps <= BASE_FEE_BPS, OFTFeeError::InvalidFeeBps);
175
-
176
- let current_default = Self::__default_fee_bps(env);
177
- assert_with_error!(env, current_default != default_fee_bps, OFTFeeError::SameValue);
181
+ /// * `default_fee_bps` - The default fee rate (>0 and <=10,000, where 10,000 = 100%) or None to remove the default fee rate
182
+ fn __set_default_fee_bps(env: &Env, default_fee_bps: &Option<u32>) {
183
+ let current = Self::__default_fee_bps(env);
184
+ assert_with_error!(env, current != *default_fee_bps, OFTFeeError::SameValue);
185
+ assert_with_error!(
186
+ env,
187
+ default_fee_bps.is_none_or(|bps| bps > 0 && bps <= BASE_FEE_BPS),
188
+ OFTFeeError::InvalidFeeBps
189
+ );
178
190
 
179
- OFTFeeStorage::set_default_fee_bps(env, &default_fee_bps);
180
-
181
- DefaultFeeBpsSet { fee_bps: default_fee_bps }.publish(env);
191
+ OFTFeeStorage::set_or_remove_default_fee_bps(env, default_fee_bps);
192
+ DefaultFeeBpsSet { fee_bps: *default_fee_bps }.publish(env);
182
193
  }
183
194
 
184
195
  /// Sets or removes the fee rate for a specific destination endpoint.
185
196
  ///
197
+ /// - `Some(0)`: explicitly sets zero fee for this destination, overriding the default fee.
198
+ /// - `None`: removes the per-destination override; falls back to the default fee.
199
+ ///
186
200
  /// # Arguments
187
201
  /// * `dst_eid` - The destination endpoint ID
188
202
  /// * `fee_bps` - The fee rate (0-10,000), or None to remove the fee configuration
189
- fn __set_fee_bps(env: &Env, dst_eid: u32, fee_bps: Option<u64>) {
203
+ fn __set_fee_bps(env: &Env, dst_eid: u32, fee_bps: &Option<u32>) {
190
204
  let current_fee_bps = Self::__fee_bps(env, dst_eid);
191
- assert_with_error!(env, current_fee_bps != fee_bps, OFTFeeError::SameValue);
192
-
193
- match fee_bps {
194
- Some(bps) => {
195
- assert_with_error!(env, bps <= BASE_FEE_BPS, OFTFeeError::InvalidFeeBps);
196
- OFTFeeStorage::set_fee_bps(env, dst_eid, &bps);
197
- }
198
- None => {
199
- OFTFeeStorage::remove_fee_bps(env, dst_eid);
200
- }
201
- }
205
+ assert_with_error!(env, current_fee_bps != *fee_bps, OFTFeeError::SameValue);
206
+ assert_with_error!(env, fee_bps.is_none_or(|bps| bps <= BASE_FEE_BPS), OFTFeeError::InvalidFeeBps);
207
+
208
+ OFTFeeStorage::set_or_remove_fee_bps(env, dst_eid, fee_bps);
202
209
 
203
- FeeBpsSet { dst_eid, fee_bps }.publish(env);
210
+ FeeBpsSet { dst_eid, fee_bps: *fee_bps }.publish(env);
204
211
  }
205
212
 
206
- /// Sets the address where collected fees will be deposited.
213
+ /// Sets or removes the address where collected fees will be deposited.
207
214
  ///
208
215
  /// # Arguments
209
- /// * `fee_deposit_address` - The address to deposit fees to
210
- fn __set_fee_deposit_address(env: &Env, fee_deposit_address: &Address) {
211
- if let Some(current_address) = OFTFeeStorage::fee_deposit_address(env) {
212
- assert_with_error!(env, current_address != *fee_deposit_address, OFTFeeError::SameValue);
213
- }
214
- OFTFeeStorage::set_fee_deposit_address(env, fee_deposit_address);
216
+ /// * `fee_deposit_address` - The address to deposit fees to, or None to remove the fee deposit address
217
+ fn __set_fee_deposit_address(env: &Env, fee_deposit_address: &Option<Address>) {
218
+ let current = Self::__fee_deposit_address(env);
219
+ assert_with_error!(env, current != *fee_deposit_address, OFTFeeError::SameValue);
220
+ OFTFeeStorage::set_or_remove_fee_deposit_address(env, fee_deposit_address);
215
221
  FeeDepositAddressSet { fee_deposit_address: fee_deposit_address.clone() }.publish(env);
216
222
  }
217
223
 
@@ -219,27 +225,23 @@ pub trait OFTFeeInternal {
219
225
  // View Functions
220
226
  // =========================================================================
221
227
 
222
- /// Returns the effective fee rate for a destination (destination-specific or default).
223
- fn __effective_fee_bps(env: &Env, dst_eid: u32) -> u64 {
224
- if OFTFeeStorage::has_fee_bps(env, dst_eid) {
225
- OFTFeeStorage::fee_bps(env, dst_eid).unwrap()
226
- } else {
227
- OFTFeeStorage::default_fee_bps(env)
228
- }
228
+ /// Returns the effective fee rate for a destination (destination-specific or default, 0 if neither).
229
+ fn __effective_fee_bps(env: &Env, dst_eid: u32) -> u32 {
230
+ Self::__fee_bps(env, dst_eid).or_else(|| Self::__default_fee_bps(env)).unwrap_or(0)
229
231
  }
230
232
 
231
- /// Returns the default fee rate in basis points.
232
- fn __default_fee_bps(env: &Env) -> u64 {
233
+ /// Returns the default fee rate in basis points, if set.
234
+ fn __default_fee_bps(env: &Env) -> Option<u32> {
233
235
  OFTFeeStorage::default_fee_bps(env)
234
236
  }
235
237
 
236
238
  /// Returns the fee rate for a specific destination, if set.
237
- fn __fee_bps(env: &Env, dst_eid: u32) -> Option<u64> {
239
+ fn __fee_bps(env: &Env, dst_eid: u32) -> Option<u32> {
238
240
  OFTFeeStorage::fee_bps(env, dst_eid)
239
241
  }
240
242
 
241
243
  /// Returns the fee deposit address.
242
- fn __fee_deposit_address(env: &Env) -> Address {
243
- OFTFeeStorage::fee_deposit_address(env).unwrap_or_panic(env, OFTFeeError::InvalidFeeDepositAddress)
244
+ fn __fee_deposit_address(env: &Env) -> Option<Address> {
245
+ OFTFeeStorage::fee_deposit_address(env)
244
246
  }
245
247
  }
@@ -9,7 +9,6 @@ use utils::auth::Auth;
9
9
  #[storage]
10
10
  pub enum OFTPausableStorage {
11
11
  #[instance(bool)]
12
- #[default(false)]
13
12
  Paused,
14
13
  }
15
14
 
@@ -84,7 +83,7 @@ pub trait OFTPausableInternal {
84
83
  /// * `paused` - `true` to pause, `false` to unpause
85
84
  fn __set_paused(env: &Env, paused: bool) {
86
85
  assert_with_error!(env, Self::__is_paused(env) != paused, OFTPausableError::PauseStatusUnchanged);
87
- OFTPausableStorage::set_paused(env, &paused);
86
+ OFTPausableStorage::set_or_remove_paused(env, if paused { &Some(true) } else { &None });
88
87
  PausedSet { paused }.publish(env);
89
88
  }
90
89
 
@@ -94,6 +93,6 @@ pub trait OFTPausableInternal {
94
93
 
95
94
  /// Returns the paused state of the OFT.
96
95
  fn __is_paused(env: &Env) -> bool {
97
- OFTPausableStorage::paused(env)
96
+ OFTPausableStorage::has_paused(env)
98
97
  }
99
98
  }
@@ -15,12 +15,23 @@ pub enum Direction {
15
15
  Outbound,
16
16
  }
17
17
 
18
+ #[contracttype]
19
+ #[derive(Clone, Debug, Eq, PartialEq)]
20
+ #[repr(u8)]
21
+ pub enum Mode {
22
+ /// Net rate limit: releases decrement in-flight amount
23
+ Net,
24
+ /// Gross rate limit: releases do not affect in-flight amount
25
+ Gross,
26
+ }
27
+
18
28
  /// Configuration for rate limiting, used as input parameter.
19
29
  #[contracttype]
20
30
  #[derive(Clone, Debug, Eq, PartialEq)]
21
31
  pub struct RateLimitConfig {
22
32
  pub limit: i128,
23
33
  pub window_seconds: u64,
34
+ pub mode: Mode,
24
35
  }
25
36
 
26
37
  /// Internal storage struct for rate limit state.
@@ -30,6 +41,7 @@ pub struct RateLimitConfig {
30
41
  /// - `config.window_seconds` defines how long it takes for the bucket to fully drain
31
42
  /// - Tokens decay linearly over time: `decay = elapsed_time * limit / window_seconds`
32
43
  /// - Current in-flight = `in_flight_on_last_update - decay` (clamped to 0)
44
+ /// - `config.mode` determines whether releases affect in-flight (Net) or not (Gross)
33
45
  #[contracttype]
34
46
  #[derive(Clone, Debug, Eq, PartialEq)]
35
47
  struct RateLimitState {
@@ -98,7 +110,7 @@ pub trait RateLimiter: RateLimiterInternal + Auth {
98
110
  env: &soroban_sdk::Env,
99
111
  direction: &oft::rate_limiter::Direction,
100
112
  eid: u32,
101
- config: Option<oft::rate_limiter::RateLimitConfig>,
113
+ config: &Option<oft::rate_limiter::RateLimitConfig>,
102
114
  ) {
103
115
  Self::__set_rate_limit(env, direction, eid, config);
104
116
  }
@@ -163,6 +175,7 @@ pub trait RateLimiterInternal {
163
175
 
164
176
  /// Releases the specified amount back to the rate limit capacity.
165
177
  /// Used internally by `__credit` to release outbound capacity on inbound messages.
178
+ /// Only releases for Net mode; Gross mode ignores releases.
166
179
  fn __release_rate_limit_capacity(env: &Env, direction: &Direction, eid: u32, amount: i128) {
167
180
  assert_with_error!(env, amount >= 0, RateLimitError::InvalidAmount);
168
181
 
@@ -170,6 +183,11 @@ pub trait RateLimiterInternal {
170
183
  return;
171
184
  };
172
185
 
186
+ // Gross mode does not release capacity
187
+ if state.config.mode == Mode::Gross {
188
+ return;
189
+ }
190
+
173
191
  // Apply decay and update timestamp
174
192
  let in_flight = calculate_decayed_in_flight(env, &state);
175
193
  state.in_flight_on_last_update = (in_flight - amount).max(0);
@@ -188,10 +206,10 @@ pub trait RateLimiterInternal {
188
206
  /// * `direction` - The direction (Inbound or Outbound)
189
207
  /// * `eid` - The endpoint ID
190
208
  /// * `config` - The rate limit configuration, or None to remove the rate limit
191
- fn __set_rate_limit(env: &Env, direction: &Direction, eid: u32, config: Option<RateLimitConfig>) {
209
+ fn __set_rate_limit(env: &Env, direction: &Direction, eid: u32, config: &Option<RateLimitConfig>) {
192
210
  let current_state = RateLimitStorage::rate_limit(env, direction, eid);
193
211
  let current_config = current_state.as_ref().map(|s| s.config.clone());
194
- assert_with_error!(env, current_config != config, RateLimitError::SameValue);
212
+ assert_with_error!(env, current_config != *config, RateLimitError::SameValue);
195
213
 
196
214
  match config {
197
215
  Some(cfg) => {
@@ -213,13 +231,12 @@ pub trait RateLimiterInternal {
213
231
  };
214
232
 
215
233
  RateLimitStorage::set_rate_limit(env, direction, eid, &state);
216
- RateLimitSet { direction: direction.clone(), eid, config: Some(cfg) }.publish(env);
217
234
  }
218
235
  None => {
219
236
  RateLimitStorage::remove_rate_limit(env, direction, eid);
220
- RateLimitSet { direction: direction.clone(), eid, config: None }.publish(env);
221
237
  }
222
238
  }
239
+ RateLimitSet { direction: direction.clone(), eid, config: config.clone() }.publish(env);
223
240
  }
224
241
 
225
242
  // =========================================================================
@@ -0,0 +1,18 @@
1
+ //! MintBurnable trait - the interface the OFT uses to mint/burn tokens.
2
+ //!
3
+ //! Any contract that implements `mint` and `burn` can serve as a mintburnable
4
+
5
+ use soroban_sdk::{contractclient, Address, Env};
6
+
7
+ /// The mint/burn interface for OFT MintBurn operations.
8
+ ///
9
+ /// Both a raw Stellar Asset Contract (SAC) and a token wrapper satisfy this
10
+ /// interface, allowing the OFT to be agnostic about the underlying implementation.
11
+ #[contractclient(name = "MintBurnableClient")]
12
+ pub trait MintBurnable {
13
+ /// Mints `amount` tokens to `to`.
14
+ fn mint(env: &Env, to: &Address, amount: i128);
15
+
16
+ /// Burns `amount` tokens from `from`.
17
+ fn burn(env: &Env, from: &Address, amount: i128);
18
+ }
@@ -0,0 +1,3 @@
1
+ mod mint_burnable;
2
+
3
+ pub use mint_burnable::*;
@@ -1,9 +1,11 @@
1
1
  #![no_std]
2
2
 
3
- pub mod extensions;
4
- pub mod oft_types;
3
+ mod extensions;
4
+ mod interfaces;
5
+ mod oft_types;
5
6
 
6
7
  pub use extensions::*;
8
+ pub use interfaces::*;
7
9
  pub use oft_types::*;
8
10
 
9
11
  cfg_if::cfg_if! {
@@ -10,12 +10,10 @@ use crate::{
10
10
  use common_macros::{contract_impl, storage};
11
11
  use oapp_macros::oapp;
12
12
  use oft_core::{
13
- errors::OFTError,
14
- impl_oft_lz_receive,
15
- types::{OFTFeeDetail, OFTLimit, OFTReceipt, SendParam},
16
- utils as oft_utils, OFTCore, OFTInternal,
13
+ impl_oft_lz_receive, utils as oft_utils, OFTCore, OFTError, OFTFeeDetail, OFTInternal, OFTLimit, OFTReceipt,
14
+ SendParam,
17
15
  };
18
- use soroban_sdk::{assert_with_error, vec, Address, Bytes, Env, Vec};
16
+ use soroban_sdk::{assert_with_error, Address, Bytes, Env, Vec};
19
17
 
20
18
  // =========================================================================
21
19
  // Storage
@@ -42,17 +40,17 @@ impl OFT {
42
40
  pub fn __constructor(
43
41
  env: &Env,
44
42
  token: &Address,
45
- owner: &Address,
46
- endpoint: &Address,
47
- delegate: &Option<Address>,
48
43
  shared_decimals: u32,
49
44
  oft_type: OftType,
45
+ owner: &Address,
46
+ endpoint: &Address,
47
+ delegate: &Address,
50
48
  ) {
51
- Self::__initialize_oft(env, owner, token, endpoint, delegate, shared_decimals);
49
+ Self::__initialize_oft(env, token, shared_decimals, owner, endpoint, delegate);
52
50
  OFTStorage::set_oft_type(env, &oft_type);
53
51
  }
54
52
 
55
- /// Returns the type of operation for this OFT (LockUnlock or MintBurn)
53
+ /// Returns the OFT type with its target address and configuration.
56
54
  pub fn oft_type(env: &Env) -> OftType {
57
55
  OFTStorage::oft_type(env).unwrap()
58
56
  }
@@ -61,22 +59,19 @@ impl OFT {
61
59
  /// OFTCore trait implementation for standard OFT with extensions
62
60
  #[contract_impl(contracttrait)]
63
61
  impl OFTCore for OFT {
64
- fn quote_oft(env: &Env, send_param: &SendParam) -> (OFTLimit, Vec<OFTFeeDetail>, OFTReceipt) {
65
- let (_, _, oft_receipt) = Self::__quote_oft(env, send_param);
62
+ fn quote_oft(env: &Env, from: &Address, send_param: &SendParam) -> (OFTLimit, Vec<OFTFeeDetail>, OFTReceipt) {
63
+ let (mut limit, mut fee_details, receipt) = Self::__quote_oft(env, from, send_param);
66
64
 
67
65
  // fee details (only include if there's an actual fee)
68
- let fee_amount_ld = oft_receipt.amount_sent_ld - oft_receipt.amount_received_ld;
69
- let fee_details = if fee_amount_ld > 0 {
70
- vec![env, OFTFeeDetail { fee_amount_ld, description: Bytes::from_slice(env, b"OFT Fee") }]
71
- } else {
72
- vec![env]
66
+ if receipt.amount_sent_ld > receipt.amount_received_ld {
67
+ let fee_amount_ld = receipt.amount_sent_ld - receipt.amount_received_ld;
68
+ fee_details.push_back(OFTFeeDetail { fee_amount_ld, description: Bytes::from_slice(env, b"OFT Fee") });
73
69
  };
74
70
 
75
71
  // rate limit capacity
76
- let capacity = Self::rate_limit_capacity(env, &Direction::Outbound, send_param.dst_eid);
77
- let oft_limit = OFTLimit { min_amount_ld: 0, max_amount_ld: capacity };
72
+ limit.max_amount_ld = Self::rate_limit_capacity(env, &Direction::Outbound, send_param.dst_eid);
78
73
 
79
- (oft_limit, fee_details, oft_receipt)
74
+ (limit, fee_details, receipt)
80
75
  }
81
76
  }
82
77
 
@@ -85,9 +80,9 @@ impl OFTInternal for OFT {
85
80
  /// Overrides default to add pausable check and fee calculation.
86
81
  ///
87
82
  /// Dust handling (consistent with EVM):
88
- /// - fee = 0: dust stays with sender
89
- /// - fee > 0: dust goes to fee recipient (included in charged fee)
90
- fn __debit_view(env: &Env, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
83
+ /// - fee = 0: dust stays with sender (amount_sent_ld has dust removed)
84
+ /// - fee > 0: dust is absorbed into the charged fee (amount_sent_ld is the full amount)
85
+ fn __debit_view(env: &Env, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> (i128, i128) {
91
86
  Self::__assert_not_paused(env);
92
87
 
93
88
  let conversion_rate = Self::__decimal_conversion_rate(env);
@@ -99,32 +94,36 @@ impl OFTInternal for OFT {
99
94
  (amount_sent_ld, amount_sent_ld)
100
95
  } else {
101
96
  // With fee: match EVM OFTFee behavior
102
- // - dust is included in charged fee (goes to fee recipient)
97
+ // - sender pays full amount_ld (no dust removed), dust is absorbed into the charged fee
103
98
  let amount_received_ld = oft_utils::remove_dust(amount_ld - fee, conversion_rate);
104
99
  (amount_ld, amount_received_ld)
105
100
  };
106
101
 
107
102
  assert_with_error!(env, amount_received_ld >= min_amount_ld, OFTError::SlippageExceeded);
108
103
 
109
- OFTReceipt { amount_sent_ld, amount_received_ld }
104
+ (amount_sent_ld, amount_received_ld)
110
105
  }
111
106
 
112
- fn __debit(env: &Env, sender: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> OFTReceipt {
107
+ fn __debit(env: &Env, sender: &Address, amount_ld: i128, min_amount_ld: i128, dst_eid: u32) -> (i128, i128) {
113
108
  // Core debit logic (based on oft_type)
114
- let oft_receipt = match Self::oft_type(env) {
115
- OftType::LockUnlock => lock_unlock::debit::<Self>(env, sender, amount_ld, min_amount_ld, dst_eid),
116
- OftType::MintBurn => mint_burn::debit::<Self>(env, sender, amount_ld, min_amount_ld, dst_eid),
109
+ let (amount_sent_ld, amount_received_ld) = match Self::oft_type(env) {
110
+ OftType::LockUnlock => {
111
+ lock_unlock::debit::<Self>(env, &Self::token(env), sender, amount_ld, min_amount_ld, dst_eid)
112
+ }
113
+ OftType::MintBurn(mint_burnable) => {
114
+ mint_burn::debit::<Self>(env, &mint_burnable, sender, amount_ld, min_amount_ld, dst_eid)
115
+ }
117
116
  };
118
117
 
119
118
  // Rate limit checks (using amount_received_ld - the actual cross-chain amount)
120
- Self::__consume_rate_limit_capacity(env, &Direction::Outbound, dst_eid, oft_receipt.amount_received_ld);
121
- Self::__release_rate_limit_capacity(env, &Direction::Inbound, dst_eid, oft_receipt.amount_received_ld);
119
+ Self::__consume_rate_limit_capacity(env, &Direction::Outbound, dst_eid, amount_received_ld);
120
+ Self::__release_rate_limit_capacity(env, &Direction::Inbound, dst_eid, amount_received_ld);
122
121
 
123
122
  // Charge fee
124
- let fee = oft_receipt.amount_sent_ld - oft_receipt.amount_received_ld;
125
- Self::__charge_fee(env, &Self::__token(env), sender, fee);
123
+ let fee = amount_sent_ld - amount_received_ld;
124
+ Self::__charge_fee(env, &Self::token(env), sender, fee);
126
125
 
127
- oft_receipt
126
+ (amount_sent_ld, amount_received_ld)
128
127
  }
129
128
 
130
129
  fn __credit(env: &Env, to: &Address, amount_ld: i128, src_eid: u32) -> i128 {
@@ -133,8 +132,8 @@ impl OFTInternal for OFT {
133
132
 
134
133
  // Core credit logic (based on mode)
135
134
  let amount_credited = match Self::oft_type(env) {
136
- OftType::LockUnlock => lock_unlock::credit::<Self>(env, to, amount_ld, src_eid),
137
- OftType::MintBurn => mint_burn::credit::<Self>(env, to, amount_ld, src_eid),
135
+ OftType::LockUnlock => lock_unlock::credit::<Self>(env, &Self::token(env), to, amount_ld, src_eid),
136
+ OftType::MintBurn(mint_burnable) => mint_burn::credit::<Self>(env, &mint_burnable, to, amount_ld, src_eid),
138
137
  };
139
138
 
140
139
  // Rate limit checks
@@ -2,45 +2,49 @@
2
2
  //!
3
3
  //! This OFT type locks tokens in the contract on debit (send) and unlocks
4
4
  //! tokens from the contract on credit (receive).
5
- //! Used for OFT adapters that wrap existing tokens.
5
+ //! Operates directly on the token via standard SEP-41 `transfer`.
6
6
 
7
- use oft_core::{types::OFTReceipt, OFTCore};
7
+ use oft_core::OFTCore;
8
8
  use soroban_sdk::{token::TokenClient, Address, Env};
9
9
 
10
10
  /// Debit tokens using LockUnlock OFT type (locks tokens in contract).
11
11
  ///
12
12
  /// # Parameters
13
13
  /// * `env` - The Soroban environment
14
+ /// * `token` - Address of the token
14
15
  /// * `sender` - Address of the token sender
15
16
  /// * `amount_ld` - Amount to debit in local decimals
16
17
  /// * `min_amount_ld` - Minimum amount that must be received (for slippage protection)
17
18
  /// * `dst_eid` - Destination endpoint ID
18
19
  ///
19
20
  /// # Returns
20
- /// `OFTReceipt` containing the amount sent and amount received
21
+ /// * `amount_sent_ld` - The amount sent in local decimals
22
+ /// * `amount_received_ld` - The amount received in local decimals on the remote
21
23
  pub fn debit<T: OFTCore>(
22
24
  env: &Env,
25
+ token: &Address,
23
26
  sender: &Address,
24
27
  amount_ld: i128,
25
28
  min_amount_ld: i128,
26
29
  dst_eid: u32,
27
- ) -> OFTReceipt {
28
- let receipt: OFTReceipt = T::__debit_view(env, amount_ld, min_amount_ld, dst_eid);
29
- TokenClient::new(env, &T::token(env)).transfer(sender, env.current_contract_address(), &receipt.amount_received_ld);
30
- receipt
30
+ ) -> (i128, i128) {
31
+ let (amount_sent_ld, amount_received_ld) = T::__debit_view(env, amount_ld, min_amount_ld, dst_eid);
32
+ TokenClient::new(env, token).transfer(sender, env.current_contract_address(), &amount_received_ld);
33
+ (amount_sent_ld, amount_received_ld)
31
34
  }
32
35
 
33
36
  /// Credit tokens using LockUnlock OFT type (unlocks tokens from contract).
34
37
  ///
35
38
  /// # Parameters
36
39
  /// * `env` - The Soroban environment
40
+ /// * `token` - Address of the token
37
41
  /// * `to` - Address of the token recipient
38
42
  /// * `amount_ld` - Amount to credit in local decimals
39
43
  /// * `_src_eid` - Source endpoint ID (unused)
40
44
  ///
41
45
  /// # Returns
42
46
  /// The amount credited
43
- pub fn credit<T: OFTCore>(env: &Env, to: &Address, amount_ld: i128, _src_eid: u32) -> i128 {
44
- TokenClient::new(env, &T::token(env)).transfer(&env.current_contract_address(), to, &amount_ld);
47
+ pub fn credit<T: OFTCore>(env: &Env, token: &Address, to: &Address, amount_ld: i128, _src_eid: u32) -> i128 {
48
+ TokenClient::new(env, token).transfer(&env.current_contract_address(), to, &amount_ld);
45
49
  amount_ld
46
50
  }