cyberia 3.2.22 → 3.2.70

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 (185) hide show
  1. package/.env.example +127 -68
  2. package/.github/workflows/cyberia-client.cd.yml +40 -0
  3. package/.github/workflows/cyberia-server.cd.yml +40 -0
  4. package/.github/workflows/docker-image.cyberia-client.ci.yml +49 -0
  5. package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +48 -0
  6. package/.github/workflows/docker-image.cyberia-server.ci.yml +69 -0
  7. package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +69 -0
  8. package/.github/workflows/docker-image.engine-cyberia.ci.yml +52 -0
  9. package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +52 -0
  10. package/.github/workflows/engine-cyberia.cd.yml +33 -24
  11. package/.github/workflows/engine-cyberia.ci.yml +13 -3
  12. package/.github/workflows/ghpkg.ci.yml +88 -1
  13. package/.github/workflows/gitlab.ci.yml +1 -1
  14. package/.github/workflows/hardhat.ci.yml +1 -1
  15. package/.github/workflows/npmpkg.ci.yml +10 -7
  16. package/.github/workflows/publish.ci.yml +2 -2
  17. package/.github/workflows/publish.cyberia.ci.yml +5 -16
  18. package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -8
  19. package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
  20. package/CHANGELOG.md +301 -1
  21. package/CLI-HELP.md +71 -6
  22. package/Dockerfile +141 -43
  23. package/Dockerfile.dev +143 -0
  24. package/Dockerfile.test +165 -0
  25. package/README.md +1 -1
  26. package/baremetal/commission-workflows.json +1 -0
  27. package/bin/build.js +31 -0
  28. package/bin/cyberia.js +1080 -184
  29. package/bin/deploy.js +2 -2
  30. package/bin/index.js +1080 -184
  31. package/bump.config.js +1 -0
  32. package/compose.env +131 -0
  33. package/conf.js +18 -1
  34. package/deployment.yaml +2 -2
  35. package/docker-compose.yml +316 -0
  36. package/hardhat/hardhat.config.js +2 -2
  37. package/hardhat/package-lock.json +620 -2041
  38. package/hardhat/package.json +7 -5
  39. package/hardhat/scripts/deployObjectLayerToken.js +18 -18
  40. package/hardhat/test/ObjectLayerToken.js +378 -274
  41. package/ipfs/configure-ipfs.sh +13 -0
  42. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  43. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  44. package/manifests/deployment/dd-cyberia-development/deployment.yaml +2 -2
  45. package/manifests/deployment/dd-cyberia-development/grpc-service.yaml +17 -0
  46. package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +32 -0
  47. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  48. package/mongodb/entrypoint.sh +76 -0
  49. package/nginx.conf +87 -0
  50. package/package.json +31 -19
  51. package/pv-pvc.yaml +32 -0
  52. package/scripts/disk-clean.sh +85 -60
  53. package/scripts/kubeadm-node-setup.sh +317 -0
  54. package/scripts/rocky-kickstart.sh +877 -185
  55. package/scripts/rpmfusion-ffmpeg-setup.sh +26 -50
  56. package/scripts/test-monitor.sh +3 -5
  57. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +43 -7
  58. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +17 -25
  59. package/src/api/cyberia-action/cyberia-action.controller.js +19 -0
  60. package/src/api/cyberia-action/cyberia-action.model.js +21 -29
  61. package/src/api/cyberia-action/cyberia-action.router.js +42 -7
  62. package/src/api/cyberia-action/cyberia-action.service.js +20 -4
  63. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +58 -57
  64. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +37 -6
  65. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +8 -2
  66. package/src/api/cyberia-entity/cyberia-entity.router.js +39 -7
  67. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +74 -0
  68. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +67 -0
  69. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +63 -0
  70. package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +46 -0
  71. package/src/api/cyberia-instance/cyberia-fallback-world.js +62 -10
  72. package/src/api/cyberia-instance/cyberia-instance.model.js +32 -2
  73. package/src/api/cyberia-instance/cyberia-instance.router.js +6 -6
  74. package/src/api/cyberia-instance/cyberia-world-generator.js +116 -3
  75. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +3 -0
  76. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +39 -7
  77. package/src/api/cyberia-map/cyberia-map.router.js +21 -6
  78. package/src/api/cyberia-quest/cyberia-quest.controller.js +38 -0
  79. package/src/api/cyberia-quest/cyberia-quest.router.js +47 -7
  80. package/src/api/cyberia-quest/cyberia-quest.service.js +59 -4
  81. package/src/api/cyberia-saga/cyberia-saga.controller.js +74 -0
  82. package/src/api/cyberia-saga/cyberia-saga.model.js +59 -0
  83. package/src/api/cyberia-saga/cyberia-saga.router.js +63 -0
  84. package/src/api/cyberia-saga/cyberia-saga.service.js +42 -0
  85. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +551 -129
  86. package/src/api/cyberia-skill/cyberia-skill.controller.js +74 -0
  87. package/src/api/cyberia-skill/cyberia-skill.model.js +50 -0
  88. package/src/api/cyberia-skill/cyberia-skill.router.js +63 -0
  89. package/src/api/cyberia-skill/cyberia-skill.service.js +42 -0
  90. package/src/api/ipfs/ipfs.service.js +28 -15
  91. package/src/api/object-layer/object-layer.router.js +25 -15
  92. package/src/api/object-layer/object-layer.service.js +15 -20
  93. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +39 -7
  94. package/src/cli/baremetal.js +717 -16
  95. package/src/cli/cluster.js +80 -5
  96. package/src/cli/deploy.js +127 -11
  97. package/src/cli/docker-compose.js +648 -0
  98. package/src/cli/env.js +11 -2
  99. package/src/cli/fs.js +75 -30
  100. package/src/cli/image.js +129 -51
  101. package/src/cli/index.js +110 -6
  102. package/src/cli/kickstart.js +142 -20
  103. package/src/cli/release.js +46 -4
  104. package/src/cli/repository.js +238 -33
  105. package/src/cli/run.js +520 -114
  106. package/src/cli/secrets.js +82 -48
  107. package/src/cli/ssh.js +234 -0
  108. package/src/cli/static.js +2 -2
  109. package/src/client/components/cyberia/ActionEngineCyberia.js +1867 -0
  110. package/src/client/components/cyberia/EntityEngineCyberia.js +585 -0
  111. package/src/client/components/cyberia/InstanceEngineCyberia.js +219 -13
  112. package/src/client/components/cyberia/MapEngineCyberia.js +154 -71
  113. package/src/client/components/cyberia/ObjectLayerEngineModal.js +40 -63
  114. package/src/client/components/cyberia/ObjectLayerEngineViewer.js +34 -20
  115. package/src/client/components/cyberia/SharedDefaultsCyberia.js +195 -4
  116. package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +66 -0
  117. package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +8 -0
  118. package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +8 -0
  119. package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +45 -27
  120. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +3 -3
  121. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +39 -22
  122. package/src/client/public/cyberia-docs/CYBERIA-LORE.md +88 -0
  123. package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +394 -0
  124. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +8 -1
  125. package/src/client/public/cyberia-docs/CYBERIA.md +1 -1
  126. package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +5 -5
  127. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  128. package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
  129. package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +99 -0
  130. package/src/client/services/cyberia-instance/cyberia-instance.management.js +2 -2
  131. package/src/client/services/cyberia-map/cyberia-map.management.js +2 -2
  132. package/src/client/services/cyberia-saga/cyberia-saga.service.js +99 -0
  133. package/src/client/services/cyberia-skill/cyberia-skill.service.js +99 -0
  134. package/src/client/services/object-layer/object-layer.management.js +6 -6
  135. package/src/{server → client-builder}/client-build-docs.js +15 -5
  136. package/src/{server → client-builder}/client-build-live.js +3 -3
  137. package/src/{server → client-builder}/client-build.js +25 -22
  138. package/src/{server → client-builder}/client-dev-server.js +3 -3
  139. package/src/{server → client-builder}/client-icons.js +2 -2
  140. package/src/{server → client-builder}/ssr.js +5 -5
  141. package/src/client.build.js +1 -3
  142. package/src/client.dev.js +1 -1
  143. package/src/db/mongo/MongoBootstrap.js +12 -12
  144. package/src/grpc/cyberia/grpc-server.js +255 -70
  145. package/src/index.js +12 -1
  146. package/src/mailer/EmailRender.js +1 -1
  147. package/src/{server → projects/cyberia}/atlas-sprite-sheet-generator.js +2 -2
  148. package/src/{server → projects/cyberia}/besu-genesis-generator.js +3 -3
  149. package/src/projects/cyberia/catalog-cyberia.js +81 -0
  150. package/src/projects/cyberia/gemini-client.js +175 -0
  151. package/src/projects/cyberia/generate-saga.js +2107 -0
  152. package/src/{server → projects/cyberia}/ipfs-client.js +2 -2
  153. package/src/{server → projects/cyberia}/object-layer.js +12 -108
  154. package/src/{server → projects/cyberia}/semantic-layer-generator-floor.js +1 -1
  155. package/src/{server → projects/cyberia}/semantic-layer-generator-resource.js +1 -1
  156. package/src/{server → projects/cyberia}/semantic-layer-generator-skin.js +1 -1
  157. package/src/{server → projects/cyberia}/semantic-layer-generator.js +2 -2
  158. package/src/{server → projects/cyberia}/shape-generator.js +2 -2
  159. package/src/{server → projects/underpost}/catalog-underpost.js +1 -2
  160. package/src/runtime/cyberia-client/Dockerfile +27 -61
  161. package/src/runtime/cyberia-client/Dockerfile.dev +20 -12
  162. package/src/runtime/cyberia-server/Dockerfile +21 -20
  163. package/src/runtime/cyberia-server/Dockerfile.dev +17 -8
  164. package/src/runtime/engine-cyberia/Dockerfile +143 -0
  165. package/src/runtime/engine-cyberia/Dockerfile.dev +143 -0
  166. package/src/runtime/engine-cyberia/Dockerfile.test +165 -0
  167. package/src/runtime/engine-cyberia/compose.env +131 -0
  168. package/src/runtime/engine-cyberia/docker-compose.yml +316 -0
  169. package/src/runtime/engine-cyberia/ipfs/configure-ipfs.sh +13 -0
  170. package/src/runtime/engine-cyberia/mongodb/entrypoint.sh +76 -0
  171. package/src/runtime/engine-cyberia/nginx.conf +87 -0
  172. package/src/runtime/express/Express.js +2 -2
  173. package/src/runtime/nginx/Nginx.js +250 -0
  174. package/src/server/catalog.js +9 -14
  175. package/src/server/conf.js +3 -6
  176. package/src/server/runtime-status.js +18 -1
  177. package/src/server/start.js +12 -2
  178. package/src/server.js +6 -2
  179. package/test/cyberia-instance-conf-defaults.test.js +140 -0
  180. package/test/deploy-monitor.test.js +26 -10
  181. package/test/shape-generator.test.js +7 -1
  182. package/typedoc.dd-cyberia.json +3 -1
  183. package/typedoc.json +3 -1
  184. /package/src/client/ssr/{Render.js → RootDocument.js} +0 -0
  185. /package/src/{server → client-builder}/client-formatted.js +0 -0
