diff --git a/package-lock.json b/package-lock.json index e656eb7f9..0bb47c3e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -203,6 +203,7 @@ "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.55.2.tgz", "integrity": "sha512-5wDnoIfC75zJ2MSHv5SSzTlRL2z7jQMbqQ5jrzottuq2p3oBObv8pD/JpXWu8pRaimaxNr3/Bs/KZIGVXxJ7hg==", "license": "MIT", + "peer": true, "dependencies": { "@algolia/client-common": "5.55.2", "@algolia/requester-browser-xhr": "5.55.2", @@ -362,6 +363,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -2305,6 +2307,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -2327,6 +2330,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -2436,6 +2440,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz", "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -2857,6 +2862,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz", "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -3798,6 +3804,7 @@ "resolved": "https://registry.npmjs.org/@docusaurus/faster/-/faster-3.10.1.tgz", "integrity": "sha512-XTZhE5C1gZ/DaYYMlSk02dwP5vhpQON5QHVz1s3892mSESAywgWanURpXEDAvt4GvGuq7s+XP8rTWHZvfaJmdQ==", "license": "MIT", + "peer": true, "dependencies": { "@docusaurus/types": "3.10.1", "@rspack/core": "^1.7.10", @@ -3928,6 +3935,7 @@ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.10.1.tgz", "integrity": "sha512-2jRVrtzjf8LClGTHQlwlwuD3wQXRx3WEoF7XUarJ8Ou+0onV+SLtejsyfY9JLpfUh9hPhXM4pbBGkyAY4Bi3HQ==", "license": "MIT", + "peer": true, "dependencies": { "@docusaurus/core": "3.10.1", "@docusaurus/logger": "3.10.1", @@ -4390,31 +4398,11 @@ "node": ">=20.0" } }, - "node_modules/@emnapi/core": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz", - "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==", - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.11.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", - "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -4485,6 +4473,7 @@ "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -4528,6 +4517,7 @@ "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", @@ -4962,7 +4952,6 @@ "os": [ "openharmony" ], - "peer": true, "engines": { "node": ">=18" } @@ -6590,6 +6579,7 @@ "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz", "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==", "license": "MIT", + "peer": true, "dependencies": { "@types/mdx": "^2.0.0" }, @@ -7342,6 +7332,7 @@ "integrity": "sha512-+TXxTm58lFwXXObFAEclwKX1p1AdixcD+M7T4NeFIQzQ4F20Vr+6oybCSqW1exNA3uHqVDDFLx7TT78seVjvkg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@mischnic/json-sourcemap": "^0.1.1", "@parcel/cache": "2.15.4", @@ -9109,7 +9100,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">=14" } @@ -10092,6 +10082,7 @@ "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -10196,6 +10187,7 @@ "integrity": "sha512-1CuKjFkPxIgGdeHVuNbkxmBxkcbdc08u0aiI43pFq6yY1tTVKmXT9hFEooyyKs/sJ3xf1GPHyEwTtk9Xl8dvQw==", "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.27" @@ -11195,8 +11187,7 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -11530,6 +11521,7 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -11576,6 +11568,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz", "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -11788,6 +11781,7 @@ "integrity": "sha512-gwh4gvvlaVDKKxyfxMG+Gnu1u9X0OQBwyGLkbwB65dIzBKnxeRiJlNFqlI3zwVhNXJIs6qV7mlFCn/BIajlVig==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.63.0", "@typescript-eslint/types": "8.63.0", @@ -12263,40 +12257,6 @@ "node": ">=14.0.0" } }, - "node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@unrs/resolver-binding-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", @@ -12716,6 +12676,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -12792,6 +12753,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -12856,6 +12818,7 @@ "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.55.2.tgz", "integrity": "sha512-OyacJsaeuLUvGWOynNqYc6sx88XvyoG39wMT8SYqL3l9wwaorDW/LPRbUPfhzw0bWsUWzNCZTnFYOrWFBKsUaw==", "license": "MIT", + "peer": true, "dependencies": { "@algolia/abtesting": "1.21.2", "@algolia/client-abtesting": "5.55.2", @@ -14062,6 +14025,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.42", "caniuse-lite": "^1.0.30001800", @@ -14370,6 +14334,7 @@ "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", @@ -15522,6 +15487,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz", "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -16294,7 +16260,8 @@ "version": "0.0.1608973", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1608973.tgz", "integrity": "sha512-Tpm17fxYzt+J7VrGdc1k8YdRqS3YV7se/M6KeemEqvUbq/n7At1rWVuXMxQgpWkdwSdIEKYbU//Bve+Shm4YNQ==", - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "peer": true }, "node_modules/didyoumean": { "version": "1.2.2", @@ -16403,8 +16370,7 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/dom-converter": { "version": "0.2.0", @@ -17060,6 +17026,7 @@ "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", "hasInstallScript": true, "license": "MIT", + "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -17169,6 +17136,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.29.0.tgz", "integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==", "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -17290,6 +17258,7 @@ "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==", "devOptional": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -17412,6 +17381,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -21788,6 +21758,7 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "license": "MIT", + "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -22598,7 +22569,6 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -27179,7 +27149,6 @@ "resolved": "https://registry.npmjs.org/pg-cursor/-/pg-cursor-2.21.0.tgz", "integrity": "sha512-IYvk/j+Suhtbo/C3uOf4JLsLK/gWxOTUOmYbDsbKnLaVJDq+KwhwK6ngpRfiCk8eDMS3AmGQABZCv0cREEzHQw==", "license": "MIT", - "peer": true, "peerDependencies": { "pg": "^8" } @@ -27504,6 +27473,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", @@ -28520,6 +28490,7 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.4.tgz", "integrity": "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==", "license": "MIT", + "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -29114,6 +29085,7 @@ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.4.tgz", "integrity": "sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==", "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -29152,7 +29124,6 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -29168,7 +29139,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -29181,8 +29151,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/pretty-time": { "version": "1.1.0", @@ -29656,6 +29625,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -29732,6 +29702,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -29797,6 +29768,7 @@ "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/react": "*" }, @@ -29835,6 +29807,7 @@ "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.12.13", "history": "^4.9.0", @@ -30760,6 +30733,7 @@ "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.9" }, @@ -31080,6 +31054,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -33165,6 +33140,7 @@ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -33476,7 +33452,8 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/tsutils": { "version": "3.21.0", @@ -33533,7 +33510,6 @@ "os": [ "aix" ], - "peer": true, "engines": { "node": ">=18" } @@ -33551,7 +33527,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -33569,7 +33544,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -33587,7 +33561,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -33605,7 +33578,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=18" } @@ -33623,7 +33595,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=18" } @@ -33641,7 +33612,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -33659,7 +33629,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -33677,7 +33646,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -33695,7 +33663,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -33713,7 +33680,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -33731,7 +33697,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -33749,7 +33714,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -33767,7 +33731,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -33785,7 +33748,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -33803,7 +33765,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -33821,7 +33782,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -33839,7 +33799,6 @@ "os": [ "netbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -33857,7 +33816,6 @@ "os": [ "netbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -33875,7 +33833,6 @@ "os": [ "openbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -33893,7 +33850,6 @@ "os": [ "openbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -33911,7 +33867,6 @@ "os": [ "sunos" ], - "peer": true, "engines": { "node": ">=18" } @@ -33929,7 +33884,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -33947,7 +33901,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -33965,7 +33918,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -34201,6 +34153,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -34798,6 +34751,7 @@ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -35482,6 +35436,7 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.108.4.tgz", "integrity": "sha512-yur8LyJoeiWh47dErD+Ok7vlbmDsJ3UbbRPAoxbGJ54WpE2y5yVo5G/inUzujnYgw3tPmBRdn+G7PoxXaYC33w==", "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "^1.0.8", "@types/json-schema": "^7.0.15", @@ -35584,6 +35539,7 @@ "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@discoveryjs/json-ext": "^0.5.0", "@webpack-cli/configtest": "^1.2.0", @@ -36621,6 +36577,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } @@ -36650,11 +36607,11 @@ }, "packages/chat": { "name": "@bitcoin-computer/chat", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { - "@bitcoin-computer/components": "^0.27.0-beta.0", - "@bitcoin-computer/lib": "^0.27.0-beta.0", + "@bitcoin-computer/components": "^0.27.0-beta.1", + "@bitcoin-computer/lib": "^0.27.0-beta.1", "flowbite": "^2.3.0", "react": "^18.3.1", "react-dom": "^18.3.1" @@ -36862,13 +36819,13 @@ }, "packages/chess-app": { "name": "@bitcoin-computer/chess-app", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { - "@bitcoin-computer/chess-contracts": "^0.27.0-beta.0", - "@bitcoin-computer/components": "^0.27.0-beta.0", - "@bitcoin-computer/lib": "^0.27.0-beta.0", - "@bitcoin-computer/TBC777": "^0.27.0-beta.0", + "@bitcoin-computer/chess-contracts": "^0.27.0-beta.1", + "@bitcoin-computer/components": "^0.27.0-beta.1", + "@bitcoin-computer/lib": "^0.27.0-beta.1", + "@bitcoin-computer/TBC777": "^0.27.0-beta.1", "@esbuild-plugins/node-globals-polyfill": "^0.2.3", "buffer": "^6.0.3", "crypto-browserify": "^3.12.0", @@ -36917,11 +36874,11 @@ }, "packages/chess-contracts": { "name": "@bitcoin-computer/chess-contracts", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { - "@bitcoin-computer/lib": "^0.27.0-beta.0", - "@bitcoin-computer/secp256k1": "^0.27.0-beta.0", + "@bitcoin-computer/lib": "^0.27.0-beta.1", + "@bitcoin-computer/secp256k1": "^0.27.0-beta.1", "assert": "^2.0.0", "bip32": "^4.0.0", "buffer": "^6.0.3", @@ -36941,7 +36898,7 @@ "util": "^0.12.4" }, "devDependencies": { - "@bitcoin-computer/TBC777": "^0.27.0-beta.0", + "@bitcoin-computer/TBC777": "^0.27.0-beta.1", "@types/mocha": "^10.0.6", "@types/node": "~20.11.21", "chai": "^5.1.2", @@ -36997,10 +36954,10 @@ }, "packages/components": { "name": "@bitcoin-computer/components", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { - "@bitcoin-computer/lib": "^0.27.0-beta.0", + "@bitcoin-computer/lib": "^0.27.0-beta.1", "flowbite": "^2.3.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -37016,11 +36973,12 @@ }, "packages/dao-contracts": { "name": "@bitcoin-computer/dao-contracts", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { - "@bitcoin-computer/lib": "^0.27.0-beta.0", - "@bitcoin-computer/secp256k1": "^0.27.0-beta.0", + "@bitcoin-computer/lib": "^0.27.0-beta.1", + "@bitcoin-computer/secp256k1": "^0.27.0-beta.1", + "@bitcoin-computer/TBC20": "^0.27.0-beta.1", "assert": "^2.0.0", "buffer": "^6.0.3", "crypto-browserify": "^3.12.0", @@ -37090,7 +37048,7 @@ } }, "packages/docs-2": { - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "dependencies": { "@docusaurus/core": "3.10.1", "@docusaurus/faster": "3.10.1", @@ -37125,6 +37083,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -37161,11 +37120,11 @@ }, "packages/explorer": { "name": "@bitcoin-computer/explorer", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { - "@bitcoin-computer/components": "^0.27.0-beta.0", - "@bitcoin-computer/lib": "^0.27.0-beta.0", + "@bitcoin-computer/components": "^0.27.0-beta.1", + "@bitcoin-computer/lib": "^0.27.0-beta.1", "flowbite": "^2.3.0", "react": "^18.3.1", "react-dom": "^18.3.1" @@ -37195,12 +37154,12 @@ }, "packages/lib": { "name": "@bitcoin-computer/lib", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { "@babel/parser": "^7.23.9", - "@bitcoin-computer/nakamotojs": "^0.27.0-beta.0", - "@bitcoin-computer/secp256k1": "^0.27.0-beta.0", + "@bitcoin-computer/nakamotojs": "^0.27.0-beta.1", + "@bitcoin-computer/secp256k1": "^0.27.0-beta.1", "@endo/static-module-record": "^1.1.2", "axios": "1.16.1", "bip32": "^4.0.0", @@ -37233,7 +37192,7 @@ }, "packages/nakamotojs": { "name": "@bitcoin-computer/nakamotojs", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { "@noble/hashes": "^1.2.0", @@ -37245,7 +37204,7 @@ "varuint-bitcoin": "^1.1.2" }, "devDependencies": { - "@bitcoin-computer/secp256k1": "^0.27.0-beta.0", + "@bitcoin-computer/secp256k1": "^0.27.0-beta.1", "@types/bs58": "^4.0.0", "@types/bs58check": "^2.1.0", "@types/mocha": "^10.0.6", @@ -37300,9 +37259,9 @@ }, "packages/nextjs-template": { "name": "@bitcoin-computer/nextjs-template", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "dependencies": { - "@bitcoin-computer/lib": "^0.27.0-beta.0", + "@bitcoin-computer/lib": "^0.27.0-beta.1", "dotenv": "^16.5.0", "flowbite": "^2.3.0", "next": "^16.0.10", @@ -37356,6 +37315,7 @@ "version": "19.2.15", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -37460,6 +37420,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -37502,12 +37463,12 @@ }, "packages/nft": { "name": "@bitcoin-computer/nft", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "dependencies": { - "@bitcoin-computer/components": "^0.27.0-beta.0", - "@bitcoin-computer/lib": "^0.27.0-beta.0", - "@bitcoin-computer/swap": "^0.27.0-beta.0", - "@bitcoin-computer/TBC721": "^0.27.0-beta.0", + "@bitcoin-computer/components": "^0.27.0-beta.1", + "@bitcoin-computer/lib": "^0.27.0-beta.1", + "@bitcoin-computer/swap": "^0.27.0-beta.1", + "@bitcoin-computer/TBC721": "^0.27.0-beta.1", "flowbite": "^2.3.0", "react": "^18.3.1", "react-dom": "^18.3.1" @@ -37537,13 +37498,13 @@ }, "packages/node": { "name": "@bitcoin-computer/node", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { "@babel/core": "^7.23.9", - "@bitcoin-computer/lib": "^0.27.0-beta.0", - "@bitcoin-computer/nakamotojs": "^0.27.0-beta.0", - "@bitcoin-computer/secp256k1": "^0.27.0-beta.0", + "@bitcoin-computer/lib": "^0.27.0-beta.1", + "@bitcoin-computer/nakamotojs": "^0.27.0-beta.1", + "@bitcoin-computer/secp256k1": "^0.27.0-beta.1", "@types/node": "^20.11.20", "axios": "1.16.1", "bitcoind-rpc": "^0.9.1", @@ -37579,10 +37540,10 @@ }, "packages/nodejs-template": { "name": "@bitcoin-computer/nodejs-template", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { - "@bitcoin-computer/lib": "^0.27.0-beta.0" + "@bitcoin-computer/lib": "^0.27.0-beta.1" }, "devDependencies": { "@types/mocha": "^10.0.6", @@ -37626,7 +37587,7 @@ }, "packages/secp256k1": { "name": "@bitcoin-computer/secp256k1", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { "uint8array-tools": "0.0.7" @@ -37669,13 +37630,13 @@ }, "packages/swap": { "name": "@bitcoin-computer/swap", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { - "@bitcoin-computer/lib": "^0.27.0-beta.0", - "@bitcoin-computer/nakamotojs": "^0.27.0-beta.0", - "@bitcoin-computer/TBC20": "^0.27.0-beta.0", - "@bitcoin-computer/TBC721": "^0.27.0-beta.0" + "@bitcoin-computer/lib": "^0.27.0-beta.1", + "@bitcoin-computer/nakamotojs": "^0.27.0-beta.1", + "@bitcoin-computer/TBC20": "^0.27.0-beta.1", + "@bitcoin-computer/TBC721": "^0.27.0-beta.1" }, "devDependencies": { "@types/mocha": "^10.0.6", @@ -37708,10 +37669,10 @@ }, "packages/TBC20": { "name": "@bitcoin-computer/TBC20", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { - "@bitcoin-computer/lib": "^0.27.0-beta.0", + "@bitcoin-computer/lib": "^0.27.0-beta.1", "eslint-plugin-prettier": "^5.1.3" }, "devDependencies": { @@ -37756,10 +37717,10 @@ }, "packages/TBC721": { "name": "@bitcoin-computer/TBC721", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { - "@bitcoin-computer/lib": "^0.27.0-beta.0", + "@bitcoin-computer/lib": "^0.27.0-beta.1", "chai-match-pattern": "^1.3.0", "eslint-plugin-prettier": "^5.1.3" }, @@ -37805,10 +37766,11 @@ }, "packages/TBC777": { "name": "@bitcoin-computer/TBC777", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { - "@bitcoin-computer/lib": "^0.27.0-beta.0", + "@bitcoin-computer/lib": "^0.27.0-beta.1", + "@bitcoin-computer/TBC20": "^0.27.0-beta.1", "eslint-plugin-prettier": "^5.1.3" }, "devDependencies": { @@ -37854,11 +37816,11 @@ }, "packages/vite-template": { "name": "@bitcoin-computer/vite-template", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { - "@bitcoin-computer/components": "^0.27.0-beta.0", - "@bitcoin-computer/lib": "^0.27.0-beta.0", + "@bitcoin-computer/components": "^0.27.0-beta.1", + "@bitcoin-computer/lib": "^0.27.0-beta.1", "dotenv": "^16.4.5", "flowbite": "^2.3.0", "puppeteer": "^24.1.1", @@ -37891,11 +37853,11 @@ }, "packages/wallet": { "name": "@bitcoin-computer/wallet", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { - "@bitcoin-computer/components": "^0.27.0-beta.0", - "@bitcoin-computer/lib": "^0.27.0-beta.0", + "@bitcoin-computer/components": "^0.27.0-beta.1", + "@bitcoin-computer/lib": "^0.27.0-beta.1", "flowbite": "^2.3.0", "react": "^18.3.1", "react-dom": "^18.3.1" @@ -37925,7 +37887,7 @@ }, "packages/website": { "name": "@bitcoin-computer/website", - "version": "0.27.0-beta.0", + "version": "0.27.0-beta.1", "license": "MIT", "dependencies": { "@emotion/react": "^11.14.0", diff --git a/packages/TBC20/build/src/token.d.ts b/packages/TBC20/build/src/token.d.ts index 7d7a60be2..d0635ceee 100644 --- a/packages/TBC20/build/src/token.d.ts +++ b/packages/TBC20/build/src/token.d.ts @@ -1,23 +1,25 @@ import { Computer, Contract } from '@bitcoin-computer/lib'; -export type TokenConstructorParams = { +export type TBC20ConstructorParams = { to: string; amount: bigint; name: string; symbol?: string; [s: string]: unknown; }; -export declare class Token extends Contract { +export type TokenConstructorParams = TBC20ConstructorParams; +export declare class TBC20 extends Contract { amount: bigint; name: string; symbol: string; _owners: string[]; get root(): string; - constructor(params: TokenConstructorParams); + constructor(params: TBC20ConstructorParams); transfer(to: string, amount?: bigint): this | undefined; protected _createTransferToken(to: string, amount: bigint): this; burn(): void; - merge(tokens: Token[]): void; + merge(tokens: TBC20[]): void; } +export { TBC20 as Token }; export interface ITBC20 { deploy(): Promise; mint(publicKey: string, amount: bigint, name: string, symbol: string): Promise; @@ -25,7 +27,7 @@ export interface ITBC20 { balanceOf(publicKey: string, root: string): Promise; transfer(to: string, amount: bigint, root: string): Promise; } -export declare class TokenHelper implements ITBC20 { +export declare class TBC20Helper implements ITBC20 { name: string; symbol: string; computer: Computer; @@ -38,3 +40,4 @@ export declare class TokenHelper implements ITBC20 { balanceOf(publicKey: string, root: string): Promise; transfer(to: string, amount: bigint, root: string): Promise; } +export { TBC20Helper as TokenHelper }; diff --git a/packages/TBC20/build/src/token.js b/packages/TBC20/build/src/token.js index 3975866a8..aa32f7f83 100644 --- a/packages/TBC20/build/src/token.js +++ b/packages/TBC20/build/src/token.js @@ -1,5 +1,5 @@ import { Contract } from '@bitcoin-computer/lib'; -export class Token extends Contract { +export class TBC20 extends Contract { get root() { return this._root; } @@ -38,17 +38,18 @@ export class Token extends Contract { this.amount += total; } } -export class TokenHelper { +export { TBC20 as Token }; +export class TBC20Helper { constructor(computer, mod) { this.computer = computer; this.mod = mod; } async deploy() { - this.mod = await this.computer.deploy(`export ${Token}`); + this.mod = await this.computer.deploy(`export ${TBC20}`); return this.mod; } async mint(publicKey, amount, name, symbol) { - const token = await this.computer.new(Token, [{ to: publicKey, amount, name, symbol }], this.mod); + const token = await this.computer.new(TBC20, [{ to: publicKey, amount, name, symbol }], this.mod); return token._root; } async totalSupply(root) { @@ -81,3 +82,4 @@ export class TokenHelper { await Promise.all(results); } } +export { TBC20Helper as TokenHelper }; diff --git a/packages/TBC20/src/token.ts b/packages/TBC20/src/token.ts index dbc6b287d..90268c011 100644 --- a/packages/TBC20/src/token.ts +++ b/packages/TBC20/src/token.ts @@ -3,7 +3,7 @@ import { Computer, SmartContract, Contract } from '@bitcoin-computer/lib' // eslint-disable-next-line type Constructor = new (...args: any[]) => T -export type TokenConstructorParams = { +export type TBC20ConstructorParams = { to: string amount: bigint name: string @@ -11,10 +11,13 @@ export type TokenConstructorParams = { [s: string]: unknown } +/** @deprecated Prefer `TBC20ConstructorParams` */ +export type TokenConstructorParams = TBC20ConstructorParams + /** * Base fungible token contract for the Bitcoin Computer (TBC20 standard). */ -export class Token extends Contract { +export class TBC20 extends Contract { amount!: bigint name!: string symbol!: string @@ -24,7 +27,7 @@ export class Token extends Contract { return this._root } - constructor(params: TokenConstructorParams) { + constructor(params: TBC20ConstructorParams) { const { to, amount, name, symbol = '', ...rest } = params super({ amount, name, symbol, ...rest, _owners: [to] }) } @@ -33,11 +36,17 @@ export class Token extends Contract { * Transfer tokens to another owner. * * If `amount` is omitted, the entire balance is transferred by reassigning - * ownership of this token in place (the same UTXO/output changes owner). + * ownership of this token in place (the same UTXO/output changes owner). This + * preserves the original TBC20 behavior and keeps the output layout fixed so + * SIGHASH_SINGLE-based swaps (e.g. the `Sale` contract) continue to work. * * For a partial transfer the value is split off into a new token instance for * the recipient. Subclasses can customize that token by overriding * `_createTransferToken`. + * + * NOTE: Subclasses that must sanitize per-instance state on transfer (e.g. + * TBC777, which strips escrow/claim history) override this method to route + * full transfers through `_createTransferToken` as well. */ transfer(to: string, amount?: bigint): this | undefined { if (typeof amount === 'undefined') { @@ -71,7 +80,7 @@ export class Token extends Contract { this.amount = 0n } - merge(tokens: Token[]) { + merge(tokens: TBC20[]) { if (tokens.some((t) => t._root !== this._root)) throw new Error('Cannot merge tokens from different lineages') let total = 0n @@ -83,6 +92,9 @@ export class Token extends Contract { } } +/** @deprecated Prefer `TBC20` */ +export { TBC20 as Token } + export interface ITBC20 { deploy(): Promise mint(publicKey: string, amount: bigint, name: string, symbol: string): Promise @@ -91,7 +103,7 @@ export interface ITBC20 { transfer(to: string, amount: bigint, root: string): Promise } -export class TokenHelper implements ITBC20 { +export class TBC20Helper implements ITBC20 { name: string symbol: string computer: Computer @@ -103,7 +115,7 @@ export class TokenHelper implements ITBC20 { } async deploy() { - this.mod = await this.computer.deploy(`export ${Token}`) + this.mod = await this.computer.deploy(`export ${TBC20}`) return this.mod } @@ -114,7 +126,7 @@ export class TokenHelper implements ITBC20 { symbol: string, ): Promise { const token = await this.computer.new( - Token, + TBC20, [{ to: publicKey, amount, name, symbol }], this.mod, ) @@ -122,16 +134,16 @@ export class TokenHelper implements ITBC20 { } async totalSupply(root: string): Promise { - const rootBag = (await this.computer.sync(root)) as SmartContract + const rootBag = (await this.computer.sync(root)) as SmartContract return rootBag.amount } - private async getBags(publicKey: string, root: string): Promise[]> { + private async getBags(publicKey: string, root: string): Promise[]> { const revs = await this.computer.getOUTXOs({ publicKey, mod: this.mod }) const bags = await Promise.all( - revs.map(async (rev: string) => this.computer.sync(rev)), + revs.map(async (rev: string) => this.computer.sync(rev)), ) - return bags.flatMap((bag: SmartContract & { root: string }) => + return bags.flatMap((bag: SmartContract & { root: string }) => bag.root === root ? [bag] : [], ) } @@ -156,3 +168,6 @@ export class TokenHelper implements ITBC20 { await Promise.all(results) } } + +/** @deprecated Prefer `TBC20Helper` */ +export { TBC20Helper as TokenHelper } diff --git a/packages/TBC20/test/token.test.ts b/packages/TBC20/test/token.test.ts index 77d116406..abeb9489d 100644 --- a/packages/TBC20/test/token.test.ts +++ b/packages/TBC20/test/token.test.ts @@ -1,7 +1,7 @@ import { expect } from 'chai' import { Computer, SmartContract } from '@bitcoin-computer/lib' import dotenv from 'dotenv' -import { Token, TokenHelper } from '../src/token.js' +import { TBC20, TBC20Helper, Token, TokenHelper } from '../src/token.js' import path from 'path' const envPaths = [ @@ -21,8 +21,10 @@ const sender = new Computer({ url, chain, network }) const receiver = new Computer({ url, chain, network }) before(async () => { - await sender.faucet(10e8) - await receiver.faucet(10e8) + const u1 = await sender.faucet(10e8) + const u2 = await receiver.faucet(10e8) + await sender.waitForIndexed(u1.txId) + await receiver.waitForIndexed(u2.txId) }) // Re-faucet before every test so each operation can start from a fresh UTXO. @@ -31,21 +33,47 @@ before(async () => { // / too-long-mempool-chain). A fresh faucet output has no unconfirmed ancestors // and breaks that chain. beforeEach(async () => { - await sender.faucet(10e8) - await receiver.faucet(10e8) + const u1 = await sender.faucet(10e8) + const u2 = await receiver.faucet(10e8) + await sender.waitForIndexed(u1.txId) + await receiver.waitForIndexed(u2.txId) }) // Returns a brand new wallet funded with several independent UTXOs, so a short // sequence of dependent transactions never re-selects an already-spent output. async function fundedComputer(times = 3, sats = 2e8): Promise { const computer = new Computer({ url, chain, network }) - for (let i = 0; i < times; i++) await computer.faucet(sats) + for (let i = 0; i < times; i++) { + const u = await computer.faucet(sats) + await computer.waitForIndexed(u.txId) + } return computer } -describe('Token', () => { +// Helper.balanceOf / getBags use the indexer; after a spend the old rev can +// linger briefly. Poll until the expected balance is visible (does not change +// what we assert — only when we assert it). +async function waitForBalance( + tokenHelper: TBC20Helper, + publicKey: string, + root: string, + expected: bigint, + tries = 40, +): Promise { + for (let i = 0; i < tries; i++) { + if ((await tokenHelper.balanceOf(publicKey, root)) === expected) return + await new Promise((r) => setTimeout(r, 250)) + } +} + +describe('TBC20', () => { + it('exports Token / TokenHelper as aliases', () => { + expect(Token).to.eq(TBC20) + expect(TokenHelper).to.eq(TBC20Helper) + }) + it('Should mint a token with the expected metadata', async () => { - const token = await sender.new(Token, [{ to: sender.getPublicKey(), amount: 3n, name: 'test' }]) + const token = await sender.new(TBC20, [{ to: sender.getPublicKey(), amount: 3n, name: 'test' }]) expect(token.amount).to.eq(3n) expect(token._owners).deep.equal([sender.getPublicKey()]) @@ -59,14 +87,14 @@ describe('Token', () => { }) it('Should mint a token with a symbol', async () => { - const token = await sender.new(Token, [ + const token = await sender.new(TBC20, [ { to: sender.getPublicKey(), amount: 5n, name: 'test', symbol: 'TST' }, ]) expect(token.symbol).to.eq('TST') }) it('partial transfer creates a new token for the recipient in the same lineage', async () => { - const token1 = await sender.new(Token, [ + const token1 = await sender.new(TBC20, [ { to: sender.getPublicKey(), amount: 3n, name: 'test' }, ]) @@ -89,16 +117,16 @@ describe('Token', () => { // Both revisions persist on chain with the expected balances. We sync the // exact revisions (rather than getOUTXOs-by-publicKey) because `sender` and // `receiver` are shared across tests and hold many token UTXOs. - const senderToken = await sender.sync(token1._rev) + const senderToken = await sender.sync(token1._rev) expect(senderToken.amount).eq(2n) - const receiverToken = await receiver.sync(token2._rev) + const receiverToken = await receiver.sync(token2._rev) expect(receiverToken.amount).eq(1n) }) it('full transfer (no amount) reassigns ownership in place, keeping the same token', async () => { const computer = await fundedComputer() - const token1 = await computer.new(Token, [ + const token1 = await computer.new(TBC20, [ { to: computer.getPublicKey(), amount: 4n, name: 'test' }, ]) const idBefore = token1._id @@ -114,13 +142,13 @@ describe('Token', () => { expect(token1._owners).deep.equal([receiver.getPublicKey()]) // The recipient can read the same token revision on chain - const receiverToken = await receiver.sync(token1._rev) + const receiverToken = await receiver.sync(token1._rev) expect(receiverToken.amount).eq(4n) expect(receiverToken._owners).deep.equal([receiver.getPublicKey()]) }) it('Should throw when transferring a non-positive amount', async () => { - const token = await sender.new(Token, [{ to: sender.getPublicKey(), amount: 3n, name: 'test' }]) + const token = await sender.new(TBC20, [{ to: sender.getPublicKey(), amount: 3n, name: 'test' }]) try { await token.transfer(receiver.getPublicKey(), 0n) expect.fail('should have thrown on non-positive transfer') @@ -130,7 +158,7 @@ describe('Token', () => { }) it('Should throw when transferring more than the balance', async () => { - const token = await sender.new(Token, [{ to: sender.getPublicKey(), amount: 3n, name: 'test' }]) + const token = await sender.new(TBC20, [{ to: sender.getPublicKey(), amount: 3n, name: 'test' }]) try { await token.transfer(receiver.getPublicKey(), 4n) expect.fail('should have thrown on insufficient funds') @@ -143,7 +171,7 @@ describe('Token', () => { const computer = await fundedComputer() // Mint then split into two same-lineage tokens owned by the same wallet - const token1 = await computer.new(Token, [ + const token1 = await computer.new(TBC20, [ { to: computer.getPublicKey(), amount: 3n, name: 'test' }, ]) @@ -159,10 +187,10 @@ describe('Token', () => { it('Should refuse to merge tokens from different lineages', async () => { const computer = await fundedComputer() - const tokenA = await computer.new(Token, [ + const tokenA = await computer.new(TBC20, [ { to: computer.getPublicKey(), amount: 3n, name: 'A' }, ]) - const tokenB = await computer.new(Token, [ + const tokenB = await computer.new(TBC20, [ { to: computer.getPublicKey(), amount: 3n, name: 'B' }, ]) expect(tokenA._root).to.not.eq(tokenB._root) @@ -176,14 +204,14 @@ describe('Token', () => { }) it('Should burn a token', async () => { - const token = await sender.new(Token, [{ to: sender.getPublicKey(), amount: 3n, name: 'test' }]) + const token = await sender.new(TBC20, [{ to: sender.getPublicKey(), amount: 3n, name: 'test' }]) await token.burn() expect(token.amount).to.eq(0n) }) it('Should update the revisions correctly', async () => { const computer = await fundedComputer() - const t1 = await computer.new(Token, [ + const t1 = await computer.new(TBC20, [ { to: computer.getPublicKey(), amount: 3n, name: 'test' }, ]) const rev1 = t1._rev @@ -198,9 +226,9 @@ describe('Token', () => { }) }) -describe('TokenHelper', () => { +describe('TBC20Helper', () => { describe('mint', () => { - const tokenHelper = new TokenHelper(sender) + const tokenHelper = new TBC20Helper(sender) let root: string it('Should create the tokenHelper object', async () => { const publicKey = tokenHelper.computer.getPublicKey() @@ -208,10 +236,11 @@ describe('TokenHelper', () => { expect(root).not.to.be.undefined expect(typeof root).to.eq('string') expect(root.length).to.be.greaterThan(64) + await sender.waitForIndexed(root) }) it('Should mint a root token', async () => { - const rootToken = (await sender.sync(root)) as SmartContract + const rootToken = (await sender.sync(root)) as SmartContract expect(rootToken).not.to.be.undefined expect(rootToken._id).to.eq(root) expect(rootToken._rev).to.eq(root) @@ -224,9 +253,10 @@ describe('TokenHelper', () => { describe('totalSupply', () => { it('Should return the supply of tokens', async () => { - const tokenHelper = new TokenHelper(sender) + const tokenHelper = new TBC20Helper(sender) const publicKey = tokenHelper.computer.getPublicKey() const root = await tokenHelper.mint(publicKey, 200n, 'test', 'TST') + await sender.waitForIndexed(root) const supply = await tokenHelper.totalSupply(root) expect(supply).to.eq(200n) }) @@ -236,7 +266,7 @@ describe('TokenHelper', () => { it('Should throw an error if the root is not set', async () => { const publicKeyString = sender.getPublicKey() - const tokenHelper = new TokenHelper(sender) + const tokenHelper = new TBC20Helper(sender) expect(tokenHelper).not.to.be.undefined try { await tokenHelper.balanceOf(publicKeyString, undefined) @@ -247,9 +277,10 @@ describe('TokenHelper', () => { }) it('Should compute the balance', async () => { - const tokenHelper = new TokenHelper(sender) + const tokenHelper = new TBC20Helper(sender) const publicKey = tokenHelper.computer.getPublicKey() const root = await tokenHelper.mint(publicKey, 200n, 'test', 'TST') + await sender.waitForIndexed(root) const res = await tokenHelper.balanceOf(publicKey, root) expect(res).to.eq(200n) @@ -259,12 +290,14 @@ describe('TokenHelper', () => { describe('transfer', () => { it('Should transfer a token', async () => { const computer2 = new Computer({ url, chain, network }) - const tokenHelper = new TokenHelper(sender) + const tokenHelper = new TBC20Helper(sender) const publicKey = tokenHelper.computer.getPublicKey() const root = await tokenHelper.mint(publicKey, 200n, 'test', 'TST') + await sender.waitForIndexed(root) await tokenHelper.transfer(computer2.getPublicKey(), 20n, root) + await waitForBalance(tokenHelper, publicKey, root, 180n) const res = await tokenHelper.balanceOf(publicKey, root) expect(res).to.eq(180n) }) @@ -272,31 +305,39 @@ describe('TokenHelper', () => { it('Should transfer random amounts to different people', async () => { const computer2 = new Computer({ url, chain, network }) const computer3 = new Computer({ url, chain, network }) - const tokenHelper = new TokenHelper(sender) + const tokenHelper = new TBC20Helper(sender) const publicKey = tokenHelper.computer.getPublicKey() const root = await tokenHelper.mint(publicKey, 200n, 'multiple', 'MULT') - const amount2 = BigInt(Math.floor(Math.random() * 100)) - const amount3 = BigInt(Math.floor(Math.random() * 100)) + await sender.waitForIndexed(root) + // Keep amounts positive and within remaining balance (original allowed 0, + // which fails transfer validation; still asserts the same balance math). + const amount2 = BigInt(1 + Math.floor(Math.random() * 99)) + const amount3 = BigInt(1 + Math.floor(Math.random() * Number(200n - amount2))) await tokenHelper.transfer(computer2.getPublicKey(), amount2, root) await tokenHelper.transfer(computer3.getPublicKey(), amount3, root) + const expected = 200n - amount2 - amount3 + await waitForBalance(tokenHelper, publicKey, root, expected) const res = await tokenHelper.balanceOf(publicKey, root) - expect(res).to.eq(200n - amount2 - amount3) + expect(res).to.eq(expected) + await waitForBalance(tokenHelper, computer2.getPublicKey(), root, amount2) const res2 = await tokenHelper.balanceOf(computer2.getPublicKey(), root) expect(res2).to.eq(amount2) + await waitForBalance(tokenHelper, computer3.getPublicKey(), root, amount3) const res3 = await tokenHelper.balanceOf(computer3.getPublicKey(), root) expect(res3).to.eq(amount3) }) it('Should fail if the amount is greater than the balance', async () => { const computer2 = new Computer({ url, chain, network }) - const tokenHelper = new TokenHelper(sender) + const tokenHelper = new TBC20Helper(sender) const publicKey = tokenHelper.computer.getPublicKey() const root = await tokenHelper.mint(publicKey, 200n, 'test', 'TST') + await sender.waitForIndexed(root) try { await tokenHelper.transfer(computer2.getPublicKey(), 201n, root) diff --git a/packages/TBC777/build/src/index.d.ts b/packages/TBC777/build/src/index.d.ts index a10a44bb7..2bebcd73b 100644 --- a/packages/TBC777/build/src/index.d.ts +++ b/packages/TBC777/build/src/index.d.ts @@ -1,3 +1,3 @@ -export * from './tbc20.js'; +export { TBC20, TBC20Helper, type TBC20ConstructorParams, type ITBC20 } from '@bitcoin-computer/TBC20'; export * from './tbc777.js'; export { TBC777M } from './tbc777m.js'; diff --git a/packages/TBC777/build/src/index.js b/packages/TBC777/build/src/index.js index a10a44bb7..9e49eb09b 100644 --- a/packages/TBC777/build/src/index.js +++ b/packages/TBC777/build/src/index.js @@ -1,3 +1,3 @@ -export * from './tbc20.js'; +export { TBC20, TBC20Helper } from '@bitcoin-computer/TBC20'; export * from './tbc777.js'; export { TBC777M } from './tbc777m.js'; diff --git a/packages/TBC777/build/src/tbc20.d.ts b/packages/TBC777/build/src/tbc20.d.ts deleted file mode 100644 index af0177b14..000000000 --- a/packages/TBC777/build/src/tbc20.d.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Contract } from '@bitcoin-computer/lib'; -export type TBC20ConstructorParams = { - to: string; - amount: bigint; - name: string; - symbol?: string; - [s: string]: unknown; -}; -export declare class TBC20 extends Contract { - amount: bigint; - name: string; - symbol: string; - _owners: string[]; - get root(): string; - constructor(params: TBC20ConstructorParams); - transfer(to: string, amount?: bigint): this | undefined; - protected _createTransferToken(to: string, amount: bigint): this; - burn(): void; - merge(tokens: TBC20[]): void; -} -export interface ITBC20 { - deploy(): Promise; - mint(publicKey: string, amount: bigint, name: string, symbol: string): Promise; - totalSupply(root: string): Promise; - balanceOf(publicKey: string, root: string): Promise; - transfer(to: string, amount: bigint, root: string): Promise; -} -export declare class TBC20Helper implements ITBC20 { - name: string; - symbol: string; - computer: any; - mod: string; - constructor(computer: any, mod?: string); - deploy(): Promise; - mint(publicKey: string, amount: bigint, name: string, symbol: string): Promise; - totalSupply(root: string): Promise; - private getBags; - balanceOf(publicKey: string, root: string): Promise; - transfer(to: string, amount: bigint, root: string): Promise; -} diff --git a/packages/TBC777/build/src/tbc20.js b/packages/TBC777/build/src/tbc20.js deleted file mode 100644 index f588ba1a8..000000000 --- a/packages/TBC777/build/src/tbc20.js +++ /dev/null @@ -1,84 +0,0 @@ -import { Contract } from '@bitcoin-computer/lib'; -export class TBC20 extends Contract { - get root() { - return this._root; - } - constructor(params) { - const { to, amount, name, symbol = '', ...rest } = params; - super({ amount, name, symbol, ...rest, _owners: [to] }); - } - transfer(to, amount) { - if (typeof amount === 'undefined') { - this._owners = [to]; - return undefined; - } - if (amount <= 0n) - throw new Error('Transfer amount must be positive'); - if (this.amount < amount) - throw new Error('Insufficient funds'); - this.amount -= amount; - return this._createTransferToken(to, amount); - } - _createTransferToken(to, amount) { - const ctor = this.constructor; - const { _id, _root, _rev, _owners, ...cleanState } = this; - return new ctor({ ...cleanState, to, amount }); - } - burn() { - this.amount = 0n; - } - merge(tokens) { - if (tokens.some((t) => t._root !== this._root)) - throw new Error('Cannot merge tokens from different lineages'); - let total = 0n; - tokens.forEach((t) => { - total += t.amount; - t.burn(); - }); - this.amount += total; - } -} -export class TBC20Helper { - constructor(computer, mod) { - this.computer = computer; - this.mod = mod; - } - async deploy() { - this.mod = await this.computer.deploy(`export ${TBC20}`); - return this.mod; - } - async mint(publicKey, amount, name, symbol) { - const params = [{ to: publicKey, amount, name, symbol }]; - const token = await this.computer.new(TBC20, params, this.mod); - return token._root; - } - async totalSupply(root) { - const rootBag = await this.computer.sync(root); - return rootBag.amount; - } - async getBags(publicKey, root) { - const revs = await this.computer.getOUTXOs({ publicKey, mod: this.mod }); - const bags = await Promise.all(revs.map(async (rev) => this.computer.sync(rev))); - return bags.flatMap((bag) => (bag.root === root ? [bag] : [])); - } - async balanceOf(publicKey, root) { - if (typeof root === 'undefined') - throw new Error('Please pass a root into balanceOf.'); - const bags = await this.getBags(publicKey, root); - return bags.reduce((prev, curr) => prev + curr.amount, 0n); - } - async transfer(to, amount, root) { - const owner = this.computer.getPublicKey(); - const bags = await this.getBags(owner, root); - const results = []; - while (amount > 0 && bags.length > 0) { - const [bag] = bags.splice(0, 1); - const available = amount < bag.amount ? amount : bag.amount; - results.push(await bag.transfer(to, available)); - amount -= available; - } - if (amount > 0) - throw new Error('Could not send entire amount'); - await Promise.all(results); - } -} diff --git a/packages/TBC777/build/src/tbc777.d.ts b/packages/TBC777/build/src/tbc777.d.ts index ee3c51a5f..62deb86fe 100644 --- a/packages/TBC777/build/src/tbc777.d.ts +++ b/packages/TBC777/build/src/tbc777.d.ts @@ -1,5 +1,5 @@ import { Id, Rev, Root, Contract } from '@bitcoin-computer/lib'; -import { TBC20, TBC20ConstructorParams } from './tbc20.js'; +import { TBC20, TBC20ConstructorParams } from '@bitcoin-computer/TBC20'; export type Constructor = new (...args: any[]) => T; export type Amount = bigint; export type DepositEntry = [Root, Rev]; diff --git a/packages/TBC777/build/src/tbc777.js b/packages/TBC777/build/src/tbc777.js index d60193135..e7bbaec18 100644 --- a/packages/TBC777/build/src/tbc777.js +++ b/packages/TBC777/build/src/tbc777.js @@ -1,5 +1,5 @@ import { Contract } from '@bitcoin-computer/lib'; -import { TBC20 } from './tbc20.js'; +import { TBC20 } from '@bitcoin-computer/TBC20'; export class Escrow extends Contract { } export class EscrowAuditor { diff --git a/packages/TBC777/build/src/tbc777m.d.ts b/packages/TBC777/build/src/tbc777m.d.ts index 85bf859ad..2f2892e18 100644 --- a/packages/TBC777/build/src/tbc777m.d.ts +++ b/packages/TBC777/build/src/tbc777m.d.ts @@ -1,5 +1,5 @@ import { Contract } from '@bitcoin-computer/lib'; -import { TBC20, type TBC20ConstructorParams } from './tbc20.js'; +import { TBC20, type TBC20ConstructorParams } from '@bitcoin-computer/TBC20'; export declare abstract class Escrow extends Contract { deposits: [string, string][]; withdraws: [string, string, bigint][]; diff --git a/packages/TBC777/build/src/tbc777m.js b/packages/TBC777/build/src/tbc777m.js index 013b0307a..e802f9ecd 100644 --- a/packages/TBC777/build/src/tbc777m.js +++ b/packages/TBC777/build/src/tbc777m.js @@ -1,5 +1,5 @@ import { Contract } from '@bitcoin-computer/lib'; -import { TBC20 } from './tbc20.js'; +import { TBC20 } from '@bitcoin-computer/TBC20'; export class Escrow extends Contract { } export class TBC777M extends TBC20 { diff --git a/packages/TBC777/package.json b/packages/TBC777/package.json index 558c0629f..37f38a7fb 100644 --- a/packages/TBC777/package.json +++ b/packages/TBC777/package.json @@ -28,6 +28,7 @@ "types": "tsc --noEmit" }, "dependencies": { + "@bitcoin-computer/TBC20": "^0.27.0-beta.1", "@bitcoin-computer/lib": "^0.27.0-beta.1", "eslint-plugin-prettier": "^5.1.3" }, diff --git a/packages/TBC777/src/index.ts b/packages/TBC777/src/index.ts index 4647e6039..4e252455a 100644 --- a/packages/TBC777/src/index.ts +++ b/packages/TBC777/src/index.ts @@ -1,3 +1,3 @@ -export * from './tbc20.js' +export { TBC20, TBC20Helper, type TBC20ConstructorParams, type ITBC20 } from '@bitcoin-computer/TBC20' export * from './tbc777.js' export { TBC777M } from './tbc777m.js' diff --git a/packages/TBC777/src/tbc20.ts b/packages/TBC777/src/tbc20.ts deleted file mode 100644 index 34a6f7697..000000000 --- a/packages/TBC777/src/tbc20.ts +++ /dev/null @@ -1,155 +0,0 @@ -import { Contract } from '@bitcoin-computer/lib' -type Constructor = new (...args: any[]) => T - -export type TBC20ConstructorParams = { - to: string - amount: bigint - name: string - symbol?: string - [s: string]: unknown -} - -/** - * Base fungible token contract for the Bitcoin Computer. - */ -export class TBC20 extends Contract { - amount!: bigint - name!: string - symbol!: string - _owners!: string[] - - get root(): string { - return this._root - } - - constructor(params: TBC20ConstructorParams) { - const { to, amount, name, symbol = '', ...rest } = params - super({ amount, name, symbol, ...rest, _owners: [to] }) - } - - /** - * Transfer tokens to another owner. - * - * If `amount` is omitted, the entire balance is transferred by reassigning - * ownership of this token in place (the same UTXO/output changes owner). This - * preserves the original TBC20 behavior and keeps the output layout fixed so - * SIGHASH_SINGLE-based swaps (e.g. the `Sale` contract) continue to work. - * - * For a partial transfer the value is split off into a new token instance for - * the recipient. Subclasses can customize that token by overriding - * `_createTransferToken`. - * - * NOTE: Subclasses that must sanitize per-instance state on transfer (e.g. - * TBC777, which strips escrow/claim history) override this method to route - * full transfers through `_createTransferToken` as well. - */ - transfer(to: string, amount?: bigint): this | undefined { - if (typeof amount === 'undefined') { - this._owners = [to] - return undefined - } - - if (amount <= 0n) throw new Error('Transfer amount must be positive') - if (this.amount < amount) throw new Error('Insufficient funds') - - this.amount -= amount - return this._createTransferToken(to, amount) - } - - /** - * Factory method used by `transfer` when creating a new token for a partial - * transfer. Subclasses should override this method to control which fields - * are copied to the new token instance. - * - * Default implementation performs a shallow copy of all current state - * (preserving original behavior) while setting the new owner and amount. - */ - protected _createTransferToken(to: string, amount: bigint): this { - const ctor = this.constructor as Constructor - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { _id, _root, _rev, _owners, ...cleanState } = this - return new ctor({ ...cleanState, to, amount }) - } - - burn() { - this.amount = 0n - } - - merge(tokens: TBC20[]) { - if (tokens.some((t) => t._root !== this._root)) - throw new Error('Cannot merge tokens from different lineages') - let total = 0n - tokens.forEach((t) => { - total += t.amount - t.burn() - }) - this.amount += total - } -} - -export interface ITBC20 { - deploy(): Promise - mint(publicKey: string, amount: bigint, name: string, symbol: string): Promise - totalSupply(root: string): Promise - balanceOf(publicKey: string, root: string): Promise - transfer(to: string, amount: bigint, root: string): Promise -} - -export class TBC20Helper implements ITBC20 { - name: string - symbol: string - computer: any - mod: string - - constructor(computer: any, mod?: string) { - this.computer = computer - this.mod = mod - } - - async deploy() { - this.mod = await this.computer.deploy(`export ${TBC20}`) - return this.mod - } - - async mint( - publicKey: string, - amount: bigint, - name: string, - symbol: string, - ): Promise { - const params = [{ to: publicKey, amount, name, symbol }] - const token = await this.computer.new(TBC20, params, this.mod) - return token._root - } - - async totalSupply(root: string): Promise { - const rootBag = await this.computer.sync(root) - return rootBag.amount - } - - private async getBags(publicKey: string, root: string): Promise { - const revs = await this.computer.getOUTXOs({ publicKey, mod: this.mod }) - const bags = await Promise.all(revs.map(async (rev: string) => this.computer.sync(rev))) - return bags.flatMap((bag: TBC20) => (bag.root === root ? [bag] : [])) - } - - async balanceOf(publicKey: string, root: string): Promise { - if (typeof root === 'undefined') throw new Error('Please pass a root into balanceOf.') - const bags = await this.getBags(publicKey, root) - return bags.reduce((prev, curr) => prev + curr.amount, 0n) - } - - async transfer(to: string, amount: bigint, root: string): Promise { - const owner = this.computer.getPublicKey() - const bags = await this.getBags(owner, root) - const results = [] - while (amount > 0 && bags.length > 0) { - const [bag] = bags.splice(0, 1) - const available = amount < bag.amount ? amount : bag.amount - results.push(await bag.transfer(to, available)) - amount -= available - } - if (amount > 0) throw new Error('Could not send entire amount') - await Promise.all(results) - } -} diff --git a/packages/TBC777/src/tbc777.ts b/packages/TBC777/src/tbc777.ts index db0456232..ef6bdc83b 100644 --- a/packages/TBC777/src/tbc777.ts +++ b/packages/TBC777/src/tbc777.ts @@ -35,14 +35,14 @@ * This pattern guarantees that the minted amount exactly matches an audited * claim. * - * @see ./tbc20.ts + * @see @bitcoin-computer/TBC20 * @see https://docs.bitcoincomputer.io/ */ // TYPES & INTERFACES import { Id, Rev, Root, Contract } from '@bitcoin-computer/lib' -import { TBC20, TBC20ConstructorParams } from './tbc20.js' +import { TBC20, TBC20ConstructorParams } from '@bitcoin-computer/TBC20' export type Constructor = new (...args: any[]) => T export type Amount = bigint diff --git a/packages/TBC777/src/tbc777m.ts b/packages/TBC777/src/tbc777m.ts index 154c7fdb5..64e49de95 100644 --- a/packages/TBC777/src/tbc777m.ts +++ b/packages/TBC777/src/tbc777m.ts @@ -71,7 +71,7 @@ */ import { Contract } from '@bitcoin-computer/lib' -import { TBC20, type TBC20ConstructorParams } from './tbc20.js' +import { TBC20, type TBC20ConstructorParams } from '@bitcoin-computer/TBC20' /** * Minimal interface that any escrow contract must satisfy to be compatible with diff --git a/packages/TBC777/test/tbc777.test.ts b/packages/TBC777/test/tbc777.test.ts index d15f3dd39..25ff2d7de 100644 --- a/packages/TBC777/test/tbc777.test.ts +++ b/packages/TBC777/test/tbc777.test.ts @@ -2,7 +2,7 @@ import { expect } from 'chai' import { branded, Computer, Contract, Id, Rev, Root, SmartContract } from '@bitcoin-computer/lib' import dotenv from 'dotenv' import path from 'path' -import { TBC20 } from '../src/tbc20.js' +import { TBC20 } from '@bitcoin-computer/TBC20' import { Amount, Escrow, TBC777, EscrowAuditor } from '../src/tbc777.js' const envPaths = [path.resolve(process.cwd(), './packages/node/.env'), '../node/.env'] @@ -39,12 +39,18 @@ let white: Computer let minter: Computer let mod: string +async function faucetIndexed(c: Computer, sats: number) { + const u = await c.faucet(sats) + await c.waitForIndexed(u.txId) + return u +} + async function ensureFunds(c: Computer, minSats = 20e8) { try { const { balance } = await c.getBalance() - if (balance < minSats) await c.faucet(minSats) + if (balance < minSats) await faucetIndexed(c, minSats) } catch { - await c.faucet(minSats) + await faucetIndexed(c, minSats) } } @@ -54,7 +60,7 @@ describe('TBC777 - Programmable Escrow Token (No-Inflation Focus)', () => { black = new Computer({ url, chain, network }) white = new Computer({ url, chain, network }) - await Promise.all([black.faucet(2e8), white.faucet(2e8), minter.faucet(2e8)]) + await Promise.all([faucetIndexed(black, 2e8), faucetIndexed(white, 2e8), faucetIndexed(minter, 2e8)]) await ensureFunds(minter, 2e8) // Strip comments from all three classes before deploying @@ -77,10 +83,13 @@ describe('TBC777 - Programmable Escrow Token (No-Inflation Focus)', () => { const exp = `new TBC777({ to: '${to}', amount: ${amount}n, name: '${name}', symbol: '${symbol}' })` const { tx } = await minter.encode({ exp, mod }) await minter.broadcast(tx) + await minter.waitForIndexed(tx.getId()) }) beforeEach(async () => { - await Promise.all([black.faucet(2e8), white.faucet(2e8), minter.faucet(2e8)]) + // Fresh faucet outputs break unconfirmed descendant chains that cause + // txn-mempool-conflict / bad-txns-inputs-missingorspent under suite load. + await Promise.all([faucetIndexed(black, 2e8), faucetIndexed(white, 2e8), faucetIndexed(minter, 2e8)]) await ensureFunds(minter, 2e8) }) @@ -131,6 +140,7 @@ describe('TBC777 - Programmable Escrow Token (No-Inflation Focus)', () => { env: { escrow: escrow._rev, token: token._rev }, }) await depositor.broadcast(tx) + await depositor.waitForIndexed(tx.getId()) // Return both updated objects so callers can re-bind if needed return { @@ -152,14 +162,15 @@ describe('TBC777 - Programmable Escrow Token (No-Inflation Focus)', () => { expect(balance >= 0n).eq(true) } - /** Create a fresh token instance (via transfer for efficiency) */ /** Create a fresh token instance (mint fresh each time to avoid depleting * shared root) */ async function createFreshToken(amount = FRESH_TOKEN_AMOUNT, owner = minter.getPublicKey()) { + await faucetIndexed(minter, 2e8) const exp = `new TBC777({ to: '${owner}', amount: ${amount}n, name: '${TEST_NAME}', symbol: '${TEST_SYMBOL}' })` const { effect, tx } = await minter.encode({ exp, mod }) const fresh = branded(effect.res as SmartContract) await minter.broadcast(tx) + await minter.waitForIndexed(tx.getId()) return fresh } diff --git a/packages/TBC777/test/tbc777m.test.ts b/packages/TBC777/test/tbc777m.test.ts index 8bdf5abe0..cab20a996 100644 --- a/packages/TBC777/test/tbc777m.test.ts +++ b/packages/TBC777/test/tbc777m.test.ts @@ -2,7 +2,7 @@ import { expect } from 'chai' import { Computer, Contract, SmartContract } from '@bitcoin-computer/lib' import dotenv from 'dotenv' import path from 'path' -import { TBC20 } from '../src/tbc20.js' +import { TBC20 } from '@bitcoin-computer/TBC20' import { Escrow, TBC777M } from '../src/tbc777m.js' const envPaths = [ diff --git a/packages/components/built/Drawer.js b/packages/components/built/Drawer.js index 22f02fc8c..2df66ad0f 100644 --- a/packages/components/built/Drawer.js +++ b/packages/components/built/Drawer.js @@ -20,7 +20,7 @@ export function DrawerComponent({ Content, id, }) { } return undefined; }, []); - return (_jsxs("div", { ref: drawerRef, id: id, className: "fixed top-0 right-0 z-40 h-screen p-4 overflow-y-auto transition-transform duration-300 translate-x-full bg-white w-80 dark:bg-gray-800", tabIndex: -1, "aria-labelledby": "drawer-right-label", children: [_jsxs("button", { type: "button", "data-drawer-hide": id, "aria-controls": id, className: "text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 absolute top-2.5 end-2.5 inline-flex items-center justify-center dark:hover:bg-gray-600 dark:hover:text-white", children: [_jsx("svg", { className: "w-3 h-3", "aria-hidden": "true", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 14 14", children: _jsx("path", { stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" }) }), _jsx("span", { className: "sr-only", children: "Close menu" })] }), Content({ isOpen })] })); + return (_jsxs("div", { ref: drawerRef, id: id, className: "fixed top-0 right-0 z-40 h-screen p-4 overflow-y-auto transition-transform duration-300 translate-x-full bg-white w-full max-w-sm sm:w-80 dark:bg-gray-800", tabIndex: -1, "aria-labelledby": "drawer-right-label", children: [_jsxs("button", { type: "button", "data-drawer-hide": id, "aria-controls": id, className: "text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 absolute top-2.5 end-2.5 inline-flex items-center justify-center dark:hover:bg-gray-600 dark:hover:text-white", children: [_jsx("svg", { className: "w-3 h-3", "aria-hidden": "true", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 14 14", children: _jsx("path", { stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" }) }), _jsx("span", { className: "sr-only", children: "Close menu" })] }), Content({ isOpen })] })); } export const Drawer = { Component: DrawerComponent, diff --git a/packages/components/built/Modal.js b/packages/components/built/Modal.js index 3dab105a6..6ae6a003a 100644 --- a/packages/components/built/Modal.js +++ b/packages/components/built/Modal.js @@ -21,7 +21,7 @@ export const toggleModal = (id) => { export const ShowModalButton = ({ id, text }) => (_jsx("button", { "data-modal-target": id, "data-modal-show": id, type: "button", children: text })); export const HideModalButton = ({ id, text }) => (_jsx("button", { "data-modal-target": id, "data-modal-hide": id, type: "button", children: text })); export const ToggleModalButton = ({ id, text }) => (_jsx("button", { "data-modal-target": id, "data-modal-toggle": id, type: "button", children: text })); -export const ModalComponent = ({ title, content, contentData, id, onClickClose, hideClose, }) => (_jsx("div", { id: id, tabIndex: -1, "aria-hidden": "true", style: { zIndex: 45 }, className: "hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full", children: _jsx("div", { className: "relative p-4 w-full max-w-sm max-h-full", children: _jsxs("div", { className: "relative bg-white rounded-lg shadow dark:bg-gray-700", children: [_jsxs("div", { className: "flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600", children: [_jsx("h3", { className: "text-xl font-semibold text-gray-900 dark:text-white", children: title }), !hideClose ? (_jsxs("button", { type: "button", className: "text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white", "data-modal-hide": id, "data-modal-target": id, onClick: () => hideModal(id, onClickClose), children: [_jsx("svg", { className: "w-3 h-3", "aria-hidden": "true", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 14 14", children: _jsx("path", { stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" }) }), _jsx("span", { className: "sr-only", children: "Close modal" })] })) : (_jsx(_Fragment, {}))] }), content(contentData)] }) }) })); +export const ModalComponent = ({ title, content, contentData, id, onClickClose, hideClose, }) => (_jsx("div", { id: id, tabIndex: -1, "aria-hidden": "true", style: { zIndex: 45 }, className: "hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 justify-center items-center w-full inset-0 h-[calc(100%-1rem)] max-h-full p-2 sm:p-0", children: _jsx("div", { className: "relative p-2 sm:p-4 w-full max-w-sm max-h-full", children: _jsxs("div", { className: "relative bg-white rounded-lg shadow dark:bg-gray-700", children: [_jsxs("div", { className: "flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600", children: [_jsx("h3", { className: "text-lg sm:text-xl font-semibold text-gray-900 dark:text-white", children: title }), !hideClose ? (_jsxs("button", { type: "button", className: "text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white", "data-modal-hide": id, "data-modal-target": id, onClick: () => hideModal(id, onClickClose), children: [_jsx("svg", { className: "w-3 h-3", "aria-hidden": "true", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 14 14", children: _jsx("path", { stroke: "currentColor", strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6" }) }), _jsx("span", { className: "sr-only", children: "Close modal" })] })) : (_jsx(_Fragment, {}))] }), content(contentData)] }) }) })); export const Modal = { get: getModal, showModal, diff --git a/packages/components/src/Drawer.tsx b/packages/components/src/Drawer.tsx index a6c0b30e2..d0c01ef6d 100644 --- a/packages/components/src/Drawer.tsx +++ b/packages/components/src/Drawer.tsx @@ -44,7 +44,7 @@ export function DrawerComponent({
diff --git a/packages/components/src/Modal.tsx b/packages/components/src/Modal.tsx index 8aaadad17..06401e729 100644 --- a/packages/components/src/Modal.tsx +++ b/packages/components/src/Modal.tsx @@ -61,12 +61,12 @@ export const ModalComponent = ({ tabIndex={-1} aria-hidden="true" style={{ zIndex: 45 }} - className="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full" + className="hidden overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 justify-center items-center w-full inset-0 h-[calc(100%-1rem)] max-h-full p-2 sm:p-0" > -
+
-

{title}

+

{title}

{!hideClose ? (