@paimaexample/evm-contracts 0.3.0

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 (79) hide show
  1. package/README.md +7 -0
  2. package/deno.json +28 -0
  3. package/docs/templates/contract.hbs +144 -0
  4. package/docs/templates/helpers.js +61 -0
  5. package/docs/templates/page.hbs +7 -0
  6. package/docs/templates/properties.js +76 -0
  7. package/hardhat.config.ts +11 -0
  8. package/index.js +1 -0
  9. package/mod.ts +0 -0
  10. package/package.json +13 -0
  11. package/remappings.txt +1 -0
  12. package/src/companions/ERC165Contract.json +21 -0
  13. package/src/companions/ERC165Contract.ts +21 -0
  14. package/src/companions/ERC20Contract.json +222 -0
  15. package/src/companions/ERC20Contract.ts +222 -0
  16. package/src/companions/ERC6551RegistryContract.json +128 -0
  17. package/src/companions/ERC6551RegistryContract.ts +128 -0
  18. package/src/companions/ERC721Contract.json +248 -0
  19. package/src/companions/ERC721Contract.ts +222 -0
  20. package/src/companions/IERC1155Contract.json +295 -0
  21. package/src/companions/IERC1155Contract.ts +295 -0
  22. package/src/companions/OldERC6551RegistryContract.json +133 -0
  23. package/src/companions/OldERC6551RegistryContract.ts +133 -0
  24. package/src/companions/PaimaERC721Contract.json +787 -0
  25. package/src/companions/PaimaERC721Contract.ts +787 -0
  26. package/src/companions/PaimaL2Contract.json +134 -0
  27. package/src/companions/PaimaL2Contract.ts +134 -0
  28. package/src/companions/README.md +5 -0
  29. package/src/contracts/AnnotatedMintNft.sol +171 -0
  30. package/src/contracts/BaseState.sol +16 -0
  31. package/src/contracts/ERC1967.sol +43 -0
  32. package/src/contracts/Erc20NftSale.sol +186 -0
  33. package/src/contracts/GenericPayment.sol +60 -0
  34. package/src/contracts/NativeNftSale.sol +97 -0
  35. package/src/contracts/PaimaL2Contract.sol +54 -0
  36. package/src/contracts/Proxy/Erc20NftSaleProxy.sol +79 -0
  37. package/src/contracts/Proxy/GenericPaymentProxy.sol +64 -0
  38. package/src/contracts/Proxy/NativeNftSaleProxy.sol +72 -0
  39. package/src/contracts/Proxy/OrderbookDexProxy.sol +27 -0
  40. package/src/contracts/README.md +72 -0
  41. package/src/contracts/State.sol +25 -0
  42. package/src/contracts/dev/ERC721Dev.sol +13 -0
  43. package/src/contracts/dev/Erc20Dev.sol +13 -0
  44. package/src/contracts/dev/NativeNftSaleUpgradeDev.sol +9 -0
  45. package/src/contracts/dev/NftSaleUpgradeDev.sol +12 -0
  46. package/src/contracts/dev/NftTypeMapper.sol +38 -0
  47. package/src/contracts/dev/Token.sol +15 -0
  48. package/src/contracts/dev/UpgradeDev.sol +10 -0
  49. package/src/contracts/orderbook/IOrderbookDex.sol +215 -0
  50. package/src/contracts/orderbook/OrderbookDex.sol +435 -0
  51. package/src/contracts/token/IERC4906Agnostic.sol +17 -0
  52. package/src/contracts/token/IInverseAppProjected1155.sol +40 -0
  53. package/src/contracts/token/IInverseAppProjectedNft.sol +38 -0
  54. package/src/contracts/token/IInverseBaseProjected1155.sol +25 -0
  55. package/src/contracts/token/IInverseBaseProjectedNft.sol +29 -0
  56. package/src/contracts/token/IInverseProjected1155.sol +38 -0
  57. package/src/contracts/token/IInverseProjectedNft.sol +41 -0
  58. package/src/contracts/token/ITokenUri.sol +10 -0
  59. package/src/contracts/token/IUri.sol +13 -0
  60. package/src/contracts/token/InverseAppProjected1155.sol +218 -0
  61. package/src/contracts/token/InverseAppProjectedNft.sol +192 -0
  62. package/src/contracts/token/InverseBaseProjected1155.sol +170 -0
  63. package/src/contracts/token/InverseBaseProjectedNft.sol +158 -0
  64. package/src/plugin/common.ts +35 -0
  65. package/src/plugin/deployment.ts +161 -0
  66. package/src/plugin/mod.ts +6 -0
  67. package/src/plugin/paimaL2.ts +202 -0
  68. package/src/recommendedHardhat.ts +86 -0
  69. package/test/lib/StdInvariant.sol +96 -0
  70. package/test/lib/cheatcodes.sol +89 -0
  71. package/test/lib/console.sol +1884 -0
  72. package/test/lib/ctest.sol +678 -0
  73. package/test/src/InverseAppProjected1155.t.sol +207 -0
  74. package/test/src/InverseAppProjectedNft.t.sol +164 -0
  75. package/test/src/InverseBaseProjected1155.t.sol +171 -0
  76. package/test/src/InverseBaseProjectedNft.t.sol +141 -0
  77. package/test/src/OrderbookDex.t.sol +710 -0
  78. package/test/src/OrderbookDexInvariant.t.sol +426 -0
  79. package/test/src/PaimaL2ContractTest.sol +115 -0