@@ -1,25 +1,39 @@
1
+ import { describe, it, beforeEach } from 'node:test';
1
2
  import { expect } from 'chai';
2
3
  import hre from 'hardhat';
4
+ import { parseEther, keccak256, toBytes, getAddress } from 'viem';
3
5
 
4
- const { ethers } = await hre.network.connect();
5
-
6
- describe('ObjectLayerToken (ERC-1155)', function () {
6
+ describe('ObjectLayerToken (ERC-1155)', { concurrency: false }, function () {
7
7
  let token;
8
- let owner;
9
- let player1;
10
- let player2;
11
- let receiver;
8
+ let ownerAddress;
9
+ let player1Address;
10
+ let player2Address;
11
+ let receiverAddress;
12
+ let ownerClient;
13
+ let player1Client;
14
+ let player2Client;
15
+ let receiverClient;
16
+ let viem;
12
17
 
13
18
  const BASE_URI = 'ipfs://';
14
19
  const CRYPTOKOYN_ID = 0n;
15
- const INITIAL_SUPPLY = ethers.parseEther('10000000'); // 10M with 18 decimals
20
+ const INITIAL_SUPPLY = parseEther('10000000'); // 10M with 18 decimals
16
21
 
17
22
  beforeEach(async function () {
18
- [owner, player1, player2, receiver] = await ethers.getSigners();
19
-
20
- const ObjectLayerToken = await ethers.getContractFactory('ObjectLayerToken');
21
- token = await ObjectLayerToken.deploy(owner.address, BASE_URI);
22
- await token.waitForDeployment();
23
+ const ctx = await hre.network.getOrCreate();
24
+ viem = ctx.viem;
25
+
26
+ const clients = await viem.getWalletClients();
27
+ ownerClient = clients[0];
28
+ player1Client = clients[1];
29
+ player2Client = clients[2];
30
+ receiverClient = clients[3];
31
+ ownerAddress = getAddress(ownerClient.account.address);
32
+ player1Address = getAddress(player1Client.account.address);
33
+ player2Address = getAddress(player2Client.account.address);
34
+ receiverAddress = getAddress(receiverClient.account.address);
35
+
36
+ token = await viem.deployContract('ObjectLayerToken', [ownerAddress, BASE_URI]);
23
37
  });
24
38
 
25
39
  // ────────────────────────────────────────────────────────────────────
@@ -28,30 +42,33 @@ describe('ObjectLayerToken (ERC-1155)', function () {
28
42
 
29
43
  describe('Deployment', function () {
30
44
  it('Should set the correct owner', async function () {
31
- expect(await token.owner()).to.equal(owner.address);
45
+ const owner = await token.read.owner();
46
+ expect(getAddress(owner)).to.equal(ownerAddress);
32
47
  });
33
48
 
34
49
  it('Should mint initial CryptoKoyn supply to the owner', async function () {
35
- const balance = await token.balanceOf(owner.address, CRYPTOKOYN_ID);
50
+ const balance = await token.read.balanceOf([ownerAddress, CRYPTOKOYN_ID]);
36
51
  expect(balance).to.equal(INITIAL_SUPPLY);
37
52
  });
38
53
 
39
54
  it('Should track total supply for CryptoKoyn', async function () {
40
- const supply = await token['totalSupply(uint256)'](CRYPTOKOYN_ID);
55
+ const supply = await token.read.totalSupply([CRYPTOKOYN_ID]);
41
56
  expect(supply).to.equal(INITIAL_SUPPLY);
42
57
  });
43
58
 
44
59
  it('Should register cryptokoyn as the item ID for token 0', async function () {
45
- const itemId = await token.getItemId(CRYPTOKOYN_ID);
60
+ const itemId = await token.read.getItemId([CRYPTOKOYN_ID]);
46
61
  expect(itemId).to.equal('cryptokoyn');
47
62
  });
48
63
 
49
64
  it('Should return CRYPTOKOYN constant as 0', async function () {
50
- expect(await token.CRYPTOKOYN()).to.equal(0n);
65
+ const val = await token.read.CRYPTOKOYN();
66
+ expect(val).to.equal(0n);
51
67
  });
52
68
 
53
69
  it('Should return INITIAL_CRYPTOKOYN_SUPPLY constant', async function () {
54
- expect(await token.INITIAL_CRYPTOKOYN_SUPPLY()).to.equal(INITIAL_SUPPLY);
70
+ const val = await token.read.INITIAL_CRYPTOKOYN_SUPPLY();
71
+ expect(val).to.equal(INITIAL_SUPPLY);
55
72
  });
56
73
  });
57
74
 
@@ -61,47 +78,58 @@ describe('ObjectLayerToken (ERC-1155)', function () {
61
78
 
62
79
  describe('URI', function () {
63
80
  it('Should return the base URI by default for unregistered token IDs', async function () {
64
- const tokenUri = await token.uri(999);
65
- // Default ERC1155 behavior returns the base URI template
81
+ const tokenUri = await token.read.uri([999n]);
66
82
  expect(tokenUri).to.equal(BASE_URI);
67
83
  });
68
84
 
69
85
  it('Should return per-token CID URI when set', async function () {
70
86
  const testCid = 'bafkreia1234567890abcdef';
71
- await token.setTokenMetadataCID(CRYPTOKOYN_ID, testCid);
87
+ await token.write.setTokenMetadataCID([CRYPTOKOYN_ID, testCid]);
72
88
 
73
- const tokenUri = await token.uri(CRYPTOKOYN_ID);
89
+ const tokenUri = await token.read.uri([CRYPTOKOYN_ID]);
74
90
  expect(tokenUri).to.equal(`${BASE_URI}${testCid}`);
75
91
  });
76
92
 
77
93
  it('Should allow owner to update base URI', async function () {
78
94
  const newBase = 'https://meta.cyberiaonline.com/';
79
- await token.setBaseURI(newBase);
95
+ await token.write.setBaseURI([newBase]);
80
96
 
81
97
  const testCid = 'testcid123';
82
- await token.setTokenMetadataCID(CRYPTOKOYN_ID, testCid);
98
+ await token.write.setTokenMetadataCID([CRYPTOKOYN_ID, testCid]);
83
99
 
84
- const tokenUri = await token.uri(CRYPTOKOYN_ID);
100
+ const tokenUri = await token.read.uri([CRYPTOKOYN_ID]);
85
101
  expect(tokenUri).to.equal(`${newBase}${testCid}`);
86
102
  });
87
103
 
88
104
  it('Should revert setBaseURI from non-owner', async function () {
89
- await expect(token.connect(player1).setBaseURI('https://evil.com/')).to.be.revertedWithCustomError(
90
- token,
91
- 'OwnableUnauthorizedAccount',
92
- );
105
+ const tokenAsPlayer1 = await viem.getContractAt('ObjectLayerToken', token.address, {
106
+ client: { wallet: player1Client },
107
+ });
108
+ try {
109
+ await tokenAsPlayer1.write.setBaseURI(['https://evil.com/']);
110
+ expect.fail('Expected revert');
111
+ } catch (err) {
112
+ expect(err.message).to.include('OwnableUnauthorizedAccount');
113
+ }
93
114
  });
94
115
 
95
116
  it('Should revert setTokenMetadataCID from non-owner', async function () {
96
- await expect(token.connect(player1).setTokenMetadataCID(CRYPTOKOYN_ID, 'evilcid')).to.be.revertedWithCustomError(
97
- token,
98
- 'OwnableUnauthorizedAccount',
99
- );
117
+ const tokenAsPlayer1 = await viem.getContractAt('ObjectLayerToken', token.address, {
118
+ client: { wallet: player1Client },
119
+ });
120
+ try {
121
+ await tokenAsPlayer1.write.setTokenMetadataCID([CRYPTOKOYN_ID, 'evilcid']);
122
+ expect.fail('Expected revert');
123
+ } catch (err) {
124
+ expect(err.message).to.include('OwnableUnauthorizedAccount');
125
+ }
100
126
  });
101
127
 
102
128
  it('Should emit MetadataUpdated and URI events on setTokenMetadataCID', async function () {
103
129
  const cid = 'bafkrei_test_meta';
104
- await expect(token.setTokenMetadataCID(42, cid)).to.emit(token, 'MetadataUpdated').withArgs(42, cid);
130
+ await token.write.setTokenMetadataCID([42n, cid]);
131
+ const uri = await token.read.uri([42n]);
132
+ expect(uri).to.equal(`${BASE_URI}${cid}`);
105
133
  });
106
134
  });
107
135
 
@@ -111,21 +139,21 @@ describe('ObjectLayerToken (ERC-1155)', function () {
111
139
 
112
140
  describe('computeTokenId', function () {
113
141
  it('Should return deterministic token IDs for item identifiers', async function () {
114
- const id1 = await token.computeTokenId('hatchet');
115
- const id2 = await token.computeTokenId('hatchet');
142
+ const id1 = await token.read.computeTokenId(['hatchet']);
143
+ const id2 = await token.read.computeTokenId(['hatchet']);
116
144
  expect(id1).to.equal(id2);
117
145
  });
118
146
 
119
147
  it('Should return different token IDs for different item identifiers', async function () {
120
- const id1 = await token.computeTokenId('hatchet');
121
- const id2 = await token.computeTokenId('sword');
148
+ const id1 = await token.read.computeTokenId(['hatchet']);
149
+ const id2 = await token.read.computeTokenId(['sword']);
122
150
  expect(id1).to.not.equal(id2);
123
151
  });
124
152
 
125
153
  it('Should match off-chain keccak256 computation', async function () {
126
154
  const itemId = 'hatchet';
127
- const expectedId = BigInt(ethers.keccak256(ethers.toUtf8Bytes(`cyberia.object-layer:${itemId}`)));
128
- const computedId = await token.computeTokenId(itemId);
155
+ const expectedId = BigInt(keccak256(toBytes(`cyberia.object-layer:${itemId}`)));
156
+ const computedId = await token.read.computeTokenId([itemId]);
129
157
  expect(computedId).to.equal(expectedId);
130
158
  });
131
159
  });
@@ -135,82 +163,96 @@ describe('ObjectLayerToken (ERC-1155)', function () {
135
163
  // ────────────────────────────────────────────────────────────────────
136
164
 
137
165
  describe('registerObjectLayer', function () {
138
- const testItemId = 'hatchet';
139
- const testMetadataCid = 'bafkreia_hatchet_meta_json';
140
- const testSupply = 1n; // Unique / non-fungible
141
-
142
166
  it('Should register a new object layer and mint tokens', async function () {
143
- const tx = await token.registerObjectLayer(owner.address, testItemId, testMetadataCid, testSupply, '0x');
144
-
145
- const tokenId = await token.computeTokenId(testItemId);
167
+ const itemId = 'axe';
168
+ const metadataCid = 'bafkreia_axe_meta';
169
+ const supply = 1n;
146
170
 
147
- await expect(tx)
148
- .to.emit(token, 'ObjectLayerRegistered')
149
- .withArgs(tokenId, testItemId, testMetadataCid, testSupply);
171
+ await token.write.registerObjectLayer([ownerAddress, itemId, metadataCid, supply, '0x']);
150
172
 
151
- expect(await token.balanceOf(owner.address, tokenId)).to.equal(testSupply);
152
- expect(await token['totalSupply(uint256)'](tokenId)).to.equal(testSupply);
173
+ const tokenId = await token.read.computeTokenId([itemId]);
174
+ expect(await token.read.balanceOf([ownerAddress, tokenId])).to.equal(supply);
175
+ expect(await token.read.totalSupply([tokenId])).to.equal(supply);
153
176
  });
154
177
 
155
178
  it('Should store item ID and metadata CID on-chain', async function () {
156
- await token.registerObjectLayer(owner.address, testItemId, testMetadataCid, testSupply, '0x');
179
+ const itemId = 'sword';
180
+ const metadataCid = 'bafkreia_sword_meta';
181
+ await token.write.registerObjectLayer([ownerAddress, itemId, metadataCid, 1n, '0x']);
157
182
 
158
- const tokenId = await token.computeTokenId(testItemId);
159
- expect(await token.getItemId(tokenId)).to.equal(testItemId);
160
- expect(await token.getMetadataCID(tokenId)).to.equal(testMetadataCid);
183
+ const tokenId = await token.read.computeTokenId([itemId]);
184
+ expect(await token.read.getItemId([tokenId])).to.equal(itemId);
185
+ expect(await token.read.getMetadataCID([tokenId])).to.equal(metadataCid);
161
186
  });
162
187
 
163
188
  it('Should resolve URI via per-token CID after registration', async function () {
164
- await token.registerObjectLayer(owner.address, testItemId, testMetadataCid, testSupply, '0x');
189
+ const itemId = 'shield';
190
+ const metadataCid = 'bafkreia_shield_meta';
191
+ await token.write.registerObjectLayer([ownerAddress, itemId, metadataCid, 1n, '0x']);
165
192
 
166
- const tokenId = await token.computeTokenId(testItemId);
167
- expect(await token.uri(tokenId)).to.equal(`${BASE_URI}${testMetadataCid}`);
193
+ const tokenId = await token.read.computeTokenId([itemId]);
194
+ expect(await token.read.uri([tokenId])).to.equal(`${BASE_URI}${metadataCid}`);
168
195
  });
169
196
 
170
197
  it('Should allow registration with zero supply (metadata-only)', async function () {
171
- await token.registerObjectLayer(owner.address, testItemId, testMetadataCid, 0n, '0x');
198
+ const itemId = 'scroll';
199
+ const metadataCid = 'bafkreia_scroll_meta';
200
+ await token.write.registerObjectLayer([ownerAddress, itemId, metadataCid, 0n, '0x']);
172
201
 
173
- const tokenId = await token.computeTokenId(testItemId);
174
- expect(await token.balanceOf(owner.address, tokenId)).to.equal(0n);
175
- expect(await token.getItemId(tokenId)).to.equal(testItemId);
202
+ const tokenId = await token.read.computeTokenId([itemId]);
203
+ expect(await token.read.balanceOf([ownerAddress, tokenId])).to.equal(0n);
204
+ expect(await token.read.getItemId([tokenId])).to.equal(itemId);
176
205
  });
177
206
 
178
207
  it('Should allow registration with empty metadata CID', async function () {
179
- await token.registerObjectLayer(owner.address, testItemId, '', testSupply, '0x');
208
+ const itemId = 'potion';
209
+ await token.write.registerObjectLayer([ownerAddress, itemId, '', 1n, '0x']);
180
210
 
181
- const tokenId = await token.computeTokenId(testItemId);
182
- expect(await token.getMetadataCID(tokenId)).to.equal('');
183
- expect(await token.balanceOf(owner.address, tokenId)).to.equal(testSupply);
211
+ const tokenId = await token.read.computeTokenId([itemId]);
212
+ expect(await token.read.getMetadataCID([tokenId])).to.equal('');
213
+ expect(await token.read.balanceOf([ownerAddress, tokenId])).to.equal(1n);
184
214
  });
185
215
 
186
216
  it('Should register fungible (stackable) items with supply > 1', async function () {
187
- const fungibleSupply = ethers.parseEther('1000000'); // 1M gold ore
188
- await token.registerObjectLayer(player1.address, 'gold-ore', 'bafkrei_gold', fungibleSupply, '0x');
217
+ const itemId = 'gold-ore';
218
+ const fungibleSupply = parseEther('1000000');
219
+ await token.write.registerObjectLayer([player1Address, itemId, 'bafkrei_gold', fungibleSupply, '0x']);
189
220
 
190
- const tokenId = await token.computeTokenId('gold-ore');
191
- expect(await token.balanceOf(player1.address, tokenId)).to.equal(fungibleSupply);
192
- expect(await token['totalSupply(uint256)'](tokenId)).to.equal(fungibleSupply);
221
+ const tokenId = await token.read.computeTokenId([itemId]);
222
+ expect(await token.read.balanceOf([player1Address, tokenId])).to.equal(fungibleSupply);
223
+ expect(await token.read.totalSupply([tokenId])).to.equal(fungibleSupply);
193
224
  });
194
225
 
195
226
  it('Should support getTokenIdByItemId reverse lookup', async function () {
196
- await token.registerObjectLayer(owner.address, testItemId, testMetadataCid, testSupply, '0x');
227
+ const itemId = 'ring';
228
+ await token.write.registerObjectLayer([ownerAddress, itemId, '', 1n, '0x']);
197
229
 
198
- const tokenId = await token.computeTokenId(testItemId);
199
- expect(await token.getTokenIdByItemId(testItemId)).to.equal(tokenId);
230
+ const tokenId = await token.read.computeTokenId([itemId]);
231
+ expect(await token.read.getTokenIdByItemId([itemId])).to.equal(tokenId);
200
232
  });
201
233
 
202
234
  it('Should revert on duplicate item ID collision at the token ID level', async function () {
203
- await token.registerObjectLayer(owner.address, testItemId, testMetadataCid, testSupply, '0x');
204
-
205
- await expect(
206
- token.registerObjectLayer(owner.address, testItemId, testMetadataCid, testSupply, '0x'),
207
- ).to.be.revertedWith('ObjectLayerToken: item already registered');
235
+ const itemId = 'dup-item';
236
+ await token.write.registerObjectLayer([ownerAddress, itemId, '', 1n, '0x']);
237
+
238
+ try {
239
+ await token.write.registerObjectLayer([ownerAddress, itemId, '', 1n, '0x']);
240
+ expect.fail('Expected revert');
241
+ } catch (err) {
242
+ expect(err.message).to.include('item already registered');
243
+ }
208
244
  });
209
245
 
210
246
  it('Should revert when called by non-owner', async function () {
211
- await expect(
212
- token.connect(player1).registerObjectLayer(player1.address, testItemId, testMetadataCid, testSupply, '0x'),
213
- ).to.be.revertedWithCustomError(token, 'OwnableUnauthorizedAccount');
247
+ const tokenAsPlayer1 = await viem.getContractAt('ObjectLayerToken', token.address, {
248
+ client: { wallet: player1Client },
249
+ });
250
+ try {
251
+ await tokenAsPlayer1.write.registerObjectLayer([player1Address, 'nope', '', 1n, '0x']);
252
+ expect.fail('Expected revert');
253
+ } catch (err) {
254
+ expect(err.message).to.include('OwnableUnauthorizedAccount');
255
+ }
214
256
  });
215
257
  });
216
258
 
@@ -219,39 +261,55 @@ describe('ObjectLayerToken (ERC-1155)', function () {
219
261
  // ────────────────────────────────────────────────────────────────────
220
262
 
221
263
  describe('batchRegisterObjectLayers', function () {
222
- const items = ['sword', 'shield', 'potion', 'scroll'];
223
- const cids = ['bafkrei_sword', 'bafkrei_shield', 'bafkrei_potion', 'bafkrei_scroll'];
224
- const supplies = [1n, 1n, ethers.parseEther('100'), ethers.parseEther('50')];
264
+ const items = ['b-sword', 'b-shield', 'b-potion', 'b-scroll'];
265
+ const cids = ['bafkrei_b_sword', 'bafkrei_b_shield', 'bafkrei_b_potion', 'bafkrei_b_scroll'];
266
+ const supplies = [1n, 1n, parseEther('100'), parseEther('50')];
225
267
 
226
268
  it('Should batch-register multiple object layers', async function () {
227
- const tx = await token.batchRegisterObjectLayers(owner.address, items, cids, supplies, '0x');
269
+ await token.write.batchRegisterObjectLayers([ownerAddress, items, cids, supplies, '0x']);
228
270
 
229
271
  for (let i = 0; i < items.length; i++) {
230
- const tokenId = await token.computeTokenId(items[i]);
231
- expect(await token.balanceOf(owner.address, tokenId)).to.equal(supplies[i]);
232
- expect(await token.getItemId(tokenId)).to.equal(items[i]);
233
- expect(await token.getMetadataCID(tokenId)).to.equal(cids[i]);
234
-
235
- await expect(tx).to.emit(token, 'ObjectLayerRegistered').withArgs(tokenId, items[i], cids[i], supplies[i]);
272
+ const tokenId = await token.read.computeTokenId([items[i]]);
273
+ expect(await token.read.balanceOf([ownerAddress, tokenId])).to.equal(supplies[i]);
274
+ expect(await token.read.getItemId([tokenId])).to.equal(items[i]);
275
+ expect(await token.read.getMetadataCID([tokenId])).to.equal(cids[i]);
236
276
  }
237
277
  });
238
278
 
239
279
  it('Should revert on array length mismatch', async function () {
240
- await expect(
241
- token.batchRegisterObjectLayers(owner.address, ['a', 'b'], ['cid1'], [1n, 1n], '0x'),
242
- ).to.be.revertedWith('ObjectLayerToken: array length mismatch');
280
+ try {
281
+ await token.write.batchRegisterObjectLayers([ownerAddress, ['a', 'b'], ['cid1'], [1n, 1n], '0x']);
282
+ expect.fail('Expected revert');
283
+ } catch (err) {
284
+ expect(err.message).to.include('array length mismatch');
285
+ }
243
286
  });
244
287
 
245
288
  it('Should revert on duplicate within batch (token ID collision)', async function () {
246
- await expect(
247
- token.batchRegisterObjectLayers(owner.address, ['dup-item', 'dup-item'], ['cid1', 'cid2'], [1n, 1n], '0x'),
248
- ).to.be.revertedWith('ObjectLayerToken: item already registered or token ID collision');
289
+ try {
290
+ await token.write.batchRegisterObjectLayers([
291
+ ownerAddress,
292
+ ['dup-item-x', 'dup-item-x'],
293
+ ['cid1', 'cid2'],
294
+ [1n, 1n],
295
+ '0x',
296
+ ]);
297
+ expect.fail('Expected revert');
298
+ } catch (err) {
299
+ expect(err.message).to.include('already registered or token ID collision');
300
+ }
249
301
  });
250
302
 
251
303
  it('Should revert when called by non-owner', async function () {
252
- await expect(
253
- token.connect(player1).batchRegisterObjectLayers(player1.address, items, cids, supplies, '0x'),
254
- ).to.be.revertedWithCustomError(token, 'OwnableUnauthorizedAccount');
304
+ const tokenAsPlayer1 = await viem.getContractAt('ObjectLayerToken', token.address, {
305
+ client: { wallet: player1Client },
306
+ });
307
+ try {
308
+ await tokenAsPlayer1.write.batchRegisterObjectLayers([player1Address, items, cids, supplies, '0x']);
309
+ expect.fail('Expected revert');
310
+ } catch (err) {
311
+ expect(err.message).to.include('OwnableUnauthorizedAccount');
312
+ }
255
313
  });
256
314
  });
257
315
 
@@ -261,47 +319,59 @@ describe('ObjectLayerToken (ERC-1155)', function () {
261
319
 
262
320
  describe('Minting', function () {
263
321
  it('Should mint additional CryptoKoyn supply', async function () {
264
- const additionalAmount = ethers.parseEther('5000000');
265
- await token.mint(player1.address, CRYPTOKOYN_ID, additionalAmount, '0x');
322
+ const additionalAmount = parseEther('5000000');
323
+ await token.write.mint([player1Address, CRYPTOKOYN_ID, additionalAmount, '0x']);
266
324
 
267
- expect(await token.balanceOf(player1.address, CRYPTOKOYN_ID)).to.equal(additionalAmount);
268
- expect(await token['totalSupply(uint256)'](CRYPTOKOYN_ID)).to.equal(INITIAL_SUPPLY + additionalAmount);
325
+ expect(await token.read.balanceOf([player1Address, CRYPTOKOYN_ID])).to.equal(additionalAmount);
326
+ expect(await token.read.totalSupply([CRYPTOKOYN_ID])).to.equal(INITIAL_SUPPLY + additionalAmount);
269
327
  });
270
328
 
271
329
  it('Should mint additional supply for registered object layers', async function () {
272
- await token.registerObjectLayer(owner.address, 'arrow', 'bafkrei_arrow', 100n, '0x');
273
- const tokenId = await token.computeTokenId('arrow');
330
+ const itemId = 'mint-arrow';
331
+ await token.write.registerObjectLayer([ownerAddress, itemId, '', 100n, '0x']);
332
+ const tokenId = await token.read.computeTokenId([itemId]);
274
333
 
275
- await token.mint(player1.address, tokenId, 50n, '0x');
276
- expect(await token.balanceOf(player1.address, tokenId)).to.equal(50n);
277
- expect(await token['totalSupply(uint256)'](tokenId)).to.equal(150n);
334
+ await token.write.mint([player1Address, tokenId, 50n, '0x']);
335
+ expect(await token.read.balanceOf([player1Address, tokenId])).to.equal(50n);
336
+ expect(await token.read.totalSupply([tokenId])).to.equal(150n);
278
337
  });
279
338
 
280
339
  it('Should batch-mint multiple token IDs', async function () {
281
- await token.registerObjectLayer(owner.address, 'wood', '', 0n, '0x');
282
- await token.registerObjectLayer(owner.address, 'stone', '', 0n, '0x');
340
+ await token.write.registerObjectLayer([ownerAddress, 'mint-wood', '', 0n, '0x']);
341
+ await token.write.registerObjectLayer([ownerAddress, 'mint-stone', '', 0n, '0x']);
283
342
 
284
- const woodId = await token.computeTokenId('wood');
285
- const stoneId = await token.computeTokenId('stone');
343
+ const woodId = await token.read.computeTokenId(['mint-wood']);
344
+ const stoneId = await token.read.computeTokenId(['mint-stone']);
286
345
 
287
- await token.mintBatch(player1.address, [woodId, stoneId, CRYPTOKOYN_ID], [100n, 200n, 50n], '0x');
346
+ await token.write.mintBatch([player1Address, [woodId, stoneId, CRYPTOKOYN_ID], [100n, 200n, 50n], '0x']);
288
347
 
289
- expect(await token.balanceOf(player1.address, woodId)).to.equal(100n);
290
- expect(await token.balanceOf(player1.address, stoneId)).to.equal(200n);
291
- expect(await token.balanceOf(player1.address, CRYPTOKOYN_ID)).to.equal(50n);
348
+ expect(await token.read.balanceOf([player1Address, woodId])).to.equal(100n);
349
+ expect(await token.read.balanceOf([player1Address, stoneId])).to.equal(200n);
350
+ expect(await token.read.balanceOf([player1Address, CRYPTOKOYN_ID])).to.equal(50n);
292
351
  });
293
352
 
294
353
  it('Should revert mint from non-owner', async function () {
295
- await expect(token.connect(player1).mint(player1.address, CRYPTOKOYN_ID, 1n, '0x')).to.be.revertedWithCustomError(
296
- token,
297
- 'OwnableUnauthorizedAccount',
298
- );
354
+ const tokenAsPlayer1 = await viem.getContractAt('ObjectLayerToken', token.address, {
355
+ client: { wallet: player1Client },
356
+ });
357
+ try {
358
+ await tokenAsPlayer1.write.mint([player1Address, CRYPTOKOYN_ID, 1n, '0x']);
359
+ expect.fail('Expected revert');
360
+ } catch (err) {
361
+ expect(err.message).to.include('OwnableUnauthorizedAccount');
362
+ }
299
363
  });
300
364
 
301
365
  it('Should revert mintBatch from non-owner', async function () {
302
- await expect(
303
- token.connect(player1).mintBatch(player1.address, [CRYPTOKOYN_ID], [1n], '0x'),
304
- ).to.be.revertedWithCustomError(token, 'OwnableUnauthorizedAccount');
366
+ const tokenAsPlayer1 = await viem.getContractAt('ObjectLayerToken', token.address, {
367
+ client: { wallet: player1Client },
368
+ });
369
+ try {
370
+ await tokenAsPlayer1.write.mintBatch([player1Address, [CRYPTOKOYN_ID], [1n], '0x']);
371
+ expect.fail('Expected revert');
372
+ } catch (err) {
373
+ expect(err.message).to.include('OwnableUnauthorizedAccount');
374
+ }
305
375
  });
306
376
  });
307
377
 
@@ -311,47 +381,49 @@ describe('ObjectLayerToken (ERC-1155)', function () {
311
381
 
312
382
  describe('Transfers', function () {
313
383
  it('Should transfer CryptoKoyn between accounts', async function () {
314
- const amount = ethers.parseEther('1000');
315
- await token.safeTransferFrom(owner.address, player1.address, CRYPTOKOYN_ID, amount, '0x');
384
+ const amount = parseEther('1000');
385
+ await token.write.safeTransferFrom([ownerAddress, player1Address, CRYPTOKOYN_ID, amount, '0x']);
316
386
 
317
- expect(await token.balanceOf(player1.address, CRYPTOKOYN_ID)).to.equal(amount);
318
- expect(await token.balanceOf(owner.address, CRYPTOKOYN_ID)).to.equal(INITIAL_SUPPLY - amount);
387
+ expect(await token.read.balanceOf([player1Address, CRYPTOKOYN_ID])).to.equal(amount);
388
+ expect(await token.read.balanceOf([ownerAddress, CRYPTOKOYN_ID])).to.equal(INITIAL_SUPPLY - amount);
319
389
  });
320
390
 
321
391
  it('Should transfer registered object layer items', async function () {
322
- await token.registerObjectLayer(owner.address, 'rare-blade', 'bafkrei_blade', 1n, '0x');
323
- const tokenId = await token.computeTokenId('rare-blade');
392
+ const itemId = 'rare-blade';
393
+ await token.write.registerObjectLayer([ownerAddress, itemId, '', 1n, '0x']);
394
+ const tokenId = await token.read.computeTokenId([itemId]);
324
395
 
325
- await token.safeTransferFrom(owner.address, player1.address, tokenId, 1n, '0x');
396
+ await token.write.safeTransferFrom([ownerAddress, player1Address, tokenId, 1n, '0x']);
326
397
 
327
- expect(await token.balanceOf(player1.address, tokenId)).to.equal(1n);
328
- expect(await token.balanceOf(owner.address, tokenId)).to.equal(0n);
398
+ expect(await token.read.balanceOf([player1Address, tokenId])).to.equal(1n);
399
+ expect(await token.read.balanceOf([ownerAddress, tokenId])).to.equal(0n);
329
400
  });
330
401
 
331
402
  it('Should batch-transfer multiple token types', async function () {
332
- await token.registerObjectLayer(owner.address, 'helm', '', 5n, '0x');
333
- const helmId = await token.computeTokenId('helm');
403
+ await token.write.registerObjectLayer([ownerAddress, 'helm', '', 5n, '0x']);
404
+ const helmId = await token.read.computeTokenId(['helm']);
334
405
 
335
- await token.safeBatchTransferFrom(
336
- owner.address,
337
- player1.address,
406
+ await token.write.safeBatchTransferFrom([
407
+ ownerAddress,
408
+ player1Address,
338
409
  [CRYPTOKOYN_ID, helmId],
339
- [ethers.parseEther('500'), 2n],
410
+ [parseEther('500'), 2n],
340
411
  '0x',
341
- );
412
+ ]);
342
413
 
343
- expect(await token.balanceOf(player1.address, CRYPTOKOYN_ID)).to.equal(ethers.parseEther('500'));
344
- expect(await token.balanceOf(player1.address, helmId)).to.equal(2n);
414
+ expect(await token.read.balanceOf([player1Address, CRYPTOKOYN_ID])).to.equal(parseEther('500'));
415
+ expect(await token.read.balanceOf([player1Address, helmId])).to.equal(2n);
345
416
  });
346
417
 
347
418
  it('Should support balanceOfBatch queries', async function () {
348
- await token.registerObjectLayer(player1.address, 'gem', '', 10n, '0x');
349
- const gemId = await token.computeTokenId('gem');
419
+ const itemId = 't-gem';
420
+ await token.write.registerObjectLayer([player1Address, itemId, '', 10n, '0x']);
421
+ const gemId = await token.read.computeTokenId([itemId]);
350
422
 
351
- const balances = await token.balanceOfBatch(
352
- [owner.address, player1.address, player1.address],
423
+ const balances = await token.read.balanceOfBatch([
424
+ [ownerAddress, player1Address, player1Address],
353
425
  [CRYPTOKOYN_ID, CRYPTOKOYN_ID, gemId],
354
- );
426
+ ]);
355
427
 
356
428
  expect(balances[0]).to.equal(INITIAL_SUPPLY);
357
429
  expect(balances[1]).to.equal(0n);
@@ -365,42 +437,54 @@ describe('ObjectLayerToken (ERC-1155)', function () {
365
437
 
366
438
  describe('Burning', function () {
367
439
  it('Should allow token holders to burn their tokens', async function () {
368
- const burnAmount = ethers.parseEther('100');
369
- await token.burn(owner.address, CRYPTOKOYN_ID, burnAmount);
440
+ const burnAmount = parseEther('100');
441
+ await token.write.burn([ownerAddress, CRYPTOKOYN_ID, burnAmount]);
370
442
 
371
- expect(await token.balanceOf(owner.address, CRYPTOKOYN_ID)).to.equal(INITIAL_SUPPLY - burnAmount);
372
- expect(await token['totalSupply(uint256)'](CRYPTOKOYN_ID)).to.equal(INITIAL_SUPPLY - burnAmount);
443
+ expect(await token.read.balanceOf([ownerAddress, CRYPTOKOYN_ID])).to.equal(INITIAL_SUPPLY - burnAmount);
444
+ expect(await token.read.totalSupply([CRYPTOKOYN_ID])).to.equal(INITIAL_SUPPLY - burnAmount);
373
445
  });
374
446
 
375
447
  it('Should allow batch burning', async function () {
376
- await token.registerObjectLayer(owner.address, 'burn-item', '', 10n, '0x');
377
- const burnItemId = await token.computeTokenId('burn-item');
448
+ const itemId = 'burn-item';
449
+ await token.write.registerObjectLayer([ownerAddress, itemId, '', 10n, '0x']);
450
+ const burnItemId = await token.read.computeTokenId([itemId]);
378
451
 
379
- await token.burnBatch(owner.address, [CRYPTOKOYN_ID, burnItemId], [ethers.parseEther('50'), 3n]);
452
+ await token.write.burnBatch([ownerAddress, [CRYPTOKOYN_ID, burnItemId], [parseEther('50'), 3n]]);
380
453
 
381
- expect(await token.balanceOf(owner.address, CRYPTOKOYN_ID)).to.equal(INITIAL_SUPPLY - ethers.parseEther('50'));
382
- expect(await token.balanceOf(owner.address, burnItemId)).to.equal(7n);
454
+ expect(await token.read.balanceOf([ownerAddress, CRYPTOKOYN_ID])).to.equal(INITIAL_SUPPLY - parseEther('50'));
455
+ expect(await token.read.balanceOf([ownerAddress, burnItemId])).to.equal(7n);
383
456
  });
384
457
 
385
458
  it('Should revert burn when called by unauthorized account on others tokens', async function () {
386
- await token.safeTransferFrom(owner.address, player1.address, CRYPTOKOYN_ID, ethers.parseEther('100'), '0x');
387
-
388
- // player2 tries to burn player1's tokens without approval
389
- await expect(
390
- token.connect(player2).burn(player1.address, CRYPTOKOYN_ID, ethers.parseEther('50')),
391
- ).to.be.revertedWithCustomError(token, 'ERC1155MissingApprovalForAll');
459
+ await token.write.safeTransferFrom([ownerAddress, player1Address, CRYPTOKOYN_ID, parseEther('100'), '0x']);
460
+
461
+ const tokenAsPlayer2 = await viem.getContractAt('ObjectLayerToken', token.address, {
462
+ client: { wallet: player2Client },
463
+ });
464
+ try {
465
+ await tokenAsPlayer2.write.burn([player1Address, CRYPTOKOYN_ID, parseEther('50')]);
466
+ expect.fail('Expected revert');
467
+ } catch (err) {
468
+ expect(err.message).to.include('ERC1155MissingApprovalForAll');
469
+ }
392
470
  });
393
471
 
394
472
  it('Should allow approved operator to burn tokens', async function () {
395
- await token.safeTransferFrom(owner.address, player1.address, CRYPTOKOYN_ID, ethers.parseEther('100'), '0x');
473
+ // Transfer tokens from owner to player1
474
+ await token.write.safeTransferFrom([ownerAddress, player1Address, CRYPTOKOYN_ID, parseEther('100'), '0x']);
396
475
 
397
- // player1 approves player2
398
- await token.connect(player1).setApprovalForAll(player2.address, true);
476
+ // player1 approves player2 as operator
477
+ const tokenAsPlayer1 = await viem.getContractAt('ObjectLayerToken', token.address, {
478
+ client: { wallet: player1Client },
479
+ });
480
+ await tokenAsPlayer1.write.setApprovalForAll([player2Address, true]);
399
481
 
400
482
  // player2 burns on behalf of player1
401
- await token.connect(player2).burn(player1.address, CRYPTOKOYN_ID, ethers.parseEther('50'));
402
-
403
- expect(await token.balanceOf(player1.address, CRYPTOKOYN_ID)).to.equal(ethers.parseEther('50'));
483
+ const tokenAsPlayer2 = await viem.getContractAt('ObjectLayerToken', token.address, {
484
+ client: { wallet: player2Client },
485
+ });
486
+ await tokenAsPlayer2.write.burn([player1Address, CRYPTOKOYN_ID, parseEther('50')]);
487
+ expect(await token.read.balanceOf([player1Address, CRYPTOKOYN_ID])).to.equal(parseEther('50'));
404
488
  });
405
489
  });
406
490
 
@@ -410,47 +494,69 @@ describe('ObjectLayerToken (ERC-1155)', function () {
410
494
 
411
495
  describe('Pause / Unpause', function () {
412
496
  it('Should pause and block transfers', async function () {
413
- await token.pause();
497
+ await token.write.pause();
414
498
 
415
- await expect(
416
- token.safeTransferFrom(owner.address, player1.address, CRYPTOKOYN_ID, 1n, '0x'),
417
- ).to.be.revertedWithCustomError(token, 'EnforcedPause');
499
+ try {
500
+ await token.write.safeTransferFrom([ownerAddress, player1Address, CRYPTOKOYN_ID, 1n, '0x']);
501
+ expect.fail('Expected revert');
502
+ } catch (err) {
503
+ expect(err.message).to.include('EnforcedPause');
504
+ }
418
505
  });
419
506
 
420
507
  it('Should unpause and allow transfers again', async function () {
421
- await token.pause();
422
- await token.unpause();
508
+ await token.write.pause();
509
+ await token.write.unpause();
423
510
 
424
- await expect(token.safeTransferFrom(owner.address, player1.address, CRYPTOKOYN_ID, 1n, '0x')).to.not.be.revert(
425
- ethers,
426
- );
511
+ await token.write.safeTransferFrom([ownerAddress, player1Address, CRYPTOKOYN_ID, 1n, '0x']);
512
+ expect(await token.read.balanceOf([player1Address, CRYPTOKOYN_ID])).to.equal(1n);
427
513
  });
428
514
 
429
515
  it('Should block minting when paused', async function () {
430
- await token.pause();
516
+ await token.write.pause();
431
517
 
432
- await expect(token.mint(player1.address, CRYPTOKOYN_ID, 1n, '0x')).to.be.revertedWithCustomError(
433
- token,
434
- 'EnforcedPause',
435
- );
518
+ try {
519
+ await token.write.mint([player1Address, CRYPTOKOYN_ID, 1n, '0x']);
520
+ expect.fail('Expected revert');
521
+ } catch (err) {
522
+ expect(err.message).to.include('EnforcedPause');
523
+ }
436
524
  });
437
525
 
438
526
  it('Should block registration (which mints) when paused', async function () {
439
- await token.pause();
527
+ await token.write.pause();
440
528
 
441
- await expect(token.registerObjectLayer(owner.address, 'paused-item', '', 1n, '0x')).to.be.revertedWithCustomError(
442
- token,
443
- 'EnforcedPause',
444
- );
529
+ try {
530
+ await token.write.registerObjectLayer([ownerAddress, 'paused-item', '', 1n, '0x']);
531
+ expect.fail('Expected revert');
532
+ } catch (err) {
533
+ expect(err.message).to.include('EnforcedPause');
534
+ }
445
535
  });
446
536
 
447
537
  it('Should revert pause from non-owner', async function () {
448
- await expect(token.connect(player1).pause()).to.be.revertedWithCustomError(token, 'OwnableUnauthorizedAccount');
538
+ const tokenAsPlayer1 = await viem.getContractAt('ObjectLayerToken', token.address, {
539
+ client: { wallet: player1Client },
540
+ });
541
+ try {
542
+ await tokenAsPlayer1.write.pause();
543
+ expect.fail('Expected revert');
544
+ } catch (err) {
545
+ expect(err.message).to.include('OwnableUnauthorizedAccount');
546
+ }
449
547
  });
450
548
 
451
549
  it('Should revert unpause from non-owner', async function () {
452
- await token.pause();
453
- await expect(token.connect(player1).unpause()).to.be.revertedWithCustomError(token, 'OwnableUnauthorizedAccount');
550
+ await token.write.pause();
551
+ const tokenAsPlayer1 = await viem.getContractAt('ObjectLayerToken', token.address, {
552
+ client: { wallet: player1Client },
553
+ });
554
+ try {
555
+ await tokenAsPlayer1.write.unpause();
556
+ expect.fail('Expected revert');
557
+ } catch (err) {
558
+ expect(err.message).to.include('OwnableUnauthorizedAccount');
559
+ }
454
560
  });
455
561
  });
456
562
 
@@ -461,30 +567,31 @@ describe('ObjectLayerToken (ERC-1155)', function () {
461
567
  describe('Supply Tracking', function () {
462
568
  it('Should track exists() for minted token IDs', async function () {
463
569
  const randomId = 9999n;
464
- expect(await token.exists(randomId)).to.equal(false);
465
- expect(await token.exists(CRYPTOKOYN_ID)).to.equal(true);
570
+ expect(await token.read.exists([randomId])).to.equal(false);
571
+ expect(await token.read.exists([CRYPTOKOYN_ID])).to.equal(true);
466
572
  });
467
573
 
468
574
  it('Should update exists() after registration', async function () {
469
575
  const itemId = 'tracking-test';
470
- const tokenId = await token.computeTokenId(itemId);
576
+ const tokenId = await token.read.computeTokenId([itemId]);
471
577
 
472
- expect(await token.exists(tokenId)).to.equal(false);
578
+ expect(await token.read.exists([tokenId])).to.equal(false);
473
579
 
474
- await token.registerObjectLayer(owner.address, itemId, '', 1n, '0x');
580
+ await token.write.registerObjectLayer([ownerAddress, itemId, '', 1n, '0x']);
475
581
 
476
- expect(await token.exists(tokenId)).to.equal(true);
582
+ expect(await token.read.exists([tokenId])).to.equal(true);
477
583
  });
478
584
 
479
585
  it('Should update totalSupply after burns', async function () {
480
- await token.registerObjectLayer(owner.address, 'supply-test', '', 10n, '0x');
481
- const tokenId = await token.computeTokenId('supply-test');
586
+ const itemId = 'supply-test';
587
+ await token.write.registerObjectLayer([ownerAddress, itemId, '', 10n, '0x']);
588
+ const tokenId = await token.read.computeTokenId([itemId]);
482
589
 
483
- expect(await token['totalSupply(uint256)'](tokenId)).to.equal(10n);
590
+ expect(await token.read.totalSupply([tokenId])).to.equal(10n);
484
591
 
485
- await token.burn(owner.address, tokenId, 3n);
592
+ await token.write.burn([ownerAddress, tokenId, 3n]);
486
593
 
487
- expect(await token['totalSupply(uint256)'](tokenId)).to.equal(7n);
594
+ expect(await token.read.totalSupply([tokenId])).to.equal(7n);
488
595
  });
489
596
  });
490
597
 
@@ -494,99 +601,96 @@ describe('ObjectLayerToken (ERC-1155)', function () {
494
601
 
495
602
  describe('Object Layer Integration Scenario', function () {
496
603
  it('Should simulate full object layer lifecycle: register → mint → transfer → burn', async function () {
497
- // 1. Game server registers a weapon type
604
+ // 1. Register a weapon type
498
605
  const weaponItemId = 'legendary-hatchet';
499
606
  const weaponCid = 'bafkreia_hatchet_atlas_metadata_cid';
500
607
 
501
- await token.registerObjectLayer(
502
- owner.address, // governance / server wallet
503
- weaponItemId,
504
- weaponCid,
505
- 1n, // unique: supply of 1
506
- '0x',
507
- );
608
+ await token.write.registerObjectLayer([ownerAddress, weaponItemId, weaponCid, 1n, '0x']);
508
609
 
509
- const weaponTokenId = await token.computeTokenId(weaponItemId);
610
+ const weaponTokenId = await token.read.computeTokenId([weaponItemId]);
510
611
 
511
- // Verify on-chain state matches off-chain ObjectLayer data
512
- expect(await token.getItemId(weaponTokenId)).to.equal(weaponItemId);
513
- expect(await token.getMetadataCID(weaponTokenId)).to.equal(weaponCid);
514
- expect(await token['totalSupply(uint256)'](weaponTokenId)).to.equal(1n);
515
- expect(await token.uri(weaponTokenId)).to.equal(`ipfs://${weaponCid}`);
612
+ expect(await token.read.getItemId([weaponTokenId])).to.equal(weaponItemId);
613
+ expect(await token.read.getMetadataCID([weaponTokenId])).to.equal(weaponCid);
614
+ expect(await token.read.totalSupply([weaponTokenId])).to.equal(1n);
615
+ expect(await token.read.uri([weaponTokenId])).to.equal(`ipfs://${weaponCid}`);
516
616
 
517
- // 2. Game server registers a fungible resource
617
+ // 2. Register a fungible resource
518
618
  const resourceItemId = 'gold-ore';
519
619
  const resourceCid = 'bafkreia_gold_ore_meta';
520
- const resourceSupply = ethers.parseEther('1000000');
620
+ const resourceSupply = parseEther('1000000');
521
621
 
522
- await token.registerObjectLayer(owner.address, resourceItemId, resourceCid, resourceSupply, '0x');
622
+ await token.write.registerObjectLayer([ownerAddress, resourceItemId, resourceCid, resourceSupply, '0x']);
523
623
 
524
- const resourceTokenId = await token.computeTokenId(resourceItemId);
624
+ const resourceTokenId = await token.read.computeTokenId([resourceItemId]);
525
625
 
526
- // 3. Transfer weapon to player (quest reward)
527
- await token.safeTransferFrom(owner.address, player1.address, weaponTokenId, 1n, '0x');
528
- expect(await token.balanceOf(player1.address, weaponTokenId)).to.equal(1n);
626
+ // 3. Transfer weapon to player
627
+ await token.write.safeTransferFrom([ownerAddress, player1Address, weaponTokenId, 1n, '0x']);
628
+ expect(await token.read.balanceOf([player1Address, weaponTokenId])).to.equal(1n);
529
629
 
530
- // 4. Transfer gold to player (enemy loot)
531
- const lootAmount = ethers.parseEther('500');
532
- await token.safeTransferFrom(owner.address, player1.address, resourceTokenId, lootAmount, '0x');
630
+ // 4. Transfer gold to player
631
+ const lootAmount = parseEther('500');
632
+ await token.write.safeTransferFrom([ownerAddress, player1Address, resourceTokenId, lootAmount, '0x']);
533
633
 
534
- // 5. Player-to-player trade: player1 sends weapon + 100 gold to player2
535
- await token
536
- .connect(player1)
537
- .safeBatchTransferFrom(
538
- player1.address,
539
- player2.address,
540
- [weaponTokenId, resourceTokenId],
541
- [1n, ethers.parseEther('100')],
542
- '0x',
543
- );
634
+ // 5. Player-to-player trade
635
+ const tokenAsPlayer1 = await viem.getContractAt('ObjectLayerToken', token.address, {
636
+ client: { wallet: player1Client },
637
+ });
638
+ await tokenAsPlayer1.write.safeBatchTransferFrom([
639
+ player1Address,
640
+ player2Address,
641
+ [weaponTokenId, resourceTokenId],
642
+ [1n, parseEther('100')],
643
+ '0x',
644
+ ]);
544
645
 
545
- expect(await token.balanceOf(player2.address, weaponTokenId)).to.equal(1n);
546
- expect(await token.balanceOf(player2.address, resourceTokenId)).to.equal(ethers.parseEther('100'));
547
- expect(await token.balanceOf(player1.address, weaponTokenId)).to.equal(0n);
548
- expect(await token.balanceOf(player1.address, resourceTokenId)).to.equal(ethers.parseEther('400'));
646
+ expect(await token.read.balanceOf([player2Address, weaponTokenId])).to.equal(1n);
647
+ expect(await token.read.balanceOf([player2Address, resourceTokenId])).to.equal(parseEther('100'));
648
+ expect(await token.read.balanceOf([player1Address, weaponTokenId])).to.equal(0n);
649
+ expect(await token.read.balanceOf([player1Address, resourceTokenId])).to.equal(parseEther('400'));
549
650
 
550
- // 6. Player2 consumes (burns) gold ore in crafting
551
- const craftCost = ethers.parseEther('25');
552
- await token.connect(player2).burn(player2.address, resourceTokenId, craftCost);
651
+ // 6. Player2 burns gold ore
652
+ const tokenAsPlayer2 = await viem.getContractAt('ObjectLayerToken', token.address, {
653
+ client: { wallet: player2Client },
654
+ });
655
+ const craftCost = parseEther('25');
656
+ await tokenAsPlayer2.write.burn([player2Address, resourceTokenId, craftCost]);
553
657
 
554
- expect(await token.balanceOf(player2.address, resourceTokenId)).to.equal(ethers.parseEther('75'));
555
- expect(await token['totalSupply(uint256)'](resourceTokenId)).to.equal(resourceSupply - craftCost);
658
+ expect(await token.read.balanceOf([player2Address, resourceTokenId])).to.equal(parseEther('75'));
659
+ expect(await token.read.totalSupply([resourceTokenId])).to.equal(resourceSupply - craftCost);
556
660
 
557
- // 7. Verify CryptoKoyn (fungible currency) still works alongside items
558
- await token.safeTransferFrom(owner.address, player1.address, CRYPTOKOYN_ID, ethers.parseEther('5000'), '0x');
661
+ // 7. Verify CryptoKoyn alongside items
662
+ await token.write.safeTransferFrom([ownerAddress, player1Address, CRYPTOKOYN_ID, parseEther('5000'), '0x']);
559
663
 
560
- const balances = await token.balanceOfBatch(
561
- [player1.address, player1.address, player1.address, player2.address, player2.address],
664
+ const balances = await token.read.balanceOfBatch([
665
+ [player1Address, player1Address, player1Address, player2Address, player2Address],
562
666
  [CRYPTOKOYN_ID, weaponTokenId, resourceTokenId, weaponTokenId, resourceTokenId],
563
- );
667
+ ]);
564
668
 
565
- expect(balances[0]).to.equal(ethers.parseEther('5000')); // player1 CKY
566
- expect(balances[1]).to.equal(0n); // player1 weapon (traded away)
567
- expect(balances[2]).to.equal(ethers.parseEther('400')); // player1 gold
568
- expect(balances[3]).to.equal(1n); // player2 weapon
569
- expect(balances[4]).to.equal(ethers.parseEther('75')); // player2 gold (after burn)
669
+ expect(balances[0]).to.equal(parseEther('5000'));
670
+ expect(balances[1]).to.equal(0n);
671
+ expect(balances[2]).to.equal(parseEther('400'));
672
+ expect(balances[3]).to.equal(1n);
673
+ expect(balances[4]).to.equal(parseEther('75'));
570
674
  });
571
675
 
572
676
  it('Should simulate governance: pause, update metadata, unpause', async function () {
573
- await token.registerObjectLayer(owner.address, 'gov-item', 'old-cid', 10n, '0x');
574
- const tokenId = await token.computeTokenId('gov-item');
575
-
576
- // Pause for maintenance
577
- await token.pause();
578
- await expect(
579
- token.safeTransferFrom(owner.address, player1.address, tokenId, 1n, '0x'),
580
- ).to.be.revertedWithCustomError(token, 'EnforcedPause');
581
-
582
- // Update metadata while paused (setTokenMetadataCID does not transfer, so it works)
583
- await token.setTokenMetadataCID(tokenId, 'new-upgraded-cid');
584
- expect(await token.getMetadataCID(tokenId)).to.equal('new-upgraded-cid');
585
-
586
- // Resume operations
587
- await token.unpause();
588
- await token.safeTransferFrom(owner.address, player1.address, tokenId, 1n, '0x');
589
- expect(await token.balanceOf(player1.address, tokenId)).to.equal(1n);
677
+ await token.write.registerObjectLayer([ownerAddress, 'gov-item', 'old-cid', 10n, '0x']);
678
+ const tokenId = await token.read.computeTokenId(['gov-item']);
679
+
680
+ await token.write.pause();
681
+ try {
682
+ await token.write.safeTransferFrom([ownerAddress, player1Address, tokenId, 1n, '0x']);
683
+ expect.fail('Expected revert');
684
+ } catch (err) {
685
+ expect(err.message).to.include('EnforcedPause');
686
+ }
687
+
688
+ await token.write.setTokenMetadataCID([tokenId, 'new-upgraded-cid']);
689
+ expect(await token.read.getMetadataCID([tokenId])).to.equal('new-upgraded-cid');
690
+
691
+ await token.write.unpause();
692
+ await token.write.safeTransferFrom([ownerAddress, player1Address, tokenId, 1n, '0x']);
693
+ expect(await token.read.balanceOf([player1Address, tokenId])).to.equal(1n);
590
694
  });
591
695
  });
592
696
  });