diff --git a/package-lock.json b/package-lock.json index 39d350b..59e2d6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,15 @@ { "name": "@itheum/sdk-mx-data-nft", - "version": "3.6.0-alpha.3", + "version": "3.8.0-alpha.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@itheum/sdk-mx-data-nft", - "version": "3.6.0-alpha.3", + "version": "3.8.0-alpha.10", "license": "GPL-3.0-only", "dependencies": { - "@multiversx/sdk-core": "13.2.2", - "@multiversx/sdk-network-providers": "2.4.3", + "@multiversx/sdk-core": "^13.14.1", "bignumber.js": "9.1.2", "nft.storage": "7.2.0" }, @@ -1942,43 +1941,44 @@ "murmurhash3js-revisited": "^3.0.0" } }, + "node_modules/@multiversx/sdk-bls-wasm": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-bls-wasm/-/sdk-bls-wasm-0.3.5.tgz", + "integrity": "sha512-c0tIdQUnbBLSt6NYU+OpeGPYdL0+GV547HeHT8Xc0BKQ7Cj0v82QUoA2QRtWrR1G4MNZmLsIacZSsf6DrIS2Bw==", + "optional": true, + "engines": { + "node": ">=8.9.0" + } + }, "node_modules/@multiversx/sdk-core": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-core/-/sdk-core-13.2.2.tgz", - "integrity": "sha512-ABQuy7PcFBnl5f9yFczgaq7tX72X0M836Ky9h4HRCQd5Mao3OJ3TrgHEvxZe9SVYXtwOm337iPsbkZzVslxu9A==", + "version": "13.14.1", + "resolved": "https://registry.npmjs.org/@multiversx/sdk-core/-/sdk-core-13.14.1.tgz", + "integrity": "sha512-PlUbeAWJVbqwCjF6zhlTPUTTMTatcI3YZtOa5K+YOs4Xk5UEuZcaKacvt1fNGZiSVubBlUlNK0jaxF3Hbi+VwA==", "dependencies": { "@multiversx/sdk-transaction-decoder": "1.0.2", + "@noble/ed25519": "1.7.3", + "@noble/hashes": "1.3.0", "bech32": "1.1.4", "blake2b": "2.1.3", "buffer": "6.0.3", + "ed25519-hd-key": "1.1.2", + "ed2curve": "0.3.0", "json-bigint": "1.0.0", - "keccak": "3.0.2" + "keccak": "3.0.2", + "scryptsy": "2.1.0", + "tweetnacl": "1.0.3", + "uuid": "8.3.2" + }, + "optionalDependencies": { + "@multiversx/sdk-bls-wasm": "0.3.5", + "axios": "^1.7.4", + "bip39": "3.1.0" }, "peerDependencies": { "bignumber.js": "^9.0.1", "protobufjs": "^7.2.6" } }, - "node_modules/@multiversx/sdk-network-providers": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/@multiversx/sdk-network-providers/-/sdk-network-providers-2.4.3.tgz", - "integrity": "sha512-tJmJuxU+BjtC2q29PuzQOM4Qr6aiXujKwQXgIAPHTiuNbMc3Yi6Q4B0DC1PfI3iG+M4DONwfXknvM1uwqnY2zA==", - "dependencies": { - "axios": "1.6.8", - "bech32": "1.1.4", - "bignumber.js": "9.0.1", - "buffer": "6.0.3", - "json-bigint": "1.0.0" - } - }, - "node_modules/@multiversx/sdk-network-providers/node_modules/bignumber.js": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz", - "integrity": "sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==", - "engines": { - "node": "*" - } - }, "node_modules/@multiversx/sdk-transaction-decoder": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@multiversx/sdk-transaction-decoder/-/sdk-transaction-decoder-1.0.2.tgz", @@ -1992,6 +1992,28 @@ "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" }, + "node_modules/@noble/ed25519": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-1.7.3.tgz", + "integrity": "sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@noble/hashes": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", + "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -3275,7 +3297,8 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "optional": true }, "node_modules/available-typed-arrays": { "version": "1.0.5", @@ -3289,9 +3312,10 @@ } }, "node_modules/axios": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", - "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "optional": true, "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -3432,6 +3456,15 @@ "node": "*" } }, + "node_modules/bip39": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.1.0.tgz", + "integrity": "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==", + "optional": true, + "dependencies": { + "@noble/hashes": "^1.2.0" + } + }, "node_modules/bl": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", @@ -3725,6 +3758,15 @@ "node": ">=8" } }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/cjs-module-lexer": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", @@ -3857,6 +3899,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "optional": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -4099,6 +4142,31 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -4264,6 +4332,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "optional": true, "engines": { "node": ">=0.4.0" } @@ -4368,6 +4437,40 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/ed25519-hd-key": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ed25519-hd-key/-/ed25519-hd-key-1.1.2.tgz", + "integrity": "sha512-/0y9y6N7vM6Kj5ASr9J9wcMVDTtygxSOvYX+PJiMD7VcxCx2G03V5bLRl8Dug9EgkLFsLhGqBtQWQRcElEeWTA==", + "dependencies": { + "bip39": "3.0.2", + "create-hmac": "1.1.7", + "tweetnacl": "1.0.3" + } + }, + "node_modules/ed25519-hd-key/node_modules/@types/node": { + "version": "11.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", + "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==" + }, + "node_modules/ed25519-hd-key/node_modules/bip39": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.2.tgz", + "integrity": "sha512-J4E1r2N0tUylTKt07ibXvhpT2c5pyAFgvuA5q1H9uDy6dEGpjV8jmymh3MTYJDLCNbIVClSB9FbND49I6N24MQ==", + "dependencies": { + "@types/node": "11.11.6", + "create-hash": "^1.1.0", + "pbkdf2": "^3.0.9", + "randombytes": "^2.0.1" + } + }, + "node_modules/ed2curve": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/ed2curve/-/ed2curve-0.3.0.tgz", + "integrity": "sha512-8w2fmmq3hv9rCrcI7g9hms2pMunQr1JINfcjwR9tAyZqhtyaMN991lF/ZfHfr5tzZQ8c7y7aBgZbjfbd0fjFwQ==", + "dependencies": { + "tweetnacl": "1.x.x" + } + }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", @@ -4855,15 +4958,16 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], + "optional": true, "engines": { "node": ">=4.0" }, @@ -4882,9 +4986,10 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "optional": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -5347,6 +5452,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", @@ -7352,6 +7470,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, "node_modules/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", @@ -7452,6 +7580,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "optional": true, "engines": { "node": ">= 0.6" } @@ -7460,6 +7589,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "optional": true, "dependencies": { "mime-db": "1.52.0" }, @@ -7726,9 +7856,9 @@ } }, "node_modules/node-gyp-build": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.1.tgz", - "integrity": "sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.2.tgz", + "integrity": "sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==", "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", @@ -10653,6 +10783,21 @@ "node": ">=8" } }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -10848,9 +10993,9 @@ "dev": true }, "node_modules/protobufjs": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.3.2.tgz", - "integrity": "sha512-RXyHaACeqXeqAKGLDl68rQKbmObRsTIn4TYVUUug1KfS47YWCo5MacGITEryugIgZqORCvJWEk4l449POg5Txg==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz", + "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==", "hasInstallScript": true, "peer": true, "dependencies": { @@ -10874,7 +11019,8 @@ "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "optional": true }, "node_modules/punycode.js": { "version": "2.3.1", @@ -10945,6 +11091,14 @@ "rabin-wasm": "cli/bin.js" } }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -11294,6 +11448,15 @@ "node": ">=0.10.0" } }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -11346,6 +11509,11 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/scryptsy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz", + "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==" + }, "node_modules/semantic-release": { "version": "24.0.0", "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-24.0.0.tgz", @@ -11702,6 +11870,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -12548,6 +12728,11 @@ "typescript": ">=2.1.0 || >=2.1.0-dev || >=2.2.0-dev || >=2.3.0-dev || >=2.4.0-dev || >=2.5.0-dev || >=2.6.0-dev || >=2.7.0-dev || >=2.8.0-dev || >=2.9.0-dev || >= 3.0.0-dev || >= 3.1.0-dev" } }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -12762,6 +12947,14 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-to-istanbul": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", diff --git a/package.json b/package.json index 08466bc..2072dfd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@itheum/sdk-mx-data-nft", - "version": "3.6.1", + "version": "3.8.0-alpha.16", "description": "SDK for Itheum's Data NFT Technology on MultiversX Blockchain", "main": "out/index.js", "types": "out/index.d.js", @@ -17,8 +17,7 @@ "author": "Itheum Protocol", "license": "GPL-3.0-only", "dependencies": { - "@multiversx/sdk-core": "13.2.2", - "@multiversx/sdk-network-providers": "2.4.3", + "@multiversx/sdk-core": "13.14.1", "bignumber.js": "9.1.2", "nft.storage": "7.2.0" }, diff --git a/src/bond.ts b/src/bond.ts index 4ff49ef..f1e65a0 100644 --- a/src/bond.ts +++ b/src/bond.ts @@ -44,9 +44,9 @@ export class BondContract extends Contract { /** * Creates a new instance of the DataNftMarket which can be used to interact with the marketplace smart contract * @param env 'devnet' | 'mainnet' | 'testnet' - * @param timeout Timeout for the network provider (DEFAULT = 10000ms) + * @param timeout Timeout for the network provider (DEFAULT = 20000ms) */ - constructor(env: string, timeout: number = 10000) { + constructor(env: string, timeout: number = 20000) { super( env, new Address(bondContractAddress[env as EnvironmentsEnum]), diff --git a/src/cnft-sol-minter.ts b/src/cnft-sol-minter.ts new file mode 100644 index 0000000..0c42cf2 --- /dev/null +++ b/src/cnft-sol-minter.ts @@ -0,0 +1,263 @@ +import { + dataNFTDataStreamAdvertise, + storeToIpfsOnlyImg, + createIpfsMetadataSolCNft, + storeToIpfsFullSolCNftMetadata +} from './common/mint-utils'; +import { checkTraitsUrl, checkUrlIsUp } from './common/utils'; +import { ErrArgumentNotSet } from './errors'; +import { MinterSol } from './minter-sol'; +import { StringValidator, validateResults } from './common/validator'; +import { CNftSolPostMintMetaType } from './interfaces'; +import { SolEnvChainIDEnum } from './config'; + +export class CNftSolMinter extends MinterSol { + /** + * Creates a new instance of the `SftMinter` class, which can be used to interact with the Data NFT-FT minter smart contract + * @param env 'devnet' | 'mainnet' | 'testnet' + */ + constructor(env: string) { + super(env); + } + + /** + * Creates a `mint` transaction + * + * NOTE: The `dataStreamUrl` is being encrypted and the `media` and `metadata` urls are build and uploaded to IPFS + * + * NOTE: The `options.nftStorageToken` is required when not using custom image and traits, when using custom image and traits the traits should be compliant with the `traits` structure + * + * @param creatorAddress the address of the creator who we mint a CNft for + * @param tokenName the name of the DataNFT-FT. Between 3 and 20 alphanumeric characters, no spaces. + * @param dataMarshalUrl the url of the data marshal. A live HTTPS URL that returns a 200 OK HTTP code. + * @param dataStreamUrl the url of the data stream to be encrypted. A live HTTPS URL that returns a 200 OK HTTP code. + * @param dataPreviewUrl the url of the data preview. A live HTTPS URL that returns a 200 OK HTTP code. + * @param datasetTitle the title of the dataset. Between 10 and 60 alphanumeric characters. + * @param datasetDescription the description of the dataset. Between 10 and 400 alphanumeric characters. + * @param options [optional] below parameters are optional or required based on use case + * - imageUrl: the URL of the image for the Data NFT + * - traitsUrl: the URL of the traits for the Data NFT + * - nftStorageToken: the nft storage token to be used to upload the image and metadata to IPFS + * - extraAssets: [optional] extra URIs to attached to the NFT. Can be media files, documents, etc. These URIs are public + * - imgGenBg: [optional] the custom series bg to influence the image generation service + * - imgGenSet: [optional] the custom series layer set to influence the image generation service + * - signatureNonce: [optional] a recent nonce from the marshal network that will be signed to produce solSignature + * - solSignature: [optional] a solana signature of signatureNonce to prove creatorAddress ownership + * + */ + async mint( + creatorAddress: string, + tokenName: string, + dataMarshalUrl: string, + dataStreamUrl: string, + dataPreviewUrl: string, + datasetTitle: string, + datasetDescription: string, + options?: { + imageUrl?: string; + traitsUrl?: string; + nftStorageToken?: string; + extraAssets?: string[]; + imgGenBg?: string; + imgGenSet?: string; + signatureNonce?: string; + solSignature?: string; + } + ): Promise<{ + imageUrl: string; + metadataUrl: string; + mintMeta: CNftSolPostMintMetaType; + }> { + let imageOnIpfsUrl: string = ''; + let metadataOnIpfsUrl: string = ''; + let mintMeta: CNftSolPostMintMetaType = {}; + + try { + const { + imageUrl, + traitsUrl, + nftStorageToken, + extraAssets, + imgGenBg, + imgGenSet, + signatureNonce, + solSignature + } = options ?? {}; + + const tokenNameValidator = new StringValidator() + .notEmpty() + .alphanumeric() + .minLength(3) + .maxLength(20) + .validate(tokenName); + + const datasetTitleValidator = new StringValidator() + .notEmpty() + .minLength(10) + .maxLength(60) + .validate(datasetTitle.trim()); + + const datasetDescriptionValidator = new StringValidator() + .notEmpty() + .minLength(10) + .maxLength(400) + .validate(datasetDescription); + + validateResults([ + tokenNameValidator, + datasetTitleValidator, + datasetDescriptionValidator + ]); + + // deep validate all mandatory URLs + try { + await checkUrlIsUp(dataPreviewUrl, [200]); + await checkUrlIsUp(dataMarshalUrl + '/health-check', [200]); + } catch (error) { + throw error; + } + + // handle all logic related to data stream and ipfs gen of img,traits etc + let allDataStreamAndIPFSLogicDone = false; + + try { + const { dataNftHash, dataNftStreamUrlEncrypted } = + await dataNFTDataStreamAdvertise( + dataStreamUrl, + dataMarshalUrl, + creatorAddress // the caller is the Creator + ); + + if (!imageUrl) { + if (!nftStorageToken) { + throw new ErrArgumentNotSet( + 'nftStorageToken', + 'NFT Storage token is required when not using custom image and traits' + ); + } + + // create the img generative service API based on user options + let imgGenServiceApi = `${this.imageServiceUrl}/v1/generateNFTArt?hash=${dataNftHash}`; + + if (imgGenBg && imgGenBg.trim() !== '') { + imgGenServiceApi += `&bg=${imgGenBg.trim()}`; + } + + if (imgGenSet && imgGenSet.trim() !== '') { + imgGenServiceApi += `&set=${imgGenSet.trim()}`; + } + + let resImgCall: any = ''; + let dataImgCall: any = ''; + let _imageFile: Blob = new Blob(); + + resImgCall = await fetch(imgGenServiceApi); + dataImgCall = await resImgCall.blob(); + _imageFile = dataImgCall; + + const traitsFromImgHeader = + resImgCall.headers.get('x-nft-traits') || ''; + + const { imageOnIpfsUrl: imgOnIpfsUrl } = await storeToIpfsOnlyImg( + nftStorageToken, + _imageFile + ); + + if (!imgOnIpfsUrl || imgOnIpfsUrl === '') { + throw new Error('Saving Image to IPFS failed'); + } + + const cNftMetadataContent = createIpfsMetadataSolCNft( + tokenName, + datasetTitle, + datasetDescription, + imgOnIpfsUrl, + creatorAddress, + dataNftStreamUrlEncrypted, + dataPreviewUrl, + dataMarshalUrl, + traitsFromImgHeader, + extraAssets ?? [] + ); + + const { metadataIpfsUrl } = await storeToIpfsFullSolCNftMetadata( + nftStorageToken, + cNftMetadataContent + ); + + if (!metadataIpfsUrl || metadataIpfsUrl === '') { + throw new Error('Saving cNFT Metadata to IPFS failed'); + } + + imageOnIpfsUrl = imgOnIpfsUrl; + metadataOnIpfsUrl = metadataIpfsUrl; + } else { + if (!traitsUrl) { + throw new ErrArgumentNotSet( + 'traitsUrl', + 'Traits URL is required when using custom image' + ); + } + + await checkTraitsUrl(traitsUrl); + + imageOnIpfsUrl = imageUrl; + metadataOnIpfsUrl = traitsUrl; + } + + allDataStreamAndIPFSLogicDone = true; + } catch (e: any) { + throw e; + } + + // we not make a call to our private cNFt minter API + if (allDataStreamAndIPFSLogicDone) { + try { + const postHeaders = new Headers(); + postHeaders.append('Content-Type', 'application/json'); + + const raw = JSON.stringify({ + metadataOnIpfsUrl, + tokenName, + mintForSolAddr: creatorAddress, + solSignature: solSignature || '', + signatureNonce: signatureNonce || '', + chainId: + this.env === 'devnet' + ? SolEnvChainIDEnum.devnet + : SolEnvChainIDEnum.mainnet + }); + + const requestOptions = { + method: 'POST', + headers: postHeaders, + body: raw + }; + + let resMintCall: any = ''; + let dataMintCall: any = ''; + + resMintCall = await fetch( + this.solCNftNfMeIdMinterServiceUrl, + requestOptions + ); + + dataMintCall = await resMintCall.text(); + mintMeta = dataMintCall; + } catch (e: any) { + mintMeta = { error: true, errMsg: e.toString() }; + throw e; + } + } + } catch (error) { + console.error(error); + throw error; + } + + return { + imageUrl: imageOnIpfsUrl, + metadataUrl: metadataOnIpfsUrl, + mintMeta + }; + } +} diff --git a/src/common/mint-utils.ts b/src/common/mint-utils.ts index caadeff..5310f7c 100644 --- a/src/common/mint-utils.ts +++ b/src/common/mint-utils.ts @@ -43,10 +43,63 @@ export async function storeToIpfs( try { const imageHash = await storeImageToIpfs(image, storageToken); const traitsHash = await storeTraitsToIpfs(traits, storageToken); - return { - imageOnIpfsUrl: `https://ipfs.io/ipfs/${imageHash}`, - metadataOnIpfsUrl: `https://ipfs.io/ipfs/${traitsHash}` - }; + + if (imageHash && traitsHash) { + return { + imageOnIpfsUrl: `https://ipfs.io/ipfs/${imageHash}`, + metadataOnIpfsUrl: `https://ipfs.io/ipfs/${traitsHash}` + }; + } else { + return { + imageOnIpfsUrl: '', + metadataOnIpfsUrl: '' + }; + } + } catch (error) { + throw error; + } +} + +export async function storeToIpfsFullSolCNftMetadata( + storageToken: string, + metadataStructureSolCNft: object +): Promise<{ metadataIpfsUrl: string }> { + try { + const metadataIpfsHash = await storeTraitsToIpfs( + metadataStructureSolCNft, + storageToken + ); + + if (metadataIpfsHash) { + return { + metadataIpfsUrl: `https://ipfs.io/ipfs/${metadataIpfsHash}` + }; + } else { + return { + metadataIpfsUrl: '' + }; + } + } catch (error) { + throw error; + } +} + +export async function storeToIpfsOnlyImg( + storageToken: string, + image: Blob +): Promise<{ imageOnIpfsUrl: string }> { + try { + const imageHash = await storeImageToIpfs(image, storageToken); + + if (imageHash) { + return { + imageOnIpfsUrl: `https://ipfs.io/ipfs/${imageHash}` + }; + } else { + return { + imageOnIpfsUrl: '' + }; + } } catch (error) { throw error; } @@ -127,6 +180,49 @@ export function createIpfsMetadata( return metadata; } +export function createIpfsMetadataSolCNft( + tokenName: string, + datasetTitle: string, + datasetDescription: string, + imageOnIpfsUrl: string, + creatorAddress: string, + dataNFTStreamUrl: string, + dataNFTStreamPreviewUrl: string, + dataNFTDataMarshalUrl: string, + traits: string, + extraAssets: string[] +) { + const metadata: Record = { + name: tokenName, + description: `${datasetTitle} : ${datasetDescription}`, + image: imageOnIpfsUrl, + itheum_creator: creatorAddress, + itheum_data_stream_url: dataNFTStreamUrl, + itheum_data_preview_url: dataNFTStreamPreviewUrl, + itheum_data_marshal_url: dataNFTDataMarshalUrl, + attributes: [] as object[] + }; + + if (extraAssets && extraAssets.length > 0) { + metadata.extra_assets = extraAssets; + } + + const attributes = traits + .split(',') + .filter((element) => element.trim() !== ''); + + const metadataAttributes = []; + + for (const attribute of attributes) { + const [key, value] = attribute.split(':'); + const trait = { trait_type: key.trim(), value: value.trim() }; + metadataAttributes.push(trait); + } + + metadata.attributes = metadataAttributes; + return metadata; +} + export async function createFileFromUrl( url: string, datasetTitle: string, @@ -154,3 +250,33 @@ export async function createFileFromUrl( const _traitsFile = traits; return { image: _imageFile, traits: _traitsFile }; } + +export async function createFileFromUrlSolCNft( + url: string, + datasetTitle: string, + datasetDescription: string, + dataNFTStreamPreviewUrl: string, + address: string, + extraAssets: string[] +) { + let res: any = ''; + let data: any = ''; + let _imageFile: Blob = new Blob(); + + if (url) { + res = await fetch(url); + data = await res.blob(); + _imageFile = data; + } + + const traits = createIpfsMetadata( + res.headers.get('x-nft-traits') || '', + datasetTitle, + datasetDescription, + dataNFTStreamPreviewUrl, + address, + extraAssets + ); + const _traitsFile = traits; + return { image: _imageFile, traits: _traitsFile }; +} diff --git a/src/common/utils.ts b/src/common/utils.ts index e82a0b2..4a52e23 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -11,7 +11,6 @@ import { Bond, BondConfiguration, Compensation, - ContractConfiguration, LivelinessStakeConfiguration, NftEnumType, NftType, @@ -600,3 +599,50 @@ export function checkStatus(response: Response) { throw new ErrFetch(response.status, response.statusText); } } + +/* +Simple Caching Module helps throttle frequently used calls to RPC that fetch data +this helps speed up the client side app and also reduces calls to the RPC +we allow consumer to set a custom TTL in MS for how long data is stored in cache +*/ +const sessionCache: Record = {}; + +export function getDataFromClientSessionCache(cacheKey: string) { + const cacheObject = sessionCache[cacheKey]; + + if (!cacheObject) { + console.log(`getDataFromClientSessionCache: ${cacheKey} not found`); + return false; + } else { + // did it expire? is so, delete it from the cache + if (Date.now() - cacheObject.addedOn > cacheObject.expireAfter) { + console.log(`getDataFromClientSessionCache: ${cacheKey} expired`); + delete sessionCache[cacheKey]; // remove it from cache as its expired + return false; + } else { + console.log(`getDataFromClientSessionCache: ${cacheKey} available`); + return cacheObject.payload; + } + } +} + +export function setDataToClientSessionCache( + cacheKey: string, + jsonData: any, + ttlInMs?: number +) { + const howManyMsToCacheFor = ttlInMs || 120000; // 120000 is 2 min default TTL + + sessionCache[cacheKey] = { + payload: jsonData, + addedOn: Date.now(), + expireAfter: howManyMsToCacheFor + }; + + console.log( + 'setDataToClientSessionCache: cached for ms ', + howManyMsToCacheFor + ); + + return true; +} diff --git a/src/config.ts b/src/config.ts index d55a468..85e8731 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,3 +1,8 @@ +export enum SolEnvChainIDEnum { + devnet = 'SD', + mainnet = 'S1' +} + export enum EnvironmentsEnum { devnet = 'devnet', testnet = 'testnet', @@ -9,6 +14,7 @@ export interface Config { networkProvider: string; } +// note that in all rpc check methods below we check if window === 'undefined' as this is need for tests to pass const devnetNetworkConfig: Config = { chainID: 'D', networkProvider: 'https://devnet-api.multiversx.com' @@ -81,6 +87,22 @@ export const imageService: { [key in EnvironmentsEnum]: string } = { testnet: '' }; +export const solCNftNfMeIdMinterService: { [key in EnvironmentsEnum]: string } = + { + devnet: + 'https://api.itheumcloud-stg.com/datadexapi/solNftUtils/mintNfMeIdDataNft', + mainnet: + 'https://api.itheumcloud.com/datadexapi/solNftUtils/mintNfMeIdDataNft', + testnet: '' + }; + +export const solCNftMiscMinterService: { [key in EnvironmentsEnum]: string } = { + devnet: + 'https://api.itheumcloud-stg.com/datadexapi/solNftUtils/mintMiscDataNft', + mainnet: 'https://api.itheumcloud.com/datadexapi/solNftUtils/mintMiscDataNft', + testnet: '' +}; + export const marshalUrls = { devnet: 'https://api.itheumcloud-stg.com/datamarshalapi/router/v1', mainnet: 'https://api.itheumcloud.com/datamarshalapi/router/v1', diff --git a/src/contract-sol.ts b/src/contract-sol.ts new file mode 100644 index 0000000..9b6aecf --- /dev/null +++ b/src/contract-sol.ts @@ -0,0 +1,19 @@ +import { EnvironmentsEnum, networkConfiguration } from './config'; +import { ErrNetworkConfig } from './errors'; + +export abstract class ContractSol { + readonly chainID: string; + readonly env: string; + + protected constructor(env: string) { + if (!(env in EnvironmentsEnum)) { + throw new ErrNetworkConfig( + `Invalid environment: ${env}, Expected: 'devnet' | 'mainnet' | 'testnet'` + ); + } + + this.env = env; + const networkConfig = networkConfiguration[env as EnvironmentsEnum]; + this.chainID = networkConfig.chainID; + } +} diff --git a/src/contract.ts b/src/contract.ts index a6c77f4..4fdad4a 100644 --- a/src/contract.ts +++ b/src/contract.ts @@ -1,10 +1,9 @@ import { AbiRegistry, - ErrContract, IAddress, - SmartContract + SmartContract, + ApiNetworkProvider } from '@multiversx/sdk-core/out'; -import { ApiNetworkProvider } from '@multiversx/sdk-network-providers/out'; import { EnvironmentsEnum, networkConfiguration } from './config'; import { ErrContractAddressNotSet, ErrNetworkConfig } from './errors'; @@ -18,7 +17,7 @@ export abstract class Contract { env: string, contractAddress: IAddress, abiFile: any, - timeout: number = 10000 + timeout: number = 20000 ) { if (!(env in EnvironmentsEnum)) { throw new ErrNetworkConfig( @@ -35,7 +34,8 @@ export abstract class Contract { this.networkProvider = new ApiNetworkProvider( networkConfig.networkProvider, { - timeout: timeout + timeout: timeout, + clientName: 'ithuemDataNftSDK' } ); this.contract = new SmartContract({ diff --git a/src/datanft.ts b/src/datanft.ts index 49696c0..40bbd04 100644 --- a/src/datanft.ts +++ b/src/datanft.ts @@ -10,7 +10,9 @@ import { numberToPaddedHex, overrideMarshalUrl, parseDataNft, - validateSpecificParamsViewData + validateSpecificParamsViewData, + getDataFromClientSessionCache, + setDataToClientSessionCache } from './common/utils'; import { Config, @@ -96,8 +98,9 @@ export class DataNft implements DataNftType { /** * Sets the network configuration for the DataNft class. * @param env 'devnet' | 'mainnet' | 'testnet' + * @param useSpecificApiEndpoint optional param to use a specific RPC API endpoint for the env, if not given, defaults to config default value. */ - static setNetworkConfig(env: string) { + static setNetworkConfig(env: string, useSpecificApiEndpoint?: string) { if (!(env in EnvironmentsEnum)) { throw new ErrNetworkConfig( `Invalid environment: ${env}, Expected: 'devnet' | 'mainnet' | 'testnet'` @@ -106,6 +109,33 @@ export class DataNft implements DataNftType { this.env = env; this.networkConfiguration = networkConfiguration[env as EnvironmentsEnum]; this.apiConfiguration = apiConfiguration[env as EnvironmentsEnum]; + + console.log( + 'SDK debug: setNetworkConfig this.apiConfiguration B4 =', + this.apiConfiguration + ); + console.log( + 'SDK debug: setNetworkConfig this.networkConfiguration B4 =', + this.networkConfiguration + ); + + if (useSpecificApiEndpoint && useSpecificApiEndpoint.trim() !== '') { + this.apiConfiguration = useSpecificApiEndpoint.trim(); + this.networkConfiguration.networkProvider = useSpecificApiEndpoint.trim(); + } + + console.log( + 'SDK debug: setNetworkConfig useSpecificApiEndpoint A8 =', + useSpecificApiEndpoint + ); + console.log( + 'SDK debug: setNetworkConfig this.apiConfiguration A8 =', + this.apiConfiguration + ); + console.log( + 'SDK debug: setNetworkConfig this.networkConfiguration A8 =', + this.networkConfiguration + ); } /** @@ -148,10 +178,12 @@ export class DataNft implements DataNftType { * Each object should have a `nonce` property representing the token nonce. * An optional `tokenIdentifier` property can be provided to specify the token identifier. * If not provided, the default token identifier based on the {@link EnvironmentsEnum} + * @param clientCacheForMS optional. You can cache the response for this amount of MS if you want. Don't pass it in if you don't want caching (needs to be "undefined") * @returns An array of {@link DataNft} objects */ static async createManyFromApi( - tokens: { nonce: number; tokenIdentifier?: string }[] + tokens: { nonce: number; tokenIdentifier?: string }[], + clientCacheForMS?: number ): Promise { this.ensureNetworkConfigSet(); const identifiers = tokens.map(({ nonce, tokenIdentifier }) => @@ -160,18 +192,48 @@ export class DataNft implements DataNftType { nonce ) ); + if (identifiers.length > MAX_ITEMS) { throw new ErrTooManyItems(); } - const response = await fetch( - `${this.apiConfiguration}/nfts?identifiers=${identifiers.join( - ',' - )}&withSupply=true&size=${identifiers.length}` - ); - checkStatus(response); + // lets not make the call if not needed + if (identifiers.length === 0) { + return []; + } - const data: NftType[] = await response.json(); + console.log('SDK debug: createManyFromApi api =', this.apiConfiguration); + + const fetchUrl = `${ + this.apiConfiguration + }/nfts?identifiers=${identifiers.join(',')}&withSupply=true&size=${ + identifiers.length + }`; + + // check if its in session cache + let useCache = typeof clientCacheForMS !== 'undefined'; + let jsonDataPayload = null; + const getFromSessionCache = useCache + ? getDataFromClientSessionCache(fetchUrl) + : false; + + if (!getFromSessionCache) { + const response = await fetch(fetchUrl); + checkStatus(response); + jsonDataPayload = await response.json(); + + if (useCache) { + setDataToClientSessionCache( + fetchUrl, + jsonDataPayload, + clientCacheForMS + ); + } + } else { + jsonDataPayload = getFromSessionCache; + } + + const data: NftType[] = jsonDataPayload; try { const dataNfts = data.map((value) => parseDataNft(value)); @@ -243,10 +305,12 @@ export class DataNft implements DataNftType { * Returns an array of `DataNft` objects owned by the address * @param address the address to query * @param collections the collection identifiers to query. If not provided, the default collection identifier based on the {@link EnvironmentsEnum} + * @param clientCacheForMS optional. You can cache the response for this amount of MS if you want. Don't pass it in if you don't want caching (needs to be "undefined") */ static async ownedByAddress( address: string, - collections?: string[] + collections?: string[], + clientCacheForMS?: number ): Promise { this.ensureNetworkConfigSet(); @@ -254,15 +318,35 @@ export class DataNft implements DataNftType { collections?.join(',') || dataNftTokenIdentifier[this.env as EnvironmentsEnum]; - const res = await fetch( - `${this.apiConfiguration}/accounts/${address}/nfts?size=10000&collections=${identifiersMap}&withSupply=true` - ); + console.log('SDK debug: ownedByAddress api =', this.apiConfiguration); - checkStatus(res); + const fetchUrl = `${this.apiConfiguration}/accounts/${address}/nfts?size=10000&collections=${identifiersMap}&withSupply=true`; - const data = await res.json(); + // check if its in session cache + let useCache = typeof clientCacheForMS !== 'undefined'; + let jsonDataPayload = null; + const getFromSessionCache = useCache + ? getDataFromClientSessionCache(fetchUrl) + : false; + + if (!getFromSessionCache) { + const response = await fetch(fetchUrl); + checkStatus(response); + jsonDataPayload = await response.json(); + + if (useCache) { + setDataToClientSessionCache( + fetchUrl, + jsonDataPayload, + clientCacheForMS + ); + } + } else { + jsonDataPayload = getFromSessionCache; + } - const dataNfts: DataNft[] = this.createFromApiResponseOrBulk(data); + const dataNfts: DataNft[] = + this.createFromApiResponseOrBulk(jsonDataPayload); return dataNfts; } @@ -508,6 +592,7 @@ export class DataNft implements DataNftType { stream?: boolean; nestedIdxToStream?: number; asDeputyOnAppointerAddr?: string; + cacheDurationSeconds?: number; }): Promise { try { // S: run any format specific validation @@ -575,7 +660,9 @@ export class DataNft implements DataNftType { this.nonce )}&chainId=${chainId}&mvxNativeAuthEnable=1&mvxNativeAuthMaxExpirySeconds=${ p.mvxNativeAuthMaxExpirySeconds - }&mvxNativeAuthOrigins=${mvxNativeAuthOriginsToBase64}`; + }&mvxNativeAuthOrigins=${mvxNativeAuthOriginsToBase64}&cacheDurationSeconds=${ + p.cacheDurationSeconds || 0 + }`; type FetchConfig = { [key: string]: any; diff --git a/src/errors.ts b/src/errors.ts index 9e3642b..a6a63c5 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -20,6 +20,7 @@ export class ErrArgumentNotSet extends Error { super(`Argument "${argument}" is not set. ${message}`); } } + export class ErrInvalidArgument extends Error { public constructor(message: string) { super(`Invalid argument: ${message}`); diff --git a/src/index.ts b/src/index.ts index eb81c43..e821819 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,9 +3,12 @@ export * from './datanft'; export * from './interfaces'; export * from './marketplace'; export * from './minter'; +export * from './minter-sol'; export * from './nft-minter'; export * from './sft-minter'; +export * from './cnft-sol-minter'; export * from './bond'; export * from './contract'; +export * from './contract-sol'; export * from './liveliness-stake'; export { parseTokenIdentifier, createTokenIdentifier } from './common/utils'; diff --git a/src/interfaces.ts b/src/interfaces.ts index 7d41348..c4e75b4 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -86,6 +86,7 @@ export enum NftEnumType { SemiFungibleESDT = 'SemiFungibleESDT', MetaESDT = 'MetaESDT' } + export interface MarketplaceRequirements { acceptedTokens: string[]; acceptedPayments: string[]; @@ -234,3 +235,13 @@ export interface UserData { accumulatedRewardsBypass: BigNumber.Value; vaultNonce: number; } + +export type CNftSolPostMintMetaType = { + error?: boolean; + errMsg?: string; + assetId?: string; + leafSchema?: any; + index?: number; + root?: any; + proof?: any; +}; diff --git a/src/liveliness-stake.ts b/src/liveliness-stake.ts index 395c464..56fb9e7 100644 --- a/src/liveliness-stake.ts +++ b/src/liveliness-stake.ts @@ -33,7 +33,7 @@ import BigNumber from 'bignumber.js'; import { Token } from 'nft.storage'; export class LivelinessStake extends Contract { - constructor(env: string, timeout: number = 10000) { + constructor(env: string, timeout: number = 20000) { super( env, new Address(livelinessStakeContractAddress[env as EnvironmentsEnum]), diff --git a/src/marketplace.ts b/src/marketplace.ts index bf6b90e..fb9d627 100644 --- a/src/marketplace.ts +++ b/src/marketplace.ts @@ -14,9 +14,9 @@ import { Transaction, U64Value, U8Value, - VariadicValue + VariadicValue, + ApiNetworkProvider } from '@multiversx/sdk-core/out'; -import { ApiNetworkProvider } from '@multiversx/sdk-network-providers/out'; import dataMarketAbi from './abis/data_market.abi.json'; import { parseOffer } from './common/utils'; import { @@ -39,9 +39,9 @@ export class DataNftMarket { /** * Creates a new instance of the DataNftMarket which can be used to interact with the marketplace smart contract * @param env 'devnet' | 'mainnet' | 'testnet' - * @param timeout Timeout for the network provider (DEFAULT = 10000ms) + * @param timeout Timeout for the network provider (DEFAULT = 20000ms) */ - constructor(env: string, timeout: number = 10000) { + constructor(env: string, timeout: number = 20000) { if (!(env in EnvironmentsEnum)) { throw new ErrNetworkConfig( `Invalid environment: ${env}, Expected: 'devnet' | 'mainnet' | 'testnet'` @@ -53,7 +53,8 @@ export class DataNftMarket { this.networkProvider = new ApiNetworkProvider( networkConfig.networkProvider, { - timeout: timeout + timeout: timeout, + clientName: 'ithuemDataNftSDK' } ); const contractAddress = marketPlaceContractAddress[env as EnvironmentsEnum]; diff --git a/src/minter-sol.ts b/src/minter-sol.ts new file mode 100644 index 0000000..311f513 --- /dev/null +++ b/src/minter-sol.ts @@ -0,0 +1,22 @@ +import { + EnvironmentsEnum, + imageService, + solCNftNfMeIdMinterService, + solCNftMiscMinterService +} from './config'; +import { ContractSol } from './contract-sol'; + +export abstract class MinterSol extends ContractSol { + readonly imageServiceUrl: string; + readonly solCNftNfMeIdMinterServiceUrl: string; + readonly solCNftMiscMinterServiceUrl: string; + + protected constructor(env: string) { + super(env); + this.imageServiceUrl = imageService[env as EnvironmentsEnum]; + this.solCNftNfMeIdMinterServiceUrl = + solCNftNfMeIdMinterService[env as EnvironmentsEnum]; + this.solCNftMiscMinterServiceUrl = + solCNftMiscMinterService[env as EnvironmentsEnum]; + } +} diff --git a/src/minter.ts b/src/minter.ts index f3757bf..e82bb39 100644 --- a/src/minter.ts +++ b/src/minter.ts @@ -12,9 +12,9 @@ import { StringValue, TokenIdentifierValue, Transaction, - U64Value + U64Value, + ApiNetworkProvider } from '@multiversx/sdk-core/out'; -import { ApiNetworkProvider } from '@multiversx/sdk-network-providers/out'; import { EnvironmentsEnum, dataNftTokenIdentifier, @@ -32,7 +32,7 @@ export abstract class Minter extends Contract { env: string, contractAddress: IAddress, abiFile: any, - timeout: number = 10000 + timeout: number = 20000 ) { super(env, contractAddress, abiFile, timeout); this.imageServiceUrl = imageService[env as EnvironmentsEnum]; diff --git a/src/nft-minter.ts b/src/nft-minter.ts index 14f2db8..926c95e 100644 --- a/src/nft-minter.ts +++ b/src/nft-minter.ts @@ -29,9 +29,9 @@ export class NftMinter extends Minter { * Creates a new instance of the `NftMinter` class, which is used to interact with the factory generated smart contract. * @param env 'devnet' | 'mainnet' | 'testnet' * @param contractAddress The address of the factory generated smart contract - * @param timeout Timeout for the network provider (DEFAULT = 10000ms) + * @param timeout Timeout for the network provider (DEFAULT = 20000ms) */ - constructor(env: string, contractAddress: IAddress, timeout: number = 10000) { + constructor(env: string, contractAddress: IAddress, timeout: number = 20000) { super(env, contractAddress, dataNftLeaseAbi, timeout); } diff --git a/src/sft-minter.ts b/src/sft-minter.ts index 3d5d908..1eb2d35 100644 --- a/src/sft-minter.ts +++ b/src/sft-minter.ts @@ -37,9 +37,9 @@ export class SftMinter extends Minter { /** * Creates a new instance of the `SftMinter` class, which can be used to interact with the Data NFT-FT minter smart contract * @param env 'devnet' | 'mainnet' | 'testnet' - * @param timeout Timeout for the network provider (DEFAULT = 10000ms) + * @param timeout Timeout for the network provider (DEFAULT = 20000ms) */ - constructor(env: string, timeout: number = 10000) { + constructor(env: string, timeout: number = 20000) { super( env, new Address(minterContractAddress[env as EnvironmentsEnum]), diff --git a/tests/environment.test.ts b/tests/environment.test.ts index 0b431c0..40de54b 100644 --- a/tests/environment.test.ts +++ b/tests/environment.test.ts @@ -1,4 +1,4 @@ -import { ApiNetworkProvider } from '@multiversx/sdk-network-providers/out'; +import { ApiNetworkProvider } from '@multiversx/sdk-core/out'; import { DataNftMarket, SftMinter } from '../src/index'; describe('testing environment market', () => { @@ -8,7 +8,8 @@ describe('testing environment market', () => { expect(datanft.chainID).toStrictEqual('D'); expect(datanft.networkProvider).toStrictEqual( new ApiNetworkProvider('https://devnet-api.multiversx.com', { - timeout: 10000 + timeout: 20000, + clientName: 'ithuemDataNftSDK' }) ); }); @@ -19,29 +20,32 @@ describe('testing environment market', () => { expect(datanft.chainID).toStrictEqual('1'); expect(datanft.networkProvider).toStrictEqual( new ApiNetworkProvider('https://api.multiversx.com', { - timeout: 10000 + timeout: 20000, + clientName: 'ithuemDataNftSDK' }) ); }); test('#devnet-custom-timeout', async () => { - const datanft = new DataNftMarket('devnet', 5000); + const datanft = new DataNftMarket('devnet', 20000); expect(datanft.chainID).toStrictEqual('D'); expect(datanft.networkProvider).toStrictEqual( new ApiNetworkProvider('https://devnet-api.multiversx.com', { - timeout: 5000 + timeout: 20000, + clientName: 'ithuemDataNftSDK' }) ); }); test('#mainnet-custom-timeout', async () => { - const datanft = new DataNftMarket('mainnet', 5000); + const datanft = new DataNftMarket('mainnet', 20000); expect(datanft.chainID).toStrictEqual('1'); expect(datanft.networkProvider).toStrictEqual( new ApiNetworkProvider('https://api.multiversx.com', { - timeout: 5000 + timeout: 20000, + clientName: 'ithuemDataNftSDK' }) ); }); @@ -54,7 +58,8 @@ describe('testing environment minter', () => { expect(datanft.chainID).toStrictEqual('D'); expect(datanft.networkProvider).toStrictEqual( new ApiNetworkProvider('https://devnet-api.multiversx.com', { - timeout: 10000 + timeout: 20000, + clientName: 'ithuemDataNftSDK' }) ); }); @@ -65,29 +70,32 @@ describe('testing environment minter', () => { expect(datanft.chainID).toStrictEqual('1'); expect(datanft.networkProvider).toStrictEqual( new ApiNetworkProvider('https://api.multiversx.com', { - timeout: 10000 + timeout: 20000, + clientName: 'ithuemDataNftSDK' }) ); }); test('#devnet-custom-timeout', async () => { - const datanft = new SftMinter('devnet', 5000); + const datanft = new SftMinter('devnet', 20000); expect(datanft.chainID).toStrictEqual('D'); expect(datanft.networkProvider).toStrictEqual( new ApiNetworkProvider('https://devnet-api.multiversx.com', { - timeout: 5000 + timeout: 20000, + clientName: 'ithuemDataNftSDK' }) ); }); test('#mainnet-custom-timeout', async () => { - const datanft = new SftMinter('mainnet', 5000); + const datanft = new SftMinter('mainnet', 20000); expect(datanft.chainID).toStrictEqual('1'); expect(datanft.networkProvider).toStrictEqual( new ApiNetworkProvider('https://api.multiversx.com', { - timeout: 5000 + timeout: 20000, + clientName: 'ithuemDataNftSDK' }) ); });