@layerzerolabs/oft-mint-burn-starknet 0.2.9
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/Scarb.lock +190 -0
- package/Scarb.toml +3 -0
- package/contracts/oft_mint_burn/Scarb.toml +29 -0
- package/contracts/oft_mint_burn/src/constants.cairo +11 -0
- package/contracts/oft_mint_burn/src/erc20_mint_burn_upgradeable/constants.cairo +17 -0
- package/contracts/oft_mint_burn/src/erc20_mint_burn_upgradeable/erc20_mint_burn_upgradeable.cairo +309 -0
- package/contracts/oft_mint_burn/src/erc20_mint_burn_upgradeable/interface.cairo +86 -0
- package/contracts/oft_mint_burn/src/errors.cairo +39 -0
- package/contracts/oft_mint_burn/src/interface.cairo +95 -0
- package/contracts/oft_mint_burn/src/lib.cairo +10 -0
- package/contracts/oft_mint_burn/src/oft_mint_burn_adapter.cairo +382 -0
- package/contracts/oft_mint_burn/tests/fuzzable/contract_address.cairo +21 -0
- package/contracts/oft_mint_burn/tests/lib.cairo +8 -0
- package/contracts/oft_mint_burn/tests/test_erc20_mint_burn_upgradeable.cairo +621 -0
- package/contracts/oft_mint_burn/tests/test_mint_burn_adapter.cairo +194 -0
- package/contracts/oft_mint_burn/tests/test_mint_burn_adapter_advanced.cairo +746 -0
- package/contracts/oft_mint_burn/tests/utils.cairo +84 -0
- package/dist/3UUTAAI4.js +9 -0
- package/dist/3UUTAAI4.js.map +1 -0
- package/dist/4EGWSIX7.js +18 -0
- package/dist/4EGWSIX7.js.map +1 -0
- package/dist/54KHZH3Y.cjs +22 -0
- package/dist/54KHZH3Y.cjs.map +1 -0
- package/dist/5KFUKPR2.js +1033 -0
- package/dist/5KFUKPR2.js.map +1 -0
- package/dist/5R6WMZVR.cjs +22 -0
- package/dist/5R6WMZVR.cjs.map +1 -0
- package/dist/CCHLUK5E.cjs +1035 -0
- package/dist/CCHLUK5E.cjs.map +1 -0
- package/dist/CGU4EJTF.cjs +14 -0
- package/dist/CGU4EJTF.cjs.map +1 -0
- package/dist/DJIRVXJ3.cjs +1914 -0
- package/dist/DJIRVXJ3.cjs.map +1 -0
- package/dist/E63KEOR5.cjs +11 -0
- package/dist/E63KEOR5.cjs.map +1 -0
- package/dist/FL52ASKH.cjs +16 -0
- package/dist/FL52ASKH.cjs.map +1 -0
- package/dist/GZXOKWQY.js +1303 -0
- package/dist/GZXOKWQY.js.map +1 -0
- package/dist/IQR2DIUY.cjs +1305 -0
- package/dist/IQR2DIUY.cjs.map +1 -0
- package/dist/MNNO3GDF.js +14 -0
- package/dist/MNNO3GDF.js.map +1 -0
- package/dist/NZRVZWHQ.js +1912 -0
- package/dist/NZRVZWHQ.js.map +1 -0
- package/dist/QYG4SI7W.js +18 -0
- package/dist/QYG4SI7W.js.map +1 -0
- package/dist/UE6XWQTX.js +12 -0
- package/dist/UE6XWQTX.js.map +1 -0
- package/dist/generated/abi/e-r-c20-mint-burn-upgradeable.cjs +13 -0
- package/dist/generated/abi/e-r-c20-mint-burn-upgradeable.cjs.map +1 -0
- package/dist/generated/abi/e-r-c20-mint-burn-upgradeable.d.ts +760 -0
- package/dist/generated/abi/e-r-c20-mint-burn-upgradeable.d.ts.map +1 -0
- package/dist/generated/abi/e-r-c20-mint-burn-upgradeable.js +4 -0
- package/dist/generated/abi/e-r-c20-mint-burn-upgradeable.js.map +1 -0
- package/dist/generated/abi/o-f-t-mint-burn-adapter.cjs +13 -0
- package/dist/generated/abi/o-f-t-mint-burn-adapter.cjs.map +1 -0
- package/dist/generated/abi/o-f-t-mint-burn-adapter.d.ts +1408 -0
- package/dist/generated/abi/o-f-t-mint-burn-adapter.d.ts.map +1 -0
- package/dist/generated/abi/o-f-t-mint-burn-adapter.js +4 -0
- package/dist/generated/abi/o-f-t-mint-burn-adapter.js.map +1 -0
- package/dist/generated/abi.cjs +19 -0
- package/dist/generated/abi.cjs.map +1 -0
- package/dist/generated/abi.d.ts +3 -0
- package/dist/generated/abi.d.ts.map +1 -0
- package/dist/generated/abi.js +6 -0
- package/dist/generated/abi.js.map +1 -0
- package/dist/generated/casm.cjs +17 -0
- package/dist/generated/casm.cjs.map +1 -0
- package/dist/generated/casm.d.ts +3 -0
- package/dist/generated/casm.d.ts.map +1 -0
- package/dist/generated/casm.js +4 -0
- package/dist/generated/casm.js.map +1 -0
- package/dist/generated/sierra.cjs +17 -0
- package/dist/generated/sierra.cjs.map +1 -0
- package/dist/generated/sierra.d.ts +3 -0
- package/dist/generated/sierra.d.ts.map +1 -0
- package/dist/generated/sierra.js +4 -0
- package/dist/generated/sierra.js.map +1 -0
- package/dist/generated/verification/index.cjs +14 -0
- package/dist/generated/verification/index.cjs.map +1 -0
- package/dist/generated/verification/index.d.ts +2 -0
- package/dist/generated/verification/index.d.ts.map +1 -0
- package/dist/generated/verification/index.js +5 -0
- package/dist/generated/verification/index.js.map +1 -0
- package/dist/generated/verification/oft_mint_burn.cjs +10 -0
- package/dist/generated/verification/oft_mint_burn.cjs.map +1 -0
- package/dist/generated/verification/oft_mint_burn.d.ts +4 -0
- package/dist/generated/verification/oft_mint_burn.d.ts.map +1 -0
- package/dist/generated/verification/oft_mint_burn.js +4 -0
- package/dist/generated/verification/oft_mint_burn.js.map +1 -0
- package/dist/index.cjs +31 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
use layerzero::oapps::common::rate_limiter::structs::{
|
|
2
|
+
RateLimitConfig, RateLimitDirection, RateLimitEnabled,
|
|
3
|
+
};
|
|
4
|
+
use starknet::{ClassHash, ContractAddress};
|
|
5
|
+
|
|
6
|
+
#[starknet::interface]
|
|
7
|
+
pub trait IMintableToken<TContractState> {
|
|
8
|
+
fn permissioned_mint(ref self: TContractState, account: ContractAddress, amount: u256);
|
|
9
|
+
fn permissioned_burn(ref self: TContractState, account: ContractAddress, amount: u256);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
#[starknet::interface]
|
|
13
|
+
pub trait IOFTMintBurnAdapter<TContractState> {
|
|
14
|
+
// =============================== View ===============================
|
|
15
|
+
|
|
16
|
+
/// Gets the fee balance
|
|
17
|
+
///
|
|
18
|
+
/// # Returns
|
|
19
|
+
///
|
|
20
|
+
/// * `u256` - The fee balance
|
|
21
|
+
fn fee_balance(self: @TContractState) -> u256;
|
|
22
|
+
|
|
23
|
+
/// Gets the minter burner contract address
|
|
24
|
+
///
|
|
25
|
+
/// # Returns
|
|
26
|
+
///
|
|
27
|
+
/// * `ContractAddress` - The minter burner contract address
|
|
28
|
+
fn get_minter_burner(self: @TContractState) -> ContractAddress;
|
|
29
|
+
|
|
30
|
+
// =============================== Only Owner or Role ===============================
|
|
31
|
+
|
|
32
|
+
/// Sets the rate limits
|
|
33
|
+
///
|
|
34
|
+
/// # Arguments
|
|
35
|
+
/// * `rate_limits` - The rate limits
|
|
36
|
+
/// * `direction` - The rate limit direction
|
|
37
|
+
fn set_rate_limits(
|
|
38
|
+
ref self: TContractState,
|
|
39
|
+
rate_limits: Array<RateLimitConfig>,
|
|
40
|
+
direction: RateLimitDirection,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
/// Sets the rate limits enabled
|
|
44
|
+
///
|
|
45
|
+
/// # Arguments
|
|
46
|
+
/// * `enabled` - The rate limits enabled
|
|
47
|
+
fn set_rate_limits_enabled(ref self: TContractState, enabled: RateLimitEnabled);
|
|
48
|
+
|
|
49
|
+
/// Resets the rate limits for the given endpoint IDs.
|
|
50
|
+
///
|
|
51
|
+
/// This sets `amount_in_flight` to zero for both outbound and inbound directions.
|
|
52
|
+
/// Useful when limit thresholds are reduced below current amount in flight, or for
|
|
53
|
+
/// emergency recovery.
|
|
54
|
+
///
|
|
55
|
+
/// # Arguments
|
|
56
|
+
/// * `eids` - The endpoint IDs to reset rate limits for
|
|
57
|
+
fn reset_rate_limits(ref self: TContractState, eids: Array<u32>);
|
|
58
|
+
|
|
59
|
+
/// Withdraws the fees
|
|
60
|
+
///
|
|
61
|
+
/// # Arguments
|
|
62
|
+
/// * `to` - The address to withdraw the fees to
|
|
63
|
+
fn withdraw_fees(ref self: TContractState, to: ContractAddress);
|
|
64
|
+
|
|
65
|
+
// =============================== Upgrade ===============================
|
|
66
|
+
|
|
67
|
+
/// Upgrades the contract
|
|
68
|
+
///
|
|
69
|
+
/// # Arguments
|
|
70
|
+
/// * `new_class_hash` - The new class hash to upgrade to
|
|
71
|
+
fn upgrade(ref self: TContractState, new_class_hash: ClassHash);
|
|
72
|
+
|
|
73
|
+
/// Upgrades the contract and calls a function
|
|
74
|
+
///
|
|
75
|
+
/// # Arguments
|
|
76
|
+
/// * `new_class_hash` - The new class hash to upgrade to
|
|
77
|
+
/// * `selector` - The selector to call
|
|
78
|
+
/// * `calldata` - The calldata to pass to the function
|
|
79
|
+
///
|
|
80
|
+
/// # Returns
|
|
81
|
+
fn upgrade_and_call(
|
|
82
|
+
ref self: TContractState,
|
|
83
|
+
new_class_hash: ClassHash,
|
|
84
|
+
selector: felt252,
|
|
85
|
+
calldata: Span<felt252>,
|
|
86
|
+
) -> Span<felt252>;
|
|
87
|
+
|
|
88
|
+
// =============================== Pause ===============================
|
|
89
|
+
|
|
90
|
+
/// Pauses the contract
|
|
91
|
+
fn pause(ref self: TContractState);
|
|
92
|
+
|
|
93
|
+
/// Unpauses the contract
|
|
94
|
+
fn unpause(ref self: TContractState);
|
|
95
|
+
}
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/// A mint/burn OFT adapter with fees, rate limiting, and role-based access control.
|
|
2
|
+
///
|
|
3
|
+
/// This contract should be used to mint and burn tokens for ERC-20 token contracts
|
|
4
|
+
/// that implement the IMintableBurnable interface.
|
|
5
|
+
#[starknet::contract]
|
|
6
|
+
pub mod OFTMintBurnAdapter {
|
|
7
|
+
use core::num::traits::Zero;
|
|
8
|
+
use layerzero::common::constants::DEAD_ADDRESS;
|
|
9
|
+
use layerzero::oapps::common::fee::fee::{FeeComponent, FeeHooksDefaultImpl};
|
|
10
|
+
use layerzero::oapps::common::oapp_options_type_3::oapp_options_type_3::OAppOptionsType3Component;
|
|
11
|
+
use layerzero::oapps::common::rate_limiter::rate_limiter::{
|
|
12
|
+
RateLimiterComponent, RateLimiterHooksDefaultImpl,
|
|
13
|
+
};
|
|
14
|
+
use layerzero::oapps::common::rate_limiter::structs::{
|
|
15
|
+
RateLimitConfig, RateLimitDirection, RateLimitEnabled,
|
|
16
|
+
};
|
|
17
|
+
use layerzero::oapps::oapp::oapp_core::OAppCoreComponent;
|
|
18
|
+
use layerzero::oapps::oft::errors::err_slippage_exceeded;
|
|
19
|
+
use layerzero::oapps::oft::oft_core::default_oapp_hooks::OFTCoreOAppHooksDefaultImpl;
|
|
20
|
+
use layerzero::oapps::oft::oft_core::oft_core::OFTCoreComponent;
|
|
21
|
+
use layerzero::oapps::oft::structs::OFTDebit;
|
|
22
|
+
use lz_utils::error::assert_with_byte_array;
|
|
23
|
+
use openzeppelin::access::accesscontrol::AccessControlComponent;
|
|
24
|
+
|
|
25
|
+
// Access control roles
|
|
26
|
+
pub use openzeppelin::access::accesscontrol::AccessControlComponent::DEFAULT_ADMIN_ROLE;
|
|
27
|
+
use openzeppelin::access::ownable::OwnableComponent;
|
|
28
|
+
use openzeppelin::introspection::src5::SRC5Component;
|
|
29
|
+
use openzeppelin::security::pausable::PausableComponent;
|
|
30
|
+
use openzeppelin::token::erc20::interface::{
|
|
31
|
+
IERC20Dispatcher, IERC20DispatcherTrait, IERC20MetadataDispatcher,
|
|
32
|
+
IERC20MetadataDispatcherTrait,
|
|
33
|
+
};
|
|
34
|
+
use openzeppelin::upgrades::UpgradeableComponent;
|
|
35
|
+
use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
|
|
36
|
+
use starknet::{ClassHash, ContractAddress, get_contract_address};
|
|
37
|
+
use crate::constants::{
|
|
38
|
+
FEE_MANAGER_ROLE, PAUSE_MANAGER_ROLE, RATE_LIMITER_MANAGER_ROLE, UPGRADE_MANAGER_ROLE,
|
|
39
|
+
};
|
|
40
|
+
use crate::errors::{
|
|
41
|
+
err_caller_not_owner_or_missing_role, err_no_fees_to_withdraw, err_transfer_failed,
|
|
42
|
+
};
|
|
43
|
+
use crate::interface::{
|
|
44
|
+
IMintableTokenDispatcher, IMintableTokenDispatcherTrait, IOFTMintBurnAdapter,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
// =============================== Components ===============================
|
|
49
|
+
|
|
50
|
+
component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent);
|
|
51
|
+
component!(path: SRC5Component, storage: src5, event: SRC5Event);
|
|
52
|
+
component!(path: PausableComponent, storage: pausable, event: PausableEvent);
|
|
53
|
+
component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
|
|
54
|
+
component!(path: OAppCoreComponent, storage: oapp_core, event: OAppCoreEvent);
|
|
55
|
+
component!(path: OFTCoreComponent, storage: oft_core, event: OFTCoreEvent);
|
|
56
|
+
component!(
|
|
57
|
+
path: OAppOptionsType3Component, storage: oapp_options_type_3, event: OAppOptionsType3Event,
|
|
58
|
+
);
|
|
59
|
+
component!(path: FeeComponent, storage: fee, event: FeeEvent);
|
|
60
|
+
component!(path: RateLimiterComponent, storage: rate_limiter, event: RateLimiterEvent);
|
|
61
|
+
component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);
|
|
62
|
+
|
|
63
|
+
// =============================== Impls ===============================
|
|
64
|
+
|
|
65
|
+
#[abi(embed_v0)]
|
|
66
|
+
impl AccessControlImpl =
|
|
67
|
+
AccessControlComponent::AccessControlImpl<ContractState>;
|
|
68
|
+
impl AccessControlInternalImpl = AccessControlComponent::InternalImpl<ContractState>;
|
|
69
|
+
|
|
70
|
+
#[abi(embed_v0)]
|
|
71
|
+
impl PausableImpl = PausableComponent::PausableImpl<ContractState>;
|
|
72
|
+
impl PausableInternalImpl = PausableComponent::InternalImpl<ContractState>;
|
|
73
|
+
|
|
74
|
+
#[abi(embed_v0)]
|
|
75
|
+
impl OwnableMixinImpl = OwnableComponent::OwnableMixinImpl<ContractState>;
|
|
76
|
+
impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>;
|
|
77
|
+
|
|
78
|
+
#[abi(embed_v0)]
|
|
79
|
+
impl OAppCoreImpl = OAppCoreComponent::OAppCoreImpl<ContractState>;
|
|
80
|
+
impl OAppCoreInternalImpl = OAppCoreComponent::InternalImpl<ContractState>;
|
|
81
|
+
|
|
82
|
+
#[abi(embed_v0)]
|
|
83
|
+
impl IOAppReceiverImpl = OAppCoreComponent::OAppReceiverImpl<ContractState>;
|
|
84
|
+
#[abi(embed_v0)]
|
|
85
|
+
impl ILayerZeroReceiverImpl =
|
|
86
|
+
OAppCoreComponent::LayerZeroReceiverImpl<ContractState>;
|
|
87
|
+
|
|
88
|
+
#[abi(embed_v0)]
|
|
89
|
+
impl OFTCoreImpl = OFTCoreComponent::OFTCoreImpl<ContractState>;
|
|
90
|
+
impl OFTCoreInternalImpl = OFTCoreComponent::InternalImpl<ContractState>;
|
|
91
|
+
|
|
92
|
+
#[abi(embed_v0)]
|
|
93
|
+
impl OAppOptionsType3Impl =
|
|
94
|
+
OAppOptionsType3Component::OAppOptionsType3Impl<ContractState>;
|
|
95
|
+
impl OAppOptionsType3InternalImpl = OAppOptionsType3Component::InternalImpl<ContractState>;
|
|
96
|
+
|
|
97
|
+
#[abi(embed_v0)]
|
|
98
|
+
impl FeeImpl = FeeComponent::FeeImpl<ContractState>;
|
|
99
|
+
impl FeeInternalImpl = FeeComponent::InternalImpl<ContractState>;
|
|
100
|
+
|
|
101
|
+
#[abi(embed_v0)]
|
|
102
|
+
impl RateLimiterImpl = RateLimiterComponent::RateLimiterImpl<ContractState>;
|
|
103
|
+
|
|
104
|
+
impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl<ContractState>;
|
|
105
|
+
|
|
106
|
+
// =============================== Storage ===============================
|
|
107
|
+
|
|
108
|
+
#[storage]
|
|
109
|
+
struct Storage {
|
|
110
|
+
erc20_token: ContractAddress,
|
|
111
|
+
minter_burner: ContractAddress,
|
|
112
|
+
fee_balance: u256,
|
|
113
|
+
#[substorage(v0)]
|
|
114
|
+
access_control: AccessControlComponent::Storage,
|
|
115
|
+
#[substorage(v0)]
|
|
116
|
+
src5: SRC5Component::Storage,
|
|
117
|
+
#[substorage(v0)]
|
|
118
|
+
pausable: PausableComponent::Storage,
|
|
119
|
+
#[substorage(v0)]
|
|
120
|
+
ownable: OwnableComponent::Storage,
|
|
121
|
+
#[substorage(v0)]
|
|
122
|
+
oapp_core: OAppCoreComponent::Storage,
|
|
123
|
+
#[substorage(v0)]
|
|
124
|
+
oft_core: OFTCoreComponent::Storage,
|
|
125
|
+
#[substorage(v0)]
|
|
126
|
+
oapp_options_type_3: OAppOptionsType3Component::Storage,
|
|
127
|
+
#[substorage(v0)]
|
|
128
|
+
fee: FeeComponent::Storage,
|
|
129
|
+
#[substorage(v0)]
|
|
130
|
+
rate_limiter: RateLimiterComponent::Storage,
|
|
131
|
+
#[substorage(v0)]
|
|
132
|
+
upgradeable: UpgradeableComponent::Storage,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// =============================== Events ===============================
|
|
136
|
+
|
|
137
|
+
#[event]
|
|
138
|
+
#[derive(Drop, starknet::Event)]
|
|
139
|
+
pub enum Event {
|
|
140
|
+
FeeWithdrawn: FeeWithdrawn,
|
|
141
|
+
#[flat]
|
|
142
|
+
AccessControlEvent: AccessControlComponent::Event,
|
|
143
|
+
#[flat]
|
|
144
|
+
SRC5Event: SRC5Component::Event,
|
|
145
|
+
#[flat]
|
|
146
|
+
PausableEvent: PausableComponent::Event,
|
|
147
|
+
#[flat]
|
|
148
|
+
OwnableEvent: OwnableComponent::Event,
|
|
149
|
+
#[flat]
|
|
150
|
+
OAppCoreEvent: OAppCoreComponent::Event,
|
|
151
|
+
#[flat]
|
|
152
|
+
OFTCoreEvent: OFTCoreComponent::Event,
|
|
153
|
+
#[flat]
|
|
154
|
+
OAppOptionsType3Event: OAppOptionsType3Component::Event,
|
|
155
|
+
#[flat]
|
|
156
|
+
FeeEvent: FeeComponent::Event,
|
|
157
|
+
#[flat]
|
|
158
|
+
RateLimiterEvent: RateLimiterComponent::Event,
|
|
159
|
+
#[flat]
|
|
160
|
+
UpgradeableEvent: UpgradeableComponent::Event,
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
#[derive(Drop, starknet::Event)]
|
|
164
|
+
pub struct FeeWithdrawn {
|
|
165
|
+
pub to: ContractAddress,
|
|
166
|
+
pub amount_ld: u256,
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// =============================== Constructor ===============================
|
|
170
|
+
|
|
171
|
+
#[constructor]
|
|
172
|
+
fn constructor(
|
|
173
|
+
ref self: ContractState,
|
|
174
|
+
erc20_token: ContractAddress,
|
|
175
|
+
minter_burner: ContractAddress,
|
|
176
|
+
lz_endpoint: ContractAddress,
|
|
177
|
+
owner: ContractAddress,
|
|
178
|
+
native_token: ContractAddress,
|
|
179
|
+
shared_decimals: u8,
|
|
180
|
+
) {
|
|
181
|
+
self.erc20_token.write(erc20_token);
|
|
182
|
+
self.minter_burner.write(minter_burner);
|
|
183
|
+
|
|
184
|
+
self.ownable.initializer(owner);
|
|
185
|
+
self.oapp_core.initializer(lz_endpoint, owner, native_token);
|
|
186
|
+
|
|
187
|
+
// Get local decimals from the ERC20 token using ERC20Metadata interface
|
|
188
|
+
let token = IERC20MetadataDispatcher { contract_address: erc20_token };
|
|
189
|
+
let local_decimals = token.decimals();
|
|
190
|
+
self.oft_core.initializer(local_decimals, shared_decimals);
|
|
191
|
+
|
|
192
|
+
// Initialize access control roles - only grant DEFAULT_ADMIN_ROLE to owner
|
|
193
|
+
// Other roles should be granted explicitly after deployment
|
|
194
|
+
self.access_control._grant_role(DEFAULT_ADMIN_ROLE, owner);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
#[abi(embed_v0)]
|
|
198
|
+
impl OFTMintBurnAdapterImpl of IOFTMintBurnAdapter<ContractState> {
|
|
199
|
+
// =============================== View ===============================
|
|
200
|
+
|
|
201
|
+
fn get_minter_burner(self: @ContractState) -> ContractAddress {
|
|
202
|
+
self.minter_burner.read()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
fn fee_balance(self: @ContractState) -> u256 {
|
|
206
|
+
self.fee_balance.read()
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// =============================== Rate Limiter Manager ===============================
|
|
210
|
+
|
|
211
|
+
fn set_rate_limits(
|
|
212
|
+
ref self: ContractState,
|
|
213
|
+
rate_limits: Array<RateLimitConfig>,
|
|
214
|
+
direction: RateLimitDirection,
|
|
215
|
+
) {
|
|
216
|
+
self._assert_owner_or_role(RATE_LIMITER_MANAGER_ROLE);
|
|
217
|
+
self.rate_limiter._set_rate_limits(rate_limits, direction);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
fn set_rate_limits_enabled(ref self: ContractState, enabled: RateLimitEnabled) {
|
|
221
|
+
self._assert_owner_or_role(RATE_LIMITER_MANAGER_ROLE);
|
|
222
|
+
self.rate_limiter._set_rate_limit_enabled(enabled);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
fn reset_rate_limits(ref self: ContractState, eids: Array<u32>) {
|
|
226
|
+
self._assert_owner_or_role(RATE_LIMITER_MANAGER_ROLE);
|
|
227
|
+
self.rate_limiter._reset_rate_limits(eids);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// =============================== Fee Manager ===============================
|
|
231
|
+
|
|
232
|
+
fn withdraw_fees(ref self: ContractState, to: ContractAddress) {
|
|
233
|
+
self._assert_owner_or_role(FEE_MANAGER_ROLE);
|
|
234
|
+
|
|
235
|
+
let fee_balance = self.fee_balance.read();
|
|
236
|
+
assert_with_byte_array(fee_balance > 0, err_no_fees_to_withdraw());
|
|
237
|
+
|
|
238
|
+
self.fee_balance.write(0);
|
|
239
|
+
let token = IERC20Dispatcher { contract_address: self.erc20_token.read() };
|
|
240
|
+
let result = token.transfer(to, fee_balance);
|
|
241
|
+
assert_with_byte_array(result, err_transfer_failed(to, fee_balance));
|
|
242
|
+
|
|
243
|
+
self.emit(FeeWithdrawn { to, amount_ld: fee_balance });
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// =============================== Upgrade Manager ===============================
|
|
247
|
+
|
|
248
|
+
fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
|
|
249
|
+
self._assert_owner_or_role(UPGRADE_MANAGER_ROLE);
|
|
250
|
+
self.upgradeable.upgrade(new_class_hash);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
fn upgrade_and_call(
|
|
254
|
+
ref self: ContractState,
|
|
255
|
+
new_class_hash: ClassHash,
|
|
256
|
+
selector: felt252,
|
|
257
|
+
calldata: Span<felt252>,
|
|
258
|
+
) -> Span<felt252> {
|
|
259
|
+
self._assert_owner_or_role(UPGRADE_MANAGER_ROLE);
|
|
260
|
+
self.upgradeable.upgrade_and_call(new_class_hash, selector, calldata)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// =============================== Pausable ===============================
|
|
264
|
+
|
|
265
|
+
fn pause(ref self: ContractState) {
|
|
266
|
+
self._assert_owner_or_role(PAUSE_MANAGER_ROLE);
|
|
267
|
+
self.pausable.pause();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
fn unpause(ref self: ContractState) {
|
|
271
|
+
self._assert_owner_or_role(PAUSE_MANAGER_ROLE);
|
|
272
|
+
self.pausable.unpause();
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// =============================== Internal Access Control ===============================
|
|
277
|
+
|
|
278
|
+
#[generate_trait]
|
|
279
|
+
pub impl InternalAccessControlImpl of InternalAccessControlTrait {
|
|
280
|
+
fn _assert_owner_or_role(self: @ContractState, role: felt252) {
|
|
281
|
+
let caller = starknet::get_caller_address();
|
|
282
|
+
let is_owner = caller == self.ownable.owner();
|
|
283
|
+
let has_role = self.access_control.has_role(role, caller);
|
|
284
|
+
|
|
285
|
+
assert_with_byte_array(
|
|
286
|
+
is_owner || has_role, err_caller_not_owner_or_missing_role(role),
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// =============================== OFT Hooks ===============================
|
|
292
|
+
|
|
293
|
+
impl OFTHooksImpl of OFTCoreComponent::OFTHooks<ContractState> {
|
|
294
|
+
fn _debit(
|
|
295
|
+
ref self: OFTCoreComponent::ComponentState<ContractState>,
|
|
296
|
+
from: ContractAddress,
|
|
297
|
+
amount: u256,
|
|
298
|
+
min_amount: u256,
|
|
299
|
+
dst_eid: u32,
|
|
300
|
+
) -> OFTDebit {
|
|
301
|
+
// Check contract is not paused
|
|
302
|
+
let contract = self.get_contract();
|
|
303
|
+
contract.pausable.assert_not_paused();
|
|
304
|
+
|
|
305
|
+
let oft_debit = self._debit_view(amount, min_amount, dst_eid);
|
|
306
|
+
let fee = oft_debit.amount_sent_ld - oft_debit.amount_received_ld;
|
|
307
|
+
|
|
308
|
+
let mut contract = self.get_contract_mut();
|
|
309
|
+
contract.rate_limiter._outflow(dst_eid, oft_debit.amount_received_ld);
|
|
310
|
+
|
|
311
|
+
let minter_burner = IMintableTokenDispatcher {
|
|
312
|
+
contract_address: contract.minter_burner.read(),
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
if fee > 0 {
|
|
316
|
+
contract.fee_balance.write(contract.fee_balance.read() + fee);
|
|
317
|
+
minter_burner.permissioned_mint(get_contract_address(), fee);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
minter_burner.permissioned_burn(from, oft_debit.amount_sent_ld);
|
|
321
|
+
|
|
322
|
+
oft_debit
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
fn _debit_view(
|
|
326
|
+
self: @OFTCoreComponent::ComponentState<ContractState>,
|
|
327
|
+
amount_ld: u256,
|
|
328
|
+
min_amount_ld: u256,
|
|
329
|
+
dst_eid: u32,
|
|
330
|
+
) -> OFTDebit {
|
|
331
|
+
// Calculate a fee based on the amount BEFORE the dust is deducted.
|
|
332
|
+
let fee = self.get_contract().fee.get_fee(dst_eid, amount_ld);
|
|
333
|
+
let amount_received_ld = self._remove_dust(amount_ld - fee);
|
|
334
|
+
|
|
335
|
+
assert_with_byte_array(
|
|
336
|
+
amount_received_ld >= min_amount_ld,
|
|
337
|
+
err_slippage_exceeded(amount_received_ld, min_amount_ld),
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
let amount_sent_ld = amount_received_ld + fee;
|
|
341
|
+
|
|
342
|
+
OFTDebit { amount_sent_ld, amount_received_ld }
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
fn _credit(
|
|
346
|
+
ref self: OFTCoreComponent::ComponentState<ContractState>,
|
|
347
|
+
to: ContractAddress,
|
|
348
|
+
amount: u256,
|
|
349
|
+
src_eid: u32,
|
|
350
|
+
) -> u256 {
|
|
351
|
+
// Check contract is not paused
|
|
352
|
+
let contract = self.get_contract();
|
|
353
|
+
contract.pausable.assert_not_paused();
|
|
354
|
+
|
|
355
|
+
// Handle the zero address case
|
|
356
|
+
let to = if to.is_zero() {
|
|
357
|
+
DEAD_ADDRESS
|
|
358
|
+
} else {
|
|
359
|
+
to
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
let mut contract = self.get_contract_mut();
|
|
363
|
+
contract.rate_limiter._inflow(src_eid, amount);
|
|
364
|
+
|
|
365
|
+
let minter_burner = IMintableTokenDispatcher {
|
|
366
|
+
contract_address: contract.minter_burner.read(),
|
|
367
|
+
};
|
|
368
|
+
minter_burner.permissioned_mint(to, amount);
|
|
369
|
+
|
|
370
|
+
amount
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
fn _token(self: @OFTCoreComponent::ComponentState<ContractState>) -> ContractAddress {
|
|
374
|
+
self.get_contract().erc20_token.read()
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
fn _approval_required(self: @OFTCoreComponent::ComponentState<ContractState>) -> bool {
|
|
378
|
+
// No approval since we mint/burn.
|
|
379
|
+
false
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//! Fuzzable contract addresses for ofts tests
|
|
2
|
+
|
|
3
|
+
use core::num::traits::Zero;
|
|
4
|
+
use snforge_std::fuzzable::Fuzzable;
|
|
5
|
+
use starknet::ContractAddress;
|
|
6
|
+
|
|
7
|
+
/// Generate a random contract address
|
|
8
|
+
pub(crate) impl FuzzableContractAddress of Fuzzable<ContractAddress> {
|
|
9
|
+
fn generate() -> ContractAddress {
|
|
10
|
+
loop {
|
|
11
|
+
if let Some(address) = Fuzzable::<felt252>::generate().try_into() {
|
|
12
|
+
return address;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
fn blank() -> ContractAddress {
|
|
18
|
+
Zero::zero()
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|