@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
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,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';
|
package/contracts/oft_mint_burn/src/erc20_mint_burn_upgradeable/erc20_mint_burn_upgradeable.cairo
ADDED
|
@@ -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
|
+
}
|