@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.
Files changed (98) hide show
  1. package/Scarb.lock +190 -0
  2. package/Scarb.toml +3 -0
  3. package/contracts/oft_mint_burn/Scarb.toml +29 -0
  4. package/contracts/oft_mint_burn/src/constants.cairo +11 -0
  5. package/contracts/oft_mint_burn/src/erc20_mint_burn_upgradeable/constants.cairo +17 -0
  6. package/contracts/oft_mint_burn/src/erc20_mint_burn_upgradeable/erc20_mint_burn_upgradeable.cairo +309 -0
  7. package/contracts/oft_mint_burn/src/erc20_mint_burn_upgradeable/interface.cairo +86 -0
  8. package/contracts/oft_mint_burn/src/errors.cairo +39 -0
  9. package/contracts/oft_mint_burn/src/interface.cairo +95 -0
  10. package/contracts/oft_mint_burn/src/lib.cairo +10 -0
  11. package/contracts/oft_mint_burn/src/oft_mint_burn_adapter.cairo +382 -0
  12. package/contracts/oft_mint_burn/tests/fuzzable/contract_address.cairo +21 -0
  13. package/contracts/oft_mint_burn/tests/lib.cairo +8 -0
  14. package/contracts/oft_mint_burn/tests/test_erc20_mint_burn_upgradeable.cairo +621 -0
  15. package/contracts/oft_mint_burn/tests/test_mint_burn_adapter.cairo +194 -0
  16. package/contracts/oft_mint_burn/tests/test_mint_burn_adapter_advanced.cairo +746 -0
  17. package/contracts/oft_mint_burn/tests/utils.cairo +84 -0
  18. package/dist/3UUTAAI4.js +9 -0
  19. package/dist/3UUTAAI4.js.map +1 -0
  20. package/dist/4EGWSIX7.js +18 -0
  21. package/dist/4EGWSIX7.js.map +1 -0
  22. package/dist/54KHZH3Y.cjs +22 -0
  23. package/dist/54KHZH3Y.cjs.map +1 -0
  24. package/dist/5KFUKPR2.js +1033 -0
  25. package/dist/5KFUKPR2.js.map +1 -0
  26. package/dist/5R6WMZVR.cjs +22 -0
  27. package/dist/5R6WMZVR.cjs.map +1 -0
  28. package/dist/CCHLUK5E.cjs +1035 -0
  29. package/dist/CCHLUK5E.cjs.map +1 -0
  30. package/dist/CGU4EJTF.cjs +14 -0
  31. package/dist/CGU4EJTF.cjs.map +1 -0
  32. package/dist/DJIRVXJ3.cjs +1914 -0
  33. package/dist/DJIRVXJ3.cjs.map +1 -0
  34. package/dist/E63KEOR5.cjs +11 -0
  35. package/dist/E63KEOR5.cjs.map +1 -0
  36. package/dist/FL52ASKH.cjs +16 -0
  37. package/dist/FL52ASKH.cjs.map +1 -0
  38. package/dist/GZXOKWQY.js +1303 -0
  39. package/dist/GZXOKWQY.js.map +1 -0
  40. package/dist/IQR2DIUY.cjs +1305 -0
  41. package/dist/IQR2DIUY.cjs.map +1 -0
  42. package/dist/MNNO3GDF.js +14 -0
  43. package/dist/MNNO3GDF.js.map +1 -0
  44. package/dist/NZRVZWHQ.js +1912 -0
  45. package/dist/NZRVZWHQ.js.map +1 -0
  46. package/dist/QYG4SI7W.js +18 -0
  47. package/dist/QYG4SI7W.js.map +1 -0
  48. package/dist/UE6XWQTX.js +12 -0
  49. package/dist/UE6XWQTX.js.map +1 -0
  50. package/dist/generated/abi/e-r-c20-mint-burn-upgradeable.cjs +13 -0
  51. package/dist/generated/abi/e-r-c20-mint-burn-upgradeable.cjs.map +1 -0
  52. package/dist/generated/abi/e-r-c20-mint-burn-upgradeable.d.ts +760 -0
  53. package/dist/generated/abi/e-r-c20-mint-burn-upgradeable.d.ts.map +1 -0
  54. package/dist/generated/abi/e-r-c20-mint-burn-upgradeable.js +4 -0
  55. package/dist/generated/abi/e-r-c20-mint-burn-upgradeable.js.map +1 -0
  56. package/dist/generated/abi/o-f-t-mint-burn-adapter.cjs +13 -0
  57. package/dist/generated/abi/o-f-t-mint-burn-adapter.cjs.map +1 -0
  58. package/dist/generated/abi/o-f-t-mint-burn-adapter.d.ts +1408 -0
  59. package/dist/generated/abi/o-f-t-mint-burn-adapter.d.ts.map +1 -0
  60. package/dist/generated/abi/o-f-t-mint-burn-adapter.js +4 -0
  61. package/dist/generated/abi/o-f-t-mint-burn-adapter.js.map +1 -0
  62. package/dist/generated/abi.cjs +19 -0
  63. package/dist/generated/abi.cjs.map +1 -0
  64. package/dist/generated/abi.d.ts +3 -0
  65. package/dist/generated/abi.d.ts.map +1 -0
  66. package/dist/generated/abi.js +6 -0
  67. package/dist/generated/abi.js.map +1 -0
  68. package/dist/generated/casm.cjs +17 -0
  69. package/dist/generated/casm.cjs.map +1 -0
  70. package/dist/generated/casm.d.ts +3 -0
  71. package/dist/generated/casm.d.ts.map +1 -0
  72. package/dist/generated/casm.js +4 -0
  73. package/dist/generated/casm.js.map +1 -0
  74. package/dist/generated/sierra.cjs +17 -0
  75. package/dist/generated/sierra.cjs.map +1 -0
  76. package/dist/generated/sierra.d.ts +3 -0
  77. package/dist/generated/sierra.d.ts.map +1 -0
  78. package/dist/generated/sierra.js +4 -0
  79. package/dist/generated/sierra.js.map +1 -0
  80. package/dist/generated/verification/index.cjs +14 -0
  81. package/dist/generated/verification/index.cjs.map +1 -0
  82. package/dist/generated/verification/index.d.ts +2 -0
  83. package/dist/generated/verification/index.d.ts.map +1 -0
  84. package/dist/generated/verification/index.js +5 -0
  85. package/dist/generated/verification/index.js.map +1 -0
  86. package/dist/generated/verification/oft_mint_burn.cjs +10 -0
  87. package/dist/generated/verification/oft_mint_burn.cjs.map +1 -0
  88. package/dist/generated/verification/oft_mint_burn.d.ts +4 -0
  89. package/dist/generated/verification/oft_mint_burn.d.ts.map +1 -0
  90. package/dist/generated/verification/oft_mint_burn.js +4 -0
  91. package/dist/generated/verification/oft_mint_burn.js.map +1 -0
  92. package/dist/index.cjs +31 -0
  93. package/dist/index.cjs.map +1 -0
  94. package/dist/index.d.ts +5 -0
  95. package/dist/index.d.ts.map +1 -0
  96. package/dist/index.js +10 -0
  97. package/dist/index.js.map +1 -0
  98. package/package.json +53 -0
