Skip to content

Commit 01bac78

Browse files
Krishang NadgaudaKrishang Nadgauda
authored andcommitted
pull from origin
1 parent d14c7a6 commit 01bac78

32 files changed

+930
-55
lines changed

contracts/Forwarder.sol

Lines changed: 0 additions & 12 deletions
This file was deleted.

contracts/base/ERC1155LazyMint.sol

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import "../extension/Ownable.sol";
99
import "../extension/Royalty.sol";
1010
import "../extension/BatchMintMetadata.sol";
1111
import "../extension/LazyMint.sol";
12+
import "../extension/interface/IClaimableERC1155.sol";
1213

1314
import "../lib/TWStrings.sol";
1415

@@ -44,7 +45,16 @@ import "../lib/TWStrings.sol";
4445
*
4546
*/
4647

47-
contract ERC1155LazyMint is ERC1155, ContractMetadata, Ownable, Royalty, Multicall, BatchMintMetadata, LazyMint {
48+
contract ERC1155LazyMint is
49+
ERC1155,
50+
ContractMetadata,
51+
Ownable,
52+
Royalty,
53+
Multicall,
54+
BatchMintMetadata,
55+
LazyMint,
56+
IClaimableERC1155
57+
{
4858
using TWStrings for uint256;
4959

5060
/*//////////////////////////////////////////////////////////////
@@ -106,6 +116,7 @@ contract ERC1155LazyMint is ERC1155, ContractMetadata, Ownable, Royalty, Multica
106116
require(_tokenId < nextTokenIdToMint(), "invalid id");
107117

108118
_mint(_receiver, _tokenId, _quantity, "");
119+
emit TokensClaimed(msg.sender, _receiver, _tokenId, _quantity);
109120
}
110121

111122
/**

contracts/base/ERC721LazyMint.sol

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import "../extension/Ownable.sol";
99
import "../extension/Royalty.sol";
1010
import "../extension/BatchMintMetadata.sol";
1111
import "../extension/LazyMint.sol";
12+
import "../extension/interface/IClaimableERC721.sol";
1213

1314
import "../lib/TWStrings.sol";
1415

@@ -38,7 +39,16 @@ import "../lib/TWStrings.sol";
3839
* without paying the gas cost for actually minting the NFTs.
3940
*/
4041

41-
contract ERC721LazyMint is ERC721A, ContractMetadata, Multicall, Ownable, Royalty, BatchMintMetadata, LazyMint {
42+
contract ERC721LazyMint is
43+
ERC721A,
44+
ContractMetadata,
45+
Multicall,
46+
Ownable,
47+
Royalty,
48+
BatchMintMetadata,
49+
LazyMint,
50+
IClaimableERC721
51+
{
4252
using TWStrings for uint256;
4353

4454
/*//////////////////////////////////////////////////////////////
@@ -99,9 +109,10 @@ contract ERC721LazyMint is ERC721A, ContractMetadata, Multicall, Ownable, Royalt
99109
*/
100110
function claim(address _receiver, uint256 _quantity) public payable virtual {
101111
verifyClaim(msg.sender, _quantity); // add your claim verification logic by overriding this function
102-
112+
uint256 startTokenId = _currentIndex;
103113
require(_currentIndex + _quantity <= nextTokenIdToLazyMint, "Not enough lazy minted tokens.");
104114
_safeMint(_receiver, _quantity, "");
115+
emit TokensClaimed(msg.sender, _receiver, startTokenId, _quantity);
105116
}
106117

107118
/**

contracts/extension/TokenBundle.sol

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ abstract contract TokenBundle is ITokenBundle {
3838
function _createBundle(Token[] calldata _tokensToBind, uint256 _bundleId) internal {
3939
uint256 targetCount = _tokensToBind.length;
4040

41-
require(targetCount > 0, "no tokens to bind");
42-
require(bundle[_bundleId].count == 0, "existent at bundleId");
41+
require(targetCount > 0, "!Tokens");
42+
require(bundle[_bundleId].count == 0, "id exists");
4343

4444
for (uint256 i = 0; i < targetCount; i += 1) {
4545
_checkTokenType(_tokensToBind[i]);
@@ -51,7 +51,7 @@ abstract contract TokenBundle is ITokenBundle {
5151

5252
/// @dev Lets the calling contract update a bundle, by passing in a list of tokens and a unique id.
5353
function _updateBundle(Token[] memory _tokensToBind, uint256 _bundleId) internal {
54-
require(_tokensToBind.length > 0, "no tokens to bind");
54+
require(_tokensToBind.length > 0, "!Tokens");
5555

5656
uint256 currentCount = bundle[_bundleId].count;
5757
uint256 targetCount = _tokensToBind.length;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.0;
3+
4+
interface IClaimableERC1155 {
5+
/// @dev Emitted when tokens are claimed
6+
event TokensClaimed(
7+
address indexed claimer,
8+
address indexed receiver,
9+
uint256 indexed tokenId,
10+
uint256 quantityClaimed
11+
);
12+
13+
/**
14+
* @notice Lets an address claim multiple lazy minted NFTs at once to a recipient.
15+
* Contract creators should override this function to create custom logic for claiming,
16+
* for e.g. price collection, allowlist, max quantity, etc.
17+
*
18+
* @dev The logic in the `verifyClaim` function determines whether the caller is authorized to mint NFTs.
19+
*
20+
* @param _receiver The recipient of the tokens to mint.
21+
* @param _tokenId The tokenId of the lazy minted NFT to mint.
22+
* @param _quantity The number of tokens to mint.
23+
*/
24+
function claim(
25+
address _receiver,
26+
uint256 _tokenId,
27+
uint256 _quantity
28+
) external payable;
29+
30+
/**
31+
* @notice Override this function to add logic for claim verification, based on conditions
32+
* such as allowlist, price, max quantity etc.
33+
*
34+
* @dev Checks a request to claim NFTs against a custom condition.
35+
*
36+
* @param _claimer Caller of the claim function.
37+
* @param _tokenId The tokenId of the lazy minted NFT to mint.
38+
* @param _quantity The number of NFTs being claimed.
39+
*/
40+
function verifyClaim(
41+
address _claimer,
42+
uint256 _tokenId,
43+
uint256 _quantity
44+
) external view;
45+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.0;
3+
4+
interface IClaimableERC721 {
5+
/// @dev Emitted when tokens are claimed
6+
event TokensClaimed(
7+
address indexed claimer,
8+
address indexed receiver,
9+
uint256 indexed startTokenId,
10+
uint256 quantityClaimed
11+
);
12+
13+
/**
14+
* @notice Lets an address claim multiple lazy minted NFTs at once to a recipient.
15+
* Contract creators should override this function to create custom logic for claiming,
16+
* for e.g. price collection, allowlist, max quantity, etc.
17+
*
18+
* @dev The logic in the `verifyClaim` function determines whether the caller is authorized to mint NFTs.
19+
*
20+
* @param _receiver The recipient of the NFT to mint.
21+
* @param _quantity The number of NFTs to mint.
22+
*/
23+
function claim(address _receiver, uint256 _quantity) external payable;
24+
25+
/**
26+
* @notice Override this function to add logic for claim verification, based on conditions
27+
* such as allowlist, price, max quantity etc.
28+
*
29+
* @dev Checks a request to claim NFTs against a custom condition.
30+
*
31+
* @param _claimer Caller of the claim function.
32+
* @param _quantity The number of NFTs being claimed.
33+
*/
34+
function verifyClaim(address _claimer, uint256 _quantity) external view;
35+
}

contracts/forwarder/Forwarder.sol

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.0;
4+
5+
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
6+
import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
7+
8+
/*
9+
* @dev Minimal forwarder for GSNv2
10+
*/
11+
contract Forwarder is EIP712 {
12+
using ECDSA for bytes32;
13+
14+
struct ForwardRequest {
15+
address from;
16+
address to;
17+
uint256 value;
18+
uint256 gas;
19+
uint256 nonce;
20+
bytes data;
21+
}
22+
23+
bytes32 private constant TYPEHASH =
24+
keccak256("ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,bytes data)");
25+
26+
mapping(address => uint256) private _nonces;
27+
28+
constructor() EIP712("GSNv2 Forwarder", "0.0.1") {}
29+
30+
function getNonce(address from) public view returns (uint256) {
31+
return _nonces[from];
32+
}
33+
34+
function verify(ForwardRequest calldata req, bytes calldata signature) public view returns (bool) {
35+
address signer = _hashTypedDataV4(
36+
keccak256(abi.encode(TYPEHASH, req.from, req.to, req.value, req.gas, req.nonce, keccak256(req.data)))
37+
).recover(signature);
38+
39+
return _nonces[req.from] == req.nonce && signer == req.from;
40+
}
41+
42+
function execute(ForwardRequest calldata req, bytes calldata signature)
43+
public
44+
payable
45+
returns (bool, bytes memory)
46+
{
47+
require(verify(req, signature), "MinimalForwarder: signature does not match request");
48+
_nonces[req.from] = req.nonce + 1;
49+
50+
// solhint-disable-next-line avoid-low-level-calls
51+
(bool success, bytes memory result) = req.to.call{ gas: req.gas, value: req.value }(
52+
abi.encodePacked(req.data, req.from)
53+
);
54+
55+
if (!success) {
56+
// Next 5 lines from https://ethereum.stackexchange.com/a/83577
57+
if (result.length < 68) revert("Transaction reverted silently");
58+
assembly {
59+
result := add(result, 0x04)
60+
}
61+
revert(abi.decode(result, (string)));
62+
}
63+
// Check gas: https://ronan.eth.link/blog/ethereum-gas-dangers/
64+
assert(gasleft() > req.gas / 63);
65+
return (success, result);
66+
}
67+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// SPDX-License-Identifier: MIT
2+
// OpenZeppelin Contracts (last updated v4.5.0) (metatx/MinimalForwarder.sol)
3+
4+
pragma solidity ^0.8.0;
5+
6+
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
7+
import "../openzeppelin-presets/cryptography/EIP712ChainlessDomain.sol";
8+
9+
/**
10+
* @dev Simple minimal forwarder to be used together with an ERC2771 compatible contract. See {ERC2771Context}.
11+
*/
12+
contract ForwarderChainlessDomain is EIP712ChainlessDomain {
13+
using ECDSA for bytes32;
14+
15+
struct ForwardRequest {
16+
address from;
17+
address to;
18+
uint256 value;
19+
uint256 gas;
20+
uint256 nonce;
21+
bytes data;
22+
uint256 chainid;
23+
}
24+
25+
bytes32 private constant _TYPEHASH =
26+
keccak256(
27+
"ForwardRequest(address from,address to,uint256 value,uint256 gas,uint256 nonce,bytes data,uint256 chainid)"
28+
);
29+
30+
mapping(address => uint256) private _nonces;
31+
32+
constructor() EIP712ChainlessDomain("GSNv2 Forwarder", "0.0.1") {}
33+
34+
function getNonce(address from) public view returns (uint256) {
35+
return _nonces[from];
36+
}
37+
38+
function verify(ForwardRequest calldata req, bytes calldata signature) public view returns (bool) {
39+
address signer = _hashTypedDataV4(
40+
keccak256(
41+
abi.encode(
42+
_TYPEHASH,
43+
req.from,
44+
req.to,
45+
req.value,
46+
req.gas,
47+
req.nonce,
48+
keccak256(req.data),
49+
block.chainid
50+
)
51+
)
52+
).recover(signature);
53+
return _nonces[req.from] == req.nonce && signer == req.from;
54+
}
55+
56+
function execute(ForwardRequest calldata req, bytes calldata signature)
57+
public
58+
payable
59+
returns (bool, bytes memory)
60+
{
61+
// require(req.chainid == block.chainid, "MinimalForwarder: invalid chainId");
62+
require(verify(req, signature), "MinimalForwarder: signature does not match request");
63+
_nonces[req.from] = req.nonce + 1;
64+
65+
(bool success, bytes memory returndata) = req.to.call{ gas: req.gas, value: req.value }(
66+
abi.encodePacked(req.data, req.from)
67+
);
68+
69+
// Validate that the relayer has sent enough gas for the call.
70+
// See https://ronan.eth.link/blog/ethereum-gas-dangers/
71+
if (gasleft() <= req.gas / 63) {
72+
// We explicitly trigger invalid opcode to consume all gas and bubble-up the effects, since
73+
// neither revert or assert consume all gas since Solidity 0.8.0
74+
// https://docs.soliditylang.org/en/v0.8.0/control-structures.html#panic-via-assert-and-error-via-require
75+
assembly {
76+
invalid()
77+
}
78+
}
79+
80+
return (success, returndata);
81+
}
82+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
pragma solidity ^0.8.11;
3+
4+
import "@openzeppelin/contracts/metatx/ERC2771Context.sol";
5+
6+
contract ForwarderConsumer is ERC2771Context {
7+
address public caller;
8+
9+
constructor(address trustedForwarder) ERC2771Context(trustedForwarder) {}
10+
11+
function setCaller() external {
12+
caller = _msgSender();
13+
}
14+
}

contracts/ForwarderEOAOnly.sol renamed to contracts/forwarder/ForwarderEOAOnly.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-License-Identifier: Apache-2.0
22
pragma solidity ^0.8.11;
33

4-
import "./openzeppelin-presets/metatx/MinimalForwarderEOAOnly.sol";
4+
import "../openzeppelin-presets/metatx/MinimalForwarderEOAOnly.sol";
55

66
/*
77
* @dev Minimal forwarder for GSNv2

0 commit comments

Comments
 (0)