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.
- package/.env.example +127 -68
- package/.github/workflows/cyberia-client.cd.yml +40 -0
- package/.github/workflows/cyberia-server.cd.yml +40 -0
- package/.github/workflows/docker-image.cyberia-client.ci.yml +49 -0
- package/.github/workflows/docker-image.cyberia-client.dev.ci.yml +48 -0
- package/.github/workflows/docker-image.cyberia-server.ci.yml +69 -0
- package/.github/workflows/docker-image.cyberia-server.dev.ci.yml +69 -0
- package/.github/workflows/docker-image.engine-cyberia.ci.yml +52 -0
- package/.github/workflows/docker-image.engine-cyberia.dev.ci.yml +52 -0
- package/.github/workflows/engine-cyberia.cd.yml +33 -24
- package/.github/workflows/engine-cyberia.ci.yml +13 -3
- package/.github/workflows/ghpkg.ci.yml +88 -1
- package/.github/workflows/gitlab.ci.yml +1 -1
- package/.github/workflows/hardhat.ci.yml +1 -1
- package/.github/workflows/npmpkg.ci.yml +10 -7
- package/.github/workflows/publish.ci.yml +2 -2
- package/.github/workflows/publish.cyberia.ci.yml +5 -16
- package/.github/workflows/pwa-microservices-template-page.cd.yml +1 -8
- package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
- package/CHANGELOG.md +301 -1
- package/CLI-HELP.md +71 -6
- package/Dockerfile +141 -43
- package/Dockerfile.dev +143 -0
- package/Dockerfile.test +165 -0
- package/README.md +1 -1
- package/baremetal/commission-workflows.json +1 -0
- package/bin/build.js +31 -0
- package/bin/cyberia.js +1080 -184
- package/bin/deploy.js +2 -2
- package/bin/index.js +1080 -184
- package/bump.config.js +1 -0
- package/compose.env +131 -0
- package/conf.js +18 -1
- package/deployment.yaml +2 -2
- package/docker-compose.yml +316 -0
- package/hardhat/hardhat.config.js +2 -2
- package/hardhat/package-lock.json +620 -2041
- package/hardhat/package.json +7 -5
- package/hardhat/scripts/deployObjectLayerToken.js +18 -18
- package/hardhat/test/ObjectLayerToken.js +378 -274
- package/ipfs/configure-ipfs.sh +13 -0
- package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
- package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
- package/manifests/deployment/dd-cyberia-development/deployment.yaml +2 -2
- package/manifests/deployment/dd-cyberia-development/grpc-service.yaml +17 -0
- package/manifests/deployment/dd-cyberia-development/pv-pvc.yaml +32 -0
- package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
- package/mongodb/entrypoint.sh +76 -0
- package/nginx.conf +87 -0
- package/package.json +31 -19
- package/pv-pvc.yaml +32 -0
- package/scripts/disk-clean.sh +85 -60
- package/scripts/kubeadm-node-setup.sh +317 -0
- package/scripts/rocky-kickstart.sh +877 -185
- package/scripts/rpmfusion-ffmpeg-setup.sh +26 -50
- package/scripts/test-monitor.sh +3 -5
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +43 -7
- package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +17 -25
- package/src/api/cyberia-action/cyberia-action.controller.js +19 -0
- package/src/api/cyberia-action/cyberia-action.model.js +21 -29
- package/src/api/cyberia-action/cyberia-action.router.js +42 -7
- package/src/api/cyberia-action/cyberia-action.service.js +20 -4
- package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +58 -57
- package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +37 -6
- package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +8 -2
- package/src/api/cyberia-entity/cyberia-entity.router.js +39 -7
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.controller.js +74 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.model.js +67 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.router.js +63 -0
- package/src/api/cyberia-entity-type-default/cyberia-entity-type-default.service.js +46 -0
- package/src/api/cyberia-instance/cyberia-fallback-world.js +62 -10
- package/src/api/cyberia-instance/cyberia-instance.model.js +32 -2
- package/src/api/cyberia-instance/cyberia-instance.router.js +6 -6
- package/src/api/cyberia-instance/cyberia-world-generator.js +116 -3
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +3 -0
- package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +39 -7
- package/src/api/cyberia-map/cyberia-map.router.js +21 -6
- package/src/api/cyberia-quest/cyberia-quest.controller.js +38 -0
- package/src/api/cyberia-quest/cyberia-quest.router.js +47 -7
- package/src/api/cyberia-quest/cyberia-quest.service.js +59 -4
- package/src/api/cyberia-saga/cyberia-saga.controller.js +74 -0
- package/src/api/cyberia-saga/cyberia-saga.model.js +59 -0
- package/src/api/cyberia-saga/cyberia-saga.router.js +63 -0
- package/src/api/cyberia-saga/cyberia-saga.service.js +42 -0
- package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +551 -129
- package/src/api/cyberia-skill/cyberia-skill.controller.js +74 -0
- package/src/api/cyberia-skill/cyberia-skill.model.js +50 -0
- package/src/api/cyberia-skill/cyberia-skill.router.js +63 -0
- package/src/api/cyberia-skill/cyberia-skill.service.js +42 -0
- package/src/api/ipfs/ipfs.service.js +28 -15
- package/src/api/object-layer/object-layer.router.js +25 -15
- package/src/api/object-layer/object-layer.service.js +15 -20
- package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +39 -7
- package/src/cli/baremetal.js +717 -16
- package/src/cli/cluster.js +80 -5
- package/src/cli/deploy.js +127 -11
- package/src/cli/docker-compose.js +648 -0
- package/src/cli/env.js +11 -2
- package/src/cli/fs.js +75 -30
- package/src/cli/image.js +129 -51
- package/src/cli/index.js +110 -6
- package/src/cli/kickstart.js +142 -20
- package/src/cli/release.js +46 -4
- package/src/cli/repository.js +238 -33
- package/src/cli/run.js +520 -114
- package/src/cli/secrets.js +82 -48
- package/src/cli/ssh.js +234 -0
- package/src/cli/static.js +2 -2
- package/src/client/components/cyberia/ActionEngineCyberia.js +1867 -0
- package/src/client/components/cyberia/EntityEngineCyberia.js +585 -0
- package/src/client/components/cyberia/InstanceEngineCyberia.js +219 -13
- package/src/client/components/cyberia/MapEngineCyberia.js +154 -71
- package/src/client/components/cyberia/ObjectLayerEngineModal.js +40 -63
- package/src/client/components/cyberia/ObjectLayerEngineViewer.js +34 -20
- package/src/client/components/cyberia/SharedDefaultsCyberia.js +195 -4
- package/src/client/components/cyberia-portal/AppShellCyberiaPortal.js +66 -0
- package/src/client/components/cyberia-portal/RouterCyberiaPortal.js +8 -0
- package/src/client/components/cyberia-portal/TranslateCyberiaPortal.js +8 -0
- package/src/client/public/cyberia-docs/ACTION-SYSTEM.md +45 -27
- package/src/client/public/cyberia-docs/CYBERIA-CLI.md +3 -3
- package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +39 -22
- package/src/client/public/cyberia-docs/CYBERIA-LORE.md +88 -0
- package/src/client/public/cyberia-docs/CYBERIA-SAGA.md +394 -0
- package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +8 -1
- package/src/client/public/cyberia-docs/CYBERIA.md +1 -1
- package/src/client/public/cyberia-docs/QUEST-SYSTEM.md +5 -5
- package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
- package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
- package/src/client/services/cyberia-entity-type-default/cyberia-entity-type-default.service.js +99 -0
- package/src/client/services/cyberia-instance/cyberia-instance.management.js +2 -2
- package/src/client/services/cyberia-map/cyberia-map.management.js +2 -2
- package/src/client/services/cyberia-saga/cyberia-saga.service.js +99 -0
- package/src/client/services/cyberia-skill/cyberia-skill.service.js +99 -0
- package/src/client/services/object-layer/object-layer.management.js +6 -6
- package/src/{server → client-builder}/client-build-docs.js +15 -5
- package/src/{server → client-builder}/client-build-live.js +3 -3
- package/src/{server → client-builder}/client-build.js +25 -22
- package/src/{server → client-builder}/client-dev-server.js +3 -3
- package/src/{server → client-builder}/client-icons.js +2 -2
- package/src/{server → client-builder}/ssr.js +5 -5
- package/src/client.build.js +1 -3
- package/src/client.dev.js +1 -1
- package/src/db/mongo/MongoBootstrap.js +12 -12
- package/src/grpc/cyberia/grpc-server.js +255 -70
- package/src/index.js +12 -1
- package/src/mailer/EmailRender.js +1 -1
- package/src/{server → projects/cyberia}/atlas-sprite-sheet-generator.js +2 -2
- package/src/{server → projects/cyberia}/besu-genesis-generator.js +3 -3
- package/src/projects/cyberia/catalog-cyberia.js +81 -0
- package/src/projects/cyberia/gemini-client.js +175 -0
- package/src/projects/cyberia/generate-saga.js +2107 -0
- package/src/{server → projects/cyberia}/ipfs-client.js +2 -2
- package/src/{server → projects/cyberia}/object-layer.js +12 -108
- package/src/{server → projects/cyberia}/semantic-layer-generator-floor.js +1 -1
- package/src/{server → projects/cyberia}/semantic-layer-generator-resource.js +1 -1
- package/src/{server → projects/cyberia}/semantic-layer-generator-skin.js +1 -1
- package/src/{server → projects/cyberia}/semantic-layer-generator.js +2 -2
- package/src/{server → projects/cyberia}/shape-generator.js +2 -2
- package/src/{server → projects/underpost}/catalog-underpost.js +1 -2
- package/src/runtime/cyberia-client/Dockerfile +27 -61
- package/src/runtime/cyberia-client/Dockerfile.dev +20 -12
- package/src/runtime/cyberia-server/Dockerfile +21 -20
- package/src/runtime/cyberia-server/Dockerfile.dev +17 -8
- package/src/runtime/engine-cyberia/Dockerfile +143 -0
- package/src/runtime/engine-cyberia/Dockerfile.dev +143 -0
- package/src/runtime/engine-cyberia/Dockerfile.test +165 -0
- package/src/runtime/engine-cyberia/compose.env +131 -0
- package/src/runtime/engine-cyberia/docker-compose.yml +316 -0
- package/src/runtime/engine-cyberia/ipfs/configure-ipfs.sh +13 -0
- package/src/runtime/engine-cyberia/mongodb/entrypoint.sh +76 -0
- package/src/runtime/engine-cyberia/nginx.conf +87 -0
- package/src/runtime/express/Express.js +2 -2
- package/src/runtime/nginx/Nginx.js +250 -0
- package/src/server/catalog.js +9 -14
- package/src/server/conf.js +3 -6
- package/src/server/runtime-status.js +18 -1
- package/src/server/start.js +12 -2
- package/src/server.js +6 -2
- package/test/cyberia-instance-conf-defaults.test.js +140 -0
- package/test/deploy-monitor.test.js +26 -10
- package/test/shape-generator.test.js +7 -1
- package/typedoc.dd-cyberia.json +3 -1
- package/typedoc.json +3 -1
- /package/src/client/ssr/{Render.js → RootDocument.js} +0 -0
- /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
|
-
|
|
5
|
-
|
|
6
|
-
describe('ObjectLayerToken (ERC-1155)', function () {
|
|
6
|
+
describe('ObjectLayerToken (ERC-1155)', { concurrency: false }, function () {
|
|
7
7
|
let token;
|
|
8
|
-
let
|
|
9
|
-
let
|
|
10
|
-
let
|
|
11
|
-
let
|
|
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 =
|
|
20
|
+
const INITIAL_SUPPLY = parseEther('10000000'); // 10M with 18 decimals
|
|
16
21
|
|
|
17
22
|
beforeEach(async function () {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
90
|
-
|
|
91
|
-
|
|
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
|
|
97
|
-
|
|
98
|
-
|
|
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
|
|
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(
|
|
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
|
|
144
|
-
|
|
145
|
-
const
|
|
167
|
+
const itemId = 'axe';
|
|
168
|
+
const metadataCid = 'bafkreia_axe_meta';
|
|
169
|
+
const supply = 1n;
|
|
146
170
|
|
|
147
|
-
await
|
|
148
|
-
.to.emit(token, 'ObjectLayerRegistered')
|
|
149
|
-
.withArgs(tokenId, testItemId, testMetadataCid, testSupply);
|
|
171
|
+
await token.write.registerObjectLayer([ownerAddress, itemId, metadataCid, supply, '0x']);
|
|
150
172
|
|
|
151
|
-
|
|
152
|
-
expect(await token[
|
|
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
|
-
|
|
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(
|
|
159
|
-
expect(await token.getItemId(tokenId)).to.equal(
|
|
160
|
-
expect(await token.getMetadataCID(tokenId)).to.equal(
|
|
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
|
-
|
|
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(
|
|
167
|
-
expect(await token.uri(tokenId)).to.equal(`${BASE_URI}${
|
|
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
|
-
|
|
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(
|
|
174
|
-
expect(await token.balanceOf(
|
|
175
|
-
expect(await token.getItemId(tokenId)).to.equal(
|
|
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
|
-
|
|
208
|
+
const itemId = 'potion';
|
|
209
|
+
await token.write.registerObjectLayer([ownerAddress, itemId, '', 1n, '0x']);
|
|
180
210
|
|
|
181
|
-
const tokenId = await token.computeTokenId(
|
|
182
|
-
expect(await token.getMetadataCID(tokenId)).to.equal('');
|
|
183
|
-
expect(await token.balanceOf(
|
|
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
|
|
188
|
-
|
|
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(
|
|
191
|
-
expect(await token.balanceOf(
|
|
192
|
-
expect(await token
|
|
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
|
-
|
|
227
|
+
const itemId = 'ring';
|
|
228
|
+
await token.write.registerObjectLayer([ownerAddress, itemId, '', 1n, '0x']);
|
|
197
229
|
|
|
198
|
-
const tokenId = await token.computeTokenId(
|
|
199
|
-
expect(await token.getTokenIdByItemId(
|
|
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
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
|
212
|
-
|
|
213
|
-
)
|
|
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 = ['
|
|
224
|
-
const supplies = [1n, 1n,
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
241
|
-
token.batchRegisterObjectLayers(
|
|
242
|
-
|
|
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
|
-
|
|
247
|
-
token.batchRegisterObjectLayers(
|
|
248
|
-
|
|
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
|
|
253
|
-
|
|
254
|
-
)
|
|
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 =
|
|
265
|
-
await token.mint(
|
|
322
|
+
const additionalAmount = parseEther('5000000');
|
|
323
|
+
await token.write.mint([player1Address, CRYPTOKOYN_ID, additionalAmount, '0x']);
|
|
266
324
|
|
|
267
|
-
expect(await token.balanceOf(
|
|
268
|
-
expect(await token
|
|
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
|
-
|
|
273
|
-
|
|
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(
|
|
276
|
-
expect(await token.balanceOf(
|
|
277
|
-
expect(await token
|
|
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(
|
|
282
|
-
await token.registerObjectLayer(
|
|
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(
|
|
346
|
+
await token.write.mintBatch([player1Address, [woodId, stoneId, CRYPTOKOYN_ID], [100n, 200n, 50n], '0x']);
|
|
288
347
|
|
|
289
|
-
expect(await token.balanceOf(
|
|
290
|
-
expect(await token.balanceOf(
|
|
291
|
-
expect(await token.balanceOf(
|
|
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
|
|
296
|
-
|
|
297
|
-
|
|
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
|
|
303
|
-
|
|
304
|
-
)
|
|
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 =
|
|
315
|
-
await token.safeTransferFrom(
|
|
384
|
+
const amount = parseEther('1000');
|
|
385
|
+
await token.write.safeTransferFrom([ownerAddress, player1Address, CRYPTOKOYN_ID, amount, '0x']);
|
|
316
386
|
|
|
317
|
-
expect(await token.balanceOf(
|
|
318
|
-
expect(await token.balanceOf(
|
|
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
|
-
|
|
323
|
-
|
|
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(
|
|
396
|
+
await token.write.safeTransferFrom([ownerAddress, player1Address, tokenId, 1n, '0x']);
|
|
326
397
|
|
|
327
|
-
expect(await token.balanceOf(
|
|
328
|
-
expect(await token.balanceOf(
|
|
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(
|
|
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
|
-
|
|
337
|
-
|
|
406
|
+
await token.write.safeBatchTransferFrom([
|
|
407
|
+
ownerAddress,
|
|
408
|
+
player1Address,
|
|
338
409
|
[CRYPTOKOYN_ID, helmId],
|
|
339
|
-
[
|
|
410
|
+
[parseEther('500'), 2n],
|
|
340
411
|
'0x',
|
|
341
|
-
);
|
|
412
|
+
]);
|
|
342
413
|
|
|
343
|
-
expect(await token.balanceOf(
|
|
344
|
-
expect(await token.balanceOf(
|
|
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
|
-
|
|
349
|
-
|
|
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
|
-
[
|
|
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 =
|
|
369
|
-
await token.burn(
|
|
440
|
+
const burnAmount = parseEther('100');
|
|
441
|
+
await token.write.burn([ownerAddress, CRYPTOKOYN_ID, burnAmount]);
|
|
370
442
|
|
|
371
|
-
expect(await token.balanceOf(
|
|
372
|
-
expect(await token
|
|
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
|
-
|
|
377
|
-
|
|
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(
|
|
452
|
+
await token.write.burnBatch([ownerAddress, [CRYPTOKOYN_ID, burnItemId], [parseEther('50'), 3n]]);
|
|
380
453
|
|
|
381
|
-
expect(await token.balanceOf(
|
|
382
|
-
expect(await token.balanceOf(
|
|
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(
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
402
|
-
|
|
403
|
-
|
|
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
|
-
|
|
416
|
-
token.safeTransferFrom(
|
|
417
|
-
|
|
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
|
|
425
|
-
|
|
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
|
-
|
|
433
|
-
token,
|
|
434
|
-
'
|
|
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
|
-
|
|
442
|
-
token,
|
|
443
|
-
'
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
481
|
-
|
|
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
|
|
590
|
+
expect(await token.read.totalSupply([tokenId])).to.equal(10n);
|
|
484
591
|
|
|
485
|
-
await token.burn(
|
|
592
|
+
await token.write.burn([ownerAddress, tokenId, 3n]);
|
|
486
593
|
|
|
487
|
-
expect(await token
|
|
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.
|
|
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
|
-
|
|
512
|
-
expect(await token.
|
|
513
|
-
expect(await token.
|
|
514
|
-
expect(await token[
|
|
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.
|
|
617
|
+
// 2. Register a fungible resource
|
|
518
618
|
const resourceItemId = 'gold-ore';
|
|
519
619
|
const resourceCid = 'bafkreia_gold_ore_meta';
|
|
520
|
-
const resourceSupply =
|
|
620
|
+
const resourceSupply = parseEther('1000000');
|
|
521
621
|
|
|
522
|
-
await token.registerObjectLayer(
|
|
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
|
|
527
|
-
await token.safeTransferFrom(
|
|
528
|
-
expect(await token.balanceOf(
|
|
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
|
|
531
|
-
const lootAmount =
|
|
532
|
-
await token.safeTransferFrom(
|
|
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
|
|
535
|
-
await token
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
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(
|
|
546
|
-
expect(await token.balanceOf(
|
|
547
|
-
expect(await token.balanceOf(
|
|
548
|
-
expect(await token.balanceOf(
|
|
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
|
|
551
|
-
const
|
|
552
|
-
|
|
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(
|
|
555
|
-
expect(await token
|
|
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
|
|
558
|
-
await token.safeTransferFrom(
|
|
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
|
-
[
|
|
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(
|
|
566
|
-
expect(balances[1]).to.equal(0n);
|
|
567
|
-
expect(balances[2]).to.equal(
|
|
568
|
-
expect(balances[3]).to.equal(1n);
|
|
569
|
-
expect(balances[4]).to.equal(
|
|
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(
|
|
574
|
-
const tokenId = await token.computeTokenId('gov-item');
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
)
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
await token.unpause();
|
|
588
|
-
await token.safeTransferFrom(
|
|
589
|
-
expect(await token.balanceOf(
|
|
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
|
});
|