@@ -0,0 +1,40 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ import {IInverseProjected1155} from "./IInverseProjected1155.sol";
5
+
6
+ /// @dev A Paima Inverse Projection ERC1155 token where initialization is handled by the app-layer.
7
+ /// A standard ERC1155 that can be freely minted and stores an unique `<minter, userTokenId>` pair (used in `tokenURI`) when minted.
8
+ interface IInverseAppProjected1155 is IInverseProjected1155 {
9
+ /// @dev Emitted when `value` amount of globally-enforced `tokenId` in combination with an unique `<minter, userTokenId>` pair is minted.
10
+ event Minted(
11
+ uint256 indexed tokenId,
12
+ address indexed minter,
13
+ uint256 indexed userTokenId,
14
+ uint256 value
15
+ );
16
+
17
+ /// @notice Returns the last nonce used (or 0 if the user has never minted)
18
+ /// @dev Useful if you need to either needs to
19
+ /// 1. Check if the nonce matches the expected value, or if more NFTs need to be minted
20
+ /// 2. Use a nonce algorithm where the next nonce depends on the current nonce
21
+ function currentNonce(address user) external view returns (uint256);
22
+
23
+ /// @dev Mints `value` of a new token to the transaction sender.
24
+ /// Increases the `currentTokenId`.
25
+ /// Reverts if transaction sender is a smart contract that does not implement IERC1155Receiver-onERC1155Received.
26
+ /// Emits the `Minted` event.
27
+ /// @param value the amount of tokens to mint.
28
+ /// @param data additional data to pass to the receiver contract.
29
+ /// @param verificationData any additional data to verify the validity of the mint
30
+ /// @return id of the minted token.
31
+ function mint(
32
+ uint256 value,
33
+ bytes memory data,
34
+ bytes memory verificationData
35
+ ) external returns (uint256);
36
+
37
+ /// @dev This works identically to the other function with an extra data parameter,
38
+ /// except this function just sets data to "".
39
+ function mint(uint256 value, bytes memory data) external returns (uint256);
40
+ }
@@ -0,0 +1,38 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ import {IInverseProjectedNft} from "./IInverseProjectedNft.sol";
5
+
6
+ /// @dev A Paima Inverse Projection NFT where initialization is handled by the app-layer.
7
+ /// A standard ERC721 that can be freely minted and stores an unique `<minter, userTokenId>` pair (used in tokenURI) when minted.
8
+ /// See PRC3 for more.
9
+ interface IInverseAppProjectedNft is IInverseProjectedNft {
10
+ /// @dev Emitted when the globally-enforced tokenId in combination with an unique `<minter, userTokenId>` pair is minted.
11
+ event Minted(uint256 indexed tokenId, address indexed minter, uint256 indexed userTokenId);
12
+
13
+ /// @dev Mints a new token to address `_to`
14
+ /// Increases the `totalSupply` and `currentTokenId`.
15
+ /// Reverts if `_to` is a zero address or if it refers to smart contract but does not implement IERC721Receiver-onERC721Received.
16
+ /// Emits the `Minted` event.
17
+ /// @param _to where to send the NFT to
18
+ /// @param _verificationData any additional data to verify the validity of the mint
19
+ /// @param _data any additional data to pass to the receiver contract
20
+ /// @return id of the minted token
21
+ function mint(
22
+ address _to,
23
+ bytes memory _verificationData,
24
+ bytes memory _data
25
+ ) external returns (uint256);
26
+
27
+ /// @dev Shorthand function that calls the `mint` function with empty `_data`.
28
+ function mint(address _to, bytes memory _verificationData) external returns (uint256);
29
+
30
+ /// @dev Shorthand function that calls the `mint` function with empty `_verificationData` and empty `_data`.
31
+ function mint(address _to) external returns (uint256);
32
+
33
+ /// @notice Returns the last nonce used (or 0 if the user has never minted)
34
+ /// @dev Useful if you need to either needs to
35
+ /// 1. Check if the nonce matches the expected value, or if more NFTs need to be minted
36
+ /// 2. Use a nonce algorithm where the next nonce depends on the current nonce
37
+ function currentNonce(address seller) external view returns (uint256);
38
+ }
@@ -0,0 +1,25 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ import {IInverseProjected1155} from "./IInverseProjected1155.sol";
5
+
6
+ /// @dev A Paima Inverse Projection ERC1155 token where initialization is handled by the base-layer.
7
+ /// A standard ERC1155 that accepts calldata in the mint function for any initialization data needed in a Paima dApp.
8
+ interface IInverseBaseProjected1155 is IInverseProjected1155 {
9
+ /// @dev Emitted when `value` amount of globally-enforced `tokenId` is minted, with `initialData` provided in the `mint` function parameters.
10
+ event Minted(uint256 indexed tokenId, uint256 value, string initialData);
11
+
12
+ /// @dev Mints `value` of a new token to transaction sender, passing `initialData` to be emitted in the event.
13
+ /// Increases the `currentTokenId`.
14
+ /// Reverts if transaction sender is a smart contract that does not implement IERC1155Receiver-onERC1155Received.
15
+ /// Emits the `Minted` event.
16
+ /// @param value the amount of tokens to mint.
17
+ /// @param data additional data to pass to the receiver contract.
18
+ /// @param initialData data that is emitted in the `Minted` event.
19
+ /// @return id of the minted token.
20
+ function mint(
21
+ uint256 value,
22
+ bytes memory data,
23
+ string calldata initialData
24
+ ) external returns (uint256);
25
+ }
@@ -0,0 +1,29 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ import {IInverseProjectedNft} from "./IInverseProjectedNft.sol";
5
+
6
+ /// @dev A Paima Inverse Projection NFT where initialization is handled by the base-layer.
7
+ /// A standard ERC721 that accepts calldata in the mint function for any initialization data needed in a Paima dApp.
8
+ /// See PRC3 for more.
9
+ interface IInverseBaseProjectedNft is IInverseProjectedNft {
10
+ /// @dev Emitted when the globally-enforced tokenId is minted, with `initialData` provided in the `mint` function parameters.
11
+ event Minted(uint256 indexed tokenId, string initialData);
12
+
13
+ /// @dev Mints a new token to address `_to`, passing `initialData` to be emitted in the event.
14
+ /// Increases the `totalSupply` and `currentTokenId`.
15
+ /// Reverts if `_to` is a zero address or if it refers to smart contract but does not implement IERC721Receiver-onERC721Received.
16
+ /// Emits the `Minted` event.
17
+ /// @param _to where to send the NFT to
18
+ /// @param initialData data that is emitted in the `Minted` event
19
+ /// @param data any additional data to pass to the receiver contract
20
+ /// @return id of the minted token
21
+ function mint(
22
+ address _to,
23
+ string calldata initialData,
24
+ bytes memory data
25
+ ) external returns (uint256);
26
+
27
+ /// @dev Shorthand function that calls the `mint` function with empty `data`.
28
+ function mint(address _to, string calldata initialData) external returns (uint256);
29
+ }
@@ -0,0 +1,38 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ import {IERC1155MetadataURI} from "@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol";
5
+ import {IERC4906Agnostic} from "./IERC4906Agnostic.sol";
6
+ import {IUri} from "./IUri.sol";
7
+
8
+ /// @dev A standard ERC1155 that can be burned and has a special uri function accepting a custom base URI.
9
+ interface IInverseProjected1155 is IERC1155MetadataURI, IERC4906Agnostic {
10
+ /// @dev Emitted when `baseExtension` is updated from `oldBaseExtension` to `newBaseExtension`.
11
+ event SetBaseExtension(string oldBaseExtension, string newBaseExtension);
12
+
13
+ /// @dev Emitted when `baseUri` is updated from `oldUri` to `newUri`.
14
+ event SetBaseURI(string oldUri, string newUri);
15
+
16
+ /// @dev Burns `value` amount of token of ID `id` from transaction sender.
17
+ /// Reverts if transaction sender's balance of `id` is less than `value`.
18
+ function burn(uint256 id, uint256 value) external;
19
+
20
+ /// @dev Burns batch of `values` amounts of tokens of IDs `ids` from transaction sender.
21
+ /// Reverts if transaction sender's balance of any `id` is less than `value`.
22
+ function burnBatch(uint256[] memory ids, uint256[] memory values) external;
23
+
24
+ /// @dev Sets `_URI` as the `baseURI`.
25
+ /// Callable only by the contract owner.
26
+ /// Emits the `SetBaseURI` event.
27
+ function setBaseURI(string memory _URI) external;
28
+
29
+ /// @dev Sets `_newBaseExtension` as the `baseExtension`.
30
+ /// Callable only by the contract owner.
31
+ function setBaseExtension(string memory _newBaseExtension) external;
32
+
33
+ /// @dev Returns the token URI of specified `id` using a custom base URI.
34
+ function uri(uint256 id, string memory customBaseUri) external view returns (string memory);
35
+
36
+ /// @dev Returns the token URI of specified `id` using a call to contract implementing `IUri`.
37
+ function uri(uint256 id, IUri customUriInterface) external view returns (string memory);
38
+ }
@@ -0,0 +1,41 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ import {IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
5
+ import {IERC4906} from "@openzeppelin/contracts/interfaces/IERC4906.sol";
6
+ import {ITokenUri} from "./ITokenUri.sol";
7
+
8
+ /// @dev A standard ERC721 that can be burned and has a special tokenURI function accepting a custom base URI.
9
+ /// See PRC3 for more.
10
+ interface IInverseProjectedNft is IERC4906, IERC721Metadata {
11
+ /// @dev Emitted when `baseExtension` is updated from `oldBaseExtension` to `newBaseExtension`.
12
+ event SetBaseExtension(string oldBaseExtension, string newBaseExtension);
13
+
14
+ /// @dev Emitted when `baseUri` is updated from `oldUri` to `newUri`.
15
+ event SetBaseURI(string oldUri, string newUri);
16
+
17
+ /// @dev Burns token of ID `_tokenId`. Callable only by the owner of the specified token.
18
+ /// Reverts if `_tokenId` is not existing.
19
+ function burn(uint256 _tokenId) external;
20
+
21
+ /// @dev Sets `_URI` as the `baseURI` of the NFT.
22
+ /// Callable only by the contract owner.
23
+ /// Emits the `SetBaseURI` event.
24
+ function setBaseURI(string memory _URI) external;
25
+
26
+ /// @dev Sets `_newBaseExtension` as the `baseExtension` of the NFT.
27
+ /// Callable only by the contract owner.
28
+ function setBaseExtension(string memory _newBaseExtension) external;
29
+
30
+ /// @dev Returns the token URI of specified `tokenId` using a custom base URI.
31
+ function tokenURI(
32
+ uint256 tokenId,
33
+ string memory customBaseUri
34
+ ) external view returns (string memory);
35
+
36
+ /// @dev Returns the token URI of specified `tokenId` using a call to contract implementing `ITokenUri`.
37
+ function tokenURI(
38
+ uint256 tokenId,
39
+ ITokenUri customUriInterface
40
+ ) external view returns (string memory);
41
+ }
@@ -0,0 +1,10 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ /// @dev An interface exposing the `tokenURI` function from IERC721Metadata.
5
+ interface ITokenUri {
6
+ /**
7
+ * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
8
+ */
9
+ function tokenURI(uint256 tokenId) external view returns (string memory);
10
+ }
@@ -0,0 +1,13 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ /// @dev An interface exposing the `uri` function from IERC1155MetadataURI.
5
+ interface IUri {
6
+ /**
7
+ * @dev Returns the URI for token type `id`.
8
+ *
9
+ * If the `\{id\}` substring is present in the URI, it must be replaced by
10
+ * clients with the actual token type ID.
11
+ */
12
+ function uri(uint256 id) external view returns (string memory);
13
+ }
@@ -0,0 +1,218 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
5
+ import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
6
+ import {IERC1155MetadataURI} from "@openzeppelin/contracts/token/ERC1155/extensions/IERC1155MetadataURI.sol";
7
+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
8
+ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
9
+ import {IInverseProjected1155} from "./IInverseProjected1155.sol";
10
+ import {IInverseAppProjected1155} from "./IInverseAppProjected1155.sol";
11
+ import {IUri} from "./IUri.sol";
12
+
13
+ struct MintEntry {
14
+ address minter;
15
+ uint256 userTokenId;
16
+ }
17
+
18
+ /// @dev A Paima Inverse Projection ERC1155 token where initialization is handled by the app-layer.
19
+ /// A standard ERC1155 that can be freely minted and stores an unique `<minter, userTokenId>` pair (used in `tokenURI`) when minted.
20
+ contract InverseAppProjected1155 is IInverseAppProjected1155, ERC1155, Ownable {
21
+ using Strings for uint256;
22
+
23
+ string public name;
24
+ string public symbol;
25
+
26
+ mapping(uint256 id => MintEntry) public tokenToMint;
27
+ mapping(address minter => uint256) public mintCount;
28
+ mapping(uint256 id => uint256) private _initialSupply;
29
+ mapping(uint256 id => uint256) private _totalSupply;
30
+
31
+ /// @dev The token ID that will be minted when calling the `mint` function.
32
+ uint256 public currentTokenId;
33
+ /// @dev Base URI that is used in the `uri` function to form the start of the token URI.
34
+ string public baseURI;
35
+ /// @dev Base extension that is used in the `uri` function to form the end of the token URI.
36
+ string public baseExtension;
37
+
38
+ /// @dev Sets the token's `_name`, `_symbol`, and transfers ownership to `_owner`.
39
+ /// Also sets `currentTokenId` to 1.
40
+ constructor(
41
+ string memory _name,
42
+ string memory _symbol,
43
+ address _owner
44
+ ) ERC1155("") Ownable(_owner) {
45
+ name = _name;
46
+ symbol = _symbol;
47
+ currentTokenId = 1;
48
+ baseExtension = ".json";
49
+ }
50
+
51
+ /// @dev Returns true if this contract implements the interface defined by `interfaceId`. See EIP165.
52
+ function supportsInterface(
53
+ bytes4 interfaceId
54
+ ) public view virtual override(IERC165, ERC1155) returns (bool) {
55
+ return
56
+ interfaceId == type(IInverseProjected1155).interfaceId ||
57
+ interfaceId == type(IInverseAppProjected1155).interfaceId ||
58
+ super.supportsInterface(interfaceId);
59
+ }
60
+
61
+ function currentNonce(address user) public view virtual returns (uint256) {
62
+ return mintCount[user];
63
+ }
64
+
65
+ function validateMint(address, bytes memory) internal virtual returns (bool) {
66
+ // Base contract allows any mint
67
+ // Replace this with any custom verification logic
68
+ return true;
69
+ }
70
+
71
+ /// @dev Returns the amount of token with ID `id` that had been initially minted.
72
+ function initialSupply(uint256 id) public view virtual returns (uint256) {
73
+ return _initialSupply[id];
74
+ }
75
+
76
+ /// @dev Returns the total amount of tokens with ID `id` that currently exists.
77
+ function totalSupply(uint256 id) public view virtual returns (uint256) {
78
+ return _totalSupply[id];
79
+ }
80
+
81
+ /// @dev Mints `value` of a new token to the transaction sender.
82
+ /// Increases the `currentTokenId`.
83
+ /// Reverts if transaction sender is a smart contract that does not implement IERC1155Receiver-onERC1155Received.
84
+ /// Emits the `Minted` event.
85
+ /// @param value the amount of tokens to mint.
86
+ /// @param data additional data to pass to the receiver contract.
87
+ /// @param verificationData any additional data to verify the validity of the mint
88
+ /// @return id of the minted token.
89
+ function mint(
90
+ uint256 value,
91
+ bytes memory data,
92
+ bytes memory verificationData
93
+ ) public virtual returns (uint256) {
94
+ require(
95
+ validateMint(msg.sender, verificationData),
96
+ "InverseAppProjected1155: invalid verification data"
97
+ );
98
+ uint256 tokenId = currentTokenId;
99
+ _mint(msg.sender, tokenId, value, data);
100
+ mintCount[msg.sender] += 1;
101
+ uint256 userTokenId = mintCount[msg.sender];
102
+ tokenToMint[tokenId] = MintEntry(msg.sender, userTokenId);
103
+ _initialSupply[tokenId] = value;
104
+
105
+ currentTokenId++;
106
+
107
+ emit Minted(tokenId, msg.sender, userTokenId, value);
108
+ return tokenId;
109
+ }
110
+
111
+ /// @dev This works identically to the other function with an extra data parameter,
112
+ /// except this function just sets data to "".
113
+ function mint(uint256 value, bytes memory data) public returns (uint256) {
114
+ return mint(value, data, bytes(""));
115
+ }
116
+
117
+ /// @dev Burns `value` amount of token of ID `id` from transaction sender.
118
+ /// Reverts if transaction sender's balance of `id` is less than `value`.
119
+ function burn(uint256 id, uint256 value) public virtual {
120
+ _burn(msg.sender, id, value);
121
+ }
122
+
123
+ /// @dev Burns batch of `values` amounts of tokens of IDs `ids` from transaction sender.
124
+ /// Reverts if transaction sender's balance of any `id` is less than `value`.
125
+ function burnBatch(uint256[] memory ids, uint256[] memory values) public virtual {
126
+ _burnBatch(msg.sender, ids, values);
127
+ }
128
+
129
+ /// @dev Returns the token URI of specified `id` using the default set base URI.
130
+ function uri(
131
+ uint256 id
132
+ ) public view virtual override(ERC1155, IERC1155MetadataURI) returns (string memory) {
133
+ return uri(id, baseURI);
134
+ }
135
+
136
+ /// @dev Returns the token URI of specified `id` using a custom base URI.
137
+ function uri(
138
+ uint256 id,
139
+ string memory customBaseUri
140
+ ) public view virtual returns (string memory) {
141
+ require(_totalSupply[id] > 0, "InverseAppProjected1155: URI query for nonexistent token");
142
+ MintEntry memory entry = tokenToMint[id];
143
+ string memory URI = bytes(customBaseUri).length > 0
144
+ ? string.concat(
145
+ customBaseUri,
146
+ "eip155:",
147
+ block.chainid.toString(),
148
+ "/",
149
+ Strings.toHexString(uint160(entry.minter), 20),
150
+ "/",
151
+ entry.userTokenId.toString(),
152
+ "/",
153
+ _initialSupply[id].toString()
154
+ )
155
+ : "";
156
+ return string(abi.encodePacked(URI, baseExtension));
157
+ }
158
+
159
+ /// @dev Returns the token URI of specified `id` using a call to contract implementing `IUri`.
160
+ function uri(uint256 id, IUri customUriInterface) public view virtual returns (string memory) {
161
+ return customUriInterface.uri(id);
162
+ }
163
+
164
+ /// @dev Sets `_URI` as the `baseURI` of the NFT.
165
+ /// Callable only by the contract owner.
166
+ /// Emits the `SetBaseURI` event.
167
+ function setBaseURI(string memory _URI) public virtual onlyOwner {
168
+ string memory oldURI = baseURI;
169
+ baseURI = _URI;
170
+ emit SetBaseURI(oldURI, _URI);
171
+ }
172
+
173
+ /// @dev Sets `_newBaseExtension` as the `baseExtension` of the NFT.
174
+ /// Callable only by the contract owner.
175
+ function setBaseExtension(string memory _newBaseExtension) public virtual onlyOwner {
176
+ string memory oldBaseExtension = baseExtension;
177
+ baseExtension = _newBaseExtension;
178
+ emit SetBaseURI(oldBaseExtension, _newBaseExtension);
179
+ }
180
+
181
+ /// @dev Function that emits an event to notify third-parties (e.g. NFT marketplaces) about
182
+ /// an update to consecutive range of tokens. Can be overridden in inheriting contract.
183
+ function updateMetadataBatch(uint256 _fromTokenId, uint256 _toTokenId) public virtual {
184
+ emit BatchMetadataUpdate(_fromTokenId, _toTokenId);
185
+ }
186
+
187
+ /// @dev Function that emits an event to notify third-parties (e.g. NFT marketplaces) about
188
+ /// an update to a single token. Can be overridden in inheriting contract.
189
+ function updateMetadata(uint256 _tokenId) public virtual {
190
+ emit MetadataUpdate(_tokenId);
191
+ }
192
+
193
+ /// @dev See {ERC1155-_update}.
194
+ function _update(
195
+ address from,
196
+ address to,
197
+ uint256[] memory ids,
198
+ uint256[] memory values
199
+ ) internal virtual override {
200
+ super._update(from, to, ids, values);
201
+
202
+ if (from == address(0)) {
203
+ for (uint256 i = 0; i < ids.length; ++i) {
204
+ // Overflow check required: The rest of the code assumes that totalSupply never overflows
205
+ _totalSupply[ids[i]] += values[i];
206
+ }
207
+ }
208
+
209
+ if (to == address(0)) {
210
+ for (uint256 i = 0; i < ids.length; ++i) {
211
+ unchecked {
212
+ // Overflow not possible: values[i] <= balanceOf(from, ids[i]) <= totalSupply(ids[i])
213
+ _totalSupply[ids[i]] -= values[i];
214
+ }
215
+ }
216
+ }
217
+ }
218
+ }
@@ -0,0 +1,192 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
5
+ import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
6
+ import {IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol";
7
+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
8
+ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
9
+ import {IInverseProjectedNft} from "./IInverseProjectedNft.sol";
10
+ import {IInverseAppProjectedNft} from "./IInverseAppProjectedNft.sol";
11
+ import {ITokenUri} from "./ITokenUri.sol";
12
+
13
+ struct MintEntry {
14
+ address minter;
15
+ uint256 userTokenId;
16
+ }
17
+
18
+ /// @dev A standard ERC721 that accepts calldata in the mint function for any initialization data needed in a Paima dApp.
19
+ /// See PRC3 for more.
20
+ contract InverseAppProjectedNft is IInverseAppProjectedNft, ERC721, Ownable {
21
+ using Strings for uint256;
22
+
23
+ /// @dev tokenId => MintEntry.
24
+ mapping(uint256 => MintEntry) public tokenToMint;
25
+ mapping(address => uint256) public mintCount;
26
+
27
+ /// @dev The token ID that will be minted when calling the `mint` function.
28
+ uint256 public currentTokenId;
29
+ /// @dev Base URI that is used in the `tokenURI` function to form the start of the token URI.
30
+ string public baseURI;
31
+ /// @dev Total token supply, increased by minting and decreased by burning.
32
+ uint256 public totalSupply;
33
+ /// @dev Base extension that is used in the `tokenURI` function to form the end of the token URI.
34
+ string public baseExtension;
35
+
36
+ /// @dev Reverts if `msg.sender` is not the specified token's owner.
37
+ modifier onlyTokenOwner(uint256 tokenId) {
38
+ require(msg.sender == ownerOf(tokenId), "InverseAppProjectedNft: not owner");
39
+ _;
40
+ }
41
+
42
+ /// @dev Sets the NFT's `name`, `symbol`, and transfers ownership to `owner`.
43
+ /// Also sets `currentTokenId` to 1 and `baseExtension` to `".json"`.
44
+ constructor(
45
+ string memory name,
46
+ string memory symbol,
47
+ address owner
48
+ ) ERC721(name, symbol) Ownable(owner) {
49
+ currentTokenId = 1;
50
+ baseExtension = ".json";
51
+ }
52
+
53
+ /// @dev Returns true if this contract implements the interface defined by `interfaceId`. See EIP165.
54
+ function supportsInterface(
55
+ bytes4 interfaceId
56
+ ) public view virtual override(IERC165, ERC721) returns (bool) {
57
+ return
58
+ interfaceId == type(IInverseProjectedNft).interfaceId ||
59
+ interfaceId == type(IInverseAppProjectedNft).interfaceId ||
60
+ interfaceId == bytes4(0x49064906) ||
61
+ super.supportsInterface(interfaceId);
62
+ }
63
+
64
+ function currentNonce(address user) public view returns (uint256) {
65
+ return mintCount[user];
66
+ }
67
+
68
+ function validateMint(address, bytes memory) internal virtual returns (bool) {
69
+ // Base contract allows any mint
70
+ // Replace this with any custom verification logic
71
+ return true;
72
+ }
73
+
74
+ /// @dev Mints a new token to address `_to`
75
+ /// Increases the `totalSupply` and `currentTokenId`.
76
+ /// Reverts if `_to` is a zero address or if it refers to smart contract but does not implement IERC721Receiver-onERC721Received.
77
+ /// Emits the `Minted` event.
78
+ /// @param _to where to send the NFT to
79
+ /// @param _verificationData any additional data to verify the validity of the mint
80
+ /// @param _data any additional data to pass to the receiver contract
81
+ /// @return id of the minted token
82
+ function mint(
83
+ address _to,
84
+ bytes memory _verificationData,
85
+ bytes memory _data
86
+ ) public virtual returns (uint256) {
87
+ require(_to != address(0), "InverseAppProjectedNft: zero receiver address");
88
+ require(
89
+ validateMint(_to, _verificationData),
90
+ "InverseAppProjectedNft: invalid verification data"
91
+ );
92
+
93
+ uint256 tokenId = currentTokenId;
94
+ _safeMint(_to, tokenId, _data);
95
+ mintCount[_to] += 1;
96
+ uint256 userTokenId = mintCount[_to];
97
+ tokenToMint[tokenId] = MintEntry(_to, userTokenId);
98
+
99
+ totalSupply++;
100
+ currentTokenId++;
101
+
102
+ emit Minted(tokenId, _to, userTokenId);
103
+ return tokenId;
104
+ }
105
+
106
+ /// @dev Shorthand function that calls the `mint` function with empty `_data`.
107
+ function mint(address _to, bytes memory _verificationData) public virtual returns (uint256) {
108
+ return mint(_to, _verificationData, bytes(""));
109
+ }
110
+
111
+ /// @dev Shorthand function that calls the `mint` function with empty `_verificationData` and empty `_data`.
112
+ function mint(address _to) public returns (uint256) {
113
+ return mint(_to, bytes(""), bytes(""));
114
+ }
115
+
116
+ /// @dev Burns token of ID `_tokenId`. Callable only by the owner of the specified token.
117
+ /// Reverts if `_tokenId` does not exist.
118
+ function burn(uint256 _tokenId) public virtual onlyTokenOwner(_tokenId) {
119
+ totalSupply--;
120
+ _burn(_tokenId);
121
+ }
122
+
123
+ /// @dev Returns the `baseURI` of this NFT.
124
+ function _baseURI() internal view virtual override returns (string memory) {
125
+ return baseURI;
126
+ }
127
+
128
+ /// @dev Returns the token URI of specified `tokenId` using the default set base URI.
129
+ function tokenURI(
130
+ uint256 tokenId
131
+ ) public view virtual override(ERC721, IERC721Metadata) returns (string memory) {
132
+ return tokenURI(tokenId, _baseURI());
133
+ }
134
+
135
+ /// @dev Returns the token URI of specified `tokenId` using a custom base URI.
136
+ function tokenURI(
137
+ uint256 tokenId,
138
+ string memory customBaseUri
139
+ ) public view virtual returns (string memory) {
140
+ _requireOwned(tokenId);
141
+ MintEntry memory entry = tokenToMint[tokenId];
142
+ string memory URI = bytes(customBaseUri).length > 0
143
+ ? string.concat(
144
+ customBaseUri,
145
+ "eip155:",
146
+ block.chainid.toString(),
147
+ "/",
148
+ Strings.toHexString(uint160(entry.minter), 20),
149
+ "/",
150
+ entry.userTokenId.toString()
151
+ )
152
+ : "";
153
+ return string(abi.encodePacked(URI, baseExtension));
154
+ }
155
+
156
+ /// @dev Returns the token URI of specified `tokenId` using a call to contract implementing `ITokenUri`.
157
+ function tokenURI(
158
+ uint256 tokenId,
159
+ ITokenUri customUriInterface
160
+ ) public view returns (string memory) {
161
+ return customUriInterface.tokenURI(tokenId);
162
+ }
163
+
164
+ /// @dev Sets `_URI` as the `baseURI` of the NFT.
165
+ /// Callable only by the contract owner.
166
+ /// Emits the `SetBaseURI` event.
167
+ function setBaseURI(string memory _URI) public virtual onlyOwner {
168
+ string memory oldURI = baseURI;
169
+ baseURI = _URI;
170
+ emit SetBaseURI(oldURI, _URI);
171
+ }
172
+
173
+ /// @dev Sets `_newBaseExtension` as the `baseExtension` of the NFT.
174
+ /// Callable only by the contract owner.
175
+ function setBaseExtension(string memory _newBaseExtension) public virtual onlyOwner {
176
+ string memory oldBaseExtension = baseExtension;
177
+ baseExtension = _newBaseExtension;
178
+ emit SetBaseURI(oldBaseExtension, _newBaseExtension);
179
+ }
180
+
181
+ /// @dev Function that emits an event to notify third-parties (e.g. NFT marketplaces) about
182
+ /// an update to consecutive range of tokens. Can be overridden in inheriting contract.
183
+ function updateMetadataBatch(uint256 _fromTokenId, uint256 _toTokenId) public virtual {
184
+ emit BatchMetadataUpdate(_fromTokenId, _toTokenId);
185
+ }
186
+
187
+ /// @dev Function that emits an event to notify third-parties (e.g. NFT marketplaces) about
188
+ /// an update to a single token. Can be overridden in inheriting contract.
189
+ function updateMetadata(uint256 _tokenId) public virtual {
190
+ emit MetadataUpdate(_tokenId);
191
+ }
192
+ }