Skip to content

Commit 1367f88

Browse files
authored
Implement Max per wallet (#151)
* implemented maxMintPerWallet * tests pass * maxMintPerWalletExceeded tests pass * introduced base contracts for core and initilizable to inherit * rename commit * rename commit
1 parent 07427fe commit 1367f88

17 files changed

Lines changed: 1419 additions & 2093 deletions

src/core/token/ERC1155Base.sol

Lines changed: 424 additions & 0 deletions
Large diffs are not rendered by default.

src/core/token/ERC1155Core.sol

Lines changed: 4 additions & 408 deletions
Large diffs are not rendered by default.

src/core/token/ERC1155CoreInitializable.sol

Lines changed: 3 additions & 386 deletions
Large diffs are not rendered by default.

src/core/token/ERC20Base.sol

Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.20;
3+
4+
import {Core} from "../../Core.sol";
5+
6+
import {ERC20} from "@solady/tokens/ERC20.sol";
7+
import {ECDSA} from "@solady/utils/ECDSA.sol";
8+
import {EIP712} from "@solady/utils/EIP712.sol";
9+
import {Multicallable} from "@solady/utils/Multicallable.sol";
10+
import {ReentrancyGuard} from "@solady/utils/ReentrancyGuard.sol";
11+
12+
import {IERC20} from "../../interface/IERC20.sol";
13+
14+
import {BeforeApproveCallbackERC20} from "../../callback/BeforeApproveCallbackERC20.sol";
15+
import {BeforeBurnCallbackERC20} from "../../callback/BeforeBurnCallbackERC20.sol";
16+
import {BeforeMintCallbackERC20} from "../../callback/BeforeMintCallbackERC20.sol";
17+
import {BeforeMintWithSignatureCallbackERC20} from "../../callback/BeforeMintWithSignatureCallbackERC20.sol";
18+
import {BeforeTransferCallbackERC20} from "../../callback/BeforeTransferCallbackERC20.sol";
19+
20+
contract ERC20Base is ERC20, Multicallable, Core, EIP712 {
21+
22+
using ECDSA for bytes32;
23+
24+
/*//////////////////////////////////////////////////////////////
25+
CONSTANTS
26+
//////////////////////////////////////////////////////////////*/
27+
28+
bytes32 private constant TYPEHASH_SIGNATURE_MINT_ERC20 =
29+
keccak256("MintRequestERC20(address to,uint256 amount,bytes data)");
30+
31+
/*//////////////////////////////////////////////////////////////
32+
STORAGE
33+
//////////////////////////////////////////////////////////////*/
34+
35+
/// @notice The name of the token.
36+
string private name_;
37+
38+
/// @notice The symbol of the token.
39+
string private symbol_;
40+
41+
/// @notice The contract metadata URI of the contract.
42+
string private contractURI_;
43+
44+
/*//////////////////////////////////////////////////////////////
45+
EVENTS
46+
//////////////////////////////////////////////////////////////*/
47+
48+
/// @notice Emitted when the contract URI is updated.
49+
event ContractURIUpdated();
50+
51+
/*//////////////////////////////////////////////////////////////
52+
CONSTRUCTOR
53+
//////////////////////////////////////////////////////////////*/
54+
55+
function _initialize(
56+
string memory _name,
57+
string memory _symbol,
58+
string memory _contractURI,
59+
address _owner,
60+
address[] memory _modules,
61+
bytes[] memory _moduleInstallData
62+
) internal {
63+
// Set contract metadata
64+
name_ = _name;
65+
symbol_ = _symbol;
66+
_setupContractURI(_contractURI);
67+
_initializeOwner(_owner);
68+
69+
// Install and initialize modules
70+
require(_modules.length == _moduleInstallData.length);
71+
for (uint256 i = 0; i < _modules.length; i++) {
72+
_installModule(_modules[i], _moduleInstallData[i]);
73+
}
74+
}
75+
76+
/*//////////////////////////////////////////////////////////////
77+
VIEW FUNCTIONS
78+
//////////////////////////////////////////////////////////////*/
79+
80+
/// @notice Returns the name of the token.
81+
function name() public view override returns (string memory) {
82+
return name_;
83+
}
84+
85+
/// @notice Returns the symbol of the token.
86+
function symbol() public view override returns (string memory) {
87+
return symbol_;
88+
}
89+
90+
/**
91+
* @notice Returns the contract URI of the contract.
92+
* @return uri The contract URI of the contract.
93+
*/
94+
function contractURI() external view returns (string memory) {
95+
return contractURI_;
96+
}
97+
98+
function getSupportedCallbackFunctions()
99+
public
100+
pure
101+
override
102+
returns (SupportedCallbackFunction[] memory supportedCallbackFunctions)
103+
{
104+
supportedCallbackFunctions = new SupportedCallbackFunction[](5);
105+
supportedCallbackFunctions[0] = SupportedCallbackFunction({
106+
selector: BeforeMintCallbackERC20.beforeMintERC20.selector,
107+
mode: CallbackMode.REQUIRED
108+
});
109+
supportedCallbackFunctions[1] = SupportedCallbackFunction({
110+
selector: BeforeMintWithSignatureCallbackERC20.beforeMintWithSignatureERC20.selector,
111+
mode: CallbackMode.REQUIRED
112+
});
113+
supportedCallbackFunctions[2] = SupportedCallbackFunction({
114+
selector: BeforeTransferCallbackERC20.beforeTransferERC20.selector,
115+
mode: CallbackMode.OPTIONAL
116+
});
117+
supportedCallbackFunctions[3] = SupportedCallbackFunction({
118+
selector: BeforeBurnCallbackERC20.beforeBurnERC20.selector,
119+
mode: CallbackMode.OPTIONAL
120+
});
121+
supportedCallbackFunctions[4] = SupportedCallbackFunction({
122+
selector: BeforeApproveCallbackERC20.beforeApproveERC20.selector,
123+
mode: CallbackMode.OPTIONAL
124+
});
125+
}
126+
127+
/// @notice Returns whether a given interface is implemented by the contract.
128+
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
129+
return interfaceId == 0x01ffc9a7 // ERC165 Interface ID for ERC165
130+
|| interfaceId == 0xe8a3d485 // ERC-7572
131+
|| interfaceId == 0x7f5828d0 // ERC-173
132+
|| interfaceId == type(IERC20).interfaceId || _supportsInterfaceViaModules(interfaceId);
133+
}
134+
135+
/*//////////////////////////////////////////////////////////////
136+
EXTERNAL FUNCTIONS
137+
//////////////////////////////////////////////////////////////*/
138+
139+
/**
140+
* @notice Sets the contract URI of the contract.
141+
* @dev Only callable by contract admin.
142+
* @param contractURI The contract URI to set.
143+
*/
144+
function setContractURI(string memory contractURI) external onlyOwner {
145+
_setupContractURI(contractURI);
146+
}
147+
148+
/**
149+
* @notice Mints tokens. Calls the beforeMint hook.
150+
* @dev Reverts if beforeMint hook is absent or unsuccessful.
151+
* @param to The address to mint the tokens to.
152+
* @param amount The amount of tokens to mint.
153+
* @param data ABI encoded data to pass to the beforeMintERC20 hook.
154+
*/
155+
function mint(address to, uint256 amount, bytes calldata data) external payable {
156+
_beforeMint(to, amount, data);
157+
_mint(to, amount);
158+
}
159+
160+
/**
161+
* @notice Mints a token with a signature. Calls the beforeMint hook.
162+
* @dev Reverts if beforeMint hook is absent or unsuccessful.
163+
* @param to The address to mint the token to.
164+
* @param amount The amount of tokens to mint.
165+
* @param data ABI encoded data to pass to the beforeMint hook.
166+
* @param signature The signature produced from signing the minting request.
167+
*/
168+
function mintWithSignature(address to, uint256 amount, bytes calldata data, bytes memory signature)
169+
external
170+
payable
171+
{
172+
address signer = _hashTypedData(
173+
keccak256(abi.encode(TYPEHASH_SIGNATURE_MINT_ERC20, to, amount, keccak256(data)))
174+
).recover(signature);
175+
176+
_beforeMintWithSignature(to, amount, data, signer);
177+
_mint(to, amount);
178+
}
179+
180+
/**
181+
* @notice Burns tokens.
182+
* @dev Calls the beforeBurn hook. Skips calling the hook if it doesn't exist.
183+
* @param from The address to burn tokens from.
184+
* @param amount The amount of tokens to burn.
185+
* @param data ABI encoded arguments to pass to the beforeBurnERC20 hook.
186+
*/
187+
function burn(address from, uint256 amount, bytes calldata data) external payable {
188+
_beforeBurn(from, amount, data);
189+
190+
if (from != msg.sender) {
191+
_spendAllowance(from, msg.sender, amount);
192+
}
193+
194+
_burn(from, amount);
195+
}
196+
197+
/**
198+
* @notice Transfers tokens to a recipient.
199+
* @param to The address to transfer tokens to.
200+
* @param amount The quantity of tokens to transfer.
201+
*/
202+
function transfer(address to, uint256 amount) public override returns (bool) {
203+
_beforeTransfer(msg.sender, to, amount);
204+
return super.transfer(to, amount);
205+
}
206+
207+
/**
208+
* @notice Transfers tokens from a sender to a recipient.
209+
* @param from The address to transfer tokens from.
210+
* @param to The address to transfer tokens to.
211+
* @param amount The quantity of tokens to transfer.
212+
*/
213+
function transferFrom(address from, address to, uint256 amount) public override returns (bool) {
214+
_beforeTransfer(from, to, amount);
215+
return super.transferFrom(from, to, amount);
216+
}
217+
218+
/**
219+
* @notice Approves a spender to spend tokens on behalf of an owner.
220+
* @param spender The address to approve spending on behalf of the token owner.
221+
* @param amount The quantity of tokens to approve.
222+
*/
223+
function approve(address spender, uint256 amount) public override returns (bool) {
224+
_beforeApprove(msg.sender, spender, amount);
225+
return super.approve(spender, amount);
226+
}
227+
228+
/**
229+
* @notice Sets allowance based on token owner's signed approval.
230+
*
231+
* See https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
232+
* section].
233+
*
234+
* @param owner The account approving the tokens
235+
* @param spender The address to approve
236+
* @param amount Amount of tokens to approve
237+
*/
238+
function permit(address owner, address spender, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
239+
public
240+
override
241+
{
242+
_beforeApprove(owner, spender, amount);
243+
super.permit(owner, spender, amount, deadline, v, r, s);
244+
}
245+
246+
/*//////////////////////////////////////////////////////////////
247+
INTERNAL FUNCTIONS
248+
//////////////////////////////////////////////////////////////*/
249+
250+
/// @dev Sets contract URI
251+
function _setupContractURI(string memory _contractURI) internal {
252+
contractURI_ = _contractURI;
253+
emit ContractURIUpdated();
254+
}
255+
256+
/*//////////////////////////////////////////////////////////////
257+
CALLBACK INTERNAL FUNCTIONS
258+
//////////////////////////////////////////////////////////////*/
259+
260+
/// @dev Calls the beforeMint hook.
261+
function _beforeMint(address to, uint256 amount, bytes calldata data) internal virtual {
262+
_executeCallbackFunction(
263+
BeforeMintCallbackERC20.beforeMintERC20.selector,
264+
abi.encodeCall(BeforeMintCallbackERC20.beforeMintERC20, (to, amount, data))
265+
);
266+
}
267+
268+
/// @dev Calls the beforeMint hook.
269+
function _beforeMintWithSignature(address to, uint256 amount, bytes calldata data, address signer)
270+
internal
271+
virtual
272+
{
273+
_executeCallbackFunction(
274+
BeforeMintWithSignatureCallbackERC20.beforeMintWithSignatureERC20.selector,
275+
abi.encodeCall(
276+
BeforeMintWithSignatureCallbackERC20.beforeMintWithSignatureERC20, (to, amount, data, signer)
277+
)
278+
);
279+
}
280+
281+
/// @dev Calls the beforeTransfer hook, if installed.
282+
function _beforeTransfer(address from, address to, uint256 amount) internal virtual {
283+
_executeCallbackFunction(
284+
BeforeTransferCallbackERC20.beforeTransferERC20.selector,
285+
abi.encodeCall(BeforeTransferCallbackERC20.beforeTransferERC20, (from, to, amount))
286+
);
287+
}
288+
289+
/// @dev Calls the beforeBurn hook, if installed.
290+
function _beforeBurn(address from, uint256 amount, bytes calldata data) internal virtual {
291+
_executeCallbackFunction(
292+
BeforeBurnCallbackERC20.beforeBurnERC20.selector,
293+
abi.encodeCall(BeforeBurnCallbackERC20.beforeBurnERC20, (from, amount, data))
294+
);
295+
}
296+
297+
/// @dev Calls the beforeApprove hook, if installed.
298+
function _beforeApprove(address from, address to, uint256 amount) internal virtual {
299+
_executeCallbackFunction(
300+
BeforeApproveCallbackERC20.beforeApproveERC20.selector,
301+
abi.encodeCall(BeforeApproveCallbackERC20.beforeApproveERC20, (from, to, amount))
302+
);
303+
}
304+
305+
/// @dev Returns the domain name and version for EIP712.
306+
function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) {
307+
name = "ERC20Core";
308+
version = "1";
309+
}
310+
311+
}

0 commit comments

Comments
 (0)