diff --git a/nodejs/package-lock.json b/nodejs/package-lock.json index a95c78d..60ea6f8 100644 --- a/nodejs/package-lock.json +++ b/nodejs/package-lock.json @@ -19,6 +19,7 @@ "freeverse-apisigner-js": "^1.1.1", "freeverse-crypto-js": "^1.0.6", "freeverse-marketsigner-js": "^3.0.0", + "graphql-request": "^5.0.0", "ipfs-only-hash": "4.0.0", "isomorphic-fetch": "^3.0.0", "web3-eth": "^1.7.3", @@ -1290,6 +1291,14 @@ "@ethersproject/strings": "^5.6.0" } }, + "node_modules/@graphql-typed-document-node/core": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz", + "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==", + "peerDependencies": { + "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", @@ -5450,6 +5459,17 @@ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, + "node_modules/extract-files": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz", + "integrity": "sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==", + "engines": { + "node": "^10.17.0 || ^12.0.0 || >= 13.7.0" + }, + "funding": { + "url": "https://github.com/sponsors/jaydenseric" + } + }, "node_modules/extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -14104,6 +14124,42 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, + "node_modules/graphql": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.6.0.tgz", + "integrity": "sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==", + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/graphql-request": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-5.0.0.tgz", + "integrity": "sha512-SpVEnIo2J5k2+Zf76cUkdvIRaq5FMZvGQYnA4lUWYbc99m+fHh4CZYRRO/Ff4tCLQ613fzCm3SiDT64ubW5Gyw==", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "cross-fetch": "^3.1.5", + "extract-files": "^9.0.0", + "form-data": "^3.0.0" + }, + "peerDependencies": { + "graphql": "14 - 16" + } + }, + "node_modules/graphql-request/node_modules/form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/hamt-sharding": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/hamt-sharding/-/hamt-sharding-2.0.1.tgz", @@ -19261,6 +19317,12 @@ "@ethersproject/strings": "^5.6.0" } }, + "@graphql-typed-document-node/core": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz", + "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==", + "requires": {} + }, "@humanwhocodes/config-array": { "version": "0.9.5", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", @@ -22777,6 +22839,11 @@ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, + "extract-files": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz", + "integrity": "sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==" + }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -29245,6 +29312,35 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, + "graphql": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.6.0.tgz", + "integrity": "sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==", + "peer": true + }, + "graphql-request": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-5.0.0.tgz", + "integrity": "sha512-SpVEnIo2J5k2+Zf76cUkdvIRaq5FMZvGQYnA4lUWYbc99m+fHh4CZYRRO/Ff4tCLQ613fzCm3SiDT64ubW5Gyw==", + "requires": { + "@graphql-typed-document-node/core": "^3.1.1", + "cross-fetch": "^3.1.5", + "extract-files": "^9.0.0", + "form-data": "^3.0.0" + }, + "dependencies": { + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, "hamt-sharding": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/hamt-sharding/-/hamt-sharding-2.0.1.tgz", diff --git a/nodejs/package.json b/nodejs/package.json index e935133..2bfcf44 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -18,6 +18,7 @@ "freeverse-apisigner-js": "^1.1.1", "freeverse-crypto-js": "^1.0.6", "freeverse-marketsigner-js": "^3.0.0", + "graphql-request": "^5.0.0", "ipfs-only-hash": "4.0.0", "isomorphic-fetch": "^3.0.0", "web3-eth": "^1.7.3", diff --git a/nodejs/update_many_asset.js b/nodejs/update_many_asset.js new file mode 100644 index 0000000..4e67624 --- /dev/null +++ b/nodejs/update_many_asset.js @@ -0,0 +1,133 @@ +/* eslint-disable no-console */ +/* eslint-disable camelcase */ +/* eslint-disable no-await-in-loop */ + +/* +UPDATES AN ASSET +See asset_create for more info + +INPUTS: +* pvk: the private key of the owner of the universe +* uni: the universe id +*/ + +const pvk = '0x3B878F7892FBBFA30C8AED1DF317C19B853685E707C2CF0EE1927DC516060A54'; +const universe = '0'; +const endpoint = 'https://api.blackhole.gorengine.com/'; + +const identity = require('freeverse-crypto-js'); +const { updateAssetOp, AtomicAssetOps } = require('freeverse-apisigner-js'); +const { request, gql } = require('graphql-request'); + +// create web3 account from your private key +// (other forms of creating web3 account could be subsituted) +const universeOwnerAccount = identity.accountFromPrivateKey(pvk); + +async function getAssetNonce(id) { + const query = gql` + query ($id: String!) { + assetById(id: $id) { + nonce + } + } + `; + + const result = await request(endpoint, query, { + id, + }); + const { nonce = 0 } = result?.assetById || {}; + return parseInt(nonce, 10); +} + +async function getAllAssetsInUniverse() { + const limit = 1000; + const totalCountQuery = gql` + query ($universe: Int!) { + allAssets(condition: { universeId: $universe }) { + totalCount + } + } + `; + const totalCountResult = await request(endpoint, totalCountQuery, { + universe: parseInt(universe, 10), + }); + if (!totalCountResult || !totalCountResult.allAssets) return []; + + const query = gql` + query ($universe: Int!, $afterCursor: Cursor, $limit: Int) { + allAssets( + condition: { universeId: $universe } + first: $limit + after: $afterCursor + ) { + nodes { + id + props + metadata + } + totalCount + pageInfo { + hasNextPage + endCursor + } + } + } + `; + let assets = []; + let afterCursor; + console.log('fetching assets...'); + for ( + let i = 0; + i < Math.ceil(totalCountResult.allAssets.totalCount / limit); + i += 1 + ) { + const result = await request(endpoint, query, { + universe: parseInt(universe, 10), + afterCursor, + limit, + }); + if (!result || !result.allAssets) return []; + afterCursor = result.allAssets.pageInfo.endCursor; + assets = [...assets, ...result.allAssets.nodes]; + console.log(`${assets.length}/${totalCountResult.allAssets.totalCount}`); + } + + return assets; +} + +async function buildOps(asset, assetOps) { + const nonce = await getAssetNonce(asset.id); + const updatedAsset = { + nonce: JSON.parse(nonce), + assetId: asset.id, + props: JSON.parse(asset.props), + metadata: { test: `test-${Math.floor(Math.random() * 1000)}` }, + }; + const op = updateAssetOp(updatedAsset); + assetOps.push({ op }); +} + +const run = async () => { + const allAssets = await getAllAssetsInUniverse(); + + const n = 100; + console.log(`building mutation for update ${n} assets...`); + const assetOps = new AtomicAssetOps({ universeId: universe }); + for (let i = 0; i < n; i += 1) { + console.log(`${i}/${n}`); + await buildOps(allAssets[i], assetOps); + } + + const signature = assetOps.sign({ web3Account: universeOwnerAccount }); + const mutation = assetOps.mutation({ signature }); + + console.log('Sending Mutation...'); + + return request(endpoint, mutation).then((response) => { + console.log('Update Response:', response); + const resultTemp = response.execute.results.map((res) => JSON.parse(res)); + return resultTemp[0]; + }); +}; + +run();