@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,207 @@
1
+ // SPDX-License-Identifier: UNLICENSED
2
+ pragma solidity ^0.8.18;
3
+
4
+ import "../lib/cheatcodes.sol";
5
+ import "../lib/console.sol";
6
+ import "../lib/ctest.sol";
7
+ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
8
+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
9
+ import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
10
+ import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
11
+ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
12
+ import {IERC4906Agnostic} from "../../src/contracts/token/IERC4906Agnostic.sol";
13
+ import {InverseAppProjected1155} from "../../src/contracts/token/InverseAppProjected1155.sol";
14
+ import {IInverseAppProjected1155} from "../../src/contracts/token/IInverseAppProjected1155.sol";
15
+ import {IInverseProjected1155} from "../../src/contracts/token/IInverseProjected1155.sol";
16
+ import {IUri} from "../../src/contracts/token/IUri.sol";
17
+
18
+ contract MockTokenUri is IUri {
19
+ using Strings for uint256;
20
+
21
+ function uri(uint256 id) external view override returns (string memory) {
22
+ return string.concat("mock://", id.toString());
23
+ }
24
+ }
25
+
26
+ contract InverseAppProjected1155Test is CTest, ERC1155Holder {
27
+ using Strings for uint256;
28
+
29
+ CheatCodes vm = CheatCodes(HEVM_ADDRESS);
30
+ InverseAppProjected1155 public token;
31
+ uint256 ownedTokenId;
32
+ string baseURI = "192.168.0.1/";
33
+ address alice = 0x078D888E40faAe0f32594342c85940AF3949E666;
34
+
35
+ function setUp() public {
36
+ token = new InverseAppProjected1155("ABC", "XYZ", address(this));
37
+ ownedTokenId = token.mint(1000, bytes(""), bytes(""));
38
+ token.setBaseURI(baseURI);
39
+ }
40
+
41
+ function test_CanBurn() public {
42
+ uint256 amount = token.balanceOf(address(this), ownedTokenId);
43
+ vm.expectEmit(true, true, true, true);
44
+ emit IERC1155.TransferSingle(
45
+ address(this),
46
+ address(this),
47
+ address(0),
48
+ ownedTokenId,
49
+ amount
50
+ );
51
+ token.burn(ownedTokenId, amount);
52
+ }
53
+
54
+ function test_CanBurnBatch() public {
55
+ uint256 balance = token.balanceOf(address(this), ownedTokenId);
56
+
57
+ uint256[] memory ids = new uint256[](2);
58
+ ids[0] = ownedTokenId;
59
+ ids[1] = ownedTokenId;
60
+ uint256[] memory values = new uint256[](2);
61
+ values[0] = balance / 2;
62
+ values[1] = balance / 4;
63
+
64
+ uint256 balanceBefore = token.balanceOf(address(this), ownedTokenId);
65
+ uint256 supplyBefore = token.totalSupply(ownedTokenId);
66
+ vm.expectEmit(true, true, true, true);
67
+ emit IERC1155.TransferBatch(address(this), address(this), address(0), ids, values);
68
+ token.burnBatch(ids, values);
69
+
70
+ assertEq(
71
+ token.balanceOf(address(this), ownedTokenId),
72
+ balanceBefore - values[0] - values[1]
73
+ );
74
+ assertEq(token.totalSupply(ownedTokenId), supplyBefore - values[0] - values[1]);
75
+ }
76
+
77
+ function test_CanMint() public {
78
+ address minter = alice;
79
+ uint256 userNonce = token.currentNonce(minter) + 1;
80
+ uint256 tokenId = token.currentTokenId();
81
+ uint256 value = 500;
82
+ uint256 tokenValueUserBefore = token.balanceOf(minter, tokenId);
83
+
84
+ vm.prank(minter);
85
+ vm.expectEmit(true, true, true, true);
86
+ emit IInverseAppProjected1155.Minted(tokenId, minter, userNonce, value);
87
+ token.mint(value, bytes(""), bytes(""));
88
+
89
+ assertEq(token.balanceOf(minter, tokenId), tokenValueUserBefore + value);
90
+ }
91
+
92
+ function test_CanMintNoVerificationData() public {
93
+ address minter = alice;
94
+ uint256 userNonce = token.currentNonce(minter) + 1;
95
+ uint256 tokenId = token.currentTokenId();
96
+ uint256 value = 500;
97
+ uint256 tokenValueUserBefore = token.balanceOf(minter, 0);
98
+
99
+ vm.prank(minter);
100
+ vm.expectEmit(true, true, true, true);
101
+ emit IInverseAppProjected1155.Minted(tokenId, minter, userNonce, value);
102
+ token.mint(value, bytes(""));
103
+
104
+ assertEq(token.balanceOf(minter, tokenId), tokenValueUserBefore + value);
105
+ }
106
+
107
+ function test_CanTransfer() public {
108
+ address sender = address(this);
109
+ address recipient = alice;
110
+
111
+ uint256 tokenValueMinterBefore = token.balanceOf(sender, ownedTokenId);
112
+ uint256 tokenValueRecipientBefore = token.balanceOf(recipient, ownedTokenId);
113
+ uint256 value = tokenValueMinterBefore - 100;
114
+
115
+ token.safeTransferFrom(sender, recipient, ownedTokenId, value, bytes(""));
116
+
117
+ assertEq(token.balanceOf(sender, ownedTokenId), tokenValueMinterBefore - value);
118
+ assertEq(token.balanceOf(recipient, ownedTokenId), tokenValueRecipientBefore + value);
119
+ }
120
+
121
+ function test_TokenUriUsesBaseUriByDefault() public {
122
+ address user = address(this);
123
+ uint256 value = 1000;
124
+ uint256 tokenId = token.mint(value, bytes(""), bytes(""));
125
+ uint256 userTokenId = token.currentNonce(user);
126
+ string memory result = token.uri(tokenId);
127
+ assertEq(
128
+ result,
129
+ string.concat(
130
+ baseURI,
131
+ "eip155:",
132
+ block.chainid.toString(),
133
+ "/",
134
+ Strings.toHexString(uint160(user), 20),
135
+ "/",
136
+ userTokenId.toString(),
137
+ "/",
138
+ value.toString(),
139
+ ".json"
140
+ )
141
+ );
142
+ }
143
+
144
+ function test_TokenUriUsingCustomBaseUri() public {
145
+ address user = address(this);
146
+ uint256 value = 1000;
147
+ uint256 tokenId = token.mint(value, bytes(""), bytes(""));
148
+ uint256 userTokenId = token.currentNonce(user);
149
+ string memory customUri = "1.1.0.0/";
150
+ string memory result = token.uri(tokenId, customUri);
151
+ assertEq(
152
+ result,
153
+ string.concat(
154
+ customUri,
155
+ "eip155:",
156
+ block.chainid.toString(),
157
+ "/",
158
+ Strings.toHexString(uint160(user), 20),
159
+ "/",
160
+ userTokenId.toString(),
161
+ "/",
162
+ value.toString(),
163
+ ".json"
164
+ )
165
+ );
166
+ }
167
+
168
+ function test_TokenUriUsingCustomUriInterface() public {
169
+ IUri tokenUriInterface = new MockTokenUri();
170
+ string memory result = token.uri(ownedTokenId, tokenUriInterface);
171
+ assertEq(result, string.concat("mock://", ownedTokenId.toString()));
172
+ }
173
+
174
+ function test_SupportsInterface() public {
175
+ assertTrue(token.supportsInterface(type(IERC165).interfaceId));
176
+ assertTrue(token.supportsInterface(type(IERC1155).interfaceId));
177
+ assertTrue(token.supportsInterface(type(IInverseProjected1155).interfaceId));
178
+ assertTrue(token.supportsInterface(type(IInverseAppProjected1155).interfaceId));
179
+ }
180
+
181
+ function test_UpdateMetadataEmitsEvent() public {
182
+ uint256 tokenId = 1;
183
+ vm.expectEmit(true, true, true, true);
184
+ emit IERC4906Agnostic.MetadataUpdate(tokenId);
185
+ token.updateMetadata(tokenId);
186
+ }
187
+
188
+ function test_UpdateMetadataBatchEmitsEvent() public {
189
+ uint256 fromTokenId = 1;
190
+ uint256 toTokenId = 10;
191
+ vm.expectEmit(true, true, true, true);
192
+ emit IERC4906Agnostic.BatchMetadataUpdate(fromTokenId, toTokenId);
193
+ token.updateMetadataBatch(fromTokenId, toTokenId);
194
+ }
195
+
196
+ function test_CannotSetBaseUriUnauthorized() public {
197
+ vm.prank(alice);
198
+ vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, alice));
199
+ token.setBaseURI("test");
200
+ }
201
+
202
+ function test_CannotSetBaseExtensionUnauthorized() public {
203
+ vm.prank(alice);
204
+ vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, alice));
205
+ token.setBaseExtension("test");
206
+ }
207
+ }
@@ -0,0 +1,164 @@
1
+ // SPDX-License-Identifier: UNLICENSED
2
+ pragma solidity ^0.8.18;
3
+
4
+ import "../lib/cheatcodes.sol";
5
+ import "../lib/console.sol";
6
+ import "../lib/ctest.sol";
7
+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
8
+ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
9
+ import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
10
+ import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
11
+ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
12
+ import {IERC4906} from "@openzeppelin/contracts/interfaces/IERC4906.sol";
13
+ import {InverseAppProjectedNft} from "../../src/contracts/token/InverseAppProjectedNft.sol";
14
+ import {IInverseAppProjectedNft} from "../../src/contracts/token/IInverseAppProjectedNft.sol";
15
+ import {ITokenUri} from "../../src/contracts/token/ITokenUri.sol";
16
+
17
+ contract MockTokenUri is ITokenUri {
18
+ using Strings for uint256;
19
+
20
+ function tokenURI(uint256 tokenId) external view override returns (string memory) {
21
+ return string.concat("mock://", tokenId.toString());
22
+ }
23
+ }
24
+
25
+ contract MockDataReceiver {
26
+ bytes public dataReceived;
27
+ function onERC721Received(
28
+ address,
29
+ address,
30
+ uint256,
31
+ bytes calldata data
32
+ ) public returns (bytes4) {
33
+ dataReceived = data;
34
+ return this.onERC721Received.selector;
35
+ }
36
+ }
37
+
38
+ contract InverseAppProjectedNftTest is CTest, ERC721Holder {
39
+ using Strings for uint256;
40
+
41
+ CheatCodes vm = CheatCodes(HEVM_ADDRESS);
42
+ InverseAppProjectedNft public nft;
43
+ uint256 ownedTokenId;
44
+ string baseURI = "192.168.0.1/";
45
+ address alice = 0x078D888E40faAe0f32594342c85940AF3949E666;
46
+
47
+ function setUp() public {
48
+ nft = new InverseAppProjectedNft("ABC", "XYZ", address(this));
49
+ ownedTokenId = nft.mint(address(this));
50
+ nft.setBaseURI(baseURI);
51
+ }
52
+
53
+ function test_CanBurn() public {
54
+ nft.burn(ownedTokenId);
55
+ }
56
+
57
+ function test_CanMint() public {
58
+ vm.prank(alice);
59
+ vm.expectEmit(true, true, true, true);
60
+ emit IInverseAppProjectedNft.Minted(2, address(this), 2);
61
+ nft.mint(address(this), bytes(""), bytes(""));
62
+ }
63
+
64
+ function test_CanMintNoData() public {
65
+ vm.prank(alice);
66
+ vm.expectEmit(true, true, true, true);
67
+ emit IInverseAppProjectedNft.Minted(2, address(this), 2);
68
+ nft.mint(address(this), bytes(""));
69
+ }
70
+
71
+ function test_CanMintNoDataNoVerificationData() public {
72
+ vm.prank(alice);
73
+ vm.expectEmit(true, true, true, true);
74
+ emit IInverseAppProjectedNft.Minted(2, address(this), 2);
75
+ nft.mint(address(this));
76
+ }
77
+
78
+ function test_MintPassesDataToReceiver() public {
79
+ MockDataReceiver receiver = new MockDataReceiver();
80
+ bytes memory data = bytes("data");
81
+ nft.mint(address(receiver), "", data);
82
+ assertEq(keccak256(receiver.dataReceived()), keccak256(data));
83
+ }
84
+
85
+ function test_CanTransfer() public {
86
+ nft.transferFrom(address(this), alice, ownedTokenId);
87
+ }
88
+
89
+ function test_TokenUriUsesBaseUriByDefault() public {
90
+ string memory result = nft.tokenURI(ownedTokenId);
91
+ assertEq(
92
+ result,
93
+ string.concat(
94
+ "192.168.0.1/eip155:31337/",
95
+ Strings.toHexString(uint160(address(this)), 20),
96
+ "/",
97
+ "1.json"
98
+ )
99
+ );
100
+ }
101
+
102
+ function test_TokenUriUsingCustomBaseUri() public {
103
+ string memory result = nft.tokenURI(ownedTokenId, "1.1.0.0/");
104
+ assertEq(
105
+ result,
106
+ string.concat(
107
+ "1.1.0.0/eip155:31337/",
108
+ Strings.toHexString(uint160(address(this)), 20),
109
+ "/",
110
+ "1.json"
111
+ )
112
+ );
113
+ }
114
+
115
+ function test_TokenUriUsingCustomUriInterface() public {
116
+ ITokenUri tokenUriInterface = new MockTokenUri();
117
+ string memory result = nft.tokenURI(ownedTokenId, tokenUriInterface);
118
+ assertEq(result, string.concat("mock://", ownedTokenId.toString()));
119
+ }
120
+
121
+ function test_SupportsInterface() public {
122
+ assertTrue(nft.supportsInterface(type(IERC165).interfaceId));
123
+ assertTrue(nft.supportsInterface(type(IERC721).interfaceId));
124
+ assertTrue(nft.supportsInterface(bytes4(0x49064906)));
125
+ }
126
+
127
+ function test_UpdateMetadataEmitsEvent() public {
128
+ uint256 tokenId = 1;
129
+ vm.expectEmit(true, true, true, true);
130
+ emit IERC4906.MetadataUpdate(tokenId);
131
+ nft.updateMetadata(tokenId);
132
+ }
133
+
134
+ function test_UpdateMetadataBatchEmitsEvent() public {
135
+ uint256 fromTokenId = 1;
136
+ uint256 toTokenId = 10;
137
+ vm.expectEmit(true, true, true, true);
138
+ emit IERC4906.BatchMetadataUpdate(fromTokenId, toTokenId);
139
+ nft.updateMetadataBatch(fromTokenId, toTokenId);
140
+ }
141
+
142
+ function test_CannotMintToZeroAddress() public {
143
+ vm.expectRevert("InverseAppProjectedNft: zero receiver address");
144
+ nft.mint(address(0));
145
+ }
146
+
147
+ function test_CannotBurnUnauthorized() public {
148
+ vm.prank(alice);
149
+ vm.expectRevert("InverseAppProjectedNft: not owner");
150
+ nft.burn(ownedTokenId);
151
+ }
152
+
153
+ function test_CannotSetBaseUriUnauthorized() public {
154
+ vm.prank(alice);
155
+ vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, alice));
156
+ nft.setBaseURI("test");
157
+ }
158
+
159
+ function test_CannotSetBaseExtensionUnauthorized() public {
160
+ vm.prank(alice);
161
+ vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, alice));
162
+ nft.setBaseExtension("test");
163
+ }
164
+ }
@@ -0,0 +1,171 @@
1
+ // SPDX-License-Identifier: UNLICENSED
2
+ pragma solidity ^0.8.18;
3
+
4
+ import "../lib/cheatcodes.sol";
5
+ import "../lib/console.sol";
6
+ import "../lib/ctest.sol";
7
+ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
8
+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
9
+ import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
10
+ import {ERC1155Holder} from "@openzeppelin/contracts/token/ERC1155/utils/ERC1155Holder.sol";
11
+ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
12
+ import {IERC4906Agnostic} from "../../src/contracts/token/IERC4906Agnostic.sol";
13
+ import {InverseBaseProjected1155} from "../../src/contracts/token/InverseBaseProjected1155.sol";
14
+ import {IInverseBaseProjected1155} from "../../src/contracts/token/IInverseBaseProjected1155.sol";
15
+ import {IInverseProjected1155} from "../../src/contracts/token/IInverseProjected1155.sol";
16
+ import {IUri} from "../../src/contracts/token/IUri.sol";
17
+
18
+ contract MockTokenUri is IUri {
19
+ using Strings for uint256;
20
+
21
+ function uri(uint256 id) external view override returns (string memory) {
22
+ return string.concat("mock://", id.toString());
23
+ }
24
+ }
25
+
26
+ contract InverseBaseProjected1155Test is CTest, ERC1155Holder {
27
+ using Strings for uint256;
28
+
29
+ CheatCodes vm = CheatCodes(HEVM_ADDRESS);
30
+ InverseBaseProjected1155 public token;
31
+ uint256 ownedTokenId;
32
+ string baseURI = "192.168.0.1/";
33
+ address alice = 0x078D888E40faAe0f32594342c85940AF3949E666;
34
+
35
+ function setUp() public {
36
+ token = new InverseBaseProjected1155("ABC", "XYZ", address(this));
37
+ ownedTokenId = token.mint(1000, bytes(""), "");
38
+ token.setBaseURI(baseURI);
39
+ }
40
+
41
+ function test_CanBurn() public {
42
+ uint256 amount = token.balanceOf(address(this), ownedTokenId);
43
+ token.burn(ownedTokenId, amount);
44
+ }
45
+
46
+ function test_CanBurnBatch() public {
47
+ uint256 balance = token.balanceOf(address(this), ownedTokenId);
48
+
49
+ uint256[] memory ids = new uint256[](2);
50
+ ids[0] = ownedTokenId;
51
+ ids[1] = ownedTokenId;
52
+ uint256[] memory values = new uint256[](2);
53
+ values[0] = balance / 2;
54
+ values[1] = balance / 4;
55
+
56
+ uint256 balanceBefore = token.balanceOf(address(this), ownedTokenId);
57
+ uint256 supplyBefore = token.totalSupply(ownedTokenId);
58
+ vm.expectEmit(true, true, true, true);
59
+ emit IERC1155.TransferBatch(address(this), address(this), address(0), ids, values);
60
+ token.burnBatch(ids, values);
61
+
62
+ assertEq(
63
+ token.balanceOf(address(this), ownedTokenId),
64
+ balanceBefore - values[0] - values[1]
65
+ );
66
+ assertEq(token.totalSupply(ownedTokenId), supplyBefore - values[0] - values[1]);
67
+ }
68
+
69
+ function test_CanMint() public {
70
+ address minter = alice;
71
+ uint256 tokenId = token.currentTokenId();
72
+ uint256 value = 500;
73
+ uint256 tokenValueUserBefore = token.balanceOf(minter, tokenId);
74
+
75
+ vm.prank(minter);
76
+ vm.expectEmit(true, true, true, true);
77
+ emit IInverseBaseProjected1155.Minted(tokenId, value, "abcd");
78
+ token.mint(value, bytes(""), "abcd");
79
+
80
+ assertEq(token.balanceOf(minter, tokenId), tokenValueUserBefore + value);
81
+ }
82
+
83
+ function test_CanTransfer() public {
84
+ address sender = address(this);
85
+ address recipient = alice;
86
+
87
+ uint256 tokenValueMinterBefore = token.balanceOf(sender, ownedTokenId);
88
+ uint256 tokenValueRecipientBefore = token.balanceOf(recipient, ownedTokenId);
89
+ uint256 value = tokenValueMinterBefore - 100;
90
+
91
+ token.safeTransferFrom(sender, recipient, ownedTokenId, value, bytes(""));
92
+
93
+ assertEq(token.balanceOf(sender, ownedTokenId), tokenValueMinterBefore - value);
94
+ assertEq(token.balanceOf(recipient, ownedTokenId), tokenValueRecipientBefore + value);
95
+ }
96
+
97
+ function test_TokenUriUsesBaseUriByDefault() public {
98
+ uint256 value = 1000;
99
+ uint256 tokenId = token.mint(value, bytes(""), "");
100
+ string memory result = token.uri(tokenId);
101
+ assertEq(
102
+ result,
103
+ string.concat(
104
+ baseURI,
105
+ "eip155:",
106
+ block.chainid.toString(),
107
+ "/",
108
+ tokenId.toString(),
109
+ ".json"
110
+ )
111
+ );
112
+ }
113
+
114
+ function test_TokenUriUsingCustomBaseUri() public {
115
+ uint256 value = 1000;
116
+ uint256 tokenId = token.mint(value, bytes(""), "");
117
+ string memory customUri = "1.1.0.0/";
118
+ string memory result = token.uri(tokenId, customUri);
119
+ assertEq(
120
+ result,
121
+ string.concat(
122
+ customUri,
123
+ "eip155:",
124
+ block.chainid.toString(),
125
+ "/",
126
+ tokenId.toString(),
127
+ ".json"
128
+ )
129
+ );
130
+ }
131
+
132
+ function test_TokenUriUsingCustomUriInterface() public {
133
+ IUri tokenUriInterface = new MockTokenUri();
134
+ string memory result = token.uri(ownedTokenId, tokenUriInterface);
135
+ assertEq(result, string.concat("mock://", ownedTokenId.toString()));
136
+ }
137
+
138
+ function test_SupportsInterface() public {
139
+ assertTrue(token.supportsInterface(type(IERC165).interfaceId));
140
+ assertTrue(token.supportsInterface(type(IERC1155).interfaceId));
141
+ assertTrue(token.supportsInterface(type(IInverseProjected1155).interfaceId));
142
+ assertTrue(token.supportsInterface(type(IInverseBaseProjected1155).interfaceId));
143
+ }
144
+
145
+ function test_UpdateMetadataEmitsEvent() public {
146
+ uint256 tokenId = 1;
147
+ vm.expectEmit(true, true, true, true);
148
+ emit IERC4906Agnostic.MetadataUpdate(tokenId);
149
+ token.updateMetadata(tokenId);
150
+ }
151
+
152
+ function test_UpdateMetadataBatchEmitsEvent() public {
153
+ uint256 fromTokenId = 1;
154
+ uint256 toTokenId = 10;
155
+ vm.expectEmit(true, true, true, true);
156
+ emit IERC4906Agnostic.BatchMetadataUpdate(fromTokenId, toTokenId);
157
+ token.updateMetadataBatch(fromTokenId, toTokenId);
158
+ }
159
+
160
+ function test_CannotSetBaseUriUnauthorized() public {
161
+ vm.prank(alice);
162
+ vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, alice));
163
+ token.setBaseURI("test");
164
+ }
165
+
166
+ function test_CannotSetBaseExtensionUnauthorized() public {
167
+ vm.prank(alice);
168
+ vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, alice));
169
+ token.setBaseExtension("test");
170
+ }
171
+ }
@@ -0,0 +1,141 @@
1
+ // SPDX-License-Identifier: UNLICENSED
2
+ pragma solidity ^0.8.18;
3
+
4
+ import "../lib/cheatcodes.sol";
5
+ import "../lib/console.sol";
6
+ import "../lib/ctest.sol";
7
+ import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
8
+ import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
9
+ import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
10
+ import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
11
+ import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
12
+ import {IERC4906} from "@openzeppelin/contracts/interfaces/IERC4906.sol";
13
+ import {InverseBaseProjectedNft} from "../../src/contracts/token/InverseBaseProjectedNft.sol";
14
+ import {IInverseBaseProjectedNft} from "../../src/contracts/token/IInverseBaseProjectedNft.sol";
15
+ import {ITokenUri} from "../../src/contracts/token/ITokenUri.sol";
16
+
17
+ contract MockTokenUri is ITokenUri {
18
+ using Strings for uint256;
19
+
20
+ function tokenURI(uint256 tokenId) external view override returns (string memory) {
21
+ return string.concat("mock://", tokenId.toString());
22
+ }
23
+ }
24
+
25
+ contract MockDataReceiver {
26
+ bytes public dataReceived;
27
+ function onERC721Received(
28
+ address,
29
+ address,
30
+ uint256,
31
+ bytes calldata data
32
+ ) public returns (bytes4) {
33
+ dataReceived = data;
34
+ return this.onERC721Received.selector;
35
+ }
36
+ }
37
+
38
+ contract InverseBaseProjectedNftTest is CTest, ERC721Holder {
39
+ using Strings for uint256;
40
+
41
+ CheatCodes vm = CheatCodes(HEVM_ADDRESS);
42
+ InverseBaseProjectedNft public nft;
43
+ uint256 ownedTokenId;
44
+ string baseURI = "192.168.0.1/";
45
+ address alice = 0x078D888E40faAe0f32594342c85940AF3949E666;
46
+
47
+ function setUp() public {
48
+ nft = new InverseBaseProjectedNft("ABC", "XYZ", address(this));
49
+ ownedTokenId = nft.mint(address(this), "");
50
+ nft.setBaseURI(baseURI);
51
+ }
52
+
53
+ function test_CanBurn() public {
54
+ nft.burn(ownedTokenId);
55
+ }
56
+
57
+ function test_CanMint() public {
58
+ vm.prank(alice);
59
+ vm.expectEmit(true, true, true, true);
60
+ emit IInverseBaseProjectedNft.Minted(2, "abcd");
61
+ nft.mint(address(this), "abcd", bytes(""));
62
+ }
63
+
64
+ function test_CanMintNoData() public {
65
+ vm.prank(alice);
66
+ vm.expectEmit(true, true, true, true);
67
+ emit IInverseBaseProjectedNft.Minted(2, "abcd");
68
+ nft.mint(address(this), "abcd");
69
+ }
70
+
71
+ function test_MintPassesDataToReceiver() public {
72
+ MockDataReceiver receiver = new MockDataReceiver();
73
+ bytes memory data = bytes("data");
74
+ nft.mint(address(receiver), "", data);
75
+ assertEq(keccak256(receiver.dataReceived()), keccak256(data));
76
+ }
77
+
78
+ function test_CanTransfer() public {
79
+ nft.transferFrom(address(this), alice, ownedTokenId);
80
+ }
81
+
82
+ function test_TokenUriUsesBaseUriByDefault() public {
83
+ string memory result = nft.tokenURI(ownedTokenId);
84
+ assertEq(result, "192.168.0.1/eip155:31337/1.json");
85
+ }
86
+
87
+ function test_TokenUriUsingCustomBaseUri() public {
88
+ string memory result = nft.tokenURI(ownedTokenId, "1.1.0.0/");
89
+ assertEq(result, "1.1.0.0/eip155:31337/1.json");
90
+ }
91
+
92
+ function test_TokenUriUsingCustomUriInterface() public {
93
+ ITokenUri tokenUriInterface = new MockTokenUri();
94
+ string memory result = nft.tokenURI(ownedTokenId, tokenUriInterface);
95
+ assertEq(result, string.concat("mock://", ownedTokenId.toString()));
96
+ }
97
+
98
+ function test_SupportsInterface() public {
99
+ assertTrue(nft.supportsInterface(type(IERC165).interfaceId));
100
+ assertTrue(nft.supportsInterface(type(IERC721).interfaceId));
101
+ assertTrue(nft.supportsInterface(bytes4(0x49064906)));
102
+ }
103
+
104
+ function test_UpdateMetadataEmitsEvent() public {
105
+ uint256 tokenId = 1;
106
+ vm.expectEmit(true, true, true, true);
107
+ emit IERC4906.MetadataUpdate(tokenId);
108
+ nft.updateMetadata(tokenId);
109
+ }
110
+
111
+ function test_UpdateMetadataBatchEmitsEvent() public {
112
+ uint256 fromTokenId = 1;
113
+ uint256 toTokenId = 10;
114
+ vm.expectEmit(true, true, true, true);
115
+ emit IERC4906.BatchMetadataUpdate(fromTokenId, toTokenId);
116
+ nft.updateMetadataBatch(fromTokenId, toTokenId);
117
+ }
118
+
119
+ function test_CannotMintToZeroAddress() public {
120
+ vm.expectRevert("InverseBaseProjectedNft: zero receiver address");
121
+ nft.mint(address(0), "");
122
+ }
123
+
124
+ function test_CannotBurnUnauthorized() public {
125
+ vm.prank(alice);
126
+ vm.expectRevert("InverseBaseProjectedNft: not owner");
127
+ nft.burn(ownedTokenId);
128
+ }
129
+
130
+ function test_CannotSetBaseUriUnauthorized() public {
131
+ vm.prank(alice);
132
+ vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, alice));
133
+ nft.setBaseURI("test");
134
+ }
135
+
136
+ function test_CannotSetBaseExtensionUnauthorized() public {
137
+ vm.prank(alice);
138
+ vm.expectRevert(abi.encodeWithSelector(Ownable.OwnableUnauthorizedAccount.selector, alice));
139
+ nft.setBaseExtension("test");
140
+ }
141
+ }