package/Scarb.lock ADDED
@@ -0,0 +1,190 @@
1
+ # Code generated by scarb DO NOT EDIT.
2
+ version = 1
3
+
4
+ [[package]]
5
+ name = "enumerable_set"
6
+ version = "0.1.0"
7
+
8
+ [[package]]
9
+ name = "layerzero"
10
+ version = "0.1.0"
11
+ dependencies = [
12
+ "enumerable_set",
13
+ "lz_utils",
14
+ "multisig",
15
+ "openzeppelin",
16
+ ]
17
+
18
+ [[package]]
19
+ name = "lz_utils"
20
+ version = "0.1.0"
21
+
22
+ [[package]]
23
+ name = "multisig"
24
+ version = "0.1.0"
25
+ dependencies = [
26
+ "enumerable_set",
27
+ "lz_utils",
28
+ ]
29
+
30
+ [[package]]
31
+ name = "oft_mint_burn"
32
+ version = "0.1.0"
33
+ dependencies = [
34
+ "layerzero",
35
+ "lz_utils",
36
+ "openzeppelin",
37
+ "snforge_std",
38
+ "starkware_utils_testing",
39
+ ]
40
+
41
+ [[package]]
42
+ name = "openzeppelin"
43
+ version = "2.0.0"
44
+ source = "registry+https://scarbs.xyz/"
45
+ checksum = "sha256:5e4fdecc957cfca7854d95912dc72d9f725517c063b116512900900add29fd77"
46
+ dependencies = [
47
+ "openzeppelin_access",
48
+ "openzeppelin_account",
49
+ "openzeppelin_finance",
50
+ "openzeppelin_governance",
51
+ "openzeppelin_introspection",
52
+ "openzeppelin_merkle_tree",
53
+ "openzeppelin_presets",
54
+ "openzeppelin_security",
55
+ "openzeppelin_token",
56
+ "openzeppelin_upgrades",
57
+ "openzeppelin_utils",
58
+ ]
59
+
60
+ [[package]]
61
+ name = "openzeppelin_access"
62
+ version = "2.0.0"
63
+ source = "registry+https://scarbs.xyz/"
64
+ checksum = "sha256:511681dd26d814ee2bc996d44ff8cb4aaa5ae9d14272130def7eb901cf004850"
65
+ dependencies = [
66
+ "openzeppelin_introspection",
67
+ ]
68
+
69
+ [[package]]
70
+ name = "openzeppelin_account"
71
+ version = "2.0.0"
72
+ source = "registry+https://scarbs.xyz/"
73
+ checksum = "sha256:fb3381c50d68b028d3801feb43df378e2bd62137b6884844f8f60aefe796188b"
74
+ dependencies = [
75
+ "openzeppelin_introspection",
76
+ "openzeppelin_utils",
77
+ ]
78
+
79
+ [[package]]
80
+ name = "openzeppelin_finance"
81
+ version = "2.0.0"
82
+ source = "registry+https://scarbs.xyz/"
83
+ checksum = "sha256:e9456ef69502a87c4c99bf50145351b50950f8b11244847d92935c466c4ba787"
84
+ dependencies = [
85
+ "openzeppelin_access",
86
+ "openzeppelin_token",
87
+ ]
88
+
89
+ [[package]]
90
+ name = "openzeppelin_governance"
91
+ version = "2.0.0"
92
+ source = "registry+https://scarbs.xyz/"
93
+ checksum = "sha256:056e6d6f3d48193b53f06283884f8a9675f986fc85425f6a40e8c1aeb3b3ecfa"
94
+ dependencies = [
95
+ "openzeppelin_access",
96
+ "openzeppelin_account",
97
+ "openzeppelin_introspection",
98
+ "openzeppelin_token",
99
+ "openzeppelin_utils",
100
+ ]
101
+
102
+ [[package]]
103
+ name = "openzeppelin_introspection"
104
+ version = "2.0.0"
105
+ source = "registry+https://scarbs.xyz/"
106
+ checksum = "sha256:87773ed6cd2318f169283ecbbb161890d1996260a80302d81ec45b70ee5e54c1"
107
+
108
+ [[package]]
109
+ name = "openzeppelin_merkle_tree"
110
+ version = "2.0.0"
111
+ source = "registry+https://scarbs.xyz/"
112
+ checksum = "sha256:47f80c9ce59557774243214f8e75c5e866f30f3d8daa755855f6ffd01c89ca89"
113
+
114
+ [[package]]
115
+ name = "openzeppelin_presets"
116
+ version = "2.0.0"
117
+ source = "registry+https://scarbs.xyz/"
118
+ checksum = "sha256:36c761ee923f1dc0887c0eab8c224b49ac242dbfe9163fbb0b08562042ab3d98"
119
+ dependencies = [
120
+ "openzeppelin_access",
121
+ "openzeppelin_account",
122
+ "openzeppelin_finance",
123
+ "openzeppelin_introspection",
124
+ "openzeppelin_token",
125
+ "openzeppelin_upgrades",
126
+ "openzeppelin_utils",
127
+ ]
128
+
129
+ [[package]]
130
+ name = "openzeppelin_security"
131
+ version = "2.0.0"
132
+ source = "registry+https://scarbs.xyz/"
133
+ checksum = "sha256:902932ec296c2f400e0ac7c579edeaafd6067b6ce6d9854c1191de28e396ffe3"
134
+
135
+ [[package]]
136
+ name = "openzeppelin_token"
137
+ version = "2.0.0"
138
+ source = "registry+https://scarbs.xyz/"
139
+ checksum = "sha256:6fe61f63b5a6706018265fb7373b6e5bd3ff829bdc760b2b90296b1e708d180c"
140
+ dependencies = [
141
+ "openzeppelin_access",
142
+ "openzeppelin_account",
143
+ "openzeppelin_introspection",
144
+ "openzeppelin_utils",
145
+ ]
146
+
147
+ [[package]]
148
+ name = "openzeppelin_upgrades"
149
+ version = "2.0.0"
150
+ source = "registry+https://scarbs.xyz/"
151
+ checksum = "sha256:560d57a9c3f3ec5a476e82fec8963c93c8df63a4ff9ff134f64ab8383bde3c61"
152
+
153
+ [[package]]
154
+ name = "openzeppelin_utils"
155
+ version = "2.0.0"
156
+ source = "registry+https://scarbs.xyz/"
157
+ checksum = "sha256:bf799c794139837f397975ffdf6a7ed5032d198bbf70e87a8f44f144a9dfc505"
158
+
159
+ [[package]]
160
+ name = "snforge_scarb_plugin"
161
+ version = "0.49.0"
162
+ source = "registry+https://scarbs.xyz/"
163
+ checksum = "sha256:903150f0e9542e4277d417029eea4c03af0db398b581f9f7ae3ebbdac9afc657"
164
+
165
+ [[package]]
166
+ name = "snforge_std"
167
+ version = "0.49.0"
168
+ source = "registry+https://scarbs.xyz/"
169
+ checksum = "sha256:73d73653cc4356ec51b92a6bec9d8385b20318170c2f2ade7891e5185a0e7e64"
170
+ dependencies = [
171
+ "snforge_scarb_plugin",
172
+ ]
173
+
174
+ [[package]]
175
+ name = "starkware_utils"
176
+ version = "1.0.0"
177
+ source = "git+https://github.com/starkware-libs/starkware-starknet-utils?rev=e1955423808045de868987b8fb0b43f5cbdf5699#e1955423808045de868987b8fb0b43f5cbdf5699"
178
+ dependencies = [
179
+ "openzeppelin",
180
+ ]
181
+
182
+ [[package]]
183
+ name = "starkware_utils_testing"
184
+ version = "1.0.0"
185
+ source = "git+https://github.com/starkware-libs/starkware-starknet-utils?rev=e1955423808045de868987b8fb0b43f5cbdf5699#e1955423808045de868987b8fb0b43f5cbdf5699"
186
+ dependencies = [
187
+ "openzeppelin",
188
+ "snforge_std",
189
+ "starkware_utils",
190
+ ]
package/Scarb.toml ADDED
@@ -0,0 +1,3 @@
1
+ [workspace]
2
+ members = ["contracts/*"]
3
+
@@ -0,0 +1,29 @@
1
+ [package]
2
+ name = "oft_mint_burn"
3
+ version = "0.1.0"
4
+ edition = "2024_07"
5
+
6
+ # See more keys and their definitions at https://docs.swmansion.com/scarb/docs/reference/manifest.html
7
+
8
+ [dependencies]
9
+ starknet = "2.14.0"
10
+ openzeppelin = "2.0.0"
11
+ lz_utils = { path = "../../node_modules/@layerzerolabs/protocol-starknet-v2/libs/lz_utils" }
12
+ layerzero = { path = "../../node_modules/@layerzerolabs/protocol-starknet-v2/layerzero" }
13
+
14
+ [dev-dependencies]
15
+ snforge_std = "0.49.0"
16
+ assert_macros = "2.12.0"
17
+ starkware_utils_testing = { git = "https://github.com/starkware-libs/starkware-starknet-utils", rev = "e1955423808045de868987b8fb0b43f5cbdf5699" }
18
+
19
+ [[target.starknet-contract]]
20
+ sierra = true
21
+ casm = true
22
+
23
+
24
+ [scripts]
25
+ test = "snforge test"
26
+
27
+ [tool.scarb]
28
+ allow-prebuilt-plugins = ["snforge_scarb_plugin", "snforge_std"]
29
+
@@ -0,0 +1,11 @@
1
+ /// Role identifier for addresses that can manage fees
2
+ pub const FEE_MANAGER_ROLE: felt252 = 'FEE_MANAGER_ROLE';
3
+
4
+ /// Role identifier for addresses that can manage rate limits
5
+ pub const RATE_LIMITER_MANAGER_ROLE: felt252 = 'RATE_LIMITER_MANAGER_ROLE';
6
+
7
+ /// Role identifier for addresses that can upgrade the contract
8
+ pub const UPGRADE_MANAGER_ROLE: felt252 = 'UPGRADE_MANAGER_ROLE';
9
+
10
+ /// Role identifier for addresses that can pause the contract
11
+ pub const PAUSE_MANAGER_ROLE: felt252 = 'PAUSE_MANAGER_ROLE';
@@ -0,0 +1,17 @@
1
+ /// Role identifier for addresses that can mint new tokens
2
+ /// In Solidity, this would be: bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE")
3
+ pub const MINTER_ROLE: felt252 = 'MINTER_ROLE';
4
+
5
+ /// Role identifier for addresses that can burn tokens without approval
6
+ /// In Solidity, this would be: bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE")
7
+ pub const BURNER_ROLE: felt252 = 'BURNER_ROLE';
8
+
9
+ /// Role identifier for addresses that can pause the contract
10
+ /// In Solidity, this would be: bytes32 public constant PAUSE_MANAGER_ROLE =
11
+ /// keccak256("PAUSE_MANAGER_ROLE")
12
+ pub const PAUSE_MANAGER_ROLE: felt252 = 'PAUSE_MANAGER_ROLE';
13
+
14
+ /// Role identifier for addresses that can manage the allowlist
15
+ /// In Solidity, this would be: bytes32 public constant ALLOWLIST_MANAGER_ROLE =
16
+ /// keccak256("ALLOWLIST_MANAGER_ROLE")
17
+ pub const ALLOWLIST_MANAGER_ROLE: felt252 = 'ALLOWLIST_MANAGER_ROLE';
@@ -0,0 +1,309 @@
1
+ /// ERC20MintBurnUpgradeable
2
+ ///
3
+ /// Upgradeable ERC20 token with permissioned mint/burn functionality
4
+ /// This contract provides a standard ERC20 token that allows permissioned minters and burners
5
+ /// to mint and burn tokens through the IMintableToken interface.
6
+ ///
7
+ /// Key features:
8
+ /// - MINTER_ROLE: Addresses with this role can mint tokens via permissioned_mint
9
+ /// - BURNER_ROLE: Addresses with this role can burn tokens via permissioned_burn
10
+ /// - ALLOWLIST_MANAGER_ROLE: Addresses with this role can manage the allowlist
11
+ /// - DEFAULT_ADMIN_ROLE: Can grant/revoke roles
12
+ /// - Upgradeable for future improvements
13
+ /// - Allowlist support with Open/Blacklist/Whitelist modes
14
+ #[starknet::contract]
15
+ pub mod ERC20MintBurnUpgradeable {
16
+ use AccessControlComponent::DEFAULT_ADMIN_ROLE;
17
+ use layerzero::common::constants::ZERO_ADDRESS;
18
+ use layerzero::oapps::common::allow_list::allow_list::AllowlistComponent;
19
+ use layerzero::oapps::common::allow_list::errors::err_not_allowed;
20
+ use layerzero::oapps::common::allow_list::interface::AllowlistMode;
21
+ use lz_utils::error::assert_with_byte_array;
22
+ use openzeppelin::access::accesscontrol::AccessControlComponent;
23
+ use openzeppelin::introspection::src5::SRC5Component;
24
+ use openzeppelin::security::pausable::PausableComponent;
25
+ use openzeppelin::token::erc20::ERC20Component;
26
+ use openzeppelin::token::erc20::interface::IERC20Metadata;
27
+ use openzeppelin::upgrades::UpgradeableComponent;
28
+ use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess};
29
+ use starknet::{ClassHash, ContractAddress, get_caller_address};
30
+ use crate::erc20_mint_burn_upgradeable::constants::{
31
+ ALLOWLIST_MANAGER_ROLE, BURNER_ROLE, MINTER_ROLE, PAUSE_MANAGER_ROLE,
32
+ };
33
+ use crate::erc20_mint_burn_upgradeable::interface::IERC20MintBurnUpgradeable;
34
+ use crate::interface::IMintableToken;
35
+
36
+ // =============================== Components ===============================
37
+
38
+ component!(path: ERC20Component, storage: erc20, event: ERC20Event);
39
+ component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent);
40
+ component!(path: PausableComponent, storage: pausable, event: PausableEvent);
41
+ component!(path: AccessControlComponent, storage: access_control, event: AccessControlEvent);
42
+ component!(path: SRC5Component, storage: src5, event: SRC5Event);
43
+ component!(path: AllowlistComponent, storage: allowlist, event: AllowlistEvent);
44
+
45
+
46
+ // Use individual embeds instead of Mixin to allow custom decimals override
47
+ #[abi(embed_v0)]
48
+ impl ERC20Impl = ERC20Component::ERC20Impl<ContractState>;
49
+ #[abi(embed_v0)]
50
+ impl ERC20CamelOnlyImpl = ERC20Component::ERC20CamelOnlyImpl<ContractState>;
51
+ impl ERC20InternalImpl = ERC20Component::InternalImpl<ContractState>;
52
+
53
+ impl UpgradeableInternalImpl = UpgradeableComponent::InternalImpl<ContractState>;
54
+
55
+ #[abi(embed_v0)]
56
+ impl AccessControlImpl =
57
+ AccessControlComponent::AccessControlImpl<ContractState>;
58
+ impl AccessControlInternalImpl = AccessControlComponent::InternalImpl<ContractState>;
59
+
60
+ #[abi(embed_v0)]
61
+ impl PausableImpl = PausableComponent::PausableImpl<ContractState>;
62
+ impl PausableInternalImpl = PausableComponent::InternalImpl<ContractState>;
63
+
64
+ #[abi(embed_v0)]
65
+ impl AllowlistImpl = AllowlistComponent::AllowlistImpl<ContractState>;
66
+ impl AllowlistInternalImpl = AllowlistComponent::InternalImpl<ContractState>;
67
+
68
+ #[storage]
69
+ struct Storage {
70
+ #[substorage(v0)]
71
+ erc20: ERC20Component::Storage,
72
+ #[substorage(v0)]
73
+ upgradeable: UpgradeableComponent::Storage,
74
+ #[substorage(v0)]
75
+ access_control: AccessControlComponent::Storage,
76
+ #[substorage(v0)]
77
+ src5: SRC5Component::Storage,
78
+ #[substorage(v0)]
79
+ pausable: PausableComponent::Storage,
80
+ #[substorage(v0)]
81
+ allowlist: AllowlistComponent::Storage,
82
+ /// Token decimals (configurable via constructor)
83
+ decimals: u8,
84
+ }
85
+
86
+ #[event]
87
+ #[derive(Drop, starknet::Event)]
88
+ enum Event {
89
+ #[flat]
90
+ ERC20Event: ERC20Component::Event,
91
+ #[flat]
92
+ UpgradeableEvent: UpgradeableComponent::Event,
93
+ #[flat]
94
+ AccessControlEvent: AccessControlComponent::Event,
95
+ #[flat]
96
+ SRC5Event: SRC5Component::Event,
97
+ #[flat]
98
+ PausableEvent: PausableComponent::Event,
99
+ #[flat]
100
+ AllowlistEvent: AllowlistComponent::Event,
101
+ }
102
+
103
+ /// Initializes the ERC20 token with mint/burn capabilities
104
+ ///
105
+ /// # Arguments
106
+ /// * `name` - Token name
107
+ /// * `symbol` - Token symbol
108
+ /// * `decimals` - Token decimals
109
+ /// * `default_admin` - Address that will receive DEFAULT_ADMIN_ROLE
110
+ ///
111
+ /// # Notes
112
+ /// The default admin can grant MINTER_ROLE, BURNER_ROLE, and ALLOWLIST_MANAGER_ROLE to other
113
+ /// addresses. The allowlist is initialized in Open mode (all users allowed).
114
+ #[constructor]
115
+ fn constructor(
116
+ ref self: ContractState,
117
+ name: ByteArray,
118
+ symbol: ByteArray,
119
+ decimals: u8,
120
+ default_admin: ContractAddress,
121
+ ) {
122
+ assert(default_admin != ZERO_ADDRESS, 'Invalid admin: zero address');
123
+
124
+ // Initialize ERC20 with name and symbol
125
+ self.erc20.initializer(name, symbol);
126
+
127
+ // Store decimals in contract storage
128
+ self.decimals.write(decimals);
129
+
130
+ // Initialize access control
131
+ self.access_control.initializer();
132
+
133
+ // Initialize allowlist in Open mode (all users allowed by default)
134
+ self.allowlist.initializer(AllowlistMode::Open);
135
+
136
+ // Grant admin role to the specified address
137
+ // Admin can then grant MINTER_ROLE, BURNER_ROLE, and ALLOWLIST_MANAGER_ROLE as needed
138
+ self.access_control._grant_role(DEFAULT_ADMIN_ROLE, default_admin);
139
+ }
140
+
141
+ // Override ERC20Metadata to read decimals from storage
142
+ #[abi(embed_v0)]
143
+ impl ERC20MetadataImpl of IERC20Metadata<ContractState> {
144
+ fn name(self: @ContractState) -> ByteArray {
145
+ self.erc20.ERC20_name.read()
146
+ }
147
+
148
+ fn symbol(self: @ContractState) -> ByteArray {
149
+ self.erc20.ERC20_symbol.read()
150
+ }
151
+
152
+ fn decimals(self: @ContractState) -> u8 {
153
+ self.decimals.read()
154
+ }
155
+ }
156
+
157
+ #[abi(embed_v0)]
158
+ impl MintableTokenImpl of IMintableToken<ContractState> {
159
+ /// Mints tokens to address using permitted minter pattern
160
+ ///
161
+ /// # Arguments
162
+ /// * `account` - Address to mint tokens to
163
+ /// * `amount` - Amount of tokens to mint
164
+ ///
165
+ /// # Access Control
166
+ /// Only callable by accounts with MINTER_ROLE
167
+ fn permissioned_mint(ref self: ContractState, account: ContractAddress, amount: u256) {
168
+ self._assert_only_minter();
169
+ self.erc20.mint(account, amount);
170
+ }
171
+
172
+ /// Burns tokens from address using permitted burner pattern
173
+ ///
174
+ /// # Arguments
175
+ /// * `account` - Address to burn tokens from
176
+ /// * `amount` - Amount of tokens to burn
177
+ ///
178
+ /// # Access Control
179
+ /// Only callable by accounts with BURNER_ROLE
180
+ ///
181
+ /// # Notes
182
+ /// Unlike standard burn, this doesn't require approval from token holder
183
+ fn permissioned_burn(ref self: ContractState, account: ContractAddress, amount: u256) {
184
+ self._assert_only_burner();
185
+ self.erc20.burn(account, amount);
186
+ }
187
+ }
188
+
189
+ #[abi(embed_v0)]
190
+ impl ERC20MintBurnUpgradeableImpl of IERC20MintBurnUpgradeable<ContractState> {
191
+ fn upgrade(ref self: ContractState, new_class_hash: ClassHash) {
192
+ self._assert_only_default_admin();
193
+ self.upgradeable.upgrade(new_class_hash);
194
+ }
195
+
196
+ fn upgrade_and_call(
197
+ ref self: ContractState,
198
+ new_class_hash: ClassHash,
199
+ selector: felt252,
200
+ calldata: Span<felt252>,
201
+ ) -> Span<felt252> {
202
+ self._assert_only_default_admin();
203
+ self.upgradeable.upgrade_and_call(new_class_hash, selector, calldata)
204
+ }
205
+
206
+ fn pause(ref self: ContractState) {
207
+ self._assert_only_pause_manager();
208
+ self.pausable.pause();
209
+ }
210
+
211
+ fn unpause(ref self: ContractState) {
212
+ self._assert_only_pause_manager();
213
+ self.pausable.unpause();
214
+ }
215
+
216
+ fn set_allowlist_mode(ref self: ContractState, mode: AllowlistMode) {
217
+ self._assert_only_allowlist_manager();
218
+ self.allowlist._set_allowlist_mode(mode);
219
+ }
220
+
221
+ fn set_whitelisted(ref self: ContractState, users: Span<ContractAddress>, status: bool) {
222
+ self._assert_only_allowlist_manager();
223
+ self.allowlist._set_whitelisted(users, status);
224
+ }
225
+
226
+ fn set_blacklisted(ref self: ContractState, users: Span<ContractAddress>, status: bool) {
227
+ self._assert_only_allowlist_manager();
228
+ self.allowlist._set_blacklisted(users, status);
229
+ }
230
+ }
231
+
232
+ // =============================== ERC20 Hooks ===============================
233
+
234
+ // Overrides the default implementation of `ERC20Component::InternalTrait::update`
235
+ // - Mint (from == zero): NO pause check, NO allowlist check - can always mint
236
+ // - Burn (to == zero): Check pause AND allowlist on `from`
237
+ // - Transfer (both non-zero): Check pause AND allowlist on `from`, `to`,
238
+ // - Transfer from: caller if caller != from
239
+ impl ERC20Hooks of ERC20Component::ERC20HooksTrait<ContractState> {
240
+ fn before_update(
241
+ ref self: ERC20Component::ComponentState<ContractState>,
242
+ from: ContractAddress,
243
+ recipient: ContractAddress,
244
+ amount: u256,
245
+ ) {
246
+ // Mint: from == ZERO_ADDRESS - no checks, can always mint
247
+ if from == ZERO_ADDRESS {
248
+ return;
249
+ }
250
+
251
+ let contract = self.get_contract();
252
+
253
+ // For burn and transfer: check pause
254
+ contract.pausable.assert_not_paused();
255
+
256
+ // For burn (to == ZERO_ADDRESS): check allowlist on `from` only
257
+ if recipient == ZERO_ADDRESS {
258
+ assert_with_byte_array(
259
+ contract.allowlist.is_user_allowlisted(from), err_not_allowed(),
260
+ );
261
+ return;
262
+ }
263
+
264
+ // For transfer: check allowlist on `from`, `recipient`, AND caller
265
+ assert_with_byte_array(
266
+ contract.allowlist.is_user_allowlisted(from)
267
+ && contract.allowlist.is_user_allowlisted(recipient),
268
+ err_not_allowed(),
269
+ );
270
+
271
+ // handle transfer_from where caller != from
272
+ let caller = get_caller_address();
273
+ if caller != from {
274
+ assert_with_byte_array(
275
+ contract.allowlist.is_user_allowlisted(caller), err_not_allowed(),
276
+ );
277
+ }
278
+ }
279
+ }
280
+
281
+ /// Internal functions for role validation
282
+ #[generate_trait]
283
+ impl InternalImpl of InternalTrait {
284
+ /// Validates caller has DEFAULT_ADMIN_ROLE
285
+ fn _assert_only_default_admin(self: @ContractState) {
286
+ self.access_control.assert_only_role(DEFAULT_ADMIN_ROLE);
287
+ }
288
+
289
+ /// Validates caller has MINTER_ROLE
290
+ fn _assert_only_minter(self: @ContractState) {
291
+ self.access_control.assert_only_role(MINTER_ROLE);
292
+ }
293
+
294
+ /// Validates caller has BURNER_ROLE
295
+ fn _assert_only_burner(self: @ContractState) {
296
+ self.access_control.assert_only_role(BURNER_ROLE);
297
+ }
298
+
299
+ /// Validates caller has PAUSE_MANAGER_ROLE
300
+ fn _assert_only_pause_manager(self: @ContractState) {
301
+ self.access_control.assert_only_role(PAUSE_MANAGER_ROLE);
302
+ }
303
+
304
+ /// Validates caller has ALLOWLIST_MANAGER_ROLE
305
+ fn _assert_only_allowlist_manager(self: @ContractState) {
306
+ self.access_control.assert_only_role(ALLOWLIST_MANAGER_ROLE);
307
+ }
308
+ }
309
+ }
@@ -0,0 +1,86 @@
1
+ use layerzero::oapps::common::allow_list::interface::AllowlistMode;
2
+ use starknet::{ClassHash, ContractAddress};
3
+
4
+ #[starknet::interface]
5
+ pub trait IERC20MintBurnUpgradeable<TContractState> {
6
+ /// Upgrades the contract
7
+ ///
8
+ /// # Arguments
9
+ /// * `new_class_hash` - The new class hash to upgrade to
10
+ ///
11
+ /// # Events
12
+ /// * `Upgraded` - Emitted when the contract is upgraded (from OpenZeppelin's
13
+ /// [`UpgradeableComponent`])
14
+ ///
15
+ /// @dev This function is only callable by the default admin.
16
+ fn upgrade(ref self: TContractState, new_class_hash: ClassHash);
17
+
18
+ /// Upgrades the contract and calls a function
19
+ ///
20
+ /// # Arguments
21
+ /// * `new_class_hash` - The new class hash to upgrade to
22
+ /// * `selector` - The selector to call
23
+ /// * `data` - The data to pass to the function
24
+ ///
25
+ /// # Returns
26
+ /// * `Span<felt252>` - The response data from the function call
27
+ ///
28
+ /// # Events
29
+ /// * `Upgraded` - Emitted when the contract is upgraded (from OpenZeppelin's
30
+ /// [`UpgradeableComponent`])
31
+ ///
32
+ /// @dev This function is only callable by the default admin.
33
+ fn upgrade_and_call(
34
+ ref self: TContractState,
35
+ new_class_hash: ClassHash,
36
+ selector: felt252,
37
+ calldata: Span<felt252>,
38
+ ) -> Span<felt252>;
39
+
40
+ // =============================== Pause ===============================
41
+
42
+ /// Pauses the contract
43
+ ///
44
+ /// # Events
45
+ /// * `Paused` - Emitted when the contract is paused (from OpenZeppelin's
46
+ /// [`PausableComponent`])
47
+ fn pause(ref self: TContractState);
48
+
49
+ /// Unpauses the contract
50
+ ///
51
+ /// # Events
52
+ /// * `Unpaused` - Emitted when the contract is unpaused (from OpenZeppelin's
53
+ /// [`PausableComponent`])
54
+ fn unpause(ref self: TContractState);
55
+
56
+ // =============================== Allowlist ===============================
57
+
58
+ /// Sets the allowlist mode
59
+ ///
60
+ /// # Arguments
61
+ /// * `mode` - The new allowlist mode (Open, Blacklist, or Whitelist)
62
+ ///
63
+ /// # Access Control
64
+ /// Only callable by accounts with ALLOWLIST_MANAGER_ROLE
65
+ fn set_allowlist_mode(ref self: TContractState, mode: AllowlistMode);
66
+
67
+ /// Sets the whitelist status for multiple users
68
+ ///
69
+ /// # Arguments
70
+ /// * `users` - Array of user addresses
71
+ /// * `status` - Whether to whitelist (true) or unwhitelist (false)
72
+ ///
73
+ /// # Access Control
74
+ /// Only callable by accounts with ALLOWLIST_MANAGER_ROLE
75
+ fn set_whitelisted(ref self: TContractState, users: Span<ContractAddress>, status: bool);
76
+
77
+ /// Sets the blacklist status for multiple users
78
+ ///
79
+ /// # Arguments
80
+ /// * `users` - Array of user addresses
81
+ /// * `status` - Whether to blacklist (true) or unblacklist (false)
82
+ ///
83
+ /// # Access Control
84
+ /// Only callable by accounts with ALLOWLIST_MANAGER_ROLE
85
+ fn set_blacklisted(ref self: TContractState, users: Span<ContractAddress>, status: bool);
86
+ }
@@ -0,0 +1,39 @@
1
+ //! OFT Mint Burn Adapter errors
2
+
3
+ use lz_utils::error::{Error, format_error};
4
+ use starknet::ContractAddress;
5
+
6
+ #[derive(Drop)]
7
+ pub enum OFTMintBurnAdapterError {
8
+ NoFeesToWithdraw,
9
+ TransferFailed,
10
+ CallerNotOwnerOrMissingRole,
11
+ }
12
+
13
+ impl OFTMintBurnAdapterErrorImpl of Error<OFTMintBurnAdapterError> {
14
+ fn prefix() -> ByteArray {
15
+ "OFT_MINT_BURN_ADAPTER"
16
+ }
17
+
18
+ fn name(self: OFTMintBurnAdapterError) -> ByteArray {
19
+ match self {
20
+ OFTMintBurnAdapterError::NoFeesToWithdraw => "NO_FEES_TO_WITHDRAW",
21
+ OFTMintBurnAdapterError::TransferFailed => "TRANSFER_FAILED",
22
+ OFTMintBurnAdapterError::CallerNotOwnerOrMissingRole => "CALLER_NOT_OWNER_OR_MISSING_ROLE",
23
+ }
24
+ }
25
+ }
26
+
27
+ pub fn err_no_fees_to_withdraw() -> ByteArray {
28
+ format_error(OFTMintBurnAdapterError::NoFeesToWithdraw, "")
29
+ }
30
+
31
+ pub fn err_transfer_failed(to: ContractAddress, amount: u256) -> ByteArray {
32
+ format_error(
33
+ OFTMintBurnAdapterError::TransferFailed, format!("to: {:?}, amount: {:?}", to, amount),
34
+ )
35
+ }
36
+
37
+ pub fn err_caller_not_owner_or_missing_role(role: felt252) -> ByteArray {
38
+ format_error(OFTMintBurnAdapterError::CallerNotOwnerOrMissingRole, format!("role: {:?}", role))
39
+ }